Orchid-base uses task-local context.

Everything else is broken at the moment.
This commit is contained in:
2025-12-14 17:17:43 +01:00
parent 8753d4c751
commit 0b2b05d44e
22 changed files with 463 additions and 1082 deletions

View File

@@ -2,13 +2,15 @@ use std::cell::RefCell;
use std::ffi::OsStr;
use std::fmt;
use std::ops::Add;
use std::rc::Rc;
use std::sync::Arc;
use futures::future::join_all;
use itertools::Itertools;
use some_executor::task_local;
use crate::api;
use crate::interner::{Interner, Tok};
use crate::interner::{IStr, es, is};
use crate::location::Pos;
/// A point of interest in resolving the error, such as the point where
@@ -24,10 +26,10 @@ impl ErrPos {
pub fn new(msg: &str, position: Pos) -> Self {
Self { message: Some(Arc::new(msg.to_string())), position }
}
async fn from_api(api: &api::ErrLocation, i: &Interner) -> Self {
async fn from_api(api: &api::ErrLocation) -> Self {
Self {
message: Some(api.message.clone()).filter(|s| !s.is_empty()),
position: Pos::from_api(&api.location, i).await,
position: Pos::from_api(&api.location).await,
}
}
fn to_api(&self) -> api::ErrLocation {
@@ -51,7 +53,7 @@ impl fmt::Display for ErrPos {
#[derive(Clone, Debug)]
pub struct OrcErr {
pub description: Tok<String>,
pub description: IStr,
pub message: Arc<String>,
pub positions: Vec<ErrPos>,
}
@@ -63,16 +65,16 @@ impl OrcErr {
locations: self.positions.iter().map(ErrPos::to_api).collect(),
}
}
async fn from_api(api: &api::OrcError, i: &Interner) -> Self {
async fn from_api(api: &api::OrcError) -> Self {
Self {
description: Tok::from_api(api.description, i).await,
description: es(api.description).await,
message: api.message.clone(),
positions: join_all(api.locations.iter().map(|e| ErrPos::from_api(e, i))).await,
positions: join_all(api.locations.iter().map(ErrPos::from_api)).await,
}
}
}
impl PartialEq<Tok<String>> for OrcErr {
fn eq(&self, other: &Tok<String>) -> bool { self.description == *other }
impl PartialEq<IStr> for OrcErr {
fn eq(&self, other: &IStr) -> bool { self.description == *other }
}
impl From<OrcErr> for Vec<OrcErr> {
fn from(value: OrcErr) -> Self { vec![value] }
@@ -122,11 +124,8 @@ impl OrcErrv {
self.0.iter().flat_map(|e| e.positions.iter().cloned())
}
pub fn to_api(&self) -> Vec<api::OrcError> { self.0.iter().map(OrcErr::to_api).collect() }
pub async fn from_api<'a>(
api: impl IntoIterator<Item = &'a api::OrcError>,
i: &Interner,
) -> Self {
Self(join_all(api.into_iter().map(|e| OrcErr::from_api(e, i))).await)
pub async fn from_api<'a>(api: impl IntoIterator<Item = &'a api::OrcError>) -> Self {
Self(join_all(api.into_iter().map(OrcErr::from_api)).await)
}
}
impl From<OrcErr> for OrcErrv {
@@ -191,12 +190,12 @@ macro_rules! join_ok {
(@VALUES) => { Ok(()) };
}
pub fn mk_errv_floating(description: Tok<String>, message: impl AsRef<str>) -> OrcErrv {
pub fn mk_errv_floating(description: IStr, message: impl AsRef<str>) -> OrcErrv {
mk_errv::<Pos>(description, message, [])
}
pub fn mk_errv<I: Into<ErrPos>>(
description: Tok<String>,
description: IStr,
message: impl AsRef<str>,
posv: impl IntoIterator<Item = I>,
) -> OrcErrv {
@@ -210,45 +209,61 @@ pub fn mk_errv<I: Into<ErrPos>>(
pub async fn async_io_err<I: Into<ErrPos>>(
err: std::io::Error,
i: &Interner,
posv: impl IntoIterator<Item = I>,
) -> OrcErrv {
mk_errv(i.i(&err.kind().to_string()).await, err.to_string(), posv)
mk_errv(is(&err.kind().to_string()).await, err.to_string(), posv)
}
pub async fn os_str_to_string<'a, I: Into<ErrPos>>(
str: &'a OsStr,
i: &Interner,
pub async fn os_str_to_string<I: Into<ErrPos>>(
str: &OsStr,
posv: impl IntoIterator<Item = I>,
) -> OrcRes<&'a str> {
) -> OrcRes<&str> {
match str.to_str() {
Some(str) => Ok(str),
None => Err(mk_errv(
i.i("Non-unicode string").await,
is("Non-unicode string").await,
format!("{str:?} is not representable as unicode"),
posv,
)),
}
}
pub struct Reporter {
errors: RefCell<Vec<OrcErr>>,
#[derive(Clone, Default)]
struct Reporter {
errors: Rc<RefCell<Vec<OrcErr>>>,
}
impl Reporter {
pub fn report(&self, e: impl Into<OrcErrv>) { self.errors.borrow_mut().extend(e.into()) }
pub fn new() -> Self { Self { errors: RefCell::new(vec![]) } }
pub fn errv(self) -> Option<OrcErrv> { OrcErrv::new(self.errors.into_inner()).ok() }
pub fn merge<T>(self, res: OrcRes<T>) -> OrcRes<T> {
match (res, self.errv()) {
(res, None) => res,
(Ok(_), Some(errv)) => Err(errv),
(Err(e), Some(errv)) => Err(e + errv),
}
task_local! {
static REPORTER: Reporter;
}
pub async fn with_reporter<T>(fut: impl Future<Output = OrcRes<T>>) -> OrcRes<T> {
let rep = Reporter::default();
let res = REPORTER.scope(rep.clone(), fut).await;
let errors = rep.errors.take();
match (res, &errors[..]) {
(Ok(t), []) => Ok(t),
(Ok(_), [_, ..]) => Err(OrcErrv::new(errors).unwrap()),
(Err(e), _) => Err(e.extended(errors)),
}
pub fn is_empty(&self) -> bool { self.errors.borrow().is_empty() }
}
impl Default for Reporter {
fn default() -> Self { Self::new() }
pub async fn is_erroring() -> bool {
REPORTER.with(|r| {
!r.expect("Sidechannel errors must be caught by a reporter").errors.borrow().is_empty()
})
}
/// Report an error that is fatal and prevents a correct output, but
/// still allows the current task to continue and produce an approximate output.
/// This can be used for
pub fn report(e: impl Into<OrcErrv>) {
let errv = e.into();
REPORTER.with(|r| match r {
Some(r) => r.errors.borrow_mut().extend(errv),
None => panic!(
"Unhandled error! Sidechannel errors must be caught by an enclosing call to with_reporter.\n\
Error: {errv}",
),
})
}