🪄 Srcerr
Types to track error codes and details.
This library provies a SourceError
struct that holds:
ErrorCode
: Enum whose variants indicate error code, simple description.
ErrorDetail
: Enum with matching variants to ErrorCode
, but each variant contains information specific to an instance of the error.
Severity
: The severity to report the error.
This library backs onto codespan-reporting
to render diagnostic errors.
The "codespan"
feature can also be used to expose codespan
types:
srcerr = { version = "0.4.0", features = ["codespan"] }
Usage
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum SimpleErrorCode {
ValueOutOfRange,
StringTooLong,
}
impl ErrorCode for SimpleErrorCode {
const ERROR_CODE_MAX: usize = 2;
const PREFIX: &'static str = "E";
fn code(self) -> usize {
match self {
Self::ValueOutOfRange => 1,
Self::StringTooLong => 2,
}
}
fn description(self) -> &'static str {
match self {
Self::ValueOutOfRange => "Value out of range.",
Self::StringTooLong => "String provided is too long.",
}
}
}
#[derive(Debug)]
pub enum SimpleErrorDetail {
ValueOutOfRange {
file_id: usize,
value: i32,
value_byte_indices: Range<usize>,
range: RangeInclusive<u32>,
},
StringTooLong {
file_id: usize,
value: String,
value_byte_indices: Range<usize>,
limit: usize,
},
}
impl<'files> ErrorDetail<'files> for SimpleErrorDetail {
type Files = SimpleFiles<&'files str, &'files str>;
fn labels(&self) -> Vec<Label<usize>> {
match self {
Self::ValueOutOfRange {
file_id,
value_byte_indices,
range,
..
} => {
vec![
Label::primary(*file_id, value_byte_indices.clone()).with_message(format!(
"not within the range: `{}..={}`",
range.start(),
range.end()
)),
]
}
Self::StringTooLong {
file_id,
value_byte_indices,
limit,
..
} => {
vec![
Label::primary(*file_id, value_byte_indices.clone())
.with_message(format!("exceeds the {} character limit.", limit)),
]
}
}
}
fn notes(&self, _files: &Self::Files) -> Vec<String> {
match self {
Self::ValueOutOfRange { range, .. } => {
let valid_exprs = range.clone().map(|n| Cow::Owned(n.to_string()));
let suggestion = Note::valid_exprs(valid_exprs).expect("Failed to format note.");
vec![suggestion]
}
Self::StringTooLong { .. } => vec![],
}
}
}
fn value_out_of_range<'f>(
file_id: usize,
) -> SourceError<'f, SimpleErrorCode, SimpleErrorDetail, SimpleFiles<&'f str, &'f str>> {
let error_code = SimpleErrorCode::ValueOutOfRange;
let error_detail = SimpleErrorDetail::ValueOutOfRange {
file_id,
value: -1,
value_byte_indices: 21..23,
range: 1..=3,
};
let severity = Severity::Error;
SourceError::new(error_code, error_detail, severity)
}
fn string_too_long<'f>(
file_id: usize,
content: &str,
) -> SourceError<'f, SimpleErrorCode, SimpleErrorDetail, SimpleFiles<&'f str, &'f str>> {
let error_code = SimpleErrorCode::StringTooLong;
let error_detail = SimpleErrorDetail::StringTooLong {
file_id,
value: content[40..47].to_string(),
value_byte_indices: 39..48,
limit: 5,
};
let severity = Severity::Error;
SourceError::new(error_code, error_detail, severity)
}
let value_out_of_range = value_out_of_range(file_id);
let value_out_of_range = value_out_of_range.as_diagnostic(&files);
let string_too_long = string_too_long(file_id, content);
let string_too_long = string_too_long.as_diagnostic(&files);
let writer = StandardStream::stderr(ColorChoice::Always);
let config = term::Config::default();
term::emit(&mut writer.lock(), &config, &files, &value_out_of_range)?;
term::emit(&mut writer.lock(), &config, &files, &string_too_long)?;
Sample usage can be seen in the examples.
cargo run --example simple
cargo run --example source_ref_hint
cargo run --example long_expr_context
cargo run --example html > /tmp/index.html
cargo run --example codespan --features codespan
License
Licensed under either of
at your option.
Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.