yerba 0.5.0 → 0.6.0

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.
@@ -22,9 +22,9 @@ impl Document {
22
22
  }
23
23
  }
24
24
 
25
- if let Some(serde_yaml::Value::Sequence(sequence)) = self.get_value(dot_path).as_ref() {
26
- if let Some(serde_yaml::Value::Mapping(map)) = sequence.first() {
27
- if let Some((serde_yaml::Value::String(key_name), _)) = map.iter().next() {
25
+ if let Some(yaml_serde::Value::Sequence(sequence)) = self.get_value(dot_path).as_ref() {
26
+ if let Some(yaml_serde::Value::Mapping(map)) = sequence.first() {
27
+ if let Some((yaml_serde::Value::String(key_name), _)) = map.iter().next() {
28
28
  let deep_path = if dot_path.is_empty() {
29
29
  format!("[].{}", key_name)
30
30
  } else {
@@ -126,23 +126,109 @@ impl Document {
126
126
  Self::validate_path(dot_path)?;
127
127
 
128
128
  if let Ok(current_node) = self.navigate(dot_path) {
129
- if current_node.descendants().find_map(BlockSeq::cast).is_some() {
129
+ if current_node.descendants().find_map(BlockSeq::cast).is_some()
130
+ || matches!(self.get_value(dot_path).as_ref(), Some(yaml_serde::Value::Sequence(sequence)) if sequence.is_empty())
131
+ {
130
132
  return self.insert_sequence_item(dot_path, value, position);
131
133
  }
132
134
  }
133
135
 
136
+ let selector = crate::selector::Selector::parse(dot_path);
137
+ let has_wildcard = selector.has_wildcard() || selector.has_brackets();
134
138
  let (parent_path, key) = dot_path.rsplit_once('.').unwrap_or(("", dot_path));
135
139
 
140
+ if has_wildcard {
141
+ let count = self.navigate_all_compact(parent_path).len();
142
+
143
+ if count == 0 {
144
+ return Ok(());
145
+ }
146
+
147
+ for i in (0..count).rev() {
148
+ let nodes = self.navigate_all_compact(parent_path);
149
+
150
+ let node = match nodes.get(i) {
151
+ Some(node) => node.clone(),
152
+ None => continue,
153
+ };
154
+
155
+ let map = match node.descendants().find_map(BlockMap::cast) {
156
+ Some(map) => map,
157
+ None => continue,
158
+ };
159
+
160
+ if find_entry_by_key(&map, key).is_some() {
161
+ continue;
162
+ }
163
+
164
+ let entries: Vec<_> = map.entries().collect();
165
+
166
+ if entries.is_empty() {
167
+ continue;
168
+ }
169
+
170
+ let first_entry = entries.first().unwrap();
171
+
172
+ let start_col = {
173
+ let offset: usize = first_entry.syntax().text_range().start().into();
174
+ let source = self.to_string();
175
+ let before = &source[..offset];
176
+
177
+ offset - before.rfind('\n').map(|p| p + 1).unwrap_or(0)
178
+ };
179
+
180
+ let indent = " ".repeat(start_col);
181
+ let new_entry_text = format!("{}: {}", key, value);
182
+
183
+ match &position {
184
+ InsertPosition::After(target_key) => {
185
+ let target = find_entry_by_key(&map, target_key);
186
+ let after_node = target.map(|e| e.syntax().clone()).unwrap_or_else(|| entries.last().unwrap().syntax().clone());
187
+ let new_text = format!("\n{}{}", indent, new_entry_text);
188
+
189
+ self.insert_after_node(&after_node, &new_text)?;
190
+ }
191
+
192
+ InsertPosition::Before(target_key) => {
193
+ if let Some(target_entry) = find_entry_by_key(&map, target_key) {
194
+ let target_range = target_entry.syntax().text_range();
195
+ let replacement = format!("{}\n{}", new_entry_text, indent);
196
+ let insert_range = TextRange::new(target_range.start(), target_range.start());
197
+
198
+ self.apply_edit(insert_range, &replacement)?;
199
+ } else {
200
+ let last_entry = entries.last().unwrap();
201
+ let new_text = format!("\n{}{}", indent, new_entry_text);
202
+
203
+ self.insert_after_node(last_entry.syntax(), &new_text)?;
204
+ }
205
+ }
206
+
207
+ _ => {
208
+ let last_entry = entries.last().unwrap();
209
+ let new_text = format!("\n{}{}", indent, new_entry_text);
210
+
211
+ self.insert_after_node(last_entry.syntax(), &new_text)?;
212
+ }
213
+ }
214
+ }
215
+
216
+ return Ok(());
217
+ }
218
+
136
219
  self.insert_map_key(parent_path, key, value, position)
137
220
  }
138
221
 
139
222
  fn insert_sequence_item(&mut self, dot_path: &str, value: &str, position: InsertPosition) -> Result<(), YerbaError> {
140
223
  let current_node = self.navigate(dot_path)?;
141
224
 
142
- let sequence = current_node
143
- .descendants()
144
- .find_map(BlockSeq::cast)
145
- .ok_or_else(|| YerbaError::NotASequence(dot_path.to_string()))?;
225
+ let Some(sequence) = current_node.descendants().find_map(BlockSeq::cast) else {
226
+ if matches!(self.get_value(dot_path).as_ref(), Some(yaml_serde::Value::Sequence(sequence)) if sequence.is_empty()) {
227
+ return self.replace_empty_inline_sequence(dot_path, value);
228
+ }
229
+
230
+ return Err(YerbaError::NotASequence(dot_path.to_string()));
231
+ };
146
232
 
147
233
  let entries: Vec<_> = sequence.entries().collect();
148
234
 
@@ -308,7 +394,34 @@ impl Document {
308
394
  .map(|entry| preceding_whitespace_indent(entry.syntax()))
309
395
  .unwrap_or_default();
310
396
 
311
- let new_entry_text = format!("{}: {}", key, value);
397
+ let is_block_value = value.contains('\n') || value.starts_with("- ");
398
+ let new_entry_text = if is_block_value {
399
+ let value_indent = format!("{} ", indent);
400
+ let lines: Vec<&str> = value.lines().collect();
401
+
402
+ let min_indent = lines
403
+ .iter()
404
+ .filter(|line| !line.trim().is_empty())
405
+ .map(|line| line.len() - line.trim_start().len())
406
+ .min()
407
+ .unwrap_or(0);
408
+
409
+ let indented_lines: Vec<String> = lines
410
+ .iter()
411
+ .map(|line| {
412
+ if line.trim().is_empty() {
413
+ String::new()
414
+ } else {
415
+ let relative = &line[min_indent..];
416
+ format!("{}{}", value_indent, relative)
417
+ }
418
+ })
419
+ .collect();
420
+
421
+ format!("{}:\n{}", key, indented_lines.join("\n"))
422
+ } else {
423
+ format!("{}: {}", key, value)
424
+ };
312
425
 
313
426
  match position {
314
427
  InsertPosition::Last => {
@@ -381,4 +494,86 @@ impl Document {
381
494
  }
382
495
  }
383
496
  }
497
+
498
+ fn replace_empty_inline_sequence(&mut self, dot_path: &str, value: &str) -> Result<(), YerbaError> {
499
+ let (parent_path, key) = dot_path.rsplit_once('.').unwrap_or(("", dot_path));
500
+ let parent_node = self.navigate(parent_path)?;
501
+ let map = parent_node
502
+ .descendants()
503
+ .find_map(BlockMap::cast)
504
+ .ok_or_else(|| YerbaError::NotASequence(dot_path.to_string()))?;
505
+ let entry = find_entry_by_key(&map, key).ok_or_else(|| YerbaError::SelectorNotFound(dot_path.to_string()))?;
506
+
507
+ let item_indent = format!("{} ", preceding_whitespace_indent(entry.syntax()));
508
+ let new_item = Self::format_sequence_item(value, &item_indent);
509
+ let current_node = self.navigate(dot_path)?;
510
+ let mut range = current_node.text_range();
511
+ let inline_comment = self.trailing_inline_comment(&current_node);
512
+
513
+ if let Some(previous) = current_node.prev_sibling_or_token().and_then(|element| element.into_token()) {
514
+ if previous.kind() == SyntaxKind::WHITESPACE && !previous.text().contains('\n') {
515
+ range = TextRange::new(previous.text_range().start(), range.end());
516
+ }
517
+ }
518
+
519
+ if let Some((_, comment_end)) = inline_comment {
520
+ range = TextRange::new(range.start(), comment_end);
521
+ }
522
+
523
+ let replacement = match inline_comment {
524
+ Some((comment_text, _)) => format!(" {}\n{}{}", comment_text, item_indent, new_item),
525
+ None => format!("\n{}{}", item_indent, new_item),
526
+ };
527
+
528
+ self.apply_edit(range, &replacement)
529
+ }
530
+
531
+ fn trailing_inline_comment(&self, node: &SyntaxNode) -> Option<(String, rowan::TextSize)> {
532
+ let source = self.root.text().to_string();
533
+ let start: usize = node.text_range().end().into();
534
+ let rest = &source[start..];
535
+ let line_end = rest.find('\n').unwrap_or(rest.len());
536
+ let trailing = &rest[..line_end];
537
+ let comment_start = trailing.find('#')?;
538
+
539
+ if !trailing[..comment_start].trim().is_empty() {
540
+ return None;
541
+ }
542
+
543
+ Some((trailing[comment_start..].to_string(), rowan::TextSize::from((start + line_end) as u32)))
544
+ }
545
+
546
+ fn format_sequence_item(value: &str, indent: &str) -> String {
547
+ if value.contains('\n') {
548
+ let item_indent = format!("{} ", indent);
549
+ let lines: Vec<&str> = value.split('\n').collect();
550
+
551
+ let min_indent = lines
552
+ .iter()
553
+ .skip(1)
554
+ .filter(|line| !line.trim().is_empty())
555
+ .map(|line| line.len() - line.trim_start().len())
556
+ .min()
557
+ .unwrap_or(0);
558
+
559
+ let indented: Vec<String> = lines
560
+ .iter()
561
+ .enumerate()
562
+ .map(|(index, line)| {
563
+ if index == 0 {
564
+ line.to_string()
565
+ } else if line.trim().is_empty() {
566
+ String::new()
567
+ } else {
568
+ let relative = &line[min_indent..];
569
+ format!("{}{}", item_indent, relative)
570
+ }
571
+ })
572
+ .collect();
573
+
574
+ format!("- {}", indented.join("\n"))
575
+ } else {
576
+ format!("- {}", value)
577
+ }
578
+ }
384
579
  }
@@ -5,7 +5,7 @@ mod insert;
5
5
  mod schema;
6
6
  mod set;
7
7
  mod sort;
8
- mod style;
8
+ pub mod style;
9
9
  mod unique;
10
10
  pub use unique::DuplicateInfo;
11
11
 
@@ -25,9 +25,9 @@ use std::fs;
25
25
  use std::path::{Path, PathBuf};
26
26
 
27
27
  use rowan::ast::AstNode;
28
- use rowan::TextRange;
28
+ use rowan::{TextRange, TextSize};
29
29
 
30
- use yaml_parser::ast::{BlockMap, BlockSeq, Root};
30
+ use yaml_parser::ast::{BlockMap, BlockMapEntry, BlockSeq, Root};
31
31
  use yaml_parser::{SyntaxKind, SyntaxNode, SyntaxToken};
32
32
 
33
33
  use crate::error::YerbaError;
@@ -35,7 +35,7 @@ use crate::QuoteStyle;
35
35
 
36
36
  use crate::syntax::{
37
37
  dedent_block_scalar, extract_scalar, extract_scalar_text, find_entry_by_key, find_scalar_token, format_scalar_value, is_map_key, is_yaml_non_string,
38
- preceding_whitespace_indent, removal_range, unescape_double_quoted, unescape_single_quoted, ScalarValue,
38
+ preceding_whitespace_indent, preceding_whitespace_token, removal_range, unescape_double_quoted, unescape_single_quoted, ScalarValue,
39
39
  };
40
40
 
41
41
  #[derive(Debug, Clone)]
@@ -297,6 +297,45 @@ impl Document {
297
297
  self.apply_edit(range, text)
298
298
  }
299
299
 
300
+ fn remove_map_entry(&mut self, entry: &BlockMapEntry) -> Result<(), YerbaError> {
301
+ let entry_node = entry.syntax();
302
+ let entry_range = entry_node.text_range();
303
+
304
+ let has_block_value = entry
305
+ .value()
306
+ .map(|value| {
307
+ value
308
+ .syntax()
309
+ .descendants()
310
+ .any(|descendant| descendant.kind() == SyntaxKind::BLOCK_SEQ || descendant.kind() == SyntaxKind::BLOCK_MAP)
311
+ })
312
+ .unwrap_or(false);
313
+
314
+ if !has_block_value {
315
+ return self.remove_node(entry_node);
316
+ }
317
+
318
+ let end = if let Some(next_sibling) = entry_node.next_sibling() {
319
+ next_sibling.text_range().start()
320
+ } else {
321
+ entry_node.parent().map(|parent| parent.text_range().end()).unwrap_or(entry_range.end())
322
+ };
323
+
324
+ let start = if let Some(whitespace_token) = preceding_whitespace_token(entry_node) {
325
+ let whitespace_text = whitespace_token.text();
326
+ let whitespace_start = whitespace_token.text_range().start();
327
+
328
+ whitespace_text
329
+ .rfind('\n')
330
+ .map(|offset| whitespace_start + TextSize::from(offset as u32))
331
+ .unwrap_or(whitespace_start)
332
+ } else {
333
+ entry_range.start()
334
+ };
335
+
336
+ self.apply_edit(TextRange::new(start, end), "")
337
+ }
338
+
300
339
  fn remove_node(&mut self, node: &SyntaxNode) -> Result<(), YerbaError> {
301
340
  let inline_comment = self.find_inline_comment(node);
302
341
  let range = removal_range(node);
@@ -445,11 +484,11 @@ pub(crate) fn compute_location(source: &str, start_offset: usize, end_offset: us
445
484
  }
446
485
  }
447
486
 
448
- pub fn collect_selectors(value: &serde_yaml::Value, prefix: &str, selectors: &mut Vec<String>) {
487
+ pub fn collect_selectors(value: &yaml_serde::Value, prefix: &str, selectors: &mut Vec<String>) {
449
488
  match value {
450
- serde_yaml::Value::Mapping(map) => {
489
+ yaml_serde::Value::Mapping(map) => {
451
490
  for (key, child) in map {
452
- if let serde_yaml::Value::String(key_string) = key {
491
+ if let yaml_serde::Value::String(key_string) = key {
453
492
  let selector = if prefix.is_empty() {
454
493
  key_string.clone()
455
494
  } else {
@@ -462,7 +501,7 @@ pub fn collect_selectors(value: &serde_yaml::Value, prefix: &str, selectors: &mu
462
501
  }
463
502
  }
464
503
 
465
- serde_yaml::Value::Sequence(sequence) => {
504
+ yaml_serde::Value::Sequence(sequence) => {
466
505
  let bracket_prefix = format!("{}[]", prefix);
467
506
  selectors.push(bracket_prefix.clone());
468
507
 
@@ -475,21 +514,21 @@ pub fn collect_selectors(value: &serde_yaml::Value, prefix: &str, selectors: &mu
475
514
  }
476
515
  }
477
516
 
478
- pub(crate) fn node_to_yaml_value(node: &SyntaxNode) -> serde_yaml::Value {
517
+ pub(crate) fn node_to_yaml_value(node: &SyntaxNode) -> yaml_serde::Value {
479
518
  if let Some(sequence) = node.descendants().find_map(BlockSeq::cast) {
480
519
  let map_position = node.descendants().find_map(BlockMap::cast).map(|map| map.syntax().text_range().start());
481
520
 
482
521
  let sequence_position = sequence.syntax().text_range().start();
483
522
 
484
523
  if map_position.is_none() || sequence_position <= map_position.unwrap() {
485
- let values: Vec<serde_yaml::Value> = sequence.entries().map(|entry| node_to_yaml_value(entry.syntax())).collect();
524
+ let values: Vec<yaml_serde::Value> = sequence.entries().map(|entry| node_to_yaml_value(entry.syntax())).collect();
486
525
 
487
- return serde_yaml::Value::Sequence(values);
526
+ return yaml_serde::Value::Sequence(values);
488
527
  }
489
528
  }
490
529
 
491
530
  if let Some(map) = node.descendants().find_map(BlockMap::cast) {
492
- let mut mapping = serde_yaml::Mapping::new();
531
+ let mut mapping = yaml_serde::Mapping::new();
493
532
 
494
533
  for entry in map.entries() {
495
534
  let key = entry.key().and_then(|key_node| extract_scalar_text(key_node.syntax())).unwrap_or_default();
@@ -497,18 +536,18 @@ pub(crate) fn node_to_yaml_value(node: &SyntaxNode) -> serde_yaml::Value {
497
536
  let value = entry
498
537
  .value()
499
538
  .map(|value_node| node_to_yaml_value(value_node.syntax()))
500
- .unwrap_or(serde_yaml::Value::Null);
539
+ .unwrap_or(yaml_serde::Value::Null);
501
540
 
502
- mapping.insert(serde_yaml::Value::String(key), value);
541
+ mapping.insert(yaml_serde::Value::String(key), value);
503
542
  }
504
543
 
505
- return serde_yaml::Value::Mapping(mapping);
544
+ return yaml_serde::Value::Mapping(mapping);
506
545
  }
507
546
 
508
547
  if let Some(sequence) = node.descendants().find_map(BlockSeq::cast) {
509
- let values: Vec<serde_yaml::Value> = sequence.entries().map(|entry| node_to_yaml_value(entry.syntax())).collect();
548
+ let values: Vec<yaml_serde::Value> = sequence.entries().map(|entry| node_to_yaml_value(entry.syntax())).collect();
510
549
 
511
- return serde_yaml::Value::Sequence(values);
550
+ return yaml_serde::Value::Sequence(values);
512
551
  }
513
552
 
514
553
  if let Some(block_scalar) = node.descendants().find(|child| child.kind() == SyntaxKind::BLOCK_SCALAR) {
@@ -521,35 +560,46 @@ pub(crate) fn node_to_yaml_value(node: &SyntaxNode) -> serde_yaml::Value {
521
560
 
522
561
  let text = dedent_block_scalar(&text);
523
562
 
524
- return serde_yaml::Value::String(text);
563
+ return yaml_serde::Value::String(text);
564
+ }
565
+
566
+ if node
567
+ .descendants()
568
+ .any(|descendant| descendant.kind() == SyntaxKind::FLOW_SEQ || descendant.kind() == SyntaxKind::FLOW_MAP)
569
+ {
570
+ let text = node.text().to_string().trim().to_string();
571
+
572
+ if let Ok(value) = yaml_serde::from_str(&text) {
573
+ return value;
574
+ }
525
575
  }
526
576
 
527
577
  if let Some(scalar) = extract_scalar(node) {
528
578
  use crate::syntax::{detect_yaml_type, is_yaml_truthy, YerbaValueType};
529
579
 
530
580
  return match detect_yaml_type(&scalar) {
531
- YerbaValueType::Null => serde_yaml::Value::Null,
532
- YerbaValueType::Boolean => serde_yaml::Value::Bool(is_yaml_truthy(&scalar.text)),
581
+ YerbaValueType::Null => yaml_serde::Value::Null,
582
+ YerbaValueType::Boolean => yaml_serde::Value::Bool(is_yaml_truthy(&scalar.text)),
533
583
 
534
584
  YerbaValueType::Integer => scalar
535
585
  .text
536
586
  .parse::<i64>()
537
- .map(|n| serde_yaml::Value::Number(serde_yaml::Number::from(n)))
538
- .unwrap_or(serde_yaml::Value::String(scalar.text)),
587
+ .map(|n| yaml_serde::Value::Number(yaml_serde::Number::from(n)))
588
+ .unwrap_or(yaml_serde::Value::String(scalar.text)),
539
589
 
540
590
  YerbaValueType::Float => scalar
541
591
  .text
542
592
  .parse::<f64>()
543
- .map(|n| serde_yaml::Value::Number(serde_yaml::Number::from(n)))
544
- .unwrap_or(serde_yaml::Value::String(scalar.text)),
593
+ .map(|n| yaml_serde::Value::Number(yaml_serde::Number::from(n)))
594
+ .unwrap_or(yaml_serde::Value::String(scalar.text)),
545
595
 
546
- YerbaValueType::String => serde_yaml::Value::String(scalar.text),
596
+ YerbaValueType::String => yaml_serde::Value::String(scalar.text),
547
597
  };
548
598
  }
549
599
 
550
600
  let text = node.text().to_string();
551
601
 
552
- serde_yaml::from_str(&text).unwrap_or(serde_yaml::Value::String(text))
602
+ yaml_serde::from_str(&text).unwrap_or(yaml_serde::Value::String(text))
553
603
  }
554
604
 
555
605
  pub(crate) fn parse_condition(condition: &str) -> Option<(String, &str, String)> {
@@ -577,10 +577,10 @@ impl Document {
577
577
  };
578
578
 
579
579
  let labels: Vec<String> = match self.get_value(&items_selector) {
580
- Some(serde_yaml::Value::Sequence(sequence)) => sequence
580
+ Some(yaml_serde::Value::Sequence(sequence)) => sequence
581
581
  .iter()
582
582
  .map(|value| match value {
583
- serde_yaml::Value::String(string) => string.clone(),
583
+ yaml_serde::Value::String(string) => string.clone(),
584
584
  _ => String::new(),
585
585
  })
586
586
  .collect(),