20 std::cerr <<
"usage:\n"
21 <<
" haio convert input.png [filters] output.ppm\n"
22 <<
" haio convert png:- [filters] ppm:-\n"
26 auto out = std::string(option.spellings[0]);
27 if (option.args == 0)
return out;
28 out += option.optional ?
" [" + std::string(option.takes) +
"]" :
" " + std::string(option.takes);
34 if (option.filter) width = std::max(width, spelled(option).size());
38 if (!option.filter)
continue;
39 const auto line = spelled(option);
40 std::cerr <<
" " << line << std::string(width - line.size() + 2,
' ') << option.help <<
'\n';
43 std::cerr <<
"\nevery option also takes its value after an =, and several answer to more than\n"
44 "one spelling; docs/convert-ebnf.md lists them all.\n";
49 std::cerr <<
"[error] " << error.message;
50 if (!error.token.empty()) std::cerr <<
": " << error.token;
54std::vector<uint8_t> readStream(std::istream& in) {
55 return {std::istreambuf_iterator<char>(in), std::istreambuf_iterator<char>()};
58std::vector<uint8_t> readFile(
const std::filesystem::path& path) {
59 std::ifstream in(path, std::ios::binary);
60 if (!in)
throw std::runtime_error(
"could not open input: " + path.string());
61 return readStream(in);
64Blob readInputBlob(
Command& command) {
66 blob.path = command.inputPath;
67 blob.format = command.inputFormat;
69 blob.data = command.inputPath ==
"-" ? readStream(std::cin) : readFile(command.inputPath);
71 const auto found =
Detect(blob.data);
72 const bool explicitFormat = !command.inputFormatName.empty();
74 if (found && found.format != command.inputFormat) {
75 std::cerr <<
"warning: " << command.inputPath <<
" is " <<
formatName(found.format)
76 <<
", not " <<
formatName(command.inputFormat);
79 std::cerr <<
"; decoding as " <<
formatName(command.inputFormat) <<
" anyway\n";
81 std::cerr <<
"; decoding as " <<
formatName(found.format) <<
'\n';
82 command.inputFormat = found.format;
83 blob.format = found.format;
84 blob.color = found.color;
88 blob.color = found.color;
94void writeOutput(
const Command& command,
const std::vector<uint8_t>& data) {
95 if (command.outputIsStdout) {
96 std::cout.write(
reinterpret_cast<const char*
>(data.data()),
static_cast<std::streamsize
>(data.size()));
97 if (!std::cout)
throw std::runtime_error(
"could not write output: stdout");
101 std::ofstream out(command.outputPath, std::ios::binary);
102 if (!out)
throw std::runtime_error(
"could not open output: " + command.outputPath);
103 out.write(
reinterpret_cast<const char*
>(data.data()),
static_cast<std::streamsize
>(data.size()));
104 if (!out)
throw std::runtime_error(
"could not write output: " + command.outputPath);
117 printError(command.error);
122 if (command.hasGenerator) {
126 auto input = readInputBlob(command);
128 const auto output =
runPipeline(std::move(input), pipeline);
130 std::cerr <<
"[error] " << output.error().message <<
'\n';
134 writeOutput(command, output->data);
136 }
catch (
const std::exception& err) {
137 std::cerr <<
"[error] " << err.what() <<
'\n';
constexpr Option options[]
Pipeline buildPipeline(const Command &command)
Command parseArgs(int argc, char *argv[])
int runCli(int argc, char *argv[])
Result< Blob > runPipeline(Blob input, const Pipeline &pipeline, std::optional< std::chrono::steady_clock::time_point > deadline=std::nullopt)
constexpr std::string_view formatName(Format wanted)
std::string_view contentTypeFor(Format format)