1#![allow(unused_assignments)]
2use crate::{Span, Type, ast::RedirectionSource, did_you_mean};
3use miette::Diagnostic;
4use serde::{Deserialize, Serialize};
5use std::{
6 fmt::Display,
7 str::{Utf8Error, from_utf8},
8};
9use thiserror::Error;
10
11#[derive(Clone, Debug, Error, Diagnostic, Serialize, Deserialize, PartialEq)]
12pub enum ParseError {
13 #[error("Extra tokens in code.")]
17 #[diagnostic(code(nu::parser::extra_tokens), help("Try removing them."))]
18 ExtraTokens(#[label = "extra tokens"] Span),
19
20 #[error("Invalid characters after closing delimiter")]
21 #[diagnostic(
22 code(nu::parser::extra_token_after_closing_delimiter),
23 help("Try removing them.")
24 )]
25 ExtraTokensAfterClosingDelimiter(#[label = "invalid characters"] Span),
26
27 #[error("Extra positional argument.")]
28 #[diagnostic(code(nu::parser::extra_positional), help("Usage: {0}"))]
29 ExtraPositional(String, #[label = "extra positional argument"] Span),
30
31 #[error("Required positional parameter after optional parameter")]
32 #[diagnostic(code(nu::parser::required_after_optional))]
33 RequiredAfterOptional(
34 String,
35 #[label = "required parameter {0} after optional parameter"] Span,
36 ),
37
38 #[error("Unexpected end of code.")]
39 #[diagnostic(code(nu::parser::unexpected_eof))]
40 UnexpectedEof(String, #[label("expected closing {0}")] Span),
41
42 #[error("Unclosed delimiter.")]
43 #[diagnostic(code(nu::parser::unclosed_delimiter))]
44 Unclosed(&'static str, #[label("unclosed {0}")] Span),
45
46 #[error("Unbalanced delimiter.")]
47 #[diagnostic(code(nu::parser::unbalanced_delimiter))]
48 Unbalanced(
49 &'static str,
50 &'static str,
51 #[label("unbalanced {0} and {1}")] Span,
52 ),
53
54 #[error("Parse mismatch during operation.")]
55 #[diagnostic(code(nu::parser::parse_mismatch))]
56 Expected(&'static str, #[label("expected {0}")] Span),
57
58 #[error("Parse mismatch during operation.")]
59 #[diagnostic(code(nu::parser::parse_mismatch_with_full_string_msg))]
60 ExpectedWithStringMsg(String, #[label("expected {0}")] Span),
61
62 #[error("Parse mismatch during operation.")]
63 #[diagnostic(code(nu::parser::parse_mismatch_with_did_you_mean))]
64 ExpectedWithDidYouMean(&'static str, DidYouMean, #[label("expected {0}. {1}")] Span),
65
66 #[error("Command does not support {0} input.")]
67 #[diagnostic(code(nu::parser::input_type_mismatch))]
68 InputMismatch(String, #[label("command doesn't support {0} input")] Span),
69
70 #[error("Command output doesn't match {0}.")]
71 #[diagnostic(code(nu::parser::output_type_mismatch))]
72 OutputMismatch(
73 Type,
74 String,
75 #[label("expected {0}, but command outputs {1}")] Span,
76 ),
77
78 #[error("Type mismatch during operation.")]
79 #[diagnostic(code(nu::parser::type_mismatch))]
80 Mismatch(String, String, #[label("expected {0}, found {1}")] Span), #[error("The '&&' operator is not supported in Nushell")]
83 #[diagnostic(
84 code(nu::parser::shell_andand),
85 help("use ';' instead of the shell '&&', or 'and' instead of the boolean '&&'")
86 )]
87 ShellAndAnd(#[label("instead of '&&', use ';' or 'and'")] Span),
88
89 #[error("The '||' operator is not supported in Nushell")]
90 #[diagnostic(
91 code(nu::parser::shell_oror),
92 help("use 'try' instead of the shell '||', or 'or' instead of the boolean '||'")
93 )]
94 ShellOrOr(#[label("instead of '||', use 'try' or 'or'")] Span),
95
96 #[error("The '2>' shell operation is 'err>' in Nushell.")]
97 #[diagnostic(code(nu::parser::shell_err))]
98 ShellErrRedirect(#[label("use 'err>' instead of '2>' in Nushell")] Span),
99
100 #[error("The '2>&1' shell operation is 'out+err>' in Nushell.")]
101 #[diagnostic(
102 code(nu::parser::shell_outerr),
103 help("Nushell redirection will write all of stdout before stderr.")
104 )]
105 ShellOutErrRedirect(#[label("use 'out+err>' instead of '2>&1' in Nushell")] Span),
106
107 #[error("Multiple redirections provided for {0}.")]
108 #[diagnostic(code(nu::parser::multiple_redirections))]
109 MultipleRedirections(
110 RedirectionSource,
111 #[label = "first redirection"] Span,
112 #[label = "second redirection"] Span,
113 ),
114
115 #[error("Unexpected redirection.")]
116 #[diagnostic(code(nu::parser::unexpected_redirection))]
117 UnexpectedRedirection {
118 #[label = "redirecting nothing"]
119 span: Span,
120 },
121
122 #[error("The '{op}' operator does not work on values of type '{unsupported}'.")]
124 #[diagnostic(code(nu::parser::operator_unsupported_type))]
125 OperatorUnsupportedType {
126 op: &'static str,
127 unsupported: Type,
128 #[label = "does not support '{unsupported}'"]
129 op_span: Span,
130 #[label("{unsupported}")]
131 unsupported_span: Span,
132 #[help]
133 help: Option<&'static str>,
134 },
135
136 #[error("Types '{lhs}' and '{rhs}' are not compatible for the '{op}' operator.")]
138 #[diagnostic(code(nu::parser::operator_incompatible_types))]
139 OperatorIncompatibleTypes {
140 op: &'static str,
141 lhs: Type,
142 rhs: Type,
143 #[label = "does not operate between '{lhs}' and '{rhs}'"]
144 op_span: Span,
145 #[label("{lhs}")]
146 lhs_span: Span,
147 #[label("{rhs}")]
148 rhs_span: Span,
149 #[help]
150 help: Option<&'static str>,
151 },
152
153 #[error("Capture of mutable variable.")]
154 #[diagnostic(code(nu::parser::expected_keyword))]
155 CaptureOfMutableVar(#[label("capture of mutable variable")] Span),
156
157 #[error("Expected keyword.")]
158 #[diagnostic(code(nu::parser::expected_keyword))]
159 ExpectedKeyword(String, #[label("expected {0}")] Span),
160
161 #[error("Unexpected keyword.")]
162 #[diagnostic(
163 code(nu::parser::unexpected_keyword),
164 help("'{0}' keyword is allowed only in a module.")
165 )]
166 UnexpectedKeyword(String, #[label("unexpected {0}")] Span),
167
168 #[error("Can't create alias to parser keyword.")]
169 #[diagnostic(
170 code(nu::parser::cant_alias_keyword),
171 help("Only the following keywords can be aliased: {0}.")
172 )]
173 CantAliasKeyword(String, #[label("not supported in alias")] Span),
174
175 #[error("Can't create alias to expression.")]
176 #[diagnostic(
177 code(nu::parser::cant_alias_expression),
178 help("Only command calls can be aliased.")
179 )]
180 CantAliasExpression(String, #[label("aliasing {0} is not supported")] Span),
181
182 #[error("Unknown operator")]
183 #[diagnostic(code(nu::parser::unknown_operator), help("{1}"))]
184 UnknownOperator(
185 &'static str,
186 &'static str,
187 #[label("Operator '{0}' not supported")] Span,
188 ),
189
190 #[error("Statement used in pipeline.")]
191 #[diagnostic(
192 code(nu::parser::unexpected_keyword),
193 help(
194 "'{0}' keyword is not allowed in pipeline. Use '{0}' by itself, outside of a pipeline."
195 )
196 )]
197 BuiltinCommandInPipeline(String, #[label("not allowed in pipeline")] Span),
198
199 #[error("{0} statement used in pipeline.")]
200 #[diagnostic(
201 code(nu::parser::unexpected_keyword),
202 help(
203 "Assigning '{1}' to '{2}' does not produce a value to be piped. If the pipeline result is meant to be assigned to '{2}', use '{0} {2} = ({1} | ...)'."
204 )
205 )]
206 AssignInPipeline(String, String, String, #[label("'{0}' in pipeline")] Span),
207
208 #[error("`{0}` used as variable name.")]
209 #[diagnostic(
210 code(nu::parser::name_is_builtin_var),
211 help(
212 "'{0}' is the name of a builtin Nushell variable and cannot be used as a variable name"
213 )
214 )]
215 NameIsBuiltinVar(String, #[label("already a builtin variable")] Span),
216
217 #[error("Incorrect value")]
218 #[diagnostic(code(nu::parser::incorrect_value), help("{2}"))]
219 IncorrectValue(String, #[label("unexpected {0}")] Span, String),
220
221 #[error("Invalid binary string.")]
222 #[diagnostic(code(nu::parser::invalid_binary_string), help("{1}"))]
223 InvalidBinaryString(#[label("invalid binary string")] Span, String),
224
225 #[error("Multiple rest params.")]
226 #[diagnostic(code(nu::parser::multiple_rest_params))]
227 MultipleRestParams(#[label = "multiple rest params"] Span),
228
229 #[error("Variable not found.")]
230 #[diagnostic(code(nu::parser::variable_not_found))]
231 VariableNotFound(DidYouMean, #[label = "variable not found. {0}"] Span),
232
233 #[error("Use $env.{0} instead of ${0}.")]
234 #[diagnostic(code(nu::parser::env_var_not_var))]
235 EnvVarNotVar(String, #[label = "use $env.{0} instead of ${0}"] Span),
236
237 #[error("Variable name not supported.")]
238 #[diagnostic(code(nu::parser::variable_not_valid))]
239 VariableNotValid(#[label = "variable name can't contain spaces or quotes"] Span),
240
241 #[error("Alias name not supported.")]
242 #[diagnostic(code(nu::parser::variable_not_valid))]
243 AliasNotValid(
244 #[label = "alias name can't be a number, a filesize, or contain #, ^, or %"] Span,
245 ),
246
247 #[error("Command name not supported.")]
248 #[diagnostic(code(nu::parser::variable_not_valid))]
249 CommandDefNotValid(
250 #[label = "command name can't be a number, a filesize, or contain #, ^, or %"] Span,
251 ),
252
253 #[error("Module not found.")]
254 #[diagnostic(
255 code(nu::parser::module_not_found),
256 help(
257 "module files and their paths must be available before your script is run as parsing occurs before anything is evaluated"
258 )
259 )]
260 ModuleNotFound(#[label = "module {1} not found"] Span, String),
261
262 #[error("Missing mod.nu file.")]
263 #[diagnostic(
264 code(nu::parser::module_missing_mod_nu_file),
265 help(
266 "Directory {0} is missing a mod.nu file.\n\nWhen importing a directory as a Nushell module, it needs to contain a mod.nu file (can be empty). Alternatively, you can use .nu files in the directory as modules individually."
267 )
268 )]
269 ModuleMissingModNuFile(
270 String,
271 #[label = "module directory is missing a mod.nu file"] Span,
272 ),
273
274 #[error("Circular import.")]
275 #[diagnostic(code(nu::parser::circular_import), help("{0}"))]
276 CircularImport(String, #[label = "detected circular import"] Span),
277
278 #[error("Can't export {0} named same as the module.")]
279 #[diagnostic(
280 code(nu::parser::named_as_module),
281 help(
282 "Module {1} can't export {0} named the same as the module. Either change the module name, or export `{2}` {0}."
283 )
284 )]
285 NamedAsModule(
286 String,
287 String,
288 String,
289 #[label = "can't export from module {1}"] Span,
290 ),
291
292 #[error("Module already contains 'main' command.")]
293 #[diagnostic(
294 code(nu::parser::module_double_main),
295 help("Tried to add 'main' command to module '{0}' but it has already been added.")
296 )]
297 ModuleDoubleMain(
298 String,
299 #[label = "module '{0}' already contains 'main'"] Span,
300 ),
301
302 #[error("Can't export alias defined as 'main'.")]
303 #[diagnostic(
304 code(nu::parser::export_main_alias_not_allowed),
305 help(
306 "Exporting aliases as 'main' is not allowed. Either rename the alias or convert it to a custom command."
307 )
308 )]
309 ExportMainAliasNotAllowed(#[label = "can't export from module"] Span),
310
311 #[error("Active overlay not found.")]
312 #[diagnostic(code(nu::parser::active_overlay_not_found))]
313 ActiveOverlayNotFound(#[label = "not an active overlay"] Span),
314
315 #[error("Overlay prefix mismatch.")]
316 #[diagnostic(
317 code(nu::parser::overlay_prefix_mismatch),
318 help(
319 "Overlay {0} already exists {1} a prefix. To add it again, do it {1} the --prefix flag."
320 )
321 )]
322 OverlayPrefixMismatch(
323 String,
324 String,
325 #[label = "already exists {1} a prefix"] Span,
326 ),
327
328 #[error("Module or overlay not found.")]
329 #[diagnostic(
330 code(nu::parser::module_or_overlay_not_found),
331 help(
332 "Requires either an existing overlay, a module, or an import pattern defining a module."
333 )
334 )]
335 ModuleOrOverlayNotFound(#[label = "not a module or an overlay"] Span),
336
337 #[error("Cannot remove the last overlay.")]
338 #[diagnostic(
339 code(nu::parser::cant_remove_last_overlay),
340 help("At least one overlay must always be active.")
341 )]
342 CantRemoveLastOverlay(#[label = "this is the last overlay, can't remove it"] Span),
343
344 #[error("Cannot hide default overlay.")]
345 #[diagnostic(
346 code(nu::parser::cant_hide_default_overlay),
347 help("'{0}' is a default overlay. Default overlays cannot be hidden.")
348 )]
349 CantHideDefaultOverlay(String, #[label = "can't hide overlay"] Span),
350
351 #[error("Cannot add overlay.")]
352 #[diagnostic(code(nu::parser::cant_add_overlay_help), help("{0}"))]
353 CantAddOverlayHelp(String, #[label = "cannot add this overlay"] Span),
354
355 #[error("Duplicate command definition within a block.")]
356 #[diagnostic(code(nu::parser::duplicate_command_def))]
357 DuplicateCommandDef(#[label = "defined more than once"] Span),
358
359 #[error("Unknown command.")]
360 #[diagnostic(
361 code(nu::parser::unknown_command),
362 )]
364 UnknownCommand(#[label = "unknown command"] Span),
365
366 #[error("Non-UTF8 string.")]
367 #[diagnostic(code(nu::parser::non_utf8))]
368 NonUtf8(#[label = "non-UTF8 string"] Span),
369
370 #[error("The `{0}` command doesn't have flag `{1}`.")]
371 #[diagnostic(code(nu::parser::unknown_flag), help("{3}"))]
372 UnknownFlag(String, String, #[label = "unknown flag"] Span, String),
373
374 #[error("Unknown type.")]
375 #[diagnostic(code(nu::parser::unknown_type))]
376 UnknownType(#[label = "unknown type"] Span),
377
378 #[error("Missing flag argument.")]
379 #[diagnostic(code(nu::parser::missing_flag_param))]
380 MissingFlagParam(String, #[label = "flag missing {0} argument"] Span),
381
382 #[error("Only the last flag in a short flag batch can take an argument.")]
383 #[diagnostic(code(nu::parser::only_last_flag_in_batch_can_take_arg))]
384 OnlyLastFlagInBatchCanTakeArg(#[label = "only the last flag can take args"] Span),
385
386 #[error("Missing required positional argument.")]
387 #[diagnostic(
388 code(nu::parser::missing_positional),
389 help("Usage: {2}. Use `--help` for more information.")
390 )]
391 MissingPositional(String, #[label("missing {0}")] Span, String),
392
393 #[error("Missing argument to `{1}`.")]
394 #[diagnostic(code(nu::parser::keyword_missing_arg))]
395 KeywordMissingArgument(
396 String,
397 String,
398 #[label("missing {0} value that follows {1}")] Span,
399 ),
400
401 #[error("Missing type.")]
402 #[diagnostic(code(nu::parser::missing_type))]
403 MissingType(#[label = "expected type"] Span),
404
405 #[error("Type mismatch.")]
406 #[diagnostic(code(nu::parser::type_mismatch))]
407 TypeMismatch(Type, Type, #[label("expected {0}, found {1}")] Span), #[error("Type mismatch.")]
410 #[diagnostic(code(nu::parser::type_mismatch_help), help("{3}"))]
411 TypeMismatchHelp(Type, Type, #[label("expected {0}, found {1}")] Span, String), #[error("Missing required flag.")]
414 #[diagnostic(code(nu::parser::missing_required_flag))]
415 MissingRequiredFlag(String, #[label("missing required flag {0}")] Span),
416
417 #[error("Incomplete math expression.")]
418 #[diagnostic(code(nu::parser::incomplete_math_expression))]
419 IncompleteMathExpression(#[label = "incomplete math expression"] Span),
420
421 #[error("Unknown state.")]
422 #[diagnostic(code(nu::parser::unknown_state))]
423 UnknownState(String, #[label("{0}")] Span),
424
425 #[error("Internal error.")]
426 #[diagnostic(code(nu::parser::unknown_state))]
427 InternalError(String, #[label("{0}")] Span),
428
429 #[error("Parser incomplete.")]
430 #[diagnostic(code(nu::parser::parser_incomplete))]
431 IncompleteParser(#[label = "parser support missing for this expression"] Span),
432
433 #[error("Rest parameter needs a name.")]
434 #[diagnostic(code(nu::parser::rest_needs_name))]
435 RestNeedsName(#[label = "needs a parameter name"] Span),
436
437 #[error("Parameter not correct type.")]
438 #[diagnostic(code(nu::parser::parameter_mismatch_type))]
439 ParameterMismatchType(
440 String,
441 String,
442 String,
443 #[label = "parameter {0} needs to be '{1}' instead of '{2}'"] Span,
444 ),
445
446 #[error("Default values should be constant expressions.")]
447 #[diagnostic(code(nu::parser::non_constant_default_value))]
448 NonConstantDefaultValue(#[label = "expected a constant value"] Span),
449
450 #[error("Extra columns.")]
451 #[diagnostic(code(nu::parser::extra_columns))]
452 ExtraColumns(
453 usize,
454 #[label("expected {0} column{}", if *.0 == 1 { "" } else { "s" })] Span,
455 ),
456
457 #[error("Missing columns.")]
458 #[diagnostic(code(nu::parser::missing_columns))]
459 MissingColumns(
460 usize,
461 #[label("expected {0} column{}", if *.0 == 1 { "" } else { "s" })] Span,
462 ),
463
464 #[error("{0}")]
465 #[diagnostic(code(nu::parser::assignment_mismatch))]
466 AssignmentMismatch(String, String, #[label("{1}")] Span),
467
468 #[error("Wrong import pattern structure.")]
469 #[diagnostic(code(nu::parser::wrong_import_pattern))]
470 WrongImportPattern(String, #[label = "{0}"] Span),
471
472 #[error("Export not found.")]
473 #[diagnostic(code(nu::parser::export_not_found))]
474 ExportNotFound(#[label = "could not find imports"] Span),
475
476 #[error("File not found")]
477 #[diagnostic(
478 code(nu::parser::sourced_file_not_found),
479 help("sourced files need to be available before your script is run")
480 )]
481 SourcedFileNotFound(String, #[label("File not found: {0}")] Span),
482
483 #[error("File not found")]
484 #[diagnostic(
485 code(nu::parser::registered_file_not_found),
486 help("registered files need to be available before your script is run")
487 )]
488 RegisteredFileNotFound(String, #[label("File not found: {0}")] Span),
489
490 #[error("File not found")]
491 #[diagnostic(code(nu::parser::file_not_found))]
492 FileNotFound(String, #[label("File not found: {0}")] Span),
493
494 #[error("Plugin not found")]
495 #[diagnostic(
496 code(nu::parser::plugin_not_found),
497 help(
498 "plugins need to be added to the plugin registry file before your script is run (see `plugin add`)"
499 )
500 )]
501 PluginNotFound {
502 name: String,
503 #[label("Plugin not found: {name}")]
504 name_span: Span,
505 #[label("in this registry file")]
506 plugin_config_span: Option<Span>,
507 },
508
509 #[error("Invalid literal")] #[diagnostic()]
511 InvalidLiteral(String, String, #[label("{0} in {1}")] Span),
512
513 #[error("{0}")]
514 #[diagnostic()]
515 LabeledError(String, String, #[label("{1}")] Span),
516
517 #[error("{error}")]
518 #[diagnostic(help("{help}"))]
519 LabeledErrorWithHelp {
520 error: String,
521 label: String,
522 help: String,
523 #[label("{label}")]
524 span: Span,
525 },
526
527 #[error("Redirection can not be used with {0}.")]
528 #[diagnostic()]
529 RedirectingBuiltinCommand(
530 &'static str,
531 #[label("not allowed here")] Span,
532 #[label("...and here")] Option<Span>,
533 ),
534
535 #[error("This command does not have a ...rest parameter")]
536 #[diagnostic(
537 code(nu::parser::unexpected_spread_arg),
538 help(
539 "To spread arguments, the command needs to define a multi-positional parameter in its signature, such as ...rest"
540 )
541 )]
542 UnexpectedSpreadArg(String, #[label = "unexpected spread argument"] Span),
543
544 #[error("Assignment to an immutable variable.")]
550 #[diagnostic(
551 code(nu::parser::assignment_requires_mutable_variable),
552 help("declare the variable with `mut`, or shadow it again with `let`")
553 )]
554 AssignmentRequiresMutableVar(#[label("needs to be a mutable variable")] Span),
555
556 #[error("Assignment operations require a variable.")]
562 #[diagnostic(
563 code(nu::parser::assignment_requires_variable),
564 help("try assigning to a variable or a cell path of a variable")
565 )]
566 AssignmentRequiresVar(#[label("needs to be a variable")] Span),
567
568 #[error("Attributes must be followed by a definition.")]
569 #[diagnostic(
570 code(nu::parser::attribute_requires_definition),
571 help("try following this line with a `def` or `extern` definition")
572 )]
573 AttributeRequiresDefinition(#[label("must be followed by a definition")] Span),
574}
575
576impl ParseError {
577 pub fn span(&self) -> Span {
578 match self {
579 ParseError::ExtraTokens(s) => *s,
580 ParseError::ExtraPositional(_, s) => *s,
581 ParseError::UnexpectedEof(_, s) => *s,
582 ParseError::Unclosed(_, s) => *s,
583 ParseError::Unbalanced(_, _, s) => *s,
584 ParseError::Expected(_, s) => *s,
585 ParseError::ExpectedWithStringMsg(_, s) => *s,
586 ParseError::ExpectedWithDidYouMean(_, _, s) => *s,
587 ParseError::Mismatch(_, _, s) => *s,
588 ParseError::OperatorUnsupportedType { op_span, .. } => *op_span,
589 ParseError::OperatorIncompatibleTypes { op_span, .. } => *op_span,
590 ParseError::ExpectedKeyword(_, s) => *s,
591 ParseError::UnexpectedKeyword(_, s) => *s,
592 ParseError::CantAliasKeyword(_, s) => *s,
593 ParseError::CantAliasExpression(_, s) => *s,
594 ParseError::BuiltinCommandInPipeline(_, s) => *s,
595 ParseError::AssignInPipeline(_, _, _, s) => *s,
596 ParseError::NameIsBuiltinVar(_, s) => *s,
597 ParseError::CaptureOfMutableVar(s) => *s,
598 ParseError::IncorrectValue(_, s, _) => *s,
599 ParseError::InvalidBinaryString(s, _) => *s,
600 ParseError::MultipleRestParams(s) => *s,
601 ParseError::VariableNotFound(_, s) => *s,
602 ParseError::EnvVarNotVar(_, s) => *s,
603 ParseError::VariableNotValid(s) => *s,
604 ParseError::AliasNotValid(s) => *s,
605 ParseError::CommandDefNotValid(s) => *s,
606 ParseError::ModuleNotFound(s, _) => *s,
607 ParseError::ModuleMissingModNuFile(_, s) => *s,
608 ParseError::NamedAsModule(_, _, _, s) => *s,
609 ParseError::ModuleDoubleMain(_, s) => *s,
610 ParseError::ExportMainAliasNotAllowed(s) => *s,
611 ParseError::CircularImport(_, s) => *s,
612 ParseError::ModuleOrOverlayNotFound(s) => *s,
613 ParseError::ActiveOverlayNotFound(s) => *s,
614 ParseError::OverlayPrefixMismatch(_, _, s) => *s,
615 ParseError::CantRemoveLastOverlay(s) => *s,
616 ParseError::CantHideDefaultOverlay(_, s) => *s,
617 ParseError::CantAddOverlayHelp(_, s) => *s,
618 ParseError::DuplicateCommandDef(s) => *s,
619 ParseError::UnknownCommand(s) => *s,
620 ParseError::NonUtf8(s) => *s,
621 ParseError::UnknownFlag(_, _, s, _) => *s,
622 ParseError::RequiredAfterOptional(_, s) => *s,
623 ParseError::UnknownType(s) => *s,
624 ParseError::MissingFlagParam(_, s) => *s,
625 ParseError::OnlyLastFlagInBatchCanTakeArg(s) => *s,
626 ParseError::MissingPositional(_, s, _) => *s,
627 ParseError::KeywordMissingArgument(_, _, s) => *s,
628 ParseError::MissingType(s) => *s,
629 ParseError::TypeMismatch(_, _, s) => *s,
630 ParseError::TypeMismatchHelp(_, _, s, _) => *s,
631 ParseError::InputMismatch(_, s) => *s,
632 ParseError::OutputMismatch(_, _, s) => *s,
633 ParseError::MissingRequiredFlag(_, s) => *s,
634 ParseError::IncompleteMathExpression(s) => *s,
635 ParseError::UnknownState(_, s) => *s,
636 ParseError::InternalError(_, s) => *s,
637 ParseError::IncompleteParser(s) => *s,
638 ParseError::RestNeedsName(s) => *s,
639 ParseError::ParameterMismatchType(_, _, _, s) => *s,
640 ParseError::NonConstantDefaultValue(s) => *s,
641 ParseError::ExtraColumns(_, s) => *s,
642 ParseError::MissingColumns(_, s) => *s,
643 ParseError::AssignmentMismatch(_, _, s) => *s,
644 ParseError::WrongImportPattern(_, s) => *s,
645 ParseError::ExportNotFound(s) => *s,
646 ParseError::SourcedFileNotFound(_, s) => *s,
647 ParseError::RegisteredFileNotFound(_, s) => *s,
648 ParseError::FileNotFound(_, s) => *s,
649 ParseError::PluginNotFound { name_span, .. } => *name_span,
650 ParseError::LabeledError(_, _, s) => *s,
651 ParseError::ShellAndAnd(s) => *s,
652 ParseError::ShellOrOr(s) => *s,
653 ParseError::ShellErrRedirect(s) => *s,
654 ParseError::ShellOutErrRedirect(s) => *s,
655 ParseError::MultipleRedirections(_, _, s) => *s,
656 ParseError::UnexpectedRedirection { span } => *span,
657 ParseError::UnknownOperator(_, _, s) => *s,
658 ParseError::InvalidLiteral(_, _, s) => *s,
659 ParseError::LabeledErrorWithHelp { span: s, .. } => *s,
660 ParseError::RedirectingBuiltinCommand(_, s, _) => *s,
661 ParseError::UnexpectedSpreadArg(_, s) => *s,
662 ParseError::ExtraTokensAfterClosingDelimiter(s) => *s,
663 ParseError::AssignmentRequiresVar(s) => *s,
664 ParseError::AssignmentRequiresMutableVar(s) => *s,
665 ParseError::AttributeRequiresDefinition(s) => *s,
666 }
667 }
668}
669
670#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
671pub struct DidYouMean(Option<String>);
672
673fn did_you_mean_impl(possibilities_bytes: &[&[u8]], input_bytes: &[u8]) -> Option<String> {
674 let input = from_utf8(input_bytes).ok()?;
675 let possibilities = possibilities_bytes
676 .iter()
677 .map(|p| from_utf8(p))
678 .collect::<Result<Vec<&str>, Utf8Error>>()
679 .ok()?;
680 did_you_mean(&possibilities, input)
681}
682impl DidYouMean {
683 pub fn new(possibilities_bytes: &[&[u8]], input_bytes: &[u8]) -> DidYouMean {
684 DidYouMean(did_you_mean_impl(possibilities_bytes, input_bytes))
685 }
686}
687
688impl From<Option<String>> for DidYouMean {
689 fn from(value: Option<String>) -> Self {
690 Self(value)
691 }
692}
693
694impl Display for DidYouMean {
695 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
696 if let Some(suggestion) = &self.0 {
697 write!(f, "Did you mean '{suggestion}'?")
698 } else {
699 write!(f, "")
700 }
701 }
702}