cartulary 0.3.0-alpha.1

The knowledge layer of your project — decisions, issues, docs, all in one place.
Documentation
//! Opaque identifier for where a loaded entry came from.
//!
//! Adapters build it from whatever shape they happen to use to
//! address an entry: a filesystem path, a primary key, a git object
//! id, … The domain treats the value as a black box (no parsing, no
//! dispatching) and displays it verbatim.
//!
//! # URI convention
//!
//! Adapters follow a URI-like scheme so the rendered value carries
//! its provenance to a human reader. The scheme is convention only —
//! the domain does not parse it.
//!
//! | Scheme       | Meaning                                                 |
//! |--------------|---------------------------------------------------------|
//! | `memory://`  | constructed in process memory, not (yet) persisted      |
//! | `file://`    | filesystem-backed (see `FsIssueRepository`)             |
//! | `git://`     | reserved for a future git adapter                       |
//! | `sql://`     | reserved for a future SQL adapter                       |
//!
//! [`EntryLocator::default()`] yields `memory://`, used by every
//! in-memory constructor (use-case create paths, test fixtures, …).
//! Adapters override it at load time.

use std::fmt;

#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct EntryLocator(String);

impl serde::Serialize for EntryLocator {
    fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
        s.serialize_str(&self.0)
    }
}

impl EntryLocator {
    pub fn new(value: impl Into<String>) -> Self {
        Self(value.into())
    }

    /// Locator for an entry that exists only in process memory:
    /// a value built by a constructor, a fixture, or a use-case
    /// before any persistence happens. Adapters overwrite this
    /// value when they materialise the entry on their backend.
    pub fn memory() -> Self {
        Self::new("memory://")
    }

    pub fn as_str(&self) -> &str {
        &self.0
    }
}

impl Default for EntryLocator {
    fn default() -> Self {
        Self::memory()
    }
}

impl fmt::Display for EntryLocator {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_str(&self.0)
    }
}

impl From<&str> for EntryLocator {
    fn from(value: &str) -> Self {
        Self::new(value)
    }
}

impl From<String> for EntryLocator {
    fn from(value: String) -> Self {
        Self::new(value)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn memory_yields_the_memory_scheme() {
        assert_eq!(EntryLocator::memory().as_str(), "memory://");
    }

    #[test]
    fn default_is_memory() {
        assert_eq!(EntryLocator::default(), EntryLocator::memory());
    }

    #[test]
    fn new_preserves_the_input_verbatim() {
        let loc = EntryLocator::new("file:///docs/issues/0042/index.md");
        assert_eq!(loc.as_str(), "file:///docs/issues/0042/index.md");
    }

    #[test]
    fn display_round_trips_the_inner_value() {
        let loc = EntryLocator::new("git://main@abc:adr/0001.md");
        assert_eq!(loc.to_string(), "git://main@abc:adr/0001.md");
    }
}