Refactor config updates (#13802)

# Description
This PR standardizes updates to the config through a new
`UpdateFromValue` trait. For now, this trait is private in case we need
to make changes to it.

Note that this PR adds some additional `ShellError` cases to create
standard error messages for config errors. A follow-up PR will move
usages of the old error cases to these new ones. This PR also uses
`Type::custom` in lots of places (e.g., for string enums). Not sure if
this is something we want to encourage.

# User-Facing Changes
Should be none.
This commit is contained in:
Ian Manske
2024-10-11 09:40:32 -07:00
committed by GitHub
parent 02313e6819
commit fce6146576
32 changed files with 1343 additions and 1487 deletions

View File

@ -15,8 +15,8 @@ use thiserror::Error;
/// forwards most methods, except for `.source_code()`, which we provide.
#[derive(Error)]
#[error("{0}")]
pub struct CliError<'src>(
pub &'src (dyn miette::Diagnostic + Send + Sync + 'static),
struct CliError<'src>(
pub &'src dyn miette::Diagnostic,
pub &'src StateWorkingSet<'src>,
);
@ -48,10 +48,7 @@ pub fn report_compile_error(working_set: &StateWorkingSet, error: &CompileError)
report_error(working_set, error);
}
fn report_error(
working_set: &StateWorkingSet,
error: &(dyn miette::Diagnostic + Send + Sync + 'static),
) {
fn report_error(working_set: &StateWorkingSet, error: &dyn miette::Diagnostic) {
eprintln!("Error: {:?}", CliError(error, working_set));
// reset vt processing, aka ansi because illbehaved externals can break it
#[cfg(windows)]
@ -60,10 +57,7 @@ fn report_error(
}
}
fn report_warning(
working_set: &StateWorkingSet,
error: &(dyn miette::Diagnostic + Send + Sync + 'static),
) {
fn report_warning(working_set: &StateWorkingSet, error: &dyn miette::Diagnostic) {
eprintln!("Warning: {:?}", CliError(error, working_set));
// reset vt processing, aka ansi because illbehaved externals can break it
#[cfg(windows)]

View File

@ -0,0 +1,56 @@
use crate::{ShellError, Span, Type};
use miette::Diagnostic;
use thiserror::Error;
/// The errors that may occur when updating the config
#[derive(Clone, Debug, PartialEq, Error, Diagnostic)]
pub enum ConfigError {
#[error("Type mismatch at {path}")]
#[diagnostic(code(nu::shell::type_mismatch))]
TypeMismatch {
path: String,
expected: Type,
actual: Type,
#[label = "expected {expected}, but got {actual}"]
span: Span,
},
#[error("Invalid value for {path}")]
#[diagnostic(code(nu::shell::invalid_value))]
InvalidValue {
path: String,
valid: String,
actual: String,
#[label = "expected {valid}, but got {actual}"]
span: Span,
},
#[error("Unknown config option: {path}")]
#[diagnostic(code(nu::shell::unknown_config_option))]
UnknownOption {
path: String,
#[label("remove this")]
span: Span,
},
#[error("{path} requires a '{column}' column")]
#[diagnostic(code(nu::shell::missing_required_column))]
MissingRequiredColumn {
path: String,
column: &'static str,
#[label("has no '{column}' column")]
span: Span,
},
#[error("{path} is deprecated")]
#[diagnostic(
code(nu::shell::deprecated_config_option),
help("please {suggestion} instead")
)]
Deprecated {
path: String,
suggestion: &'static str,
#[label("deprecated")]
span: Span,
},
// TODO: remove this
#[error(transparent)]
#[diagnostic(transparent)]
ShellError(#[from] ShellError),
}

View File

@ -1,5 +1,6 @@
pub mod cli_error;
mod compile_error;
mod config_error;
mod labeled_error;
mod parse_error;
mod parse_warning;
@ -10,6 +11,7 @@ pub use cli_error::{
report_shell_warning,
};
pub use compile_error::CompileError;
pub use config_error::ConfigError;
pub use labeled_error::{ErrorLabel, LabeledError};
pub use parse_error::{DidYouMean, ParseError};
pub use parse_warning::ParseWarning;

View File

@ -1,13 +1,12 @@
use crate::{
ast::Operator, engine::StateWorkingSet, format_shell_error, record, ConfigError, LabeledError,
ParseError, Span, Spanned, Type, Value,
};
use miette::Diagnostic;
use serde::{Deserialize, Serialize};
use std::{io, num::NonZeroI32};
use thiserror::Error;
use crate::{
ast::Operator, engine::StateWorkingSet, format_shell_error, record, LabeledError, ParseError,
Span, Spanned, Value,
};
/// The fundamental error type for the evaluation engine. These cases represent different kinds of errors
/// the evaluator might face, along with helpful spans to label. An error renderer will take this error value
/// and pass it into an error viewer to display to the user.
@ -108,6 +107,34 @@ pub enum ShellError {
span: Span,
},
/// A value's type did not match the expected type.
///
/// ## Resolution
///
/// Convert the value to the correct type or provide a value of the correct type.
#[error("Type mismatch")]
#[diagnostic(code(nu::shell::type_mismatch))]
RuntimeTypeMismatch {
expected: Type,
actual: Type,
#[label = "expected {expected}, but got {actual}"]
span: Span,
},
/// A value had the correct type but is otherwise invalid.
///
/// ## Resolution
///
/// Ensure the value meets the criteria in the error message.
#[error("Invalid value")]
#[diagnostic(code(nu::shell::invalid_value))]
InvalidValue {
valid: String,
actual: String,
#[label = "expected {valid}, but got {actual}"]
span: Span,
},
/// A command received an argument with correct type but incorrect value.
///
/// ## Resolution
@ -1085,30 +1112,28 @@ pub enum ShellError {
span: Span,
},
/// The value given for this configuration is not supported.
/// Failed to update the config due to one or more errors.
///
/// ## Resolution
///
/// Refer to the specific error message for details and convert values as needed.
#[error("Unsupported config value")]
#[diagnostic(code(nu::shell::unsupported_config_value))]
UnsupportedConfigValue {
expected: String,
value: String,
#[label("expected {expected}, got {value}")]
span: Span,
/// Refer to the error messages for specific details.
#[error("Encountered {} error(s) when updating config", errors.len())]
#[diagnostic(code(nu::shell::invalid_config))]
InvalidConfig {
#[related]
errors: Vec<ConfigError>,
},
/// An expected configuration value is not present.
/// A value was missing a required column.
///
/// ## Resolution
///
/// Refer to the specific error message and add the configuration value to your config file as needed.
#[error("Missing config value")]
#[diagnostic(code(nu::shell::missing_config_value))]
MissingConfigValue {
missing_value: String,
#[label("missing {missing_value}")]
/// Make sure the value has the required column.
#[error("Value is missing a required '{column}' column")]
#[diagnostic(code(nu::shell::missing_required_column))]
MissingRequiredColumn {
column: &'static str,
#[label("has no '{column}' column")]
span: Span,
},