1 module dud.convert; 2 3 import std.array : empty, front; 4 import std.algorithm.iteration : filter; 5 import std.path; 6 import std.experimental.logger; 7 import std.file; 8 import std.format : format; 9 import std.stdio; 10 import std.typecons : nullable, Nullable; 11 12 import dud.options; 13 import dud.pkgdescription; 14 import dud.pkgdescription.json; 15 import dud.pkgdescription.sdl; 16 import dud.pkgdescription.output; 17 import dud.pkgdescription.exception; 18 19 @safe: 20 21 int convert(ref string[] args) { 22 const OptionReturn!(ConvertOptions) opts = getConvertOptions(args); 23 if(args == ["convert"]) { 24 writefln("Failed to process cmd options [%(%s, %)]", args); 25 return 1; 26 } 27 28 if(opts.common.help) { 29 () @trusted { writeOptions(stdout.lockingTextWriter(), opts); }(); 30 return 0; 31 } 32 33 const string relPath = opts.options.inputFilename.empty 34 ? dubFileInCWD() 35 : opts.options.inputFilename; 36 37 tracef(opts.common.vverbose, "Relative input file name '%s'", relPath); 38 const string absNormInputPath = relPath.absolutePath().buildNormalizedPath(); 39 tracef(opts.common.vverbose, "Absolute normalized input file name '%s'", 40 absNormInputPath); 41 42 if(!exists(absNormInputPath)) { 43 writefln("Input '%s' doesn't exists in the filesystem", 44 absNormInputPath); 45 return 1; 46 } 47 48 if(!isFile(absNormInputPath)) { 49 writefln("No File '%s' exists in the filesystem", absNormInputPath); 50 return 1; 51 } 52 53 const string inExt = extension(absNormInputPath); 54 tracef(opts.common.vverbose, "Input file extension '%s'", inExt); 55 56 if(inExt != ".json" && inExt != ".sdl") { 57 writefln("The file '%s' has an unsupported extension '%s'", 58 absNormInputPath, inExt); 59 return 0; 60 } 61 62 const string outFilename = opts.options.outputFilename.empty 63 ? buildOutFilename(opts.options.outputTargetType) 64 : opts.options.outputFilename; 65 66 tracef(opts.common.vverbose, "Relative output file name '%s'", outFilename); 67 const string absNormOutputPath = outFilename.absolutePath() 68 .buildNormalizedPath(); 69 tracef(opts.common.vverbose, "Absolute normalized output file name '%s'", 70 outFilename); 71 72 if(absNormOutputPath.empty 73 && opts.options.outputTargetType == ConvertTargetFormat.undefined) 74 { 75 writefln("Could determine output file name as target format was " 76 ~ "undefined"); 77 return 1; 78 } 79 80 if(!opts.options.override_ && exists(absNormOutputPath)) { 81 writefln("The given output file '%s' exists and no option were set" 82 ~ " to override the file", absNormOutputPath); 83 return 1; 84 } 85 86 const string outExt = extension(absNormOutputPath); 87 tracef(opts.common.vverbose, "Output file extension '%s'", outExt); 88 89 if(!extMatchesConvertTargetFormat(outExt, opts.options.outputTargetType)) { 90 writefln("The target format '%s' does not match the given output file" 91 ~ " name '%s'", opts.options.outputTargetType, absNormOutputPath); 92 return 1; 93 } 94 95 Nullable!PackageDescription nParse = parse(absNormInputPath, inExt, 96 opts.common); 97 if(nParse.isNull()) { 98 writefln("Failed to parse file '%s'", absNormInputPath); 99 return 2; 100 } 101 102 PackageDescription nnParse = nParse.get(); 103 104 tracef(opts.common.vverbose, "Write output to '%s'", absNormOutputPath); 105 const int writeRslt = writeOutput(nnParse, absNormOutputPath, outExt, 106 opts.common); 107 if(writeRslt != 0) { 108 writefln("Failed to copy the PackageDescription into file '%s'", 109 absNormOutputPath); 110 return 1; 111 } 112 113 if(!opts.options.keepInput) { 114 tracef("Removing '%s", absNormInputPath); 115 remove(absNormInputPath); 116 if(exists(absNormInputPath)) { 117 writefln("Failed to remove '%s'", absNormInputPath); 118 return 1; 119 } 120 } 121 122 return 0; 123 } 124 125 string dubFileInCWD() { 126 const string cwd = getcwd(); 127 const string js = buildPath(cwd, "dub.json"); 128 const string sdl = buildPath(cwd, "dub.sdl"); 129 const string pkg = buildPath(cwd, "package.json"); 130 131 auto ret = [js, sdl, pkg].filter!(it => exists(it)); 132 return ret.empty ? "" : ret.front; 133 } 134 135 string buildOutFilename(const ConvertTargetFormat ctf) { 136 const string cwd = getcwd(); 137 final switch(ctf) { 138 case ConvertTargetFormat.json: 139 return buildPath(cwd, "dub.json"); 140 case ConvertTargetFormat.sdl: 141 return buildPath(cwd, "dub.sdl"); 142 case ConvertTargetFormat.undefined: 143 return ""; 144 } 145 } 146 147 bool extMatchesConvertTargetFormat(string ext, const ConvertTargetFormat ctf) 148 pure nothrow @nogc 149 { 150 final switch(ctf) { 151 case ConvertTargetFormat.json: 152 return ext == ".json"; 153 case ConvertTargetFormat.sdl: 154 return ext == ".sdl"; 155 case ConvertTargetFormat.undefined: 156 return true; 157 } 158 } 159 160 Nullable!PackageDescription parse(const string absNormPath, const string inExt, 161 const ref CommonOptions opts) 162 { 163 const string input = readText(absNormPath); 164 Nullable!PackageDescription ret; 165 try { 166 ret = inExt == ".json" 167 ? nullable(jsonToPackageDescription(input)) 168 : nullable(sdlToPackageDescription(input)); 169 } catch(DudPkgDescriptionException e) { 170 () @trusted { 171 tracef(opts.vverbose, "%s %s", e.toString(), e.info); 172 }(); 173 printExceptionChain(e); 174 } 175 return ret; 176 } 177 178 int writeOutput(PackageDescription pkg, const string absOutputFilename, 179 const string ext, const ref CommonOptions opts) 180 { 181 import std.json; 182 auto f = File(absOutputFilename, "w"); 183 184 try { 185 if(ext == ".json") { 186 JSONValue tmp = dud.pkgdescription.output.toJSON(pkg); 187 f.writeln(tmp.toPrettyString()); 188 } else { 189 toSDL(pkg, f.lockingTextWriter()); 190 } 191 } catch(DudPkgDescriptionException e) { 192 () @trusted { 193 tracef(opts.vverbose, "%s %s", e.toString(), e.info); 194 }(); 195 printExceptionChain(e); 196 return 1; 197 } 198 return 0; 199 } 200 201 void printExceptionChain(Throwable it) @trusted { 202 while(it !is null) { 203 writefln(it.msg); 204 it = it.next(); 205 } 206 }