use super::{
Accidental, Beam, Chord, Cue, Dot, Duration, Footnote, Grace, Instrument, Level, Listen, Lyric, Notations, Notehead,
NoteheadText, Pitch, Play, Rest, Staff, Stem, Tie, TimeModification, Type, Unpitched, Voice,
};
use crate::datatypes::{
Color, Divisions, FontFamily, FontSize, FontStyle, FontWeight, Id, NonNegativeDecimal, Tenths, TimeOnly, YesNo,
};
use alloc::{string::String, vec::Vec};
use musicxml_internal::*;
use musicxml_macros::*;
#[derive(Debug, Default, PartialEq, Eq, AttributeDeserialize, AttributeSerialize)]
pub struct NoteAttributes {
pub attack: Option<Divisions>,
pub color: Option<Color>,
pub default_x: Option<Tenths>,
pub default_y: Option<Tenths>,
pub dynamics: Option<NonNegativeDecimal>,
pub end_dynamics: Option<NonNegativeDecimal>,
pub font_family: Option<FontFamily>,
pub font_size: Option<FontSize>,
pub font_style: Option<FontStyle>,
pub font_weight: Option<FontWeight>,
pub id: Option<Id>,
pub pizzicato: Option<YesNo>,
pub print_dot: Option<YesNo>,
pub print_leger: Option<YesNo>,
pub print_lyric: Option<YesNo>,
pub print_object: Option<YesNo>,
pub print_spacing: Option<YesNo>,
pub relative_x: Option<Tenths>,
pub relative_y: Option<Tenths>,
pub release: Option<Divisions>,
pub time_only: Option<TimeOnly>,
}
#[derive(Debug, PartialEq, Eq)]
pub enum AudibleType {
Pitch(Pitch),
Unpitched(Unpitched),
Rest(Rest),
}
#[derive(Debug, PartialEq, Eq)]
pub struct GraceNormalInfo {
pub chord: Option<Chord>,
pub audible: AudibleType,
pub tie: Vec<Tie>,
}
impl ContentDeserializer for GraceNormalInfo {
fn deserialize(elements: &[XmlElement]) -> Result<Self, String> {
let mut chord: Option<Chord> = None;
let mut audible: Option<AudibleType> = None;
let mut tie: Vec<Tie> = Vec::new();
for element in elements {
match element.name.as_str() {
"chord" => chord = Some(Chord::deserialize(element)?),
"pitch" => audible = Some(AudibleType::Pitch(Pitch::deserialize(element)?)),
"unpitched" => audible = Some(AudibleType::Unpitched(Unpitched::deserialize(element)?)),
"rest" => audible = Some(AudibleType::Rest(Rest::deserialize(element)?)),
"tie" => tie.push(Tie::deserialize(element)?),
_ => {}
}
}
Ok(GraceNormalInfo {
chord,
audible: if let Some(audible) = audible {
audible
} else {
Err("Missing <pitch>, <unpitched>, or <rest> element")?
},
tie,
})
}
}
impl ContentSerializer for GraceNormalInfo {
fn serialize(element: &Self) -> Vec<XmlElement> {
let mut elements: Vec<XmlElement> = Vec::new();
if let Some(content) = &element.chord {
elements.push(Chord::serialize(content));
}
match &element.audible {
AudibleType::Pitch(content) => elements.push(Pitch::serialize(content)),
AudibleType::Unpitched(content) => elements.push(Unpitched::serialize(content)),
AudibleType::Rest(content) => elements.push(Rest::serialize(content)),
}
for el in &element.tie {
elements.push(Tie::serialize(el));
}
elements
}
}
#[derive(Debug, PartialEq, Eq)]
pub struct GraceCueInfo {
pub cue: Cue,
pub chord: Option<Chord>,
pub audible: AudibleType,
}
impl ContentDeserializer for GraceCueInfo {
fn deserialize(elements: &[XmlElement]) -> Result<Self, String> {
let mut cue: Option<Cue> = None;
let mut chord: Option<Chord> = None;
let mut audible: Option<AudibleType> = None;
for element in elements {
match element.name.as_str() {
"cue" => cue = Some(Cue::deserialize(element)?),
"chord" => chord = Some(Chord::deserialize(element)?),
"pitch" => audible = Some(AudibleType::Pitch(Pitch::deserialize(element)?)),
"unpitched" => audible = Some(AudibleType::Unpitched(Unpitched::deserialize(element)?)),
"rest" => audible = Some(AudibleType::Rest(Rest::deserialize(element)?)),
_ => {}
}
}
Ok(GraceCueInfo {
cue: if let Some(cue) = cue {
cue
} else {
Err("Missing <cue> element")?
},
chord,
audible: if let Some(audible) = audible {
audible
} else {
Err("Missing <pitch>, <unpitched>, or <rest> element")?
},
})
}
}
impl ContentSerializer for GraceCueInfo {
fn serialize(element: &Self) -> Vec<XmlElement> {
let mut elements: Vec<XmlElement> = Vec::new();
elements.push(Cue::serialize(&element.cue));
if let Some(content) = &element.chord {
elements.push(Chord::serialize(content));
}
match &element.audible {
AudibleType::Pitch(content) => elements.push(Pitch::serialize(content)),
AudibleType::Unpitched(content) => elements.push(Unpitched::serialize(content)),
AudibleType::Rest(content) => elements.push(Rest::serialize(content)),
}
elements
}
}
#[derive(Debug, PartialEq, Eq)]
pub enum GraceType {
Cue(GraceCueInfo),
Normal(GraceNormalInfo),
}
#[derive(Debug, PartialEq, Eq)]
pub struct GraceInfo {
pub grace: Grace,
pub info: GraceType,
}
impl ContentDeserializer for GraceInfo {
fn deserialize(elements: &[XmlElement]) -> Result<Self, String> {
Ok(GraceInfo {
grace: Grace::deserialize(elements.first().ok_or("Missing <grace> sub-element")?)?,
info: if elements.iter().any(|el| el.name == "cue") {
GraceType::Cue(GraceCueInfo::deserialize(elements)?)
} else {
GraceType::Normal(GraceNormalInfo::deserialize(elements)?)
},
})
}
}
impl ContentSerializer for GraceInfo {
fn serialize(element: &Self) -> Vec<XmlElement> {
let mut elements: Vec<XmlElement> = Vec::new();
elements.push(Grace::serialize(&element.grace));
match &element.info {
GraceType::Cue(content) => elements.extend(GraceCueInfo::serialize(content)),
GraceType::Normal(content) => elements.extend(GraceNormalInfo::serialize(content)),
}
elements
}
}
#[derive(Debug, PartialEq, Eq)]
pub struct CueInfo {
pub cue: Cue,
pub chord: Option<Chord>,
pub audible: AudibleType,
pub duration: Duration,
}
impl ContentDeserializer for CueInfo {
fn deserialize(elements: &[XmlElement]) -> Result<Self, String> {
let mut cue: Option<Cue> = None;
let mut chord: Option<Chord> = None;
let mut audible: Option<AudibleType> = None;
let mut duration: Option<Duration> = None;
for element in elements {
match element.name.as_str() {
"cue" => cue = Some(Cue::deserialize(element)?),
"chord" => chord = Some(Chord::deserialize(element)?),
"pitch" => audible = Some(AudibleType::Pitch(Pitch::deserialize(element)?)),
"unpitched" => audible = Some(AudibleType::Unpitched(Unpitched::deserialize(element)?)),
"rest" => audible = Some(AudibleType::Rest(Rest::deserialize(element)?)),
"duration" => duration = Some(Duration::deserialize(element)?),
_ => {}
}
}
Ok(CueInfo {
cue: if let Some(cue) = cue {
cue
} else {
Err("Missing <cue> element")?
},
chord,
audible: if let Some(audible) = audible {
audible
} else {
Err("Missing <pitch>, <unpitched>, or <rest> element")?
},
duration: if let Some(duration) = duration {
duration
} else {
Err("Missing <duration> element")?
},
})
}
}
impl ContentSerializer for CueInfo {
fn serialize(element: &Self) -> Vec<XmlElement> {
let mut elements: Vec<XmlElement> = Vec::new();
elements.push(Cue::serialize(&element.cue));
if let Some(content) = &element.chord {
elements.push(Chord::serialize(content));
}
match &element.audible {
AudibleType::Pitch(content) => elements.push(Pitch::serialize(content)),
AudibleType::Unpitched(content) => elements.push(Unpitched::serialize(content)),
AudibleType::Rest(content) => elements.push(Rest::serialize(content)),
}
elements.push(Duration::serialize(&element.duration));
elements
}
}
#[derive(Debug, PartialEq, Eq)]
pub struct NormalInfo {
pub chord: Option<Chord>,
pub audible: AudibleType,
pub duration: Duration,
pub tie: Vec<Tie>,
}
impl ContentDeserializer for NormalInfo {
fn deserialize(elements: &[XmlElement]) -> Result<Self, String> {
let mut chord: Option<Chord> = None;
let mut audible: Option<AudibleType> = None;
let mut duration: Option<Duration> = None;
let mut tie: Vec<Tie> = Vec::new();
for element in elements {
match element.name.as_str() {
"chord" => chord = Some(Chord::deserialize(element)?),
"pitch" => audible = Some(AudibleType::Pitch(Pitch::deserialize(element)?)),
"unpitched" => audible = Some(AudibleType::Unpitched(Unpitched::deserialize(element)?)),
"rest" => audible = Some(AudibleType::Rest(Rest::deserialize(element)?)),
"duration" => duration = Some(Duration::deserialize(element)?),
"tie" => tie.push(Tie::deserialize(element)?),
_ => {}
}
}
Ok(NormalInfo {
chord,
audible: if let Some(audible) = audible {
audible
} else {
Err("Missing <pitch>, <unpitched>, or <rest> element")?
},
duration: if let Some(duration) = duration {
duration
} else {
Err("Missing <duration> element")?
},
tie,
})
}
}
impl ContentSerializer for NormalInfo {
fn serialize(element: &Self) -> Vec<XmlElement> {
let mut elements: Vec<XmlElement> = Vec::new();
if let Some(content) = &element.chord {
elements.push(Chord::serialize(content));
}
match &element.audible {
AudibleType::Pitch(content) => elements.push(Pitch::serialize(content)),
AudibleType::Unpitched(content) => elements.push(Unpitched::serialize(content)),
AudibleType::Rest(content) => elements.push(Rest::serialize(content)),
}
elements.push(Duration::serialize(&element.duration));
for el in &element.tie {
elements.push(Tie::serialize(el));
}
elements
}
}
#[derive(Debug, PartialEq, Eq)]
pub enum NoteType {
Grace(GraceInfo),
Cue(CueInfo),
Normal(NormalInfo),
}
#[derive(Debug, PartialEq, Eq)]
pub struct NoteContents {
pub info: NoteType,
pub instrument: Vec<Instrument>,
pub footnote: Option<Footnote>,
pub level: Option<Level>,
pub voice: Option<Voice>,
pub r#type: Option<Type>,
pub dot: Vec<Dot>,
pub accidental: Option<Accidental>,
pub time_modification: Option<TimeModification>,
pub stem: Option<Stem>,
pub notehead: Option<Notehead>,
pub notehead_text: Option<NoteheadText>,
pub staff: Option<Staff>,
pub beam: Vec<Beam>,
pub notations: Vec<Notations>,
pub lyric: Vec<Lyric>,
pub play: Option<Play>,
pub listen: Option<Listen>,
}
impl ContentDeserializer for NoteContents {
fn deserialize(elements: &[XmlElement]) -> Result<Self, String> {
let mut note_contents = NoteContents {
info: if elements.first().ok_or("Missing <note> sub-element")?.name == "grace" {
NoteType::Grace(GraceInfo::deserialize(elements)?)
} else if elements.first().ok_or("Missing <note> sub-element")?.name == "cue" {
NoteType::Cue(CueInfo::deserialize(elements)?)
} else {
NoteType::Normal(NormalInfo::deserialize(elements)?)
},
instrument: vec![],
footnote: None,
level: None,
voice: None,
r#type: None,
dot: vec![],
accidental: None,
time_modification: None,
stem: None,
notehead: None,
notehead_text: None,
staff: None,
beam: vec![],
notations: vec![],
lyric: vec![],
play: None,
listen: None,
};
for element in elements {
match element.name.as_str() {
"instrument" => note_contents.instrument.push(Instrument::deserialize(element)?),
"footnote" => note_contents.footnote = Some(Footnote::deserialize(element)?),
"level" => note_contents.level = Some(Level::deserialize(element)?),
"voice" => note_contents.voice = Some(Voice::deserialize(element)?),
"type" => note_contents.r#type = Some(Type::deserialize(element)?),
"dot" => note_contents.dot.push(Dot::deserialize(element)?),
"accidental" => note_contents.accidental = Some(Accidental::deserialize(element)?),
"time-modification" => note_contents.time_modification = Some(TimeModification::deserialize(element)?),
"stem" => note_contents.stem = Some(Stem::deserialize(element)?),
"notehead" => note_contents.notehead = Some(Notehead::deserialize(element)?),
"notehead-text" => note_contents.notehead_text = Some(NoteheadText::deserialize(element)?),
"staff" => note_contents.staff = Some(Staff::deserialize(element)?),
"beam" => note_contents.beam.push(Beam::deserialize(element)?),
"notations" => note_contents.notations.push(Notations::deserialize(element)?),
"lyric" => note_contents.lyric.push(Lyric::deserialize(element)?),
"play" => note_contents.play = Some(Play::deserialize(element)?),
"listen" => note_contents.listen = Some(Listen::deserialize(element)?),
_ => {}
}
}
Ok(note_contents)
}
}
impl ContentSerializer for NoteContents {
fn serialize(element: &Self) -> Vec<XmlElement> {
let mut elements: Vec<XmlElement> = Vec::new();
match &element.info {
NoteType::Grace(content) => elements.extend(GraceInfo::serialize(content)),
NoteType::Cue(content) => elements.extend(CueInfo::serialize(content)),
NoteType::Normal(content) => elements.extend(NormalInfo::serialize(content)),
}
for el in &element.instrument {
elements.push(Instrument::serialize(el));
}
if let Some(content) = &element.footnote {
elements.push(Footnote::serialize(content));
}
if let Some(content) = &element.level {
elements.push(Level::serialize(content));
}
if let Some(content) = &element.voice {
elements.push(Voice::serialize(content));
}
if let Some(content) = &element.r#type {
elements.push(Type::serialize(content));
}
for content in &element.dot {
elements.push(Dot::serialize(content));
}
if let Some(content) = &element.accidental {
elements.push(Accidental::serialize(content));
}
if let Some(content) = &element.time_modification {
elements.push(TimeModification::serialize(content));
}
if let Some(content) = &element.stem {
elements.push(Stem::serialize(content));
}
if let Some(content) = &element.notehead {
elements.push(Notehead::serialize(content));
}
if let Some(content) = &element.notehead_text {
elements.push(NoteheadText::serialize(content));
}
if let Some(content) = &element.staff {
elements.push(Staff::serialize(content));
}
for content in &element.beam {
elements.push(Beam::serialize(content));
}
for content in &element.notations {
elements.push(Notations::serialize(content));
}
for content in &element.lyric {
elements.push(Lyric::serialize(content));
}
if let Some(content) = &element.play {
elements.push(Play::serialize(content));
}
if let Some(content) = &element.listen {
elements.push(Listen::serialize(content));
}
elements
}
}
#[derive(Debug, PartialEq, Eq, ElementDeserialize, ElementSerialize)]
pub struct Note {
pub attributes: NoteAttributes,
#[flatten]
pub content: NoteContents,
}
#[cfg(test)]
mod note_tests {
use super::*;
use crate::datatypes::StartStop;
use crate::elements::*;
use crate::parser::parse_from_xml_str;
use alloc::string::ToString;
#[test]
fn deserialize_valid1() {
let result = parse_from_xml_str::<Note>(
"<note attack=\"2\">
<grace/>
<chord/>
<pitch>
<step>C</step>
<octave>4</octave>
</pitch>
<tie type=\"start\"/>
<tie type=\"stop\"/>
</note>",
);
assert!(result.is_ok());
assert_eq!(
result.unwrap(),
Note {
attributes: NoteAttributes {
attack: Some(Divisions(2)),
..Default::default()
},
content: NoteContents {
info: NoteType::Grace(GraceInfo {
grace: Grace {
attributes: GraceAttributes::default(),
content: ()
},
info: GraceType::Normal(GraceNormalInfo {
chord: Some(Chord {
attributes: (),
content: ()
}),
audible: AudibleType::Pitch(Pitch {
attributes: (),
content: PitchContents {
step: Step {
attributes: (),
content: crate::datatypes::Step::C
},
alter: None,
octave: Octave {
attributes: (),
content: crate::datatypes::Octave(4)
},
}
}),
tie: vec![
Tie {
attributes: TieAttributes {
r#type: StartStop::Start,
time_only: None
},
content: ()
},
Tie {
attributes: TieAttributes {
r#type: StartStop::Stop,
time_only: None
},
content: ()
},
],
}),
}),
instrument: vec![],
footnote: None,
level: None,
voice: None,
r#type: None,
dot: vec![],
accidental: None,
time_modification: None,
stem: None,
notehead: None,
notehead_text: None,
staff: None,
beam: vec![],
notations: vec![],
lyric: vec![],
play: None,
listen: None,
},
}
);
}
#[test]
fn deserialize_valid2() {
let result = parse_from_xml_str::<Note>(
"<note>
<grace/>
<cue/>
<unpitched>
<display-step>C</display-step>
<display-octave>4</display-octave>
</unpitched>
</note>",
);
assert!(result.is_ok());
assert_eq!(
result.unwrap(),
Note {
attributes: NoteAttributes::default(),
content: NoteContents {
info: NoteType::Grace(GraceInfo {
grace: Grace {
attributes: GraceAttributes::default(),
content: ()
},
info: GraceType::Cue(GraceCueInfo {
cue: Cue {
attributes: (),
content: ()
},
chord: None,
audible: AudibleType::Unpitched(Unpitched {
attributes: (),
content: UnpitchedContents {
display_step: DisplayStep {
attributes: (),
content: crate::datatypes::Step::C
},
display_octave: DisplayOctave {
attributes: (),
content: crate::datatypes::Octave(4)
},
}
}),
}),
}),
instrument: vec![],
footnote: None,
level: None,
voice: None,
r#type: None,
dot: vec![],
accidental: None,
time_modification: None,
stem: None,
notehead: None,
notehead_text: None,
staff: None,
beam: vec![],
notations: vec![],
lyric: vec![],
play: None,
listen: None,
},
}
);
}
#[test]
fn deserialize_valid3() {
let result = parse_from_xml_str::<Note>(
"<note>
<cue/>
<unpitched>
<display-step>C</display-step>
<display-octave>4</display-octave>
</unpitched>
<duration>4</duration>
</note>",
);
assert!(result.is_ok());
assert_eq!(
result.unwrap(),
Note {
attributes: NoteAttributes::default(),
content: NoteContents {
info: NoteType::Cue(CueInfo {
cue: Cue {
attributes: (),
content: ()
},
chord: None,
audible: AudibleType::Unpitched(Unpitched {
attributes: (),
content: UnpitchedContents {
display_step: DisplayStep {
attributes: (),
content: crate::datatypes::Step::C
},
display_octave: DisplayOctave {
attributes: (),
content: crate::datatypes::Octave(4)
},
}
}),
duration: Duration {
attributes: (),
content: crate::datatypes::PositiveDivisions(4)
}
}),
instrument: vec![],
footnote: None,
level: None,
voice: None,
r#type: None,
dot: vec![],
accidental: None,
time_modification: None,
stem: None,
notehead: None,
notehead_text: None,
staff: None,
beam: vec![],
notations: vec![],
lyric: vec![],
play: None,
listen: None,
},
}
);
}
#[test]
fn deserialize_valid4() {
let result = parse_from_xml_str::<Note>(
"<note>
<rest>
<display-step>C</display-step>
<display-octave>4</display-octave>
</rest>
<duration>4</duration>
<tie type=\"start\"/>
</note>",
);
assert!(result.is_ok());
assert_eq!(
result.unwrap(),
Note {
attributes: NoteAttributes::default(),
content: NoteContents {
info: NoteType::Normal(NormalInfo {
chord: None,
audible: AudibleType::Rest(Rest {
attributes: RestAttributes::default(),
content: RestContents {
display_step: Some(DisplayStep {
attributes: (),
content: crate::datatypes::Step::C
}),
display_octave: Some(DisplayOctave {
attributes: (),
content: crate::datatypes::Octave(4)
}),
}
}),
duration: Duration {
attributes: (),
content: crate::datatypes::PositiveDivisions(4)
},
tie: vec![Tie {
attributes: TieAttributes {
r#type: StartStop::Start,
time_only: None
},
content: ()
}],
}),
instrument: vec![],
footnote: None,
level: None,
voice: None,
r#type: None,
dot: vec![],
accidental: None,
time_modification: None,
stem: None,
notehead: None,
notehead_text: None,
staff: None,
beam: vec![],
notations: vec![],
lyric: vec![],
play: None,
listen: None,
},
}
);
}
#[test]
fn deserialize_valid5() {
let result = parse_from_xml_str::<Note>(
"<note>
<grace/>
<chord/>
<pitch>
<step>C</step>
<octave>4</octave>
</pitch>
<tie type=\"start\"/>
<tie type=\"stop\"/>
<instrument id=\"I1\"/>
<instrument id=\"I2\"/>
<notehead>fa up</notehead>
</note>",
);
assert!(result.is_ok());
assert_eq!(
result.unwrap(),
Note {
attributes: NoteAttributes::default(),
content: NoteContents {
info: NoteType::Grace(GraceInfo {
grace: Grace {
attributes: GraceAttributes::default(),
content: ()
},
info: GraceType::Normal(GraceNormalInfo {
chord: Some(Chord {
attributes: (),
content: ()
}),
audible: AudibleType::Pitch(Pitch {
attributes: (),
content: PitchContents {
step: Step {
attributes: (),
content: crate::datatypes::Step::C
},
alter: None,
octave: Octave {
attributes: (),
content: crate::datatypes::Octave(4)
},
}
}),
tie: vec![
Tie {
attributes: TieAttributes {
r#type: StartStop::Start,
time_only: None
},
content: ()
},
Tie {
attributes: TieAttributes {
r#type: StartStop::Stop,
time_only: None
},
content: ()
},
],
}),
}),
instrument: vec![
Instrument {
attributes: InstrumentAttributes {
id: crate::datatypes::IdRef("I1".to_string())
},
content: ()
},
Instrument {
attributes: InstrumentAttributes {
id: crate::datatypes::IdRef("I2".to_string())
},
content: ()
},
],
footnote: None,
level: None,
voice: None,
r#type: None,
dot: vec![],
accidental: None,
time_modification: None,
stem: None,
notehead: Some(Notehead {
attributes: NoteheadAttributes::default(),
content: crate::datatypes::NoteheadValue::FaUp
}),
notehead_text: None,
staff: None,
beam: vec![],
notations: vec![],
lyric: vec![],
play: None,
listen: None,
},
}
);
}
}