yerba 0.7.1-aarch64-linux-gnu → 0.7.3-aarch64-linux-gnu

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 707ccb13f6302905ec41b66a43e393c948ffd61b0bcd465ceba44523ab271b79
4
- data.tar.gz: 4e894b3193f091b2171e7c1049e41c68c4efdc104c60b80c0d710d0dea961267
3
+ metadata.gz: f833e343807c30f0370315dd082a4bb931ef283ab61da1917185bd993c192185
4
+ data.tar.gz: b6a4090bea313c06a5a7c6ac23638e8d223c3869a6d117fa4f3508260bae1245
5
5
  SHA512:
6
- metadata.gz: 303099f898671db798cac8c13cc54b820ceb00aa61a1473636f20ea485c393d6732f339e315ff313ef33e24411196e6b5e94b1bd026ed9a1cd9f593bc97b1392
7
- data.tar.gz: 976fdc7159877d38f43578beafb71d5ee8f6db6f78ee0326dbe1b58fdd5e0c8815ed8e5e62bdcd6a9e4416e1d2792c4849e82764ff07665f7030cb1403474a84
6
+ metadata.gz: 1bea0a8fd3cf92f0e8f814884d35b1b8ebfba4ef28498f377f3f4343e23db5cfaa50f48ac0e8dd5832f32824410ce3bdad208c60dfc97e9c74d42a5d179c83e9
7
+ data.tar.gz: e49bc007a98d7871b67cad0c4095512db1c28f88f1b2b2868fff0913ea4970df85a859100fba6149c7f93ce77dd42f1c27fb0cb7eef4888ec98c121633636091
Binary file
@@ -72,6 +72,11 @@ void yerba_document_free(struct Document *document);
72
72
 
73
73
  struct YerbaGetResult yerba_document_get(const struct Document *document, const char *path);
74
74
 
75
+ /**
76
+ * Caller must free with yerba_string_free.
77
+ */
78
+ char *yerba_document_source(const struct Document *document, const char *path);
79
+
75
80
  /**
76
81
  * Caller must free with yerba_string_free.
77
82
  */
data/ext/yerba/yerba.c CHANGED
@@ -119,10 +119,12 @@ static VALUE document_initialize(int argc, VALUE *argv, VALUE self) {
119
119
  if (NIL_P(path)) {
120
120
  result = yerba_document_parse("---\n");
121
121
  rb_iv_set(self, "@path", Qnil);
122
+ rb_iv_set(self, "@loaded_mtime", Qnil);
122
123
  } else {
123
124
  const char *file_path = StringValueCStr(path);
124
125
  result = yerba_document_parse_file(file_path);
125
126
  rb_iv_set(self, "@path", path);
127
+ rb_iv_set(self, "@loaded_mtime", rb_funcall(rb_cFile, rb_intern("mtime"), 1, path));
126
128
  }
127
129
 
128
130
  if (!result.document) {
@@ -297,6 +299,19 @@ static VALUE document_valid_selector_p(VALUE self, VALUE path) {
297
299
  return yerba_document_valid_selector(document, StringValueCStr(path)) ? Qtrue : Qfalse;
298
300
  }
299
301
 
302
+ /* document.source(path) → raw YAML text of the node at path */
303
+ static VALUE document_source(VALUE self, VALUE path) {
304
+ struct Document *document = get_document(self);
305
+ char *text = yerba_document_source(document, StringValueCStr(path));
306
+
307
+ if (!text) return Qnil;
308
+
309
+ VALUE result = make_utf8_string(text);
310
+ yerba_string_free(text);
311
+
312
+ return result;
313
+ }
314
+
300
315
  /* document.get_value(path) → parsed Ruby object (Hash/Array/String/Integer/etc) */
301
316
  static VALUE document_get_value(VALUE self, VALUE path) {
302
317
  struct Document *document = get_document(self);
@@ -1091,6 +1106,7 @@ void Init_yerba(void) {
1091
1106
  rb_define_method(rb_cDocument, "blank_lines", document_blank_lines, 2);
1092
1107
  rb_define_method(rb_cDocument, "apply_yerbafile", document_apply_yerbafile, -1);
1093
1108
  rb_define_method(rb_cDocument, "validate_schema", document_validate_schema, -1);
1109
+ rb_define_method(rb_cDocument, "source", document_source, 1);
1094
1110
  rb_define_method(rb_cDocument, "to_s", document_to_s, 0);
1095
1111
  rb_define_method(rb_cDocument, "write!", document_save, 0);
1096
1112
  rb_define_method(rb_cDocument, "changed?", document_changed_p, 0);
Binary file
Binary file
Binary file
Binary file
@@ -64,9 +64,16 @@ module Yerba
64
64
 
65
65
  def save_to!(path)
66
66
  @path = path
67
+ @loaded_mtime = nil
67
68
  save!
68
69
  end
69
70
 
71
+ def stale?
72
+ return false unless @path && @loaded_mtime
73
+
74
+ File.mtime(@path) != @loaded_mtime
75
+ end
76
+
70
77
  def dig(*keys)
71
78
  keys.reduce(self) { |node, key| node.nil? ? nil : node[key] }
72
79
  end
@@ -106,15 +113,18 @@ module Yerba
106
113
  end
107
114
 
108
115
  def save!(apply: false)
116
+ check_stale!
109
117
  Yerbafile.apply!(self, apply) if apply
110
118
  write!
111
-
119
+ @loaded_mtime = File.mtime(@path) if @path
112
120
  self
113
121
  end
114
122
 
115
123
  def apply!(yerbafile = nil)
124
+ check_stale!
116
125
  apply(yerbafile)
117
126
  write! if changed?
127
+ @loaded_mtime = File.mtime(@path) if @path
118
128
 
119
129
  self
120
130
  end
@@ -157,6 +167,16 @@ module Yerba
157
167
  raise Yerba::SelectorNotFoundError, message
158
168
  end
159
169
 
170
+ private
171
+
172
+ def check_stale!
173
+ return unless stale?
174
+
175
+ raise Yerba::StaleFileError, "#{@path} was modified externally since it was loaded"
176
+ end
177
+
178
+ public
179
+
160
180
  def inspect
161
181
  if path
162
182
  "#<Yerba::Document path=#{path.inspect}>"
data/lib/yerba/map.rb CHANGED
@@ -161,6 +161,10 @@ module Yerba
161
161
  end
162
162
  alias to_hash to_h
163
163
 
164
+ def to_s
165
+ source || to_yaml
166
+ end
167
+
164
168
  def to_yaml
165
169
  to_hash.map do |key, val|
166
170
  formatted = format_value(val)
data/lib/yerba/node.rb CHANGED
@@ -20,6 +20,12 @@ module Yerba
20
20
  !@document.nil? || !@file_path.nil?
21
21
  end
22
22
 
23
+ def source
24
+ return nil unless connected? && @selector
25
+
26
+ document.source(@selector)
27
+ end
28
+
23
29
  module ClassMethods
24
30
  def from_document(document, selector, location = nil, key = nil, **attributes)
25
31
  instance = allocate
@@ -281,6 +281,10 @@ module Yerba
281
281
  end.join("\n")
282
282
  end
283
283
 
284
+ def to_s
285
+ source || super
286
+ end
287
+
284
288
  def inspect
285
289
  list = items
286
290
  preview = list.first(5).map(&:inspect).join(", ")
data/lib/yerba/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Yerba
4
- VERSION = "0.7.1"
4
+ VERSION = "0.7.3"
5
5
  end
data/lib/yerba.rb CHANGED
@@ -30,6 +30,7 @@ module Yerba
30
30
  class ExecutableNotFoundError < StandardError; end
31
31
  class CompilationError < StandardError; end
32
32
  class SelectorNotFoundError < StandardError; end
33
+ class StaleFileError < StandardError; end
33
34
 
34
35
  GEM_NAME = "yerba"
35
36
  EXECUTABLE_NAME = "yerba"
data/rust/Cargo.lock CHANGED
@@ -1772,7 +1772,7 @@ dependencies = [
1772
1772
 
1773
1773
  [[package]]
1774
1774
  name = "yerba"
1775
- version = "0.7.1"
1775
+ version = "0.7.3"
1776
1776
  dependencies = [
1777
1777
  "cbindgen",
1778
1778
  "clap",
data/rust/Cargo.toml CHANGED
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "yerba"
3
- version = "0.7.1"
3
+ version = "0.7.3"
4
4
  edition = "2021"
5
5
  authors = ["Marco Roth <marco.roth@intergga.ch>"]
6
6
  description = "YAML Editing and Refactoring with Better Accuracy"
@@ -11,7 +11,7 @@ impl Document {
11
11
  }
12
12
 
13
13
  pub fn filter_with_selectors(&self, dot_path: &str, condition: &str) -> Vec<(yaml_serde::Value, String, usize)> {
14
- let source = self.root.text().to_string();
14
+ let source = self.source_text();
15
15
 
16
16
  self
17
17
  .navigate_all_compact(dot_path)
@@ -19,7 +19,7 @@ impl Document {
19
19
  .filter(|node| self.evaluate_condition_on_node(node, condition))
20
20
  .map(|node| {
21
21
  let offset: usize = node.text_range().start().into();
22
- let line = source[..offset].matches('\n').count() + 1;
22
+ let line = line_at(&source, offset);
23
23
 
24
24
  (node_to_yaml_value(node), super::get::node_selector(node), line)
25
25
  })
@@ -52,7 +52,7 @@ impl Document {
52
52
  }
53
53
 
54
54
  for node in &target_nodes {
55
- if let Some(sequence) = node.descendants().find_map(BlockSeq::cast) {
55
+ if let Some(sequence) = find_block_sequence(node) {
56
56
  for entry in sequence.entries() {
57
57
  if let Some(text) = entry.flow().and_then(|flow| extract_scalar_text(flow.syntax())) {
58
58
  if text == right {
@@ -67,7 +67,7 @@ impl Document {
67
67
  }
68
68
  "not_contains" => {
69
69
  for node in &target_nodes {
70
- if let Some(sequence) = node.descendants().find_map(BlockSeq::cast) {
70
+ if let Some(sequence) = find_block_sequence(node) {
71
71
  for entry in sequence.entries() {
72
72
  if let Some(text) = entry.flow().and_then(|flow| extract_scalar_text(flow.syntax())) {
73
73
  if text == right {
@@ -13,10 +13,7 @@ impl Document {
13
13
  let (parent_path, source_key) = source_path.rsplit_once('.').unwrap_or(("", source_path));
14
14
  let parent_node = self.navigate(parent_path)?;
15
15
 
16
- let map = parent_node
17
- .descendants()
18
- .find_map(BlockMap::cast)
19
- .ok_or_else(|| YerbaError::SelectorNotFound(source_path.to_string()))?;
16
+ let map = find_block_map(&parent_node).ok_or_else(|| YerbaError::SelectorNotFound(source_path.to_string()))?;
20
17
 
21
18
  let entry = find_entry_by_key(&map, source_key).ok_or_else(|| YerbaError::SelectorNotFound(source_path.to_string()))?;
22
19
  let key_node = entry.key().ok_or_else(|| YerbaError::SelectorNotFound(source_path.to_string()))?;
@@ -46,10 +43,7 @@ impl Document {
46
43
 
47
44
  if parent_path.is_empty() && !has_wildcard {
48
45
  let parent_node = self.navigate(&parent_path)?;
49
- let map = parent_node
50
- .descendants()
51
- .find_map(BlockMap::cast)
52
- .ok_or_else(|| YerbaError::SelectorNotFound(dot_path.to_string()))?;
46
+ let map = find_block_map(&parent_node).ok_or_else(|| YerbaError::SelectorNotFound(dot_path.to_string()))?;
53
47
 
54
48
  let entry = find_entry_by_key(&map, last_key).ok_or_else(|| YerbaError::SelectorNotFound(dot_path.to_string()))?;
55
49
 
@@ -67,10 +61,7 @@ impl Document {
67
61
  }
68
62
 
69
63
  if parent_nodes.len() == 1 && !has_wildcard {
70
- let map = parent_nodes[0]
71
- .descendants()
72
- .find_map(BlockMap::cast)
73
- .ok_or_else(|| YerbaError::SelectorNotFound(dot_path.to_string()))?;
64
+ let map = find_block_map(&parent_nodes[0]).ok_or_else(|| YerbaError::SelectorNotFound(dot_path.to_string()))?;
74
65
 
75
66
  let entry = find_entry_by_key(&map, last_key).ok_or_else(|| YerbaError::SelectorNotFound(dot_path.to_string()))?;
76
67
 
@@ -80,24 +71,16 @@ impl Document {
80
71
  let mut ranges: Vec<TextRange> = Vec::new();
81
72
 
82
73
  for parent_node in &parent_nodes {
83
- if let Some(map) = parent_node.descendants().find_map(BlockMap::cast) {
74
+ if let Some(map) = find_block_map(parent_node) {
84
75
  if let Some(entry) = find_entry_by_key(&map, last_key) {
85
76
  ranges.push(removal_range(entry.syntax()));
86
77
  }
87
78
  }
88
79
  }
89
80
 
90
- if ranges.is_empty() {
91
- return Ok(());
92
- }
93
-
94
- ranges.reverse();
95
-
96
- for range in ranges {
97
- self.apply_edit(range, "")?;
98
- }
81
+ let edits = ranges.into_iter().map(|range| (range, String::new())).collect();
99
82
 
100
- Ok(())
83
+ self.apply_edits(edits)
101
84
  }
102
85
 
103
86
  crate::selector::SelectorSegment::Index(index) => self.remove_at(&parent_path, *index),
@@ -108,10 +91,7 @@ impl Document {
108
91
  pub fn remove(&mut self, dot_path: &str, value: &str) -> Result<(), YerbaError> {
109
92
  let current_node = self.navigate(dot_path)?;
110
93
 
111
- let sequence = current_node
112
- .descendants()
113
- .find_map(BlockSeq::cast)
114
- .ok_or_else(|| YerbaError::NotASequence(dot_path.to_string()))?;
94
+ let sequence = find_block_sequence(&current_node).ok_or_else(|| YerbaError::NotASequence(dot_path.to_string()))?;
115
95
 
116
96
  let target_entry = sequence
117
97
  .entries()
@@ -132,10 +112,7 @@ impl Document {
132
112
 
133
113
  let current_node = self.navigate(dot_path)?;
134
114
 
135
- let sequence = current_node
136
- .descendants()
137
- .find_map(BlockSeq::cast)
138
- .ok_or_else(|| YerbaError::NotASequence(dot_path.to_string()))?;
115
+ let sequence = find_block_sequence(&current_node).ok_or_else(|| YerbaError::NotASequence(dot_path.to_string()))?;
139
116
 
140
117
  let entries: Vec<_> = sequence.entries().collect();
141
118
 
@@ -83,7 +83,7 @@ impl Document {
83
83
  }
84
84
 
85
85
  pub fn get_all_located(&self, dot_path: &str) -> Vec<LocatedNode> {
86
- let source = self.root.text().to_string();
86
+ let source = self.source_text();
87
87
  let file_path = self.path.as_ref().map(|p| p.to_string_lossy().to_string());
88
88
 
89
89
  self
@@ -91,7 +91,7 @@ impl Document {
91
91
  .iter()
92
92
  .map(|node| {
93
93
  let offset: usize = node.text_range().start().into();
94
- let line = source[..offset].matches('\n').count() + 1;
94
+ let line = line_at(&source, offset);
95
95
  let selector = node_selector(node);
96
96
  let is_scalar = !node
97
97
  .descendants()
@@ -108,13 +108,8 @@ impl Document {
108
108
  let node_type = if is_scalar {
109
109
  "scalar"
110
110
  } else {
111
- let map_pos = node.descendants().find_map(BlockMap::cast).map(|m| m.syntax().text_range().start());
112
- let seq_pos = node.descendants().find_map(BlockSeq::cast).map(|s| s.syntax().text_range().start());
113
-
114
- match (map_pos, seq_pos) {
115
- (Some(m), Some(s)) if s < m => "sequence",
116
- (Some(_), _) => "map",
117
- (None, Some(_)) => "sequence",
111
+ match first_collection(node) {
112
+ Some(FirstCollection::Sequence(_)) => "sequence",
118
113
  _ => "map",
119
114
  }
120
115
  };
@@ -142,7 +137,7 @@ impl Document {
142
137
 
143
138
  pub fn get_node_info(&self, dot_path: &str) -> NodeInfo {
144
139
  let selector = crate::selector::Selector::parse(dot_path);
145
- let source = self.root.text().to_string();
140
+ let source = self.source_text();
146
141
 
147
142
  let (location, key_name, key_location) = self.resolve_location(dot_path, &source);
148
143
 
@@ -364,7 +359,7 @@ impl Document {
364
359
  self.navigate(parent_path).ok()?
365
360
  };
366
361
 
367
- let map = parent_node.descendants().find_map(BlockMap::cast)?;
362
+ let map = find_block_map(&parent_node)?;
368
363
 
369
364
  find_entry_by_key(&map, last_key).map(|entry| entry.syntax().clone())
370
365
  }
@@ -375,7 +370,7 @@ impl Document {
375
370
  Err(_) => return Vec::new(),
376
371
  };
377
372
 
378
- let sequence = match current_node.descendants().find_map(BlockSeq::cast) {
373
+ let sequence = match find_block_sequence(&current_node) {
379
374
  Some(sequence) => sequence,
380
375
  None => return Vec::new(),
381
376
  };
@@ -403,17 +398,16 @@ impl Document {
403
398
  pub fn get_sequence_indent(&self, dot_path: &str) -> Option<&'static str> {
404
399
  let current_node = self.navigate(dot_path).ok()?;
405
400
 
406
- let sequence = current_node.descendants().find_map(BlockSeq::cast)?;
401
+ let sequence = find_block_sequence(&current_node)?;
407
402
  let first_entry = sequence.entries().next()?;
408
403
 
409
404
  let entry_indent = preceding_whitespace_indent(first_entry.syntax());
410
405
 
411
406
  let entry_node = current_node.ancestors().find(|ancestor| ancestor.kind() == SyntaxKind::BLOCK_MAP_ENTRY)?;
412
407
 
413
- let source = self.root.text().to_string();
408
+ let source = self.source_text();
414
409
  let entry_start: usize = entry_node.text_range().start().into();
415
- let line_start = source[..entry_start].rfind('\n').map(|position| position + 1).unwrap_or(0);
416
- let key_indent = &source[line_start..entry_start];
410
+ let key_indent = &source[line_start_at(&source, entry_start)..entry_start];
417
411
 
418
412
  if entry_indent.len() > key_indent.len() {
419
413
  Some("indented")