unm_engine_qq 0.4.0

The QQ engine for UnblockNeteaseMusic.
Documentation
use std::error::Error;
use std::fmt::Display;

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct QQResourceIdentifier<'a> {
    pub mid: &'a str,
    pub file: &'a str,
}

impl<'a> QQResourceIdentifier<'a> {
    pub fn serialize(&self) -> String {
        log::debug!("Serializing a QQResourceIdentifier to String…");
        concat_string::concat_string!(self.mid, ":::", self.file)
    }

    pub fn deserialize(serialized: &'a str) -> DeserializationResult<Self> {
        log::debug!("Deserializing “{serialized}” to QQResourceIdentifier…");

        let mut parts = serialized.splitn(2, ":::");
        let mid = parts.next().ok_or(DeserializationFailed("song"))?;
        let file = parts.next().ok_or(DeserializationFailed("file"))?;

        Ok(Self { mid, file })
    }
}

#[derive(Debug)]
pub struct DeserializationFailed(&'static str);
pub type DeserializationResult<T> = Result<T, DeserializationFailed>;

impl Display for DeserializationFailed {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "the attempt to extract “{}” from 'serialized' parameter failed",
            self.0
        )
    }
}

impl Error for DeserializationFailed {}

#[cfg(test)]
mod tests {
    use super::QQResourceIdentifier;

    #[test]
    fn test_identifier_serialization() {
        let identifier = QQResourceIdentifier {
            mid: "mid",
            file: "file",
        };

        assert_eq!(identifier.serialize(), "mid:::file");
    }

    #[test]
    fn test_identifier_deserialization() {
        let identifier = "mid113:::file113";

        assert_eq!(
            QQResourceIdentifier::deserialize(identifier).unwrap(),
            QQResourceIdentifier {
                mid: "mid113",
                file: "file113",
            }
        );
    }

    #[test]
    fn test_identifier_failed_deserialization() {
        let identifier = "mid113::file113";

        let deserialized = QQResourceIdentifier::deserialize(identifier);
        assert!(deserialized.is_err());
    }
}