pacenotes_installer/
schema.rs1pub struct Schema {
10 pub game: &'static str,
11 pub toml: &'static [u8],
13 pub categories: &'static [Category],
14}
15
16pub struct Category {
17 pub id: &'static str,
18 pub select: &'static str,
20 pub install_root: &'static str,
22}
23
24include!(concat!(env!("OUT_DIR"), "/schema_table.rs"));
25
26const ALSO_ACCEPTED: &[(&str, &[&str])] = &[];
29
30pub fn of(game: &str) -> Option<&'static Schema> {
31 SCHEMAS.iter().find(|schema| schema.game == game)
32}
33
34pub fn sha256_of(game: &str) -> Option<String> {
36 of(game).map(|schema| crate::sha256::hex_digest(schema.toml))
37}
38
39pub fn accepts(game: &str, sha256: &str) -> bool {
41 if sha256_of(game).is_some_and(|ours| ours == sha256) {
42 return true;
43 }
44 ALSO_ACCEPTED
45 .iter()
46 .any(|(id, hashes)| *id == game && hashes.contains(&sha256))
47}
48
49pub fn choice_categories(game: &str) -> Vec<&'static str> {
52 categories(game)
53 .iter()
54 .filter(|category| category.select == pacenotes_shared::SELECT_ONE)
55 .map(|category| category.id)
56 .collect()
57}
58
59pub fn categories(game: &str) -> &'static [Category] {
61 of(game).map(|schema| schema.categories).unwrap_or(&[])
62}
63
64pub fn install_roots_known(game: &str) -> bool {
66 categories(game)
67 .iter()
68 .all(|category| category.install_root == pacenotes_shared::INSTALL_ROOT_GAME)
69}
70
71#[derive(Clone, Debug, PartialEq, Eq)]
73pub enum Incompatible {
74 NoSchemaPublished,
76 GameUnknown,
77 Different {
78 ours: String,
79 theirs: String,
80 unknown_categories: Vec<String>,
82 },
83}
84
85pub fn check(
87 game: &str,
88 published: Option<&pacenotes_shared::GameSchema>,
89) -> Result<(), Incompatible> {
90 let Some(published) = published else {
91 return Err(Incompatible::NoSchemaPublished);
92 };
93 if of(game).is_none() {
94 return Err(Incompatible::GameUnknown);
95 }
96 if accepts(game, &published.sha256) {
97 return Ok(());
98 }
99 let known: Vec<&str> = categories(game)
100 .iter()
101 .map(|category| category.id)
102 .collect();
103 Err(Incompatible::Different {
104 ours: sha256_of(game).unwrap_or_default(),
105 theirs: published.sha256.clone(),
106 unknown_categories: published
107 .categories
108 .iter()
109 .filter(|category| !known.contains(&category.id.as_str()))
110 .map(|category| category.id.clone())
111 .collect(),
112 })
113}
114
115#[cfg(test)]
116mod tests;