loonfs-api 0.3.1

Wire types and durable-format codecs for LoonFS.
Documentation
//! Per-operation options shared by the embedded runtime and HTTP client.

use crate::{
    AccessGrants, AccessRevisionNo, ActorId, AttributeKey, AttributeRevisionNo, AttributeValue,
    CommitId, CommitPrecondition, DeleteDirectoryBehavior, DestinationBehavior, InodeId,
    RevisionNo,
};
use crate::{SnapshotId, Subject};
use std::collections::BTreeMap;

/// Whether a path or inode read includes the attribute projection.
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub enum AttributeInclusion {
    /// Include the inode's attribute map and revision.
    Include,
    /// Omit the inode's attribute map and revision.
    #[default]
    Omit,
}

impl std::fmt::Display for AttributeInclusion {
    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        formatter.write_str(match self {
            Self::Include => "true",
            Self::Omit => "false",
        })
    }
}

/// Commit settings shared by every filesystem mutation.
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[serde(deny_unknown_fields)]
pub struct CommitOptions {
    /// Actor responsible for the commit, as supplied by the application.
    pub actor_id: ActorId,
    /// The subject whose grants authorize the commit.
    #[serde(skip)]
    pub subject: Option<Subject>,
    /// The optional idempotency key, generated by LoonFS when absent.
    pub commit_id: Option<CommitId>,
    /// The optional commit message that forms part of the commit identity.
    pub message: Option<String>,
    /// Ordered admission conditions evaluated before any operations.
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub preconditions: Vec<CommitPrecondition>,
}

impl CommitOptions {
    /// Creates settings with no commit ID or message.
    pub fn new(actor: ActorId) -> Self {
        Self {
            actor_id: actor,
            subject: None,
            commit_id: None,
            message: None,
            preconditions: Vec::new(),
        }
    }
}

/// Options for stating one path.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct StatPathOptions {
    /// Whether to include the inode's attribute map and revision, enabled by default.
    pub include_attributes: AttributeInclusion,
    /// Read the entry from this snapshot.
    pub snapshot_id: Option<SnapshotId>,
}

impl Default for StatPathOptions {
    fn default() -> Self {
        Self {
            include_attributes: AttributeInclusion::Include,
            snapshot_id: None,
        }
    }
}

/// Options for listing a directory.
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct ListPathEntriesOptions {
    /// Whether to include each entry's attribute map and revision, disabled by default.
    pub include_attributes: AttributeInclusion,
    /// Read the directory from this snapshot.
    pub snapshot_id: Option<SnapshotId>,
}

/// Options for listing a directory's children by parent inode.
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct ListInodeChildrenOptions {
    /// Whether to include each entry's attribute map and revision, disabled by default.
    pub include_attributes: AttributeInclusion,
    /// Read the directory from this snapshot.
    pub snapshot_id: Option<SnapshotId>,
}

/// Options for writing and removing an inode's attributes.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct UpdateAttributesOptions {
    /// The attributes to write, replacing values for matching keys and leaving other
    /// keys unchanged.
    pub set: BTreeMap<AttributeKey, AttributeValue>,
    /// Keys to remove.
    pub remove: Vec<AttributeKey>,
    /// Actor, commit ID, and message.
    pub commit: CommitOptions,
    /// The inode that the path must still resolve to before the update.
    pub expected_inode_id: Option<InodeId>,
    /// With an inode precondition, the attribute revision that must still be current.
    pub expected_attributes_revision_no: Option<AttributeRevisionNo>,
}

impl UpdateAttributesOptions {
    /// Creates an empty attribute update for this actor.
    pub fn new(actor: ActorId) -> Self {
        Self {
            set: BTreeMap::new(),
            remove: Vec::new(),
            commit: CommitOptions::new(actor),
            expected_inode_id: None,
            expected_attributes_revision_no: None,
        }
    }
}

/// Options for replacing an inode's access row.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct UpdateAccessOptions {
    /// Whether the directory stops inheritance from its ancestors.
    pub boundary: bool,
    /// The complete direct grants after the update.
    pub grants: AccessGrants,
    /// Actor, commit ID, and message.
    pub commit: CommitOptions,
    /// The inode that the path must still resolve to before the update.
    pub expected_inode_id: Option<InodeId>,
    /// With an inode precondition, the access revision that must still be current.
    pub expected_access_revision_no: Option<AccessRevisionNo>,
}

impl UpdateAccessOptions {
    /// An update that replaces the grants and clears the boundary.
    pub fn new(actor: ActorId, grants: AccessGrants) -> Self {
        Self {
            boundary: false,
            grants,
            commit: CommitOptions::new(actor),
            expected_inode_id: None,
            expected_access_revision_no: None,
        }
    }
}

/// Options for writing a file path.
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[serde(deny_unknown_fields)]
pub struct PutFileOptions {
    /// Create-only or replace-existing behavior.
    pub behavior: DestinationBehavior,
    /// Actor, commit ID, and message.
    pub commit: CommitOptions,
    /// The inode that the path must still reference when using `Replace` behavior.
    pub expected_inode_id: Option<InodeId>,
    /// The revision that must still be current when using `Replace` behavior with
    /// `expected_inode_id`.
    pub expected_revision_no: Option<RevisionNo>,
}

impl PutFileOptions {
    /// Creates options that refuse to replace an existing file.
    pub fn new(actor: ActorId) -> Self {
        Self {
            behavior: DestinationBehavior::NoReplace,
            commit: CommitOptions::new(actor),
            expected_inode_id: None,
            expected_revision_no: None,
        }
    }
}

/// Options for creating a directory.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CreateDirectoryOptions {
    /// Actor, commit ID, and message.
    pub commit: CommitOptions,
    /// Also create missing ancestor directories, like `put_file` does.
    pub parents: bool,
}

impl CreateDirectoryOptions {
    /// Creates options that do not create missing parent directories.
    pub fn new(actor: ActorId) -> Self {
        Self {
            commit: CommitOptions::new(actor),
            parents: false,
        }
    }
}

/// Options for deleting a path.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DeleteOptions {
    /// Directory delete behavior.
    pub behavior: DeleteDirectoryBehavior,
    /// Actor, commit ID, and message.
    pub commit: CommitOptions,
    /// The inode that the path must still resolve to before deletion.
    pub expected_inode_id: Option<InodeId>,
}

impl DeleteOptions {
    /// Creates options for a non-recursive delete.
    pub fn new(actor: ActorId) -> Self {
        Self {
            behavior: DeleteDirectoryBehavior::NonRecursive,
            commit: CommitOptions::new(actor),
            expected_inode_id: None,
        }
    }
}

/// Options for moving a path.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MoveOptions {
    /// Create-only or replace-existing behavior for the destination.
    pub behavior: DestinationBehavior,
    /// Actor, commit ID, and message.
    pub commit: CommitOptions,
    /// The inode that the destination must still reference when using `Replace` behavior.
    pub expected_destination_inode_id: Option<InodeId>,
    /// The revision that must still be current when using `Replace` behavior with
    /// `expected_destination_inode_id`.
    pub expected_destination_revision_no: Option<RevisionNo>,
}

impl MoveOptions {
    /// Creates options that refuse to replace the destination.
    pub fn new(actor: ActorId) -> Self {
        Self {
            behavior: DestinationBehavior::NoReplace,
            commit: CommitOptions::new(actor),
            expected_destination_inode_id: None,
            expected_destination_revision_no: None,
        }
    }
}

/// Options for copying a file path.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CopyOptions {
    /// Create-only or replace-existing behavior for the destination.
    pub behavior: DestinationBehavior,
    /// Actor, commit ID, and message.
    pub commit: CommitOptions,
    /// The inode that the destination must still reference when using `Replace` behavior.
    pub expected_destination_inode_id: Option<InodeId>,
    /// The revision that must still be current when using `Replace` behavior with
    /// `expected_destination_inode_id`.
    pub expected_destination_revision_no: Option<RevisionNo>,
}

impl CopyOptions {
    /// Creates options that refuse to replace the destination.
    pub fn new(actor: ActorId) -> Self {
        Self {
            behavior: DestinationBehavior::NoReplace,
            commit: CommitOptions::new(actor),
            expected_destination_inode_id: None,
            expected_destination_revision_no: None,
        }
    }
}

/// Options for restoring a file revision by path.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RestoreRevisionOptions {
    /// Actor, commit ID, and message.
    pub commit: CommitOptions,
}

impl RestoreRevisionOptions {
    /// Creates restore options for this actor.
    pub fn new(actor: ActorId) -> Self {
        Self {
            commit: CommitOptions::new(actor),
        }
    }
}

/// Options for recovering a deleted file or subtree.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct UndeleteOptions {
    /// Actor, commit ID, and message.
    pub commit: CommitOptions,
}

impl UndeleteOptions {
    /// Creates undelete options for this actor.
    pub fn new(actor: ActorId) -> Self {
        Self {
            commit: CommitOptions::new(actor),
        }
    }
}

/// Options for starting a direct multipart upload.
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct DirectMultipartUploadOptions {
    /// The byte length of every part except the last, or `None` for the server
    /// default; providers allow at most 10,000 parts.
    pub part_size_bytes: Option<u64>,
}

/// Selects the source state for a namespace fork.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ForkNamespaceOptions {
    /// Application-supplied actor creating the namespace.
    pub actor_id: ActorId,
    /// Fork from this live snapshot instead of the current head.
    pub snapshot_id: Option<crate::SnapshotId>,
}

impl ForkNamespaceOptions {
    /// Selects the current head.
    pub fn new(actor_id: ActorId) -> Self {
        Self {
            actor_id,
            snapshot_id: None,
        }
    }
}