1 /**
2  * Defines everything required to interact with the Coveralls.io API.
3  */
4 module doveralls.args;
5 
6 import std.typecons, std.array;
7 
8 /**
9  * A hash representing the coverage data from a single run of a test suite.
10  *
11  * You must specify either repo_token or a service name and job id.
12  */
13 struct CoverallsArgs
14 {
15 public:
16     /**
17      * REQUIRED
18      * The secret token for your repository, found at the bottom of your
19      * repository's page on Coveralls.
20      */
21     string repo_token;
22 
23     /**
24      * REQUIRED
25      * The CI service or other environment in which the test suite was run.
26      * This can be anything, but certain services have special features
27      * (travis-ci, travis-pro, or coveralls-ruby).
28      */
29     string service_name;
30 
31     /**
32      * OPTIONAL
33      * A unique identifier of the job on the service specified by service_name.
34      */
35     string service_job_id;
36 
37     /**
38      * REQUIRED
39      * An array of source files, including their coverage data.
40      */
41     SourceFile[] source_files;
42 
43     /**
44      * OPTIONAL
45      * A hash of Git data that can be used to display more information to users.
46      *
47      * Example:
48      * ---
49      * "git": {
50      *   "head": {
51      *     "id": "b31f08d07ae564b08237e5a336e478b24ccc4a65",
52      *     "author_name": "Nick Merwin",
53      *     "author_email": "...",
54      *     "committer_name": "Nick Merwin",
55      *     "committer_email": "...",
56      *     "message": "version bump"
57      *   },
58      *   "branch": "master",
59      *   "remotes": [
60      *     {
61      *       "name": "origin",
62      *       "url": "git@github.com:lemurheavy/coveralls-ruby.git"
63      *     }
64      *   ]
65      * }
66      * ---
67      */
68     GitEntry git;
69 
70     /**
71      * OPTIONAL
72      * A timestamp of when the job ran. Must be parsable by Ruby.
73      *
74      * Example:
75      * ---
76      * "run_at": "2013-02-18 00:52:48 -0800"
77      * ---
78      */
79     string run_at;
80 
81     /// Converts the instance to json
82     auto toJson()
83     {
84         import medea;
85         import std.algorithm, std.array;
86 
87         ObjectValue root = new ObjectValue;
88 
89         mixin( stringValue!q{repo_token} );
90         mixin( stringValue!q{service_name} );
91         mixin( stringValue!q{service_job_id} );
92         root[ "source_files" ] = new ArrayValue( cast(Value[])source_files.map!( file => file.toJson() ).array() );
93         root[ "git" ] = git.toJson();
94         mixin( stringValue!q{run_at} );
95 
96         return root;
97     }
98 
99 static: // Struct definitions
100     struct SourceFile
101     {
102     public:
103         /**
104          * REQUIRED
105          * Represents the file path of this source file. Must be unique in the
106          * job. Can include slashes. The file type for syntax highlighting will
107          * be determined from the file extension in this parameter.
108          */
109         string name;
110 
111         /**
112          * REQUIRED
113          * The full source code of this file. Newlines should use UNIX-style
114          * line endings (\n).
115          */
116         string source;
117 
118         /**
119          * REQUIRED
120          * The coverage data for this file for the file's job.
121          *
122          * The item at index 0 represents the coverage for line 1 of the source
123          * code.
124          *
125          * Acceptable values in the array:
126          *     A positive integer if the line is covered, representing the
127          *         number of times the line is hit during the test suite.
128          *     0 if the line is not covered by the test suite.
129          *     null to indicate the line is not relevant to code coverage
130          *         (it may be whitespace or a comment).
131          *
132          * Example:
133          *     [null, 1, 0, null, 4, 15, null]
134          */
135         Nullable!uint[] coverage;
136 
137         /// Converts instance to json.
138         auto toJson()
139         {
140             import medea;
141 
142             Value[] coverageValues;
143             foreach( val; coverage )
144             {
145                 if( val.isNull )
146                     coverageValues ~= new NullValue;
147                 else
148                     coverageValues ~= new UIntegerValue( val );
149             }
150 
151             return new ObjectValue( [
152                 "name": cast(Value) new StringValue( name ),
153                 "source": cast(Value) new StringValue( source ),
154                 "coverage": cast(Value) new ArrayValue( coverageValues )
155             ] );
156         }
157     }
158 
159     struct GitEntry
160     {
161     public: // Member definitions
162         Commit head;
163         string branch;
164         Remote[] remotes;
165 
166         auto toJson()
167         {
168             import medea;
169             import std.algorithm, std.array;
170             ObjectValue root = new ObjectValue;
171 
172             root[ "head" ] = head.toJson();
173             mixin( stringValue!q{branch} );
174             root[ "remotes" ] = new ArrayValue( cast(Value[])remotes.map!( remote => remote.toJson() ).array() );
175 
176             return root;
177         }
178 
179     static: // Struct definitions
180         struct Commit
181         {
182         public:
183             string id;
184             string author_name;
185             string author_email;
186             string committer_name;
187             string committer_email;
188             string message;
189 
190             mixin( stringOnlyToJson!q{Commit} );
191         }
192 
193         struct Remote
194         {
195             string name;
196             string url;
197 
198             mixin( stringOnlyToJson!q{Remote} );
199         }
200     }
201 }
202 
203 private enum stringOnlyToJson( string struckt ) = q{
204     auto toJson()
205     {
206         import medea;
207         ObjectValue root = new ObjectValue;
208 
209         foreach( memberName; __traits(allMembers, $struckt) )
210         {
211             static if( is( typeof(__traits(getMember, this, memberName )) == string ) )
212             {
213                 mixin( stringValue!memberName );
214             }
215         }
216 
217         return root;
218     }
219 }.replace( "$struckt", struckt );
220 
221 private enum stringValue( string name ) = q{
222     if( $name && $name.length )
223     {
224         //root[ "$name" ] = new StringValue( args.$name );
225         root[ "$name" ] = new StringValue( $name );
226     }
227 }.replace( "$name", name );