acter/api/
utils.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
use eyeball_im::VectorDiff;

// copied from ruma, which - unfortunately, doesn’t expose this helper
// https://github.com/ruma/ruma/blob/fec7d23cfd44266cc396f5abc08ea821dc138d6d/crates/ruma-common/src/events/room/message.rs#L937
pub fn parse_markdown(text: String) -> Option<String> {
    use pulldown_cmark::{Event, Options, Parser, Tag};

    const OPTIONS: Options = Options::ENABLE_TABLES.union(Options::ENABLE_STRIKETHROUGH);

    let mut found_first_paragraph = false;

    let parser_events: Vec<_> = Parser::new_ext(&text, OPTIONS)
        .map(|event| match event {
            Event::SoftBreak => Event::HardBreak,
            _ => event,
        })
        .collect();
    let has_markdown = parser_events.iter().any(|ref event| {
        let is_text = matches!(event, Event::Text(_));
        let is_break = matches!(event, Event::HardBreak);
        let is_first_paragraph_start = if matches!(event, Event::Start(Tag::Paragraph)) {
            if found_first_paragraph {
                false
            } else {
                found_first_paragraph = true;
                true
            }
        } else {
            false
        };
        let is_paragraph_end = matches!(event, Event::End(Tag::Paragraph));

        !is_text && !is_break && !is_first_paragraph_start && !is_paragraph_end
    });

    if !has_markdown {
        return None;
    }

    let mut html_body = String::new();
    pulldown_cmark::html::push_html(&mut html_body, parser_events.into_iter());

    Some(html_body)
}

pub struct ApiVectorDiff<T> {
    action: String,
    values: Option<Vec<T>>,
    index: Option<usize>,
    value: Option<T>,
}

impl<T> ApiVectorDiff<T>
where
    T: Clone,
{
    pub fn action(&self) -> String {
        self.action.clone()
    }

    pub fn values(&self) -> Option<Vec<T>> {
        match self.action.as_str() {
            "Append" | "Reset" => self.values.clone(),
            _ => None,
        }
    }

    pub fn index(&self) -> Option<usize> {
        match self.action.as_str() {
            "Insert" | "Set" | "Remove" => self.index,
            _ => None,
        }
    }

    pub fn value(&self) -> Option<T> {
        match self.action.as_str() {
            "Insert" | "Set" | "PushBack" | "PushFront" => self.value.clone(),
            _ => None,
        }
    }
}

impl<T> ApiVectorDiff<T> {
    pub fn current_items(values: Vec<T>) -> Self {
        ApiVectorDiff {
            action: "Reset".to_string(),
            values: Some(values),
            index: None,
            value: None,
        }
    }
}

pub fn remap_for_diff<E, T, C>(diff: VectorDiff<E>, mapper: C) -> ApiVectorDiff<T>
where
    C: Fn(E) -> T,
    T: Clone,
    E: Clone,
{
    match diff {
        // Append the given elements at the end of the `Vector` and notify subscribers
        VectorDiff::Append { values } => ApiVectorDiff {
            action: "Append".to_string(),
            values: Some(values.into_iter().map(mapper).collect()),
            index: None,
            value: None,
        },
        // Insert an element at the given position and notify subscribers
        VectorDiff::Insert { index, value } => ApiVectorDiff {
            action: "Insert".to_string(),
            values: None,
            index: Some(index),
            value: Some(mapper(value)),
        },
        // Replace the element at the given position, notify subscribers and return the previous element at that position
        VectorDiff::Set { index, value } => ApiVectorDiff {
            action: "Set".to_string(),
            values: None,
            index: Some(index),
            value: Some(mapper(value)),
        },
        // Remove the element at the given position, notify subscribers and return the element
        VectorDiff::Remove { index } => ApiVectorDiff {
            action: "Remove".to_string(),
            values: None,
            index: Some(index),
            value: None,
        },
        // Add an element at the back of the list and notify subscribers
        VectorDiff::PushBack { value } => ApiVectorDiff {
            action: "PushBack".to_string(),
            values: None,
            index: None,
            value: Some(mapper(value)),
        },
        // Add an element at the front of the list and notify subscribers
        VectorDiff::PushFront { value } => ApiVectorDiff {
            action: "PushFront".to_string(),
            values: None,
            index: None,
            value: Some(mapper(value)),
        },
        // Remove the last element, notify subscribers and return the element
        VectorDiff::PopBack => ApiVectorDiff {
            action: "PopBack".to_string(),
            values: None,
            index: None,
            value: None,
        },
        // Remove the first element, notify subscribers and return the element
        VectorDiff::PopFront => ApiVectorDiff {
            action: "PopFront".to_string(),
            values: None,
            index: None,
            value: None,
        },
        // Clear out all of the elements in this `Vector` and notify subscribers
        VectorDiff::Clear => ApiVectorDiff {
            action: "Clear".to_string(),
            values: None,
            index: None,
            value: None,
        },
        VectorDiff::Reset { values } => ApiVectorDiff {
            action: "Reset".to_string(),
            values: Some(values.into_iter().map(mapper).collect()),
            index: None,
            value: None,
        },
        // Truncate the vector to `len` elements and notify subscribers
        VectorDiff::Truncate { length } => ApiVectorDiff {
            action: "Truncate".to_string(),
            values: None,
            index: Some(length),
            value: None,
        },
    }
}