1#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
2pub enum AbcSpecVersion {
3 #[default]
4 V21,
5 V22Draft,
6}
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
9pub enum ParseMode {
10 #[default]
11 Strict,
12 Loose,
13 Recover,
14}
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
17pub struct DiagnosticOptions {
18 pub suppress_croma_carrier_warnings: bool,
19}
20
21impl DiagnosticOptions {
22 pub fn suppress_croma_carrier_warnings(mut self) -> Self {
23 self.suppress_croma_carrier_warnings = true;
24 self
25 }
26
27 pub(crate) fn should_emit_croma_carrier_warning(self, name: &str) -> bool {
28 !self.suppress_croma_carrier_warnings
29 || !(is_croma_carrier_name(name) || is_croma_managed_midi_name(name))
30 }
31}
32
33pub fn is_croma_carrier_name(name: &str) -> bool {
44 let name = name.trim_start();
45 name.get(.."croma-".len())
46 .is_some_and(|prefix| prefix.eq_ignore_ascii_case("croma-"))
47 || is_compact_croma_carrier_name(name)
48}
49
50fn is_compact_croma_carrier_name(name: &str) -> bool {
51 name.split_once(char::is_whitespace)
52 .is_some_and(|(head, code)| {
53 head.eq_ignore_ascii_case(crate::lower::carrier::COMPACT_NAMESPACE)
54 && !code.trim().is_empty()
55 })
56}
57
58fn is_croma_managed_midi_name(name: &str) -> bool {
59 name.trim_start().eq_ignore_ascii_case("MIDI")
60}
61
62#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
63pub struct ParseOptions {
64 pub spec: AbcSpecVersion,
65 pub mode: ParseMode,
66 pub diagnostics: DiagnosticOptions,
67}
68
69impl ParseOptions {
70 pub fn new(spec: AbcSpecVersion, mode: ParseMode) -> Self {
71 Self {
72 spec,
73 mode,
74 diagnostics: DiagnosticOptions::default(),
75 }
76 }
77
78 pub fn suppress_croma_carrier_warnings(mut self) -> Self {
79 self.diagnostics = self.diagnostics.suppress_croma_carrier_warnings();
80 self
81 }
82}
83
84#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
85pub struct ExportOptions {
86 pub spec: AbcSpecVersion,
87 pub parse_mode: ParseMode,
88 pub diagnostics: DiagnosticOptions,
89 pub write: MusicXmlWriteOptions,
92}
93
94impl ExportOptions {
95 pub fn parse_options(self) -> ParseOptions {
96 ParseOptions {
97 spec: self.spec,
98 mode: self.parse_mode,
99 diagnostics: self.diagnostics,
100 }
101 }
102
103 pub fn suppress_croma_carrier_warnings(mut self) -> Self {
104 self.diagnostics = self.diagnostics.suppress_croma_carrier_warnings();
105 self
106 }
107
108 pub fn engrave(mut self) -> Self {
110 self.write = MusicXmlWriteOptions::engrave();
111 self
112 }
113}
114
115#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
123pub struct MusicXmlWriteOptions {
124 pub beams: bool,
126 pub tuplet_display: TupletDisplay,
128 pub stems: bool,
130 pub rest_placement: bool,
132 pub slur_placement: bool,
134 pub tie_orientation: bool,
136}
137
138impl MusicXmlWriteOptions {
139 pub fn engrave() -> Self {
141 Self {
142 beams: true,
143 tuplet_display: TupletDisplay::EngravingDefault,
144 stems: true,
145 rest_placement: true,
146 slur_placement: true,
147 tie_orientation: true,
148 }
149 }
150
151 pub fn any_enabled(self) -> bool {
154 self.beams
155 || self.stems
156 || self.rest_placement
157 || self.slur_placement
158 || self.tie_orientation
159 || self.tuplet_display != TupletDisplay::AsEncoded
160 }
161}
162
163#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
166pub enum TupletDisplay {
167 #[default]
169 AsEncoded,
170 EngravingDefault,
173}
174
175impl From<ExportOptions> for ParseOptions {
176 fn from(options: ExportOptions) -> Self {
177 options.parse_options()
178 }
179}
180
181#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
182pub struct LowerOptions;
183
184#[cfg(test)]
185#[path = "options_tests.rs"]
186mod tests;