serialize_quill_delta/
lib.rs

1use serde::{Deserialize, Serialize};
2use std::collections::BTreeMap;
3
4#[derive(Deserialize, Serialize, PartialEq, Debug)]
5pub struct Op {
6    #[serde(flatten)]
7    pub content: OpContent,
8    #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
9    pub attributes: BTreeMap<String, serde_json::Value>,
10}
11
12#[derive(Deserialize, Serialize, PartialEq, Debug)]
13#[serde(rename_all = "lowercase")]
14pub enum OpContent {
15    Insert(serde_json::Value),
16}
17
18#[derive(Deserialize, Serialize)]
19pub struct Delta {
20    pub ops: Vec<Op>,
21}
22
23impl Delta {
24    pub fn plain_text(&self) -> String {
25        self.ops
26            .iter()
27            .filter_map(|op| match &op.content {
28                OpContent::Insert(value) => match value {
29                    serde_json::Value::String(str) => Some(str.as_str()),
30                    _ => None,
31                },
32                // For the future: there can be other kinds of operations.
33                _ => None,
34            })
35            .collect()
36    }
37}
38
39#[cfg(test)]
40mod tests {
41    use super::{Delta, Op, OpContent};
42
43    const D1: &str = r#"{
44      "ops": [ { "insert": "Hello\n\nLet's write some code!\n" } ]
45    }"#;
46
47    const D2: &str = r#"{
48      "ops": [
49        {
50          "attributes": {
51            "bold": true
52          },
53          "insert": "Hello"
54        },
55        {
56          "insert": "\n\nLet's write some "
57        },
58        {
59          "attributes": {
60            "italic": true
61          },
62          "insert": "code"
63        },
64        {
65          "insert": "!\n"
66        }
67      ]
68    }"#;
69
70    #[test]
71    fn deserialize() {
72        let d1_delta: Delta = serde_json::from_str(D1).unwrap();
73        assert_eq!(d1_delta.ops.len(), 1);
74        assert_eq!(
75            d1_delta.ops[0],
76            Op {
77                content: OpContent::Insert(serde_json::Value::String(String::from(
78                    "Hello\n\nLet's write some code!\n"
79                ))),
80                attributes: std::collections::BTreeMap::new(),
81            },
82        );
83
84        let d2_delta: Delta = serde_json::from_str(D2).unwrap();
85        let mut d2_attributes = std::collections::BTreeMap::new();
86        d2_attributes.insert(String::from("bold"), serde_json::Value::Bool(true));
87        assert_eq!(d2_delta.ops.len(), 4);
88        assert_eq!(
89            d2_delta.ops[0],
90            Op {
91                content: OpContent::Insert(serde_json::Value::String(String::from("Hello"))),
92                attributes: d2_attributes,
93            },
94        );
95    }
96
97    #[test]
98    fn serialize() {
99        let d1_delta: Delta = serde_json::from_str(D1).unwrap();
100        assert_eq!(
101            serde_json::to_string(&d1_delta).unwrap(),
102            r#"{"ops":[{"insert":"Hello\n\nLet's write some code!\n"}]}"#
103        );
104    }
105
106    #[test]
107    fn plain_text() {
108        let d1_delta: Delta = serde_json::from_str(D1).unwrap();
109        assert_eq!(d1_delta.plain_text(), "Hello\n\nLet's write some code!\n");
110
111        // Same text, no formatting.
112        let d2_delta: Delta = serde_json::from_str(D2).unwrap();
113        assert_eq!(d2_delta.plain_text(), "Hello\n\nLet's write some code!\n");
114    }
115}