cartulary 0.3.0-alpha.1

The knowledge layer of your project — decisions, issues, docs, all in one place.
Documentation
use crate::domain::model::decision_record::DecisionRecord;
use crate::domain::model::record_ref::DecisionRecordRef;
use crate::domain::usecases::decision_record::DecisionRecordRepository;

/// Retrieve a single decision record by ID.
///
/// Returns `Ok(Some(record))` if found, `Ok(None)` if not found, or `Err` on
/// repository failure.
pub fn show_decision_record(
    repo: &dyn DecisionRecordRepository,
    id: &DecisionRecordRef,
) -> anyhow::Result<Option<DecisionRecord>> {
    repo.find_by_id(id)
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::domain::usecases::decision_record::tests::{
        adr, FakeDecisionRecordRepository, RecordFixture,
    };

    // ── Tests ─────────────────────────────────────────────────────────────────

    #[test]
    fn show_returns_the_record_when_it_exists() {
        scenario()
            .given(adr("Use Rust").with_id("ADR-0001"))
            .when_show("ADR-0001")
            .then_title("Use Rust");
    }

    #[test]
    fn show_returns_none_for_an_unknown_id() {
        scenario().when_show("ADR-0099").then_not_found();
    }

    // ── Scenario ──────────────────────────────────────────────────────────────

    fn scenario() -> Scenario {
        Scenario {
            repo: FakeDecisionRecordRepository::with_records(vec![]),
        }
    }

    struct Scenario {
        repo: FakeDecisionRecordRepository,
    }

    impl Scenario {
        fn given(mut self, fixture: RecordFixture) -> Self {
            let raw = fixture
                .id
                .as_deref()
                .expect("given() requires an explicit id — use .with_id()")
                .to_string();
            let numeric = DecisionRecordRef::new(&raw)
                .unwrap_or_else(|_| panic!("given(): invalid id {raw:?}"));
            self.repo.push_record(fixture.build(numeric));
            self
        }

        fn when_show(self, id: &str) -> ShowOutcome {
            let record_ref = DecisionRecordRef::new(id)
                .unwrap_or_else(|_| panic!("when_show: invalid id {id:?}"));
            ShowOutcome {
                result: show_decision_record(&self.repo, &record_ref)
                    .expect("show_decision_record failed unexpectedly"),
            }
        }
    }

    struct ShowOutcome {
        result: Option<DecisionRecord>,
    }

    impl ShowOutcome {
        fn then_title(self, expected: &str) -> Self {
            let record = self.result.as_ref().expect("expected Some, got None");
            assert_eq!(record.title.as_str(), expected);
            self
        }

        fn then_not_found(self) -> Self {
            assert!(self.result.is_none(), "expected None, got Some");
            self
        }
    }
}