yamlette/model/yaml/
pairs.rs

1extern crate skimmer;
2
3use crate::model::renderer::{Node, Renderer};
4use crate::model::style::CommonStyles;
5use crate::model::{model_alias, model_tag, Model, Rope, Tagged, TaggedValue};
6
7use std::any::Any;
8use std::borrow::Cow;
9use std::iter::Iterator;
10
11pub static TAG: &'static str = "tag:yaml.org,2002:pairs";
12
13#[derive(Clone, Copy)]
14pub struct Pairs;
15
16impl Pairs {
17    pub fn get_tag() -> Cow<'static, str> {
18        Cow::from(TAG)
19    }
20}
21
22impl Model for Pairs {
23    fn get_tag(&self) -> Cow<'static, str> {
24        Self::get_tag()
25    }
26
27    fn as_any(&self) -> &dyn Any {
28        self
29    }
30
31    fn as_mut_any(&mut self) -> &mut dyn Any {
32        self
33    }
34
35    fn is_dictionary(&self) -> bool {
36        true
37    }
38
39    fn compose(
40        &self,
41        renderer: &Renderer,
42        value: TaggedValue,
43        tags: &mut dyn Iterator<Item = &(Cow<'static, str>, Cow<'static, str>)>,
44        children: &mut [Rope],
45    ) -> Rope {
46        compose(self, renderer, value, tags, children)
47    }
48}
49
50pub fn compose(
51    model: &dyn Model,
52    renderer: &Renderer,
53    value: TaggedValue,
54    tags: &mut dyn Iterator<Item = &(Cow<'static, str>, Cow<'static, str>)>,
55    children: &mut [Rope],
56) -> Rope {
57    let value: PairsValue =
58        match <TaggedValue as Into<Result<PairsValue, TaggedValue>>>::into(value) {
59            Ok(value) => value,
60            Err(_) => panic!("Not a PairsValue"),
61        };
62
63    if children.len() == 0 {
64        return compose_empty(model, value, tags);
65    }
66
67    if value.styles.flow() {
68        if value.styles.multiline() {
69            compose_flow_multiline(model, value, tags, children)
70        } else if value.styles.respect_threshold() {
71            compose_flow_respect_threshold(model, renderer, value, tags, children)
72        } else {
73            compose_flow_no_threshold(model, value, tags, children)
74        }
75    } else {
76        compose_block(model, value, tags, children)
77    }
78}
79
80fn compose_empty(
81    model: &dyn Model,
82    mut value: PairsValue,
83    tags: &mut dyn Iterator<Item = &(Cow<'static, str>, Cow<'static, str>)>,
84) -> Rope {
85    if let Some(alias) = value.take_alias() {
86        if value.styles.issue_tag() {
87            Rope::from(vec![
88                model_tag(model, tags),
89                Node::Space,
90                model_alias(model, alias),
91                Node::Space,
92                Node::SquareBrackets,
93            ])
94        } else {
95            Rope::from(vec![
96                model_alias(model, alias),
97                Node::Space,
98                Node::SquareBrackets,
99            ])
100        }
101    } else {
102        if value.styles.issue_tag() {
103            Rope::from(vec![
104                model_tag(model, tags),
105                Node::Space,
106                Node::SquareBrackets,
107            ])
108        } else {
109            Rope::from(Node::SquareBrackets)
110        }
111    }
112}
113
114fn compose_flow_multiline(
115    model: &dyn Model,
116    mut value: PairsValue,
117    tags: &mut dyn Iterator<Item = &(Cow<'static, str>, Cow<'static, str>)>,
118    children: &mut [Rope],
119) -> Rope {
120    let indent_len = value.styles.indent() as usize;
121    let issue_tag = value.styles.issue_tag();
122    let alias = value.take_alias();
123
124    let mut rope_length = 3;
125
126    if issue_tag {
127        rope_length += 2;
128    }
129    if alias.is_some() {
130        rope_length += 2;
131    }
132
133    for child in children.iter() {
134        rope_length += child.len() + 1; // colon+space / comma+newline
135    }
136
137    let mut rope = Rope::with_capacity(rope_length);
138
139    if issue_tag {
140        rope.push(model_tag(model, tags));
141        if let Some(alias) = alias {
142            rope.push(Node::Space);
143            rope.push(model_alias(model, alias));
144        }
145        rope.push(Node::Space);
146    } else if let Some(alias) = alias {
147        rope.push(model_alias(model, alias));
148        rope.push(Node::Space);
149    }
150
151    rope.push(Node::SquareBracketOpen);
152    rope.push(Node::NewlineIndent(indent_len));
153
154    let last_child_idx = children.len() - 1;
155    let penult_child_idx = children.len() - 2;
156
157    let mut i = 0;
158    loop {
159        if i > last_child_idx {
160            break;
161        }
162
163        {
164            let key = unsafe { children.get_unchecked_mut(i) };
165
166            key.indent(indent_len);
167
168            rope.knit(key);
169        }
170
171        if i == last_child_idx {
172            rope.push(Node::ColonNewline);
173            break;
174        } else {
175            rope.push(Node::ColonSpace);
176        }
177
178        {
179            let val = unsafe { children.get_unchecked_mut(i + 1) };
180
181            rope.knit(val);
182        }
183
184        if i == penult_child_idx {
185            rope.push(Node::NewlineIndent(0));
186        } else {
187            rope.push(Node::CommaNewlineIndent(indent_len));
188        }
189
190        i += 2;
191    }
192
193    rope.push(Node::SquareBracketClose);
194
195    rope
196}
197
198fn compose_flow_respect_threshold(
199    model: &dyn Model,
200    renderer: &Renderer,
201    mut value: PairsValue,
202    tags: &mut dyn Iterator<Item = &(Cow<'static, str>, Cow<'static, str>)>,
203    children: &mut [Rope],
204) -> Rope {
205    let indent_len = value.styles.indent() as usize;
206    let compact = value.styles.compact();
207    let threshold = value.styles.threshold() as usize;
208    let issue_tag = value.styles.issue_tag();
209    let alias = value.take_alias();
210
211    let mut rope_length = 3;
212
213    if issue_tag {
214        rope_length += 2;
215    }
216    if alias.is_some() {
217        rope_length += 2;
218    }
219
220    for child in children.iter() {
221        rope_length += child.len() + 2;
222    }
223
224    let mut rope = Rope::with_capacity(rope_length);
225
226    if issue_tag {
227        rope.push(model_tag(model, tags));
228        if let Some(alias) = alias {
229            rope.push(Node::Space);
230            rope.push(model_alias(model, alias));
231        }
232        rope.push(Node::Space);
233    } else if let Some(alias) = alias {
234        rope.push(model_alias(model, alias));
235        rope.push(Node::Space);
236    }
237
238    rope.push(Node::SquareBracketOpen);
239    if !compact {
240        rope.push(Node::Space);
241    }
242
243    let last_child_idx = children.len() - 1;
244    let penult_child_idx = children.len() - 2;
245
246    let comma_len = renderer.node_len(&Node::Comma);
247    let colon_len = renderer.node_len(&Node::Colon);
248    let space_len = renderer.node_len(&Node::Space);
249
250    let mut line_len = rope.bytes_len(renderer);
251
252    let mut i = 0;
253    loop {
254        if i > last_child_idx {
255            break;
256        }
257
258        {
259            let key = unsafe { children.get_unchecked_mut(i) };
260
261            key.indent(indent_len);
262
263            let (key_first_line_len, nl) = key.first_line_bytes_len(renderer);
264
265            if !compact {
266                line_len += space_len;
267            }
268            line_len += key_first_line_len;
269
270            if i != 0 {
271                if line_len > threshold {
272                    rope.push(Node::NewlineIndent(0));
273                    line_len = if nl {
274                        let (last_line_len, _) = key.last_line_bytes_len(renderer);
275                        last_line_len
276                    } else {
277                        key_first_line_len
278                    };
279                } else {
280                    if !compact {
281                        rope.push(Node::Space);
282                    }
283
284                    if nl {
285                        let (last_line_len, _) = key.last_line_bytes_len(renderer);
286                        line_len = last_line_len;
287                    }
288                }
289            }
290
291            rope.knit(key);
292        }
293
294        if i == last_child_idx {
295            rope.push(Node::Colon);
296            break;
297        }
298
299        {
300            let val = unsafe { children.get_unchecked_mut(i + 1) };
301
302            val.indent(indent_len);
303
304            let (first_line_len, nl) = val.first_line_bytes_len(renderer);
305
306            line_len += colon_len + space_len + first_line_len + comma_len;
307
308            if line_len > threshold {
309                rope.push(Node::ColonNewline);
310                if nl {
311                    let (last_line_len, _) = val.last_line_bytes_len(renderer);
312                    line_len = last_line_len;
313                } else {
314                    line_len = first_line_len;
315                }
316            } else {
317                rope.push(Node::ColonSpace);
318
319                if nl {
320                    let (last_line_len, _) = val.last_line_bytes_len(renderer);
321                    line_len = last_line_len;
322                }
323            }
324
325            rope.knit(val);
326        }
327
328        if i != penult_child_idx {
329            rope.push(Node::Comma);
330        } else if !compact {
331            rope.push(Node::Space);
332        }
333
334        i += 2;
335    }
336
337    rope.push(Node::SquareBracketClose);
338
339    rope
340}
341
342fn compose_flow_no_threshold(
343    model: &dyn Model,
344    mut value: PairsValue,
345    tags: &mut dyn Iterator<Item = &(Cow<'static, str>, Cow<'static, str>)>,
346    children: &mut [Rope],
347) -> Rope {
348    let indent_len = value.styles.indent() as usize;
349    let compact = value.styles.compact();
350    let issue_tag = value.styles.issue_tag();
351    let alias = value.take_alias();
352
353    let mut rope_length = 3;
354
355    if issue_tag {
356        rope_length += 2;
357    }
358    if alias.is_some() {
359        rope_length += 2;
360    }
361
362    for child in children.iter() {
363        rope_length += child.len() + 1; // colon+space / comma+newline
364    }
365
366    let mut rope = Rope::with_capacity(rope_length);
367
368    if issue_tag {
369        rope.push(model_tag(model, tags));
370        if let Some(alias) = alias {
371            rope.push(Node::Space);
372            rope.push(model_alias(model, alias));
373        }
374        rope.push(Node::Space);
375    } else if let Some(alias) = alias {
376        rope.push(model_alias(model, alias));
377        rope.push(Node::Space);
378    }
379
380    rope.push(Node::SquareBracketOpen);
381    if !compact {
382        rope.push(Node::Space);
383    }
384
385    let last_child_idx = children.len() - 1;
386    let penult_child_idx = children.len() - 2;
387
388    let mut i = 0;
389    loop {
390        if i > last_child_idx {
391            break;
392        }
393
394        {
395            let key = unsafe { children.get_unchecked_mut(i) };
396
397            key.indent(indent_len);
398
399            rope.knit(key);
400        }
401
402        if i == last_child_idx {
403            rope.push(Node::Colon);
404            break;
405        } else {
406            rope.push(Node::ColonSpace);
407        }
408
409        {
410            let val = unsafe { children.get_unchecked_mut(i + 1) };
411
412            val.indent(indent_len);
413
414            rope.knit(val);
415        }
416
417        if i != penult_child_idx {
418            if compact {
419                rope.push(Node::Comma);
420            } else {
421                rope.push(Node::CommaSpace);
422            }
423        } else if !compact {
424            rope.push(Node::Space);
425        }
426
427        i += 2;
428    }
429
430    rope.push(Node::SquareBracketClose);
431
432    rope
433}
434
435fn compose_block(
436    model: &dyn Model,
437    mut value: PairsValue,
438    tags: &mut dyn Iterator<Item = &(Cow<'static, str>, Cow<'static, str>)>,
439    children: &mut [Rope],
440) -> Rope {
441    let indent_len = value.styles.indent() as usize;
442    let issue_tag = value.styles.issue_tag();
443    let alias = value.take_alias();
444
445    let mut rope_length = if issue_tag { 3 } else { 1 };
446    for child in children.iter() {
447        rope_length += child.len() + 3;
448    }
449    if alias.is_some() {
450        rope_length += 2;
451    }
452
453    let mut rope = Rope::with_capacity(rope_length);
454
455    if issue_tag {
456        rope.push(model_tag(model, tags));
457        if let Some(alias) = alias {
458            rope.push(Node::Space);
459            rope.push(model_alias(model, alias));
460        }
461        rope.push(Node::NewlineIndent(0));
462    } else if let Some(alias) = alias {
463        rope.push(model_alias(model, alias));
464        rope.push(Node::NewlineIndent(0));
465    }
466
467    let last_child_idx = children.len() - 1;
468    let penult_child_idx = if children.len() < 2 {
469        0
470    } else {
471        children.len() - 2
472    };
473
474    let mut i = 0;
475
476    loop {
477        if i > last_child_idx {
478            break;
479        }
480
481        if i == 0 {
482            rope.push(Node::HyphenSpace);
483        }
484
485        {
486            let key = unsafe { children.get_unchecked_mut(i) };
487
488            let is_multiline = key.is_multiline();
489            let is_flow = key.is_flow_opening();
490
491            if is_multiline && !is_flow {
492                rope.push(Node::QuestionNewlineIndent(indent_len));
493                key.indent(indent_len);
494            }
495
496            rope.knit(key);
497        }
498
499        if i == last_child_idx {
500            rope.push(Node::ColonNewline);
501            break;
502        }
503
504        {
505            let val = unsafe { children.get_unchecked_mut(i + 1) };
506
507            let is_multiline = val.is_multiline();
508            let is_flow = val.is_flow_opening();
509
510            if is_multiline && !is_flow {
511                rope.push(Node::ColonNewlineIndent(indent_len));
512                val.indent(indent_len);
513                rope.knit(val);
514
515                rope.push(Node::IndentHyphenSpace(0));
516            } else {
517                rope.push(Node::ColonSpace);
518                rope.knit(val);
519
520                if i == penult_child_idx {
521                    rope.push(Node::Newline);
522                } else {
523                    rope.push(Node::NewlineIndentHyphenSpace(0));
524                }
525            }
526        }
527
528        i += 2;
529    }
530
531    rope
532}
533
534#[derive(Debug)]
535pub struct PairsValue {
536    styles: CommonStyles,
537    alias: Option<Cow<'static, str>>,
538}
539
540impl PairsValue {
541    pub fn new(styles: CommonStyles, alias: Option<Cow<'static, str>>) -> PairsValue {
542        PairsValue {
543            styles: styles,
544            alias: alias,
545        }
546    }
547
548    pub fn take_alias(&mut self) -> Option<Cow<'static, str>> {
549        self.alias.take()
550    }
551}
552
553impl Tagged for PairsValue {
554    fn get_tag(&self) -> Cow<'static, str> {
555        Cow::from(TAG)
556    }
557
558    fn as_any(&self) -> &dyn Any {
559        self as &dyn Any
560    }
561
562    fn as_mut_any(&mut self) -> &mut dyn Any {
563        self as &mut dyn Any
564    }
565}
566
567#[cfg(all(test, not(feature = "dev")))]
568mod tests {
569    use super::*;
570
571    #[test]
572    fn tag() {
573        let pairs = Pairs;
574
575        assert_eq!(pairs.get_tag(), TAG);
576    }
577}