melodium-common 0.10.1

Common Mélodium elements and traits
Documentation
use crate::descriptor::{
    Context, Function, Identifier, IdentifierRequirement, Model, PackageRequirement, Status,
    Treatment,
};
use core::fmt::{Debug, Display, Formatter};
use downcast_rs::{impl_downcast, Downcast};
use std::{path::PathBuf, sync::Arc};

#[derive(Debug, Clone)]
pub enum LoadingErrorKind {
    NoPackage {
        package_requirement: PackageRequirement,
    },
    NoEntryPointProvided,
    UnreachableFile {
        path: PathBuf,
        error: String,
    },
    WrongConfiguration {
        package: String,
    },
    NotFound {
        element: String,
    },
    CircularReference {
        identifier: IdentifierRequirement,
    },
    RepositoryError {
        error: Arc<dyn RepositoryError>,
    },
    ContentError {
        error: Arc<dyn ContentError>,
    },
    ContextExpected {
        expecter: Option<Identifier>,
        identifier_requirement: IdentifierRequirement,
    },
    FunctionExpected {
        expecter: Option<Identifier>,
        identifier_requirement: IdentifierRequirement,
    },
    ModelExpected {
        expecter: Option<Identifier>,
        identifier_requirement: IdentifierRequirement,
    },
    TreatmentExpected {
        expecter: Option<Identifier>,
        identifier_requirement: IdentifierRequirement,
    },
    JeuFormatError {
        error: String,
    },
    MappingFormatError {
        error: String,
    },
    UncompatiblePlatform {
        platform: String,
    },
    LibraryLoadingError {
        path: PathBuf,
        error: String,
    },
}

impl Display for LoadingErrorKind {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        match self {
            LoadingErrorKind::NoPackage {
                package_requirement,
            } => write!(f, "No package '{package_requirement}' found"),
            LoadingErrorKind::NoEntryPointProvided => write!(f, "No entry point provided"),
            LoadingErrorKind::UnreachableFile { path, error } => write!(
                f,
                "File '{path}' cannot be read: {error}",
                path = path.to_string_lossy()
            ),
            LoadingErrorKind::WrongConfiguration { package } => {
                write!(f, "Package '{package}' have wrong configuration")
            }
            LoadingErrorKind::NotFound { element } => write!(f, "Element '{element}' not found"),
            LoadingErrorKind::CircularReference { identifier } => {
                write!(f, "Element '{identifier}' cause a circular reference")
            }
            LoadingErrorKind::RepositoryError { error } => write!(f, "{error}"),
            LoadingErrorKind::ContentError { error } => write!(f, "{error}"),
            LoadingErrorKind::ContextExpected {
                expecter,
                identifier_requirement,
            } => match expecter {
                Some(expecter) => write!(
                    f,
                    "'{expecter}' expected a context, but '{identifier_requirement}' is not"
                ),
                None => write!(f, "'{identifier_requirement}' is not a context"),
            },
            LoadingErrorKind::FunctionExpected {
                expecter,
                identifier_requirement,
            } => match expecter {
                Some(expecter) => write!(
                    f,
                    "'{expecter}' expected a function, but '{identifier_requirement}' is not"
                ),
                None => write!(f, "'{identifier_requirement}' is not a function"),
            },
            LoadingErrorKind::ModelExpected {
                expecter,
                identifier_requirement,
            } => match expecter {
                Some(expecter) => write!(
                    f,
                    "'{expecter}' expected a model, but '{identifier_requirement}' is not"
                ),
                None => write!(f, "'{identifier_requirement}' is not a model"),
            },
            LoadingErrorKind::TreatmentExpected {
                expecter,
                identifier_requirement,
            } => match expecter {
                Some(expecter) => write!(
                    f,
                    "'{expecter}' expected a treatment, but '{identifier_requirement}' is not"
                ),
                None => write!(f, "'{identifier_requirement}' is not a treatment"),
            },
            LoadingErrorKind::JeuFormatError { error } => {
                write!(f, "Jeu data cannot be processed: {error}")
            }
            LoadingErrorKind::MappingFormatError { error } => {
                write!(f, "Mapped package cannot be processed: {error}")
            }
            LoadingErrorKind::UncompatiblePlatform { platform } => {
                write!(f, "Platform '{platform}' is not compatible")
            }
            LoadingErrorKind::LibraryLoadingError { path, error } => write!(
                f,
                "Loading '{path}' failed: {error}",
                path = path.to_string_lossy()
            ),
        }
    }
}

#[derive(Debug, Clone)]
pub struct LoadingError {
    pub id: u32,
    pub kind: LoadingErrorKind,
}

impl LoadingError {
    pub fn no_package(id: u32, package_requirement: PackageRequirement) -> Self {
        Self {
            id,
            kind: LoadingErrorKind::NoPackage {
                package_requirement,
            },
        }
    }

    pub fn no_entry_point_provided(id: u32) -> Self {
        Self {
            id,
            kind: LoadingErrorKind::NoEntryPointProvided,
        }
    }

    pub fn unreachable_file(id: u32, path: PathBuf, error: String) -> Self {
        Self {
            id,
            kind: LoadingErrorKind::UnreachableFile { path, error },
        }
    }

    pub fn wrong_configuration(id: u32, package: String) -> Self {
        Self {
            id,
            kind: LoadingErrorKind::WrongConfiguration { package },
        }
    }

    pub fn not_found(id: u32, element: String) -> Self {
        Self {
            id,
            kind: LoadingErrorKind::NotFound { element },
        }
    }

    pub fn circular_reference(id: u32, identifier: IdentifierRequirement) -> Self {
        Self {
            id,
            kind: LoadingErrorKind::CircularReference { identifier },
        }
    }

    pub fn repository_error(id: u32, error: Arc<dyn RepositoryError>) -> Self {
        Self {
            id,
            kind: LoadingErrorKind::RepositoryError { error },
        }
    }

    pub fn content_error(id: u32, error: Arc<dyn ContentError>) -> Self {
        Self {
            id,
            kind: LoadingErrorKind::ContentError { error },
        }
    }

    pub fn context_expected(
        id: u32,
        expecter: Option<Identifier>,
        identifier_requirement: IdentifierRequirement,
    ) -> Self {
        Self {
            id,
            kind: LoadingErrorKind::ContextExpected {
                expecter,
                identifier_requirement,
            },
        }
    }

    pub fn function_expected(
        id: u32,
        expecter: Option<Identifier>,
        identifier_requirement: IdentifierRequirement,
    ) -> Self {
        Self {
            id,
            kind: LoadingErrorKind::FunctionExpected {
                expecter,
                identifier_requirement,
            },
        }
    }

    pub fn model_expected(
        id: u32,
        expecter: Option<Identifier>,
        identifier_requirement: IdentifierRequirement,
    ) -> Self {
        Self {
            id,
            kind: LoadingErrorKind::ModelExpected {
                expecter,
                identifier_requirement,
            },
        }
    }

    pub fn treatment_expected(
        id: u32,
        expecter: Option<Identifier>,
        identifier_requirement: IdentifierRequirement,
    ) -> Self {
        Self {
            id,
            kind: LoadingErrorKind::TreatmentExpected {
                expecter,
                identifier_requirement,
            },
        }
    }

    pub fn jeu_format_error(id: u32, error: String) -> Self {
        Self {
            id,
            kind: LoadingErrorKind::JeuFormatError { error },
        }
    }

    pub fn mapping_format_error(id: u32, error: String) -> Self {
        Self {
            id,
            kind: LoadingErrorKind::MappingFormatError { error },
        }
    }

    pub fn uncompatible_platform(id: u32, platform: String) -> Self {
        Self {
            id,
            kind: LoadingErrorKind::UncompatiblePlatform { platform },
        }
    }

    pub fn library_loading_error(id: u32, path: PathBuf, error: String) -> Self {
        Self {
            id,
            kind: LoadingErrorKind::LibraryLoadingError { path, error },
        }
    }
}

impl Display for LoadingError {
    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
        write!(f, "L{:04}: {}", self.id, self.kind)
    }
}

pub type LoadingErrors = Vec<LoadingError>;
pub type LoadingResult<T> = Status<T, LoadingError, LoadingError>;

pub trait Loader {
    fn load_context(
        &self,
        identifier_requirement: &IdentifierRequirement,
    ) -> LoadingResult<Arc<dyn Context>>;
    fn load_function(
        &self,
        identifier_requirement: &IdentifierRequirement,
    ) -> LoadingResult<Arc<dyn Function>>;
    fn load_model(
        &self,
        identifier_requirement: &IdentifierRequirement,
    ) -> LoadingResult<Arc<dyn Model>>;
    fn load_treatment(
        &self,
        identifier_requirement: &IdentifierRequirement,
    ) -> LoadingResult<Arc<dyn Treatment>>;
}

pub trait RepositoryError: Display + Debug + Downcast + Send + Sync {}
impl_downcast!(RepositoryError);

pub type RepositoryErrors = Vec<Arc<dyn RepositoryError>>;

pub trait ContentError: Display + Debug + Downcast + Send + Sync {}
impl_downcast!(ContentError);

pub type ContentErrors = Vec<Arc<dyn ContentError>>;