Skip to main content

pogo_masterfile/
masterfile.rs

1//! The Masterfile struct + top-level methods. Per-group accessor methods
2//! are emitted into the impl block in `lib.rs` by codegen — that file
3//! contains an `impl Masterfile { /* per-group methods */ }` block.
4
5use std::collections::HashMap;
6
7use pogo_masterfile_types::MasterfileEntry;
8
9use crate::GroupIndexes;
10use crate::error::Result;
11
12#[cfg(feature = "async")]
13use crate::builder::MasterfileBuilder;
14
15pub struct Masterfile {
16    pub(crate) entries: Vec<MasterfileEntry>,
17    pub(crate) by_id: HashMap<String, usize>,
18    pub(crate) groups: GroupIndexes,
19}
20
21impl Masterfile {
22    // ── Loaders ──────────────────────────────────────────────────
23
24    #[cfg(feature = "async")]
25    pub fn builder() -> MasterfileBuilder {
26        MasterfileBuilder::new()
27    }
28
29    /// Convenience: equivalent to `Masterfile::builder().fetch().await`.
30    #[cfg(feature = "async")]
31    pub async fn from_remote() -> Result<Self> {
32        Self::builder().fetch().await
33    }
34
35    /// Parse from a JSON string. Sync — no I/O.
36    pub fn from_json(json: &str) -> Result<Self> {
37        let entries = pogo_masterfile_types::parse_masterfile(json)?;
38        Ok(Self::from_entries(entries))
39    }
40
41    /// Wrap an already-parsed entry vec.
42    pub fn from_entries(entries: Vec<MasterfileEntry>) -> Self {
43        let by_id = entries
44            .iter()
45            .enumerate()
46            .map(|(i, e)| (e.template_id().to_string(), i))
47            .collect();
48        let groups = GroupIndexes::build(&entries);
49        Self {
50            entries,
51            by_id,
52            groups,
53        }
54    }
55
56    // ── Top-level lookups ────────────────────────────────────────
57
58    pub fn get_entry(&self, id: &str) -> Option<&MasterfileEntry> {
59        let &idx = self.by_id.get(id)?;
60        self.entries.get(idx)
61    }
62
63    pub fn has(&self, id: &str) -> bool {
64        self.by_id.contains_key(id)
65    }
66
67    // ── Iteration / introspection ────────────────────────────────
68
69    pub fn entries(&self) -> &[MasterfileEntry] {
70        &self.entries
71    }
72
73    pub fn len(&self) -> usize {
74        self.entries.len()
75    }
76
77    pub fn is_empty(&self) -> bool {
78        self.entries.is_empty()
79    }
80
81    pub fn iter(&self) -> std::slice::Iter<'_, MasterfileEntry> {
82        self.entries.iter()
83    }
84
85    pub fn template_ids(&self) -> impl Iterator<Item = &str> + '_ {
86        self.entries.iter().map(MasterfileEntry::template_id)
87    }
88
89    // ── Mutation (require &mut self — invalidates outstanding borrows) ──
90
91    #[cfg(feature = "async")]
92    pub async fn refresh(&mut self) -> Result<()> {
93        let fresh = Self::from_remote().await?;
94        *self = fresh;
95        Ok(())
96    }
97
98    pub fn update(&mut self, entries: Vec<MasterfileEntry>) {
99        *self = Self::from_entries(entries);
100    }
101}
102
103// IntoIterator so `for entry in &mf { ... }` works.
104impl<'a> IntoIterator for &'a Masterfile {
105    type Item = &'a MasterfileEntry;
106    type IntoIter = std::slice::Iter<'a, MasterfileEntry>;
107    fn into_iter(self) -> Self::IntoIter {
108        self.iter()
109    }
110}