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 );