melodium-common 0.10.1

Common Mélodium elements and traits
Documentation
use super::Version;
use core::{
    convert::TryFrom,
    fmt::{Display, Formatter},
    result::Result,
    str::FromStr,
};

#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub struct Identifier {
    version: Option<Version>,
    path: Vec<String>,
    name: String,
}

impl Identifier {
    pub fn new(path: Vec<String>, name: &str) -> Self {
        if path.is_empty() {
            panic!("Identifier path cannot be empty.")
        }

        Self {
            version: None,
            path,
            name: name.to_string(),
        }
    }

    pub fn new_versionned(version: &Version, path: Vec<String>, name: &str) -> Self {
        if path.is_empty() {
            panic!("Identifier path cannot be empty.")
        }

        Self {
            version: Some(version.clone()),
            path,
            name: name.to_string(),
        }
    }

    pub fn new_optionally_versionned(
        version: Option<&Version>,
        path: Vec<String>,
        name: &str,
    ) -> Self {
        if path.is_empty() {
            panic!("Identifier path cannot be empty.")
        }

        Self {
            version: version.cloned(),
            path,
            name: name.to_string(),
        }
    }

    pub fn version(&self) -> Option<&Version> {
        self.version.as_ref()
    }

    pub fn root(&self) -> &String {
        &self.path.first().unwrap()
    }

    pub fn path(&self) -> &Vec<String> {
        &self.path
    }

    pub fn name(&self) -> &str {
        &self.name
    }

    pub fn with_version(&self, version: &Version) -> Self {
        Self {
            version: Some(version.clone()),
            path: self.path.clone(),
            name: self.name.clone(),
        }
    }

    pub fn with_optional_version(&self, version: Option<&Version>) -> Self {
        Self {
            version: version.cloned(),
            path: self.path.clone(),
            name: self.name.clone(),
        }
    }

    pub fn without_version(&self) -> Self {
        Self {
            version: None,
            path: self.path.clone(),
            name: self.name.clone(),
        }
    }
}

impl Display for Identifier {
    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
        write!(f, "{}::{}", self.path.join("/"), self.name)
    }
}

impl FromStr for Identifier {
    type Err = String;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        Self::try_from(s)
    }
}

impl Ord for Identifier {
    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
        let c = self.to_string().cmp(&other.to_string());
        if c == core::cmp::Ordering::Equal {
            if let (Some(s), Some(o)) = (self.version(), other.version()) {
                s.cmp(o)
            } else {
                c
            }
        } else {
            c
        }
    }
}

impl PartialOrd for Identifier {
    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
        let c = self.to_string().partial_cmp(&other.to_string());
        if c == Some(core::cmp::Ordering::Equal) {
            if let (Some(s), Some(o)) = (self.version(), other.version()) {
                s.partial_cmp(o)
            } else {
                c
            }
        } else {
            c
        }
    }
}

impl TryFrom<&str> for Identifier {
    type Error = String;

    fn try_from(value: &str) -> Result<Self, Self::Error> {
        let full = value.split("::").collect::<Vec<_>>();
        if full.len() == 2 {
            let path = full[0];
            let name = full[1];

            let path = path.split('/').map(|s| s.to_string()).collect::<Vec<_>>();
            if path.len() >= 1 {
                Ok(Self {
                    version: None,
                    path,
                    name: name.to_string(),
                })
            } else {
                Err(value.to_string())
            }
        } else {
            Err(value.to_string())
        }
    }
}

impl TryFrom<&String> for Identifier {
    type Error = String;

    fn try_from(value: &String) -> Result<Self, Self::Error> {
        Self::try_from(value.as_str())
    }
}