yggdrasil/models/
public_note.rs1use chrono::{DateTime, Utc};
8use serde::{Deserialize, Serialize};
9
10use super::note::{Note, NoteKind};
11
12#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
13pub struct PublicNote {
14 pub id: i32,
15 pub slug: String,
16 pub version: i32,
19 pub kind: NoteKind,
20 pub title: String,
21 pub summary: String,
22 pub content_html: String,
23 pub toc_html: String,
24 pub tags: Vec<String>,
25 #[cfg_attr(
26 target_arch = "wasm32",
27 serde(deserialize_with = "crate::models::rfc3339::deserialize")
28 )]
29 pub created_at: DateTime<Utc>,
30 #[cfg_attr(
32 target_arch = "wasm32",
33 serde(deserialize_with = "crate::models::rfc3339::deserialize")
34 )]
35 pub updated_at: DateTime<Utc>,
36 #[serde(default)]
38 pub sections: u32,
39}
40
41impl From<Note> for PublicNote {
42 fn from(note: Note) -> Self {
43 Self {
44 id: note.id,
45 slug: note.slug,
46 version: note.version,
47 kind: note.kind,
48 title: note.title,
49 summary: note.summary,
50 content_html: note.content_html,
51 toc_html: note.toc_html,
52 tags: note.tags,
53 created_at: note.created_at,
54 updated_at: note.updated_at,
55 sections: note.sections,
56 }
57 }
58}
59
60impl PublicNote {
61 pub fn display_title(&self) -> String {
64 super::note::display_title(self.kind, &self.title, &self.summary)
65 }
66}
67
68#[cfg(test)]
69mod tests {
70 use super::*;
71 use crate::models::note::NoteKind;
72
73 fn sample_note() -> Note {
74 let now = Utc::now();
75 Note {
76 id: 7,
77 slug: "garden".into(),
78 version: 3,
79 kind: NoteKind::Topic,
80 title: "生长中的想法".into(),
81 content_md: "仅编辑需要的原文".into(),
82 content_html: "<p>正文</p>".into(),
83 toc_html: "<nav>目录</nav>".into(),
84 summary: "摘要".into(),
85 tags: vec!["rust".into()],
86 created_at: now,
87 updated_at: now,
88 published_at: Some(now),
89 published_version: Some(3),
90 knowledge_version: Some(2),
91 deleted_at: None,
92 notebook_ids: vec![1, 2],
93 sections: 0,
94 }
95 }
96
97 #[test]
98 fn public_note_omits_markdown_and_internal_fields() {
99 let payload = serde_json::to_value(PublicNote::from(sample_note())).unwrap();
100 assert!(
101 payload.get("content_md").is_none(),
102 "公开 DTO 不得携带编辑 Markdown"
103 );
104 assert!(
105 payload.get("published_version").is_none(),
106 "工作稿版本不外发"
107 );
108 assert!(
109 payload.get("knowledge_version").is_none(),
110 "知识库版本不外发"
111 );
112 assert!(payload.get("deleted_at").is_none());
113 assert!(payload.get("notebook_ids").is_none(), "笔记本归属不外发");
114 assert_eq!(payload["version"], 3);
115 assert_eq!(payload["content_html"], "<p>正文</p>");
116 }
117
118 #[test]
119 fn display_title_matches_note_rules() {
120 let mut public = PublicNote::from(sample_note());
121 assert_eq!(public.display_title(), "生长中的想法");
122 public.title = String::new();
123 assert_eq!(public.display_title(), "摘要");
124 public.summary = String::new();
125 assert_eq!(public.display_title(), "主题笔记");
126 }
127}