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, 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); 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 auto ctime = unixTimeToStdTime(gtime.time); 83 auto ctimeoff = gtime.offset.minutes(); 84 return SysTime(ctime, new immutable SimpleTimeZone(ctimeoff)); 85 } 86 87 git_time toGitTime(SysTime time) 88 { 89 git_time ret; 90 ret.time = stdTimeToUnixTime(time.stdTime); 91 ret.offset = cast(int)((time.timezone.utcToTZ(time.stdTime) - time.stdTime) / (10_000_000*60)); 92 return ret; 93 } 94 95 // TODO: unit tests for time functions! 96 97 98 /** 99 Converts the passed char slice to a C string, returning the null pointer for 100 empty strings. 101 102 libgit2 generally only switches to the default for optional string 103 parameters if they are null, vs. just the empty string. 104 */ 105 const(char)* gitStr(const(char)[] s) 106 { 107 import std.conv : toStringz; 108 return s.length ? s.toStringz : null; 109 } 110 111 mixin template RefCountedGitObject(T, alias free_function, bool define_chandle = true) 112 { 113 public: 114 bool opCast(T)() const if (is(T == bool)) { return cHandle !is null; } 115 116 package: 117 static if (define_chandle) { 118 @property inout(T)* cHandle() inout { return _data._payload; } 119 } 120 121 private: 122 struct Payload 123 { 124 this(T* payload) 125 { 126 _payload = payload; 127 } 128 129 ~this() 130 { 131 if (_payload !is null) 132 { 133 free_function(_payload); 134 _payload = null; 135 } 136 } 137 138 /// Should never perform copy 139 @disable this(this); 140 141 /// Should never perform assign 142 @disable void opAssign(typeof(this)); 143 144 T* _payload; 145 } 146 147 import std.typecons : RefCounted, RefCountedAutoInitialize; 148 alias RefCounted!(Payload, RefCountedAutoInitialize.no) Data; 149 Data _data; 150 }