xiss_map/
map.rs

1use std::{
2    collections::hash_map::Entry,
3    error, fmt,
4    io::{self},
5};
6
7use rustc_hash::FxHashMap;
8use smol_str::SmolStr;
9
10use crate::{parser, Id, IdKind};
11
12#[derive(Debug, Clone, Copy)]
13pub struct ModuleIndex(usize);
14
15#[derive(Debug)]
16pub struct Map {
17    index: FxHashMap<SmolStr, ModuleIndex>,
18    items: Vec<Box<Module>>,
19}
20
21impl Map {
22    pub fn new() -> Self {
23        Self {
24            index: FxHashMap::default(),
25            items: Vec::new(),
26        }
27    }
28
29    pub fn get_by_index(&self, index: ModuleIndex) -> Option<&Module> {
30        self.items.get(index.0).map(|m| &**m)
31    }
32
33    pub fn get_mut_by_index(&mut self, index: ModuleIndex) -> Option<&mut Module> {
34        self.items.get_mut(index.0).map(|m| &mut **m)
35    }
36
37    pub fn get_by_id(&self, id: &str) -> Option<&Module> {
38        if let Some(index) = self.index.get(id) {
39            self.get_by_index(*index)
40        } else {
41            None
42        }
43    }
44
45    pub fn get_mut_by_id(&mut self, id: &str) -> Option<&mut Module> {
46        if let Some(index) = self.index.get(id) {
47            self.get_mut_by_index(*index)
48        } else {
49            None
50        }
51    }
52
53    /// Imports css map from [io::BufRead].
54    pub fn import<R: io::BufRead>(&mut self, reader: &mut R) -> Result<(), Error> {
55        let mut contents = String::new();
56        reader.read_to_string(&mut contents)?;
57        let mut p = parser::Parser::new(&contents);
58        let mut module_index = ModuleIndex(0);
59        while let Some((kind, module_id, local_id, global_id)) = p.next_id()? {
60            if let Some(module_id) = module_id {
61                let i = match self.index.entry(SmolStr::from(module_id).clone()) {
62                    Entry::Occupied(entry) => *entry.get(),
63                    Entry::Vacant(entry) => {
64                        let index = ModuleIndex(self.items.len());
65                        self.items
66                            .push(Box::new(Module::new(entry.key().clone(), index)));
67                        *entry.insert(index)
68                    }
69                };
70                module_index = i;
71            }
72            if let Some(module) = self.get_mut_by_index(module_index) {
73                let id = Id::new(kind, module.id.clone(), local_id.into(), global_id.into());
74                match kind {
75                    IdKind::Class => insert_id(&mut module.classes, id)?,
76                    IdKind::Var => insert_id(&mut module.vars, id)?,
77                    IdKind::Keyframes => insert_id(&mut module.keyframes, id)?,
78                }
79            }
80        }
81        Ok(())
82    }
83}
84
85fn insert_id<'a>(map: &mut FxHashMap<SmolStr, Id>, id: Id) -> Result<(), Error> {
86    match map.entry(id.local_id.clone()) {
87        Entry::Occupied(..) => Err(Error::DuplicateEntry(id)),
88        Entry::Vacant(v) => {
89            v.insert(id);
90            Ok(())
91        }
92    }
93}
94
95#[derive(Debug)]
96pub struct Module {
97    pub id: SmolStr,
98    pub index: ModuleIndex,
99    pub classes: FxHashMap<SmolStr, Id>,
100    pub vars: FxHashMap<SmolStr, Id>,
101    pub keyframes: FxHashMap<SmolStr, Id>,
102}
103
104impl Module {
105    pub fn new(id: SmolStr, index: ModuleIndex) -> Self {
106        Self {
107            id,
108            index,
109            classes: FxHashMap::default(),
110            vars: FxHashMap::default(),
111            keyframes: FxHashMap::default(),
112        }
113    }
114}
115
116#[derive(Debug)]
117pub enum Error {
118    IOError(io::Error),
119    ParserError(parser::Error),
120    DuplicateEntry(Id),
121}
122
123impl From<io::Error> for Error {
124    fn from(value: io::Error) -> Self {
125        Error::IOError(value)
126    }
127}
128
129impl From<parser::Error> for Error {
130    fn from(value: parser::Error) -> Self {
131        Error::ParserError(value)
132    }
133}
134
135impl error::Error for Error {}
136
137impl fmt::Display for Error {
138    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
139        match self {
140            Error::IOError(err) => err.fmt(f),
141            Error::ParserError(err) => err.fmt(f),
142            Error::DuplicateEntry(id) => write!(f, "Duplicate entry: '{}'", id),
143        }
144    }
145}