yerba 0.2.0-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.
Files changed (56) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +21 -0
  3. data/README.md +528 -0
  4. data/exe/yerba +6 -0
  5. data/ext/yerba/extconf.rb +111 -0
  6. data/ext/yerba/yerba.c +752 -0
  7. data/lib/yerba/3.2/yerba.so +0 -0
  8. data/lib/yerba/3.3/yerba.so +0 -0
  9. data/lib/yerba/3.4/yerba.so +0 -0
  10. data/lib/yerba/4.0/yerba.so +0 -0
  11. data/lib/yerba/collection.rb +31 -0
  12. data/lib/yerba/document.rb +59 -0
  13. data/lib/yerba/formatting.rb +18 -0
  14. data/lib/yerba/location.rb +5 -0
  15. data/lib/yerba/map.rb +166 -0
  16. data/lib/yerba/scalar.rb +85 -0
  17. data/lib/yerba/sequence.rb +182 -0
  18. data/lib/yerba/version.rb +5 -0
  19. data/lib/yerba.rb +131 -0
  20. data/rust/Cargo.lock +805 -0
  21. data/rust/Cargo.toml +36 -0
  22. data/rust/build.rs +11 -0
  23. data/rust/cbindgen.toml +27 -0
  24. data/rust/rustfmt.toml +2 -0
  25. data/rust/src/commands/apply.rs +5 -0
  26. data/rust/src/commands/blank_lines.rs +58 -0
  27. data/rust/src/commands/check.rs +5 -0
  28. data/rust/src/commands/delete.rs +35 -0
  29. data/rust/src/commands/get.rs +194 -0
  30. data/rust/src/commands/init.rs +89 -0
  31. data/rust/src/commands/insert.rs +106 -0
  32. data/rust/src/commands/mate.rs +55 -0
  33. data/rust/src/commands/mod.rs +349 -0
  34. data/rust/src/commands/move_item.rs +54 -0
  35. data/rust/src/commands/move_key.rs +87 -0
  36. data/rust/src/commands/quote_style.rs +62 -0
  37. data/rust/src/commands/remove.rs +35 -0
  38. data/rust/src/commands/rename.rs +37 -0
  39. data/rust/src/commands/set.rs +59 -0
  40. data/rust/src/commands/sort.rs +52 -0
  41. data/rust/src/commands/sort_keys.rs +62 -0
  42. data/rust/src/commands/version.rs +8 -0
  43. data/rust/src/document.rs +2237 -0
  44. data/rust/src/error.rs +45 -0
  45. data/rust/src/ffi.rs +991 -0
  46. data/rust/src/json.rs +128 -0
  47. data/rust/src/lib.rs +29 -0
  48. data/rust/src/main.rs +72 -0
  49. data/rust/src/quote_style.rs +42 -0
  50. data/rust/src/selector.rs +241 -0
  51. data/rust/src/syntax.rs +262 -0
  52. data/rust/src/yaml_writer.rs +89 -0
  53. data/rust/src/yerbafile.rs +475 -0
  54. data/sig/yerba.rbs +3 -0
  55. data/yerba.gemspec +52 -0
  56. metadata +108 -0
@@ -0,0 +1,262 @@
1
+ use rowan::ast::AstNode;
2
+ use rowan::{TextRange, TextSize};
3
+
4
+ use yaml_parser::ast::{BlockMap, BlockMapEntry};
5
+ use yaml_parser::{SyntaxKind, SyntaxNode, SyntaxToken};
6
+
7
+ #[derive(Debug, Clone, PartialEq)]
8
+ pub struct ScalarValue {
9
+ pub text: String,
10
+ pub kind: SyntaxKind,
11
+ }
12
+
13
+ #[repr(C)]
14
+ #[derive(Debug, Clone, Copy, PartialEq)]
15
+ pub enum YerbaValueType {
16
+ Null = 0,
17
+ Boolean = 1,
18
+ Integer = 2,
19
+ Float = 3,
20
+ String = 4,
21
+ }
22
+
23
+ pub fn detect_yaml_type(scalar: &ScalarValue) -> YerbaValueType {
24
+ if scalar.kind != SyntaxKind::PLAIN_SCALAR {
25
+ return YerbaValueType::String;
26
+ }
27
+
28
+ detect_yaml_type_from_plain(&scalar.text)
29
+ }
30
+
31
+ pub fn extract_scalar(node: &SyntaxNode) -> Option<ScalarValue> {
32
+ let token = find_scalar_token(node)?;
33
+
34
+ let text = match token.kind() {
35
+ SyntaxKind::PLAIN_SCALAR => token.text().to_string(),
36
+
37
+ SyntaxKind::DOUBLE_QUOTED_SCALAR => {
38
+ let raw = token.text();
39
+ unescape_double_quoted(&raw[1..raw.len() - 1])
40
+ }
41
+
42
+ SyntaxKind::SINGLE_QUOTED_SCALAR => {
43
+ let raw = token.text();
44
+ unescape_single_quoted(&raw[1..raw.len() - 1])
45
+ }
46
+
47
+ _ => return None,
48
+ };
49
+
50
+ Some(ScalarValue {
51
+ text,
52
+ kind: token.kind(),
53
+ })
54
+ }
55
+
56
+ pub fn is_map_key(token: &SyntaxToken) -> bool {
57
+ token
58
+ .parent_ancestors()
59
+ .any(|ancestor| ancestor.kind() == SyntaxKind::BLOCK_MAP_KEY)
60
+ }
61
+
62
+ pub fn find_entry_by_key(map: &BlockMap, key: &str) -> Option<BlockMapEntry> {
63
+ map.entries().find(|entry| {
64
+ entry
65
+ .key()
66
+ .and_then(|key_node| extract_scalar_text(key_node.syntax()))
67
+ .map(|key_text| key_text == key)
68
+ .unwrap_or(false)
69
+ })
70
+ }
71
+
72
+ pub fn find_scalar_token(node: &SyntaxNode) -> Option<SyntaxToken> {
73
+ node
74
+ .descendants_with_tokens()
75
+ .filter_map(|element| element.into_token())
76
+ .find(|token| {
77
+ matches!(
78
+ token.kind(),
79
+ SyntaxKind::PLAIN_SCALAR | SyntaxKind::DOUBLE_QUOTED_SCALAR | SyntaxKind::SINGLE_QUOTED_SCALAR
80
+ )
81
+ })
82
+ }
83
+
84
+ pub fn format_scalar_value(value: &str, kind: SyntaxKind) -> String {
85
+ match kind {
86
+ SyntaxKind::DOUBLE_QUOTED_SCALAR => {
87
+ let escaped = value.replace('\\', "\\\\").replace('"', "\\\"");
88
+ format!("\"{}\"", escaped)
89
+ }
90
+
91
+ SyntaxKind::SINGLE_QUOTED_SCALAR => {
92
+ let escaped = value.replace('\'', "''");
93
+ format!("'{}'", escaped)
94
+ }
95
+
96
+ _ => value.to_string(),
97
+ }
98
+ }
99
+
100
+ pub fn extract_scalar_text(node: &SyntaxNode) -> Option<String> {
101
+ let token = find_scalar_token(node)?;
102
+
103
+ match token.kind() {
104
+ SyntaxKind::PLAIN_SCALAR => Some(token.text().to_string()),
105
+
106
+ SyntaxKind::DOUBLE_QUOTED_SCALAR => {
107
+ let text = token.text();
108
+ let inner = &text[1..text.len() - 1];
109
+
110
+ Some(unescape_double_quoted(inner))
111
+ }
112
+
113
+ SyntaxKind::SINGLE_QUOTED_SCALAR => {
114
+ let text = token.text();
115
+ let inner = &text[1..text.len() - 1];
116
+
117
+ Some(unescape_single_quoted(inner))
118
+ }
119
+
120
+ _ => None,
121
+ }
122
+ }
123
+
124
+ pub fn unescape_double_quoted(text: &str) -> String {
125
+ text.replace("\\\"", "\"").replace("\\\\", "\\")
126
+ }
127
+
128
+ pub fn unescape_single_quoted(text: &str) -> String {
129
+ text.replace("''", "'")
130
+ }
131
+
132
+ pub fn preceding_whitespace_indent(node: &SyntaxNode) -> String {
133
+ if let Some(token) = preceding_whitespace_token(node) {
134
+ let text = token.text();
135
+
136
+ if let Some(newline) = text.rfind('\n') {
137
+ return text[newline + 1..].to_string();
138
+ }
139
+ }
140
+
141
+ let start_offset: usize = node.text_range().start().into();
142
+ let root = node.ancestors().last().unwrap_or_else(|| node.clone());
143
+ let source = root.text().to_string();
144
+
145
+ if start_offset > 0 {
146
+ let before = &source[..start_offset];
147
+
148
+ if let Some(newline_position) = before.rfind('\n') {
149
+ return before[newline_position + 1..].to_string();
150
+ }
151
+ }
152
+
153
+ String::new()
154
+ }
155
+
156
+ pub fn preceding_whitespace_token(node: &SyntaxNode) -> Option<SyntaxToken> {
157
+ node
158
+ .prev_sibling_or_token()
159
+ .and_then(|sibling| sibling.into_token())
160
+ .filter(|token| token.kind() == SyntaxKind::WHITESPACE)
161
+ }
162
+
163
+ pub fn following_whitespace_token(node: &SyntaxNode) -> Option<SyntaxToken> {
164
+ node
165
+ .next_sibling_or_token()
166
+ .and_then(|sibling| sibling.into_token())
167
+ .filter(|token| token.kind() == SyntaxKind::WHITESPACE)
168
+ }
169
+
170
+ pub fn removal_range(node: &SyntaxNode) -> TextRange {
171
+ let node_range = node.text_range();
172
+
173
+ if let Some(whitespace_token) = preceding_whitespace_token(node) {
174
+ let whitespace_text = whitespace_token.text();
175
+ let whitespace_start = whitespace_token.text_range().start();
176
+
177
+ let remove_from = whitespace_text
178
+ .rfind('\n')
179
+ .map(|offset| whitespace_start + TextSize::from(offset as u32))
180
+ .unwrap_or(whitespace_start);
181
+
182
+ return TextRange::new(remove_from, node_range.end());
183
+ }
184
+
185
+ if let Some(whitespace_token) = following_whitespace_token(node) {
186
+ return TextRange::new(node_range.start(), whitespace_token.text_range().end());
187
+ }
188
+
189
+ node_range
190
+ }
191
+
192
+ pub fn is_yaml_non_string(value: &str) -> bool {
193
+ detect_yaml_type_from_plain(value) != YerbaValueType::String
194
+ }
195
+
196
+ pub fn is_yaml_truthy(value: &str) -> bool {
197
+ matches!(
198
+ value,
199
+ "true" | "True" | "TRUE" | "yes" | "Yes" | "YES" | "on" | "On" | "ON" | "y" | "Y"
200
+ )
201
+ }
202
+
203
+ pub fn detect_yaml_type_from_plain(value: &str) -> YerbaValueType {
204
+ // Null (YAML 1.1 + 1.2)
205
+ if matches!(value, "null" | "Null" | "NULL" | "~" | "") {
206
+ return YerbaValueType::Null;
207
+ }
208
+
209
+ // Boolean (YAML 1.2 + 1.1)
210
+ if matches!(
211
+ value,
212
+ "true"
213
+ | "True"
214
+ | "TRUE"
215
+ | "false"
216
+ | "False"
217
+ | "FALSE"
218
+ | "yes"
219
+ | "Yes"
220
+ | "YES"
221
+ | "no"
222
+ | "No"
223
+ | "NO"
224
+ | "on"
225
+ | "On"
226
+ | "ON"
227
+ | "off"
228
+ | "Off"
229
+ | "OFF"
230
+ | "y"
231
+ | "Y"
232
+ | "n"
233
+ | "N"
234
+ ) {
235
+ return YerbaValueType::Boolean;
236
+ }
237
+
238
+ // Integer
239
+ if value.parse::<i64>().is_ok() {
240
+ return YerbaValueType::Integer;
241
+ }
242
+
243
+ // Octal (0o...) and hex (0x...)
244
+ if value.starts_with("0x") || value.starts_with("0X") || value.starts_with("0o") || value.starts_with("0O") {
245
+ return YerbaValueType::Integer;
246
+ }
247
+
248
+ // Special floats (YAML 1.1 + 1.2)
249
+ if matches!(
250
+ value,
251
+ ".inf" | ".Inf" | ".INF" | "-.inf" | "-.Inf" | "-.INF" | "+.inf" | "+.Inf" | "+.INF" | ".nan" | ".NaN" | ".NAN"
252
+ ) {
253
+ return YerbaValueType::Float;
254
+ }
255
+
256
+ // Float
257
+ if value.parse::<f64>().is_ok() {
258
+ return YerbaValueType::Float;
259
+ }
260
+
261
+ YerbaValueType::String
262
+ }
@@ -0,0 +1,89 @@
1
+ use serde_json::Value;
2
+
3
+ use crate::QuoteStyle;
4
+
5
+ pub fn json_to_yaml_text(value: &Value, quote_style: &QuoteStyle, indent: usize) -> String {
6
+ match value {
7
+ Value::Object(map) => {
8
+ let prefix = " ".repeat(indent);
9
+
10
+ map
11
+ .iter()
12
+ .map(|(k, v)| match v {
13
+ Value::Array(arr) => {
14
+ let items: Vec<String> = arr
15
+ .iter()
16
+ .map(|item| match item {
17
+ Value::Object(_) => {
18
+ let inner = json_to_yaml_text(item, quote_style, indent + 4);
19
+ format!("{} - {}", prefix, inner.trim_start())
20
+ }
21
+
22
+ _ => format!("{} - {}", prefix, format_yaml_scalar(item, quote_style)),
23
+ })
24
+ .collect();
25
+
26
+ format!("{}{}:\n{}", prefix, k, items.join("\n"))
27
+ }
28
+
29
+ Value::Object(_) => {
30
+ let inner = json_to_yaml_text(v, quote_style, indent + 2);
31
+ format!("{}{}:\n{}", prefix, k, inner)
32
+ }
33
+
34
+ _ => format!("{}{}: {}", prefix, k, format_yaml_scalar(v, quote_style)),
35
+ })
36
+ .collect::<Vec<_>>()
37
+ .join("\n")
38
+ }
39
+
40
+ _ => {
41
+ let prefix = " ".repeat(indent);
42
+
43
+ format!("{}{}", prefix, format_yaml_scalar(value, quote_style))
44
+ }
45
+ }
46
+ }
47
+
48
+ fn format_yaml_scalar(value: &Value, quote_style: &QuoteStyle) -> String {
49
+ match value {
50
+ Value::Null => "null".to_string(),
51
+ Value::Bool(boolean) => boolean.to_string(),
52
+ Value::Number(number) => number.to_string(),
53
+ Value::String(string) => match quote_style {
54
+ QuoteStyle::Double => {
55
+ let escaped = string.replace('\\', "\\\\").replace('"', "\\\"");
56
+
57
+ format!("\"{}\"", escaped)
58
+ }
59
+
60
+ QuoteStyle::Single => {
61
+ let escaped = string.replace('\'', "''");
62
+
63
+ format!("'{}'", escaped)
64
+ }
65
+
66
+ QuoteStyle::Plain => {
67
+ if crate::syntax::is_yaml_non_string(string) {
68
+ format!("\"{}\"", string.replace('"', "\\\""))
69
+ } else {
70
+ string.clone()
71
+ }
72
+ }
73
+
74
+ _ => {
75
+ let escaped = string.replace('\\', "\\\\").replace('"', "\\\"");
76
+
77
+ format!("\"{}\"", escaped)
78
+ }
79
+ },
80
+
81
+ Value::Array(array) => {
82
+ let items: Vec<String> = array.iter().map(|item| format_yaml_scalar(item, quote_style)).collect();
83
+
84
+ format!("[{}]", items.join(", "))
85
+ }
86
+
87
+ Value::Object(_) => format!("{:?}", value),
88
+ }
89
+ }