#[cfg(feature = "abc")]
pub mod abc;
mod error;
#[cfg(feature = "mei")]
pub mod mei;
#[cfg(feature = "midi")]
pub mod midi;
#[cfg(feature = "mscz")]
pub mod mscz;
#[cfg(feature = "musicxml")]
pub mod musicxml;
mod report;
pub(crate) const MAX_INPUT_BYTES: usize = 64 * 1024 * 1024;
#[cfg(feature = "abc")]
pub(crate) const MAX_ABC_LINE_BYTES: usize = 1024 * 1024;
pub(crate) const MAX_MIDI_EVENTS: usize = 500_000;
pub use error::Error;
pub use report::{
Diagnostic, DiagnosticSeverity, ExportReport, ImportReport, REPORT_SCHEMA_VERSION,
};
#[cfg(test)]
mod security_tests {
use super::Error;
#[cfg(feature = "midi")]
#[test]
fn midi_rejects_input_over_baseline_limit() {
let data = vec![0_u8; super::MAX_INPUT_BYTES + 1];
assert!(matches!(
super::midi::parse_midi(&data),
Err(Error::TooLarge(size)) if size == super::MAX_INPUT_BYTES + 1
));
}
#[cfg(feature = "abc")]
#[test]
fn abc_rejects_pathological_line_length() {
let text = format!("X:1\n{}", "x".repeat(super::MAX_ABC_LINE_BYTES + 1));
assert!(matches!(
super::abc::parse_abc(&text),
Err(Error::Abc(message)) if message.contains("line exceeds")
));
}
#[cfg(feature = "abc")]
#[test]
fn abc_rejects_input_over_baseline_limit() {
let text = "x".repeat(super::MAX_INPUT_BYTES + 1);
assert!(matches!(
super::abc::parse_abc(&text),
Err(Error::TooLarge(size)) if size == super::MAX_INPUT_BYTES + 1
));
}
}
#[cfg(feature = "musicxml")]
pub use musicxml::{parse_musicxml, parse_mxl, serialize_musicxml};
#[cfg(feature = "musicxml")]
pub fn parse_musicxml_with_report(xml: &str) -> Result<ImportReport, Error> {
let score = parse_musicxml(xml)?;
Ok(ImportReport {
schema_version: REPORT_SCHEMA_VERSION,
format: "musicxml".to_string(),
score,
diagnostics: musicxml::loss_diagnostics(xml),
})
}
#[cfg(feature = "musicxml")]
pub fn parse_mxl_with_report(data: &[u8]) -> Result<ImportReport, Error> {
Ok(ImportReport::for_format(parse_mxl(data)?, "mxl"))
}
#[cfg(feature = "mei")]
pub use mei::{parse_mei, serialize_mei};
#[cfg(feature = "mei")]
pub fn parse_mei_with_report(text: &str) -> Result<ImportReport, Error> {
mei::parse_mei_with_report(text)
}
#[cfg(feature = "mei")]
pub fn serialize_mei_with_report(
score: &acorde_core::Score,
) -> Result<ExportReport<String>, Error> {
Ok(ExportReport {
schema_version: REPORT_SCHEMA_VERSION,
format: "mei".to_string(),
output: serialize_mei(score)?,
diagnostics: mei::export_loss_diagnostics(score),
})
}
#[cfg(feature = "musicxml")]
pub fn serialize_musicxml_with_report(
score: &acorde_core::Score,
) -> Result<ExportReport<String>, Error> {
Ok(ExportReport {
schema_version: REPORT_SCHEMA_VERSION,
format: "musicxml".to_string(),
output: serialize_musicxml(score)?,
diagnostics: musicxml::export_loss_diagnostics(score),
})
}
#[cfg(feature = "midi")]
pub use midi::{parse_midi, serialize_midi, serialize_midi_region};
#[cfg(feature = "midi")]
pub fn parse_midi_with_report(data: &[u8]) -> Result<ImportReport, Error> {
let score = parse_midi(data)?;
let diagnostics = midi::loss_diagnostics(data)?;
Ok(ImportReport {
schema_version: REPORT_SCHEMA_VERSION,
format: "midi".to_string(),
score,
diagnostics,
})
}
#[cfg(feature = "midi")]
pub fn serialize_midi_with_report(
score: &acorde_core::Score,
) -> Result<ExportReport<Vec<u8>>, Error> {
Ok(ExportReport {
schema_version: REPORT_SCHEMA_VERSION,
format: "midi".to_string(),
output: serialize_midi(score)?,
diagnostics: midi::export_loss_diagnostics(score),
})
}
#[cfg(feature = "abc")]
pub use abc::{parse_abc, serialize_abc};
#[cfg(feature = "abc")]
pub fn parse_abc_with_report(text: &str) -> Result<ImportReport, Error> {
let score = parse_abc(text)?;
Ok(ImportReport {
schema_version: REPORT_SCHEMA_VERSION,
format: "abc".to_string(),
score,
diagnostics: abc::loss_diagnostics(text),
})
}
#[cfg(feature = "abc")]
pub fn serialize_abc_with_report(
score: &acorde_core::Score,
) -> Result<ExportReport<String>, Error> {
Ok(ExportReport {
schema_version: REPORT_SCHEMA_VERSION,
format: "abc".to_string(),
output: serialize_abc(score)?,
diagnostics: abc::export_loss_diagnostics(score),
})
}
#[cfg(feature = "mscz")]
pub use mscz::{parse_mscx, parse_mscz};
#[cfg(feature = "mscz")]
pub fn parse_mscx_with_report(text: &str) -> Result<ImportReport, Error> {
let score = parse_mscx(text)?;
let mut diagnostics = mscz::loss_diagnostics(text);
diagnostics.extend(mscz::tab_position_diagnostics(&score));
Ok(ImportReport {
schema_version: REPORT_SCHEMA_VERSION,
format: "mscx".to_string(),
score,
diagnostics,
})
}
#[cfg(feature = "mscz")]
pub fn parse_mscz_with_report(data: &[u8]) -> Result<ImportReport, Error> {
let score = parse_mscz(data)?;
let mut diagnostics = mscz::loss_diagnostics_mscz(data)?;
diagnostics.extend(mscz::tab_position_diagnostics(&score));
Ok(ImportReport {
schema_version: REPORT_SCHEMA_VERSION,
format: "mscz".to_string(),
score,
diagnostics,
})
}