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