denigma 4.0.0
Loading...
Searching...
No Matches
conversion.h
1/*
2 * Copyright (C) 2026, Robert Patterson
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to deal
6 * in the Software without restriction, including without limitation the rights
7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 * copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 * THE SOFTWARE.
21 */
22#pragma once
23
24#include <cassert>
25#include <cstddef>
26#include <functional>
27#include <memory>
28#include <ostream>
29#include <span>
30#include <stdexcept>
31#include <string>
32#include <string_view>
33#include <vector>
34#include <utility>
35
36#include "denigma/io/random_access_reader.h"
37
40namespace denigma {
41
44enum class FormatId
45{
46 Musx,
47 EnigmaXml,
48 MnxJson,
49 MusicXml,
50 MssXml,
51 Svg
52};
53
57{
58 Info,
59 Warning,
60 Error,
61 Verbose
62};
63
67{
68 MessageSeverity severity{ MessageSeverity::Info };
69 std::string message;
70};
71
75{
77 std::string sourceName;
79 bool validate{ true };
81 bool verbose{ false };
83 bool quiet{ false };
85 bool allFontsAvailable{ false };
87 std::function<void(MessageSeverity severity, std::string_view message)> logCallback = [](MessageSeverity, std::string_view) {};
88};
89
93{
94public:
95 virtual ~IOptions() = default;
96};
97
101{
104};
105
109{
110public:
112 [[nodiscard]] std::span<const Diagnostic> diagnostics() const noexcept
113 {
114 return m_diagnostics;
115 }
116
118 [[nodiscard]] bool hasError() const noexcept
119 {
120 return m_hasError;
121 }
122
124 explicit operator bool() const noexcept
125 {
126 return !hasError();
127 }
128
130 void addDiagnostic(MessageSeverity severity, std::string message)
131 {
132 addDiagnostic(Diagnostic{ severity, std::move(message) });
133 }
134
136 void addDiagnostic(Diagnostic diagnostic)
137 {
138 m_hasError = m_hasError || diagnostic.severity == MessageSeverity::Error;
139 m_diagnostics.push_back(std::move(diagnostic));
140 }
141
142private:
143 std::vector<Diagnostic> m_diagnostics;
144 bool m_hasError{};
145};
146
150template <typename OptionsT>
151OptionsT optionsFromRequest(const ConversionRequest& request, std::string_view converterName)
152{
153 if (!request.options) {
154 return {};
155 }
156 if (const auto* options = dynamic_cast<const OptionsT*>(request.options)) {
157 return *options;
158 }
159 assert(false && "incompatible conversion options");
160 throw std::invalid_argument(std::string(converterName) + " received incompatible conversion options.");
161}
162
166{
167public:
168 virtual ~IConverter() = default;
169
171 [[nodiscard]] virtual FormatId sourceFormat() const = 0;
173 [[nodiscard]] virtual FormatId targetFormat() const = 0;
174
176 virtual ConversionResult convert(std::span<const std::byte> input,
177 std::ostream& output,
178 const ConversionRequest& request = {}) const = 0;
179};
180
184{
185public:
186 virtual ~IReaderConverter() = default;
187
189 [[nodiscard]] virtual FormatId sourceFormat() const = 0;
191 [[nodiscard]] virtual FormatId targetFormat() const = 0;
192
195 std::ostream& output,
196 const ConversionRequest& request = {}) const = 0;
197};
198
200using MultiOutputCallback = std::function<void(std::string_view suggestedName, std::span<const std::byte> data)>;
201
205{
206public:
207 virtual ~IMultiOutputConverter() = default;
208
210 [[nodiscard]] virtual FormatId sourceFormat() const = 0;
212 [[nodiscard]] virtual FormatId targetFormat() const = 0;
213
215 virtual ConversionResult convert(std::span<const std::byte> input,
216 const MultiOutputCallback& outputCallback,
217 const ConversionRequest& request = {}) const = 0;
218};
219
223{
224public:
225 virtual ~IReaderMultiOutputConverter() = default;
226
228 [[nodiscard]] virtual FormatId sourceFormat() const = 0;
230 [[nodiscard]] virtual FormatId targetFormat() const = 0;
231
234 const MultiOutputCallback& outputCallback,
235 const ConversionRequest& request = {}) const = 0;
236};
237
241{
242public:
244 void add(std::unique_ptr<IConverter> converter)
245 {
246 if (!converter) {
247 throw std::invalid_argument("converter cannot be null");
248 }
249 m_converters.emplace_back(std::move(converter));
250 }
251
253 void add(std::unique_ptr<IMultiOutputConverter> converter)
254 {
255 if (!converter) {
256 throw std::invalid_argument("converter cannot be null");
257 }
258 m_multiOutputConverters.emplace_back(std::move(converter));
259 }
260
262 void add(std::unique_ptr<IReaderConverter> converter)
263 {
264 if (!converter) {
265 throw std::invalid_argument("converter cannot be null");
266 }
267 m_readerConverters.emplace_back(std::move(converter));
268 }
269
271 void add(std::unique_ptr<IReaderMultiOutputConverter> converter)
272 {
273 if (!converter) {
274 throw std::invalid_argument("converter cannot be null");
275 }
276 m_readerMultiOutputConverters.emplace_back(std::move(converter));
277 }
278
280 [[nodiscard]] const IConverter* find(FormatId sourceFormat, FormatId targetFormat) const
281 {
282 for (const auto& converter : m_converters) {
283 if (converter->sourceFormat() == sourceFormat && converter->targetFormat() == targetFormat) {
284 return converter.get();
285 }
286 }
287 return nullptr;
288 }
289
291 [[nodiscard]] const IMultiOutputConverter* findMultiOutput(FormatId sourceFormat, FormatId targetFormat) const
292 {
293 for (const auto& converter : m_multiOutputConverters) {
294 if (converter->sourceFormat() == sourceFormat && converter->targetFormat() == targetFormat) {
295 return converter.get();
296 }
297 }
298 return nullptr;
299 }
300
302 [[nodiscard]] const IReaderConverter* findReader(FormatId sourceFormat, FormatId targetFormat) const
303 {
304 for (const auto& converter : m_readerConverters) {
305 if (converter->sourceFormat() == sourceFormat && converter->targetFormat() == targetFormat) {
306 return converter.get();
307 }
308 }
309 return nullptr;
310 }
311
313 [[nodiscard]] const IReaderMultiOutputConverter* findReaderMultiOutput(FormatId sourceFormat, FormatId targetFormat) const
314 {
315 for (const auto& converter : m_readerMultiOutputConverters) {
316 if (converter->sourceFormat() == sourceFormat && converter->targetFormat() == targetFormat) {
317 return converter.get();
318 }
319 }
320 return nullptr;
321 }
322
323private:
324 std::vector<std::unique_ptr<IConverter>> m_converters;
325 std::vector<std::unique_ptr<IMultiOutputConverter>> m_multiOutputConverters;
326 std::vector<std::unique_ptr<IReaderConverter>> m_readerConverters;
327 std::vector<std::unique_ptr<IReaderMultiOutputConverter>> m_readerMultiOutputConverters;
328};
329
330} // namespace denigma
Result metadata returned after a conversion completes.
Definition conversion.h:109
void addDiagnostic(MessageSeverity severity, std::string message)
Adds a diagnostic and updates the error state if needed.
Definition conversion.h:130
bool hasError() const noexcept
Returns true when at least one diagnostic has severity Error.
Definition conversion.h:118
void addDiagnostic(Diagnostic diagnostic)
Adds a diagnostic and updates the error state if needed.
Definition conversion.h:136
std::span< const Diagnostic > diagnostics() const noexcept
Returns the diagnostics collected during conversion.
Definition conversion.h:112
Lightweight registry for locating converters by source and target format.
Definition conversion.h:241
void add(std::unique_ptr< IReaderMultiOutputConverter > converter)
Adds a reader-backed multi-output converter to the registry.
Definition conversion.h:271
const IReaderConverter * findReader(FormatId sourceFormat, FormatId targetFormat) const
Returns the first registered reader-backed converter matching the requested formats,...
Definition conversion.h:302
const IReaderMultiOutputConverter * findReaderMultiOutput(FormatId sourceFormat, FormatId targetFormat) const
Returns the first registered reader-backed multi-output converter matching the requested formats,...
Definition conversion.h:313
const IMultiOutputConverter * findMultiOutput(FormatId sourceFormat, FormatId targetFormat) const
Returns the first registered multi-output converter matching the requested formats,...
Definition conversion.h:291
const IConverter * find(FormatId sourceFormat, FormatId targetFormat) const
Returns the first registered converter matching the requested formats, or nullptr.
Definition conversion.h:280
void add(std::unique_ptr< IMultiOutputConverter > converter)
Adds a multi-output converter to the registry.
Definition conversion.h:253
void add(std::unique_ptr< IConverter > converter)
Adds a converter to the registry.
Definition conversion.h:244
void add(std::unique_ptr< IReaderConverter > converter)
Adds a reader-backed converter to the registry.
Definition conversion.h:262
Public interface implemented by each conversion adapter.
Definition conversion.h:166
virtual ~IConverter()=default
virtual destructor
virtual FormatId sourceFormat() const =0
Returns the source format accepted by this converter.
virtual FormatId targetFormat() const =0
Returns the target format produced by this converter.
virtual ConversionResult convert(std::span< const std::byte > input, std::ostream &output, const ConversionRequest &request={}) const =0
Converts the input memory buffer and writes the converted output to the provided stream.
Public interface implemented by adapters that may produce multiple output documents.
Definition conversion.h:205
virtual FormatId targetFormat() const =0
Returns the target format produced by this converter.
virtual ConversionResult convert(std::span< const std::byte > input, const MultiOutputCallback &outputCallback, const ConversionRequest &request={}) const =0
Converts the input memory buffer and invokes outputCallback once for each generated output.
virtual FormatId sourceFormat() const =0
Returns the source format accepted by this converter.
virtual ~IMultiOutputConverter()=default
virtual destructor
Base class for adapter-specific option structs.
Definition conversion.h:93
virtual ~IOptions()=default
Virtual destructor.
Random-access byte reader used for container formats such as MUSX.
Definition random_access_reader.h:32
Public interface implemented by adapters whose input is a random-access container.
Definition conversion.h:184
virtual ConversionResult convert(const IRandomAccessReader &input, std::ostream &output, const ConversionRequest &request={}) const =0
Converts the input reader and writes the converted output to the provided stream.
virtual ~IReaderConverter()=default
virtual destructor
virtual FormatId targetFormat() const =0
Returns the target format produced by this converter.
virtual FormatId sourceFormat() const =0
Returns the source format accepted by this converter.
Public interface implemented by reader-backed adapters that may produce multiple output documents.
Definition conversion.h:223
virtual ConversionResult convert(const IRandomAccessReader &input, const MultiOutputCallback &outputCallback, const ConversionRequest &request={}) const =0
Converts the input reader and invokes outputCallback once for each generated output.
virtual ~IReaderMultiOutputConverter()=default
virtual destructor
virtual FormatId sourceFormat() const =0
Returns the source format accepted by this converter.
virtual FormatId targetFormat() const =0
Returns the target format produced by this converter.
Core public API for the Denigma conversion libraries.
Definition articulations.h:32
FormatId
Stable identifiers for converter input and output formats.
Definition conversion.h:45
@ MssXml
MuseScore style sheet XML.
@ EnigmaXml
Finale Enigma XML.
@ Svg
Scalable Vector Graphics XML.
@ MnxJson
MNX JSON as produced by mnxdom.
@ Musx
Finale MUSX archive.
@ MusicXml
MusicXML score-partwise XML.
MessageSeverity
Severity for log messages and diagnostics emitted by converters.
Definition conversion.h:57
OptionsT optionsFromRequest(const ConversionRequest &request, std::string_view converterName)
Returns typed options from an erased request, or default options when none were supplied.
Definition conversion.h:151
std::function< void(std::string_view suggestedName, std::span< const std::byte > data)> MultiOutputCallback
Callback used by converters that may emit zero, one, or many output buffers.
Definition conversion.h:200
Options common to all public converter adapters.
Definition conversion.h:75
std::function< void(MessageSeverity severity, std::string_view message)> logCallback
Optional callback that receives converter log messages. Defaults to no-op.
Definition conversion.h:87
bool validate
Enables converter-specific output validation when supported.
Definition conversion.h:79
bool allFontsAvailable
Every source font will be available in the environment that reads the converted output.
Definition conversion.h:85
std::string sourceName
Caller-supplied source name used for diagnostics and metadata.
Definition conversion.h:77
bool verbose
Enables verbose logging when supported by the caller.
Definition conversion.h:81
bool quiet
Suppresses info/verbose logging when true.
Definition conversion.h:83
Type-erased request used by registry-based converter calls.
Definition conversion.h:101
const IOptions * options
Adapter-specific options. Must remain valid for the duration of the conversion call.
Definition conversion.h:103
A non-fatal message emitted by a converter.
Definition conversion.h:67
std::string message
Human-readable diagnostic text.
Definition conversion.h:69
MessageSeverity severity
Message severity.
Definition conversion.h:68