Deduplicate code in setup of serialization converters

The setup of the input and output converters is fairly complex but was
made harder to read by nearly-duplicating its logic for input and for
output. Break out into a separate function to make clear what parts
differ between the two and what parts don't. In the process, allow the
search for a named converter to continue past a match that doesn't
support the needed direction, on the off chance of a name collision.
Make an error message more articulate in the process.

Pick-to: 6.6 6.5
Task-number: QTBUG-111228
Change-Id: I2a9de8b406c538098076f388fc8a1980b91fe16b
Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
This commit is contained in:
Edward Welbourne 2023-09-04 17:55:47 +02:00
parent 02d8dc5f8c
commit 6a7b7272cd

View File

@ -1,4 +1,5 @@
// Copyright (C) 2018 Intel Corporation.
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "converter.h"
@ -27,6 +28,50 @@ Converter::~Converter()
availableConverters->removeAll(this);
}
static const Converter *prepareConverter(QString format, Converter::Direction direction,
QFile *stream)
{
const bool out = direction == Converter::Direction::Out;
const QIODevice::OpenMode mode = out
? QIODevice::WriteOnly | QIODevice::Truncate
: QIODevice::ReadOnly;
const char *dirn = out ? "output" : "input";
if (stream->fileName().isEmpty())
stream->open(out ? stdout : stdin, mode);
else
stream->open(mode);
if (!stream->isOpen()) {
fprintf(stderr, "Could not open \"%s\" for %s: %s\n",
qPrintable(stream->fileName()), dirn, qPrintable(stream->errorString()));
} else if (format == "auto"_L1) {
for (const Converter *conv : std::as_const(*availableConverters)) {
if (conv->directions().testFlag(direction) && conv->probeFile(stream))
return conv;
}
if (out) // Failure to identify output format can be remedied by loadFile().
return nullptr;
// Input format, however, we must know before we can call that:
fprintf(stderr, "Could not determine input format. Specify it with the -I option.\n");
} else {
for (const Converter *conv : std::as_const(*availableConverters)) {
if (conv->name() == format) {
if (!conv->directions().testFlag(direction)) {
fprintf(stderr, "File format \"%s\" cannot be used for %s\n",
qPrintable(format), dirn);
continue; // on the off chance there's another with the same name
}
return conv;
}
}
fprintf(stderr, "Unknown %s file format \"%s\"\n", dirn, qPrintable(format));
}
exit(EXIT_FAILURE);
Q_UNREACHABLE_RETURN(nullptr);
}
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
@ -103,91 +148,13 @@ int main(int argc, char *argv[])
return EXIT_FAILURE;
}
const Converter *inconv = nullptr;
QString format = parser.value(inputFormatOption);
if (format != "auto"_L1) {
for (const Converter *conv : std::as_const(*availableConverters)) {
if (conv->name() == format) {
inconv = conv;
break;
}
}
if (!inconv || !inconv->directions().testFlag(Converter::Direction::In)) {
fprintf(stderr, inconv ? "File format \"%s\" cannot be used for input\n"
: "Unknown input file format \"%s\"\n", qPrintable(format));
return EXIT_FAILURE;
}
}
const Converter *outconv = nullptr;
format = parser.value(outputFormatOption);
if (format != "auto"_L1) {
for (const Converter *conv : std::as_const(*availableConverters)) {
if (conv->name() == format) {
outconv = conv;
break;
}
}
if (!outconv || !outconv->directions().testFlag(Converter::Direction::Out)) {
fprintf(stderr, outconv ? "File format \"%s\" cannot be used for output\n"
: "Unknown output file format \"%s\"\n", qPrintable(format));
return EXIT_FAILURE;
}
}
QStringList files = parser.positionalArguments();
QFile input(files.value(0));
QFile output(files.value(1));
if (input.fileName().isEmpty())
input.open(stdin, QIODevice::ReadOnly);
else
input.open(QIODevice::ReadOnly);
if (!input.isOpen()) {
fprintf(stderr, "Could not open \"%s\" for reading: %s\n",
qPrintable(input.fileName()), qPrintable(input.errorString()));
return EXIT_FAILURE;
}
if (output.fileName().isEmpty())
output.open(stdout, QIODevice::WriteOnly | QIODevice::Truncate);
else
output.open(QIODevice::WriteOnly | QIODevice::Truncate);
if (!output.isOpen()) {
fprintf(stderr, "Could not open \"%s\" for writing: %s\n",
qPrintable(output.fileName()), qPrintable(output.errorString()));
return EXIT_FAILURE;
}
if (!inconv) {
// probe the input to find a file format
for (const Converter *conv : std::as_const(*availableConverters)) {
if (conv->directions().testFlag(Converter::Direction::In)
&& conv->probeFile(&input)) {
inconv = conv;
break;
}
}
if (!inconv) {
fprintf(stderr, "Could not determine input format. pass -I option.\n");
return EXIT_FAILURE;
}
}
if (!outconv) {
// probe the output to find a file format
for (const Converter *conv : std::as_const(*availableConverters)) {
if (conv->directions().testFlag(Converter::Direction::Out)
&& conv->probeFile(&output)) {
outconv = conv;
break;
}
}
// If that failed, loadFile() shall supply a fallback.
}
const Converter *inconv = prepareConverter(parser.value(inputFormatOption),
Converter::Direction::In, &input);
const Converter *outconv = prepareConverter(parser.value(outputFormatOption),
Converter::Direction::Out, &output);
// now finally perform the conversion
QVariant data = inconv->loadFile(&input, outconv);