1 /* 2 * Copyright Andrej Mitrovic 2013, David Nadlinger 2014. 3 * Distributed under the Boost Software License, Version 1.0. 4 * (See accompanying file LICENSE_1_0.txt or copy at 5 * http://www.boost.org/LICENSE_1_0.txt) 6 */ 7 module git.util; 8 9 /** 10 Contains utility functions for this package. 11 */ 12 13 import std.array; 14 import std.conv; 15 import std.datetime; 16 import std.exception; 17 import std..string; 18 19 import deimos.git2.errors; 20 import deimos.git2.types : git_time; 21 22 import git.exception; 23 24 /** 25 Require the result to be either 1 or 0. If it is, return the boolean value, 26 otherwise throw a GitException. 27 */ 28 package bool requireBool(int result, string file = __FILE__, size_t line = __LINE__) 29 { 30 require(result == 0 || result == 1); 31 return result == 1; 32 } 33 34 /** 35 Call this function when an error code is returned from a git function. 36 It will retrieve the last error and throw a GitException. 37 38 $(RED Note:) assert or in blocks should be used to verify arguments (such as strings) 39 before calling Git functions since Git itself does not check pointers for null. 40 Passing null pointers to Git functions usually results in access violations. 41 */ 42 package void require(bool state, Exception next = null, string file = __FILE__, size_t line = __LINE__) 43 { 44 if (state) 45 return; 46 47 const(git_error)* gitError = giterr_last(); 48 49 enforce(gitError !is null, 50 "Error: No Git error thrown, error condition check is likely invalid."); 51 52 const msg = format("Git error (%s): %s.", cast(git_error_t)gitError.klass, to!string(gitError.message)); 53 54 giterr_clear(); 55 throw new GitException(msg, file, line, next); 56 } 57 58 /// 59 unittest 60 { 61 import deimos.git2.oid; 62 git_oid oid; 63 assertThrown!GitException(require(git_oid_fromstr(&oid, "foobar") == 0)); 64 } 65 66 /** Return a posix-native path, replacing backslashes with forward slashes. */ 67 string toPosixPath(string input) 68 { 69 return input.replace(`\`, `/`); 70 } 71 72 /// 73 unittest 74 { 75 assert(`foo/bar\doo`.toPosixPath == r"foo/bar/doo"); 76 } 77 78 alias toSlice = to!(const(char)[]); 79 80 SysTime toSysTime(git_time gtime) 81 { 82 import core.stdc.time; 83 auto ctime = unixTimeToStdTime(cast(time_t)gtime.time); 84 auto ctimeoff = gtime.offset.minutes(); 85 return SysTime(ctime, new immutable SimpleTimeZone(ctimeoff)); 86 } 87 88 git_time toGitTime(SysTime time) 89 { 90 git_time ret; 91 ret.time = stdTimeToUnixTime(time.stdTime); 92 ret.offset = cast(int)((time.timezone.utcToTZ(time.stdTime) - time.stdTime) / (10_000_000*60)); 93 return ret; 94 } 95 96 // TODO: unit tests for time functions! 97 98 99 /** 100 Converts the passed char slice to a C string, returning the null pointer for 101 empty strings. 102 103 libgit2 generally only switches to the default for optional string 104 parameters if they are null, vs. just the empty string. 105 */ 106 const(char)* gitStr(const(char)[] s) 107 { 108 import std..string : toStringz; 109 return s.length ? s.toStringz : null; 110 } 111 112 mixin template RefCountedGitObject(T, alias free_function, bool define_chandle = true) 113 { 114 public: 115 bool opCast(T)() const if (is(T == bool)) { return cHandle !is null; } 116 117 package: 118 static if (define_chandle) { 119 @property inout(T)* cHandle() inout { return _data._payload; } 120 } 121 122 private: 123 struct Payload 124 { 125 this(T* payload) 126 { 127 _payload = payload; 128 } 129 130 ~this() 131 { 132 if (_payload !is null) 133 { 134 free_function(_payload); 135 _payload = null; 136 } 137 } 138 139 /// Should never perform copy 140 @disable this(this); 141 142 /// Should never perform assign 143 @disable void opAssign(typeof(this)); 144 145 T* _payload; 146 } 147 148 import std.typecons : RefCounted, RefCountedAutoInitialize; 149 alias RefCounted!(Payload, RefCountedAutoInitialize.no) Data; 150 Data _data; 151 }