Skip to main content

Ecosystem

Struct Ecosystem 

Source
pub struct Ecosystem { /* private fields */ }
Expand description

An ecosystem of applications that share one settings folder.

The id names the folder and the shared file where names are lowercase by custom, on Linux and other Unix systems; the title names the folder where a user sees it written like a name, on macOS and Windows, and always in the Documents folder. File names are always the lowercase id: quvyta.conf, code.conf.

use qframe::storage::Ecosystem;

let ecosystem = Ecosystem::QUVYTA;
if let (Some(folder), Some(file)) = (ecosystem.config_dir(), ecosystem.app_file("code")) {
    assert_eq!(file, folder.join("code.conf"));
}

Implementations§

Source§

impl Ecosystem

Source

pub const QUVYTA: Ecosystem

The Quvyta ecosystem: ~/.config/quvyta on Linux and other Unix systems, ~/Library/Application Support/Quvyta on macOS, %APPDATA%\Quvyta on Windows.

Source

pub const fn new(id: &'static str, title: &'static str) -> Self

An ecosystem with a lowercase id for folder and file names, such as quvyta, and a title for the places a user reads it as a name, such as Quvyta.

Source

pub fn id(&self) -> &'static str

The lowercase name of the ecosystem’s folder on Linux and other Unix systems and of its shared file.

Source

pub fn title(&self) -> &'static str

The ecosystem’s name as a user reads it.

Source

pub fn config_dir(&self) -> Option<PathBuf>

The folder every settings file of the ecosystem lives in:

  • Linux and other Unix systems: $XDG_CONFIG_HOME/<id> when XDG_CONFIG_HOME is an absolute path, else $HOME/.config/<id>.
  • macOS: $HOME/Library/Application Support/<title>.
  • Windows: %APPDATA%\<title>, the roaming folder.

None when there is no home folder, as for config_dir. The folder is not created.

Source

pub fn shared_file(&self) -> Option<PathBuf>

The settings every application of the ecosystem shares: <config_dir>/<id>.conf, such as quvyta.conf.

Source

pub fn app_file(&self, app: &str) -> Option<PathBuf>

The settings file of application app: <config_dir>/<app>.conf, such as code.conf. app is the application’s lowercase id. An application whose id is the ecosystem’s own would get the shared file, so give it another id.

Source

pub fn app_dir(&self, app: &str) -> Option<PathBuf>

The folder for the other configuration files of application app, next to its settings file: <config_dir>/<app>. Not created.

Source

pub fn state_dir(&self, app: &str) -> Option<PathBuf>

Where application app of the ecosystem keeps its state, such as the result of its last background check: <state folder>/<ecosystem>/<app>.

  • Linux and other Unix systems: $XDG_STATE_HOME/<id>/<app> when XDG_STATE_HOME is an absolute path, else $HOME/.local/state/<id>/<app>.
  • macOS: $HOME/Library/Application Support/<title>/<app>.
  • Windows: %LOCALAPPDATA%\<title>\<app>.

None when there is no home folder, as for state_dir. Not created.

Source

pub fn cache_dir(&self, app: &str) -> Option<PathBuf>

Where application app of the ecosystem keeps files it can rebuild at any time: <cache folder>/<ecosystem>/<app>.

  • Linux and other Unix systems: $XDG_CACHE_HOME/<id>/<app> when XDG_CACHE_HOME is an absolute path, else $HOME/.cache/<id>/<app>.
  • macOS: $HOME/Library/Caches/<title>/<app>.
  • Windows: %LOCALAPPDATA%\<title>\<app>.

None when there is no home folder, as for cache_dir. Not created.

Source

pub fn workspace_dir(&self, app_title: &str) -> Option<PathBuf>

Where the work the user makes with an application is kept by default: <documents>/<ecosystem title>/<app_title>, such as ~/Documents/Quvyta/Code, in the Documents folder under the name the user’s desktop gave it. app_title is the application’s name as the user reads it. None when there is no home folder. Not created.

Source

pub fn adopt(&self, app: &str, legacy_dir: &Path) -> Migration

Moves the settings of application app from the folder it used before it joined the ecosystem into the ecosystem’s layout, once, without losing anything.

legacy_dir/settings.toml becomes app_file; every other file under legacy_dir, at any depth, goes to the same place under app_dir. When legacy_dir already is the application’s folder, only settings.toml moves and the other files stay where they are. Call it at start, before Settings::load_member.

Every file is copied first, with its permissions, then read back and compared, and only then removed from the old place. A file whose new place is already taken stays where it is, and so do symbolic links, which are never followed; nothing is overwritten or merged. Old folders left empty are removed, from the deepest up; a folder with anything left in it is kept. A missing legacy_dir is nothing to do, so calling it again after a finished move changes nothing. Whatever stayed behind is in the report, with the reason.

A crash in the middle of a move can leave the new file empty next to the old one; the old one is still whole, and the next call reports the pair instead of choosing between them.

Source

pub fn adopt_in( &self, config_dir: &Path, app: &str, legacy_dir: &Path, ) -> Migration

adopt into config_dir as the ecosystem’s folder instead of this platform’s, for a test or a demo that must leave the user’s own settings alone: the settings become <config_dir>/<app>.conf and the other files move under <config_dir>/<app>.

Source§

impl Ecosystem

Source

pub fn members(&self) -> &'static [Member]

Every member of the ecosystem, in the order a launcher lists them: MEMBERS for Ecosystem::QUVYTA, nothing for an ecosystem the framework does not know the members of.

Source§

impl Ecosystem

Source

pub fn preferences(&self, app: &str, i18n: &I18n) -> Preferences

Resolves the shared preferences of application app: for each of language, theme, icons and reduced motion, the application’s own value when its file names one other than the ecosystem’s id, else the value in the shared file, else the value detected on this machine. A key missing from the application’s file follows the ecosystem, as the ecosystem’s id does, so a file written by hand before the ecosystem shared anything follows it too.

Detection reads the environment: the language as I18n::detect finds it among the languages i18n knows (English when it knows none of the system’s), the theme always monochrome, the icons as the strongest set the terminal and the installed fonts allow (detect_glyph_mode), reduced motion always off.

When the shared file does not exist it is created with the detected values, so the next application that starts finds them. A broken line never stops anything: that key falls back to the detected value and the reason, located at file, line and column, is in Preferences::diagnostics. Without a home folder nothing is read or written and every value is detected.

The rest of the application’s settings are read as before, with Settings::load_member; this only resolves the keys Shared names.

Source

pub fn preferences_in( &self, config_dir: &Path, app: &str, i18n: &I18n, ) -> Preferences

preferences with config_dir as the ecosystem’s folder instead of this platform’s, for a test or a demo that must leave the user’s own files alone.

Source

pub fn preferences_without_saving(&self, app: &str, i18n: &I18n) -> Preferences

preferences without writing anything: a missing shared file is left missing and its keys are detected instead.

For an application whose first start shows a setup wizard: a wizard closed half-way leaves the user’s settings folder as empty as it found it, and the wizard’s Finish writes both files. An application without a wizard uses preferences, so the first application to start leaves the shared file for the next one.

Source

pub fn preferences_without_saving_in( &self, config_dir: &Path, app: &str, i18n: &I18n, ) -> Preferences

preferences_without_saving with config_dir as the ecosystem’s folder instead of this platform’s, for a test or a demo.

Source

pub fn set( &self, app: &str, key: Shared, value: &str, scope: Scope, ) -> Result<()>

Changes shared preference key of application app to value, for the whole ecosystem or for the application alone:

scopeshared fileapplication’s file
Scope::Ecosystemkey = valuekey = "<ecosystem id>"
Scope::Appunchangedkey = value

Each file is read from disk right before it is written and only key changes in it, so two applications changing preferences at the same time both keep their change instead of one writing back what it read earlier. On Unix systems the ecosystem’s folder is held with an advisory lock from the reading to the writing, so even two changes in the same instant follow one another; elsewhere the window between them is a few microseconds. Files are written with atomic_write; the other keys stay as they were, though comments do not survive, as with Settings::save. The running application is not switched; use Preferences::apply or the matching Command for that.

§Errors

Returns an error of kind io::ErrorKind::InvalidInput when value is not a valid value of key (an unknown icon mode, the ecosystem’s own id, an empty text), of kind io::ErrorKind::NotFound when there is no home folder, and any error from writing.

Source

pub fn set_in( &self, config_dir: &Path, app: &str, key: Shared, value: &str, scope: Scope, ) -> Result<()>

set with config_dir as the ecosystem’s folder instead of this platform’s.

§Errors

As set, except that there is always a folder.

Source

pub fn follow(&self, app: &str, key: Shared) -> Result<()>

Puts application app back on the ecosystem’s value of key: its own file says the ecosystem’s id and the shared file is neither read nor written, so the next resolution answers the shared value with Source::Ecosystem and no other application changes. The one way back from a value of an application’s own, for the settings screen that lists every member of the ecosystem: “follow the shared setting” on one member’s cell must not change what the whole ecosystem draws with.

The file is read from disk right before it is written and only key changes in it, as set does, with the ecosystem’s folder held by an advisory lock on Unix. A missing file is created holding that one key. A key that already follows the ecosystem is left alone, file and all. Comments do not survive a change, as with Settings::save. The running application is not switched; use Preferences::apply for that.

§Errors

Returns an error of kind io::ErrorKind::NotFound when there is no home folder, of kind io::ErrorKind::InvalidData when the application’s file cannot be read as settings, and any error from writing. A file that could not be read is left exactly as it was.

Source

pub fn follow_in(&self, config_dir: &Path, app: &str, key: Shared) -> Result<()>

follow with config_dir as the ecosystem’s folder instead of this platform’s, for a test or a demo that must leave the user’s own files alone.

use qframe::storage::{Ecosystem, Shared};

std::fs::write(folder.join("code.conf"), "theme = \"amber\"\n").expect("the file");
Ecosystem::QUVYTA.follow_in(&folder, "code", Shared::Theme).expect("follow");
assert_eq!(std::fs::read_to_string(folder.join("code.conf")).expect("read"), "theme = \"quvyta\"\n");
assert!(!folder.join("quvyta.conf").exists(), "the shared file is left alone");
§Errors

As follow, except that there is always a folder.

Source§

impl Ecosystem

Source

pub fn settle(&self, app: &str) -> Result<Vec<Shared>>

Looks once at the shared keys application app wrote into its own file as fixed values, and makes the application follow the ecosystem again where nothing is lost by it.

For an application that used to write a choice meant for every member into its own file alone. For each of language, theme, icons and reduced motion that its file names as a value of its own:

  • the same value as the shared file holds: the key becomes the ecosystem’s id, so the application follows the shared value from now on;
  • another value: it stays, since it is what the person chose for this application.

Reduced motion was each application’s own before it was shared, so a shared file may not hold it yet; it then counts as off, the value every application reads from such a file. An application that kept motion follows the ecosystem again, one that reduced it keeps that.

Then the file takes Settings::SHARED_CHECKED = true and every later call changes nothing. The mark sits in the application’s own file, beside the values it speaks for, so it travels with them when the settings folder is copied to another machine. Without it, a person who later chose “only here” for the value the ecosystem happens to share would find the choice undone at the next start. A missing file is created holding the mark alone: an application that starts without one writes its shared keys the new way, and nothing it writes afterwards is a leftover to look at. A Setup still counts such a file as no settings at all.

Call it at start, before the application’s settings are loaded (Settings::load_member), so what it loads already holds the change; a copy loaded earlier would save the old values back. The file is read right before it is written and only the changed keys and the mark change in it, with the ecosystem’s folder held by an advisory lock on Unix, as set does. Comments do not survive a change, as with Settings::save. The shared file is only read.

Returns the keys that now follow the ecosystem, in the order of Shared::ALL; empty when nothing changed or the look was already taken.

§Errors

Returns an error of kind io::ErrorKind::NotFound when there is no home folder, of kind io::ErrorKind::InvalidData when the application’s file cannot be read as settings, which is then left exactly as it was, and any error from writing.

Source

pub fn settle_in(&self, config_dir: &Path, app: &str) -> Result<Vec<Shared>>

settle with config_dir as the ecosystem’s folder instead of this platform’s, for a test or a demo that must leave the user’s own files alone.

use qframe::storage::{Ecosystem, Shared};

std::fs::write(folder.join("quvyta.conf"), "theme = \"nordic\"\nlanguage = \"tr\"\n").expect("shared");
std::fs::write(folder.join("desktop.conf"), "theme = \"nordic\"\nlanguage = \"en\"\n").expect("own");
let now_following = Ecosystem::QUVYTA.settle_in(&folder, "desktop").expect("settle");
assert_eq!(now_following, [Shared::Theme]);
let own = std::fs::read_to_string(folder.join("desktop.conf")).expect("read");
assert_eq!(own, "theme = \"quvyta\"\nlanguage = \"en\"\nshared-checked = true\n");
§Errors

As settle, except that there is always a folder.

Source§

impl Ecosystem

Source

pub fn update_notice(&self) -> bool

Whether the ecosystem’s applications say when a newer version is out: the shared file’s update-notice, on when the file or the key is missing. Off without a home folder, where nothing could remember that it was asked.

Source

pub fn update_notice_in(&self, config_dir: &Path) -> bool

update_notice with config_dir as the ecosystem’s folder instead of this platform’s, for a test or a demo.

Source

pub fn set_update_notice(&self, on: bool) -> Result<()>

Turns the update notice on or off for every application of the ecosystem, in the shared file. The file is read right before it is written and only this key changes in it, as set does.

§Errors

Returns an error of kind io::ErrorKind::NotFound when there is no home folder, and any error from writing.

Source

pub fn set_update_notice_in(&self, config_dir: &Path, on: bool) -> Result<()>

set_update_notice with config_dir as the ecosystem’s folder instead of this platform’s.

use qframe::storage::Ecosystem;

assert!(Ecosystem::QUVYTA.update_notice_in(&folder), "on until someone turns it off");
Ecosystem::QUVYTA.set_update_notice_in(&folder, false).expect("saved");
assert!(!Ecosystem::QUVYTA.update_notice_in(&folder));
§Errors

Any error from writing.

Trait Implementations§

Source§

impl Clone for Ecosystem

Source§

fn clone(&self) -> Self

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Copy for Ecosystem

Source§

impl Debug for Ecosystem

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Eq for Ecosystem

Source§

impl PartialEq for Ecosystem

Source§

fn eq(&self, other: &Self) -> bool

Equality operator ==. Read more
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Inequality operator !=. Read more
Source§

impl StructuralPartialEq for Ecosystem

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<Q, K> Equivalent<K> for Q
where Q: Eq + ?Sized, K: Borrow<Q> + ?Sized,

Source§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.