@owenlamont/ryl 0.4.1 → 0.4.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.
Files changed (217) hide show
  1. package/README.md +13 -0
  2. package/bin/ryl.js +96 -1
  3. package/npm-platforms.json +124 -0
  4. package/package.json +46 -12
  5. package/.github/CODEOWNERS +0 -1
  6. package/.github/dependabot.yml +0 -13
  7. package/.github/workflows/ci.yml +0 -107
  8. package/.github/workflows/release.yml +0 -613
  9. package/.github/workflows/update_dependencies.yml +0 -61
  10. package/.github/workflows/update_linters.yml +0 -56
  11. package/.pre-commit-config.yaml +0 -87
  12. package/.yamllint +0 -4
  13. package/AGENTS.md +0 -200
  14. package/Cargo.lock +0 -908
  15. package/Cargo.toml +0 -32
  16. package/clippy.toml +0 -1
  17. package/docs/config-presets.md +0 -100
  18. package/img/benchmark-5x5-5runs.svg +0 -2176
  19. package/pyproject.toml +0 -42
  20. package/ruff.toml +0 -107
  21. package/rumdl.toml +0 -20
  22. package/rust-toolchain.toml +0 -3
  23. package/rustfmt.toml +0 -3
  24. package/scripts/benchmark_perf_vs_yamllint.py +0 -400
  25. package/scripts/coverage-missing.ps1 +0 -80
  26. package/scripts/coverage-missing.sh +0 -60
  27. package/src/bin/discover_config_bin.rs +0 -24
  28. package/src/cli_support.rs +0 -33
  29. package/src/conf/mod.rs +0 -85
  30. package/src/config.rs +0 -2099
  31. package/src/decoder.rs +0 -326
  32. package/src/discover.rs +0 -31
  33. package/src/lib.rs +0 -19
  34. package/src/lint.rs +0 -558
  35. package/src/main.rs +0 -535
  36. package/src/migrate.rs +0 -233
  37. package/src/rules/anchors.rs +0 -517
  38. package/src/rules/braces.rs +0 -77
  39. package/src/rules/brackets.rs +0 -77
  40. package/src/rules/colons.rs +0 -475
  41. package/src/rules/commas.rs +0 -372
  42. package/src/rules/comments.rs +0 -299
  43. package/src/rules/comments_indentation.rs +0 -243
  44. package/src/rules/document_end.rs +0 -175
  45. package/src/rules/document_start.rs +0 -84
  46. package/src/rules/empty_lines.rs +0 -152
  47. package/src/rules/empty_values.rs +0 -255
  48. package/src/rules/float_values.rs +0 -259
  49. package/src/rules/flow_collection.rs +0 -562
  50. package/src/rules/hyphens.rs +0 -104
  51. package/src/rules/indentation.rs +0 -803
  52. package/src/rules/key_duplicates.rs +0 -218
  53. package/src/rules/key_ordering.rs +0 -303
  54. package/src/rules/line_length.rs +0 -326
  55. package/src/rules/mod.rs +0 -25
  56. package/src/rules/new_line_at_end_of_file.rs +0 -23
  57. package/src/rules/new_lines.rs +0 -95
  58. package/src/rules/octal_values.rs +0 -121
  59. package/src/rules/quoted_strings.rs +0 -577
  60. package/src/rules/span_utils.rs +0 -37
  61. package/src/rules/trailing_spaces.rs +0 -65
  62. package/src/rules/truthy.rs +0 -420
  63. package/tests/brackets_carriage_return.rs +0 -114
  64. package/tests/build_global_cfg_error.rs +0 -23
  65. package/tests/cli_anchors_rule.rs +0 -143
  66. package/tests/cli_braces_rule.rs +0 -104
  67. package/tests/cli_brackets_rule.rs +0 -104
  68. package/tests/cli_colons_rule.rs +0 -65
  69. package/tests/cli_commas_rule.rs +0 -104
  70. package/tests/cli_comments_indentation_rule.rs +0 -61
  71. package/tests/cli_comments_rule.rs +0 -67
  72. package/tests/cli_config_data_error.rs +0 -30
  73. package/tests/cli_config_flags.rs +0 -66
  74. package/tests/cli_config_migrate.rs +0 -229
  75. package/tests/cli_document_end_rule.rs +0 -92
  76. package/tests/cli_document_start_rule.rs +0 -92
  77. package/tests/cli_empty_lines_rule.rs +0 -87
  78. package/tests/cli_empty_values_rule.rs +0 -68
  79. package/tests/cli_env_config.rs +0 -34
  80. package/tests/cli_exit_and_errors.rs +0 -41
  81. package/tests/cli_file_encoding.rs +0 -203
  82. package/tests/cli_float_values_rule.rs +0 -64
  83. package/tests/cli_format_options.rs +0 -316
  84. package/tests/cli_global_cfg_relaxed.rs +0 -20
  85. package/tests/cli_hyphens_rule.rs +0 -104
  86. package/tests/cli_indentation_rule.rs +0 -65
  87. package/tests/cli_invalid_project_config.rs +0 -39
  88. package/tests/cli_key_duplicates_rule.rs +0 -104
  89. package/tests/cli_key_ordering_rule.rs +0 -59
  90. package/tests/cli_line_length_rule.rs +0 -85
  91. package/tests/cli_list_files.rs +0 -29
  92. package/tests/cli_new_line_rule.rs +0 -141
  93. package/tests/cli_new_lines_rule.rs +0 -119
  94. package/tests/cli_octal_values_rule.rs +0 -60
  95. package/tests/cli_quoted_strings_rule.rs +0 -47
  96. package/tests/cli_toml_config.rs +0 -119
  97. package/tests/cli_trailing_spaces_rule.rs +0 -77
  98. package/tests/cli_truthy_rule.rs +0 -83
  99. package/tests/cli_yaml_files_negation.rs +0 -45
  100. package/tests/colons_rule.rs +0 -303
  101. package/tests/common/compat.rs +0 -114
  102. package/tests/common/fake_env.rs +0 -93
  103. package/tests/common/mod.rs +0 -1
  104. package/tests/conf_builtin.rs +0 -9
  105. package/tests/config_anchors.rs +0 -84
  106. package/tests/config_braces.rs +0 -121
  107. package/tests/config_brackets.rs +0 -127
  108. package/tests/config_commas.rs +0 -79
  109. package/tests/config_comments.rs +0 -65
  110. package/tests/config_comments_indentation.rs +0 -20
  111. package/tests/config_deep_merge_nonstring_key.rs +0 -24
  112. package/tests/config_document_end.rs +0 -54
  113. package/tests/config_document_start.rs +0 -55
  114. package/tests/config_empty_lines.rs +0 -48
  115. package/tests/config_empty_values.rs +0 -35
  116. package/tests/config_env_errors.rs +0 -23
  117. package/tests/config_env_invalid_inline.rs +0 -15
  118. package/tests/config_env_missing.rs +0 -63
  119. package/tests/config_env_shim.rs +0 -301
  120. package/tests/config_explicit_file_parse_error.rs +0 -55
  121. package/tests/config_extended_features.rs +0 -225
  122. package/tests/config_extends_inline.rs +0 -185
  123. package/tests/config_extends_sequence.rs +0 -18
  124. package/tests/config_find_project_home_boundary.rs +0 -54
  125. package/tests/config_find_project_two_files_in_cwd.rs +0 -47
  126. package/tests/config_float_values.rs +0 -34
  127. package/tests/config_from_yaml_paths.rs +0 -32
  128. package/tests/config_hyphens.rs +0 -51
  129. package/tests/config_ignore_errors.rs +0 -243
  130. package/tests/config_ignore_overrides.rs +0 -83
  131. package/tests/config_indentation.rs +0 -65
  132. package/tests/config_invalid_globs.rs +0 -16
  133. package/tests/config_invalid_types.rs +0 -19
  134. package/tests/config_key_duplicates.rs +0 -34
  135. package/tests/config_key_ordering.rs +0 -70
  136. package/tests/config_line_length.rs +0 -65
  137. package/tests/config_locale.rs +0 -111
  138. package/tests/config_merge.rs +0 -26
  139. package/tests/config_new_lines.rs +0 -89
  140. package/tests/config_octal_values.rs +0 -33
  141. package/tests/config_quoted_strings.rs +0 -195
  142. package/tests/config_rule_level.rs +0 -147
  143. package/tests/config_rules_non_string_keys.rs +0 -23
  144. package/tests/config_scalar_overrides.rs +0 -27
  145. package/tests/config_to_toml.rs +0 -110
  146. package/tests/config_toml_coverage.rs +0 -80
  147. package/tests/config_toml_discovery.rs +0 -304
  148. package/tests/config_trailing_spaces.rs +0 -152
  149. package/tests/config_truthy.rs +0 -77
  150. package/tests/config_yaml_files.rs +0 -62
  151. package/tests/config_yaml_files_all_non_string.rs +0 -15
  152. package/tests/config_yaml_files_empty.rs +0 -30
  153. package/tests/coverage_commas.rs +0 -46
  154. package/tests/decoder_decode.rs +0 -338
  155. package/tests/discover_config_bin_all.rs +0 -66
  156. package/tests/discover_config_bin_env_invalid_yaml.rs +0 -26
  157. package/tests/discover_config_bin_project_config_parse_error.rs +0 -24
  158. package/tests/discover_config_bin_user_global_error.rs +0 -26
  159. package/tests/discover_module.rs +0 -30
  160. package/tests/discover_per_file_dir.rs +0 -10
  161. package/tests/discover_per_file_project_config_error.rs +0 -21
  162. package/tests/float_values.rs +0 -43
  163. package/tests/lint_multi_errors.rs +0 -32
  164. package/tests/main_yaml_ok_filtering.rs +0 -30
  165. package/tests/migrate_module.rs +0 -259
  166. package/tests/resolve_ctx_empty_parent.rs +0 -16
  167. package/tests/rule_anchors.rs +0 -442
  168. package/tests/rule_braces.rs +0 -258
  169. package/tests/rule_brackets.rs +0 -217
  170. package/tests/rule_commas.rs +0 -205
  171. package/tests/rule_comments.rs +0 -197
  172. package/tests/rule_comments_indentation.rs +0 -127
  173. package/tests/rule_document_end.rs +0 -118
  174. package/tests/rule_document_start.rs +0 -60
  175. package/tests/rule_empty_lines.rs +0 -96
  176. package/tests/rule_empty_values.rs +0 -102
  177. package/tests/rule_float_values.rs +0 -109
  178. package/tests/rule_hyphens.rs +0 -65
  179. package/tests/rule_indentation.rs +0 -455
  180. package/tests/rule_key_duplicates.rs +0 -76
  181. package/tests/rule_key_ordering.rs +0 -207
  182. package/tests/rule_line_length.rs +0 -200
  183. package/tests/rule_new_lines.rs +0 -51
  184. package/tests/rule_octal_values.rs +0 -53
  185. package/tests/rule_quoted_strings.rs +0 -290
  186. package/tests/rule_trailing_spaces.rs +0 -41
  187. package/tests/rule_truthy.rs +0 -236
  188. package/tests/user_global_invalid_yaml.rs +0 -32
  189. package/tests/yamllint_compat_anchors.rs +0 -280
  190. package/tests/yamllint_compat_braces.rs +0 -411
  191. package/tests/yamllint_compat_brackets.rs +0 -364
  192. package/tests/yamllint_compat_colons.rs +0 -298
  193. package/tests/yamllint_compat_colors.rs +0 -80
  194. package/tests/yamllint_compat_commas.rs +0 -375
  195. package/tests/yamllint_compat_comments.rs +0 -167
  196. package/tests/yamllint_compat_comments_indentation.rs +0 -281
  197. package/tests/yamllint_compat_config.rs +0 -170
  198. package/tests/yamllint_compat_document_end.rs +0 -243
  199. package/tests/yamllint_compat_document_start.rs +0 -136
  200. package/tests/yamllint_compat_empty_lines.rs +0 -117
  201. package/tests/yamllint_compat_empty_values.rs +0 -179
  202. package/tests/yamllint_compat_float_values.rs +0 -216
  203. package/tests/yamllint_compat_hyphens.rs +0 -223
  204. package/tests/yamllint_compat_indentation.rs +0 -398
  205. package/tests/yamllint_compat_key_duplicates.rs +0 -139
  206. package/tests/yamllint_compat_key_ordering.rs +0 -170
  207. package/tests/yamllint_compat_line_length.rs +0 -375
  208. package/tests/yamllint_compat_list.rs +0 -127
  209. package/tests/yamllint_compat_new_line.rs +0 -133
  210. package/tests/yamllint_compat_newline_types.rs +0 -185
  211. package/tests/yamllint_compat_octal_values.rs +0 -172
  212. package/tests/yamllint_compat_quoted_strings.rs +0 -154
  213. package/tests/yamllint_compat_syntax.rs +0 -200
  214. package/tests/yamllint_compat_trailing_spaces.rs +0 -162
  215. package/tests/yamllint_compat_truthy.rs +0 -130
  216. package/tests/yamllint_compat_yaml_files.rs +0 -81
  217. package/typos.toml +0 -2
@@ -1,255 +0,0 @@
1
- use saphyr_parser::{Event, Parser, Span, SpannedEventReceiver};
2
-
3
- use crate::config::YamlLintConfig;
4
-
5
- pub const ID: &str = "empty-values";
6
-
7
- const BLOCK_MAPPING_MESSAGE: &str = "empty value in block mapping";
8
- const FLOW_MAPPING_MESSAGE: &str = "empty value in flow mapping";
9
- const BLOCK_SEQUENCE_MESSAGE: &str = "empty value in block sequence";
10
-
11
- #[derive(Debug, Clone, Copy, PartialEq, Eq)]
12
- pub struct Config {
13
- forbid_block_mappings: bool,
14
- forbid_flow_mappings: bool,
15
- forbid_block_sequences: bool,
16
- }
17
-
18
- impl Config {
19
- #[must_use]
20
- pub fn resolve(cfg: &YamlLintConfig) -> Self {
21
- let forbid_block_mappings = cfg
22
- .rule_option(ID, "forbid-in-block-mappings")
23
- .and_then(saphyr::YamlOwned::as_bool)
24
- .unwrap_or(true);
25
-
26
- let forbid_flow_mappings = cfg
27
- .rule_option(ID, "forbid-in-flow-mappings")
28
- .and_then(saphyr::YamlOwned::as_bool)
29
- .unwrap_or(true);
30
-
31
- let forbid_block_sequences = cfg
32
- .rule_option(ID, "forbid-in-block-sequences")
33
- .and_then(saphyr::YamlOwned::as_bool)
34
- .unwrap_or(true);
35
-
36
- Self {
37
- forbid_block_mappings,
38
- forbid_flow_mappings,
39
- forbid_block_sequences,
40
- }
41
- }
42
- }
43
-
44
- #[derive(Debug, Clone, PartialEq, Eq)]
45
- pub struct Violation {
46
- pub line: usize,
47
- pub column: usize,
48
- pub message: String,
49
- }
50
-
51
- #[must_use]
52
- pub fn check(buffer: &str, cfg: &Config) -> Vec<Violation> {
53
- let mut parser = Parser::new_from_str(buffer);
54
- let mut receiver = EmptyValuesReceiver::new(cfg);
55
- let _ = parser.load(&mut receiver, true);
56
- receiver.diagnostics
57
- }
58
-
59
- #[derive(Clone, Copy, Debug, PartialEq, Eq)]
60
- enum MappingStyle {
61
- Block,
62
- Flow,
63
- }
64
-
65
- #[derive(Clone, Copy, Debug, PartialEq, Eq)]
66
- enum SequenceStyle {
67
- Block,
68
- Flow,
69
- }
70
-
71
- #[derive(Clone, Copy, Debug, PartialEq, Eq)]
72
- struct NodeContext {
73
- mapping: Option<(MappingStyle, NodePosition)>,
74
- in_block_sequence: bool,
75
- }
76
-
77
- #[derive(Clone, Copy, Debug, PartialEq, Eq)]
78
- enum NodePosition {
79
- Key,
80
- Value,
81
- }
82
-
83
- struct EmptyValuesReceiver<'cfg> {
84
- config: &'cfg Config,
85
- containers: Vec<ContainerState>,
86
- diagnostics: Vec<Violation>,
87
- }
88
-
89
- impl<'cfg> EmptyValuesReceiver<'cfg> {
90
- const fn new(config: &'cfg Config) -> Self {
91
- Self {
92
- config,
93
- containers: Vec::new(),
94
- diagnostics: Vec::new(),
95
- }
96
- }
97
-
98
- fn reset(&mut self) {
99
- self.containers.clear();
100
- }
101
-
102
- fn begin_node(&mut self) -> NodeContext {
103
- let mapping = if let Some(ContainerState::Mapping { style, expect_key }) =
104
- self.containers.last_mut()
105
- {
106
- if *expect_key {
107
- *expect_key = false;
108
- Some((*style, NodePosition::Key))
109
- } else {
110
- *expect_key = true;
111
- Some((*style, NodePosition::Value))
112
- }
113
- } else {
114
- None
115
- };
116
-
117
- let in_block_sequence = matches!(
118
- self.containers.last(),
119
- Some(ContainerState::Sequence {
120
- style: SequenceStyle::Block,
121
- })
122
- );
123
-
124
- NodeContext {
125
- mapping,
126
- in_block_sequence,
127
- }
128
- }
129
-
130
- fn push_mapping(&mut self, span: Span) {
131
- let style = if span.is_empty() {
132
- MappingStyle::Block
133
- } else {
134
- MappingStyle::Flow
135
- };
136
- self.containers.push(ContainerState::Mapping {
137
- style,
138
- expect_key: true,
139
- });
140
- }
141
-
142
- fn push_sequence(&mut self, span: Span) {
143
- let style = if span.is_empty() {
144
- SequenceStyle::Block
145
- } else {
146
- SequenceStyle::Flow
147
- };
148
- self.containers.push(ContainerState::Sequence { style });
149
- }
150
-
151
- fn push_container(&mut self, span: Span, kind: ContainerKind) {
152
- match kind {
153
- ContainerKind::Mapping => self.push_mapping(span),
154
- ContainerKind::Sequence => self.push_sequence(span),
155
- }
156
- }
157
-
158
- fn pop_container(&mut self) {
159
- let _ = self.containers.pop();
160
- }
161
-
162
- fn handle_scalar(&mut self, span: Span, ctx: NodeContext) {
163
- if span.is_empty() {
164
- if let Some((style, NodePosition::Value)) = ctx.mapping {
165
- match style {
166
- MappingStyle::Block if self.config.forbid_block_mappings => {
167
- self.record(span, BLOCK_MAPPING_MESSAGE);
168
- }
169
- MappingStyle::Flow if self.config.forbid_flow_mappings => {
170
- self.record(span, FLOW_MAPPING_MESSAGE);
171
- }
172
- _ => {}
173
- }
174
- return;
175
- }
176
-
177
- if ctx.in_block_sequence && self.config.forbid_block_sequences {
178
- self.record(span, BLOCK_SEQUENCE_MESSAGE);
179
- }
180
- }
181
- }
182
-
183
- fn record(&mut self, span: Span, message: &str) {
184
- let line = span.start.line();
185
- let column =
186
- if let Some(ContainerState::Mapping { .. }) = self.containers.last() {
187
- span.start.col() + 2
188
- } else {
189
- span.start.col() + 1
190
- };
191
- self.diagnostics.push(Violation {
192
- line,
193
- column,
194
- message: message.to_string(),
195
- });
196
- }
197
- }
198
-
199
- #[derive(Clone, Copy, Debug, PartialEq, Eq)]
200
- enum ContainerKind {
201
- Mapping,
202
- Sequence,
203
- }
204
-
205
- enum ContainerState {
206
- Mapping {
207
- style: MappingStyle,
208
- expect_key: bool,
209
- },
210
- Sequence {
211
- style: SequenceStyle,
212
- },
213
- }
214
-
215
- impl SpannedEventReceiver<'_> for EmptyValuesReceiver<'_> {
216
- fn on_event(&mut self, event: Event<'_>, span: Span) {
217
- match event {
218
- Event::StreamStart
219
- | Event::DocumentStart(_)
220
- | Event::StreamEnd
221
- | Event::DocumentEnd => {
222
- self.reset();
223
- }
224
- Event::MappingStart(_, _) => {
225
- self.begin_node();
226
- self.push_container(span, ContainerKind::Mapping);
227
- }
228
- Event::SequenceStart(_, _) => {
229
- self.begin_node();
230
- self.push_container(span, ContainerKind::Sequence);
231
- }
232
- Event::MappingEnd | Event::SequenceEnd => {
233
- self.pop_container();
234
- }
235
- Event::Scalar(_, _, _, _) => {
236
- let ctx = self.begin_node();
237
- self.handle_scalar(span, ctx);
238
- }
239
- Event::Alias(_) => {
240
- self.begin_node();
241
- }
242
- Event::Nothing => {}
243
- }
244
- }
245
- }
246
-
247
- #[allow(dead_code)]
248
- pub fn coverage_touch_nothing_branch() {
249
- use saphyr_parser::{Marker, Span};
250
-
251
- let cfg_struct = crate::config::YamlLintConfig::default();
252
- let config = Config::resolve(&cfg_struct);
253
- let mut receiver = EmptyValuesReceiver::new(&config);
254
- receiver.on_event(Event::Nothing, Span::empty(Marker::default()));
255
- }
@@ -1,259 +0,0 @@
1
- use saphyr_parser::{Event, Parser, ScalarStyle, Span, SpannedEventReceiver};
2
-
3
- use crate::config::YamlLintConfig;
4
- use crate::rules::span_utils::span_char_index_to_byte;
5
-
6
- pub const ID: &str = "float-values";
7
-
8
- #[derive(Debug, Clone)]
9
- pub struct Config {
10
- flags: u8,
11
- }
12
-
13
- const REQUIRE_NUMERAL_FLAG: u8 = 1 << 0;
14
- const FORBID_SCIENTIFIC_FLAG: u8 = 1 << 1;
15
- const FORBID_NAN_FLAG: u8 = 1 << 2;
16
- const FORBID_INF_FLAG: u8 = 1 << 3;
17
-
18
- impl Config {
19
- #[must_use]
20
- pub fn resolve(cfg: &YamlLintConfig) -> Self {
21
- let require_numeral_before_decimal = cfg
22
- .rule_option(ID, "require-numeral-before-decimal")
23
- .and_then(saphyr::YamlOwned::as_bool)
24
- .unwrap_or(false);
25
-
26
- let forbid_scientific_notation = cfg
27
- .rule_option(ID, "forbid-scientific-notation")
28
- .and_then(saphyr::YamlOwned::as_bool)
29
- .unwrap_or(false);
30
-
31
- let forbid_nan = cfg
32
- .rule_option(ID, "forbid-nan")
33
- .and_then(saphyr::YamlOwned::as_bool)
34
- .unwrap_or(false);
35
-
36
- let forbid_inf = cfg
37
- .rule_option(ID, "forbid-inf")
38
- .and_then(saphyr::YamlOwned::as_bool)
39
- .unwrap_or(false);
40
-
41
- let mut flags = 0u8;
42
- if require_numeral_before_decimal {
43
- flags |= REQUIRE_NUMERAL_FLAG;
44
- }
45
- if forbid_scientific_notation {
46
- flags |= FORBID_SCIENTIFIC_FLAG;
47
- }
48
- if forbid_nan {
49
- flags |= FORBID_NAN_FLAG;
50
- }
51
- if forbid_inf {
52
- flags |= FORBID_INF_FLAG;
53
- }
54
-
55
- Self { flags }
56
- }
57
-
58
- const fn require_numeral_before_decimal(&self) -> bool {
59
- (self.flags & REQUIRE_NUMERAL_FLAG) != 0
60
- }
61
-
62
- const fn forbid_scientific_notation(&self) -> bool {
63
- (self.flags & FORBID_SCIENTIFIC_FLAG) != 0
64
- }
65
-
66
- const fn forbid_nan(&self) -> bool {
67
- (self.flags & FORBID_NAN_FLAG) != 0
68
- }
69
-
70
- const fn forbid_inf(&self) -> bool {
71
- (self.flags & FORBID_INF_FLAG) != 0
72
- }
73
- }
74
-
75
- #[derive(Debug, Clone, PartialEq, Eq)]
76
- pub struct Violation {
77
- pub line: usize,
78
- pub column: usize,
79
- pub message: String,
80
- }
81
-
82
- #[must_use]
83
- pub fn check(buffer: &str, cfg: &Config) -> Vec<Violation> {
84
- let mut parser = Parser::new_from_str(buffer);
85
- let mut receiver = FloatValuesReceiver::new(cfg, buffer);
86
- let _ = parser.load(&mut receiver, true);
87
- receiver.diagnostics
88
- }
89
-
90
- struct FloatValuesReceiver<'cfg, 'input> {
91
- config: &'cfg Config,
92
- buffer: &'input str,
93
- chars: Vec<(usize, char)>,
94
- buffer_len: usize,
95
- diagnostics: Vec<Violation>,
96
- }
97
-
98
- impl<'cfg, 'input> FloatValuesReceiver<'cfg, 'input> {
99
- fn new(config: &'cfg Config, buffer: &'input str) -> Self {
100
- Self {
101
- config,
102
- buffer,
103
- chars: buffer.char_indices().collect(),
104
- buffer_len: buffer.len(),
105
- diagnostics: Vec::new(),
106
- }
107
- }
108
-
109
- fn handle_scalar(&mut self, value: &str, span: Span) {
110
- let line = span.start.line();
111
- let column = span.start.col() + 1;
112
-
113
- if self.config.forbid_nan() && is_nan(value) {
114
- self.diagnostics.push(Violation {
115
- line,
116
- column,
117
- message: format!(
118
- "forbidden not a number value \"{}\"",
119
- self.original_scalar(span, value)
120
- ),
121
- });
122
- }
123
-
124
- if self.config.forbid_inf() && is_inf(value) {
125
- self.diagnostics.push(Violation {
126
- line,
127
- column,
128
- message: format!(
129
- "forbidden infinite value \"{}\"",
130
- self.original_scalar(span, value)
131
- ),
132
- });
133
- }
134
-
135
- if self.config.forbid_scientific_notation() && is_scientific_notation(value) {
136
- self.diagnostics.push(Violation {
137
- line,
138
- column,
139
- message: format!(
140
- "forbidden scientific notation \"{}\"",
141
- self.original_scalar(span, value)
142
- ),
143
- });
144
- }
145
-
146
- if self.config.require_numeral_before_decimal()
147
- && is_missing_numeral_before_decimal(value)
148
- {
149
- self.diagnostics.push(Violation {
150
- line,
151
- column,
152
- message: format!(
153
- "forbidden decimal missing 0 prefix \"{}\"",
154
- self.original_scalar(span, value)
155
- ),
156
- });
157
- }
158
- }
159
-
160
- fn original_scalar<'a>(&'a self, span: Span, fallback: &'a str) -> &'a str {
161
- let start_char = span.start.index();
162
- let end_char = span.end.index();
163
- let start = span_char_index_to_byte(&self.chars, start_char, self.buffer_len);
164
- let end = span_char_index_to_byte(&self.chars, end_char, self.buffer_len);
165
- let range_start = start.min(end);
166
- let range_end = start.max(end);
167
- self.buffer.get(range_start..range_end).unwrap_or(fallback)
168
- }
169
- }
170
-
171
- impl<'input> SpannedEventReceiver<'input> for FloatValuesReceiver<'_, 'input> {
172
- fn on_event(&mut self, event: Event<'input>, span: Span) {
173
- if let Event::Scalar(value, style, _, tag) = event {
174
- if tag.is_some() || !matches!(style, ScalarStyle::Plain) {
175
- return;
176
- }
177
- self.handle_scalar(value.as_ref(), span);
178
- }
179
- }
180
- }
181
-
182
- fn is_nan(value: &str) -> bool {
183
- matches!(value, ".nan" | ".NaN" | ".NAN")
184
- }
185
-
186
- fn is_inf(value: &str) -> bool {
187
- let trimmed = without_sign(value);
188
- matches!(trimmed, ".inf" | ".Inf" | ".INF")
189
- }
190
-
191
- fn is_scientific_notation(value: &str) -> bool {
192
- let trimmed = without_sign(value);
193
- let Some((mantissa, exponent)) = split_exponent(trimmed) else {
194
- return false;
195
- };
196
- if !is_valid_exponent(exponent) {
197
- return false;
198
- }
199
- if let Some(digits) = mantissa.strip_prefix('.') {
200
- return !digits.is_empty() && digits.chars().all(|c| c.is_ascii_digit());
201
- }
202
-
203
- if mantissa.is_empty() {
204
- return false;
205
- }
206
-
207
- let mut parts = mantissa.splitn(2, '.');
208
- let int_part = parts.next().unwrap();
209
- if int_part.is_empty() || !int_part.chars().all(|c| c.is_ascii_digit()) {
210
- return false;
211
- }
212
- if let Some(frac_part) = parts.next() {
213
- return frac_part.chars().all(|c| c.is_ascii_digit());
214
- }
215
- true
216
- }
217
-
218
- fn is_missing_numeral_before_decimal(value: &str) -> bool {
219
- let trimmed = without_sign(value);
220
- if !trimmed.starts_with('.') {
221
- return false;
222
- }
223
- let after_dot = &trimmed[1..];
224
- if after_dot.is_empty() {
225
- return false;
226
- }
227
-
228
- let (digits, exponent) = match split_exponent(after_dot) {
229
- Some((mantissa, exponent)) => (mantissa, Some(exponent)),
230
- None => (after_dot, None),
231
- };
232
-
233
- if digits.is_empty() || !digits.chars().all(|c| c.is_ascii_digit()) {
234
- return false;
235
- }
236
-
237
- exponent.is_none_or(is_valid_exponent)
238
- }
239
-
240
- fn split_exponent(value: &str) -> Option<(&str, &str)> {
241
- let idx = value.find(['e', 'E'])?;
242
- Some(value.split_at(idx))
243
- }
244
-
245
- fn is_valid_exponent(exponent: &str) -> bool {
246
- let rest = exponent
247
- .strip_prefix('e')
248
- .or_else(|| exponent.strip_prefix('E'))
249
- .expect("exponent fragment must start with e/E");
250
- let rest = without_sign(rest);
251
- !rest.is_empty() && rest.chars().all(|c| c.is_ascii_digit())
252
- }
253
-
254
- fn without_sign(value: &str) -> &str {
255
- value
256
- .strip_prefix('+')
257
- .or_else(|| value.strip_prefix('-'))
258
- .unwrap_or(value)
259
- }