1use chrono::NaiveDate;
2use serde::{Deserialize, Serialize};
3use std::collections::BTreeMap;
4use std::path::{Path, PathBuf};
5
6use super::kind::Kind;
7use super::status::Status;
8
9#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct Node {
12 pub id: String,
14 #[serde(
15 serialize_with = "serialize_path_forward",
16 deserialize_with = "deserialize_path"
17 )]
18 pub path: PathBuf,
19 pub title: String,
20 pub kind: Kind,
21
22 pub status: Status,
24 #[serde(skip_serializing_if = "Option::is_none")]
25 pub created: Option<NaiveDate>,
26 #[serde(skip_serializing_if = "Option::is_none")]
27 pub updated: Option<NaiveDate>,
28 #[serde(skip_serializing_if = "Option::is_none")]
29 pub reviewed: Option<NaiveDate>,
30 #[serde(skip_serializing_if = "Option::is_none")]
31 pub owner: Option<String>,
32
33 #[serde(default, skip_serializing_if = "Vec::is_empty")]
35 pub supersedes: Vec<String>,
36 #[serde(skip_serializing_if = "Option::is_none")]
37 pub superseded_by: Option<String>,
38 #[serde(default, skip_serializing_if = "Vec::is_empty")]
39 pub implements: Vec<String>,
40 #[serde(default, skip_serializing_if = "Vec::is_empty")]
41 pub related: Vec<String>,
42 #[serde(default, skip_serializing_if = "Vec::is_empty")]
43 pub tags: Vec<String>,
44
45 #[serde(default)]
47 pub orphan_ok: bool,
48
49 #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
51 pub attrs: BTreeMap<String, serde_json::Value>,
52}
53
54pub fn serialize_path_forward<S: serde::Serializer>(path: &Path, s: S) -> Result<S::Ok, S::Error> {
58 s.serialize_str(&path.to_string_lossy().replace('\\', "/"))
59}
60
61pub fn deserialize_path<'de, D: serde::Deserializer<'de>>(d: D) -> Result<PathBuf, D::Error> {
63 let s = String::deserialize(d)?;
64 Ok(PathBuf::from(s))
65}