#[cfg(test)]
pub(crate) mod tests;
use boa_engine::{
Context, JsData, JsObject, JsResult, JsString, JsSymbol, JsValue, js_error, js_string,
native_function::NativeFunction, object::ObjectInitializer, property::Attribute,
};
use boa_gc::{Finalize, Trace};
use std::rc::Rc;
pub trait ProcessProvider: Trace {
fn cwd(&self) -> JsResult<JsString>;
fn env(&self) -> impl IntoIterator<Item = (JsString, JsString)>;
}
#[derive(Debug, Trace, Finalize)]
pub struct StdProcessProvider;
impl ProcessProvider for StdProcessProvider {
fn cwd(&self) -> JsResult<JsString> {
let path = std::env::current_dir().map_err(
|e| js_error!(TypeError: "failed to get current working directory: {}", e.to_string()),
)?;
Ok(js_string!(path.to_string_lossy()))
}
fn env(&self) -> impl IntoIterator<Item = (JsString, JsString)> {
std::env::vars().map(|(k, v)| (js_string!(k), js_string!(v)))
}
}
#[derive(Debug, Trace, Finalize, JsData)]
pub struct Process;
impl Process {
pub const NAME: JsString = js_string!("process");
pub fn init_with_provider<P>(context: &mut Context, provider: P) -> JsResult<JsObject>
where
P: ProcessProvider + 'static,
{
fn process_method<P: ProcessProvider + 'static>(
f: fn(&JsValue, &[JsValue], &P, &mut Context) -> JsResult<JsValue>,
provider: Rc<P>,
) -> NativeFunction {
unsafe {
NativeFunction::from_closure(move |this, args, context| {
f(this, args, &provider, context)
})
}
}
let provider = Rc::new(provider);
let env = JsObject::default(context.intrinsics());
for (key, value) in provider.env() {
env.set(key, JsValue::from(value), false, context)?;
}
Ok(ObjectInitializer::new(context)
.property(
JsSymbol::to_string_tag(),
Self::NAME,
Attribute::CONFIGURABLE,
)
.property(
js_string!("env"),
env,
Attribute::WRITABLE | Attribute::CONFIGURABLE,
)
.function(
process_method(
|_, _, provider, _| provider.cwd().map(JsValue::from),
provider.clone(),
),
js_string!("cwd"),
0,
)
.build())
}
pub fn register_with_provider<P>(context: &mut Context, provider: P) -> JsResult<()>
where
P: ProcessProvider + 'static,
{
let process_object = Self::init_with_provider(context, provider)?;
context.register_global_property(
js_string!(Self::NAME),
process_object,
Attribute::CONFIGURABLE,
)?;
Ok(())
}
pub fn init(context: &mut Context) -> JsResult<JsObject> {
Self::init_with_provider(context, StdProcessProvider)
}
pub fn register(context: &mut Context) -> JsResult<()> {
Self::register_with_provider(context, StdProcessProvider)
}
}