fiberplane_models/notebooks/
front_matter.rs1use crate::front_matter_schemas::SerializableEqFloat;
2pub use crate::labels::Label;
3use crate::timestamps::*;
4use base64uuid::Base64Uuid;
5#[cfg(feature = "fp-bindgen")]
6use fp_bindgen::prelude::Serializable;
7use serde::{Deserialize, Serialize};
8use serde_json::Value;
9use std::{collections::BTreeMap, str::FromStr};
10use strum_macros::Display;
11use thiserror::Error;
12use typed_builder::TypedBuilder;
13
14pub type FrontMatter = BTreeMap<String, FrontMatterValue>;
22
23#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, Display)]
53#[cfg_attr(
54 feature = "fp-bindgen",
55 derive(Serializable),
56 fp(rust_module = "fiberplane_models::notebooks::front_matter")
57)]
58#[non_exhaustive]
59#[serde(untagged)]
60pub enum FrontMatterValue {
61 DateTime(FrontMatterDateTimeValue),
63 DateTimeList(FrontMatterDateTimeList),
65
66 User(FrontMatterUserValue),
68 UserList(FrontMatterUserList),
70
71 String(FrontMatterStringValue),
73 StringList(FrontMatterStringList),
75
76 Number(FrontMatterNumberValue),
78 NumberList(FrontMatterNumberList),
80}
81
82#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, Error)]
84#[cfg_attr(
85 feature = "fp-bindgen",
86 derive(Serializable),
87 fp(rust_module = "fiberplane_models::notebooks::front_matter")
88)]
89#[non_exhaustive]
90#[serde(rename_all = "snake_case", tag = "type")]
91pub enum FrontMatterValidationError {
92 #[error("unexpected format: {message}")]
94 Format { message: String },
95
96 #[error("unexpected variant: expected {expected} but got {got}")]
98 Variant { got: String, expected: String },
99}
100
101impl FrontMatterValidationError {
102 pub(crate) fn wrong_variant(got: &str, expected: &str) -> Self {
103 Self::Variant {
104 got: got.to_string(),
105 expected: expected.to_string(),
106 }
107 }
108}
109
110impl From<serde_json::Value> for FrontMatterValue {
111 fn from(value: serde_json::Value) -> Self {
112 serde_json::from_value(value)
113 .expect("with the untagged variant, all serde_json::Value are deserializable.")
114 }
115}
116
117impl TryFrom<&str> for FrontMatterValue {
118 type Error = FrontMatterValidationError;
119
120 fn try_from(value: &str) -> Result<Self, Self::Error> {
121 serde_json::from_str(value).map_err(|err| FrontMatterValidationError::Format {
122 message: err.to_string(),
123 })
124 }
125}
126
127impl FrontMatterValue {
128 pub fn get_type(&self) -> &'static str {
130 match self {
131 FrontMatterValue::Number(_) => "number",
132 FrontMatterValue::NumberList(_) => "number_list",
133 FrontMatterValue::String(_) => "string",
134 FrontMatterValue::StringList(_) => "string_list",
135 FrontMatterValue::DateTime(_) => "date_time",
136 FrontMatterValue::DateTimeList(_) => "date_time_list",
137 FrontMatterValue::User(_) => "user",
138 FrontMatterValue::UserList(_) => "user_list",
139 }
140 }
141}
142
143impl TryFrom<serde_json::Value> for FrontMatterNumberValue {
144 type Error = FrontMatterValidationError;
145
146 fn try_from(value: serde_json::Value) -> Result<Self, Self::Error> {
147 if let Value::Number(num) = value {
148 return Ok(Self(
149 num.as_f64()
150 .ok_or_else(|| FrontMatterValidationError::Format {
151 message: "invalid number".to_string(),
152 })?
153 .into(),
154 ));
155 }
156
157 Err(FrontMatterValidationError::wrong_variant(
158 "untyped", "number",
159 ))
160 }
161}
162
163impl From<f64> for FrontMatterNumberValue {
164 fn from(value: f64) -> Self {
165 Self(value.into())
166 }
167}
168
169impl TryFrom<serde_json::Value> for FrontMatterNumberList {
170 type Error = FrontMatterValidationError;
171
172 fn try_from(value: serde_json::Value) -> Result<Self, Self::Error> {
173 if let Value::Array(array) = value {
174 return Ok(Self(
175 array
176 .into_iter()
177 .map(FrontMatterNumberValue::try_from)
178 .collect::<Result<Vec<_>, _>>()?,
179 ));
180 }
181
182 Err(FrontMatterValidationError::wrong_variant(
183 "'not a list'",
184 "number_list",
185 ))
186 }
187}
188
189impl TryFrom<serde_json::Value> for FrontMatterStringValue {
190 type Error = FrontMatterValidationError;
191
192 fn try_from(value: serde_json::Value) -> Result<Self, Self::Error> {
193 if let Value::String(strr) = value {
194 return Ok(Self(strr));
195 }
196
197 Err(FrontMatterValidationError::wrong_variant(
198 "untyped", "string",
199 ))
200 }
201}
202
203impl From<&str> for FrontMatterStringValue {
204 fn from(value: &str) -> Self {
205 Self(value.to_string())
206 }
207}
208
209impl From<String> for FrontMatterStringValue {
210 fn from(value: String) -> Self {
211 Self(value)
212 }
213}
214
215impl TryFrom<serde_json::Value> for FrontMatterStringList {
216 type Error = FrontMatterValidationError;
217
218 fn try_from(value: serde_json::Value) -> Result<Self, Self::Error> {
219 if let Value::Array(array) = value {
220 return Ok(Self(
221 array
222 .into_iter()
223 .map(FrontMatterStringValue::try_from)
224 .collect::<Result<Vec<_>, _>>()?,
225 ));
226 }
227
228 Err(FrontMatterValidationError::wrong_variant(
229 "'not a list'",
230 "string_list",
231 ))
232 }
233}
234
235impl TryFrom<serde_json::Value> for FrontMatterDateTimeValue {
236 type Error = FrontMatterValidationError;
237
238 fn try_from(value: serde_json::Value) -> Result<Self, Self::Error> {
239 if let Value::String(strr) = value {
240 return Ok(Self(strr.parse().map_err(|err| {
241 FrontMatterValidationError::Format {
242 message: format!("invalid timestamp: {err}"),
243 }
244 })?));
245 }
246
247 Err(FrontMatterValidationError::wrong_variant(
248 "untyped",
249 "date_time",
250 ))
251 }
252}
253
254impl TryFrom<&str> for FrontMatterDateTimeValue {
255 type Error = FrontMatterValidationError;
256
257 fn try_from(value: &str) -> Result<Self, Self::Error> {
258 let value = Timestamp::parse(value).map_err(|err| FrontMatterValidationError::Format {
259 message: err.to_string(),
260 })?;
261 Ok(Self(value))
262 }
263}
264
265impl FromStr for FrontMatterDateTimeValue {
266 type Err = FrontMatterValidationError;
267
268 fn from_str(s: &str) -> Result<Self, Self::Err> {
269 Self::try_from(s)
270 }
271}
272
273impl TryFrom<serde_json::Value> for FrontMatterDateTimeList {
274 type Error = FrontMatterValidationError;
275
276 fn try_from(value: serde_json::Value) -> Result<Self, Self::Error> {
277 if let Value::Array(array) = value {
278 return Ok(Self(
279 array
280 .into_iter()
281 .map(FrontMatterDateTimeValue::try_from)
282 .collect::<Result<Vec<_>, _>>()?,
283 ));
284 }
285
286 Err(FrontMatterValidationError::wrong_variant(
287 "'not a list'",
288 "date_time_list",
289 ))
290 }
291}
292
293impl TryFrom<serde_json::Value> for FrontMatterUserValue {
294 type Error = FrontMatterValidationError;
295
296 fn try_from(value: serde_json::Value) -> Result<Self, Self::Error> {
297 serde_json::from_value(value).map_err(|err| FrontMatterValidationError::Format {
298 message: format!("invalid user: {err}"),
299 })
300 }
301}
302
303impl TryFrom<&str> for FrontMatterUserValue {
304 type Error = FrontMatterValidationError;
305
306 fn try_from(value: &str) -> Result<Self, Self::Error> {
307 let value =
308 Base64Uuid::parse_str(value).map_err(|err| FrontMatterValidationError::Format {
309 message: err.to_string(),
310 })?;
311 Ok(Self::from(value))
312 }
313}
314
315impl FromStr for FrontMatterUserValue {
316 type Err = FrontMatterValidationError;
317
318 fn from_str(s: &str) -> Result<Self, Self::Err> {
319 Self::try_from(s)
320 }
321}
322
323impl TryFrom<serde_json::Value> for FrontMatterUserList {
324 type Error = FrontMatterValidationError;
325
326 fn try_from(value: serde_json::Value) -> Result<Self, Self::Error> {
327 if let Value::Array(array) = value {
328 return Ok(Self(
329 array
330 .into_iter()
331 .map(FrontMatterUserValue::try_from)
332 .collect::<Result<Vec<_>, _>>()?,
333 ));
334 }
335
336 Err(FrontMatterValidationError::wrong_variant(
337 "'not a list'",
338 "user_list",
339 ))
340 }
341}
342
343impl From<FrontMatterUserValue> for FrontMatterValue {
344 fn from(v: FrontMatterUserValue) -> Self {
345 Self::User(v)
346 }
347}
348
349impl From<FrontMatterDateTimeValue> for FrontMatterValue {
350 fn from(v: FrontMatterDateTimeValue) -> Self {
351 Self::DateTime(v)
352 }
353}
354
355impl From<FrontMatterStringValue> for FrontMatterValue {
356 fn from(v: FrontMatterStringValue) -> Self {
357 Self::String(v)
358 }
359}
360
361impl From<FrontMatterNumberValue> for FrontMatterValue {
362 fn from(v: FrontMatterNumberValue) -> Self {
363 Self::Number(v)
364 }
365}
366
367impl From<FrontMatterUserList> for FrontMatterValue {
368 fn from(v: FrontMatterUserList) -> Self {
369 Self::UserList(v)
370 }
371}
372
373impl From<FrontMatterDateTimeList> for FrontMatterValue {
374 fn from(v: FrontMatterDateTimeList) -> Self {
375 Self::DateTimeList(v)
376 }
377}
378
379impl From<FrontMatterStringList> for FrontMatterValue {
380 fn from(v: FrontMatterStringList) -> Self {
381 Self::StringList(v)
382 }
383}
384
385impl From<FrontMatterNumberList> for FrontMatterValue {
386 fn from(v: FrontMatterNumberList) -> Self {
387 Self::NumberList(v)
388 }
389}
390
391#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)]
392#[cfg_attr(
393 feature = "fp-bindgen",
394 derive(Serializable),
395 fp(rust_module = "fiberplane_models::notebooks::front_matter")
396)]
397#[non_exhaustive]
398#[repr(transparent)]
399pub struct FrontMatterNumberValue(pub SerializableEqFloat);
400
401impl std::ops::DerefMut for FrontMatterNumberValue {
402 fn deref_mut(&mut self) -> &mut Self::Target {
403 &mut self.0
404 }
405}
406
407impl std::ops::Deref for FrontMatterNumberValue {
408 type Target = SerializableEqFloat;
409
410 fn deref(&self) -> &Self::Target {
411 &self.0
412 }
413}
414
415#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)]
416#[cfg_attr(
417 feature = "fp-bindgen",
418 derive(Serializable),
419 fp(rust_module = "fiberplane_models::notebooks::front_matter")
420)]
421#[non_exhaustive]
422#[repr(transparent)]
423pub struct FrontMatterNumberList(pub Vec<FrontMatterNumberValue>);
424
425impl std::ops::DerefMut for FrontMatterNumberList {
426 fn deref_mut(&mut self) -> &mut Self::Target {
427 &mut self.0
428 }
429}
430
431impl std::ops::Deref for FrontMatterNumberList {
432 type Target = Vec<FrontMatterNumberValue>;
433
434 fn deref(&self) -> &Self::Target {
435 &self.0
436 }
437}
438
439#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)]
440#[cfg_attr(
441 feature = "fp-bindgen",
442 derive(Serializable),
443 fp(rust_module = "fiberplane_models::notebooks::front_matter")
444)]
445#[non_exhaustive]
446#[repr(transparent)]
447pub struct FrontMatterStringValue(pub String);
448
449impl std::ops::DerefMut for FrontMatterStringValue {
450 fn deref_mut(&mut self) -> &mut Self::Target {
451 &mut self.0
452 }
453}
454
455impl std::ops::Deref for FrontMatterStringValue {
456 type Target = String;
457
458 fn deref(&self) -> &Self::Target {
459 &self.0
460 }
461}
462
463#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)]
464#[cfg_attr(
465 feature = "fp-bindgen",
466 derive(Serializable),
467 fp(rust_module = "fiberplane_models::notebooks::front_matter")
468)]
469#[non_exhaustive]
470#[repr(transparent)]
471pub struct FrontMatterStringList(pub Vec<FrontMatterStringValue>);
472
473impl std::ops::DerefMut for FrontMatterStringList {
474 fn deref_mut(&mut self) -> &mut Self::Target {
475 &mut self.0
476 }
477}
478
479impl std::ops::Deref for FrontMatterStringList {
480 type Target = Vec<FrontMatterStringValue>;
481
482 fn deref(&self) -> &Self::Target {
483 &self.0
484 }
485}
486
487#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)]
488#[cfg_attr(
489 feature = "fp-bindgen",
490 derive(Serializable),
491 fp(rust_module = "fiberplane_models::notebooks::front_matter")
492)]
493#[non_exhaustive]
494#[repr(transparent)]
495pub struct FrontMatterDateTimeValue(pub Timestamp);
496
497impl std::ops::DerefMut for FrontMatterDateTimeValue {
498 fn deref_mut(&mut self) -> &mut Self::Target {
499 &mut self.0
500 }
501}
502
503impl std::ops::Deref for FrontMatterDateTimeValue {
504 type Target = Timestamp;
505
506 fn deref(&self) -> &Self::Target {
507 &self.0
508 }
509}
510
511impl<T: Into<Timestamp>> From<T> for FrontMatterDateTimeValue {
512 fn from(value: T) -> Self {
513 Self(value.into())
514 }
515}
516
517#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)]
518#[cfg_attr(
519 feature = "fp-bindgen",
520 derive(Serializable),
521 fp(rust_module = "fiberplane_models::notebooks::front_matter")
522)]
523#[non_exhaustive]
524#[repr(transparent)]
525pub struct FrontMatterDateTimeList(pub Vec<FrontMatterDateTimeValue>);
526
527impl std::ops::DerefMut for FrontMatterDateTimeList {
528 fn deref_mut(&mut self) -> &mut Self::Target {
529 &mut self.0
530 }
531}
532
533impl std::ops::Deref for FrontMatterDateTimeList {
534 type Target = Vec<FrontMatterDateTimeValue>;
535
536 fn deref(&self) -> &Self::Target {
537 &self.0
538 }
539}
540
541#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, TypedBuilder)]
542#[cfg_attr(
543 feature = "fp-bindgen",
544 derive(Serializable),
545 fp(rust_module = "fiberplane_models::notebooks::front_matter")
546)]
547#[non_exhaustive]
548#[serde(rename_all = "snake_case")]
549pub struct FrontMatterUserValue {
550 #[builder(setter(into))]
551 pub id: Base64Uuid,
552 #[builder(setter(into))]
553 pub name: String,
554}
555
556impl std::ops::DerefMut for FrontMatterUserValue {
557 fn deref_mut(&mut self) -> &mut Self::Target {
558 &mut self.id
559 }
560}
561
562impl std::ops::Deref for FrontMatterUserValue {
563 type Target = Base64Uuid;
564
565 fn deref(&self) -> &Self::Target {
566 &self.id
567 }
568}
569
570impl From<Base64Uuid> for FrontMatterUserValue {
571 fn from(value: Base64Uuid) -> Self {
572 Self {
573 id: value,
574 name: String::new(),
575 }
576 }
577}
578
579#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)]
580#[cfg_attr(
581 feature = "fp-bindgen",
582 derive(Serializable),
583 fp(rust_module = "fiberplane_models::notebooks::front_matter")
584)]
585#[non_exhaustive]
586#[repr(transparent)]
587pub struct FrontMatterUserList(pub Vec<FrontMatterUserValue>);
588
589impl std::ops::DerefMut for FrontMatterUserList {
590 fn deref_mut(&mut self) -> &mut Self::Target {
591 &mut self.0
592 }
593}
594
595impl std::ops::Deref for FrontMatterUserList {
596 type Target = Vec<FrontMatterUserValue>;
597
598 fn deref(&self) -> &Self::Target {
599 &self.0
600 }
601}