1use std::collections::HashMap;
7use std::fs;
8use std::path::Path;
9
10use crate::error::DecodeError;
11use crate::profile::Profile;
12use crate::profile_parser::parse_profiles_json;
13
14#[derive(Debug, Clone)]
16pub struct ProfileDb {
17 profiles: HashMap<i32, Profile>,
18}
19
20impl ProfileDb {
21 pub fn load_builtin() -> Result<Self, DecodeError> {
29 let json = include_str!("../data/profiles.json");
30 let profiles = parse_profiles_json(json)?;
31 let map: HashMap<i32, Profile> = profiles.into_iter().map(|p| (p.prefix, p)).collect();
32 Ok(Self { profiles: map })
33 }
34
35 pub fn load_external<P: AsRef<Path>>(&mut self, path: P) -> Result<(), DecodeError> {
41 let data = fs::read_to_string(path.as_ref())
42 .map_err(|e| DecodeError::Profile(format!("failed to read '{}': {e}", path.as_ref().display())))?;
43 let profiles = parse_profiles_json(&data)?;
44 for p in profiles {
45 self.profiles.insert(p.prefix, p);
46 }
47 Ok(())
48 }
49
50 #[must_use]
52 pub fn get(&self, prefix: i32) -> Option<&Profile> {
53 self.profiles.get(&prefix)
54 }
55
56 #[must_use]
58 pub fn all(&self) -> &HashMap<i32, Profile> {
59 &self.profiles
60 }
61
62 #[must_use]
64 pub fn len(&self) -> usize {
65 self.profiles.len()
66 }
67
68 #[must_use]
70 pub fn is_empty(&self) -> bool {
71 self.profiles.is_empty()
72 }
73}
74
75#[cfg(test)]
80#[allow(clippy::unwrap_used)]
81mod tests {
82 use super::*;
83
84 #[test]
85 fn load_builtin_has_54_profiles() {
86 let db = ProfileDb::load_builtin().unwrap();
87 assert_eq!(db.len(), 54);
88 }
89
90 #[test]
91 fn get_1007_returns_correct_profile() {
92 let db = ProfileDb::load_builtin().unwrap();
93 let p = db.get(1007).expect("profile 1007 should exist");
94 assert_eq!(p.prefix, 1007);
95 assert_eq!(p.width, 480);
96 assert_eq!(p.height, 864);
97 assert_eq!(p.encoding, crate::profile::Encoding::Rgb565);
98 assert_eq!(p.frame_byte_length, 829_440);
99 assert!(!p.is_padded);
100 assert!(p.little_endian); }
102
103 #[test]
104 fn get_9999_returns_none() {
105 let db = ProfileDb::load_builtin().unwrap();
106 assert!(db.get(9999).is_none());
107 }
108
109 #[test]
110 fn get_1061_has_use_mhni_dimensions() {
111 let db = ProfileDb::load_builtin().unwrap();
112 let p = db.get(1061).expect("profile 1061 should exist");
113 assert!(p.use_mhni_dimensions);
114 }
115
116 #[test]
117 fn get_3004_has_padded_slot() {
118 let db = ProfileDb::load_builtin().unwrap();
119 let p = db.get(3004).expect("profile 3004 should exist");
120 assert!(p.is_padded);
121 assert_eq!(p.slot_size, 8192);
122 assert_eq!(p.encoding, crate::profile::Encoding::Rgb555);
123 }
124
125 #[test]
126 fn all_returns_full_map() {
127 let db = ProfileDb::load_builtin().unwrap();
128 let all = db.all();
129 assert!(all.contains_key(&1007));
130 assert!(all.contains_key(&3011));
131 assert_eq!(all.len(), 54);
132 }
133}