Skip to main content

io_m2dir/
entry.rs

1//! Entry-level m2dir coroutines and their shared types.
2//!
3//! An entry is a single message file inside an m2dir, named
4//! `<date>,<checksum>.<nonce>` per the specification. This module holds
5//! the [`M2dirEntry`] handle and its companions, shared by the store,
6//! get, list and delete coroutines below.
7
8pub mod delete;
9pub mod get;
10pub mod list;
11pub mod store;
12
13use alloc::{string::String, vec::Vec};
14
15use thiserror::Error;
16
17use crate::{flag::M2dirFlags, path::M2dirPath};
18
19/// Errors that can occur while parsing or validating an entry
20/// filename.
21#[derive(Clone, Debug, Error)]
22pub enum M2dirEntryParseError {
23    /// The given path is not a regular file.
24    #[error("path {0} is not a regular file")]
25    NotFile(M2dirPath),
26    /// The path has no final filename component.
27    #[error("path {0} is missing a filename")]
28    MissingFilename(M2dirPath),
29    /// The filename does not match the m2dir specification.
30    #[error("entry {path} does not match filename spec: {reason}")]
31    InvalidFilename {
32        path: M2dirPath,
33        reason: &'static str,
34    },
35    /// The checksum embedded in the filename does not match the file
36    /// contents.
37    #[error("invalid checksum for {path}: expected {expected:?}, got {got:?}")]
38    InvalidChecksum {
39        path: M2dirPath,
40        expected: String,
41        got: String,
42    },
43}
44
45/// A single entry inside an [`M2dir`](crate::m2dir::M2dir).
46#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
47pub struct M2dirEntry {
48    id: String,
49    path: M2dirPath,
50}
51
52impl M2dirEntry {
53    /// Builds an [`M2dirEntry`] from a path and its unique id without
54    /// checking the on-disk checksum. Used by coroutines that have
55    /// just delivered the entry and trust their own checksum.
56    pub fn from_parts(id: impl Into<String>, path: impl Into<M2dirPath>) -> Self {
57        Self {
58            id: id.into(),
59            path: path.into(),
60        }
61    }
62
63    /// Returns the path to the entry file.
64    pub fn path(&self) -> &M2dirPath {
65        &self.path
66    }
67
68    /// Returns the unique identifier of the entry (the
69    /// `<checksum>.<nonce>` portion of the filename).
70    pub fn id(&self) -> &str {
71        &self.id
72    }
73
74    /// Returns the checksum portion of the id (the chunk before the
75    /// last `.`).
76    pub fn checksum(&self) -> &str {
77        self.id.rsplit_once('.').map(|(c, _)| c).unwrap_or(&self.id)
78    }
79}
80
81/// An [`M2dirEntry`] paired with its file contents and flags metadata.
82///
83/// Produced by the bulk reads on
84/// [`M2dirClient`](crate::client::M2dirClient), which fetch an entry's
85/// bytes and its `.flags` sidecar in one pass.
86#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
87pub struct M2dirFullEntry {
88    entry: M2dirEntry,
89    contents: Vec<u8>,
90    flags: M2dirFlags,
91}
92
93impl M2dirFullEntry {
94    /// Builds a full entry from its resolved handle, file contents and
95    /// flags set.
96    pub fn from_parts(entry: M2dirEntry, contents: Vec<u8>, flags: M2dirFlags) -> Self {
97        Self {
98            entry,
99            contents,
100            flags,
101        }
102    }
103
104    /// Returns the underlying entry handle (id and on-disk path).
105    pub fn entry(&self) -> &M2dirEntry {
106        &self.entry
107    }
108
109    /// Returns the raw bytes read from the entry file.
110    pub fn contents(&self) -> &[u8] {
111        &self.contents
112    }
113
114    /// Returns the flags read from the entry's `.flags` sidecar.
115    pub fn flags(&self) -> &M2dirFlags {
116        &self.flags
117    }
118}