forked from Orchid/orchid
Temporary commit to try fix halting
This commit is contained in:
@@ -3,6 +3,9 @@ name = "orchid-base"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[features]
|
||||
mocks = []
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
|
||||
28
orchid-base/src/ctx.rs
Normal file
28
orchid-base/src/ctx.rs
Normal file
@@ -0,0 +1,28 @@
|
||||
use std::any::{TypeId, type_name};
|
||||
use std::marker::PhantomData;
|
||||
use std::rc::Rc;
|
||||
|
||||
use orchid_api_traits::MsgSet;
|
||||
|
||||
use crate::error::Reporter;
|
||||
use crate::interner::Interner;
|
||||
use crate::reqnot::{Client, DynClient};
|
||||
|
||||
pub trait CtxDyn {
|
||||
fn i(&self) -> Interner;
|
||||
fn rep(&self) -> &Reporter;
|
||||
fn client(&self, msg_set: TypeId) -> Option<Rc<dyn DynClient>>;
|
||||
fn msg_set_type(&self) -> TypeId;
|
||||
}
|
||||
|
||||
pub struct Ctx(Rc<dyn CtxDyn>);
|
||||
impl Ctx {
|
||||
pub fn i(&self) -> Interner { self.0.i() }
|
||||
pub fn rep(&self) -> &Reporter { self.0.rep() }
|
||||
pub fn client<T: MsgSet>(&self) -> Client<T> {
|
||||
let Some(dyn_client) = self.0.client(TypeId::of::<T>()) else {
|
||||
panic!("Incorrect message set {} passed", type_name::<T>());
|
||||
};
|
||||
Client(dyn_client, PhantomData)
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,22 @@
|
||||
use std::cell::RefCell;
|
||||
use std::ffi::OsStr;
|
||||
use std::fmt;
|
||||
use std::ops::Add;
|
||||
use std::fmt::{self, Display};
|
||||
use std::ops::{Add, AddAssign, Deref};
|
||||
use std::pin::Pin;
|
||||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
|
||||
use futures::future::join_all;
|
||||
use async_fn_stream::stream;
|
||||
use async_once_cell::{Lazy, OnceCell};
|
||||
use futures::future::{join_all, ready};
|
||||
use futures::lock::Mutex;
|
||||
use futures::{Stream, StreamExt, stream};
|
||||
use itertools::Itertools;
|
||||
|
||||
use crate::api;
|
||||
use crate::interner::{Interner, Tok};
|
||||
use crate::ctx::Ctx;
|
||||
use crate::format::{FmtCtx, FmtUnit, Format};
|
||||
use crate::interner::{IStr, Interner};
|
||||
use crate::location::Pos;
|
||||
|
||||
/// A point of interest in resolving the error, such as the point where
|
||||
@@ -50,40 +58,126 @@ impl fmt::Display for ErrPos {
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct OrcErr {
|
||||
pub description: Tok<String>,
|
||||
struct SingleError {
|
||||
pub description: IStr,
|
||||
pub message: Arc<String>,
|
||||
pub positions: Vec<ErrPos>,
|
||||
}
|
||||
impl OrcErr {
|
||||
fn to_api(&self) -> api::OrcError {
|
||||
api::OrcError {
|
||||
description: self.description.to_api(),
|
||||
message: self.message.clone(),
|
||||
locations: self.positions.iter().map(ErrPos::to_api).collect(),
|
||||
}
|
||||
}
|
||||
async fn from_api(api: &api::OrcError, i: &Interner) -> Self {
|
||||
Self {
|
||||
description: Tok::from_api(api.description, i).await,
|
||||
message: api.message.clone(),
|
||||
positions: join_all(api.locations.iter().map(|e| ErrPos::from_api(e, i))).await,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl PartialEq<Tok<String>> for OrcErr {
|
||||
fn eq(&self, other: &Tok<String>) -> bool { self.description == *other }
|
||||
}
|
||||
impl From<OrcErr> for Vec<OrcErr> {
|
||||
fn from(value: OrcErr) -> Self { vec![value] }
|
||||
}
|
||||
impl fmt::Display for OrcErr {
|
||||
impl fmt::Display for SingleError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let pstr = self.positions.iter().map(|p| format!("{p}")).join("; ");
|
||||
write!(f, "{}: {} @ {}", self.description, self.message, pstr)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct OrcErr {
|
||||
singles: OnceCell<OwnedOrcErr>,
|
||||
futures: Mutex<Vec<Pin<Box<dyn Future<Output = OwnedOrcErr>>>>>,
|
||||
}
|
||||
impl OrcErr {
|
||||
pub async fn into_owned(self) -> OwnedOrcErr {
|
||||
self.to_owned().await;
|
||||
self.singles.into_inner().expect("Initialized above")
|
||||
}
|
||||
pub async fn to_owned(&self) -> &OwnedOrcErr {
|
||||
self
|
||||
.singles
|
||||
.get_or_init(async {
|
||||
let results = join_all(self.futures.lock().await.drain(..)).await;
|
||||
OwnedOrcErr(results.iter().flat_map(|err| err.0.iter()).cloned().collect())
|
||||
})
|
||||
.await
|
||||
}
|
||||
fn into_futures(self) -> Vec<Pin<Box<dyn Future<Output = OwnedOrcErr>>>> {
|
||||
match self.singles.into_inner() {
|
||||
Some(val) => vec![Box::pin(ready(val))],
|
||||
None => self.futures.into_inner(),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl From<OwnedOrcErr> for OrcErr {
|
||||
fn from(value: OwnedOrcErr) -> Self {
|
||||
Self { singles: OnceCell::from(value), futures: Mutex::new(vec![]) }
|
||||
}
|
||||
}
|
||||
impl<T: Future<Output = OrcErr> + 'static> From<T> for OrcErr {
|
||||
fn from(value: T) -> Self {
|
||||
Self {
|
||||
singles: OnceCell::new(),
|
||||
futures: Mutex::new(vec![Box::pin(async { value.await.into_owned().await })]),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Add for OrcErr {
|
||||
type Output = Self;
|
||||
fn add(mut self, mut rhs: Self) -> Self::Output {
|
||||
if let (Some(l), Some(r)) = (self.singles.get_mut(), rhs.singles.get_mut()) {
|
||||
l.0.extend(r.0.drain(..));
|
||||
return self;
|
||||
}
|
||||
Self {
|
||||
singles: OnceCell::new(),
|
||||
futures: Mutex::new(self.into_futures().into_iter().chain(rhs.into_futures()).collect()),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl AddAssign for OrcErr {
|
||||
fn add_assign(&mut self, mut rhs: Self) {
|
||||
if let (Some(l), Some(r)) = (self.singles.get_mut(), rhs.singles.get_mut()) {
|
||||
l.0.extend(r.0.drain(..));
|
||||
} else {
|
||||
let mut temp = Self { futures: Mutex::default(), singles: OnceCell::new() };
|
||||
std::mem::swap(self, &mut temp);
|
||||
self.futures.get_mut().extend(temp.into_futures().into_iter().chain(rhs.into_futures()));
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Format for OrcErr {
|
||||
async fn print<'a>(&'a self, _: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit {
|
||||
format!("{}", self.to_owned().await).into()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct OwnedOrcErr(Vec<SingleError>);
|
||||
impl OwnedOrcErr {
|
||||
pub fn to_api(&self) -> Vec<api::OrcError> {
|
||||
self
|
||||
.0
|
||||
.iter()
|
||||
.map(|err| api::OrcError {
|
||||
description: err.description.to_api(),
|
||||
message: err.message.clone(),
|
||||
locations: err.positions.iter().map(|pos| pos.to_api()).collect(),
|
||||
})
|
||||
.collect_vec()
|
||||
}
|
||||
pub async fn from_api(api: impl IntoIterator<Item = &api::OrcError>, i: &Interner) -> Self {
|
||||
Self(
|
||||
join_all(api.into_iter().map(|e| async {
|
||||
SingleError {
|
||||
description: i.es(e.description).await,
|
||||
message: e.message.clone(),
|
||||
positions: join_all(e.locations.iter().map(|pos| ErrPos::from_api(pos, i))).await,
|
||||
}
|
||||
}))
|
||||
.await,
|
||||
)
|
||||
}
|
||||
}
|
||||
impl Display for OwnedOrcErr {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.0.iter().join("\n"))
|
||||
}
|
||||
}
|
||||
impl fmt::Debug for OwnedOrcErr {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "OwnedOrcErr({self}") }
|
||||
}
|
||||
impl Add for OwnedOrcErr {
|
||||
type Output = Self;
|
||||
fn add(self, rhs: Self) -> Self::Output { Self(self.0.into_iter().chain(rhs.0).collect()) }
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct EmptyErrv;
|
||||
impl fmt::Display for EmptyErrv {
|
||||
@@ -92,70 +186,7 @@ impl fmt::Display for EmptyErrv {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct OrcErrv(Vec<OrcErr>);
|
||||
impl OrcErrv {
|
||||
pub fn new(errors: impl IntoIterator<Item = OrcErr>) -> Result<Self, EmptyErrv> {
|
||||
let v = errors.into_iter().collect_vec();
|
||||
if v.is_empty() { Err(EmptyErrv) } else { Ok(Self(v)) }
|
||||
}
|
||||
#[must_use]
|
||||
pub fn extended<T>(mut self, errors: impl IntoIterator<Item = T>) -> Self
|
||||
where Self: Extend<T> {
|
||||
self.extend(errors);
|
||||
self
|
||||
}
|
||||
#[must_use]
|
||||
pub fn len(&self) -> usize { self.0.len() }
|
||||
#[must_use]
|
||||
pub fn is_empty(&self) -> bool { self.len() == 0 }
|
||||
#[must_use]
|
||||
pub fn any(&self, f: impl FnMut(&OrcErr) -> bool) -> bool { self.0.iter().any(f) }
|
||||
#[must_use]
|
||||
pub fn keep_only(self, f: impl FnMut(&OrcErr) -> bool) -> Option<Self> {
|
||||
let v = self.0.into_iter().filter(f).collect_vec();
|
||||
if v.is_empty() { None } else { Some(Self(v)) }
|
||||
}
|
||||
#[must_use]
|
||||
pub fn one(&self) -> Option<&OrcErr> { (self.0.len() == 1).then(|| &self.0[9]) }
|
||||
pub fn pos_iter(&self) -> impl Iterator<Item = ErrPos> + '_ {
|
||||
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)
|
||||
}
|
||||
}
|
||||
impl From<OrcErr> for OrcErrv {
|
||||
fn from(value: OrcErr) -> Self { Self(vec![value]) }
|
||||
}
|
||||
impl Add for OrcErrv {
|
||||
type Output = Self;
|
||||
fn add(self, rhs: Self) -> Self::Output { Self(self.0.into_iter().chain(rhs.0).collect_vec()) }
|
||||
}
|
||||
impl Extend<OrcErr> for OrcErrv {
|
||||
fn extend<T: IntoIterator<Item = OrcErr>>(&mut self, iter: T) { self.0.extend(iter) }
|
||||
}
|
||||
impl Extend<OrcErrv> for OrcErrv {
|
||||
fn extend<T: IntoIterator<Item = OrcErrv>>(&mut self, iter: T) {
|
||||
self.0.extend(iter.into_iter().flatten())
|
||||
}
|
||||
}
|
||||
impl IntoIterator for OrcErrv {
|
||||
type IntoIter = std::vec::IntoIter<OrcErr>;
|
||||
type Item = OrcErr;
|
||||
fn into_iter(self) -> Self::IntoIter { self.0.into_iter() }
|
||||
}
|
||||
impl fmt::Display for OrcErrv {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.0.iter().join("\n"))
|
||||
}
|
||||
}
|
||||
|
||||
pub type OrcRes<T> = Result<T, OrcErrv>;
|
||||
pub type OrcRes<T> = Result<T, OrcErr>;
|
||||
|
||||
pub fn join_ok<T, U>(left: OrcRes<T>, right: OrcRes<U>) -> OrcRes<(T, U)> {
|
||||
match (left, right) {
|
||||
@@ -191,62 +222,80 @@ macro_rules! join_ok {
|
||||
(@VALUES) => { Ok(()) };
|
||||
}
|
||||
|
||||
pub fn mk_errv_floating(description: Tok<String>, message: impl AsRef<str>) -> OrcErrv {
|
||||
mk_errv::<Pos>(description, message, [])
|
||||
}
|
||||
|
||||
pub fn mk_errv<I: Into<ErrPos>>(
|
||||
description: Tok<String>,
|
||||
message: impl AsRef<str>,
|
||||
posv: impl IntoIterator<Item = I>,
|
||||
) -> OrcErrv {
|
||||
OrcErr {
|
||||
description,
|
||||
message: Arc::new(message.as_ref().to_string()),
|
||||
positions: posv.into_iter().map_into().collect(),
|
||||
impl Ctx {
|
||||
pub fn mk_err_floating(
|
||||
&self,
|
||||
description: impl AsRef<str> + 'static,
|
||||
message: impl AsRef<str> + 'static,
|
||||
) -> OrcErr {
|
||||
self.mk_err::<Pos>(description, message, [])
|
||||
}
|
||||
.into()
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
pub async fn os_str_to_string<'a, I: Into<ErrPos>>(
|
||||
str: &'a OsStr,
|
||||
i: &Interner,
|
||||
posv: impl IntoIterator<Item = I>,
|
||||
) -> OrcRes<&'a str> {
|
||||
match str.to_str() {
|
||||
Some(str) => Ok(str),
|
||||
None => Err(mk_errv(
|
||||
i.i("Non-unicode string").await,
|
||||
format!("{str:?} is not representable as unicode"),
|
||||
posv,
|
||||
)),
|
||||
pub fn mk_err<I: Into<ErrPos>>(
|
||||
&self,
|
||||
description: impl AsRef<str> + 'static,
|
||||
message: impl AsRef<str> + 'static,
|
||||
posv: impl IntoIterator<Item = I> + 'static,
|
||||
) -> OrcErr {
|
||||
let i = self.i();
|
||||
async move {
|
||||
OwnedOrcErr(vec![SingleError {
|
||||
description: i.is(description.as_ref()).await,
|
||||
message: Arc::new(message.as_ref().to_string()),
|
||||
positions: posv.into_iter().map_into().collect(),
|
||||
}])
|
||||
.into()
|
||||
}
|
||||
.into()
|
||||
}
|
||||
pub async fn async_io_err<I: Into<ErrPos>>(
|
||||
&self,
|
||||
err: std::io::Error,
|
||||
posv: impl IntoIterator<Item = I> + 'static,
|
||||
) -> OrcErr {
|
||||
self.mk_err(err.kind().to_string(), err.to_string(), posv)
|
||||
}
|
||||
pub fn os_str_to_string<'a, I: Into<ErrPos>>(
|
||||
&self,
|
||||
str: &'a OsStr,
|
||||
posv: impl IntoIterator<Item = I> + 'static,
|
||||
) -> OrcRes<&'a str> {
|
||||
match str.to_str() {
|
||||
Some(str) => Ok(str),
|
||||
None => Err(self.mk_err(
|
||||
"Non-unicode string",
|
||||
format!("{str:?} is not representable as unicode"),
|
||||
posv,
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Reporter {
|
||||
errors: RefCell<Vec<OrcErr>>,
|
||||
errors: RefCell<Option<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),
|
||||
pub fn report(&self, e: impl Into<OrcErr>) {
|
||||
match &mut *self.errors.borrow_mut() {
|
||||
slot @ None => *slot = Some(e.into()),
|
||||
Some(err) => *err += e.into(),
|
||||
}
|
||||
}
|
||||
pub fn is_empty(&self) -> bool { self.errors.borrow().is_empty() }
|
||||
pub fn new() -> Self { Self { errors: RefCell::new(None) } }
|
||||
pub fn res(self) -> Result<(), OrcErr> {
|
||||
match self.errors.into_inner() {
|
||||
Some(e) => Err(e),
|
||||
None => Ok(()),
|
||||
}
|
||||
}
|
||||
pub fn merge<T>(self, res: OrcRes<T>) -> OrcRes<T> {
|
||||
match (res, self.res()) {
|
||||
(res, Ok(())) => res,
|
||||
(Ok(_), Err(e)) => Err(e),
|
||||
(Err(e), Err(e2)) => Err(e + e2),
|
||||
}
|
||||
}
|
||||
pub fn is_empty(&self) -> bool { self.errors.borrow().is_none() }
|
||||
}
|
||||
|
||||
impl Default for Reporter {
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
use std::any::Any;
|
||||
use std::borrow::Borrow;
|
||||
use std::future::Future;
|
||||
use std::hash::BuildHasher as _;
|
||||
use std::hash::{BuildHasher as _, Hash};
|
||||
use std::num::NonZeroU64;
|
||||
use std::ops::Deref;
|
||||
use std::rc::Rc;
|
||||
use std::sync::atomic;
|
||||
use std::{fmt, hash};
|
||||
|
||||
use futures::future::LocalBoxFuture;
|
||||
use futures::lock::Mutex;
|
||||
use hashbrown::{HashMap, HashSet};
|
||||
use itertools::Itertools as _;
|
||||
@@ -15,296 +17,460 @@ use orchid_api_traits::Request;
|
||||
use crate::api;
|
||||
use crate::reqnot::{DynRequester, Requester};
|
||||
|
||||
/// Clippy crashes while verifying `Tok: Sized` without this and I cba to create
|
||||
/// a minimal example
|
||||
#[derive(Clone)]
|
||||
struct ForceSized<T>(T);
|
||||
// /// Clippy crashes while verifying `Tok: Sized` without this and I cba to
|
||||
// create /// a minimal example
|
||||
// #[derive(Clone)]
|
||||
// struct ForceSized<T>(T);
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Tok<T: Interned> {
|
||||
data: Rc<T>,
|
||||
marker: ForceSized<T::Marker>,
|
||||
}
|
||||
impl<T: Interned> Tok<T> {
|
||||
pub fn new(data: Rc<T>, marker: T::Marker) -> Self { Self { data, marker: ForceSized(marker) } }
|
||||
pub fn to_api(&self) -> T::Marker { self.marker.0 }
|
||||
pub async fn from_api<M>(marker: M, i: &Interner) -> Self
|
||||
where M: InternMarker<Interned = T> {
|
||||
i.ex(marker).await
|
||||
}
|
||||
pub fn rc(&self) -> Rc<T> { self.data.clone() }
|
||||
}
|
||||
impl<T: Interned> Deref for Tok<T> {
|
||||
type Target = T;
|
||||
// #[derive(Clone)]
|
||||
// pub struct Tok<T: Interned> {
|
||||
// data: Rc<T>,
|
||||
// marker: ForceSized<T::Marker>,
|
||||
// }
|
||||
// impl<T: Interned> Tok<T> {
|
||||
// pub fn new(data: Rc<T>, marker: T::Marker) -> Self { Self { data, marker:
|
||||
// ForceSized(marker) } } pub fn to_api(&self) -> T::Marker { self.marker.0 }
|
||||
// pub async fn from_api<M>(marker: M, i: &Interner) -> Self
|
||||
// where M: InternMarker<Interned = T> {
|
||||
// i.ex(marker).await
|
||||
// }
|
||||
// pub fn rc(&self) -> Rc<T> { self.data.clone() }
|
||||
// }
|
||||
// impl<T: Interned> Deref for Tok<T> {
|
||||
// type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target { self.data.as_ref() }
|
||||
}
|
||||
impl<T: Interned> Ord for Tok<T> {
|
||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering { self.to_api().cmp(&other.to_api()) }
|
||||
}
|
||||
impl<T: Interned> PartialOrd for Tok<T> {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> { Some(self.cmp(other)) }
|
||||
}
|
||||
impl<T: Interned> Eq for Tok<T> {}
|
||||
impl<T: Interned> PartialEq for Tok<T> {
|
||||
fn eq(&self, other: &Self) -> bool { self.cmp(other).is_eq() }
|
||||
}
|
||||
impl<T: Interned> hash::Hash for Tok<T> {
|
||||
fn hash<H: hash::Hasher>(&self, state: &mut H) { self.to_api().hash(state) }
|
||||
}
|
||||
impl<T: Interned + fmt::Display> fmt::Display for Tok<T> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", &*self.data)
|
||||
}
|
||||
}
|
||||
impl<T: Interned + fmt::Debug> fmt::Debug for Tok<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "Token({} -> {:?})", self.to_api().get_id(), self.data.as_ref())
|
||||
}
|
||||
}
|
||||
// fn deref(&self) -> &Self::Target { self.data.as_ref() }
|
||||
// }
|
||||
// impl<T: Interned> Ord for Tok<T> {
|
||||
// fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||
// self.to_api().cmp(&other.to_api()) } }
|
||||
// impl<T: Interned> PartialOrd for Tok<T> {
|
||||
// fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||
// Some(self.cmp(other)) } }
|
||||
// impl<T: Interned> Eq for Tok<T> {}
|
||||
// impl<T: Interned> PartialEq for Tok<T> {
|
||||
// fn eq(&self, other: &Self) -> bool { self.cmp(other).is_eq() }
|
||||
// }
|
||||
// impl<T: Interned> hash::Hash for Tok<T> {
|
||||
// fn hash<H: hash::Hasher>(&self, state: &mut H) { self.to_api().hash(state) }
|
||||
// }
|
||||
// impl<T: Interned + fmt::Display> fmt::Display for Tok<T> {
|
||||
// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
// write!(f, "{}", &*self.data)
|
||||
// }
|
||||
// }
|
||||
// impl<T: Interned + fmt::Debug> fmt::Debug for Tok<T> {
|
||||
// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
// write!(f, "Token({} -> {:?})", self.to_api().get_id(), self.data.as_ref())
|
||||
// }
|
||||
// }
|
||||
|
||||
pub trait Interned: Eq + hash::Hash + Clone + fmt::Debug + Internable<Interned = Self> {
|
||||
type Marker: InternMarker<Interned = Self> + Sized;
|
||||
fn intern(
|
||||
self: Rc<Self>,
|
||||
req: &(impl DynRequester<Transfer = api::IntReq> + ?Sized),
|
||||
) -> impl Future<Output = Self::Marker>;
|
||||
fn bimap(interner: &mut TypedInterners) -> &mut Bimap<Self>;
|
||||
}
|
||||
// pub trait Interned: Eq + hash::Hash + Clone + fmt::Debug +
|
||||
// Internable<Interned = Self> { type Marker: InternMarker<Interned = Self> +
|
||||
// Sized; fn intern(
|
||||
// self: Rc<Self>,
|
||||
// req: &(impl DynRequester<Transfer = api::IntReq> + ?Sized),
|
||||
// ) -> impl Future<Output = Self::Marker>;
|
||||
// fn bimap(interner: &mut TypedInterners) -> &mut Bimap<Self>;
|
||||
// }
|
||||
|
||||
pub trait Internable: fmt::Debug {
|
||||
type Interned: Interned;
|
||||
fn get_owned(&self) -> Rc<Self::Interned>;
|
||||
}
|
||||
// pub trait Internable: fmt::Debug {
|
||||
// type Interned: Interned;
|
||||
// fn get_owned(&self) -> Rc<Self::Interned>;
|
||||
// }
|
||||
|
||||
pub trait InternMarker: Copy + PartialEq + Eq + PartialOrd + Ord + hash::Hash + Sized {
|
||||
type Interned: Interned<Marker = Self>;
|
||||
/// Only called on replicas
|
||||
fn resolve(self, i: &Interner) -> impl Future<Output = Tok<Self::Interned>>;
|
||||
fn get_id(self) -> NonZeroU64;
|
||||
fn from_id(id: NonZeroU64) -> Self;
|
||||
}
|
||||
// pub trait InternMarker: Copy + PartialEq + Eq + PartialOrd + Ord + hash::Hash
|
||||
// + Sized { type Interned: Interned<Marker = Self>;
|
||||
// /// Only called on replicas
|
||||
// fn resolve(self, i: &Interner) -> impl Future<Output = Tok<Self::Interned>>;
|
||||
// fn get_id(self) -> NonZeroU64;
|
||||
// fn from_id(id: NonZeroU64) -> Self;
|
||||
// }
|
||||
|
||||
impl Interned for String {
|
||||
type Marker = api::TStr;
|
||||
async fn intern(
|
||||
self: Rc<Self>,
|
||||
req: &(impl DynRequester<Transfer = api::IntReq> + ?Sized),
|
||||
) -> Self::Marker {
|
||||
req.request(api::InternStr(self.to_string())).await
|
||||
}
|
||||
fn bimap(interners: &mut TypedInterners) -> &mut Bimap<Self> { &mut interners.strings }
|
||||
}
|
||||
impl InternMarker for api::TStr {
|
||||
type Interned = String;
|
||||
async fn resolve(self, i: &Interner) -> Tok<Self::Interned> {
|
||||
Tok::new(Rc::new(i.0.master.as_ref().unwrap().request(api::ExternStr(self)).await), self)
|
||||
}
|
||||
fn get_id(self) -> NonZeroU64 { self.0 }
|
||||
fn from_id(id: NonZeroU64) -> Self { Self(id) }
|
||||
}
|
||||
impl Internable for str {
|
||||
type Interned = String;
|
||||
fn get_owned(&self) -> Rc<Self::Interned> { Rc::new(self.to_string()) }
|
||||
}
|
||||
impl Internable for String {
|
||||
type Interned = String;
|
||||
fn get_owned(&self) -> Rc<Self::Interned> { Rc::new(self.to_string()) }
|
||||
}
|
||||
// impl Interned for String {
|
||||
// type Marker = api::TStr;
|
||||
// async fn intern(
|
||||
// self: Rc<Self>,
|
||||
// req: &(impl DynRequester<Transfer = api::IntReq> + ?Sized),
|
||||
// ) -> Self::Marker {
|
||||
// req.request(api::InternStr(self.to_string())).await
|
||||
// }
|
||||
// fn bimap(interners: &mut TypedInterners) -> &mut Bimap<Self> { &mut
|
||||
// interners.strings } }
|
||||
// impl InternMarker for api::TStr {
|
||||
// type Interned = String;
|
||||
// async fn resolve(self, i: &Interner) -> Tok<Self::Interned> {
|
||||
// Tok::new(Rc::new(i.0.master.as_ref().unwrap().
|
||||
// request(api::ExternStr(self)).await), self) }
|
||||
// fn get_id(self) -> NonZeroU64 { self.0 }
|
||||
// fn from_id(id: NonZeroU64) -> Self { Self(id) }
|
||||
// }
|
||||
// impl Internable for str {
|
||||
// type Interned = String;
|
||||
// fn get_owned(&self) -> Rc<Self::Interned> { Rc::new(self.to_string()) }
|
||||
// }
|
||||
// impl Internable for String {
|
||||
// type Interned = String;
|
||||
// fn get_owned(&self) -> Rc<Self::Interned> { Rc::new(self.to_string()) }
|
||||
// }
|
||||
|
||||
impl Interned for Vec<Tok<String>> {
|
||||
type Marker = api::TStrv;
|
||||
async fn intern(
|
||||
self: Rc<Self>,
|
||||
req: &(impl DynRequester<Transfer = api::IntReq> + ?Sized),
|
||||
) -> Self::Marker {
|
||||
req.request(api::InternStrv(self.iter().map(|t| t.to_api()).collect())).await
|
||||
}
|
||||
fn bimap(interners: &mut TypedInterners) -> &mut Bimap<Self> { &mut interners.vecs }
|
||||
}
|
||||
impl InternMarker for api::TStrv {
|
||||
type Interned = Vec<Tok<String>>;
|
||||
async fn resolve(self, i: &Interner) -> Tok<Self::Interned> {
|
||||
let rep = i.0.master.as_ref().unwrap().request(api::ExternStrv(self)).await;
|
||||
let data = futures::future::join_all(rep.into_iter().map(|m| i.ex(m))).await;
|
||||
Tok::new(Rc::new(data), self)
|
||||
}
|
||||
fn get_id(self) -> NonZeroU64 { self.0 }
|
||||
fn from_id(id: NonZeroU64) -> Self { Self(id) }
|
||||
}
|
||||
impl Internable for [Tok<String>] {
|
||||
type Interned = Vec<Tok<String>>;
|
||||
fn get_owned(&self) -> Rc<Self::Interned> { Rc::new(self.to_vec()) }
|
||||
}
|
||||
impl<const N: usize> Internable for [Tok<String>; N] {
|
||||
type Interned = Vec<Tok<String>>;
|
||||
fn get_owned(&self) -> Rc<Self::Interned> { Rc::new(self.to_vec()) }
|
||||
}
|
||||
impl Internable for Vec<Tok<String>> {
|
||||
type Interned = Vec<Tok<String>>;
|
||||
fn get_owned(&self) -> Rc<Self::Interned> { Rc::new(self.to_vec()) }
|
||||
}
|
||||
// impl Interned for Vec<IStr> {
|
||||
// type Marker = api::TStrv;
|
||||
// async fn intern(
|
||||
// self: Rc<Self>,
|
||||
// req: &(impl DynRequester<Transfer = api::IntReq> + ?Sized),
|
||||
// ) -> Self::Marker {
|
||||
// req.request(api::InternStrv(self.iter().map(|t|
|
||||
// t.to_api()).collect())).await }
|
||||
// fn bimap(interners: &mut TypedInterners) -> &mut Bimap<Self> { &mut
|
||||
// interners.vecs } }
|
||||
// impl InternMarker for api::TStrv {
|
||||
// type Interned = Vec<IStr>;
|
||||
// async fn resolve(self, i: &Interner) -> Tok<Self::Interned> {
|
||||
// let rep =
|
||||
// i.0.master.as_ref().unwrap().request(api::ExternStrv(self)).await; let data
|
||||
// = futures::future::join_all(rep.into_iter().map(|m| i.ex(m))).await;
|
||||
// Tok::new(Rc::new(data), self)
|
||||
// }
|
||||
// fn get_id(self) -> NonZeroU64 { self.0 }
|
||||
// fn from_id(id: NonZeroU64) -> Self { Self(id) }
|
||||
// }
|
||||
// impl Internable for [IStr] {
|
||||
// type Interned = Vec<IStr>;
|
||||
// fn get_owned(&self) -> Rc<Self::Interned> { Rc::new(self.to_vec()) }
|
||||
// }
|
||||
// impl<const N: usize> Internable for [IStr; N] {
|
||||
// type Interned = Vec<IStr>;
|
||||
// fn get_owned(&self) -> Rc<Self::Interned> { Rc::new(self.to_vec()) }
|
||||
// }
|
||||
// impl Internable for Vec<IStr> {
|
||||
// type Interned = Vec<IStr>;
|
||||
// fn get_owned(&self) -> Rc<Self::Interned> { Rc::new(self.to_vec()) }
|
||||
// }
|
||||
// impl Internable for Vec<api::TStr> {
|
||||
// type Interned = Vec<Tok<String>>;
|
||||
// type Interned = Vec<IStr>;
|
||||
// fn get_owned(&self) -> Arc<Self::Interned> {
|
||||
// Arc::new(self.iter().map(|ts| deintern(*ts)).collect())
|
||||
// }
|
||||
// }
|
||||
// impl Internable for [api::TStr] {
|
||||
// type Interned = Vec<Tok<String>>;
|
||||
// type Interned = Vec<IStr>;
|
||||
// fn get_owned(&self) -> Arc<Self::Interned> {
|
||||
// Arc::new(self.iter().map(|ts| deintern(*ts)).collect())
|
||||
// }
|
||||
// }
|
||||
|
||||
/// The number of references held to any token by the interner.
|
||||
const BASE_RC: usize = 3;
|
||||
|
||||
#[test]
|
||||
fn base_rc_correct() {
|
||||
let tok = Tok::new(Rc::new("foo".to_string()), api::TStr(1.try_into().unwrap()));
|
||||
let mut bimap = Bimap::default();
|
||||
bimap.insert(tok.clone());
|
||||
assert_eq!(Rc::strong_count(&tok.data), BASE_RC + 1, "the bimap plus the current instance");
|
||||
macro_rules! token_def {
|
||||
($type:ident, $trait:ident, $deref:ty, $api_repr:ty, $type_name:expr) => {
|
||||
#[derive(Clone)]
|
||||
pub struct $type(Rc<dyn $trait>);
|
||||
impl $type {
|
||||
pub fn new<T: $trait + 'static>(t: T) -> Self { Self(Rc::new(t) as _) }
|
||||
pub fn to_api(&self) -> $api_repr { self.0.to_api() }
|
||||
pub fn inner(&self) -> &dyn Any { self.0.as_ref() }
|
||||
fn addr(&self) -> usize { Rc::as_ptr(&self.0).addr() }
|
||||
}
|
||||
pub trait $trait: Deref<Target = $deref> + Any {
|
||||
fn to_api(&self) -> $api_repr;
|
||||
}
|
||||
impl Deref for $type {
|
||||
type Target = $deref;
|
||||
fn deref(&self) -> &Self::Target { self.0.deref() }
|
||||
}
|
||||
impl Eq for $type {}
|
||||
impl PartialEq for $type {
|
||||
fn eq(&self, other: &Self) -> bool { Rc::ptr_eq(&self.0, &other.0) }
|
||||
}
|
||||
impl Ord for $type {
|
||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering { self.addr().cmp(&other.addr()) }
|
||||
}
|
||||
impl PartialOrd for $type {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> { Some(self.cmp(other)) }
|
||||
}
|
||||
impl Hash for $type {
|
||||
fn hash<H: hash::Hasher>(&self, state: &mut H) { self.addr().hash(state) }
|
||||
}
|
||||
impl std::fmt::Debug for $type {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_tuple($type_name).field(&self.to_api().0).field(&self.deref()).finish()
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub struct Bimap<T: Interned> {
|
||||
intern: HashMap<Rc<T>, Tok<T>>,
|
||||
by_id: HashMap<T::Marker, Tok<T>>,
|
||||
}
|
||||
impl<T: Interned> Bimap<T> {
|
||||
pub fn insert(&mut self, token: Tok<T>) {
|
||||
self.intern.insert(token.data.clone(), token.clone());
|
||||
self.by_id.insert(token.to_api(), token);
|
||||
}
|
||||
|
||||
pub fn by_marker(&self, marker: T::Marker) -> Option<Tok<T>> { self.by_id.get(&marker).cloned() }
|
||||
|
||||
pub fn by_value<Q: Eq + hash::Hash>(&self, q: &Q) -> Option<Tok<T>>
|
||||
where T: Borrow<Q> {
|
||||
(self.intern.raw_entry())
|
||||
.from_hash(self.intern.hasher().hash_one(q), |k| k.as_ref().borrow() == q)
|
||||
.map(|p| p.1.clone())
|
||||
}
|
||||
|
||||
pub fn sweep_replica(&mut self) -> Vec<T::Marker> {
|
||||
(self.intern)
|
||||
.extract_if(|k, _| Rc::strong_count(k) == BASE_RC)
|
||||
.map(|(_, v)| {
|
||||
self.by_id.remove(&v.to_api());
|
||||
v.to_api()
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn sweep_master(&mut self, retained: HashSet<T::Marker>) {
|
||||
self.intern.retain(|k, v| BASE_RC < Rc::strong_count(k) || retained.contains(&v.to_api()))
|
||||
}
|
||||
token_def!(IStr, IStrDyn, str, api::TStr, "IStr");
|
||||
impl std::fmt::Display for IStr {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.deref()) }
|
||||
}
|
||||
|
||||
impl<T: Interned> Default for Bimap<T> {
|
||||
fn default() -> Self { Self { by_id: HashMap::new(), intern: HashMap::new() } }
|
||||
}
|
||||
token_def!(IVec, IVecDyn, [IStr], api::TVec, "IVec");
|
||||
|
||||
pub trait UpComm {
|
||||
fn up<R: Request>(&self, req: R) -> R::Response;
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct TypedInterners {
|
||||
strings: Bimap<String>,
|
||||
vecs: Bimap<Vec<Tok<String>>>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct InternerData {
|
||||
interners: Mutex<TypedInterners>,
|
||||
master: Option<Box<dyn DynRequester<Transfer = api::IntReq>>>,
|
||||
}
|
||||
#[derive(Clone, Default)]
|
||||
pub struct Interner(Rc<InternerData>);
|
||||
#[derive(Clone)]
|
||||
pub struct Interner(Rc<dyn InternerDyn>);
|
||||
impl Interner {
|
||||
pub fn new_master() -> Self { Self::default() }
|
||||
pub fn new_replica(req: impl DynRequester<Transfer = api::IntReq> + 'static) -> Self {
|
||||
Self(Rc::new(InternerData { master: Some(Box::new(req)), interners: Mutex::default() }))
|
||||
pub fn new<T: InternerDyn + 'static>(t: T) -> Self { Self(Rc::new(t) as _) }
|
||||
pub async fn is(&self, s: &(impl Borrow<str> + ?Sized)) -> IStr {
|
||||
IStr(self.0.is(s.borrow()).await)
|
||||
}
|
||||
/// Intern some data; query its identifier if not known locally
|
||||
pub async fn i<T: Interned>(&self, t: &(impl Internable<Interned = T> + ?Sized)) -> Tok<T> {
|
||||
let data = t.get_owned();
|
||||
let mut g = self.0.interners.lock().await;
|
||||
let typed = T::bimap(&mut g);
|
||||
if let Some(tok) = typed.by_value(&data) {
|
||||
return tok;
|
||||
}
|
||||
let marker = match &self.0.master {
|
||||
Some(c) => data.clone().intern(&**c).await,
|
||||
None =>
|
||||
T::Marker::from_id(NonZeroU64::new(ID.fetch_add(1, atomic::Ordering::Relaxed)).unwrap()),
|
||||
};
|
||||
let tok = Tok::new(data, marker);
|
||||
T::bimap(&mut g).insert(tok.clone());
|
||||
tok
|
||||
}
|
||||
/// Extern an identifier; query the data it represents if not known locally
|
||||
pub async fn ex<M: InternMarker>(&self, marker: M) -> Tok<M::Interned> {
|
||||
if let Some(tok) = M::Interned::bimap(&mut *self.0.interners.lock().await).by_marker(marker) {
|
||||
return tok;
|
||||
}
|
||||
assert!(self.0.master.is_some(), "ID not in local interner and this is master");
|
||||
let token = marker.resolve(self).await;
|
||||
M::Interned::bimap(&mut *self.0.interners.lock().await).insert(token.clone());
|
||||
token
|
||||
}
|
||||
pub async fn sweep_replica(&self) -> api::Retained {
|
||||
assert!(self.0.master.is_some(), "Not a replica");
|
||||
let mut g = self.0.interners.lock().await;
|
||||
api::Retained { strings: g.strings.sweep_replica(), vecs: g.vecs.sweep_replica() }
|
||||
}
|
||||
pub async fn sweep_master(&self, retained: api::Retained) {
|
||||
assert!(self.0.master.is_none(), "Not master");
|
||||
let mut g = self.0.interners.lock().await;
|
||||
g.strings.sweep_master(retained.strings.into_iter().collect());
|
||||
g.vecs.sweep_master(retained.vecs.into_iter().collect());
|
||||
pub async fn iv(&self, s: &(impl Borrow<[IStr]> + ?Sized)) -> IVec {
|
||||
IVec(self.0.iv(s.borrow()).await)
|
||||
}
|
||||
pub async fn es(&self, m: api::TStr) -> IStr { IStr(self.0.es(m).await) }
|
||||
pub async fn ev(&self, m: api::TVec) -> IVec { IVec(self.0.ev(m).await) }
|
||||
}
|
||||
impl fmt::Debug for Interner {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "Interner{{ replica: {} }}", self.0.master.is_none())
|
||||
}
|
||||
pub trait InternerDyn {
|
||||
fn is(&self, s: &str) -> LocalBoxFuture<Rc<dyn IStrDyn>>;
|
||||
fn iv(&self, v: &[IStr]) -> LocalBoxFuture<Rc<dyn IVecDyn>>;
|
||||
fn es(&self, m: api::TStr) -> LocalBoxFuture<Rc<dyn IStrDyn>>;
|
||||
fn ev(&self, m: api::TVec) -> LocalBoxFuture<Rc<dyn IVecDyn>>;
|
||||
}
|
||||
|
||||
static ID: atomic::AtomicU64 = atomic::AtomicU64::new(1);
|
||||
#[cfg(any(feature = "mocks", test))]
|
||||
pub mod test {
|
||||
|
||||
pub fn merge_retained(into: &mut api::Retained, from: &api::Retained) {
|
||||
into.strings = into.strings.iter().chain(&from.strings).copied().unique().collect();
|
||||
into.vecs = into.vecs.iter().chain(&from.vecs).copied().unique().collect();
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use std::cell::RefCell;
|
||||
use std::num::NonZero;
|
||||
use std::pin::Pin;
|
||||
|
||||
use orchid_api_traits::{Decode, enc_vec};
|
||||
use test_executors::spin_on;
|
||||
use std::sync::atomic::AtomicU64;
|
||||
|
||||
use super::*;
|
||||
use crate::api;
|
||||
use crate::testing::AsyncMonitor;
|
||||
|
||||
#[test]
|
||||
fn test_i() {
|
||||
let i = Interner::new_master();
|
||||
let _: Tok<String> = spin_on(i.i("foo"));
|
||||
let _: Tok<Vec<Tok<String>>> = spin_on(i.i(&[spin_on(i.i("bar")), spin_on(i.i("baz"))]));
|
||||
pub(crate) struct DummyIStr(NonZeroU64, String);
|
||||
impl Deref for DummyIStr {
|
||||
type Target = str;
|
||||
fn deref(&self) -> &Self::Target { &self.1 }
|
||||
}
|
||||
impl IStrDyn for DummyIStr {
|
||||
fn to_api(&self) -> api::TStr { api::TStr(self.0) }
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_coding() {
|
||||
spin_on(async {
|
||||
let coded = api::TStr(NonZero::new(3u64).unwrap());
|
||||
let mut enc = &enc_vec(&coded).await[..];
|
||||
api::TStr::decode(Pin::new(&mut enc)).await;
|
||||
assert_eq!(enc, [], "Did not consume all of {enc:?}")
|
||||
})
|
||||
pub(crate) struct DummyIStrv(NonZeroU64, Vec<IStr>);
|
||||
impl Deref for DummyIStrv {
|
||||
type Target = [IStr];
|
||||
fn deref(&self) -> &Self::Target { &self.1 }
|
||||
}
|
||||
impl IVecDyn for DummyIStrv {
|
||||
fn to_api(&self) -> api::TVec { api::TVec(self.0) }
|
||||
}
|
||||
|
||||
pub(crate) struct DummyInterner(
|
||||
RefCell<(
|
||||
HashMap<String, NonZeroU64>,
|
||||
HashMap<NonZeroU64, Rc<DummyIStr>>,
|
||||
HashMap<NonZeroU64, Rc<DummyIStrv>>,
|
||||
)>,
|
||||
AsyncMonitor<InternerEvent>,
|
||||
);
|
||||
pub enum InternerEvent {
|
||||
ExternStr(Rc<DummyIStr>),
|
||||
ExternVec(Rc<DummyIStrv>),
|
||||
InternStr { token: Rc<DummyIStr>, new: bool },
|
||||
InternVec { token: Rc<DummyIStrv>, new: bool },
|
||||
}
|
||||
impl DummyInterner {
|
||||
pub fn new(monitor: AsyncMonitor<InternerEvent>) -> Interner {
|
||||
Interner(Rc::new(Self(RefCell::default(), monitor)))
|
||||
}
|
||||
}
|
||||
impl InternerDyn for DummyInterner {
|
||||
fn es(&self, m: api::TStr) -> LocalBoxFuture<Rc<dyn IStrDyn>> {
|
||||
let state = self.0.borrow();
|
||||
let istr = state.1.get(&m.0).unwrap_or_else(|| panic!("Externed nonexistent {m:?}")).clone();
|
||||
Box::pin(async {
|
||||
self.1.notify(InternerEvent::ExternStr(istr.clone())).await;
|
||||
istr as Rc<dyn IStrDyn>
|
||||
})
|
||||
}
|
||||
fn ev(&self, m: api::TVec) -> LocalBoxFuture<Rc<dyn IVecDyn>> {
|
||||
let state = self.0.borrow();
|
||||
let ivec = state.2.get(&m.0).unwrap_or_else(|| panic!("Externed nonexistent {m:?}")).clone();
|
||||
Box::pin(async {
|
||||
self.1.notify(InternerEvent::ExternVec(ivec.clone())).await;
|
||||
ivec as Rc<dyn IVecDyn>
|
||||
})
|
||||
}
|
||||
fn is(&self, s: &str) -> LocalBoxFuture<Rc<dyn IStrDyn>> {
|
||||
let mut this = self.0.borrow_mut();
|
||||
let id = *(this.0.entry(format!("{s:?}")))
|
||||
.or_insert_with(|| NonZero::new(COUNTER.fetch_add(1, atomic::Ordering::Relaxed)).unwrap());
|
||||
let (tok, new) = match this.1.entry(id) {
|
||||
hashbrown::hash_map::Entry::Occupied(ent) => (ent.get().clone(), false),
|
||||
hashbrown::hash_map::Entry::Vacant(ent) =>
|
||||
(ent.insert(Rc::new(DummyIStr(id, s.to_string()))).clone(), true),
|
||||
};
|
||||
Box::pin(async move {
|
||||
self.1.notify(InternerEvent::InternStr { token: tok.clone(), new }).await;
|
||||
tok as _
|
||||
})
|
||||
}
|
||||
fn iv(&self, s: &[IStr]) -> LocalBoxFuture<Rc<dyn IVecDyn>> {
|
||||
let mut this = self.0.borrow_mut();
|
||||
let id = *(this.0.entry(format!("{s:?}")))
|
||||
.or_insert_with(|| NonZero::new(COUNTER.fetch_add(1, atomic::Ordering::Relaxed)).unwrap());
|
||||
let (tok, new) = match this.2.entry(id) {
|
||||
hashbrown::hash_map::Entry::Occupied(ent) => (ent.get().clone(), false),
|
||||
hashbrown::hash_map::Entry::Vacant(ent) =>
|
||||
(ent.insert(Rc::new(DummyIStrv(id, s.to_vec()))).clone(), true),
|
||||
};
|
||||
Box::pin(async move {
|
||||
self.1.notify(InternerEvent::InternVec { token: tok.clone(), new }).await;
|
||||
tok as _
|
||||
})
|
||||
}
|
||||
}
|
||||
static COUNTER: AtomicU64 = AtomicU64::new(1);
|
||||
}
|
||||
|
||||
// /// The number of references held to any token by the interner.
|
||||
// const BASE_RC: usize = 3;
|
||||
|
||||
// #[test]
|
||||
// fn base_rc_correct() {
|
||||
// let tok = Tok::new(Rc::new("foo".to_string()),
|
||||
// api::TStr(1.try_into().unwrap())); let mut bimap = Bimap::default();
|
||||
// bimap.insert(tok.clone());
|
||||
// assert_eq!(Rc::strong_count(&tok.data), BASE_RC + 1, "the bimap plus the
|
||||
// current instance"); }
|
||||
|
||||
// pub struct Bimap<K, V, Tok> {
|
||||
// intern: HashMap<V, Tok>,
|
||||
// by_id: HashMap<K, Tok>,
|
||||
// }
|
||||
// impl<K> Bimap<T> {
|
||||
// pub fn insert(&mut self, token: Tok<T>) {
|
||||
// self.intern.insert(token.data.clone(), token.clone());
|
||||
// self.by_id.insert(token.to_api(), token);
|
||||
// }
|
||||
|
||||
// pub fn by_marker(&self, marker: T::Marker) -> Option<Tok<T>> {
|
||||
// self.by_id.get(&marker).cloned() }
|
||||
|
||||
// pub fn by_value<Q: Eq + hash::Hash>(&self, q: &Q) -> Option<Tok<T>>
|
||||
// where T: Borrow<Q> {
|
||||
// (self.intern.raw_entry())
|
||||
// .from_hash(self.intern.hasher().hash_one(q), |k| k.as_ref().borrow() == q)
|
||||
// .map(|p| p.1.clone())
|
||||
// }
|
||||
|
||||
// pub fn sweep_replica(&mut self) -> Vec<T::Marker> {
|
||||
// (self.intern)
|
||||
// .extract_if(|k, _| Rc::strong_count(k) == BASE_RC)
|
||||
// .map(|(_, v)| {
|
||||
// self.by_id.remove(&v.to_api());
|
||||
// v.to_api()
|
||||
// })
|
||||
// .collect()
|
||||
// }
|
||||
|
||||
// pub fn sweep_master(&mut self, retained: HashSet<T::Marker>) {
|
||||
// self.intern.retain(|k, v| BASE_RC < Rc::strong_count(k) ||
|
||||
// retained.contains(&v.to_api())) }
|
||||
// }
|
||||
|
||||
// impl<T: Interned> Default for Bimap<T> {
|
||||
// fn default() -> Self { Self { by_id: HashMap::new(), intern: HashMap::new()
|
||||
// } } }
|
||||
|
||||
// pub trait UpComm {
|
||||
// fn up<R: Request>(&self, req: R) -> R::Response;
|
||||
// }
|
||||
|
||||
// #[derive(Default)]
|
||||
// pub struct TypedInterners {
|
||||
// strings: Bimap<String>,
|
||||
// vecs: Bimap<Vec<IStr>>,
|
||||
// }
|
||||
|
||||
// #[derive(Default)]
|
||||
// pub struct InternerData {
|
||||
// interners: Mutex<TypedInterners>,
|
||||
// master: Option<Box<dyn DynRequester<Transfer = api::IntReq>>>,
|
||||
// }
|
||||
// #[derive(Clone, Default)]
|
||||
// pub struct Interner(Rc<InternerData>);
|
||||
// impl Interner {
|
||||
// pub fn new_master() -> Self { Self::default() }
|
||||
// pub fn new_replica(req: impl DynRequester<Transfer = api::IntReq> + 'static)
|
||||
// -> Self { Self(Rc::new(InternerData { master: Some(Box::new(req)),
|
||||
// interners: Mutex::default() })) }
|
||||
// /// Intern some data; query its identifier if not known locally
|
||||
// pub async fn i<T: Interned>(&self, t: &(impl Internable<Interned = T> +
|
||||
// ?Sized)) -> Tok<T> { let data = t.get_owned();
|
||||
// let mut g = self.0.interners.lock().await;
|
||||
// let typed = T::bimap(&mut g);
|
||||
// if let Some(tok) = typed.by_value(&data) {
|
||||
// return tok;
|
||||
// }
|
||||
// let marker = match &self.0.master {
|
||||
// Some(c) => data.clone().intern(&**c).await,
|
||||
// None =>
|
||||
// T::Marker::from_id(NonZeroU64::new(ID.fetch_add(1,
|
||||
// atomic::Ordering::Relaxed)).unwrap()), };
|
||||
// let tok = Tok::new(data, marker);
|
||||
// T::bimap(&mut g).insert(tok.clone());
|
||||
// tok
|
||||
// }
|
||||
// /// Extern an identifier; query the data it represents if not known locally
|
||||
// pub async fn ex<M: InternMarker>(&self, marker: M) -> Tok<M::Interned> {
|
||||
// if let Some(tok) = M::Interned::bimap(&mut
|
||||
// *self.0.interners.lock().await).by_marker(marker) { return tok;
|
||||
// }
|
||||
// assert!(self.0.master.is_some(), "ID not in local interner and this is
|
||||
// master"); let token = marker.resolve(self).await;
|
||||
// M::Interned::bimap(&mut
|
||||
// *self.0.interners.lock().await).insert(token.clone()); token
|
||||
// }
|
||||
// pub async fn sweep_replica(&self) -> api::Retained {
|
||||
// assert!(self.0.master.is_some(), "Not a replica");
|
||||
// let mut g = self.0.interners.lock().await;
|
||||
// api::Retained { strings: g.strings.sweep_replica(), vecs:
|
||||
// g.vecs.sweep_replica() } }
|
||||
// pub async fn sweep_master(&self, retained: api::Retained) {
|
||||
// assert!(self.0.master.is_none(), "Not master");
|
||||
// let mut g = self.0.interners.lock().await;
|
||||
// g.strings.sweep_master(retained.strings.into_iter().collect());
|
||||
// g.vecs.sweep_master(retained.vecs.into_iter().collect());
|
||||
// }
|
||||
// }
|
||||
// impl fmt::Debug for Interner {
|
||||
// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
// write!(f, "Interner{{ replica: {} }}", self.0.master.is_none())
|
||||
// }
|
||||
// }
|
||||
|
||||
// static ID: atomic::AtomicU64 = atomic::AtomicU64::new(1);
|
||||
|
||||
// pub fn merge_retained(into: &mut api::Retained, from: &api::Retained) {
|
||||
// into.strings =
|
||||
// into.strings.iter().chain(&from.strings).copied().unique().collect();
|
||||
// into.vecs = into.vecs.iter().chain(&from.vecs).copied().unique().collect();
|
||||
// }
|
||||
|
||||
// #[cfg(test)]
|
||||
// mod test {
|
||||
// use std::num::NonZero;
|
||||
// use std::pin::Pin;
|
||||
|
||||
// use orchid_api_traits::{Decode, enc_vec};
|
||||
// use test_executors::spin_on;
|
||||
|
||||
// use super::*;
|
||||
// use crate::api;
|
||||
|
||||
// #[test]
|
||||
// fn test_i() {
|
||||
// let i = Interner::new_master();
|
||||
// let _: IStr = spin_on(i.i("foo"));
|
||||
// let _: Tok<Vec<IStr>> = spin_on(i.i(&[spin_on(i.i("bar")),
|
||||
// spin_on(i.i("baz"))])); }
|
||||
|
||||
// #[test]
|
||||
// fn test_coding() {
|
||||
// spin_on(async {
|
||||
// let coded = api::TStr(NonZero::new(3u64).unwrap());
|
||||
// let mut enc = &enc_vec(&coded).await[..];
|
||||
// api::TStr::decode(Pin::new(&mut enc)).await;
|
||||
// assert_eq!(enc, [], "Did not consume all of {enc:?}")
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
|
||||
@@ -7,6 +7,7 @@ pub mod builtin;
|
||||
pub mod char_filter;
|
||||
pub mod clone;
|
||||
pub mod combine;
|
||||
pub mod ctx;
|
||||
pub mod error;
|
||||
pub mod event;
|
||||
pub mod format;
|
||||
@@ -25,6 +26,7 @@ pub mod pure_seq;
|
||||
pub mod reqnot;
|
||||
pub mod sequence;
|
||||
pub mod side;
|
||||
pub mod testing;
|
||||
mod tl_cache;
|
||||
pub mod tokens;
|
||||
pub mod tree;
|
||||
|
||||
@@ -7,12 +7,12 @@ use std::ops::Range;
|
||||
use trait_set::trait_set;
|
||||
|
||||
use crate::error::ErrPos;
|
||||
use crate::interner::{Interner, Tok};
|
||||
use crate::interner::{IStr, Interner};
|
||||
use crate::name::Sym;
|
||||
use crate::{api, match_mapping, sym};
|
||||
|
||||
trait_set! {
|
||||
pub trait GetSrc = FnMut(&Sym) -> Tok<String>;
|
||||
pub trait GetSrc = FnMut(&Sym) -> IStr;
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
@@ -131,24 +131,21 @@ pub struct CodeGenInfo {
|
||||
/// formatted like a Rust namespace
|
||||
pub generator: Sym,
|
||||
/// Unformatted user message with relevant circumstances and parameters
|
||||
pub details: Tok<String>,
|
||||
pub details: IStr,
|
||||
}
|
||||
impl CodeGenInfo {
|
||||
/// A codegen marker with no user message and parameters
|
||||
pub async fn new_short(generator: Sym, i: &Interner) -> Self {
|
||||
Self { generator, details: i.i("").await }
|
||||
Self { generator, details: i.is("").await }
|
||||
}
|
||||
/// A codegen marker with a user message or parameters
|
||||
pub async fn new_details(generator: Sym, details: impl AsRef<str>, i: &Interner) -> Self {
|
||||
Self { generator, details: i.i(details.as_ref()).await }
|
||||
Self { generator, details: i.is(details.as_ref()).await }
|
||||
}
|
||||
/// Syntactic location
|
||||
pub fn pos(&self) -> Pos { Pos::Gen(self.clone()) }
|
||||
pub async fn from_api(api: &api::CodeGenInfo, i: &Interner) -> Self {
|
||||
Self {
|
||||
generator: Sym::from_api(api.generator, i).await,
|
||||
details: Tok::from_api(api.details, i).await,
|
||||
}
|
||||
Self { generator: Sym::from_api(api.generator, i).await, details: i.es(api.details).await }
|
||||
}
|
||||
pub fn to_api(&self) -> api::CodeGenInfo {
|
||||
api::CodeGenInfo { generator: self.generator.to_api(), details: self.details.to_api() }
|
||||
|
||||
@@ -12,52 +12,48 @@ use itertools::Itertools;
|
||||
use trait_set::trait_set;
|
||||
|
||||
use crate::api;
|
||||
use crate::interner::{InternMarker, Interner, Tok};
|
||||
use crate::interner::{IStr, IVec, Interner};
|
||||
|
||||
trait_set! {
|
||||
/// Traits that all name iterators should implement
|
||||
pub trait NameIter = Iterator<Item = Tok<String>> + DoubleEndedIterator + ExactSizeIterator;
|
||||
pub trait NameIter = Iterator<Item = IStr> + DoubleEndedIterator + ExactSizeIterator;
|
||||
}
|
||||
|
||||
/// A token path which may be empty. [VName] is the non-empty version
|
||||
#[derive(Clone, Default, Hash, PartialEq, Eq)]
|
||||
pub struct VPath(Vec<Tok<String>>);
|
||||
pub struct VPath(Vec<IStr>);
|
||||
impl VPath {
|
||||
/// Collect segments into a vector
|
||||
pub fn new(items: impl IntoIterator<Item = Tok<String>>) -> Self {
|
||||
Self(items.into_iter().collect())
|
||||
}
|
||||
pub fn new(items: impl IntoIterator<Item = IStr>) -> Self { Self(items.into_iter().collect()) }
|
||||
/// Number of path segments
|
||||
pub fn len(&self) -> usize { self.0.len() }
|
||||
/// Whether there are any path segments. In other words, whether this is a
|
||||
/// valid name
|
||||
pub fn is_empty(&self) -> bool { self.len() == 0 }
|
||||
/// Prepend some tokens to the path
|
||||
pub fn prefix(self, items: impl IntoIterator<Item = Tok<String>>) -> Self {
|
||||
pub fn prefix(self, items: impl IntoIterator<Item = IStr>) -> Self {
|
||||
Self(items.into_iter().chain(self.0).collect())
|
||||
}
|
||||
/// Append some tokens to the path
|
||||
pub fn suffix(self, items: impl IntoIterator<Item = Tok<String>>) -> Self {
|
||||
pub fn suffix(self, items: impl IntoIterator<Item = IStr>) -> Self {
|
||||
Self(self.0.into_iter().chain(items).collect())
|
||||
}
|
||||
/// Partition the string by `::` namespace separators
|
||||
pub async fn parse(s: &str, i: &Interner) -> Self {
|
||||
Self(if s.is_empty() { vec![] } else { join_all(s.split("::").map(|s| i.i(s))).await })
|
||||
Self(if s.is_empty() { vec![] } else { join_all(s.split("::").map(|s| i.is(s))).await })
|
||||
}
|
||||
/// Walk over the segments
|
||||
pub fn str_iter(&self) -> impl Iterator<Item = &'_ str> {
|
||||
Box::new(self.0.iter().map(|s| s.as_str()))
|
||||
}
|
||||
pub fn str_iter(&self) -> impl Iterator<Item = &'_ str> { self.0.iter().map(|s| s.as_ref()) }
|
||||
/// Try to convert into non-empty version
|
||||
pub fn into_name(self) -> Result<VName, EmptyNameError> { VName::new(self.0) }
|
||||
/// Add a token to the path. Since now we know that it can't be empty, turn it
|
||||
/// into a name.
|
||||
pub fn name_with_suffix(self, name: Tok<String>) -> VName {
|
||||
pub fn name_with_suffix(self, name: IStr) -> VName {
|
||||
VName(self.into_iter().chain([name]).collect())
|
||||
}
|
||||
/// Add a token to the beginning of the. Since now we know that it can't be
|
||||
/// empty, turn it into a name.
|
||||
pub fn name_with_prefix(self, name: Tok<String>) -> VName {
|
||||
pub fn name_with_prefix(self, name: IStr) -> VName {
|
||||
VName([name].into_iter().chain(self).collect())
|
||||
}
|
||||
|
||||
@@ -65,7 +61,7 @@ impl VPath {
|
||||
pub async fn from_path(path: &Path, ext: &str, i: &Interner) -> Option<(Self, bool)> {
|
||||
async fn to_vpath(p: &Path, i: &Interner) -> Option<VPath> {
|
||||
let tok_opt_v =
|
||||
join_all(p.iter().map(|c| OptionFuture::from(c.to_str().map(|s| i.i(s))))).await;
|
||||
join_all(p.iter().map(|c| OptionFuture::from(c.to_str().map(|s| i.is(s))))).await;
|
||||
tok_opt_v.into_iter().collect::<Option<_>>().map(VPath)
|
||||
}
|
||||
match path.extension().map(|s| s.to_str()) {
|
||||
@@ -83,30 +79,28 @@ impl fmt::Display for VPath {
|
||||
write!(f, "{}", self.str_iter().join("::"))
|
||||
}
|
||||
}
|
||||
impl FromIterator<Tok<String>> for VPath {
|
||||
fn from_iter<T: IntoIterator<Item = Tok<String>>>(iter: T) -> Self {
|
||||
Self(iter.into_iter().collect())
|
||||
}
|
||||
impl FromIterator<IStr> for VPath {
|
||||
fn from_iter<T: IntoIterator<Item = IStr>>(iter: T) -> Self { Self(iter.into_iter().collect()) }
|
||||
}
|
||||
impl IntoIterator for VPath {
|
||||
type Item = Tok<String>;
|
||||
type Item = IStr;
|
||||
type IntoIter = vec::IntoIter<Self::Item>;
|
||||
fn into_iter(self) -> Self::IntoIter { self.0.into_iter() }
|
||||
}
|
||||
impl Borrow<[Tok<String>]> for VPath {
|
||||
fn borrow(&self) -> &[Tok<String>] { &self.0[..] }
|
||||
impl Borrow<[IStr]> for VPath {
|
||||
fn borrow(&self) -> &[IStr] { &self.0[..] }
|
||||
}
|
||||
impl Deref for VPath {
|
||||
type Target = [Tok<String>];
|
||||
type Target = [IStr];
|
||||
fn deref(&self) -> &Self::Target { self.borrow() }
|
||||
}
|
||||
|
||||
impl<T> Index<T> for VPath
|
||||
where [Tok<String>]: Index<T>
|
||||
where [IStr]: Index<T>
|
||||
{
|
||||
type Output = <[Tok<String>] as Index<T>>::Output;
|
||||
type Output = <[IStr] as Index<T>>::Output;
|
||||
|
||||
fn index(&self, index: T) -> &Self::Output { &Borrow::<[Tok<String>]>::borrow(self)[index] }
|
||||
fn index(&self, index: T) -> &Self::Output { &Borrow::<[IStr]>::borrow(self)[index] }
|
||||
}
|
||||
|
||||
/// A mutable representation of a namespaced identifier of at least one segment.
|
||||
@@ -116,11 +110,11 @@ where [Tok<String>]: Index<T>
|
||||
/// See also [Sym] for the immutable representation, and [VPath] for possibly
|
||||
/// empty values
|
||||
#[derive(Clone, Hash, PartialEq, Eq)]
|
||||
pub struct VName(Vec<Tok<String>>);
|
||||
pub struct VName(Vec<IStr>);
|
||||
impl VName {
|
||||
/// Assert that the sequence isn't empty and wrap it in [VName] to represent
|
||||
/// this invariant
|
||||
pub fn new(items: impl IntoIterator<Item = Tok<String>>) -> Result<Self, EmptyNameError> {
|
||||
pub fn new(items: impl IntoIterator<Item = IStr>) -> Result<Self, EmptyNameError> {
|
||||
let data: Vec<_> = items.into_iter().collect();
|
||||
if data.is_empty() { Err(EmptyNameError) } else { Ok(Self(data)) }
|
||||
}
|
||||
@@ -128,27 +122,27 @@ impl VName {
|
||||
name: impl IntoIterator<Item = api::TStr>,
|
||||
i: &Interner,
|
||||
) -> Result<Self, EmptyNameError> {
|
||||
Self::new(join_all(name.into_iter().map(|m| Tok::from_api(m, i))).await)
|
||||
Self::new(join_all(name.into_iter().map(|m| i.es(m))).await)
|
||||
}
|
||||
/// Unwrap the enclosed vector
|
||||
pub fn into_vec(self) -> Vec<Tok<String>> { self.0 }
|
||||
pub fn into_vec(self) -> Vec<IStr> { self.0 }
|
||||
/// Get a reference to the enclosed vector
|
||||
pub fn vec(&self) -> &Vec<Tok<String>> { &self.0 }
|
||||
pub fn vec(&self) -> &Vec<IStr> { &self.0 }
|
||||
/// Mutable access to the underlying vector. To ensure correct results, this
|
||||
/// must never be empty.
|
||||
pub fn vec_mut(&mut self) -> &mut Vec<Tok<String>> { &mut self.0 }
|
||||
pub fn vec_mut(&mut self) -> &mut Vec<IStr> { &mut self.0 }
|
||||
/// Intern the name and return a [Sym]
|
||||
pub async fn to_sym(&self, i: &Interner) -> Sym { Sym(i.i(&self.0[..]).await) }
|
||||
pub async fn to_sym(&self, i: &Interner) -> Sym { Sym(i.iv(&self.0[..]).await) }
|
||||
/// If this name has only one segment, return it
|
||||
pub fn as_root(&self) -> Option<Tok<String>> { self.0.iter().exactly_one().ok().cloned() }
|
||||
pub fn as_root(&self) -> Option<IStr> { self.0.iter().exactly_one().ok().cloned() }
|
||||
/// Prepend the segments to this name
|
||||
#[must_use = "This is a pure function"]
|
||||
pub fn prefix(self, items: impl IntoIterator<Item = Tok<String>>) -> Self {
|
||||
pub fn prefix(self, items: impl IntoIterator<Item = IStr>) -> Self {
|
||||
Self(items.into_iter().chain(self.0).collect())
|
||||
}
|
||||
/// Append the segments to this name
|
||||
#[must_use = "This is a pure function"]
|
||||
pub fn suffix(self, items: impl IntoIterator<Item = Tok<String>>) -> Self {
|
||||
pub fn suffix(self, items: impl IntoIterator<Item = IStr>) -> Self {
|
||||
Self(self.0.into_iter().chain(items).collect())
|
||||
}
|
||||
/// Read a `::` separated namespaced name
|
||||
@@ -159,7 +153,7 @@ impl VName {
|
||||
Self::parse(s, i).await.expect("empty literal !?")
|
||||
}
|
||||
/// Obtain an iterator over the segments of the name
|
||||
pub fn iter(&self) -> impl Iterator<Item = Tok<String>> + '_ { self.0.iter().cloned() }
|
||||
pub fn iter(&self) -> impl Iterator<Item = IStr> + '_ { self.0.iter().cloned() }
|
||||
}
|
||||
impl fmt::Debug for VName {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "VName({self})") }
|
||||
@@ -170,22 +164,22 @@ impl fmt::Display for VName {
|
||||
}
|
||||
}
|
||||
impl IntoIterator for VName {
|
||||
type Item = Tok<String>;
|
||||
type Item = IStr;
|
||||
type IntoIter = vec::IntoIter<Self::Item>;
|
||||
fn into_iter(self) -> Self::IntoIter { self.0.into_iter() }
|
||||
}
|
||||
impl<T> Index<T> for VName
|
||||
where [Tok<String>]: Index<T>
|
||||
where [IStr]: Index<T>
|
||||
{
|
||||
type Output = <[Tok<String>] as Index<T>>::Output;
|
||||
type Output = <[IStr] as Index<T>>::Output;
|
||||
|
||||
fn index(&self, index: T) -> &Self::Output { &self.deref()[index] }
|
||||
}
|
||||
impl Borrow<[Tok<String>]> for VName {
|
||||
fn borrow(&self) -> &[Tok<String>] { self.0.borrow() }
|
||||
impl Borrow<[IStr]> for VName {
|
||||
fn borrow(&self) -> &[IStr] { self.0.borrow() }
|
||||
}
|
||||
impl Deref for VName {
|
||||
type Target = [Tok<String>];
|
||||
type Target = [IStr];
|
||||
fn deref(&self) -> &Self::Target { self.borrow() }
|
||||
}
|
||||
|
||||
@@ -193,11 +187,9 @@ impl Deref for VName {
|
||||
/// empty sequence
|
||||
#[derive(Debug, Copy, Clone, Default, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct EmptyNameError;
|
||||
impl TryFrom<&[Tok<String>]> for VName {
|
||||
impl TryFrom<&[IStr]> for VName {
|
||||
type Error = EmptyNameError;
|
||||
fn try_from(value: &[Tok<String>]) -> Result<Self, Self::Error> {
|
||||
Self::new(value.iter().cloned())
|
||||
}
|
||||
fn try_from(value: &[IStr]) -> Result<Self, Self::Error> { Self::new(value.iter().cloned()) }
|
||||
}
|
||||
|
||||
/// An interned representation of a namespaced identifier.
|
||||
@@ -206,36 +198,36 @@ impl TryFrom<&[Tok<String>]> for VName {
|
||||
///
|
||||
/// See also [VName]
|
||||
#[derive(Clone, Hash, PartialEq, Eq)]
|
||||
pub struct Sym(Tok<Vec<Tok<String>>>);
|
||||
pub struct Sym(IVec);
|
||||
impl Sym {
|
||||
/// Assert that the sequence isn't empty, intern it and wrap it in a [Sym] to
|
||||
/// represent this invariant
|
||||
pub async fn new(
|
||||
v: impl IntoIterator<Item = Tok<String>>,
|
||||
v: impl IntoIterator<Item = IStr>,
|
||||
i: &Interner,
|
||||
) -> Result<Self, EmptyNameError> {
|
||||
let items = v.into_iter().collect_vec();
|
||||
Self::from_tok(i.i(&items).await)
|
||||
Self::from_tok(i.iv(&items).await)
|
||||
}
|
||||
/// Read a `::` separated namespaced name.
|
||||
pub async fn parse(s: &str, i: &Interner) -> Result<Self, EmptyNameError> {
|
||||
Ok(Sym(i.i(&VName::parse(s, i).await?.into_vec()).await))
|
||||
Ok(Sym(i.iv(&VName::parse(s, i).await?.into_vec()).await))
|
||||
}
|
||||
/// Assert that a token isn't empty, and wrap it in a [Sym]
|
||||
pub fn from_tok(t: Tok<Vec<Tok<String>>>) -> Result<Self, EmptyNameError> {
|
||||
pub fn from_tok(t: IVec) -> Result<Self, EmptyNameError> {
|
||||
if t.is_empty() { Err(EmptyNameError) } else { Ok(Self(t)) }
|
||||
}
|
||||
/// Grab the interner token
|
||||
pub fn tok(&self) -> Tok<Vec<Tok<String>>> { self.0.clone() }
|
||||
pub fn tok(&self) -> IVec { self.0.clone() }
|
||||
/// Get a number unique to this name suitable for arbitrary ordering.
|
||||
pub fn id(&self) -> NonZeroU64 { self.0.to_api().get_id() }
|
||||
pub fn id(&self) -> NonZeroU64 { self.0.to_api().0 }
|
||||
/// Extern the sym for editing
|
||||
pub fn to_vname(&self) -> VName { VName(self[..].to_vec()) }
|
||||
pub async fn from_api(marker: api::TStrv, i: &Interner) -> Sym {
|
||||
Self::from_tok(Tok::from_api(marker, i).await).expect("Empty sequence found for serialized Sym")
|
||||
pub async fn from_api(marker: api::TVec, i: &Interner) -> Sym {
|
||||
Self::from_tok(i.ev(marker).await).expect("Empty sequence found for serialized Sym")
|
||||
}
|
||||
pub fn to_api(&self) -> api::TStrv { self.tok().to_api() }
|
||||
pub async fn suffix(&self, tokv: impl IntoIterator<Item = Tok<String>>, i: &Interner) -> Sym {
|
||||
pub fn to_api(&self) -> api::TVec { self.tok().to_api() }
|
||||
pub async fn suffix(&self, tokv: impl IntoIterator<Item = IStr>, i: &Interner) -> Sym {
|
||||
Self::new(self.0.iter().cloned().chain(tokv), i).await.unwrap()
|
||||
}
|
||||
}
|
||||
@@ -248,17 +240,17 @@ impl fmt::Display for Sym {
|
||||
}
|
||||
}
|
||||
impl<T> Index<T> for Sym
|
||||
where [Tok<String>]: Index<T>
|
||||
where [IStr]: Index<T>
|
||||
{
|
||||
type Output = <[Tok<String>] as Index<T>>::Output;
|
||||
type Output = <[IStr] as Index<T>>::Output;
|
||||
|
||||
fn index(&self, index: T) -> &Self::Output { &self.deref()[index] }
|
||||
}
|
||||
impl Borrow<[Tok<String>]> for Sym {
|
||||
fn borrow(&self) -> &[Tok<String>] { &self.0[..] }
|
||||
impl Borrow<[IStr]> for Sym {
|
||||
fn borrow(&self) -> &[IStr] { &self.0[..] }
|
||||
}
|
||||
impl Deref for Sym {
|
||||
type Target = [Tok<String>];
|
||||
type Target = [IStr];
|
||||
fn deref(&self) -> &Self::Target { self.borrow() }
|
||||
}
|
||||
|
||||
@@ -266,15 +258,15 @@ impl Deref for Sym {
|
||||
/// handled together in datastructures. The names can never be empty
|
||||
#[allow(clippy::len_without_is_empty)] // never empty
|
||||
pub trait NameLike:
|
||||
'static + Clone + Eq + Hash + fmt::Debug + fmt::Display + Borrow<[Tok<String>]>
|
||||
'static + Clone + Eq + Hash + fmt::Debug + fmt::Display + Borrow<[IStr]>
|
||||
{
|
||||
/// Convert into held slice
|
||||
fn as_slice(&self) -> &[Tok<String>] { Borrow::<[Tok<String>]>::borrow(self) }
|
||||
fn as_slice(&self) -> &[IStr] { Borrow::<[IStr]>::borrow(self) }
|
||||
/// Get iterator over tokens
|
||||
fn segs(&self) -> impl NameIter + '_ { self.as_slice().iter().cloned() }
|
||||
/// Get iterator over string segments
|
||||
fn str_iter(&self) -> impl Iterator<Item = &'_ str> + '_ {
|
||||
self.as_slice().iter().map(|t| t.as_str())
|
||||
self.as_slice().iter().map(|t| t.as_ref())
|
||||
}
|
||||
/// Fully resolve the name for printing
|
||||
#[must_use]
|
||||
@@ -286,19 +278,19 @@ pub trait NameLike:
|
||||
NonZeroUsize::try_from(self.segs().count()).expect("NameLike never empty")
|
||||
}
|
||||
/// Like slice's `split_first` except we know that it always returns Some
|
||||
fn split_first_seg(&self) -> (Tok<String>, &[Tok<String>]) {
|
||||
fn split_first_seg(&self) -> (IStr, &[IStr]) {
|
||||
let (foot, torso) = self.as_slice().split_last().expect("NameLike never empty");
|
||||
(foot.clone(), torso)
|
||||
}
|
||||
/// Like slice's `split_last` except we know that it always returns Some
|
||||
fn split_last_seg(&self) -> (Tok<String>, &[Tok<String>]) {
|
||||
fn split_last_seg(&self) -> (IStr, &[IStr]) {
|
||||
let (foot, torso) = self.as_slice().split_last().expect("NameLike never empty");
|
||||
(foot.clone(), torso)
|
||||
}
|
||||
/// Get the first element
|
||||
fn first_seg(&self) -> Tok<String> { self.split_first_seg().0 }
|
||||
fn first_seg(&self) -> IStr { self.split_first_seg().0 }
|
||||
/// Get the last element
|
||||
fn last_seg(&self) -> Tok<String> { self.split_last_seg().0 }
|
||||
fn last_seg(&self) -> IStr { self.split_last_seg().0 }
|
||||
}
|
||||
|
||||
impl NameLike for Sym {}
|
||||
@@ -313,9 +305,9 @@ impl NameLike for VName {}
|
||||
macro_rules! sym {
|
||||
($seg1:tt $( :: $seg:tt)* ; $i:expr) => { async {
|
||||
$crate::name::Sym::from_tok(
|
||||
$i.i(&[
|
||||
$i.i(stringify!($seg1)).await
|
||||
$( , $i.i(stringify!($seg)).await )*
|
||||
$i.iv(&[
|
||||
$i.is(stringify!($seg1)).await
|
||||
$( , $i.is(stringify!($seg)).await )*
|
||||
])
|
||||
.await
|
||||
).unwrap()
|
||||
@@ -331,8 +323,8 @@ macro_rules! sym {
|
||||
macro_rules! vname {
|
||||
($seg1:tt $( :: $seg:tt)* ; $i:expr) => { async {
|
||||
$crate::name::VName::new([
|
||||
$i.i(stringify!($seg1)).await
|
||||
$( , $i.i(stringify!($seg)).await )*
|
||||
$i.is(stringify!($seg1)).await
|
||||
$( , $i.is(stringify!($seg)).await )*
|
||||
]).unwrap()
|
||||
} };
|
||||
}
|
||||
@@ -344,8 +336,8 @@ macro_rules! vname {
|
||||
macro_rules! vpath {
|
||||
($seg1:tt $( :: $seg:tt)+ ; $i:expr) => { async {
|
||||
$crate::name::VPath(vec![
|
||||
$i.i(stringify!($seg1)).await
|
||||
$( , $i.i(stringify!($seg)).await )+
|
||||
$i.is(stringify!($seg1)).await
|
||||
$( , $i.is(stringify!($seg)).await )+
|
||||
])
|
||||
} };
|
||||
() => {
|
||||
@@ -360,35 +352,37 @@ mod test {
|
||||
use test_executors::spin_on;
|
||||
|
||||
use super::{NameLike, Sym, VName};
|
||||
use crate::interner::{Interner, Tok};
|
||||
use crate::interner::test::DummyInterner;
|
||||
use crate::interner::{IStr, Interner};
|
||||
use crate::name::VPath;
|
||||
use crate::testing::AsyncMonitor;
|
||||
|
||||
#[test]
|
||||
fn recur() {
|
||||
spin_on(async {
|
||||
let i = Interner::new_master();
|
||||
let i = DummyInterner::new(AsyncMonitor::default());
|
||||
let myname = vname!(foo::bar; i).await;
|
||||
let _borrowed_slice: &[Tok<String>] = myname.borrow();
|
||||
let _deref_pathslice: &[Tok<String>] = &myname;
|
||||
let _as_slice_out: &[Tok<String>] = myname.as_slice();
|
||||
let _borrowed_slice: &[IStr] = myname.borrow();
|
||||
let _deref_pathslice: &[IStr] = &myname;
|
||||
let _as_slice_out: &[IStr] = myname.as_slice();
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn literals() {
|
||||
spin_on(async {
|
||||
let i = Interner::new_master();
|
||||
let i = DummyInterner::new(AsyncMonitor::default());
|
||||
assert_eq!(
|
||||
sym!(foo::bar::baz; i).await,
|
||||
Sym::new([i.i("foo").await, i.i("bar").await, i.i("baz").await], &i).await.unwrap()
|
||||
Sym::new([i.is("foo").await, i.is("bar").await, i.is("baz").await], &i).await.unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
vname!(foo::bar::baz; i).await,
|
||||
VName::new([i.i("foo").await, i.i("bar").await, i.i("baz").await]).unwrap()
|
||||
VName::new([i.is("foo").await, i.is("bar").await, i.is("baz").await]).unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
vpath!(foo::bar::baz; i).await,
|
||||
VPath::new([i.i("foo").await, i.i("bar").await, i.i("baz").await])
|
||||
VPath::new([i.is("foo").await, i.is("bar").await, i.is("baz").await])
|
||||
);
|
||||
})
|
||||
}
|
||||
|
||||
@@ -3,8 +3,8 @@ use std::ops::Range;
|
||||
|
||||
use ordered_float::NotNan;
|
||||
|
||||
use crate::error::{OrcErrv, mk_errv};
|
||||
use crate::interner::Interner;
|
||||
use crate::ctx::Ctx;
|
||||
use crate::error::OrcErr;
|
||||
use crate::location::SrcRange;
|
||||
use crate::name::Sym;
|
||||
|
||||
@@ -55,14 +55,14 @@ pub struct NumError {
|
||||
pub kind: NumErrorKind,
|
||||
}
|
||||
|
||||
pub async fn num_to_errv(
|
||||
pub fn num_to_errv(
|
||||
NumError { kind, range }: NumError,
|
||||
offset: u32,
|
||||
source: &Sym,
|
||||
i: &Interner,
|
||||
) -> OrcErrv {
|
||||
mk_errv(
|
||||
i.i("Failed to parse number").await,
|
||||
ctx: &Ctx,
|
||||
) -> OrcErr {
|
||||
ctx.mk_err(
|
||||
"Failed to parse number",
|
||||
match kind {
|
||||
NumErrorKind::NaN => "NaN emerged during parsing",
|
||||
NumErrorKind::InvalidDigit => "non-digit character encountered",
|
||||
|
||||
@@ -1,34 +1,21 @@
|
||||
use std::fmt::{self, Display};
|
||||
use std::iter;
|
||||
use std::ops::Deref;
|
||||
use std::rc::Rc;
|
||||
|
||||
use futures::FutureExt;
|
||||
use futures::future::join_all;
|
||||
use itertools::Itertools;
|
||||
|
||||
use crate::api;
|
||||
use crate::error::{OrcErrv, OrcRes, Reporter, mk_errv};
|
||||
use crate::ctx::Ctx;
|
||||
use crate::error::{OrcErr, OrcRes, Reporter};
|
||||
use crate::format::{FmtCtx, FmtUnit, Format, fmt};
|
||||
use crate::interner::{Interner, Tok};
|
||||
use crate::interner::{IStr, Interner};
|
||||
use crate::location::SrcRange;
|
||||
use crate::name::{Sym, VName, VPath};
|
||||
use crate::tree::{ExprRepr, ExtraTok, Paren, TokTree, Token, ttv_fmt, ttv_range};
|
||||
|
||||
pub trait ParseCtx {
|
||||
#[must_use]
|
||||
fn i(&self) -> &Interner;
|
||||
#[must_use]
|
||||
fn rep(&self) -> &Reporter;
|
||||
}
|
||||
pub struct ParseCtxImpl<'a> {
|
||||
pub i: &'a Interner,
|
||||
pub r: &'a Reporter,
|
||||
}
|
||||
impl ParseCtx for ParseCtxImpl<'_> {
|
||||
fn i(&self) -> &Interner { self.i }
|
||||
fn rep(&self) -> &Reporter { self.r }
|
||||
}
|
||||
|
||||
pub fn name_start(c: char) -> bool { c.is_alphabetic() || c == '_' }
|
||||
pub fn name_char(c: char) -> bool { name_start(c) || c.is_numeric() }
|
||||
pub fn op_char(c: char) -> bool { !name_char(c) && !c.is_whitespace() && !"()[]{}\\".contains(c) }
|
||||
@@ -103,22 +90,22 @@ impl<A: ExprRepr, X: ExtraTok> Format for Snippet<'_, A, X> {
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Comment {
|
||||
pub text: Tok<String>,
|
||||
pub text: IStr,
|
||||
pub sr: SrcRange,
|
||||
}
|
||||
impl Comment {
|
||||
// XXX: which of these four are actually used?
|
||||
pub async fn from_api(c: &api::Comment, src: Sym, i: &Interner) -> Self {
|
||||
Self { text: i.ex(c.text).await, sr: SrcRange::new(c.range.clone(), &src) }
|
||||
pub async fn from_api(c: &api::Comment, src: Sym, cx: &Ctx) -> Self {
|
||||
Self { text: cx.i().es(c.text).await, sr: SrcRange::new(c.range.clone(), &src) }
|
||||
}
|
||||
pub async fn from_tk(tk: &TokTree<impl ExprRepr, impl ExtraTok>, i: &Interner) -> Option<Self> {
|
||||
pub async fn from_tk(tk: &TokTree<impl ExprRepr, impl ExtraTok>, cx: &Ctx) -> Option<Self> {
|
||||
match &tk.tok {
|
||||
Token::Comment(text) => Some(Self { text: i.i(&**text).await, sr: tk.sr.clone() }),
|
||||
Token::Comment(text) => Some(Self { text: cx.i().is(&**text).await, sr: tk.sr.clone() }),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn to_tk<R: ExprRepr, X: ExtraTok>(&self) -> TokTree<R, X> {
|
||||
TokTree { tok: Token::Comment(self.text.rc().clone()), sr: self.sr.clone() }
|
||||
pub fn to_tk<A: ExprRepr, X: ExtraTok>(&self) -> TokTree<A, X> {
|
||||
TokTree { tok: Token::Comment(Rc::new(self.text.to_string())), sr: self.sr.clone() }
|
||||
}
|
||||
pub fn to_api(&self) -> api::Comment {
|
||||
api::Comment { range: self.sr.range(), text: self.text.to_api() }
|
||||
@@ -130,7 +117,7 @@ impl fmt::Display for Comment {
|
||||
}
|
||||
|
||||
pub async fn line_items<'a, A: ExprRepr, X: ExtraTok>(
|
||||
ctx: &impl ParseCtx,
|
||||
ctx: &Ctx,
|
||||
snip: Snippet<'a, A, X>,
|
||||
) -> Vec<Parsed<'a, Vec<Comment>, A, X>> {
|
||||
let mut items = Vec::new();
|
||||
@@ -146,7 +133,7 @@ pub async fn line_items<'a, A: ExprRepr, X: ExtraTok>(
|
||||
Some(i) => {
|
||||
let (cmts, tail) = line.split_at(i);
|
||||
let comments = join_all(comments.drain(..).chain(cmts.cur).map(|t| async {
|
||||
Comment::from_tk(t, ctx.i()).await.expect("All are comments checked above")
|
||||
Comment::from_tk(t, ctx).await.expect("All are comments checked above")
|
||||
}))
|
||||
.await;
|
||||
items.push(Parsed { output: comments, tail });
|
||||
@@ -157,56 +144,50 @@ pub async fn line_items<'a, A: ExprRepr, X: ExtraTok>(
|
||||
}
|
||||
|
||||
pub async fn try_pop_no_fluff<'a, A: ExprRepr, X: ExtraTok>(
|
||||
ctx: &impl ParseCtx,
|
||||
ctx: &Ctx,
|
||||
snip: Snippet<'a, A, X>,
|
||||
) -> ParseRes<'a, &'a TokTree<A, X>, A, X> {
|
||||
match snip.skip_fluff().pop_front() {
|
||||
Some((output, tail)) => Ok(Parsed { output, tail }),
|
||||
None => Err(mk_errv(
|
||||
ctx.i().i("Unexpected end").await,
|
||||
"Line ends abruptly; more tokens were expected",
|
||||
[snip.sr()],
|
||||
)),
|
||||
None => Err(
|
||||
ctx.mk_err("Unexpected end", "Line ends abruptly; more tokens were expected", [snip.sr()]),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn expect_end(
|
||||
ctx: &impl ParseCtx,
|
||||
snip: Snippet<'_, impl ExprRepr, impl ExtraTok>,
|
||||
) -> OrcRes<()> {
|
||||
pub async fn expect_end(ctx: &Ctx, snip: Snippet<'_, impl ExprRepr, impl ExtraTok>) -> OrcRes<()> {
|
||||
match snip.skip_fluff().get(0) {
|
||||
Some(surplus) => Err(mk_errv(
|
||||
ctx.i().i("Extra code after end of line").await,
|
||||
"Code found after the end of the line",
|
||||
[surplus.sr.pos()],
|
||||
)),
|
||||
Some(surplus) =>
|
||||
Err(ctx.mk_err("Extra code after end of line", "Code found after the end of the line", [
|
||||
surplus.sr.pos(),
|
||||
])),
|
||||
None => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn expect_tok<'a, A: ExprRepr, X: ExtraTok>(
|
||||
ctx: &impl ParseCtx,
|
||||
ctx: &Ctx,
|
||||
snip: Snippet<'a, A, X>,
|
||||
tok: Tok<String>,
|
||||
tok: IStr,
|
||||
) -> ParseRes<'a, (), A, X> {
|
||||
let Parsed { output: head, tail } = try_pop_no_fluff(ctx, snip).await?;
|
||||
match &head.tok {
|
||||
Token::Name(n) if *n == tok => Ok(Parsed { output: (), tail }),
|
||||
t => Err(mk_errv(
|
||||
ctx.i().i("Expected specific keyword").await,
|
||||
format!("Expected {tok} but found {:?}", fmt(t, ctx.i()).await),
|
||||
t => Err(ctx.mk_err(
|
||||
"Expected specific keyword",
|
||||
format!("Expected {tok} but found {:?}", fmt(t, &ctx.i()).await),
|
||||
[head.sr()],
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn token_errv<A: ExprRepr, X: ExtraTok>(
|
||||
ctx: &impl ParseCtx,
|
||||
ctx: &Ctx,
|
||||
tok: &TokTree<A, X>,
|
||||
description: &'static str,
|
||||
message: impl FnOnce(&str) -> String,
|
||||
) -> OrcErrv {
|
||||
mk_errv(ctx.i().i(description).await, message(&fmt(tok, ctx.i()).await), [tok.sr.pos()])
|
||||
) -> OrcErr {
|
||||
ctx.mk_err(description, message(&fmt(tok, &ctx.i()).await), [tok.sr.pos()])
|
||||
}
|
||||
|
||||
pub struct Parsed<'a, T, H: ExprRepr, X: ExtraTok> {
|
||||
@@ -217,12 +198,12 @@ pub struct Parsed<'a, T, H: ExprRepr, X: ExtraTok> {
|
||||
pub type ParseRes<'a, T, H, X> = OrcRes<Parsed<'a, T, H, X>>;
|
||||
|
||||
pub async fn parse_multiname<'a, A: ExprRepr, X: ExtraTok>(
|
||||
ctx: &impl ParseCtx,
|
||||
ctx: &Ctx,
|
||||
tail: Snippet<'a, A, X>,
|
||||
) -> ParseRes<'a, Vec<Import>, A, X> {
|
||||
let Some((tt, tail)) = tail.skip_fluff().pop_front() else {
|
||||
return Err(mk_errv(
|
||||
ctx.i().i("Expected token").await,
|
||||
return Err(ctx.mk_err(
|
||||
"Expected token",
|
||||
"Expected a name, a parenthesized list of names, or a globstar.",
|
||||
[tail.sr().pos()],
|
||||
));
|
||||
@@ -231,17 +212,14 @@ pub async fn parse_multiname<'a, A: ExprRepr, X: ExtraTok>(
|
||||
#[allow(clippy::type_complexity)] // it's an internal function
|
||||
pub async fn rec<A: ExprRepr, X: ExtraTok>(
|
||||
tt: &TokTree<A, X>,
|
||||
ctx: &impl ParseCtx,
|
||||
) -> OrcRes<Vec<(Vec<Tok<String>>, Option<Tok<String>>, SrcRange)>> {
|
||||
ctx: &Ctx,
|
||||
) -> OrcRes<Vec<(Vec<IStr>, Option<IStr>, SrcRange)>> {
|
||||
let ttpos = tt.sr.pos();
|
||||
match &tt.tok {
|
||||
Token::NS(ns, body) => {
|
||||
if !ns.starts_with(name_start) {
|
||||
ctx.rep().report(mk_errv(
|
||||
ctx.i().i("Unexpected name prefix").await,
|
||||
"Only names can precede ::",
|
||||
[ttpos],
|
||||
))
|
||||
let err = ctx.mk_err("Unexpected name prefix", "Only names can precede ::", [ttpos]);
|
||||
ctx.rep().report(err)
|
||||
};
|
||||
let out = Box::pin(rec(body, ctx)).await?;
|
||||
Ok(out.into_iter().update(|i| i.0.push(ns.clone())).collect_vec())
|
||||
@@ -264,9 +242,9 @@ pub async fn parse_multiname<'a, A: ExprRepr, X: ExtraTok>(
|
||||
Ok(o)
|
||||
},
|
||||
t => {
|
||||
return Err(mk_errv(
|
||||
ctx.i().i("Unrecognized name end").await,
|
||||
format!("Names cannot end with {:?} tokens", fmt(t, ctx.i()).await),
|
||||
return Err(ctx.mk_err(
|
||||
"Unrecognized name end",
|
||||
format!("Names cannot end with {:?} tokens", fmt(t, &ctx.i()).await),
|
||||
[ttpos],
|
||||
));
|
||||
},
|
||||
@@ -285,7 +263,7 @@ pub async fn parse_multiname<'a, A: ExprRepr, X: ExtraTok>(
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Import {
|
||||
pub path: VPath,
|
||||
pub name: Option<Tok<String>>,
|
||||
pub name: Option<IStr>,
|
||||
pub sr: SrcRange,
|
||||
}
|
||||
impl Import {
|
||||
@@ -296,14 +274,14 @@ impl Import {
|
||||
None => self.path.into_name().expect("Import cannot be empty"),
|
||||
}
|
||||
}
|
||||
pub fn new(sr: SrcRange, path: VPath, name: Tok<String>) -> Self {
|
||||
pub fn new(sr: SrcRange, path: VPath, name: IStr) -> Self {
|
||||
Import { path, name: Some(name), sr }
|
||||
}
|
||||
pub fn new_glob(sr: SrcRange, path: VPath) -> Self { Import { path, name: None, sr } }
|
||||
}
|
||||
impl Display for Import {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}::{}", self.path.iter().join("::"), self.name.as_ref().map_or("*", |t| t.as_str()))
|
||||
write!(f, "{}::{}", self.path, self.name.as_deref().unwrap_or("*"))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,15 +5,18 @@ use std::marker::PhantomData;
|
||||
use std::mem;
|
||||
use std::ops::{BitAnd, Deref};
|
||||
use std::pin::Pin;
|
||||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::time::Duration;
|
||||
|
||||
use derive_destructure::destructure;
|
||||
use dyn_clone::{DynClone, clone_box};
|
||||
use futures::channel::mpsc;
|
||||
use futures::channel::mpsc::{self, Sender};
|
||||
use futures::channel::oneshot;
|
||||
use futures::future::LocalBoxFuture;
|
||||
use futures::lock::Mutex;
|
||||
use futures::{SinkExt, StreamExt};
|
||||
use futures::{AsyncBufRead, AsyncWrite, SinkExt, Stream, StreamExt};
|
||||
use hashbrown::HashMap;
|
||||
use orchid_api_traits::{Channel, Coding, Decode, Encode, MsgSet, Request};
|
||||
use trait_set::trait_set;
|
||||
@@ -23,6 +26,71 @@ use crate::logging::Logger;
|
||||
|
||||
pub struct Receipt<'a>(PhantomData<&'a mut ()>);
|
||||
|
||||
/// This object holds an exclusive lock on the outbound pipe.
|
||||
pub trait DynRequestWriter {
|
||||
fn writer(&mut self) -> Pin<&mut dyn AsyncWrite>;
|
||||
/// Release the outbound pipe and wait for the response to begin.
|
||||
fn get_response(self: Box<Self>) -> Pin<Box<dyn Future<Output = Box<dyn DynResponseHandle>>>>;
|
||||
}
|
||||
/// This object holds an exclusive lock on the inbound pipe.
|
||||
pub trait DynResponseHandle {
|
||||
fn reader(&mut self) -> Pin<&mut dyn AsyncBufRead>;
|
||||
fn finish(self: Box<Self>) -> Pin<Box<dyn Future<Output = ()>>>;
|
||||
}
|
||||
/// This object holds an exclusive lock on the outbound pipe.
|
||||
pub trait DynNotifWriter {
|
||||
fn writer(&mut self) -> Pin<&mut dyn AsyncWrite>;
|
||||
fn finish(self: Box<Self>) -> Pin<Box<dyn Future<Output = ()>>>;
|
||||
}
|
||||
|
||||
pub trait DynClient {
|
||||
fn request(&self) -> Pin<Box<dyn Future<Output = Box<dyn DynRequestWriter>>>>;
|
||||
fn notif(&self) -> Pin<Box<dyn Future<Output = Box<dyn DynNotifWriter>>>>;
|
||||
}
|
||||
|
||||
pub struct Client<T: MsgSet>(pub(crate) Rc<dyn DynClient>, pub(crate) PhantomData<T>);
|
||||
impl<T: MsgSet> Client<T> {
|
||||
pub async fn notify<Notif: Into<<T::Out as Channel>::Notif>>(&self, notif: Notif) {
|
||||
let mut notif_writer = self.0.notif().await;
|
||||
notif.into().encode(notif_writer.writer()).await;
|
||||
notif_writer.finish().await;
|
||||
}
|
||||
pub async fn request<Req: Request + Into<<T::Out as Channel>::Req>>(
|
||||
&self,
|
||||
req: Req,
|
||||
) -> Req::Response {
|
||||
let root_req = req.into();
|
||||
let mut req_writer = self.0.request().await;
|
||||
root_req.encode(req_writer.writer()).await;
|
||||
let mut req_hand = req_writer.get_response().await;
|
||||
let res = Req::Response::decode(req_hand.reader()).await;
|
||||
req_hand.finish().await;
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DuplexServerState {
|
||||
pending_outbound: HashMap<u64, Box<dyn FnOnce(&mut dyn AsyncBufRead)>>,
|
||||
sender: Pin<Box<dyn AsyncWrite>>,
|
||||
receiver: Pin<Box<dyn AsyncBufRead>>,
|
||||
}
|
||||
pub enum ServerEvent<T: MsgSet> {
|
||||
Notif(<T::In as Channel>::Notif),
|
||||
Req(RequestHandle<T>, <T::In as Channel>::Req),
|
||||
}
|
||||
pub async fn run_duplex_server<T: MsgSet>(
|
||||
sender: Pin<Box<dyn AsyncWrite>>,
|
||||
receiver: Pin<Box<dyn AsyncBufRead>>,
|
||||
) -> (impl Stream<Item = ServerEvent<T>>, Client<T>) {
|
||||
let sender = Rc::new(Mutex::new(sender));
|
||||
let receiver = Rc::new(Mutex::new(receiver));
|
||||
let pending_outbound = Rc::new(Mutex::new(HashMap::new()));
|
||||
}
|
||||
pub struct DuplexServer(Rc<Mutex<DuplexServerState>>);
|
||||
impl DuplexServer {
|
||||
pub fn receive(msg: )
|
||||
}
|
||||
|
||||
trait_set! {
|
||||
pub trait SendFn<T: MsgSet> =
|
||||
for<'a> FnMut(&'a [u8], ReqNot<T>) -> LocalBoxFuture<'a, ()>
|
||||
@@ -52,11 +120,10 @@ impl ReqHandlish for &'_ dyn ReqHandlish {
|
||||
}
|
||||
|
||||
#[derive(destructure)]
|
||||
pub struct RequestHandle<'a, MS: MsgSet> {
|
||||
pub struct RequestHandle<MS: MsgSet> {
|
||||
defer_drop: RefCell<Vec<Box<dyn Any>>>,
|
||||
fulfilled: AtomicBool,
|
||||
id: u64,
|
||||
_reqlt: PhantomData<&'a mut ()>,
|
||||
parent: ReqNot<MS>,
|
||||
}
|
||||
impl<'a, MS: MsgSet + 'static> RequestHandle<'a, MS> {
|
||||
@@ -89,7 +156,7 @@ impl<'a, MS: MsgSet + 'static> RequestHandle<'a, MS> {
|
||||
impl<MS: MsgSet> ReqHandlish for RequestHandle<'_, MS> {
|
||||
fn defer_drop_objsafe(&self, val: Box<dyn Any>) { self.defer_drop.borrow_mut().push(val); }
|
||||
}
|
||||
impl<MS: MsgSet> Drop for RequestHandle<'_, MS> {
|
||||
impl<MS: MsgSet> Drop for RequestHandle<MS> {
|
||||
fn drop(&mut self) {
|
||||
let done = self.fulfilled.load(Ordering::Relaxed);
|
||||
debug_assert!(done, "Request {} dropped without response", self.id)
|
||||
@@ -123,7 +190,7 @@ impl<T: MsgSet> ReqNot<T> {
|
||||
notif: impl NotifFn<T>,
|
||||
req: impl ReqFn<T>,
|
||||
) -> Self {
|
||||
Self(
|
||||
let this = Self(
|
||||
Arc::new(Mutex::new(ReqNotData {
|
||||
id: 1,
|
||||
send: Box::new(send),
|
||||
@@ -132,7 +199,13 @@ impl<T: MsgSet> ReqNot<T> {
|
||||
responses: HashMap::new(),
|
||||
})),
|
||||
logger,
|
||||
)
|
||||
);
|
||||
let (sig_send, sig_recv) = std::sync::mpsc::sync_channel(0);
|
||||
std::thread::spawn(move || {
|
||||
std::thread::sleep(Duration::from_secs(10));
|
||||
sig_send.send(()).expect("Crash!");
|
||||
});
|
||||
this
|
||||
}
|
||||
|
||||
/// Can be called from a polling thread or dispatched in any other way
|
||||
|
||||
23
orchid-base/src/testing.rs
Normal file
23
orchid-base/src/testing.rs
Normal file
@@ -0,0 +1,23 @@
|
||||
#![cfg(any(feature = "mocks", test))]
|
||||
|
||||
use std::future::ready;
|
||||
use std::pin::Pin;
|
||||
use std::rc::Rc;
|
||||
|
||||
pub struct AsyncMonitor<E: 'static>(Rc<dyn Fn(E) -> Pin<Box<dyn Future<Output = ()>>>>);
|
||||
impl<E: 'static> AsyncMonitor<E> {
|
||||
pub fn new<F: AsyncFn(E) -> () + 'static>(f: F) -> Self {
|
||||
let f_rc = Rc::new(f);
|
||||
AsyncMonitor(Rc::new(move |e| {
|
||||
let f_rc = f_rc.clone();
|
||||
Box::pin(async move { f_rc(e).await })
|
||||
}))
|
||||
}
|
||||
pub async fn notify(&self, e: E) -> () { (self.0)(e).await }
|
||||
}
|
||||
impl<E: 'static> Default for AsyncMonitor<E> {
|
||||
fn default() -> Self { Self(Rc::new(|_| Box::pin(ready(())))) }
|
||||
}
|
||||
impl<E: 'static> Clone for AsyncMonitor<E> {
|
||||
fn clone(&self) -> Self { Self(self.0.clone()) }
|
||||
}
|
||||
@@ -12,9 +12,9 @@ use never::Never;
|
||||
use orchid_api_traits::Coding;
|
||||
use trait_set::trait_set;
|
||||
|
||||
use crate::error::OrcErrv;
|
||||
use crate::error::OwnedOrcErr;
|
||||
use crate::format::{FmtCtx, FmtUnit, Format, Variants};
|
||||
use crate::interner::{Interner, Tok};
|
||||
use crate::interner::{IStr, Interner};
|
||||
use crate::location::{Pos, SrcRange};
|
||||
use crate::name::{Sym, VName, VPath};
|
||||
use crate::parse::Snippet;
|
||||
@@ -113,11 +113,11 @@ impl<H: ExprRepr, X: ExtraTok> TokTree<H, X> {
|
||||
let pos = SrcRange::new(tt.range.clone(), src);
|
||||
let tok = match_mapping!(&tt.token, api::Token => Token::<H, X> {
|
||||
BR,
|
||||
NS(n => Tok::from_api(*n, i).await,
|
||||
NS(n => i.es(*n).await,
|
||||
b => Box::new(Self::from_api(b, hctx, xctx, src, i).boxed_local().await)),
|
||||
Bottom(e => OrcErrv::from_api(e, i).await),
|
||||
Bottom(e => OwnedOrcErr::from_api(e, i).await),
|
||||
LambdaHead(arg => Box::new(Self::from_api(arg, hctx, xctx, src, i).boxed_local().await)),
|
||||
Name(n => Tok::from_api(*n, i).await),
|
||||
Name(n => i.es(*n).await),
|
||||
S(*par, b => ttv_from_api(b, hctx, xctx, src, i).await),
|
||||
Comment(c.clone()),
|
||||
NewExpr(expr => X::from_api(expr, xctx, pos.clone(), i).await),
|
||||
@@ -145,8 +145,8 @@ impl<H: ExprRepr, X: ExtraTok> TokTree<H, X> {
|
||||
api::TokenTree { range: self.sr.range.clone(), token }
|
||||
}
|
||||
|
||||
pub fn is_kw(&self, tk: Tok<String>) -> bool { self.tok.is_kw(tk) }
|
||||
pub fn as_name(&self) -> Option<Tok<String>> {
|
||||
pub fn is_kw(&self, tk: IStr) -> bool { self.tok.is_kw(tk) }
|
||||
pub fn as_name(&self) -> Option<IStr> {
|
||||
if let Token::Name(n) = &self.tok { Some(n.clone()) } else { None }
|
||||
}
|
||||
pub fn as_multiname(&self) -> Result<VName, &TokTree<H, X>> {
|
||||
@@ -245,9 +245,9 @@ pub enum Token<H: ExprRepr, X: ExtraTok> {
|
||||
/// stretches to the end of the enclosing parens or the end of the const line
|
||||
LambdaHead(Box<TokTree<H, X>>),
|
||||
/// A binding, operator, or a segment of a namespaced::name
|
||||
Name(Tok<String>),
|
||||
Name(IStr),
|
||||
/// A namespace prefix, like `my_ns::` followed by a token
|
||||
NS(Tok<String>, Box<TokTree<H, X>>),
|
||||
NS(IStr, Box<TokTree<H, X>>),
|
||||
/// A line break
|
||||
BR,
|
||||
/// `()`, `[]`, or `{}`
|
||||
@@ -259,11 +259,11 @@ pub enum Token<H: ExprRepr, X: ExtraTok> {
|
||||
/// A grammar error emitted by a lexer plugin if it was possible to continue
|
||||
/// reading. Parsers should treat it as an atom unless it prevents parsing,
|
||||
/// in which case both this and a relevant error should be returned.
|
||||
Bottom(OrcErrv),
|
||||
Bottom(OwnedOrcErr),
|
||||
}
|
||||
impl<H: ExprRepr, X: ExtraTok> Token<H, X> {
|
||||
pub fn at(self, sr: SrcRange) -> TokTree<H, X> { TokTree { sr, tok: self } }
|
||||
pub fn is_kw(&self, tk: Tok<String>) -> bool { matches!(self, Token::Name(n) if *n == tk) }
|
||||
pub fn is_kw(&self, tk: IStr) -> bool { matches!(self, Token::Name(n) if *n == tk) }
|
||||
pub fn as_s(&self, par: Paren) -> Option<&[TokTree<H, X>]> {
|
||||
match self {
|
||||
Self::S(p, b) if *p == par => Some(b),
|
||||
@@ -275,8 +275,7 @@ impl<H: ExprRepr, X: ExtraTok> Format for Token<H, X> {
|
||||
async fn print<'a>(&'a self, c: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit {
|
||||
match self {
|
||||
Self::BR => "\n".to_string().into(),
|
||||
Self::Bottom(err) if err.len() == 1 => format!("Bottom({}) ", err.one().unwrap()).into(),
|
||||
Self::Bottom(err) => format!("Botttom(\n{}) ", indent(&err.to_string())).into(),
|
||||
Self::Bottom(err) => format!("Botttom({}) ", indent(&err.to_string())).into(),
|
||||
Self::Comment(c) => format!("--[{c}]--").into(),
|
||||
Self::LambdaHead(arg) =>
|
||||
tl_cache!(Rc<Variants>: Rc::new(Variants::default().bounded("\\{0b}.")))
|
||||
|
||||
@@ -13,13 +13,13 @@ pub enum Loaded {
|
||||
Code(Arc<String>),
|
||||
/// Conceptually equivalent to the list of *.orc files in a folder, without
|
||||
/// the extension
|
||||
Collection(Arc<Vec<Tok<String>>>),
|
||||
Collection(Arc<Vec<IStr>>),
|
||||
}
|
||||
impl Loaded {
|
||||
/// Is the loaded item source code (not a collection)?
|
||||
pub fn is_code(&self) -> bool { matches!(self, Loaded::Code(_)) }
|
||||
/// Collect the elements in a collection rreport
|
||||
pub fn collection(items: impl IntoIterator<Item = Tok<String>>) -> Self {
|
||||
pub fn collection(items: impl IntoIterator<Item = IStr>) -> Self {
|
||||
Self::Collection(Arc::new(items.into_iter().collect()))
|
||||
}
|
||||
}
|
||||
@@ -55,7 +55,7 @@ impl ErrorSansOrigin for CodeNotFound {
|
||||
/// formats and other sources for libraries and dependencies.
|
||||
pub trait VirtFS {
|
||||
/// Implementation of [VirtFS::read]
|
||||
fn get(&self, path: &[Tok<String>], full_path: &PathSlice) -> FSResult;
|
||||
fn get(&self, path: &[IStr], full_path: &PathSlice) -> FSResult;
|
||||
/// Discover information about a path without reading it.
|
||||
///
|
||||
/// Implement this if your vfs backend can do expensive operations
|
||||
@@ -68,7 +68,7 @@ pub trait VirtFS {
|
||||
}
|
||||
/// Convert a path into a human-readable string that is meaningful in the
|
||||
/// target context.
|
||||
fn display(&self, path: &[Tok<String>]) -> Option<String>;
|
||||
fn display(&self, path: &[IStr]) -> Option<String>;
|
||||
/// Convert the FS handler into a type-erased version of itself for packing in
|
||||
/// a tree.
|
||||
fn rc(self) -> Rc<dyn VirtFS>
|
||||
@@ -81,15 +81,15 @@ pub trait VirtFS {
|
||||
}
|
||||
|
||||
impl VirtFS for &dyn VirtFS {
|
||||
fn get(&self, path: &[Tok<String>], full_path: &PathSlice) -> FSResult {
|
||||
fn get(&self, path: &[IStr], full_path: &PathSlice) -> FSResult {
|
||||
(*self).get(path, full_path)
|
||||
}
|
||||
fn display(&self, path: &[Tok<String>]) -> Option<String> { (*self).display(path) }
|
||||
fn display(&self, path: &[IStr]) -> Option<String> { (*self).display(path) }
|
||||
}
|
||||
|
||||
impl<T: VirtFS + ?Sized> VirtFS for Rc<T> {
|
||||
fn get(&self, path: &[Tok<String>], full_path: &PathSlice) -> FSResult {
|
||||
fn get(&self, path: &[IStr], full_path: &PathSlice) -> FSResult {
|
||||
(**self).get(path, full_path)
|
||||
}
|
||||
fn display(&self, path: &[Tok<String>]) -> Option<String> { (**self).display(path) }
|
||||
fn display(&self, path: &[IStr]) -> Option<String> { (**self).display(path) }
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ impl<'a> Combine for &'a dyn VirtFS {
|
||||
pub type DeclTree = ModEntry<Rc<dyn VirtFS>, (), ()>;
|
||||
|
||||
impl VirtFS for DeclTree {
|
||||
fn get(&self, path: &[Tok<String>], full_path: &PathSlice) -> FSResult {
|
||||
fn get(&self, path: &[IStr], full_path: &PathSlice) -> FSResult {
|
||||
match &self.member {
|
||||
ModMember::Item(it) => it.get(path, full_path),
|
||||
ModMember::Sub(module) => match path.split_first() {
|
||||
@@ -44,7 +44,7 @@ impl VirtFS for DeclTree {
|
||||
}
|
||||
}
|
||||
|
||||
fn display(&self, path: &[Tok<String>]) -> Option<String> {
|
||||
fn display(&self, path: &[IStr]) -> Option<String> {
|
||||
let (head, tail) = path.split_first()?;
|
||||
match &self.member {
|
||||
ModMember::Item(it) => it.display(path),
|
||||
@@ -54,16 +54,16 @@ impl VirtFS for DeclTree {
|
||||
}
|
||||
|
||||
impl VirtFS for String {
|
||||
fn display(&self, _: &[Tok<String>]) -> Option<String> { None }
|
||||
fn get(&self, path: &[Tok<String>], full_path: &PathSlice) -> FSResult {
|
||||
fn display(&self, _: &[IStr]) -> Option<String> { None }
|
||||
fn get(&self, path: &[IStr], full_path: &PathSlice) -> FSResult {
|
||||
(path.is_empty().then(|| Loaded::Code(Arc::new(self.as_str().to_string()))))
|
||||
.ok_or_else(|| CodeNotFound::new(full_path.to_vpath()).pack())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> VirtFS for &'a str {
|
||||
fn display(&self, _: &[Tok<String>]) -> Option<String> { None }
|
||||
fn get(&self, path: &[Tok<String>], full_path: &PathSlice) -> FSResult {
|
||||
fn display(&self, _: &[IStr]) -> Option<String> { None }
|
||||
fn get(&self, path: &[IStr], full_path: &PathSlice) -> FSResult {
|
||||
(path.is_empty().then(|| Loaded::Code(Arc::new(self.to_string()))))
|
||||
.ok_or_else(|| CodeNotFound::new(full_path.to_vpath()).pack())
|
||||
}
|
||||
|
||||
@@ -99,14 +99,14 @@ impl DirNode {
|
||||
}
|
||||
}
|
||||
|
||||
fn mk_pathbuf(&self, path: &[Tok<String>]) -> PathBuf {
|
||||
fn mk_pathbuf(&self, path: &[IStr]) -> PathBuf {
|
||||
let mut fpath = self.root.clone();
|
||||
path.iter().for_each(|seg| fpath.push(seg.as_str()));
|
||||
fpath
|
||||
}
|
||||
}
|
||||
impl VirtFS for DirNode {
|
||||
fn get(&self, path: &[Tok<String>], full_path: &PathSlice) -> FSResult {
|
||||
fn get(&self, path: &[IStr], full_path: &PathSlice) -> FSResult {
|
||||
let fpath = self.mk_pathbuf(path);
|
||||
let mut binding = self.cached.borrow_mut();
|
||||
let (_, res) = (binding.raw_entry_mut().from_key(&fpath))
|
||||
@@ -114,7 +114,7 @@ impl VirtFS for DirNode {
|
||||
res.clone()
|
||||
}
|
||||
|
||||
fn display(&self, path: &[Tok<String>]) -> Option<String> {
|
||||
fn display(&self, path: &[IStr]) -> Option<String> {
|
||||
let pathbuf = self.mk_pathbuf(path).with_extension(self.ext());
|
||||
Some(pathbuf.to_string_lossy().to_string())
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ impl EmbeddedFS {
|
||||
}
|
||||
|
||||
impl VirtFS for EmbeddedFS {
|
||||
fn get(&self, path: &[Tok<String>], full_path: &PathSlice) -> FSResult {
|
||||
fn get(&self, path: &[IStr], full_path: &PathSlice) -> FSResult {
|
||||
if path.is_empty() {
|
||||
return Ok(Loaded::collection(self.tree.keys(|_| true)));
|
||||
}
|
||||
@@ -67,7 +67,7 @@ impl VirtFS for EmbeddedFS {
|
||||
ModMember::Sub(sub) => Loaded::collection(sub.keys(|_| true)),
|
||||
})
|
||||
}
|
||||
fn display(&self, path: &[Tok<String>]) -> Option<String> {
|
||||
fn display(&self, path: &[IStr]) -> Option<String> {
|
||||
let Self { gen, suffix, .. } = self;
|
||||
Some(format!("{}{suffix} in {gen}", path.iter().join("/")))
|
||||
}
|
||||
|
||||
@@ -21,18 +21,18 @@ impl<'a> PrefixFS<'a> {
|
||||
add: VPath::parse(add.as_ref()),
|
||||
}
|
||||
}
|
||||
fn proc_path(&self, path: &[Tok<String>]) -> Option<Vec<Tok<String>>> {
|
||||
fn proc_path(&self, path: &[IStr]) -> Option<Vec<IStr>> {
|
||||
let path = path.strip_prefix(self.remove.as_slice())?;
|
||||
Some(self.add.0.iter().chain(path).cloned().collect_vec())
|
||||
}
|
||||
}
|
||||
impl<'a> VirtFS for PrefixFS<'a> {
|
||||
fn get(&self, path: &[Tok<String>], full_path: &PathSlice) -> super::FSResult {
|
||||
fn get(&self, path: &[IStr], full_path: &PathSlice) -> super::FSResult {
|
||||
let path =
|
||||
self.proc_path(path).ok_or_else(|| CodeNotFound::new(full_path.to_vpath()).pack())?;
|
||||
self.wrapped.get(&path, full_path)
|
||||
}
|
||||
fn display(&self, path: &[Tok<String>]) -> Option<String> {
|
||||
fn display(&self, path: &[IStr]) -> Option<String> {
|
||||
self.wrapped.display(&self.proc_path(path)?)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user