Skip to content

Commit

Permalink
Allow reformatting of multiple files
Browse files Browse the repository at this point in the history
When using -i or --test.

See google#210.
  • Loading branch information
sbarzowski committed Jul 14, 2017
1 parent 12d0d1f commit c5c6858
Showing 1 changed file with 134 additions and 71 deletions.
205 changes: 134 additions & 71 deletions cmd/jsonnet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,8 @@ void usage(std::ostream &o)
o << " -h / --help This message\n";
o << " -e / --exec Treat filename as code\n";
o << " -o / --output-file <file> Write to the output file rather than stdout\n";
o << " -i / --in-place Update the Jsonnet file in place. Same as -o <filename>\n";
o << " --test Exit with failure if reformatting changed the file.\n";
o << " -i / --in-place Update the Jsonnet file(s) in place. Same as -o <filename>\n";
o << " --test Exit with failure if reformatting changed the file(s).\n";
o << " -n / --indent <n> Number of spaces to indent by (default 0, means no change)\n";
o << " --max-blank-lines <n> Max vertical spacing, 0 means no change (default 2)\n";
o << " --string-style <d|s|l> Enforce double, single quotes or 'leave' (the default)\n";
Expand All @@ -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 << "<filename> can be - (stdin)\n";
Expand Down Expand Up @@ -152,7 +153,7 @@ enum Command {
/** Class for representing configuration read from command line flags. */
struct JsonnetConfig {
Command cmd;
std::string inputFile;
std::vector<std::string> inputFiles;
std::string outputFile;
bool filenameIsCode;

Expand Down Expand Up @@ -455,49 +456,73 @@ 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);
return false;
bool multiple_files_allowed = config->cmd == FMT &&
(config->fmtTest || config->fmtInPlace);
if (!multiple_files_allowed) {
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->inputFile = filename;
config->inputFiles = remaining_args;
return true;
}


/** Reads Jsonnet code from the input file or stdin into the input buffer. */
static bool read_input(JsonnetConfig* config, std::string* input) {
if (config->filenameIsCode) {
*input = config->inputFile;
config->inputFile = "<cmdline>";
static bool read_input_content(std::string filename, std::string *input) {
// Input file "-" tells Jsonnet to read stdin.
if (filename == "-") {
input->assign(std::istreambuf_iterator<char>(std::cin),
std::istreambuf_iterator<char>());
} else {
// Input file "-" tell Jsonnet to read stdin.
if (config->inputFile == "-") {
config->inputFile = "<stdin>";
input->assign(std::istreambuf_iterator<char>(std::cin),
std::istreambuf_iterator<char>());
} 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<char>(f),
std::istreambuf_iterator<char>());
if (!f.good()) {
std::string msg = "Reading input file: " + config->inputFile;
perror(msg.c_str());
return false;
}
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<char>(f),
std::istreambuf_iterator<char>());
if (!f.good()) {
std::string msg = "Reading input file: " + filename;
perror(msg.c_str());
return false;
}
}
return true;
}

void change_special_filename(JsonnetConfig *config, std::string *filename) {
if (config->filenameIsCode) {
*filename = "<cmdline>";
} else if (*filename == "-") {
*filename = "<stdin>";
}
}

/** Gets Jsonnet code from any source into the input buffer and changes
* the filename if it's not an actual filename (e.g. "-"). */
static bool read_input(JsonnetConfig* config, std::string *filename,
std::string* input) {
bool ok;
if (config->filenameIsCode) {
*input = *filename;
ok = true;
} else {
ok = read_input_content(*filename, input);
}
// Let's change the filename to something we can show the user
// if it is not a real filename.
change_special_filename(config, filename);
return ok;
}

/** Writes output files for multiple file output */
static bool write_multi_output_files(JsonnetVm* vm, char* output,
const std::string& output_dir)
Expand Down Expand Up @@ -618,32 +643,33 @@ 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, &config.inputFiles[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) {
std::cerr << output;
std::cerr.flush();
jsonnet_realloc(vm, output, 0);
jsonnet_destroy(vm);
return EXIT_FAILURE;
Expand Down Expand Up @@ -673,38 +699,75 @@ 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(&config, &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;
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);
if (!ok) {
return 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, &config.inputFiles[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);
output = jsonnet_fmt_snippet(vm, config.inputFiles[0].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;
if (error) {
std::cerr << output;
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);
Expand Down

0 comments on commit c5c6858

Please sign in to comment.