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 }