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; }
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; }
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}