yerba 0.7.2 → 0.7.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -8,10 +8,7 @@ impl Document {
8
8
 
9
9
  let current_node = self.navigate(dot_path)?;
10
10
 
11
- let sequence = current_node
12
- .descendants()
13
- .find_map(BlockSeq::cast)
14
- .ok_or_else(|| YerbaError::NotASequence(dot_path.to_string()))?;
11
+ let sequence = find_block_sequence(&current_node).ok_or_else(|| YerbaError::NotASequence(dot_path.to_string()))?;
15
12
 
16
13
  let entries: Vec<_> = sequence.entries().collect();
17
14
 
@@ -25,10 +22,7 @@ impl Document {
25
22
 
26
23
  let current_node = self.navigate(dot_path)?;
27
24
 
28
- let map = current_node
29
- .descendants()
30
- .find_map(BlockMap::cast)
31
- .ok_or_else(|| YerbaError::SelectorNotFound(dot_path.to_string()))?;
25
+ let map = find_block_map(&current_node).ok_or_else(|| YerbaError::SelectorNotFound(dot_path.to_string()))?;
32
26
 
33
27
  let entries: Vec<_> = map.entries().collect();
34
28
 
@@ -38,10 +32,7 @@ impl Document {
38
32
  pub fn resolve_key_index(&self, dot_path: &str, reference: &str) -> Result<usize, YerbaError> {
39
33
  let current_node = self.navigate(dot_path)?;
40
34
 
41
- let map = current_node
42
- .descendants()
43
- .find_map(BlockMap::cast)
44
- .ok_or_else(|| YerbaError::SelectorNotFound(dot_path.to_string()))?;
35
+ let map = find_block_map(&current_node).ok_or_else(|| YerbaError::SelectorNotFound(dot_path.to_string()))?;
45
36
 
46
37
  if let Ok(index) = reference.parse::<usize>() {
47
38
  let length = map.entries().count();
@@ -70,10 +61,7 @@ impl Document {
70
61
  pub fn resolve_sequence_index(&self, dot_path: &str, reference: &str) -> Result<usize, YerbaError> {
71
62
  let current_node = self.navigate(dot_path)?;
72
63
 
73
- let sequence = current_node
74
- .descendants()
75
- .find_map(BlockSeq::cast)
76
- .ok_or_else(|| YerbaError::NotASequence(dot_path.to_string()))?;
64
+ let sequence = find_block_sequence(&current_node).ok_or_else(|| YerbaError::NotASequence(dot_path.to_string()))?;
77
65
 
78
66
  if let Ok(index) = reference.parse::<usize>() {
79
67
  let length = sequence.entries().count();
@@ -118,7 +106,7 @@ impl Document {
118
106
  Err(_) => return Ok(()),
119
107
  };
120
108
 
121
- let map = match current_node.descendants().find_map(BlockMap::cast) {
109
+ let map = match find_block_map(&current_node) {
122
110
  Some(map) => map,
123
111
  None => return Ok(()),
124
112
  };
@@ -146,59 +134,15 @@ impl Document {
146
134
  Err(_) => return Ok(()),
147
135
  };
148
136
 
149
- let map = match current_node.descendants().find_map(BlockMap::cast) {
137
+ let map = match find_block_map(&current_node) {
150
138
  Some(map) => map,
151
139
  None => return Ok(()),
152
140
  };
153
141
 
154
- let entries: Vec<_> = map.entries().collect();
155
-
156
- if entries.len() <= 1 {
157
- return Ok(());
158
- }
159
-
160
- let (groups, range) = collect_groups_with_range(map.syntax());
161
-
162
- let mut keyed: Vec<(String, EntryGroup)> = entries
163
- .iter()
164
- .zip(groups)
165
- .map(|(entry, group)| {
166
- let key_name = entry.key().and_then(|key_node| extract_scalar_text(key_node.syntax())).unwrap_or_default();
167
- (key_name, group)
168
- })
169
- .collect();
170
-
171
- let original_keys: Vec<String> = keyed.iter().map(|(key, _)| key.clone()).collect();
172
-
173
- keyed.sort_by(|(key_a, _), (key_b, _)| {
174
- let position_a = key_order.iter().position(|&key| key == key_a);
175
- let position_b = key_order.iter().position(|&key| key == key_b);
176
-
177
- match (position_a, position_b) {
178
- (Some(a), Some(b)) => a.cmp(&b),
179
- (Some(_), None) => std::cmp::Ordering::Less,
180
- (None, Some(_)) => std::cmp::Ordering::Greater,
181
- (None, None) => {
182
- let original_a = original_keys.iter().position(|key| key == key_a).unwrap();
183
- let original_b = original_keys.iter().position(|key| key == key_b).unwrap();
184
- original_a.cmp(&original_b)
185
- }
186
- }
187
- });
188
-
189
- let sorted_keys: Vec<&str> = keyed.iter().map(|(key, _)| key.as_str()).collect();
190
- let orig_refs: Vec<&str> = original_keys.iter().map(|key| key.as_str()).collect();
191
-
192
- if sorted_keys == orig_refs {
193
- return Ok(());
142
+ match sort_map_edit(&map, key_order) {
143
+ Some(edit) => self.apply_edits(vec![edit]),
144
+ None => Ok(()),
194
145
  }
195
-
196
- let indent = entries.get(1).map(|entry| preceding_whitespace_indent(entry.syntax())).unwrap_or_default();
197
-
198
- let sorted_groups: Vec<EntryGroup> = keyed.into_iter().map(|(_, group)| group).collect();
199
- let map_text = rebuild_from_groups(&sorted_groups, &indent, false);
200
-
201
- self.apply_edit(range, &map_text)
202
146
  }
203
147
 
204
148
  pub fn sort_each_keys(&mut self, dot_path: &str, key_order: &[&str]) -> Result<(), YerbaError> {
@@ -218,92 +162,24 @@ impl Document {
218
162
  let mut edits: Vec<(TextRange, String)> = Vec::new();
219
163
 
220
164
  for current_node in &nodes {
221
- let sequence = match current_node.descendants().find_map(BlockSeq::cast) {
165
+ let sequence = match find_block_sequence(current_node) {
222
166
  Some(sequence) => sequence,
223
167
  None => continue,
224
168
  };
225
169
 
226
170
  for entry in sequence.entries() {
227
- let entry_node = entry.syntax();
228
-
229
- let map = match entry_node.descendants().find_map(BlockMap::cast) {
171
+ let map = match find_block_map(entry.syntax()) {
230
172
  Some(map) => map,
231
173
  None => continue,
232
174
  };
233
175
 
234
- let entries: Vec<_> = map.entries().collect();
235
-
236
- if entries.len() <= 1 {
237
- continue;
238
- }
239
-
240
- let (groups, group_range) = collect_groups_with_range(map.syntax());
241
-
242
- let mut keyed: Vec<(String, EntryGroup)> = entries
243
- .iter()
244
- .zip(groups)
245
- .map(|(entry, group)| {
246
- let key_name = entry.key().and_then(|key_node| extract_scalar_text(key_node.syntax())).unwrap_or_default();
247
- (key_name, group)
248
- })
249
- .collect();
250
-
251
- let original_keys: Vec<String> = keyed.iter().map(|(key, _)| key.clone()).collect();
252
-
253
- keyed.sort_by(|(key_a, _), (key_b, _)| {
254
- let position_a = key_order.iter().position(|&key| key == key_a);
255
- let position_b = key_order.iter().position(|&key| key == key_b);
256
-
257
- match (position_a, position_b) {
258
- (Some(a), Some(b)) => a.cmp(&b),
259
- (Some(_), None) => std::cmp::Ordering::Less,
260
- (None, Some(_)) => std::cmp::Ordering::Greater,
261
-
262
- (None, None) => {
263
- let original_a = original_keys.iter().position(|key| key == key_a).unwrap();
264
- let original_b = original_keys.iter().position(|key| key == key_b).unwrap();
265
-
266
- original_a.cmp(&original_b)
267
- }
268
- }
269
- });
270
-
271
- let sorted_keys: Vec<&str> = keyed.iter().map(|(key, _)| key.as_str()).collect();
272
- let orig_refs: Vec<&str> = original_keys.iter().map(|key| key.as_str()).collect();
273
-
274
- if sorted_keys == orig_refs {
275
- continue;
176
+ if let Some(edit) = sort_map_edit(&map, key_order) {
177
+ edits.push(edit);
276
178
  }
277
-
278
- let indent = entries.get(1).map(|entry| preceding_whitespace_indent(entry.syntax())).unwrap_or_default();
279
-
280
- let sorted_groups: Vec<EntryGroup> = keyed.into_iter().map(|(_, group)| group).collect();
281
- let map_text = rebuild_from_groups(&sorted_groups, &indent, false);
282
- edits.push((group_range, map_text));
283
179
  }
284
180
  }
285
181
 
286
- if edits.is_empty() {
287
- return Ok(());
288
- }
289
-
290
- edits.reverse();
291
-
292
- let source = self.root.text().to_string();
293
- let mut new_source = source;
294
-
295
- for (range, replacement) in edits {
296
- let start: usize = range.start().into();
297
- let end: usize = range.end().into();
298
-
299
- new_source.replace_range(start..end, &replacement);
300
- }
301
-
302
- let path = self.path.take();
303
- *self = Self::parse(&new_source)?;
304
- self.path = path;
305
-
306
- Ok(())
182
+ self.apply_edits(edits)
307
183
  }
308
184
 
309
185
  pub fn validate_each_sort_keys(&self, dot_path: &str, key_order: &[&str]) -> Result<(), YerbaError> {
@@ -323,13 +199,13 @@ impl Document {
323
199
  let mut all_unknown: Vec<String> = Vec::new();
324
200
 
325
201
  for current_node in &nodes {
326
- let sequence = match current_node.descendants().find_map(BlockSeq::cast) {
202
+ let sequence = match find_block_sequence(current_node) {
327
203
  Some(sequence) => sequence,
328
204
  None => continue,
329
205
  };
330
206
 
331
207
  for entry in sequence.entries() {
332
- if let Some(map) = entry.syntax().descendants().find_map(BlockMap::cast) {
208
+ if let Some(map) = find_block_map(entry.syntax()) {
333
209
  for map_entry in map.entries() {
334
210
  if let Some(key_name) = map_entry.key().and_then(|key_node| extract_scalar_text(key_node.syntax())) {
335
211
  if !key_order.contains(&key_name.as_str()) && !all_unknown.contains(&key_name) {
@@ -355,86 +231,15 @@ impl Document {
355
231
 
356
232
  let current_node = self.navigate(dot_path)?;
357
233
 
358
- let sequence = match current_node.descendants().find_map(BlockSeq::cast) {
234
+ let sequence = match find_block_sequence(&current_node) {
359
235
  Some(sequence) => sequence,
360
236
  None => return Ok(()),
361
237
  };
362
238
 
363
- let entries: Vec<_> = sequence.entries().collect();
364
-
365
- if entries.len() <= 1 {
366
- return Ok(());
367
- }
368
-
369
- let (groups, range) = collect_groups_with_range(sequence.syntax());
370
-
371
- let mut sortable: Vec<(Vec<String>, EntryGroup)> = entries
372
- .iter()
373
- .zip(groups)
374
- .map(|(entry, group)| {
375
- let sort_values = if sort_fields.is_empty() {
376
- vec![entry.flow().and_then(|flow| extract_scalar_text(flow.syntax())).unwrap_or_default()]
377
- } else {
378
- sort_fields
379
- .iter()
380
- .map(|field| {
381
- if field.path.is_empty() {
382
- entry.flow().and_then(|flow| extract_scalar_text(flow.syntax())).unwrap_or_default()
383
- } else {
384
- let nodes = navigate_from_node(entry.syntax(), &field.path);
385
- nodes.first().and_then(extract_scalar_text).unwrap_or_default()
386
- }
387
- })
388
- .collect()
389
- };
390
-
391
- (sort_values, group)
392
- })
393
- .collect();
394
-
395
- let original_bodies: Vec<String> = sortable.iter().map(|(_, group)| group.body.clone()).collect();
396
-
397
- sortable.sort_by(|(values_a, _), (values_b, _)| {
398
- for (index, field) in sort_fields.iter().enumerate().take(values_a.len()) {
399
- let value_a = &values_a[index];
400
- let value_b = &values_b[index];
401
-
402
- let ordering = if case_sensitive {
403
- value_a.cmp(value_b)
404
- } else {
405
- value_a.to_lowercase().cmp(&value_b.to_lowercase())
406
- };
407
-
408
- let ordering = if field.ascending { ordering } else { ordering.reverse() };
409
-
410
- if ordering != std::cmp::Ordering::Equal {
411
- return ordering;
412
- }
413
- }
414
-
415
- if sort_fields.is_empty() && !values_a.is_empty() && !values_b.is_empty() {
416
- return if case_sensitive {
417
- values_a[0].cmp(&values_b[0])
418
- } else {
419
- values_a[0].to_lowercase().cmp(&values_b[0].to_lowercase())
420
- };
421
- }
422
-
423
- std::cmp::Ordering::Equal
424
- });
425
-
426
- let sorted_bodies: Vec<String> = sortable.iter().map(|(_, group)| group.body.clone()).collect();
427
-
428
- if sorted_bodies == original_bodies {
429
- return Ok(());
239
+ match sort_sequence_edit(&sequence, sort_fields, case_sensitive) {
240
+ Some(edit) => self.apply_edits(vec![edit]),
241
+ None => Ok(()),
430
242
  }
431
-
432
- let indent = entries.get(1).map(|entry| preceding_whitespace_indent(entry.syntax())).unwrap_or_default();
433
-
434
- let sorted_groups: Vec<EntryGroup> = sortable.into_iter().map(|(_, group)| group).collect();
435
- let sequence_text = rebuild_from_groups(&sorted_groups, &indent, true);
436
-
437
- self.apply_edit(range, &sequence_text)
438
243
  }
439
244
 
440
245
  fn sort_each_items(&mut self, dot_path: &str, sort_fields: &[SortField], case_sensitive: bool) -> Result<(), YerbaError> {
@@ -445,7 +250,6 @@ impl Document {
445
250
  };
446
251
 
447
252
  let parent_nodes = self.navigate_all_compact(parent_path);
448
- let source = self.root.text().to_string();
449
253
  let mut edits: Vec<(TextRange, String)> = Vec::new();
450
254
 
451
255
  for parent_node in &parent_nodes {
@@ -456,108 +260,18 @@ impl Document {
456
260
  };
457
261
 
458
262
  for child_node in &child_nodes {
459
- let sequence = match child_node.descendants().find_map(BlockSeq::cast) {
263
+ let sequence = match find_block_sequence(child_node) {
460
264
  Some(sequence) => sequence,
461
265
  None => continue,
462
266
  };
463
267
 
464
- let entries: Vec<_> = sequence.entries().collect();
465
-
466
- if entries.len() <= 1 {
467
- continue;
468
- }
469
-
470
- let (groups, group_range) = collect_groups_with_range(sequence.syntax());
471
-
472
- let mut sortable: Vec<(Vec<String>, EntryGroup)> = entries
473
- .iter()
474
- .zip(groups)
475
- .map(|(entry, group)| {
476
- let sort_values = if sort_fields.is_empty() {
477
- vec![entry.flow().and_then(|flow| extract_scalar_text(flow.syntax())).unwrap_or_default()]
478
- } else {
479
- sort_fields
480
- .iter()
481
- .map(|field| {
482
- if field.path.is_empty() {
483
- entry.flow().and_then(|flow| extract_scalar_text(flow.syntax())).unwrap_or_default()
484
- } else {
485
- let nodes = navigate_from_node(entry.syntax(), &field.path);
486
- nodes.first().and_then(extract_scalar_text).unwrap_or_default()
487
- }
488
- })
489
- .collect()
490
- };
491
-
492
- (sort_values, group)
493
- })
494
- .collect();
495
-
496
- let original_bodies: Vec<String> = sortable.iter().map(|(_, group)| group.body.clone()).collect();
497
-
498
- sortable.sort_by(|(values_a, _), (values_b, _)| {
499
- for (index, field) in sort_fields.iter().enumerate().take(values_a.len()) {
500
- let value_a = &values_a[index];
501
- let value_b = &values_b[index];
502
-
503
- let ordering = if case_sensitive {
504
- value_a.cmp(value_b)
505
- } else {
506
- value_a.to_lowercase().cmp(&value_b.to_lowercase())
507
- };
508
-
509
- let ordering = if field.ascending { ordering } else { ordering.reverse() };
510
-
511
- if ordering != std::cmp::Ordering::Equal {
512
- return ordering;
513
- }
514
- }
515
-
516
- if sort_fields.is_empty() && !values_a.is_empty() && !values_b.is_empty() {
517
- return if case_sensitive {
518
- values_a[0].cmp(&values_b[0])
519
- } else {
520
- values_a[0].to_lowercase().cmp(&values_b[0].to_lowercase())
521
- };
522
- }
523
-
524
- std::cmp::Ordering::Equal
525
- });
526
-
527
- let sorted_bodies: Vec<String> = sortable.iter().map(|(_, group)| group.body.clone()).collect();
528
-
529
- if sorted_bodies == original_bodies {
530
- continue;
268
+ if let Some(edit) = sort_sequence_edit(&sequence, sort_fields, case_sensitive) {
269
+ edits.push(edit);
531
270
  }
532
-
533
- let indent = entries.get(1).map(|entry| preceding_whitespace_indent(entry.syntax())).unwrap_or_default();
534
- let sorted_groups: Vec<EntryGroup> = sortable.into_iter().map(|(_, group)| group).collect();
535
- let sequence_text = rebuild_from_groups(&sorted_groups, &indent, true);
536
-
537
- edits.push((group_range, sequence_text));
538
271
  }
539
272
  }
540
273
 
541
- if edits.is_empty() {
542
- return Ok(());
543
- }
544
-
545
- edits.reverse();
546
-
547
- let mut new_source = source;
548
-
549
- for (range, replacement) in edits {
550
- let start: usize = range.start().into();
551
- let end: usize = range.end().into();
552
-
553
- new_source.replace_range(start..end, &replacement);
554
- }
555
-
556
- let path = self.path.take();
557
- *self = Self::parse(&new_source)?;
558
- self.path = path;
559
-
560
- Ok(())
274
+ self.apply_edits(edits)
561
275
  }
562
276
 
563
277
  pub fn reorder_items(&mut self, dot_path: &str, by: &str, desired_order: &[&str]) -> Result<(), YerbaError> {
@@ -634,6 +348,132 @@ impl Document {
634
348
  }
635
349
  }
636
350
 
351
+ fn sort_map_edit(map: &BlockMap, key_order: &[&str]) -> Option<(TextRange, String)> {
352
+ let entries: Vec<_> = map.entries().collect();
353
+
354
+ if entries.len() <= 1 {
355
+ return None;
356
+ }
357
+
358
+ let (groups, range) = collect_groups_with_range(map.syntax());
359
+
360
+ let mut keyed: Vec<(String, EntryGroup)> = entries
361
+ .iter()
362
+ .zip(groups)
363
+ .map(|(entry, group)| {
364
+ let key_name = entry.key().and_then(|key_node| extract_scalar_text(key_node.syntax())).unwrap_or_default();
365
+ (key_name, group)
366
+ })
367
+ .collect();
368
+
369
+ let original_keys: Vec<String> = keyed.iter().map(|(key, _)| key.clone()).collect();
370
+
371
+ keyed.sort_by(|(key_a, _), (key_b, _)| {
372
+ let position_a = key_order.iter().position(|&key| key == key_a);
373
+ let position_b = key_order.iter().position(|&key| key == key_b);
374
+
375
+ match (position_a, position_b) {
376
+ (Some(a), Some(b)) => a.cmp(&b),
377
+ (Some(_), None) => std::cmp::Ordering::Less,
378
+ (None, Some(_)) => std::cmp::Ordering::Greater,
379
+ (None, None) => {
380
+ let original_a = original_keys.iter().position(|key| key == key_a).unwrap();
381
+ let original_b = original_keys.iter().position(|key| key == key_b).unwrap();
382
+
383
+ original_a.cmp(&original_b)
384
+ }
385
+ }
386
+ });
387
+
388
+ let sorted_keys: Vec<&str> = keyed.iter().map(|(key, _)| key.as_str()).collect();
389
+ let original_refs: Vec<&str> = original_keys.iter().map(|key| key.as_str()).collect();
390
+
391
+ if sorted_keys == original_refs {
392
+ return None;
393
+ }
394
+
395
+ let indent = entries.get(1).map(|entry| preceding_whitespace_indent(entry.syntax())).unwrap_or_default();
396
+ let sorted_groups: Vec<EntryGroup> = keyed.into_iter().map(|(_, group)| group).collect();
397
+
398
+ Some((range, rebuild_from_groups(&sorted_groups, &indent, false)))
399
+ }
400
+
401
+ fn sort_sequence_edit(sequence: &BlockSeq, sort_fields: &[SortField], case_sensitive: bool) -> Option<(TextRange, String)> {
402
+ let entries: Vec<_> = sequence.entries().collect();
403
+
404
+ if entries.len() <= 1 {
405
+ return None;
406
+ }
407
+
408
+ let (groups, range) = collect_groups_with_range(sequence.syntax());
409
+
410
+ let mut sortable: Vec<(Vec<String>, EntryGroup)> = entries
411
+ .iter()
412
+ .zip(groups)
413
+ .map(|(entry, group)| (entry_sort_values(entry, sort_fields), group))
414
+ .collect();
415
+
416
+ let original_bodies: Vec<String> = sortable.iter().map(|(_, group)| group.body.clone()).collect();
417
+
418
+ sortable.sort_by(|(values_a, _), (values_b, _)| compare_sort_values(values_a, values_b, sort_fields, case_sensitive));
419
+
420
+ let sorted_bodies: Vec<String> = sortable.iter().map(|(_, group)| group.body.clone()).collect();
421
+
422
+ if sorted_bodies == original_bodies {
423
+ return None;
424
+ }
425
+
426
+ let indent = entries.get(1).map(|entry| preceding_whitespace_indent(entry.syntax())).unwrap_or_default();
427
+ let sorted_groups: Vec<EntryGroup> = sortable.into_iter().map(|(_, group)| group).collect();
428
+
429
+ Some((range, rebuild_from_groups(&sorted_groups, &indent, true)))
430
+ }
431
+
432
+ fn entry_sort_values(entry: &yaml_parser::ast::BlockSeqEntry, sort_fields: &[SortField]) -> Vec<String> {
433
+ let scalar_value = || entry.flow().and_then(|flow| extract_scalar_text(flow.syntax())).unwrap_or_default();
434
+
435
+ if sort_fields.is_empty() {
436
+ return vec![scalar_value()];
437
+ }
438
+
439
+ sort_fields
440
+ .iter()
441
+ .map(|field| {
442
+ if field.path.is_empty() {
443
+ scalar_value()
444
+ } else {
445
+ let nodes = navigate_from_node(entry.syntax(), &field.path);
446
+ nodes.first().and_then(extract_scalar_text).unwrap_or_default()
447
+ }
448
+ })
449
+ .collect()
450
+ }
451
+
452
+ fn compare_sort_values(values_a: &[String], values_b: &[String], sort_fields: &[SortField], case_sensitive: bool) -> std::cmp::Ordering {
453
+ let compare = |value_a: &String, value_b: &String| {
454
+ if case_sensitive {
455
+ value_a.cmp(value_b)
456
+ } else {
457
+ value_a.to_lowercase().cmp(&value_b.to_lowercase())
458
+ }
459
+ };
460
+
461
+ for (index, field) in sort_fields.iter().enumerate().take(values_a.len()) {
462
+ let ordering = compare(&values_a[index], &values_b[index]);
463
+ let ordering = if field.ascending { ordering } else { ordering.reverse() };
464
+
465
+ if ordering != std::cmp::Ordering::Equal {
466
+ return ordering;
467
+ }
468
+ }
469
+
470
+ if sort_fields.is_empty() && !values_a.is_empty() && !values_b.is_empty() {
471
+ return compare(&values_a[0], &values_b[0]);
472
+ }
473
+
474
+ std::cmp::Ordering::Equal
475
+ }
476
+
637
477
  fn strip_bracket_suffix(path: &str) -> Option<&str> {
638
478
  if path == "[]" {
639
479
  Some("")