Replace panics with errors in thread spawning (#12040)

# Description
Replace panics with errors in thread spawning.

Also adds `IntoSpanned` trait for easily constructing `Spanned`, and an
implementation of `From<Spanned<std::io::Error>>` for `ShellError`,
which is used to provide context for the error wherever there was a span
conveniently available. In general this should make it more convenient
to do the right thing with `std::io::Error` and always add a span to it
when it's possible to do so.

# User-Facing Changes
Fewer panics!

# Tests + Formatting
- 🟢 `toolkit fmt`
- 🟢 `toolkit clippy`
- 🟢 `toolkit test`
- 🟢 `toolkit test stdlib`
This commit is contained in:
Devyn Cairns
2024-03-02 09:14:02 -08:00
committed by GitHub
parent 8c112c9efd
commit 626d597527
13 changed files with 176 additions and 114 deletions

View File

@ -868,8 +868,7 @@ pub fn print_if_stream(
let _ = stderr.write_all(&bytes);
}
}
})
.expect("could not create thread");
})?;
}
if let Some(stream) = stream {

View File

@ -2,7 +2,9 @@ use miette::Diagnostic;
use serde::{Deserialize, Serialize};
use thiserror::Error;
use crate::{ast::Operator, engine::StateWorkingSet, format_error, ParseError, Span, Value};
use crate::{
ast::Operator, engine::StateWorkingSet, format_error, 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
@ -1361,6 +1363,15 @@ impl From<std::io::Error> for ShellError {
}
}
impl From<Spanned<std::io::Error>> for ShellError {
fn from(error: Spanned<std::io::Error>) -> Self {
ShellError::IOErrorSpanned {
msg: error.item.to_string(),
span: error.span,
}
}
}
impl std::convert::From<Box<dyn std::error::Error>> for ShellError {
fn from(input: Box<dyn std::error::Error>) -> ShellError {
ShellError::IOError {

View File

@ -3,14 +3,33 @@ use serde::{Deserialize, Serialize};
/// A spanned area of interest, generic over what kind of thing is of interest
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub struct Spanned<T>
where
T: Clone + std::fmt::Debug,
{
pub struct Spanned<T> {
pub item: T,
pub span: Span,
}
/// Helper trait to create [`Spanned`] more ergonomically.
pub trait IntoSpanned: Sized {
/// Wrap items together with a span into [`Spanned`].
///
/// # Example
///
/// ```
/// # use nu_protocol::{Span, IntoSpanned};
/// # let span = Span::test_data();
/// let spanned = "Hello, world!".into_spanned(span);
/// assert_eq!("Hello, world!", spanned.item);
/// assert_eq!(span, spanned.span);
/// ```
fn into_spanned(self, span: Span) -> Spanned<Self>;
}
impl<T> IntoSpanned for T {
fn into_spanned(self, span: Span) -> Spanned<Self> {
Spanned { item: self, span }
}
}
/// Spans are a global offset across all seen files, which are cached in the engine's state. The start and
/// end offset together make the inclusive start/exclusive end pair for where to underline to highlight
/// a given point of interest.