dipper 0.5.3

An out-of-the-box modular dependency injection web application framework.
Documentation
use std::{
    collections::{BTreeMap, btree_map::Iter},
    fmt::Display,
    fs, io,
    io::Write,
    path::PathBuf,
    str::FromStr,
    sync::Arc,
};

use cubix::getset2;
use semver::Version;
use serde::{Deserialize, Deserializer, Serialize, Serializer, ser::SerializeMap};
use tokio::sync::RwLock;

use super::Interface;
use crate::sealed::Sealed;

/// Global maifest list
pub type GlobalManifestMap = &'static BTreeMap<String, Arc<Manifest>>;

pub(crate) static mut MANIFEST_LIST: BTreeMap<String, Arc<Manifest>> = BTreeMap::new();

#[allow(static_mut_refs)]
static _X_: GlobalManifestMap = unsafe { &MANIFEST_LIST };

pub(crate) unsafe fn reg_manifest(manifest: Arc<Manifest>) {
    #[allow(static_mut_refs)]
    if let Some(old_manifest) = MANIFEST_LIST.insert(manifest.name().clone(), manifest.clone()) {
        panic!(
            "Repeatedly registering modules with the same name:\nNew={}\nOld={}",
            manifest.to_xml_string(),
            old_manifest.to_xml_string()
        )
    }
}

/// New the global maifest list iterator.
pub fn maifest_list() -> Iter<'static, String, Arc<Manifest>> {
    _X_.iter()
}

pub trait FullManifest: Interface {
    fn full_manifest(&self, routable_preset: bool) -> Manifest;
}
impl<T: ?Sized + Interface> FullManifest for T {
    #[allow(clippy::as_conversions)]
    fn full_manifest(&self, routable_preset: bool) -> Manifest {
        let depends: Vec<String> = self.depends().iter().map(|m| m.name().to_owned()).collect();
        let mut manifest = self.manifest();
        let manifest_ptr = Arc::as_ptr(&manifest.routable) as *mut RwLock<bool>;
        unsafe {
            let rwlock = &mut *manifest_ptr;
            *rwlock.get_mut() = routable_preset;
        }
        manifest.set_depends(depends);
        manifest
    }
}

/// A module manifest.
#[derive(Debug, Clone, Serialize, Deserialize, getset2::Getset2)]
#[getset2(set_with(pub), get_ref(pub))]
pub struct Manifest {
    ///  name
    name: String,
    /// Short summary of the 's purpose
    summary: String,
    /// Long description of the 's purpose
    description: String,
    /// Author
    author: String,
    ///  home page
    homepage: String,
    /// Category
    category: String,
    /// Version
    version: Version,
    /// Data files always loaded
    data: Vec<PathBuf>,
    /// Data files only loaded in demonstration mode
    demo: Vec<PathBuf>,
    #[getset2(skip)]
    depends: Vec<String>,
    #[getset2(skip)]
    #[serde(
        serialize_with = "serialize_arc_rwlock_bool",
        deserialize_with = "deserialize_arc_rwlock_bool"
    )]
    routable: Arc<RwLock<bool>>,
}

impl Manifest {
    pub fn to_xml_string(&self) -> String {
        let mut writer = String::new();
        let mut ser = quick_xml::se::Serializer::new(&mut writer);
        ser.indent(' ', 2);
        self.serialize(ser)
            .expect("'Manifest' does not support being serialized into XML.");
        writer
    }
    #[inline(always)]
    pub fn set_depends(&mut self, depends: Vec<String>) {
        self.depends = depends;
    }
    #[inline(always)]
    pub const fn depends(&self) -> &Vec<String> {
        &self.depends
    }
    #[inline(always)]
    pub const fn routable(&self) -> &Arc<RwLock<bool>> {
        &self.routable
    }
    #[allow(dead_code)]
    fn output_xml(&self, manifest_dir: PathBuf) -> io::Result<()> {
        fs::create_dir_all(manifest_dir.clone())?;
        let mut f = fs::File::create(manifest_dir.join(self.name().to_owned() + ".xml"))?;
        f.write_all(r##"<?xml version="1.0" encoding="UTF-8"?>"##.as_bytes())?;
        let _ = f.write(b"\n")?;
        f.write_all(self.to_xml_string().as_bytes())?;
        Ok(())
    }
}

impl Default for Manifest {
    fn default() -> Self {
        Self {
            name: Default::default(),
            summary: Default::default(),
            description: Default::default(),
            author: Default::default(),
            homepage: Default::default(),
            category: Default::default(),
            version: Version::new(0, 1, 0),
            data: Default::default(),
            demo: Default::default(),
            depends: Default::default(),
            routable: Default::default(),
        }
    }
}

impl Display for Manifest {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", quick_xml::se::to_string(self).map_err(|_| std::fmt::Error)?)
    }
}

impl FromStr for Manifest {
    type Err = quick_xml::DeError;

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

fn serialize_arc_rwlock_bool<S>(value: &Arc<RwLock<bool>>, serializer: S) -> Result<S::Ok, S::Error>
where
    S: Serializer,
{
    tokio::task::block_in_place(|| {
        let rt = tokio::runtime::Handle::current();
        rt.block_on(async {
            let read_guard = value.read().await;
            *read_guard
        })
    })
    .serialize(serializer)
}

fn deserialize_arc_rwlock_bool<'de, D>(deserializer: D) -> Result<Arc<RwLock<bool>>, D::Error>
where
    D: Deserializer<'de>,
{
    let b = bool::deserialize(deserializer)?;
    Ok(Arc::new(RwLock::new(b)))
}

pub trait XmlDisplay: Sealed {
    fn to_xml_string(&self) -> String;
}

impl Sealed for BTreeMap<String, Arc<Manifest>> {}

impl XmlDisplay for BTreeMap<String, Arc<Manifest>> {
    fn to_xml_string(&self) -> String {
        #[derive(Debug, Serialize)]
        struct ManifestMap<'a>(
            #[serde(serialize_with = "serialize_btreemap_with_arc")] &'a BTreeMap<String, Arc<Manifest>>,
        );
        fn serialize_btreemap_with_arc<S>(
            map: &BTreeMap<String, Arc<Manifest>>,
            serializer: S,
        ) -> Result<S::Ok, S::Error>
        where
            S: Serializer,
        {
            let mut map_ser = serializer.serialize_map(Some(map.len()))?;
            for (k, v) in map {
                map_ser.serialize_entry(k, &**v)?;
            }
            map_ser.end()
        }
        let mut writer = String::new();
        let mut ser = quick_xml::se::Serializer::new(&mut writer);
        ser.indent(' ', 2);
        ManifestMap(self)
            .serialize(ser)
            .expect("'Manifest' does not support being serialized into XML.");
        writer
    }
}

#[test]
fn demo() {
    let manifest = Manifest {
        name: "_name".to_string(), // Replace with actual  name
        summary: "Short (1 phrase/line) summary of the 's purpose".to_string(),
        description: "Long description of 's purpose".to_string(),
        author: "My Company".to_string(),
        homepage: "https://www.yourcompany.com".to_string(),
        category: "Uncategorized".to_owned(),
        version: "0.1.0".parse().unwrap(),
        data: vec!["views/views.xml".into(), "views/templates.xml".into()],
        demo: vec!["demo/demo.xml".into()],
        depends: vec![],
        routable: Default::default(),
    };

    println!("{}", manifest);
    println!("{}", manifest.to_xml_string());
}