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