Skip to main content

qframe/storage/
update_notice.rs

1//! The family's one switch for the update notice: whether its applications ask once a day if a
2//! newer version of themselves is out.
3//!
4//! It lives in the family's shared file only, as `update-notice = false`, because it is a choice
5//! about the family rather than one application: a person who does not want to be told turns it
6//! off once. A missing key is on, so a family that never heard of the notice has it.
7
8use std::fs;
9use std::io;
10use std::path::Path;
11
12use super::{Family, SettingValue, Settings};
13
14/// The switch as the shared file `settings` has it: on unless it says `false`.
15pub(super) fn from_shared(settings: &Settings) -> bool {
16    settings.get::<bool>(Settings::UPDATE_NOTICE).unwrap_or(true)
17}
18
19impl Family {
20    /// Whether the family's applications say when a newer version is out: the shared file's
21    /// `update-notice`, on when the file or the key is missing. Off without a home folder, where
22    /// nothing could remember that it was asked.
23    #[must_use]
24    pub fn update_notice(&self) -> bool {
25        self.config_dir().is_some_and(|dir| self.update_notice_in(&dir))
26    }
27
28    /// [`update_notice`](Self::update_notice) with `config_dir` as the family's folder instead of
29    /// this platform's, for a test or a demo.
30    #[must_use]
31    pub fn update_notice_in(&self, config_dir: &Path) -> bool {
32        let path = config_dir.join(super::family::file_name(self.id()));
33        !path.exists() || from_shared(&Settings::open(path))
34    }
35
36    /// Turns the update notice on or off for every application of the family, in the shared file.
37    /// The file is read right before it is written and only this key changes in it, as
38    /// [`set`](Self::set) does.
39    ///
40    /// # Errors
41    ///
42    /// Returns an error of kind [`io::ErrorKind::NotFound`] when there is no home folder, and any
43    /// error from writing.
44    pub fn set_update_notice(&self, on: bool) -> io::Result<()> {
45        match self.config_dir() {
46            Some(dir) => self.set_update_notice_in(&dir, on),
47            None => Err(io::Error::new(io::ErrorKind::NotFound, "no config directory found")),
48        }
49    }
50
51    /// [`set_update_notice`](Self::set_update_notice) with `config_dir` as the family's folder
52    /// instead of this platform's.
53    ///
54    /// ```
55    /// use qframe::storage::Family;
56    ///
57    /// # let folder = std::env::temp_dir().join(format!("quvyta-update-notice-doc-{}", std::process::id()));
58    /// assert!(Family::QUVYTA.update_notice_in(&folder), "on until someone turns it off");
59    /// Family::QUVYTA.set_update_notice_in(&folder, false).expect("saved");
60    /// assert!(!Family::QUVYTA.update_notice_in(&folder));
61    /// # std::fs::remove_dir_all(&folder).ok();
62    /// ```
63    ///
64    /// # Errors
65    ///
66    /// Any error from writing.
67    pub fn set_update_notice_in(&self, config_dir: &Path, on: bool) -> io::Result<()> {
68        fs::create_dir_all(config_dir)?;
69        let _held = super::preferences::hold_folder(config_dir)?;
70        let shared = config_dir.join(super::family::file_name(self.id()));
71        super::preferences::rewrite(&shared, Settings::UPDATE_NOTICE, SettingValue::Bool(on), None)
72    }
73}
74
75#[cfg(test)]
76mod tests {
77    use crate::i18n::I18n;
78    use crate::storage::Family;
79
80    fn scratch(name: &str) -> std::path::PathBuf {
81        let dir = std::env::temp_dir().join(format!("quvyta-update-notice-{name}-{}", std::process::id()));
82        let _ = std::fs::remove_dir_all(&dir);
83        dir
84    }
85
86    #[test]
87    fn the_notice_is_on_until_the_family_turns_it_off_and_other_keys_stay() {
88        let dir = scratch("switch");
89        let family = Family::QUVYTA;
90        assert!(family.update_notice_in(&dir), "a family that never chose has it");
91        std::fs::create_dir_all(&dir).expect("folder");
92        std::fs::write(dir.join("quvyta.conf"), "theme = \"amber\"\n").expect("shared file");
93        assert!(family.update_notice_in(&dir), "a shared file without the key has it");
94        family.set_update_notice_in(&dir, false).expect("saved");
95        assert!(!family.update_notice_in(&dir));
96        assert!(!family.preferences_in(&dir, "code", &I18n::builtin()).update_notice(), "preferences read it too");
97        let text = std::fs::read_to_string(dir.join("quvyta.conf")).expect("read");
98        assert!(text.contains("theme = \"amber\"") && text.contains("update-notice = false"), "{text}");
99        family.set_update_notice_in(&dir, true).expect("saved");
100        assert!(family.preferences_in(&dir, "code", &I18n::builtin()).update_notice());
101        std::fs::remove_dir_all(dir).ok();
102    }
103}