use rocket::request::FromParam; use serde::Deserialize; use std::{ collections::{HashMap, HashSet}, sync::LazyLock, }; pub static KNOWN_SOFTWARE: LazyLock = LazyLock::new(|| serde_json::from_str(include_str!("../known-software.json")).unwrap()); pub static KNOWN_SOFTWARE_NAMES: LazyLock> = LazyLock::new(|| KNOWN_SOFTWARE.get_name_map()); pub static KNOWN_SOFTWARE_NODEINFO_NAMES: LazyLock< HashMap)>, > = LazyLock::new(|| KNOWN_SOFTWARE.get_nodeinfo_name_map()); #[derive(Deserialize)] #[serde(rename_all = "camelCase")] pub struct Software { name: String, nodeinfo_name: String, build_metadata: Option, aliases: HashSet, groups: HashSet, fork_of: Option, } #[derive(Deserialize)] pub struct Group { name: String, aliases: HashSet, } #[derive(Deserialize)] pub struct KnownSoftware { software: HashMap, groups: HashMap, } impl KnownSoftware { fn get_name_map(&self) -> HashMap { let mut map = HashMap::new(); self.software.iter().for_each(|(name, software)| { software.aliases.iter().for_each(|alias| { assert_eq!(map.insert(alias.to_owned(), name.to_owned()), None); }); }); self.groups.iter().for_each(|(name, group)| { group.aliases.iter().for_each(|alias: &String| { assert_eq!(map.insert(alias.to_owned(), name.to_owned()), None); }); }); map } fn get_nodeinfo_name_map(&self) -> HashMap)> { let mut map: HashMap)> = HashMap::new(); self.software.iter().for_each(|(name, software)| { if let Some((mut_name, mut_map)) = map.get_mut(&software.nodeinfo_name) { if let Some(build_metadata) = &software.build_metadata { assert_eq!( mut_map.insert(build_metadata.to_owned(), name.to_owned()), None ); } else { *mut_name = name.to_owned(); } } else { let mut build_metadata_map = HashMap::new(); if let Some(build_metadata) = &software.build_metadata { build_metadata_map.insert(build_metadata.to_owned(), name.to_owned()); } map.insert( software.nodeinfo_name.to_owned(), (name.to_owned(), build_metadata_map), ); } }); map } } pub struct KnownInstanceSoftware<'r> { pub requested: &'r str, pub resolved: &'static String, } impl<'r> FromParam<'r> for KnownInstanceSoftware<'r> { type Error = &'r str; fn from_param(param: &'r str) -> Result { if let Some(resolved) = KNOWN_SOFTWARE_NAMES.get(param) { Ok(KnownInstanceSoftware { requested: param, resolved, }) } else { Err(param) } } }