Skip to main content

qframe/storage/
member.rs

1//! The members of an ecosystem: what every application of it is called, where it keeps its
2//! settings and how it is installed and started.
3
4use super::Ecosystem;
5
6/// One application of an ecosystem, as every other member needs to know it: a launcher that
7/// lists and opens it, a settings screen that shows whether it follows the shared preferences.
8///
9/// Only what every member needs is here; how a launcher draws a member (its icon, its words, its
10/// release status) stays in the launcher.
11///
12/// ```
13/// use qframe::storage::Ecosystem;
14///
15/// let ecosystem = Ecosystem::QUVYTA;
16/// let desk = ecosystem.members().iter().find(|member| member.id == "desk").expect("qdesk is a member");
17/// assert_eq!((desk.settings_id, desk.command), ("desktop", "qdesk"));
18/// if let (Some(folder), Some(file)) = (ecosystem.config_dir(), ecosystem.app_file(desk.settings_id)) {
19///     assert_eq!(file, folder.join("desktop.conf"));
20/// }
21/// ```
22#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
23pub struct Member {
24    /// The member's lowercase name inside the ecosystem, such as `code` or `desk`: the name its
25    /// project goes by. Not always the id of its settings file.
26    pub id: &'static str,
27    /// The id its settings file goes under in the ecosystem's folder, `<settings_id>.conf`: the
28    /// `app` the member itself gives [`Settings::load_member`](super::Settings::load_member) and
29    /// [`Ecosystem::preferences`]. qdesk's is `desktop`, the showcase's `showcase` and the
30    /// launcher's `launcher`, since the ecosystem's own id names the file every member shares.
31    pub settings_id: &'static str,
32    /// The member's name as a user reads it, such as `Code`: the name under which its work is
33    /// kept, [`Ecosystem::workspace_dir`].
34    pub title: &'static str,
35    /// The package it is installed as, such as `quvyta-code`.
36    pub package: &'static str,
37    /// The short command that starts it, such as `qcode`. The package name starts it too.
38    pub command: &'static str,
39}
40
41/// Every member of [`Ecosystem::QUVYTA`], the applications first and then the framework's
42/// showcase and the launcher, which are about the ecosystem rather than applications to work in.
43/// The framework itself is a library, so its showcase stands for it.
44pub const MEMBERS: [Member; 10] = [
45    member("code", "code", "Code", "quvyta-code", "qcode"),
46    member("focus", "focus", "Focus", "quvyta-focus", "qfocus"),
47    member("tools", "tools", "Tools", "quvyta-tools", "qtools"),
48    member("packages", "packages", "Packages", "quvyta-packages", "qpac"),
49    member("desk", "desktop", "Desktop", "quvyta-desktop", "qdesk"),
50    member("explorer", "explorer", "Explorer", "quvyta-explorer", "qexp"),
51    member("cli", "cli", "CLI", "quvyta-cli", "qcli"),
52    member("browser", "browser", "Browser", "quvyta-browser", "qbrow"),
53    member("framework", "showcase", "Framework", "quvyta-framework-showcase", "qframe"),
54    member("quvyta", "launcher", "Quvyta", "quvyta", "quvyta"),
55];
56
57const fn member(
58    id: &'static str,
59    settings_id: &'static str,
60    title: &'static str,
61    package: &'static str,
62    command: &'static str,
63) -> Member {
64    Member { id, settings_id, title, package, command }
65}
66
67impl Ecosystem {
68    /// Every member of the ecosystem, in the order a launcher lists them: [`MEMBERS`] for
69    /// [`Ecosystem::QUVYTA`], nothing for an ecosystem the framework does not know the members of.
70    #[must_use]
71    pub fn members(&self) -> &'static [Member] {
72        if *self == Self::QUVYTA { &MEMBERS } else { &[] }
73    }
74}
75
76#[cfg(test)]
77mod tests {
78    use super::*;
79
80    #[test]
81    fn every_member_has_a_settings_file_of_its_own() {
82        let members = Ecosystem::QUVYTA.members();
83        for (index, member) in members.iter().enumerate() {
84            let before = &members[..index];
85            assert!(!before.iter().any(|other| other.settings_id == member.settings_id), "{member:?}");
86            assert!(!before.iter().any(|other| other.id == member.id), "{member:?}");
87            assert!(!before.iter().any(|other| other.command == member.command), "{member:?}");
88            assert_ne!(member.settings_id, Ecosystem::QUVYTA.id(), "`quvyta.conf` is the file every member shares");
89        }
90    }
91
92    #[test]
93    fn qdesk_keeps_its_settings_in_desktop_conf() {
94        let desk = MEMBERS.iter().find(|member| member.command == "qdesk").expect("qdesk");
95        assert_eq!((desk.id, desk.settings_id, desk.package), ("desk", "desktop", "quvyta-desktop"));
96        let launcher = MEMBERS.iter().find(|member| member.package == "quvyta").expect("the launcher");
97        assert_eq!(launcher.settings_id, "launcher");
98    }
99
100    #[test]
101    fn only_the_quvyta_ecosystem_has_members() {
102        assert_eq!(Ecosystem::QUVYTA.members().len(), MEMBERS.len());
103        assert!(Ecosystem::new("tools", "Tools").members().is_empty());
104    }
105}