Skip to main content

mdvault_core/domain/
traits.rs

1//! Domain traits for note type behaviors.
2//!
3//! These traits define the polymorphic interface for first-class note types,
4//! allowing type-specific behavior without scattered if/else checks.
5
6use std::path::PathBuf;
7
8use super::context::{CreationContext, FieldPrompt, PromptContext};
9
10/// Errors that can occur during domain operations.
11#[derive(Debug, thiserror::Error)]
12pub enum DomainError {
13    #[error("ID generation failed: {0}")]
14    IdGeneration(String),
15
16    #[error("path resolution failed: {0}")]
17    PathResolution(String),
18
19    #[error("lifecycle hook failed: {0}")]
20    LifecycleHook(String),
21
22    #[error("validation failed: {0}")]
23    Validation(String),
24
25    #[error("IO error: {0}")]
26    Io(#[from] std::io::Error),
27
28    #[error("missing required field: {0}")]
29    MissingField(String),
30
31    #[error("{0}")]
32    Other(String),
33}
34
35pub type DomainResult<T> = Result<T, DomainError>;
36
37/// How does this note type generate identifiers and paths?
38pub trait NoteIdentity {
39    /// Generate a unique ID for this note type.
40    /// Returns None if this type doesn't use IDs (e.g., daily notes use dates).
41    fn generate_id(&self, ctx: &CreationContext) -> DomainResult<Option<String>>;
42
43    /// Determine the output path for this note.
44    /// This is called after prompts are collected and before_create runs.
45    fn output_path(&self, ctx: &CreationContext) -> DomainResult<PathBuf>;
46
47    /// Get the fields that this type manages (core metadata).
48    /// These fields will be preserved through template/hook modifications.
49    fn core_fields(&self) -> Vec<&'static str>;
50}
51
52/// What happens during the note lifecycle?
53pub trait NoteLifecycle {
54    /// Called before the note is written to disk.
55    /// Can modify the context (e.g., set computed vars, update counters).
56    fn before_create(&self, ctx: &mut CreationContext) -> DomainResult<()>;
57
58    /// Called after the note is successfully written to disk.
59    /// Used for side effects (logging to daily, reindexing, etc.).
60    fn after_create(&self, ctx: &CreationContext, content: &str) -> DomainResult<()>;
61}
62
63/// What interactive prompts does this type need?
64pub trait NotePrompts {
65    /// Return type-specific prompts (e.g., project selector for tasks).
66    /// These run BEFORE schema-based prompts.
67    fn type_prompts(&self, ctx: &PromptContext) -> Vec<FieldPrompt>;
68
69    /// Whether this type should prompt for schema fields.
70    /// Default: true. Override to false for types that compute all fields.
71    fn should_prompt_schema(&self) -> bool {
72        true
73    }
74}
75
76/// Combined trait for a note type behavior.
77/// All first-class types implement this.
78pub trait NoteBehavior: NoteIdentity + NoteLifecycle + NotePrompts + Send + Sync {
79    /// Get the type name for this behavior.
80    fn type_name(&self) -> &'static str;
81}