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