use sim_kernel::{Diagnostic, Severity, SourceId, Span, Symbol};
use sim_lib_music_core::{MusicError, Score};
use thiserror::Error;
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct MusicXmlLimits {
pub bytes: usize,
pub nodes: usize,
pub depth: usize,
pub text: usize,
pub parts: usize,
pub events: usize,
}
impl Default for MusicXmlLimits {
fn default() -> Self {
Self {
bytes: 4_000_000,
nodes: 200_000,
depth: 64,
text: 1_000_000,
parts: 256,
events: 1_000_000,
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum NotationIdentityKind {
Part,
Event,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct NotationIdentity {
pub kind: NotationIdentityKind,
pub canonical_path: String,
pub xml_id: String,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum NotationLossKind {
Clef,
PartName,
PitchSpelling,
DefaultedTempo,
DefaultedTimeSignature,
Velocity,
Channel,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct NotationLoss {
pub kind: NotationLossKind,
pub canonical_path: Option<String>,
pub detail: String,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct NotationReport<T> {
pub value: T,
pub diagnostics: Vec<Diagnostic>,
pub identities: Vec<NotationIdentity>,
pub losses: Vec<NotationLoss>,
}
#[derive(Debug, Error, Clone, PartialEq, Eq)]
pub enum NotationError {
#[error("unsupported duration {0}")]
UnsupportedDuration(String),
#[error("unsupported music object {0}")]
UnsupportedMusicObject(&'static str),
#[error("invalid key signature {0}")]
InvalidKey(String),
#[error("unsupported lilypond syntax")]
UnsupportedSyntax {
diagnostics: Vec<Diagnostic>,
},
#[error("unsupported musicxml-partwise profile input")]
UnsupportedMusicXml {
diagnostics: Vec<Diagnostic>,
},
#[error("musicxml {limit} limit exceeded: {actual} > {maximum}")]
MusicXmlLimit {
limit: &'static str,
actual: usize,
maximum: usize,
},
#[error("musicxml source is not valid UTF-8")]
InvalidMusicXmlUtf8,
#[error("invalid musicxml: {0}")]
InvalidMusicXml(String),
#[error(transparent)]
Music(#[from] MusicError),
}
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
pub struct NotationCodec;
impl NotationCodec {
pub fn export_lilypond_report(
&self,
score: &Score,
) -> Result<NotationReport<String>, NotationError> {
crate::export::export_lilypond_report(score)
}
pub fn export_lilypond(&self, score: &Score) -> Result<String, NotationError> {
crate::export::export_lilypond(score)
}
pub fn import_lilypond_report(
&self,
source: &str,
) -> Result<NotationReport<Score>, NotationError> {
crate::import::import_lilypond_report(source)
}
pub fn import_lilypond(&self, source: &str) -> Result<Score, NotationError> {
crate::import::import_lilypond(source)
}
pub fn import_musicxml_partwise_report(
&self,
source: &[u8],
limits: MusicXmlLimits,
) -> Result<NotationReport<Score>, NotationError> {
crate::musicxml::import_musicxml_partwise_report(source, limits)
}
pub fn export_musicxml_partwise_report(
&self,
score: &Score,
identities: &[NotationIdentity],
) -> Result<NotationReport<String>, NotationError> {
crate::musicxml::export_musicxml_partwise_report(score, identities)
}
}
pub(crate) fn error_at(message: impl Into<String>, span: Span) -> NotationError {
NotationError::UnsupportedSyntax {
diagnostics: vec![Diagnostic {
severity: Severity::Error,
message: message.into(),
source: Some(SourceId("notation:lilypond".to_owned())),
span: Some(span),
code: None,
related: Vec::new(),
}],
}
}
pub(crate) fn musicxml_error(message: impl Into<String>, span: Option<Span>) -> NotationError {
NotationError::UnsupportedMusicXml {
diagnostics: vec![Diagnostic {
severity: Severity::Error,
message: message.into(),
source: Some(SourceId("notation:musicxml-partwise".to_owned())),
span,
code: Some(Symbol::qualified("musicxml", "profile")),
related: Vec::new(),
}],
}
}
pub(crate) fn loss_diagnostic(loss: &NotationLoss) -> Diagnostic {
Diagnostic {
severity: Severity::Warning,
message: loss.detail.clone(),
source: Some(SourceId("notation:musicxml-partwise".to_owned())),
span: None,
code: Some(Symbol::qualified("musicxml", "loss")),
related: Vec::new(),
}
}