FeDirect/src/known_software.rs
CenTdemeern1 be021c4b16
Some checks are pending
Test build & run / build-run (push) Waiting to run
Add CI
Uhhhh hoping it runs ig?
2025-02-03 21:56:13 +01:00

114 lines
3.5 KiB
Rust

use rocket::request::FromParam;
use serde::Deserialize;
use std::{
collections::{HashMap, HashSet},
sync::LazyLock,
};
pub static KNOWN_SOFTWARE: LazyLock<KnownSoftware> =
LazyLock::new(|| serde_json::from_str(include_str!("../known-software.json")).unwrap());
pub static KNOWN_SOFTWARE_NAMES: LazyLock<HashMap<String, String>> =
LazyLock::new(|| KNOWN_SOFTWARE.get_name_map());
pub static KNOWN_SOFTWARE_NODEINFO_NAMES: LazyLock<
HashMap<String, (String, HashMap<String, String>)>,
> = 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<String>,
aliases: HashSet<String>,
groups: HashSet<String>,
fork_of: Option<String>,
}
#[derive(Deserialize)]
pub struct Group {
name: String,
aliases: HashSet<String>,
}
#[derive(Deserialize)]
pub struct KnownSoftware {
software: HashMap<String, Software>,
groups: HashMap<String, Group>,
}
impl KnownSoftware {
fn get_name_map(&self) -> HashMap<String, String> {
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<String, (String, HashMap<String, String>)> {
let mut map: HashMap<String, (String, HashMap<String, String>)> = 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<Self, Self::Error> {
if let Some(resolved) = KNOWN_SOFTWARE_NAMES.get(param) {
Ok(KnownInstanceSoftware {
requested: param,
resolved,
})
} else {
Err(param)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
/// If this test fails, known-software.json is invalid
#[test]
fn known_software_is_valid() {
assert!(!KNOWN_SOFTWARE.groups.is_empty());
assert!(!KNOWN_SOFTWARE.software.is_empty());
assert!(!KNOWN_SOFTWARE_NAMES.is_empty());
assert!(!KNOWN_SOFTWARE_NODEINFO_NAMES.is_empty());
}
}