forked from Orchid/orchid
158 lines
4.7 KiB
Rust
158 lines
4.7 KiB
Rust
use std::cell::OnceCell;
|
|
use std::rc::Rc;
|
|
|
|
use futures::FutureExt;
|
|
use futures::lock::Mutex;
|
|
use memo_map::MemoMap;
|
|
use orchid_base::interner::Tok;
|
|
use orchid_base::name::{NameLike, VPath};
|
|
use orchid_base::reqnot::Requester;
|
|
|
|
use crate::api;
|
|
use crate::context::{SysCtxEntry, ctx, i};
|
|
|
|
#[derive(Debug)]
|
|
pub struct ReflMemData {
|
|
// None for inferred steps
|
|
public: OnceCell<bool>,
|
|
kind: ReflMemKind,
|
|
}
|
|
#[derive(Clone, Debug)]
|
|
pub struct ReflMem(Rc<ReflMemData>);
|
|
impl ReflMem {
|
|
pub fn kind(&self) -> ReflMemKind { self.0.kind.clone() }
|
|
}
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub enum ReflMemKind {
|
|
Const,
|
|
Mod(ReflMod),
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct ReflModData {
|
|
inferred: Mutex<bool>,
|
|
path: VPath,
|
|
members: MemoMap<Tok<String>, ReflMem>,
|
|
}
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub struct ReflMod(Rc<ReflModData>);
|
|
impl ReflMod {
|
|
pub fn path(&self) -> &[Tok<String>] { &self.0.path[..] }
|
|
pub fn is_root(&self) -> bool { self.0.path.is_empty() }
|
|
async fn try_populate(&self) -> Result<(), api::LsModuleError> {
|
|
let path_tok = i().i(&self.0.path[..]).await;
|
|
let reply = match ctx().reqnot().request(api::LsModule(ctx().sys_id(), path_tok.to_api())).await
|
|
{
|
|
Err(api::LsModuleError::TreeUnavailable) =>
|
|
panic!("Reflected tree accessed outside an interpreter call. This extension is faulty."),
|
|
Err(err) => return Err(err),
|
|
Ok(details) => details,
|
|
};
|
|
for (k, v) in reply.members {
|
|
let k = i().ex(k).await;
|
|
let mem = match self.0.members.get(&k) {
|
|
Some(mem) => mem,
|
|
None => {
|
|
let path = self.0.path.clone().name_with_suffix(k.clone()).to_sym(&i()).await;
|
|
let kind = match v.kind {
|
|
api::MemberInfoKind::Constant => ReflMemKind::Const,
|
|
api::MemberInfoKind::Module =>
|
|
ReflMemKind::Mod(default_module(VPath::new(path.segs()))),
|
|
};
|
|
self.0.members.get_or_insert(&k, || default_member(self.is_root(), kind))
|
|
},
|
|
};
|
|
let _ = mem.0.public.set(v.public);
|
|
}
|
|
Ok(())
|
|
}
|
|
pub async fn get_child(&self, key: &Tok<String>) -> Option<ReflMem> {
|
|
let inferred_g = self.0.inferred.lock().await;
|
|
if let Some(mem) = self.0.members.get(key) {
|
|
return Some(mem.clone());
|
|
}
|
|
if !*inferred_g {
|
|
return None;
|
|
}
|
|
match self.try_populate().await {
|
|
Err(api::LsModuleError::InvalidPath) =>
|
|
panic!("Path became invalid since module was created"),
|
|
Err(api::LsModuleError::IsConstant) =>
|
|
panic!("Path previously contained a module but now contains a constant"),
|
|
Err(api::LsModuleError::TreeUnavailable) => unreachable!(),
|
|
Ok(()) => (),
|
|
}
|
|
self.0.members.get(key).cloned()
|
|
}
|
|
pub async fn get_by_path(&self, path: &[Tok<String>]) -> Result<ReflMem, InvalidPathError> {
|
|
let (next, tail) = path.split_first().expect("Attempted to walk by empty path");
|
|
let inferred_g = self.0.inferred.lock().await;
|
|
if let Some(next) = self.0.members.get(next) {
|
|
return if tail.is_empty() {
|
|
Ok(next.clone())
|
|
} else {
|
|
match next.kind() {
|
|
ReflMemKind::Const => Err(InvalidPathError { keep_ancestry: true }),
|
|
ReflMemKind::Mod(m) => m.get_by_path(tail).boxed_local().await,
|
|
}
|
|
};
|
|
}
|
|
if !*inferred_g {
|
|
return Err(InvalidPathError { keep_ancestry: true });
|
|
}
|
|
let candidate = default_module(self.0.path.clone().suffix([next.clone()]));
|
|
if tail.is_empty() {
|
|
return match candidate.try_populate().await {
|
|
Ok(()) => {
|
|
let tgt_mem = default_member(self.is_root(), ReflMemKind::Mod(candidate));
|
|
self.0.members.insert(next.clone(), tgt_mem.clone());
|
|
Ok(tgt_mem)
|
|
},
|
|
Err(api::LsModuleError::InvalidPath) => Err(InvalidPathError { keep_ancestry: false }),
|
|
Err(api::LsModuleError::IsConstant) => {
|
|
let const_mem = default_member(self.is_root(), ReflMemKind::Const);
|
|
self.0.members.insert(next.clone(), const_mem.clone());
|
|
Ok(const_mem)
|
|
},
|
|
Err(api::LsModuleError::TreeUnavailable) => unreachable!(),
|
|
};
|
|
}
|
|
match candidate.get_by_path(tail).boxed_local().await {
|
|
e @ Err(InvalidPathError { keep_ancestry: false }) => e,
|
|
res @ Err(InvalidPathError { keep_ancestry: true }) | res @ Ok(_) => {
|
|
let tgt_mem = default_member(self.is_root(), ReflMemKind::Mod(candidate));
|
|
self.0.members.insert(next.clone(), tgt_mem);
|
|
res
|
|
},
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
struct ReflRoot(ReflMod);
|
|
impl SysCtxEntry for ReflRoot {}
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub struct InvalidPathError {
|
|
keep_ancestry: bool,
|
|
}
|
|
|
|
fn default_module(path: VPath) -> ReflMod {
|
|
ReflMod(Rc::new(ReflModData { inferred: Mutex::new(true), path, members: MemoMap::new() }))
|
|
}
|
|
|
|
fn default_member(is_root: bool, kind: ReflMemKind) -> ReflMem {
|
|
ReflMem(Rc::new(ReflMemData {
|
|
public: if is_root { true.into() } else { OnceCell::new() },
|
|
kind,
|
|
}))
|
|
}
|
|
|
|
fn get_root() -> ReflRoot {
|
|
ctx().get_or_insert(|| ReflRoot(default_module(VPath::new([])))).clone()
|
|
}
|
|
|
|
pub fn refl() -> ReflMod { get_root().0.clone() }
|