melodium-loader 0.10.1

Loading engine for Mélodium, with dependency resolution and retrieval
Documentation
use crate::package::package::PackageTrait;
use crate::Loader;
use crate::{content::Content, PackageInfo};
use core::iter::FromIterator;
use melodium_common::descriptor::{
    Collection, Identifier, IdentifierRequirement, LoadingError, LoadingResult, PackageRequirement,
    VersionReq,
};
use semver::Version;
use std::{collections::HashMap, sync::Arc};

#[derive(Debug)]
pub struct RawPackage {
    name: String,
    version: Version,
    requirements: Vec<PackageRequirement>,
    entrypoints: HashMap<String, Identifier>,
    content: Content,
}

impl RawPackage {
    pub fn new(data: &str) -> LoadingResult<Self> {
        let mut name = None;
        let mut version = None;
        let mut requirements = Vec::new();
        for line in data.lines() {
            if line.starts_with("#!/") {
                continue;
            } else if let Some(internal_name) = line.strip_prefix("#! name = ") {
                if name.is_none() {
                    name = Some(internal_name.to_string());
                } else {
                    return LoadingResult::new_failure(LoadingError::no_package(
                        186,
                        name.map(|name| PackageRequirement::new(&name, &VersionReq::STAR))
                            .unwrap_or_else(|| {
                                PackageRequirement::new("[raw package]", &VersionReq::STAR)
                            }),
                    ));
                }
            } else if let Some(internal_version) = line.strip_prefix("#! version = ") {
                if version.is_none() {
                    version = Some(match Version::parse(internal_version) {
                        Ok(val) => val,
                        Err(_) => {
                            return LoadingResult::new_failure(LoadingError::no_package(
                                188,
                                name.map(|name| PackageRequirement::new(&name, &VersionReq::STAR))
                                    .unwrap_or_else(|| {
                                        PackageRequirement::new("[raw package]", &VersionReq::STAR)
                                    }),
                            ))
                        }
                    });
                } else {
                    return LoadingResult::new_failure(LoadingError::no_package(
                        187,
                        name.map(|name| PackageRequirement::new(&name, &VersionReq::STAR))
                            .unwrap_or_else(|| {
                                PackageRequirement::new("[raw package]", &VersionReq::STAR)
                            }),
                    ));
                }
            } else if let Some(internal_requirements) = line.strip_prefix("#! require = ") {
                for internal_requirement in internal_requirements.split(char::is_whitespace) {
                    let parts = internal_requirement.split(':').collect::<Vec<_>>();
                    if parts.len() == 2 {
                        if let Ok(version_requirement) = VersionReq::parse(parts[1]) {
                            requirements.push(PackageRequirement {
                                package: parts[0].to_string(),
                                version_requirement,
                            })
                        } else {
                            return LoadingResult::new_failure(LoadingError::wrong_configuration(
                                209,
                                "[raw package]".to_string(),
                            ));
                        }
                    } else {
                        return LoadingResult::new_failure(LoadingError::wrong_configuration(
                            208,
                            "[raw package]".to_string(),
                        ));
                    }
                }
            } else if line.starts_with("#!") {
                continue;
            } else {
                break;
            }
        }

        if version.is_none() {
            version = Some(Version::new(0, 1, 0));
        }

        if let (Some(name), Some(version)) = (&name, version) {
            Content::new(
                &name,
                data.as_bytes(),
                &version,
                &requirements
                    .iter()
                    .map(|pkg_req| (pkg_req.package.clone(), pkg_req.version_requirement.clone()))
                    .collect(),
            )
            .convert_failure_errors(|err| LoadingError::content_error(190, Arc::new(err)))
            .and_then(|content| {
                let expected_main =
                    Identifier::new_versionned(&version, vec![name.clone()], "main");
                LoadingResult::new_success(Self {
                    name: name.clone(),
                    version: version.clone(),
                    requirements,
                    entrypoints: if content.provide().contains(&expected_main) {
                        HashMap::from_iter([(
                            "main".to_string(),
                            expected_main.with_version(&version),
                        )])
                    } else {
                        HashMap::new()
                    },
                    content,
                })
            })
        } else {
            LoadingResult::new_failure(LoadingError::no_package(
                189,
                name.map(|name| PackageRequirement::new(&name, &VersionReq::STAR))
                    .unwrap_or_else(|| PackageRequirement::new("[raw package]", &VersionReq::STAR)),
            ))
        }
    }
}

impl PackageInfo for RawPackage {
    fn name(&self) -> &str {
        &self.name
    }

    fn version(&self) -> &Version {
        &self.version
    }

    fn requirements(&self) -> &Vec<PackageRequirement> {
        &self.requirements
    }

    fn entrypoints(&self) -> &HashMap<String, Identifier> {
        &self.entrypoints
    }
}

impl PackageTrait for RawPackage {
    fn embedded_collection(&self, _: &Loader) -> LoadingResult<Collection> {
        LoadingResult::new_success(Collection::new())
    }

    fn full_collection(&self, loader: &Loader) -> LoadingResult<Collection> {
        let mut result = LoadingResult::new_success(Collection::new());
        let mut collection = Collection::new();

        for need in &self.content.require() {
            if let Some(entry) = result.merge_degrade_failure(loader.get_with_load(need)) {
                collection.insert(entry);
            }
        }

        result
            .and_then(|_| {
                self.content
                    .insert_descriptors(&mut collection)
                    .convert_failure_errors(|err| LoadingError::content_error(191, Arc::new(err)))
            })
            .and(LoadingResult::new_success(collection))
    }

    fn all_identifiers(&self, _: &Loader) -> LoadingResult<Vec<Identifier>> {
        LoadingResult::new_success(
            self.content
                .provide()
                .into_iter()
                .map(|id| id.with_version(&self.version))
                .collect(),
        )
    }

    fn element(
        &self,
        loader: &Loader,
        _identifier_requirement: &IdentifierRequirement,
    ) -> LoadingResult<Collection> {
        self.full_collection(loader)
    }

    fn make_building(&self, collection: &Arc<Collection>) -> LoadingResult<()> {
        self.content
            .make_design(collection)
            .convert_failure_errors(|err| LoadingError::content_error(192, Arc::new(err)))
    }
}