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;
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()
)
}
}
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
}
}
#[derive(Debug, Clone, Serialize, Deserialize, getset2::Getset2)]
#[getset2(set_with(pub), get_ref(pub))]
pub struct Manifest {
name: String,
summary: String,
description: String,
author: String,
homepage: String,
category: String,
version: Version,
data: Vec<PathBuf>,
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(), 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());
}