yerba 0.8.0-x86_64-linux-gnu → 0.8.1-x86_64-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.
@@ -1,7 +1,7 @@
1
- use clap::ValueEnum;
2
1
  use yaml_parser::SyntaxKind;
3
2
 
4
- #[derive(Debug, Clone, PartialEq, ValueEnum)]
3
+ #[derive(Debug, Clone, PartialEq)]
4
+ #[cfg_attr(feature = "cli", derive(clap::ValueEnum))]
5
5
  pub enum KeyStyle {
6
6
  /// Unquoted key (host:)
7
7
  Plain,
@@ -34,33 +34,34 @@ impl KeyStyle {
34
34
  }
35
35
  }
36
36
 
37
- #[derive(Debug, Clone, PartialEq, ValueEnum)]
37
+ #[derive(Debug, Clone, PartialEq)]
38
+ #[cfg_attr(feature = "cli", derive(clap::ValueEnum))]
38
39
  pub enum QuoteStyle {
39
40
  /// Unquoted value (host: localhost)
40
41
  Plain,
41
42
  /// Single-quoted value (host: 'localhost')
42
- #[value(alias = "single-quoted")]
43
+ #[cfg_attr(feature = "cli", value(alias = "single-quoted"))]
43
44
  Single,
44
45
  /// Double-quoted value (host: "localhost")
45
- #[value(alias = "double-quoted")]
46
+ #[cfg_attr(feature = "cli", value(alias = "double-quoted"))]
46
47
  Double,
47
48
  /// Literal block scalar, strip trailing newline (|-)
48
- #[value(alias = "block-literal", alias = "|-")]
49
+ #[cfg_attr(feature = "cli", value(alias = "block-literal", alias = "|-"))]
49
50
  Literal,
50
51
  /// Literal block scalar, keep one trailing newline (|)
51
- #[value(alias = "|")]
52
+ #[cfg_attr(feature = "cli", value(alias = "|"))]
52
53
  LiteralClip,
53
54
  /// Literal block scalar, keep all trailing newlines (|+)
54
- #[value(alias = "|+")]
55
+ #[cfg_attr(feature = "cli", value(alias = "|+"))]
55
56
  LiteralKeep,
56
57
  /// Folded block scalar, strip trailing newline (>-)
57
- #[value(alias = "block-folded", alias = ">-")]
58
+ #[cfg_attr(feature = "cli", value(alias = "block-folded", alias = ">-"))]
58
59
  Folded,
59
60
  /// Folded block scalar, keep one trailing newline (>)
60
- #[value(alias = ">")]
61
+ #[cfg_attr(feature = "cli", value(alias = ">"))]
61
62
  FoldedClip,
62
63
  /// Folded block scalar, keep all trailing newlines (>+)
63
- #[value(alias = ">+")]
64
+ #[cfg_attr(feature = "cli", value(alias = ">+"))]
64
65
  FoldedKeep,
65
66
  }
66
67
 
data/rust/src/syntax.rs CHANGED
@@ -182,8 +182,139 @@ pub fn format_scalar_value(value: &str, kind: SyntaxKind) -> String {
182
182
  }
183
183
  }
184
184
 
185
+ const LEADING_INDICATORS: [char; 16] = ['#', '&', '*', '!', '|', '>', '\'', '"', '%', '@', '`', ',', '[', ']', '{', '}'];
186
+ const FLOW_INDICATORS: [char; 5] = [',', '[', ']', '{', '}'];
187
+
188
+ pub fn is_plain_safe(value: &str) -> bool {
189
+ if value.is_empty() || value != value.trim() {
190
+ return false;
191
+ }
192
+
193
+ if value.contains('\n') || value.contains('\t') {
194
+ return false;
195
+ }
196
+
197
+ if value.contains(": ") || value.ends_with(':') {
198
+ return false;
199
+ }
200
+
201
+ if value.contains(" #") {
202
+ return false;
203
+ }
204
+
205
+ if value.starts_with("---") || value.starts_with("...") {
206
+ return false;
207
+ }
208
+
209
+ let mut characters = value.chars();
210
+ let first = characters.next().expect("value is non-empty");
211
+
212
+ if LEADING_INDICATORS.contains(&first) {
213
+ return false;
214
+ }
215
+
216
+ if matches!(first, '-' | '?' | ':') {
217
+ return matches!(characters.next(), Some(next) if next != ' ');
218
+ }
219
+
220
+ true
221
+ }
222
+
223
+ pub fn is_plain_safe_in_flow(value: &str) -> bool {
224
+ is_plain_safe(value) && !value.contains(FLOW_INDICATORS)
225
+ }
226
+
227
+ pub fn is_quoted_scalar(value: &str) -> bool {
228
+ let bytes = value.as_bytes();
229
+
230
+ if bytes.len() < 2 {
231
+ return false;
232
+ }
233
+
234
+ match (bytes[0], bytes[bytes.len() - 1]) {
235
+ (b'"', b'"') => {
236
+ let interior = &value[1..value.len() - 1];
237
+ let mut escaped = false;
238
+
239
+ for character in interior.chars() {
240
+ if escaped {
241
+ escaped = false;
242
+ continue;
243
+ }
244
+
245
+ match character {
246
+ '\\' => escaped = true,
247
+ '"' => return false,
248
+ _ => {}
249
+ }
250
+ }
251
+
252
+ !escaped
253
+ }
254
+
255
+ (b'\'', b'\'') => {
256
+ let interior = &value[1..value.len() - 1];
257
+ let mut characters = interior.chars().peekable();
258
+
259
+ while let Some(character) = characters.next() {
260
+ if character == '\'' {
261
+ if characters.peek() == Some(&'\'') {
262
+ characters.next();
263
+ } else {
264
+ return false;
265
+ }
266
+ }
267
+ }
268
+
269
+ true
270
+ }
271
+
272
+ _ => false,
273
+ }
274
+ }
275
+
276
+ pub fn is_flow_collection(value: &str) -> bool {
277
+ (value.starts_with('[') && value.ends_with(']')) || (value.starts_with('{') && value.ends_with('}'))
278
+ }
279
+
280
+ pub fn is_raw_yaml_text(value: &str) -> bool {
281
+ value.contains('\n') || value.starts_with("- ") || is_quoted_scalar(value) || is_flow_collection(value)
282
+ }
283
+
284
+ pub fn is_valid_inline_value(value: &str) -> bool {
285
+ if value.is_empty() {
286
+ return true;
287
+ }
288
+
289
+ is_raw_yaml_text(value) || is_plain_safe(value)
290
+ }
291
+
292
+ pub fn is_inline_scalar_safe(value: &str) -> bool {
293
+ if value.is_empty() {
294
+ return true;
295
+ }
296
+
297
+ if value.contains('\n') || value.starts_with("- ") {
298
+ return false;
299
+ }
300
+
301
+ is_quoted_scalar(value) || is_flow_collection(value) || is_plain_safe(value)
302
+ }
303
+
304
+ pub fn needs_quoting(value: &str) -> bool {
305
+ is_yaml_non_string(value) || !is_plain_safe(value)
306
+ }
307
+
308
+ pub fn needs_quoting_in_flow(value: &str) -> bool {
309
+ is_yaml_non_string(value) || !is_plain_safe_in_flow(value)
310
+ }
311
+
185
312
  pub fn quote_if_needed(value: &str) -> String {
186
- if is_yaml_non_string(value) {
313
+ if is_raw_yaml_text(value) {
314
+ return value.to_string();
315
+ }
316
+
317
+ if needs_quoting(value) {
187
318
  format_scalar_value(value, SyntaxKind::DOUBLE_QUOTED_SCALAR)
188
319
  } else {
189
320
  value.to_string()
@@ -69,8 +69,8 @@ pub fn yaml_value_to_flow_text(value: &yaml_serde::Value) -> String {
69
69
  yaml_serde::Value::Number(number) => number.to_string(),
70
70
 
71
71
  yaml_serde::Value::String(string) => {
72
- if crate::syntax::is_yaml_non_string(string) {
73
- format!("\"{}\"", string.replace('"', "\\\""))
72
+ if crate::syntax::needs_quoting_in_flow(string) {
73
+ crate::syntax::format_scalar_value(string, yaml_parser::SyntaxKind::DOUBLE_QUOTED_SCALAR)
74
74
  } else {
75
75
  string.clone()
76
76
  }
@@ -162,8 +162,8 @@ fn format_yaml_scalar(value: &Value, quote_style: &QuoteStyle) -> String {
162
162
  }
163
163
 
164
164
  QuoteStyle::Plain => {
165
- if crate::syntax::is_yaml_non_string(string) {
166
- format!("\"{}\"", string.replace('"', "\\\""))
165
+ if crate::syntax::needs_quoting(string) {
166
+ crate::syntax::format_scalar_value(string, yaml_parser::SyntaxKind::DOUBLE_QUOTED_SCALAR)
167
167
  } else {
168
168
  string.clone()
169
169
  }
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: yerba
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.0
4
+ version: 0.8.1
5
5
  platform: x86_64-linux-gnu
6
6
  authors:
7
7
  - Marco Roth
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-07-26 00:00:00.000000000 Z
11
+ date: 2026-07-30 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: A CLI tool for editing YAML while preserving structure, comments, and
14
14
  format.
@@ -68,6 +68,7 @@ files:
68
68
  - rust/src/commands/set.rs
69
69
  - rust/src/commands/sort.rs
70
70
  - rust/src/commands/sort_keys.rs
71
+ - rust/src/commands/ui.rs
71
72
  - rust/src/commands/unique.rs
72
73
  - rust/src/commands/version.rs
73
74
  - rust/src/didyoumean.rs