use super::resolve_simple;
use crate::{Ctx, Loaded, Loader, Lock, Module, Mut, Ref, Resolver, Result, Script};
use std::{
collections::{hash_map::Iter as HashMapIter, HashMap},
iter::{ExactSizeIterator, FusedIterator},
ops::{Deref, DerefMut},
};
#[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "loader")))]
#[derive(Default, Clone)]
pub struct Compile<T = ()> {
data: Ref<Mut<CompileData>>,
inner: T,
}
impl<T> Deref for Compile<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl<T> DerefMut for Compile<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.inner
}
}
impl Compile {
pub fn new() -> Self {
Self::default()
}
pub fn resolver<R: Resolver>(&self, resolver: R) -> Compile<R> {
Compile {
data: self.data.clone(),
inner: resolver,
}
}
pub fn loader<L: Loader<Script>>(&self, loader: L) -> Compile<L> {
Compile {
data: self.data.clone(),
inner: loader,
}
}
pub fn modules(&self) -> ResolvedModules {
ResolvedModules(self.data.lock())
}
pub fn bytecodes(&self) -> CompiledBytecodes {
CompiledBytecodes(self.data.lock())
}
}
pub struct ResolvedModules<'i>(Lock<'i, CompileData>);
impl<'i, 'r: 'i> IntoIterator for &'r ResolvedModules<'i> {
type IntoIter = ResolvedModulesIter<'i>;
type Item = (&'i str, &'i str);
fn into_iter(self) -> Self::IntoIter {
ResolvedModulesIter(self.0.modules.iter())
}
}
pub struct ResolvedModulesIter<'r>(HashMapIter<'r, String, String>);
impl<'i> Iterator for ResolvedModulesIter<'i> {
type Item = (&'i str, &'i str);
fn next(&mut self) -> Option<Self::Item> {
self.0
.next()
.map(|(path, name)| (name.as_str(), path.as_str()))
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.0.size_hint()
}
}
impl<'i> ExactSizeIterator for ResolvedModulesIter<'i> {
fn len(&self) -> usize {
self.0.len()
}
}
impl<'i> FusedIterator for ResolvedModulesIter<'i> {}
pub struct CompiledBytecodes<'i>(Lock<'i, CompileData>);
impl<'i, 'r: 'i> IntoIterator for &'r CompiledBytecodes<'i> {
type IntoIter = CompiledBytecodesIter<'i>;
type Item = (&'i str, &'i [u8]);
fn into_iter(self) -> Self::IntoIter {
CompiledBytecodesIter {
data: &*self.0,
index: 0,
}
}
}
pub struct CompiledBytecodesIter<'r> {
data: &'r CompileData,
index: usize,
}
impl<'i> Iterator for CompiledBytecodesIter<'i> {
type Item = (&'i str, &'i [u8]);
fn next(&mut self) -> Option<Self::Item> {
let CompileData { modules, bytecodes } = &self.data;
if self.index < bytecodes.len() {
let (path, data) = &bytecodes[self.index];
self.index += 1;
modules
.get(path.as_str())
.map(|name| (name.as_str(), data.as_ref()))
} else {
None
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
let len = self.len();
(len, Some(len))
}
}
impl<'i> ExactSizeIterator for CompiledBytecodesIter<'i> {
fn len(&self) -> usize {
self.data.bytecodes.len() - self.index
}
}
impl<'i> FusedIterator for CompiledBytecodesIter<'i> {}
#[derive(Debug, Default)]
struct CompileData {
modules: HashMap<String, String>,
bytecodes: Vec<(String, Vec<u8>)>,
}
impl<R> Resolver for Compile<R>
where
R: Resolver,
{
fn resolve<'js>(&mut self, ctx: Ctx<'js>, base: &str, name: &str) -> Result<String> {
self.inner.resolve(ctx, base, name).map(|path| {
let name = resolve_simple(base, name);
self.data.lock().modules.insert(path.clone(), name);
path
})
}
}
impl<L> Loader<Script> for Compile<L>
where
L: Loader<Script>,
{
fn load<'js>(&mut self, ctx: Ctx<'js>, path: &str) -> Result<Module<'js, Loaded<Script>>> {
self.inner.load(ctx, path).and_then(|module| {
self.data
.lock()
.bytecodes
.push((path.into(), module.write_object(false)?));
Ok(module)
})
}
}