1use crate::prelude::*;
14use core::fmt;
15
16use serde::{Deserialize, Serialize};
17
18use crate::storage::{Storage, StorageError, is_valid_key};
19
20pub const PROFILES_KEY: &str = "profiles";
22
23pub const PROFILES_VERSION: u32 = 1;
25
26pub const MAX_PROFILES: usize = 8;
28
29pub const MAX_NAME_CHARS: usize = 12;
31
32#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
35#[serde(transparent)]
36pub struct ProfileId(u32);
37
38impl ProfileId {
39 pub const FIRST: Self = Self(1);
41
42 pub fn get(self) -> u32 {
44 self.0
45 }
46
47 pub fn prefix(self) -> String {
49 format!("p{}-", self.0)
50 }
51
52 pub fn key(self, key: &str) -> String {
54 format!("{}{key}", self.prefix())
55 }
56
57 fn of_key(key: &str) -> Option<Self> {
60 let (number, rest) = key.strip_prefix('p')?.split_once('-')?;
61 let canonical = !number.starts_with('0') && number.bytes().all(|b| b.is_ascii_digit());
62 let id = number
63 .parse()
64 .ok()
65 .filter(|_| canonical && !rest.is_empty())?;
66 Some(Self(id))
67 }
68}
69
70#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
72pub struct Profile {
73 pub id: ProfileId,
75 pub name: String,
77}
78
79#[derive(Debug, Clone, PartialEq, Eq)]
81pub enum ProfileError {
82 NameEmpty,
84 NameTooLong,
86 NameTaken,
88 TooMany,
90 LastOne,
92 NoSuchProfile(ProfileId),
94 Storage(StorageError),
96}
97
98impl fmt::Display for ProfileError {
100 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
101 match self {
102 Self::NameEmpty => f.write_str("the name is empty"),
103 Self::NameTooLong => write!(f, "the name is over {MAX_NAME_CHARS} characters"),
104 Self::NameTaken => f.write_str("another profile has that name"),
105 Self::TooMany => write!(f, "there are {MAX_PROFILES} profiles already"),
106 Self::LastOne => f.write_str("the only profile can't be deleted"),
107 Self::NoSuchProfile(id) => write!(f, "no profile {}", id.get()),
108 Self::Storage(e) => write!(f, "profiles: {e}"),
109 }
110 }
111}
112
113impl core::error::Error for ProfileError {}
114
115#[derive(Debug, Clone, PartialEq, Eq)]
117pub struct Profiles {
118 list: Vec<Profile>,
119 current: ProfileId,
120 next_id: u32,
122}
123
124#[derive(Debug, Serialize, Deserialize)]
126#[serde(rename = "Profiles")]
127struct ProfilesFile {
128 version: u32,
129 profiles: Vec<Profile>,
130 current: ProfileId,
131 next_id: u32,
132}
133
134impl Profiles {
135 pub fn first(name: &str) -> Self {
137 Self {
138 list: vec![Profile {
139 id: ProfileId::FIRST,
140 name: name.to_owned(),
141 }],
142 current: ProfileId::FIRST,
143 next_id: ProfileId::FIRST.0 + 1,
144 }
145 }
146
147 pub fn all(&self) -> &[Profile] {
149 &self.list
150 }
151
152 pub fn current(&self) -> ProfileId {
154 self.current
155 }
156
157 pub fn get(&self, id: ProfileId) -> Option<&Profile> {
159 self.list.iter().find(|p| p.id == id)
160 }
161
162 pub fn set_current(&mut self, id: ProfileId) -> Result<(), ProfileError> {
164 self.get(id).ok_or(ProfileError::NoSuchProfile(id))?;
165 self.current = id;
166 Ok(())
167 }
168
169 pub fn add(&mut self, name: &str) -> Result<ProfileId, ProfileError> {
172 if self.list.len() >= MAX_PROFILES {
173 return Err(ProfileError::TooMany);
174 }
175 let name = self.check_name(name, None)?;
176 let id = ProfileId(self.next_id);
177 self.next_id += 1;
178 self.list.push(Profile { id, name });
179 Ok(id)
180 }
181
182 pub fn rename(&mut self, id: ProfileId, name: &str) -> Result<(), ProfileError> {
184 self.get(id).ok_or(ProfileError::NoSuchProfile(id))?;
185 let name = self.check_name(name, Some(id))?;
186 if let Some(profile) = self.list.iter_mut().find(|p| p.id == id) {
187 profile.name = name; }
189 Ok(())
190 }
191
192 pub fn remove(&mut self, id: ProfileId) -> Result<(), ProfileError> {
195 self.get(id).ok_or(ProfileError::NoSuchProfile(id))?;
196 if self.list.len() == 1 {
197 return Err(ProfileError::LastOne);
198 }
199 self.list.retain(|p| p.id != id);
200 if self.current == id {
201 self.current = self.list[0].id;
202 }
203 Ok(())
204 }
205
206 fn check_name(&self, name: &str, except: Option<ProfileId>) -> Result<String, ProfileError> {
209 let name = name.trim();
210 if name.is_empty() {
211 return Err(ProfileError::NameEmpty);
212 }
213 if name.chars().count() > MAX_NAME_CHARS {
214 return Err(ProfileError::NameTooLong);
215 }
216 let lower = name.to_lowercase();
217 let taken = self
218 .list
219 .iter()
220 .any(|p| Some(p.id) != except && p.name.to_lowercase() == lower);
222 if taken {
223 return Err(ProfileError::NameTaken);
224 }
225 Ok(name.to_owned())
226 }
227
228 pub fn to_ron(&self) -> String {
230 let file = ProfilesFile {
231 version: PROFILES_VERSION,
232 profiles: self.list.clone(),
233 current: self.current,
234 next_id: self.next_id,
235 };
236 let config = ron::ser::PrettyConfig::new().struct_names(true);
237 ron::ser::to_string_pretty(&file, config).unwrap_or_default()
239 }
240
241 pub fn from_ron(text: &str) -> Result<Self, String> {
247 let file: ProfilesFile = ron::from_str(text).map_err(|e| format!("unreadable: {e}"))?;
248 if file.version != PROFILES_VERSION {
249 let version = file.version;
250 return Err(format!("version {version} isn't {PROFILES_VERSION}"));
251 }
252 let first = file.profiles.first().ok_or("no profile in it")?.id;
253 let current = if file.profiles.iter().any(|p| p.id == file.current) {
254 file.current
255 } else {
256 first
257 };
258 let past_all = file.profiles.iter().map(|p| p.id.0 + 1).max().unwrap_or(1);
259 Ok(Self {
260 current,
261 next_id: file.next_id.max(past_all),
262 list: file.profiles,
263 })
264 }
265
266 pub fn save(&self, storage: &mut dyn Storage) -> Result<(), StorageError> {
268 storage.write(PROFILES_KEY, &self.to_ron())
269 }
270}
271
272pub fn fits_every_profile(key: &str) -> bool {
276 is_valid_key(key) && is_valid_key(&ProfileId(u32::MAX).key(key))
277}
278
279pub fn load(
293 storage: &mut dyn Storage,
294 old_keys: &[String],
295 numbered: impl Fn(u32) -> String,
296) -> (Profiles, Vec<String>) {
297 let mut warnings = Vec::new();
298 let profiles = match storage.read(PROFILES_KEY) {
299 Ok(Some(text)) => match Profiles::from_ron(&text) {
300 Ok(profiles) => profiles,
301 Err(why) => {
302 warnings.push(format!("{PROFILES_KEY}: {why}, made again from the saves"));
304 rebuild(storage, old_keys, &numbered, &mut warnings)
305 }
306 },
307 Ok(None) => migrate(
308 storage,
309 old_keys,
310 &numbered(ProfileId::FIRST.0),
311 &mut warnings,
312 ),
313 Err(e) => {
314 warnings.push(format!("{PROFILES_KEY}: can't be read: {e}"));
316 Profiles::first(&numbered(ProfileId::FIRST.0))
317 }
318 };
319 (profiles, warnings)
320}
321
322fn rebuild(
325 storage: &mut dyn Storage,
326 old_keys: &[String],
327 numbered: &impl Fn(u32) -> String,
328 warnings: &mut Vec<String>,
329) -> Profiles {
330 let mut ids: Vec<ProfileId> = storage
331 .list()
332 .unwrap_or_default()
333 .iter()
334 .filter_map(|key| ProfileId::of_key(key))
335 .collect();
336 ids.sort_unstable();
337 ids.dedup();
338 let Some(&first) = ids.first() else {
339 return migrate(storage, old_keys, &numbered(ProfileId::FIRST.0), warnings);
340 };
341 let profiles = Profiles {
342 list: ids
343 .iter()
344 .map(|&id| Profile {
345 id,
346 name: numbered(id.0),
347 })
348 .collect(),
349 current: first,
350 next_id: ids.iter().map(|id| id.0 + 1).max().unwrap_or(1),
351 };
352 if let Err(e) = profiles.save(storage) {
353 warnings.push(format!("{PROFILES_KEY}: can't be saved: {e}"));
355 }
356 profiles
357}
358
359pub fn migrate(
368 storage: &mut dyn Storage,
369 old_keys: &[String],
370 name: &str,
371 warnings: &mut Vec<String>,
372) -> Profiles {
373 let profiles = Profiles::first(name);
374 let id = ProfileId::FIRST;
375 let mut moved = Vec::new();
376 let mut failed = false;
377 for key in old_keys {
378 let copied = match storage.read(key) {
379 Ok(None) => continue,
380 Ok(Some(value)) => match storage.read(&id.key(key)) {
381 Ok(Some(_)) => Ok(()),
382 Ok(None) => storage.write(&id.key(key), &value),
383 Err(e) => Err(e),
384 },
385 Err(e) => Err(e),
386 };
387 match copied {
388 Ok(()) => moved.push(key),
389 Err(e) => {
390 warnings.push(format!("{key}: can't be moved into the first profile: {e}"));
392 failed = true;
393 }
394 }
395 }
396 if failed {
397 warnings.push(format!(
399 "{PROFILES_KEY}: not saved, so the move is tried again"
400 ));
401 return profiles;
402 }
403 if let Err(e) = profiles.save(storage) {
404 warnings.push(format!("{PROFILES_KEY}: can't be saved: {e}"));
406 return profiles;
407 }
408 for key in moved {
409 if let Err(e) = storage.delete(key) {
410 warnings.push(format!("{key}: moved, but can't be deleted: {e}"));
412 }
413 }
414 profiles
415}
416
417pub fn delete_keys(storage: &mut dyn Storage, id: ProfileId) -> Result<(), StorageError> {
420 let keys = storage.list()?;
421 let mut result = Ok(());
422 for key in keys.iter().filter(|k| ProfileId::of_key(k) == Some(id)) {
423 let deleted = storage.delete(key);
424 if result.is_ok() {
425 result = deleted;
426 }
427 }
428 result
429}
430
431#[derive(Debug)]
437pub struct ProfileStore {
438 computer: Box<dyn Storage>,
439 profile: ProfileId,
440}
441
442impl Default for ProfileStore {
445 fn default() -> Self {
446 Self::new(
447 Box::new(crate::storage::MemoryStorage::new()),
448 ProfileId::FIRST,
449 )
450 }
451}
452
453impl ProfileStore {
454 pub fn new(computer: Box<dyn Storage>, profile: ProfileId) -> Self {
456 Self { computer, profile }
457 }
458
459 pub fn profile(&self) -> ProfileId {
461 self.profile
462 }
463
464 pub(crate) fn set_profile(&mut self, profile: ProfileId) {
466 self.profile = profile;
467 }
468
469 pub fn computer(&self) -> &dyn Storage {
471 &*self.computer
472 }
473
474 pub fn computer_mut(&mut self) -> &mut dyn Storage {
476 &mut *self.computer
477 }
478
479 pub fn into_computer(self) -> Box<dyn Storage> {
481 self.computer
482 }
483
484 fn full_key(&self, key: &str) -> Result<String, StorageError> {
486 let full = self.profile.key(key);
487 if is_valid_key(key) && is_valid_key(&full) {
488 Ok(full)
489 } else {
490 Err(StorageError::InvalidKey(key.to_owned()))
491 }
492 }
493}
494
495impl Storage for ProfileStore {
496 fn read(&self, key: &str) -> Result<Option<String>, StorageError> {
497 self.computer.read(&self.full_key(key)?)
498 }
499
500 fn write(&mut self, key: &str, value: &str) -> Result<(), StorageError> {
501 let full = self.full_key(key)?;
502 self.computer.write(&full, value)
503 }
504
505 fn delete(&mut self, key: &str) -> Result<(), StorageError> {
506 let full = self.full_key(key)?;
507 self.computer.delete(&full)
508 }
509
510 fn list(&self) -> Result<Vec<String>, StorageError> {
511 let prefix = self.profile.prefix();
512 let keys = self.computer.list()?;
513 Ok(keys
514 .iter()
515 .filter_map(|key| key.strip_prefix(&prefix).map(str::to_owned))
516 .collect())
517 }
518}
519
520#[cfg(test)]
521mod tests;