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(String, #[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 String,
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("Invalid binary string.")]
217 #[diagnostic(code(nu::parser::invalid_binary_string), help("{1}"))]
218 InvalidBinaryString(#[label("invalid binary string")] Span, String),
219
220 #[error("Multiple rest params.")]
221 #[diagnostic(code(nu::parser::multiple_rest_params))]
222 MultipleRestParams(#[label = "multiple rest params"] Span),
223
224 #[error("Variable not found.")]
225 #[diagnostic(code(nu::parser::variable_not_found))]
226 VariableNotFound(DidYouMean, #[label = "variable not found. {0}"] Span),
227
228 #[error("Use $env.{0} instead of ${0}.")]
229 #[diagnostic(code(nu::parser::env_var_not_var))]
230 EnvVarNotVar(String, #[label = "use $env.{0} instead of ${0}"] Span),
231
232 #[error("Variable name not supported.")]
233 #[diagnostic(code(nu::parser::variable_not_valid))]
234 VariableNotValid(#[label = "variable name can't contain spaces or quotes"] Span),
235
236 #[error("Alias name not supported.")]
237 #[diagnostic(code(nu::parser::variable_not_valid))]
238 AliasNotValid(
239 #[label = "alias name can't be a number, a filesize, or contain a hash # or caret ^"] Span,
240 ),
241
242 #[error("Command name not supported.")]
243 #[diagnostic(code(nu::parser::variable_not_valid))]
244 CommandDefNotValid(
245 #[label = "command name can't be a number, a filesize, or contain a hash # or caret ^"]
246 Span,
247 ),
248
249 #[error("Module not found.")]
250 #[diagnostic(
251 code(nu::parser::module_not_found),
252 help(
253 "module files and their paths must be available before your script is run as parsing occurs before anything is evaluated"
254 )
255 )]
256 ModuleNotFound(#[label = "module {1} not found"] Span, String),
257
258 #[error("Missing mod.nu file.")]
259 #[diagnostic(
260 code(nu::parser::module_missing_mod_nu_file),
261 help(
262 "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."
263 )
264 )]
265 ModuleMissingModNuFile(
266 String,
267 #[label = "module directory is missing a mod.nu file"] Span,
268 ),
269
270 #[error("Circular import.")]
271 #[diagnostic(code(nu::parser::circular_import), help("{0}"))]
272 CircularImport(String, #[label = "detected circular import"] Span),
273
274 #[error("Can't export {0} named same as the module.")]
275 #[diagnostic(
276 code(nu::parser::named_as_module),
277 help(
278 "Module {1} can't export {0} named the same as the module. Either change the module name, or export `{2}` {0}."
279 )
280 )]
281 NamedAsModule(
282 String,
283 String,
284 String,
285 #[label = "can't export from module {1}"] Span,
286 ),
287
288 #[error("Module already contains 'main' command.")]
289 #[diagnostic(
290 code(nu::parser::module_double_main),
291 help("Tried to add 'main' command to module '{0}' but it has already been added.")
292 )]
293 ModuleDoubleMain(
294 String,
295 #[label = "module '{0}' already contains 'main'"] Span,
296 ),
297
298 #[error("Can't export alias defined as 'main'.")]
299 #[diagnostic(
300 code(nu::parser::export_main_alias_not_allowed),
301 help(
302 "Exporting aliases as 'main' is not allowed. Either rename the alias or convert it to a custom command."
303 )
304 )]
305 ExportMainAliasNotAllowed(#[label = "can't export from module"] Span),
306
307 #[error("Active overlay not found.")]
308 #[diagnostic(code(nu::parser::active_overlay_not_found))]
309 ActiveOverlayNotFound(#[label = "not an active overlay"] Span),
310
311 #[error("Overlay prefix mismatch.")]
312 #[diagnostic(
313 code(nu::parser::overlay_prefix_mismatch),
314 help(
315 "Overlay {0} already exists {1} a prefix. To add it again, do it {1} the --prefix flag."
316 )
317 )]
318 OverlayPrefixMismatch(
319 String,
320 String,
321 #[label = "already exists {1} a prefix"] Span,
322 ),
323
324 #[error("Module or overlay not found.")]
325 #[diagnostic(
326 code(nu::parser::module_or_overlay_not_found),
327 help(
328 "Requires either an existing overlay, a module, or an import pattern defining a module."
329 )
330 )]
331 ModuleOrOverlayNotFound(#[label = "not a module or an overlay"] Span),
332
333 #[error("Cannot remove the last overlay.")]
334 #[diagnostic(
335 code(nu::parser::cant_remove_last_overlay),
336 help("At least one overlay must always be active.")
337 )]
338 CantRemoveLastOverlay(#[label = "this is the last overlay, can't remove it"] Span),
339
340 #[error("Cannot hide default overlay.")]
341 #[diagnostic(
342 code(nu::parser::cant_hide_default_overlay),
343 help("'{0}' is a default overlay. Default overlays cannot be hidden.")
344 )]
345 CantHideDefaultOverlay(String, #[label = "can't hide overlay"] Span),
346
347 #[error("Cannot add overlay.")]
348 #[diagnostic(code(nu::parser::cant_add_overlay_help), help("{0}"))]
349 CantAddOverlayHelp(String, #[label = "cannot add this overlay"] Span),
350
351 #[error("Duplicate command definition within a block.")]
352 #[diagnostic(code(nu::parser::duplicate_command_def))]
353 DuplicateCommandDef(#[label = "defined more than once"] Span),
354
355 #[error("Unknown command.")]
356 #[diagnostic(
357 code(nu::parser::unknown_command),
358 )]
360 UnknownCommand(#[label = "unknown command"] Span),
361
362 #[error("Non-UTF8 string.")]
363 #[diagnostic(code(nu::parser::non_utf8))]
364 NonUtf8(#[label = "non-UTF8 string"] Span),
365
366 #[error("The `{0}` command doesn't have flag `{1}`.")]
367 #[diagnostic(code(nu::parser::unknown_flag), help("{3}"))]
368 UnknownFlag(String, String, #[label = "unknown flag"] Span, String),
369
370 #[error("Unknown type.")]
371 #[diagnostic(code(nu::parser::unknown_type))]
372 UnknownType(#[label = "unknown type"] Span),
373
374 #[error("Missing flag argument.")]
375 #[diagnostic(code(nu::parser::missing_flag_param))]
376 MissingFlagParam(String, #[label = "flag missing {0} argument"] Span),
377
378 #[error("Only the last flag in a short flag batch can take an argument.")]
379 #[diagnostic(code(nu::parser::only_last_flag_in_batch_can_take_arg))]
380 OnlyLastFlagInBatchCanTakeArg(#[label = "only the last flag can take args"] Span),
381
382 #[error("Missing required positional argument.")]
383 #[diagnostic(
384 code(nu::parser::missing_positional),
385 help("Usage: {2}. Use `--help` for more information.")
386 )]
387 MissingPositional(String, #[label("missing {0}")] Span, String),
388
389 #[error("Missing argument to `{1}`.")]
390 #[diagnostic(code(nu::parser::keyword_missing_arg))]
391 KeywordMissingArgument(
392 String,
393 String,
394 #[label("missing {0} value that follows {1}")] Span,
395 ),
396
397 #[error("Missing type.")]
398 #[diagnostic(code(nu::parser::missing_type))]
399 MissingType(#[label = "expected type"] Span),
400
401 #[error("Type mismatch.")]
402 #[diagnostic(code(nu::parser::type_mismatch))]
403 TypeMismatch(Type, Type, #[label("expected {0}, found {1}")] Span), #[error("Type mismatch.")]
406 #[diagnostic(code(nu::parser::type_mismatch_help), help("{3}"))]
407 TypeMismatchHelp(Type, Type, #[label("expected {0}, found {1}")] Span, String), #[error("Missing required flag.")]
410 #[diagnostic(code(nu::parser::missing_required_flag))]
411 MissingRequiredFlag(String, #[label("missing required flag {0}")] Span),
412
413 #[error("Incomplete math expression.")]
414 #[diagnostic(code(nu::parser::incomplete_math_expression))]
415 IncompleteMathExpression(#[label = "incomplete math expression"] Span),
416
417 #[error("Unknown state.")]
418 #[diagnostic(code(nu::parser::unknown_state))]
419 UnknownState(String, #[label("{0}")] Span),
420
421 #[error("Internal error.")]
422 #[diagnostic(code(nu::parser::unknown_state))]
423 InternalError(String, #[label("{0}")] Span),
424
425 #[error("Parser incomplete.")]
426 #[diagnostic(code(nu::parser::parser_incomplete))]
427 IncompleteParser(#[label = "parser support missing for this expression"] Span),
428
429 #[error("Rest parameter needs a name.")]
430 #[diagnostic(code(nu::parser::rest_needs_name))]
431 RestNeedsName(#[label = "needs a parameter name"] Span),
432
433 #[error("Parameter not correct type.")]
434 #[diagnostic(code(nu::parser::parameter_mismatch_type))]
435 ParameterMismatchType(
436 String,
437 String,
438 String,
439 #[label = "parameter {0} needs to be '{1}' instead of '{2}'"] Span,
440 ),
441
442 #[error("Default values should be constant expressions.")]
443 #[diagnostic(code(nu::parser::non_constant_default_value))]
444 NonConstantDefaultValue(#[label = "expected a constant value"] Span),
445
446 #[error("Extra columns.")]
447 #[diagnostic(code(nu::parser::extra_columns))]
448 ExtraColumns(
449 usize,
450 #[label("expected {0} column{}", if *.0 == 1 { "" } else { "s" })] Span,
451 ),
452
453 #[error("Missing columns.")]
454 #[diagnostic(code(nu::parser::missing_columns))]
455 MissingColumns(
456 usize,
457 #[label("expected {0} column{}", if *.0 == 1 { "" } else { "s" })] Span,
458 ),
459
460 #[error("{0}")]
461 #[diagnostic(code(nu::parser::assignment_mismatch))]
462 AssignmentMismatch(String, String, #[label("{1}")] Span),
463
464 #[error("Wrong import pattern structure.")]
465 #[diagnostic(code(nu::parser::wrong_import_pattern))]
466 WrongImportPattern(String, #[label = "{0}"] Span),
467
468 #[error("Export not found.")]
469 #[diagnostic(code(nu::parser::export_not_found))]
470 ExportNotFound(#[label = "could not find imports"] Span),
471
472 #[error("File not found")]
473 #[diagnostic(
474 code(nu::parser::sourced_file_not_found),
475 help("sourced files need to be available before your script is run")
476 )]
477 SourcedFileNotFound(String, #[label("File not found: {0}")] Span),
478
479 #[error("File not found")]
480 #[diagnostic(
481 code(nu::parser::registered_file_not_found),
482 help("registered files need to be available before your script is run")
483 )]
484 RegisteredFileNotFound(String, #[label("File not found: {0}")] Span),
485
486 #[error("File not found")]
487 #[diagnostic(code(nu::parser::file_not_found))]
488 FileNotFound(String, #[label("File not found: {0}")] Span),
489
490 #[error("Plugin not found")]
491 #[diagnostic(
492 code(nu::parser::plugin_not_found),
493 help(
494 "plugins need to be added to the plugin registry file before your script is run (see `plugin add`)"
495 )
496 )]
497 PluginNotFound {
498 name: String,
499 #[label("Plugin not found: {name}")]
500 name_span: Span,
501 #[label("in this registry file")]
502 plugin_config_span: Option<Span>,
503 },
504
505 #[error("Invalid literal")] #[diagnostic()]
507 InvalidLiteral(String, String, #[label("{0} in {1}")] Span),
508
509 #[error("{0}")]
510 #[diagnostic()]
511 LabeledError(String, String, #[label("{1}")] Span),
512
513 #[error("{error}")]
514 #[diagnostic(help("{help}"))]
515 LabeledErrorWithHelp {
516 error: String,
517 label: String,
518 help: String,
519 #[label("{label}")]
520 span: Span,
521 },
522
523 #[error("Redirection can not be used with {0}.")]
524 #[diagnostic()]
525 RedirectingBuiltinCommand(
526 &'static str,
527 #[label("not allowed here")] Span,
528 #[label("...and here")] Option<Span>,
529 ),
530
531 #[error("This command does not have a ...rest parameter")]
532 #[diagnostic(
533 code(nu::parser::unexpected_spread_arg),
534 help(
535 "To spread arguments, the command needs to define a multi-positional parameter in its signature, such as ...rest"
536 )
537 )]
538 UnexpectedSpreadArg(String, #[label = "unexpected spread argument"] Span),
539
540 #[error("Assignment to an immutable variable.")]
546 #[diagnostic(
547 code(nu::parser::assignment_requires_mutable_variable),
548 help("declare the variable with `mut`, or shadow it again with `let`")
549 )]
550 AssignmentRequiresMutableVar(#[label("needs to be a mutable variable")] Span),
551
552 #[error("Assignment operations require a variable.")]
558 #[diagnostic(
559 code(nu::parser::assignment_requires_variable),
560 help("try assigning to a variable or a cell path of a variable")
561 )]
562 AssignmentRequiresVar(#[label("needs to be a variable")] Span),
563
564 #[error("Attributes must be followed by a definition.")]
565 #[diagnostic(
566 code(nu::parser::attribute_requires_definition),
567 help("try following this line with a `def` or `extern` definition")
568 )]
569 AttributeRequiresDefinition(#[label("must be followed by a definition")] Span),
570}
571
572impl ParseError {
573 pub fn span(&self) -> Span {
574 match self {
575 ParseError::ExtraTokens(s) => *s,
576 ParseError::ExtraPositional(_, s) => *s,
577 ParseError::UnexpectedEof(_, s) => *s,
578 ParseError::Unclosed(_, s) => *s,
579 ParseError::Unbalanced(_, _, s) => *s,
580 ParseError::Expected(_, s) => *s,
581 ParseError::ExpectedWithStringMsg(_, s) => *s,
582 ParseError::ExpectedWithDidYouMean(_, _, s) => *s,
583 ParseError::Mismatch(_, _, s) => *s,
584 ParseError::OperatorUnsupportedType { op_span, .. } => *op_span,
585 ParseError::OperatorIncompatibleTypes { op_span, .. } => *op_span,
586 ParseError::ExpectedKeyword(_, s) => *s,
587 ParseError::UnexpectedKeyword(_, s) => *s,
588 ParseError::CantAliasKeyword(_, s) => *s,
589 ParseError::CantAliasExpression(_, s) => *s,
590 ParseError::BuiltinCommandInPipeline(_, s) => *s,
591 ParseError::AssignInPipeline(_, _, _, s) => *s,
592 ParseError::NameIsBuiltinVar(_, s) => *s,
593 ParseError::CaptureOfMutableVar(s) => *s,
594 ParseError::IncorrectValue(_, s, _) => *s,
595 ParseError::InvalidBinaryString(s, _) => *s,
596 ParseError::MultipleRestParams(s) => *s,
597 ParseError::VariableNotFound(_, s) => *s,
598 ParseError::EnvVarNotVar(_, s) => *s,
599 ParseError::VariableNotValid(s) => *s,
600 ParseError::AliasNotValid(s) => *s,
601 ParseError::CommandDefNotValid(s) => *s,
602 ParseError::ModuleNotFound(s, _) => *s,
603 ParseError::ModuleMissingModNuFile(_, s) => *s,
604 ParseError::NamedAsModule(_, _, _, s) => *s,
605 ParseError::ModuleDoubleMain(_, s) => *s,
606 ParseError::ExportMainAliasNotAllowed(s) => *s,
607 ParseError::CircularImport(_, s) => *s,
608 ParseError::ModuleOrOverlayNotFound(s) => *s,
609 ParseError::ActiveOverlayNotFound(s) => *s,
610 ParseError::OverlayPrefixMismatch(_, _, s) => *s,
611 ParseError::CantRemoveLastOverlay(s) => *s,
612 ParseError::CantHideDefaultOverlay(_, s) => *s,
613 ParseError::CantAddOverlayHelp(_, s) => *s,
614 ParseError::DuplicateCommandDef(s) => *s,
615 ParseError::UnknownCommand(s) => *s,
616 ParseError::NonUtf8(s) => *s,
617 ParseError::UnknownFlag(_, _, s, _) => *s,
618 ParseError::RequiredAfterOptional(_, s) => *s,
619 ParseError::UnknownType(s) => *s,
620 ParseError::MissingFlagParam(_, s) => *s,
621 ParseError::OnlyLastFlagInBatchCanTakeArg(s) => *s,
622 ParseError::MissingPositional(_, s, _) => *s,
623 ParseError::KeywordMissingArgument(_, _, s) => *s,
624 ParseError::MissingType(s) => *s,
625 ParseError::TypeMismatch(_, _, s) => *s,
626 ParseError::TypeMismatchHelp(_, _, s, _) => *s,
627 ParseError::InputMismatch(_, s) => *s,
628 ParseError::OutputMismatch(_, _, s) => *s,
629 ParseError::MissingRequiredFlag(_, s) => *s,
630 ParseError::IncompleteMathExpression(s) => *s,
631 ParseError::UnknownState(_, s) => *s,
632 ParseError::InternalError(_, s) => *s,
633 ParseError::IncompleteParser(s) => *s,
634 ParseError::RestNeedsName(s) => *s,
635 ParseError::ParameterMismatchType(_, _, _, s) => *s,
636 ParseError::NonConstantDefaultValue(s) => *s,
637 ParseError::ExtraColumns(_, s) => *s,
638 ParseError::MissingColumns(_, s) => *s,
639 ParseError::AssignmentMismatch(_, _, s) => *s,
640 ParseError::WrongImportPattern(_, s) => *s,
641 ParseError::ExportNotFound(s) => *s,
642 ParseError::SourcedFileNotFound(_, s) => *s,
643 ParseError::RegisteredFileNotFound(_, s) => *s,
644 ParseError::FileNotFound(_, s) => *s,
645 ParseError::PluginNotFound { name_span, .. } => *name_span,
646 ParseError::LabeledError(_, _, s) => *s,
647 ParseError::ShellAndAnd(s) => *s,
648 ParseError::ShellOrOr(s) => *s,
649 ParseError::ShellErrRedirect(s) => *s,
650 ParseError::ShellOutErrRedirect(s) => *s,
651 ParseError::MultipleRedirections(_, _, s) => *s,
652 ParseError::UnexpectedRedirection { span } => *span,
653 ParseError::UnknownOperator(_, _, s) => *s,
654 ParseError::InvalidLiteral(_, _, s) => *s,
655 ParseError::LabeledErrorWithHelp { span: s, .. } => *s,
656 ParseError::RedirectingBuiltinCommand(_, s, _) => *s,
657 ParseError::UnexpectedSpreadArg(_, s) => *s,
658 ParseError::ExtraTokensAfterClosingDelimiter(s) => *s,
659 ParseError::AssignmentRequiresVar(s) => *s,
660 ParseError::AssignmentRequiresMutableVar(s) => *s,
661 ParseError::AttributeRequiresDefinition(s) => *s,
662 }
663 }
664}
665
666#[derive(Clone, Debug, Serialize, Deserialize)]
667pub struct DidYouMean(Option<String>);
668
669fn did_you_mean_impl(possibilities_bytes: &[&[u8]], input_bytes: &[u8]) -> Option<String> {
670 let input = from_utf8(input_bytes).ok()?;
671 let possibilities = possibilities_bytes
672 .iter()
673 .map(|p| from_utf8(p))
674 .collect::<Result<Vec<&str>, Utf8Error>>()
675 .ok()?;
676 did_you_mean(&possibilities, input)
677}
678impl DidYouMean {
679 pub fn new(possibilities_bytes: &[&[u8]], input_bytes: &[u8]) -> DidYouMean {
680 DidYouMean(did_you_mean_impl(possibilities_bytes, input_bytes))
681 }
682}
683
684impl From<Option<String>> for DidYouMean {
685 fn from(value: Option<String>) -> Self {
686 Self(value)
687 }
688}
689
690impl Display for DidYouMean {
691 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
692 if let Some(suggestion) = &self.0 {
693 write!(f, "Did you mean '{suggestion}'?")
694 } else {
695 write!(f, "")
696 }
697 }
698}