1#![allow(clippy::doc_lazy_continuation)]
2
3use std::collections::BTreeMap;
4use std::path::Path;
5
6use mime::Mime;
7use serde::{Deserialize, Serialize};
8
9use crate::crypto::Secret;
10use crate::linked_data::{BlockEncoded, DagCborCodec, Link, LinkedData};
11
12use super::maybe_mime::MaybeMime;
13
14#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
30pub struct Data {
31 mime: MaybeMime,
36 metadata: Option<BTreeMap<String, LinkedData>>,
39}
40
41impl Default for Data {
42 fn default() -> Self {
43 Self::new()
44 }
45}
46
47impl Data {
48 pub fn new() -> Self {
50 Self {
51 mime: MaybeMime(None),
52 metadata: None,
53 }
54 }
55
56 pub fn from_path(path: &Path) -> Self {
58 let mime = MaybeMime::from_path(path);
59 let metadata = BTreeMap::new();
60
61 Self {
62 mime,
63 metadata: if metadata.is_empty() {
64 None
65 } else {
66 Some(metadata)
67 },
68 }
69 }
70
71 pub fn set_metadata(&mut self, key: String, value: LinkedData) {
73 if let Some(ref mut metadata) = self.metadata {
74 metadata.insert(key, value);
75 } else {
76 let mut metadata = BTreeMap::new();
77 metadata.insert(key, value);
78 self.metadata = Some(metadata);
79 }
80 }
81
82 pub fn mime(&self) -> Option<&Mime> {
84 self.mime.0.as_ref()
85 }
86
87 pub fn metadata(&self) -> Option<&BTreeMap<String, LinkedData>> {
89 self.metadata.as_ref()
90 }
91}
92
93#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
96pub enum NodeLink {
97 Data(Link, Secret, Data),
98 Dir(Link, Secret),
99}
100
101impl NodeLink {
102 pub fn new_data_from_path(link: Link, secret: Secret, path: &Path) -> Self {
104 let data = Data::from_path(path);
105 NodeLink::Data(link, secret, data)
106 }
107
108 pub fn new_data(link: Link, secret: Secret) -> Self {
110 NodeLink::Data(link, secret, Data::new())
111 }
112
113 pub fn new_dir(link: Link, secret: Secret) -> Self {
115 NodeLink::Dir(link, secret)
116 }
117
118 pub fn link(&self) -> &Link {
119 match self {
120 NodeLink::Data(link, _, _) => link,
121 NodeLink::Dir(link, _) => link,
122 }
123 }
124
125 pub fn secret(&self) -> &Secret {
126 match self {
127 NodeLink::Data(_, secret, _) => secret,
128 NodeLink::Dir(_, secret) => secret,
129 }
130 }
131
132 pub fn data(&self) -> Option<&Data> {
134 match self {
135 NodeLink::Data(_, _, data) => Some(data),
136 NodeLink::Dir(_, _) => None,
137 }
138 }
139
140 pub fn is_dir(&self) -> bool {
142 matches!(self, NodeLink::Dir(_, _))
143 }
144
145 pub fn is_data(&self) -> bool {
147 matches!(self, NodeLink::Data(_, _, _))
148 }
149}
150
151#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
155pub struct Node {
156 links: BTreeMap<String, NodeLink>,
157}
158
159#[derive(Debug, thiserror::Error)]
160pub enum NodeError {
161 #[error("link not found")]
162 LinkNotFound(String),
163}
164
165impl BlockEncoded<DagCborCodec> for Node {}
166
167impl Node {
168 pub fn new() -> Self {
169 Node {
170 links: BTreeMap::new(),
171 }
172 }
173
174 pub fn get_link(&self, name: &str) -> Option<&NodeLink> {
175 self.links.get(name)
176 }
177
178 pub fn insert(&mut self, name: String, link: NodeLink) -> Option<NodeLink> {
179 self.links.insert(name, link)
180 }
181
182 pub fn get_links(&self) -> &BTreeMap<String, NodeLink> {
183 &self.links
184 }
185
186 pub fn del(&mut self, name: &str) -> Option<NodeLink> {
187 self.links.remove(name)
189 }
190
191 pub fn size(&self) -> usize {
192 self.links.len()
193 }
194}
195
196#[cfg(test)]
197mod test {
198 use super::*;
199
200 #[test]
201 fn test_node_encode_decode() {
202 let mut node = Node::default();
203 node.links.insert(
204 "example".to_string(),
205 NodeLink::Data(
206 Link::default(),
207 Secret::default(),
208 Data {
209 metadata: None,
210 mime: MaybeMime(None),
211 },
212 ),
213 );
214
215 let encoded = node.encode().unwrap();
216 let decoded = Node::decode(&encoded).unwrap();
217
218 assert_eq!(node, decoded);
219 }
220
221 #[test]
222 fn test_data_from_path() {
223 use std::path::PathBuf;
224
225 let path = PathBuf::from("/test/file.json");
227 let data = Data::from_path(&path);
228 assert_eq!(data.mime().map(|m| m.as_ref()), Some("application/json"));
229
230 let path = PathBuf::from("/src/main.rs");
232 let data = Data::from_path(&path);
233 assert_eq!(data.mime().map(|m| m.as_ref()), Some("text/x-rust"));
234
235 let path = PathBuf::from("/audio/song.m4a");
237 let data = Data::from_path(&path);
238 assert_eq!(data.mime().map(|m| m.as_ref()), Some("audio/m4a"));
239
240 let path = PathBuf::from("/test/file.unknownext");
242 let data = Data::from_path(&path);
243 assert_eq!(data.mime(), None);
244
245 let path = PathBuf::from("/test/README");
247 let data = Data::from_path(&path);
248 assert_eq!(data.mime(), None);
249 }
250
251 #[test]
252 fn test_node_link_constructors() {
253 use std::path::PathBuf;
254
255 let link = Link::default();
256 let secret = Secret::default();
257 let path = PathBuf::from("/test/image.png");
258
259 let node_link = NodeLink::new_data_from_path(link.clone(), secret.clone(), &path);
261 assert!(node_link.is_data());
262 assert!(!node_link.is_dir());
263 let data = node_link.data().unwrap();
264 assert_eq!(data.mime().map(|m| m.as_ref()), Some("image/png"));
265
266 let node_link = NodeLink::new_data(link.clone(), secret.clone());
268 assert!(node_link.is_data());
269 let data = node_link.data().unwrap();
270 assert_eq!(data.mime(), None);
271 assert_eq!(data.metadata(), None);
272
273 let node_link = NodeLink::new_dir(link.clone(), secret.clone());
275 assert!(node_link.is_dir());
276 assert!(!node_link.is_data());
277 assert!(node_link.data().is_none());
278 }
279}