Skip to main content

org_core/
model.rs

1use std::fmt;
2
3use serde::{Deserialize, Serialize};
4
5pub const ITEM_SCHEMA: &str = "agd.item/v1";
6pub const AGENDA_ENTRY_SCHEMA: &str = "agd.agenda-entry/v1";
7pub const MUTATION_SCHEMA: &str = "agd.mutation/v1";
8pub const ERROR_SCHEMA: &str = "agd.error/v1";
9
10#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
11pub struct Item {
12    pub schema: String,
13    #[serde(rename = "ref")]
14    pub item_ref: String,
15    pub id: Option<String>,
16    pub file: String,
17    pub line: usize,
18    pub olpath: Vec<String>,
19    pub todo: Option<String>,
20    pub done: bool,
21    pub priority: Option<String>,
22    pub title: String,
23    pub tags: Vec<String>,
24    pub inherited_tags: Vec<String>,
25    pub category: String,
26    pub planning: Planning,
27}
28
29#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
30pub struct Planning {
31    pub scheduled: Option<PlanningTimestamp>,
32    pub deadline: Option<PlanningTimestamp>,
33    pub closed: Option<PlanningTimestamp>,
34}
35
36#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
37pub struct PlanningTimestamp {
38    pub date: String,
39    pub time: Option<String>,
40    pub end_time: Option<String>,
41    pub repeater: Option<String>,
42    pub warning: Option<String>,
43    #[serde(skip_serializing_if = "Option::is_none")]
44    pub raw: Option<String>,
45}
46
47#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
48pub struct AgendaEntry {
49    pub schema: String,
50    pub item: Item,
51    pub occurrence: Occurrence,
52}
53
54#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
55pub struct Occurrence {
56    #[serde(rename = "type")]
57    pub kind: OccurrenceType,
58    pub date: String,
59    pub time: Option<String>,
60    pub extra: String,
61}
62
63#[derive(Clone, Copy, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
64#[serde(rename_all = "kebab-case")]
65pub enum OccurrenceType {
66    Scheduled,
67    PastScheduled,
68    Deadline,
69    UpcomingDeadline,
70    Timestamp,
71    Block,
72    Closed,
73    Todo,
74}
75
76#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
77pub struct Mutation {
78    pub schema: String,
79    pub ok: bool,
80    pub item: Item,
81    pub diff: String,
82    pub journal_id: String,
83}
84
85#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
86pub struct ErrorResponse {
87    pub schema: String,
88    pub code: String,
89    pub message: String,
90    #[serde(skip_serializing_if = "Option::is_none")]
91    pub current: Option<Item>,
92}
93
94#[derive(Debug)]
95pub struct AgdError {
96    response: Box<ErrorResponse>,
97    source: Option<Box<dyn std::error::Error + Send + Sync>>,
98}
99
100impl AgdError {
101    pub fn new(code: impl Into<String>, message: impl Into<String>) -> Self {
102        Self {
103            response: Box::new(ErrorResponse {
104                schema: ERROR_SCHEMA.to_string(),
105                code: code.into(),
106                message: message.into(),
107                current: None,
108            }),
109            source: None,
110        }
111    }
112
113    pub fn with_current(mut self, current: Item) -> Self {
114        self.response.current = Some(current);
115        self
116    }
117
118    pub fn with_source(
119        code: impl Into<String>,
120        message: impl Into<String>,
121        source: impl std::error::Error + Send + Sync + 'static,
122    ) -> Self {
123        Self {
124            response: Box::new(ErrorResponse {
125                schema: ERROR_SCHEMA.to_string(),
126                code: code.into(),
127                message: message.into(),
128                current: None,
129            }),
130            source: Some(Box::new(source)),
131        }
132    }
133
134    pub fn code(&self) -> &str {
135        &self.response.code
136    }
137
138    pub fn response(&self) -> &ErrorResponse {
139        &self.response
140    }
141
142    pub fn into_response(self) -> ErrorResponse {
143        *self.response
144    }
145}
146
147impl fmt::Display for AgdError {
148    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
149        formatter.write_str(&self.response.message)
150    }
151}
152
153impl std::error::Error for AgdError {
154    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
155        self.source
156            .as_deref()
157            .map(|source| source as &(dyn std::error::Error + 'static))
158    }
159}
160
161impl From<std::io::Error> for AgdError {
162    fn from(error: std::io::Error) -> Self {
163        Self::with_source("io", error.to_string(), error)
164    }
165}