Skip to main content

turbovault_parser/
models.rs

1//! Parser models that aren't shared through `turbovault-core`.
2
3use serde::{Deserialize, Serialize};
4
5/// Obsidian task checkbox states
6#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
7#[non_exhaustive]
8pub enum TaskStatus {
9    /// `[ ]` — pending/incomplete
10    Pending,
11    /// `[x]` or `[X]` — completed/done
12    Done,
13    /// `[/]` — in progress
14    InProgress,
15    /// `[-]` — cancelled
16    Cancelled,
17}
18
19impl TaskStatus {
20    /// Whether the task is done (`[x]`). Cancelled tasks are not.
21    pub fn is_completed(&self) -> bool {
22        matches!(self, TaskStatus::Done)
23    }
24
25    /// The status a checkbox character means: `x` done, `/` in progress,
26    /// `-` cancelled, anything else open.
27    pub fn from_marker(c: char) -> Self {
28        match c {
29            'x' | 'X' => TaskStatus::Done,
30            '/' => TaskStatus::InProgress,
31            '-' => TaskStatus::Cancelled,
32            _ => TaskStatus::Pending,
33        }
34    }
35}
36
37#[cfg(test)]
38mod tests {
39    use super::*;
40
41    #[test]
42    fn test_from_marker_done() {
43        assert_eq!(TaskStatus::from_marker('x'), TaskStatus::Done);
44        assert_eq!(TaskStatus::from_marker('X'), TaskStatus::Done);
45    }
46
47    #[test]
48    fn test_from_marker_in_progress() {
49        assert_eq!(TaskStatus::from_marker('/'), TaskStatus::InProgress);
50    }
51
52    #[test]
53    fn test_from_marker_cancelled() {
54        assert_eq!(TaskStatus::from_marker('-'), TaskStatus::Cancelled);
55    }
56
57    #[test]
58    fn test_from_marker_pending() {
59        assert_eq!(TaskStatus::from_marker(' '), TaskStatus::Pending);
60    }
61
62    #[test]
63    fn test_from_marker_unknown_defaults_pending() {
64        assert_eq!(TaskStatus::from_marker('?'), TaskStatus::Pending);
65        assert_eq!(TaskStatus::from_marker('a'), TaskStatus::Pending);
66    }
67
68    #[test]
69    fn test_is_completed() {
70        assert!(TaskStatus::Done.is_completed());
71        assert!(!TaskStatus::Pending.is_completed());
72        assert!(!TaskStatus::InProgress.is_completed());
73        assert!(!TaskStatus::Cancelled.is_completed());
74    }
75
76    #[test]
77    fn test_serde_roundtrip() {
78        let variants = [
79            TaskStatus::Pending,
80            TaskStatus::Done,
81            TaskStatus::InProgress,
82            TaskStatus::Cancelled,
83        ];
84
85        for variant in &variants {
86            let json = serde_json::to_string(variant)
87                .unwrap_or_else(|e| panic!("serialize {:?} failed: {}", variant, e));
88            let roundtripped: TaskStatus = serde_json::from_str(&json).unwrap_or_else(|e| {
89                panic!("deserialize {:?} from {:?} failed: {}", variant, json, e)
90            });
91            assert_eq!(
92                variant, &roundtripped,
93                "roundtrip mismatch for {:?}: serialized to {:?}",
94                variant, json
95            );
96        }
97    }
98}