From 296ad990c29ab240f479656066d0566826e5f79a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stanis=C5=82aw=20Barzowski?= Date: Tue, 11 Jul 2017 16:03:55 -0400 Subject: [PATCH] Allow reformatting of multiple files When using -i or --test. See #210. --- cmd/jsonnet.cpp | 180 +++++++++++++++++++++++++++++++----------------- 1 file changed, 115 insertions(+), 65 deletions(-) diff --git a/cmd/jsonnet.cpp b/cmd/jsonnet.cpp index b92f0a9a0..35a4fdb4c 100644 --- a/cmd/jsonnet.cpp +++ b/cmd/jsonnet.cpp @@ -91,7 +91,7 @@ void usage(std::ostream &o) o << "Available options for specifying values of 'external' variables:\n"; o << "Provide the value as a string:\n"; o << " -V / --ext-str [=] If is omitted, get from environment var \n"; - o << " --ext-str-file = Read the string from the file\n"; + o << " --ext-str-file = Read the string from the file(s)\n"; o << "Provide a value as Jsonnet code:\n"; o << " --ext-code [=] If is omitted, get from environment var \n"; o << " --ext-code-file = Read the code from the file\n"; @@ -110,7 +110,7 @@ void usage(std::ostream &o) o << " -h / --help This message\n"; o << " -e / --exec Treat filename as code\n"; o << " -o / --output-file Write to the output file rather than stdout\n"; - o << " -i / --in-place Update the Jsonnet file in place. Same as -o \n"; + o << " -i / --in-place Update the Jsonnet file(s) in place. Same as -o \n"; o << " --test Exit with failure if reformatting changed the file.\n"; o << " -n / --indent Number of spaces to indent by (default 0, means no change)\n"; o << " --max-blank-lines Max vertical spacing, 0 means no change (default 2)\n"; @@ -121,6 +121,7 @@ void usage(std::ostream &o) o << " --[no-]pad-objects { x: 1, x: 2 } instead of {x: 1, y: 2} (on by default)\n"; o << " --debug-desugaring Unparse the desugared AST without executing it\n"; o << " --version Print version\n"; + o << "Note: Multiple filenames can be provided at once when using -i or --test options.\n"; o << "\n"; o << "In all cases:\n"; o << " can be - (stdin)\n"; @@ -152,7 +153,7 @@ enum Command { /** Class for representing configuration read from command line flags. */ struct JsonnetConfig { Command cmd; - std::string inputFile; + std::vector inputFiles; std::string outputFile; bool filenameIsCode; @@ -455,44 +456,53 @@ static bool process_args(int argc, return false; } - std::string filename = remaining_args[0]; - if (remaining_args.size() > 1) { - std::cerr << "ERROR: Already specified " << want - << " as \"" << filename << "\"\n" - << std::endl; - usage(std::cerr); + if (config->cmd == FMT && (config->fmtTest || config->fmtInPlace)) { + config->inputFiles = remaining_args; + } else { + std::string filename = remaining_args[0]; + if (remaining_args.size() > 1) { + std::cerr << "ERROR: Already specified " << want + << " as \"" << filename << "\"\n" + << std::endl; + usage(std::cerr); + return false; + } + config->inputFiles = {filename}; + } + return true; +} + +static bool read_input_contents(std::string filename, std::string *input) { + std::ifstream f; + f.open(filename); + if (!f.good()) { + std::string msg = "Opening input file: " + filename; + perror(msg.c_str()); + return false; + } + input->assign(std::istreambuf_iterator(f), + std::istreambuf_iterator()); + if (!f.good()) { + std::string msg = "Reading input file: " + filename; + perror(msg.c_str()); return false; } - config->inputFile = filename; return true; } /** Reads Jsonnet code from the input file or stdin into the input buffer. */ -static bool read_input(JsonnetConfig* config, std::string* input) { +static bool read_input(JsonnetConfig* config, int index, std::string* input) { if (config->filenameIsCode) { - *input = config->inputFile; - config->inputFile = ""; + *input = config->inputFiles[index]; + config->inputFiles[index] = ""; // side effect! } else { - // Input file "-" tell Jsonnet to read stdin. - if (config->inputFile == "-") { - config->inputFile = ""; + // Input file "-" tells Jsonnet to read stdin. + if (config->inputFiles[index] == "-") { + config->inputFiles[index] = ""; // side effect! input->assign(std::istreambuf_iterator(std::cin), std::istreambuf_iterator()); } else { - std::ifstream f; - f.open(config->inputFile.c_str()); - if (!f.good()) { - std::string msg = "Opening input file: " + config->inputFile; - perror(msg.c_str()); - return false; - } - input->assign(std::istreambuf_iterator(f), - std::istreambuf_iterator()); - if (!f.good()) { - std::string msg = "Reading input file: " + config->inputFile; - perror(msg.c_str()); - return false; - } + return read_input_contents(config->inputFiles[index], input); } } return true; @@ -618,27 +628,29 @@ int main(int argc, const char **argv) return EXIT_FAILURE; } - // Read input files. - std::string input; - if (!read_input(&config, &input)) { - jsonnet_destroy(vm); - return EXIT_FAILURE; - } - // Evaluate input Jsonnet and handle any errors from Jsonnet VM. int error; char *output; switch (config.cmd) { case EVAL: { + assert(config.inputFiles.size() == 1); + + // Read input file. + std::string input; + if (!read_input(&config, 0, &input)) { + jsonnet_destroy(vm); + return EXIT_FAILURE; + } + if (config.evalMulti) { output = jsonnet_evaluate_snippet_multi( - vm, config.inputFile.c_str(), input.c_str(), &error); + vm, config.inputFiles[0].c_str(), input.c_str(), &error); } else if (config.evalStream) { output = jsonnet_evaluate_snippet_stream( - vm, config.inputFile.c_str(), input.c_str(), &error); + vm, config.inputFiles[0].c_str(), input.c_str(), &error); } else { output = jsonnet_evaluate_snippet( - vm, config.inputFile.c_str(), input.c_str(), &error); + vm, config.inputFiles[0].c_str(), input.c_str(), &error); } if (error) { @@ -673,38 +685,76 @@ int main(int argc, const char **argv) case FMT: { std::string output_file = config.outputFile; - if (config.fmtInPlace) { - if (config.inputFile == "-") { - std::cerr << "ERROR: Cannot use --in-place with stdin" << std::endl; - jsonnet_destroy(vm); - return EXIT_FAILURE; + + if (config.fmtInPlace || config.fmtTest) { + assert(config.inputFiles.size() >= 1); + for (std::string inputFile: config.inputFiles) { + if (config.fmtInPlace) { + output_file = inputFile; + + if (inputFile == "-") { + std::cerr << "ERROR: Cannot use --in-place with stdin" << std::endl; + jsonnet_destroy(vm); + return EXIT_FAILURE; + } + if (config.filenameIsCode) { + std::cerr << "ERROR: Cannot use --in-place with --exec" << std::endl; + jsonnet_destroy(vm); + return EXIT_FAILURE; + } + } + + std::string input; + if (!read_input_contents(inputFile, &input)) { + jsonnet_destroy(vm); + return EXIT_FAILURE; + } + + output = jsonnet_fmt_snippet(vm, inputFile.c_str(), input.c_str(), &error); + + if (error) { + std::cerr << output; + std::cerr.flush(); + jsonnet_realloc(vm, output, 0); + jsonnet_destroy(vm); + return EXIT_FAILURE; + } + + if (config.fmtTest) { + // Check the output matches the input. + bool ok = output == input; + jsonnet_realloc(vm, output, 0); + jsonnet_destroy(vm); + return ok ? EXIT_SUCCESS : 2; + } else { + // Write output Jsonnet. + bool successful = write_output_file(output, output_file); + jsonnet_realloc(vm, output, 0); + if (!successful) { + jsonnet_destroy(vm); + return EXIT_FAILURE; + } + } } - if (config.filenameIsCode) { - std::cerr << "ERROR: Cannot use --in-place with --exec" << std::endl; + } else { + assert(config.inputFiles.size() == 1); + // Read input file. + std::string input; + if (!read_input(&config, 0, &input)) { jsonnet_destroy(vm); return EXIT_FAILURE; } - output_file = config.inputFile; - } - - output = jsonnet_fmt_snippet(vm, config.inputFile.c_str(), input.c_str(), &error); - if (error) { - std::cerr << output; - std::cerr.flush(); - jsonnet_realloc(vm, output, 0); - jsonnet_destroy(vm); - return EXIT_FAILURE; - } + output = jsonnet_fmt_snippet(vm, config.inputFiles[0].c_str(), input.c_str(), &error); - if (config.fmtTest) { - // Check the output matches the input. - bool ok = output == input; - jsonnet_realloc(vm, output, 0); - jsonnet_destroy(vm); - return ok ? EXIT_SUCCESS : 2; + if (error) { + std::cerr << output; + std::cerr.flush(); + jsonnet_realloc(vm, output, 0); + jsonnet_destroy(vm); + return EXIT_FAILURE; + } - } else { // Write output Jsonnet. bool successful = write_output_file(output, output_file); jsonnet_realloc(vm, output, 0);