DirStorage

Struct DirStorage 

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

Directory-based entity storage with ACID guarantees and automatic migrations.

Manages one file per entity, providing:

  • Atomicity: Updates are all-or-nothing via tmp file + atomic rename
  • Consistency: Format validation on load/save
  • Isolation: Each entity has its own file
  • Durability: Explicit fsync before rename

Implementations§

Source§

impl DirStorage

Source

pub fn new( paths: AppPaths, domain_name: &str, migrator: Migrator, strategy: DirStorageStrategy, ) -> Result<Self, MigrationError>

Create a new DirStorage instance.

§Arguments
  • paths - Application paths manager
  • domain_name - Domain-specific subdirectory name (e.g., “sessions”, “tasks”)
  • migrator - Migrator instance with registered migration paths
  • strategy - Storage strategy configuration
§Behavior
  • Resolves the base path using paths.data_dir()?.join(domain_name)
  • Creates the directory if it doesn’t exist
  • Does not load existing files (lazy loading)
§Errors

Returns MigrationError::IoError if directory creation fails.

§Example
let paths = AppPaths::new("myapp");
let storage = DirStorage::new(
    paths,
    "sessions",
    migrator,
    DirStorageStrategy::default(),
)?;
Source

pub fn save<T>( &self, entity_name: &str, id: &str, entity: T, ) -> Result<(), MigrationError>
where T: Serialize,

Save an entity to a file.

§Arguments
  • entity_name - The entity name registered in the migrator
  • id - The unique identifier for this entity (used as filename)
  • entity - The entity to save
§Process
  1. Converts the entity to its latest versioned DTO
  2. Serializes to the configured format (JSON/TOML)
  3. Writes atomically using temporary file + rename
§Errors

Returns error if:

  • Entity name not registered in migrator
  • ID contains invalid characters (for Direct encoding)
  • Serialization fails
  • File write fails
§Example
let session = SessionEntity {
    id: "session-123".to_string(),
    user_id: "user-456".to_string(),
};
storage.save("session", "session-123", session)?;
Source

pub fn load<D>(&self, entity_name: &str, id: &str) -> Result<D, MigrationError>

Load an entity from a file.

§Arguments
  • entity_name - The entity name registered in the migrator
  • id - The unique identifier for the entity
§Process
  1. Gets the file path using id_to_path
  2. Reads the file content to a string
  3. Deserializes the content to a serde_json::Value
  4. Migrates the Value to the target domain type
§Errors

Returns error if:

  • Entity name not registered in migrator
  • File not found
  • Deserialization fails
  • Migration fails
§Example
let session: SessionEntity = storage.load("session", "session-123")?;
Source

pub fn list_ids(&self) -> Result<Vec<String>, MigrationError>

List all entity IDs in the storage directory.

§Returns

A sorted vector of entity IDs (decoded from filenames).

§Errors

Returns error if:

  • Directory read fails
  • Filename decoding fails
§Example
let ids = storage.list_ids()?;
for id in ids {
    println!("Found entity: {}", id);
}
Source

pub fn load_all<D>( &self, entity_name: &str, ) -> Result<Vec<(String, D)>, MigrationError>

Load all entities from the storage directory.

§Arguments
  • entity_name - The entity name registered in the migrator
§Returns

A vector of (id, entity) tuples.

§Errors

Returns error if any entity fails to load. This operation is atomic: if any load fails, the whole operation fails.

§Example
let sessions: Vec<(String, SessionEntity)> = storage.load_all("session")?;
for (id, session) in sessions {
    println!("Loaded session {} for user {}", id, session.user_id);
}
Source

pub fn exists(&self, id: &str) -> Result<bool, MigrationError>

Check if an entity exists.

§Arguments
  • id - The entity ID
§Returns

true if the file exists and is a file, false otherwise.

§Example
if storage.exists("session-123")? {
    println!("Session exists");
}
Source

pub fn delete(&self, id: &str) -> Result<(), MigrationError>

Delete an entity file.

§Arguments
  • id - The entity ID
§Behavior

This operation is idempotent: deleting a non-existent file is not an error.

§Errors

Returns error if file deletion fails (but not if file doesn’t exist).

§Example
storage.delete("session-123")?;
Source

pub fn base_path(&self) -> &Path

Returns a reference to the base directory path.

§Returns

A reference to the resolved base directory path where entities are stored.

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