SettingsManager

Struct SettingsManager 

Source
pub struct SettingsManager {
    settings_dir: PathBuf,
    manifest_path: PathBuf,
    current_version: String,
    build_uuid: String,
    root_dir: PathBuf,
}
Expand description

Manages versioned settings files and migrations.

Fields§

§settings_dir: PathBuf§manifest_path: PathBuf§current_version: String§build_uuid: String§root_dir: PathBuf

Implementations§

Source§

impl SettingsManager

Source

pub fn new(current_version: String) -> Self

Creates a new settings manager.

§Arguments
  • current_version - The current application version (e.g., from GIT_VERSION)

The build UUID is automatically obtained from the compile-time BUILD_UUID environment variable set by the core crate’s build script.

§Example
use cadmus_core::settings::versioned::SettingsManager;

let manager = SettingsManager::new(env!("GIT_VERSION").to_string());
Source

pub fn load(&self) -> Settings

Loads settings from the versioned storage, migrating if necessary.

This function is designed to be maximally resilient:

  1. Creates Settings directory if it doesn’t exist
  2. Attempts to migrate legacy Settings.toml if it exists (non-fatal if it fails)
  3. Reads the manifest to find the appropriate settings file
  4. Loads and deserializes the settings

The manifest is searched for an entry matching the current version. If no exact match exists, the entry with the most recent UUID is used. If the manifest is empty, default settings are returned.

§Returns

Returns Settings in all cases:

  • Loaded from versioned file if available
  • Loaded from most recent version if exact match not found
  • Default settings if no versions exist or all file reads fail

Never fails - returns defaults as ultimate fallback.

§Diagnostic Output

This function uses println! and eprintln! for diagnostic messages instead of tracing::* macros because logging/tracing is not yet configured at this point in the app startup sequence. Tracing is configured after settings are loaded, so using tracing macros here would result in messages being silently dropped or not properly routed.

Source

pub fn save(&self, settings: &Settings) -> Result<(), Error>

Saves settings to a versioned file and updates the manifest.

This function:

  1. Creates a new Settings-.toml file
  2. Updates the manifest with new entry
  3. Removes old files exceeding retention limit
§Arguments
  • settings - The settings to save
§Errors

Returns an error if:

  • Settings file cannot be written
  • Manifest cannot be updated
  • Old files cannot be removed
Source

fn migrate_legacy_settings(&self)

Migrates legacy Settings.toml from the root directory to the new versioned format.

This method is automatically called during load() to handle upgrades from the old settings system. If a legacy Settings.toml file exists in the application’s root directory, it is:

  1. Loaded with all existing settings preserved
  2. Saved to the new versioned location: Settings/Settings-v<version>.toml
  3. Registered in the manifest as a historical entry
  4. Deleted to prevent accidental duplication
§Behavior

This method is fully non-fatal:

  • If the legacy file doesn’t exist, returns silently (success)
  • If the legacy file can’t be read, logs a warning and returns (failure is acceptable)
  • If any write operation fails (save, manifest update, deletion), logs a warning but continues - the important part is reading the legacy settings, not cleanup

Never propagates errors because migration is opportunistic, not required for the app to function.

§Diagnostic Output

This function uses println! and eprintln! for diagnostic messages instead of tracing::* macros because logging/tracing is not yet configured during the settings loading phase (called from load()). Tracing is initialized after settings are fully loaded, so any tracing calls here would be silently discarded.

Source

fn read_manifest(&self) -> Result<SettingsManifest, Error>

Reads the settings manifest from disk.

The manifest file (.cadmus-index.toml) tracks all known settings versions and their metadata. This method loads the current manifest or returns a default empty manifest if the file doesn’t exist.

§Returns

Ok(SettingsManifest) containing:

  • All known settings file entries in order
  • Version information and timestamps for each entry

Err if the manifest file exists but cannot be read or parsed.

Source

fn write_manifest(&self, manifest: &SettingsManifest) -> Result<(), Error>

Writes the settings manifest to disk.

Persists the manifest file (.cadmus-index.toml) with all settings version entries and their metadata. This is called after any changes to the manifest (migration, version updates, cleanup).

§Arguments
  • manifest - The manifest to write to disk
§Returns

Ok(()) if the manifest was successfully written.

Err if the manifest file cannot be written or serialized.

Source

fn update_manifest_and_cleanup( &self, filename: &str, settings: &Settings, ) -> Result<(), Error>

Updates the manifest with a new settings version entry and cleans up old files.

This is called during save() when settings are persisted to a new versioned file. Combines manifest update and cleanup into a single I/O pass to minimize filesystem operations on embedded devices with slow storage.

The process:

  1. Reads the current manifest (single read)
  2. Creates a new entry for the current version with timestamp
  3. Removes any existing entry for the same version (deduplication)
  4. Appends the new entry
  5. Partitions entries to protect current version from cleanup
  6. Removes old files exceeding retention limit (only from other versions)
  7. Writes the updated manifest once (single write)
§Data Integrity

The current version’s entry is never removed, regardless of its UUID. This prevents silent data loss during version downgrades where an older build UUID would sort to the front and be considered “oldest” for cleanup purposes.

§Arguments
  • filename - The filename of the new settings file (relative to Settings directory)
  • settings - The settings containing the settings_retention configuration
§Returns

Ok(()) if the manifest was successfully updated and written.

Err if reading, updating, or writing the manifest fails.

Trait Implementations§

Source§

impl Clone for SettingsManager

Source§

fn clone(&self) -> SettingsManager

Returns a duplicate of the value. Read more
1.0.0 · Source§

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

Performs copy-assignment from source. Read more

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
§

impl<T> Downcast for T
where T: Any,

§

fn into_any(self: Box<T>) -> Box<dyn Any>

Converts Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>, which can then be downcast into Box<dyn ConcreteType> where ConcreteType implements Trait.
§

fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>

Converts Rc<Trait> (where Trait: Downcast) to Rc<Any>, which can then be further downcast into Rc<ConcreteType> where ConcreteType implements Trait.
§

fn as_any(&self) -> &(dyn Any + 'static)

Converts &Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot generate &Any’s vtable from &Trait’s.
§

fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)

Converts &mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot generate &mut Any’s vtable from &mut Trait’s.
§

impl<T> DowncastSend for T
where T: Any + Send,

§

fn into_any_send(self: Box<T>) -> Box<dyn Any + Send>

Converts Box<Trait> (where Trait: DowncastSend) to Box<dyn Any + Send>, which can then be downcast into Box<ConcreteType> where ConcreteType implements Trait.
§

impl<T> DowncastSync for T
where T: Any + Send + Sync,

§

fn into_any_sync(self: Box<T>) -> Box<dyn Any + Send + Sync>

Converts Box<Trait> (where Trait: DowncastSync) to Box<dyn Any + Send + Sync>, which can then be downcast into Box<ConcreteType> where ConcreteType implements Trait.
§

fn into_any_arc(self: Arc<T>) -> Arc<dyn Any + Send + Sync>

Converts Arc<Trait> (where Trait: DowncastSync) to Arc<Any>, which can then be downcast into Arc<ConcreteType> where ConcreteType implements Trait.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
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.

§

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

§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns [Action::Follow] only if self and other return Action::Follow. Read more
§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns [Action::Follow] if either self or other returns Action::Follow. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
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 = Infallible

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

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

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.
§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a [WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a [WithDispatch] wrapper. Read more