use crate::{Error, Ptr, Result, prelude::*};
use koto_bytecode::CompilerSettings;
use koto_runtime::ModuleImportedCallback;
use std::time::Duration;
pub struct Koto {
runtime: KotoVm,
run_tests: bool,
}
impl Default for Koto {
fn default() -> Self {
Self::new()
}
}
impl Koto {
pub fn new() -> Self {
Self::with_settings(KotoSettings::default())
}
pub fn with_settings(settings: KotoSettings) -> Self {
Self {
runtime: KotoVm::with_settings(settings.vm_settings),
run_tests: settings.run_tests,
}
}
pub fn prelude(&self) -> &KMap {
self.runtime.prelude()
}
pub fn exports(&self) -> &KMap {
self.runtime.exports()
}
pub fn exports_mut(&mut self) -> &mut KMap {
self.runtime.exports_mut()
}
pub fn compile_and_run<'a>(&mut self, script: impl Into<CompileArgs<'a>>) -> Result<KValue> {
let chunk = self.compile(script)?;
self.run(chunk)
}
pub fn compile<'a>(&mut self, args: impl Into<CompileArgs<'a>>) -> Result<Ptr<Chunk>> {
let args = args.into();
self.runtime
.loader()
.borrow_mut()
.compile_script(args.script, args.script_path, args.compiler_settings)
.map_err(Error::from)
}
pub fn run(&mut self, chunk: Ptr<Chunk>) -> Result<KValue> {
let result = self.runtime.run(chunk)?;
if self.run_tests {
self.runtime.run_tests(self.runtime.exports().clone())?;
}
if let Some(main) = self.runtime.exports().get_meta_value(&MetaKey::Main) {
self.runtime.call_function(main, &[]).map_err(From::from)
} else {
Ok(result)
}
}
pub fn call_function<'a>(
&mut self,
function: KValue,
args: impl Into<CallArgs<'a>>,
) -> Result<KValue> {
self.runtime
.call_function(function, args)
.map_err(From::from)
}
pub fn call_instance_function<'a>(
&mut self,
instance: KValue,
function: KValue,
args: impl Into<CallArgs<'a>>,
) -> Result<KValue> {
self.runtime
.call_instance_function(instance, function, args)
.map_err(From::from)
}
pub fn call_exported_function<'a>(
&mut self,
function_name: &str,
args: impl Into<CallArgs<'a>>,
) -> Result<KValue> {
match self.exports().get(function_name) {
Some(f) => self.runtime.call_function(f, args).map_err(From::from),
None => Err(Error::MissingFunction(function_name.into())),
}
}
pub fn value_to_string(&mut self, value: KValue) -> Result<String> {
self.runtime.value_to_string(&value).map_err(From::from)
}
pub fn clear_module_cache(&mut self) {
self.runtime.loader().borrow_mut().clear_cache();
}
pub fn set_args(&mut self, args: impl IntoIterator<Item = String>) -> Result<()> {
let koto_args = args.into_iter().map(KValue::from).collect::<Vec<_>>();
match self.runtime.prelude().data_mut().get("os") {
Some(KValue::Map(map)) => {
map.insert("args", KValue::Tuple(koto_args.into()));
Ok(())
}
_ => Err(Error::MissingOsModule),
}
}
pub fn set_run_tests(&mut self, enabled: bool) {
self.run_tests = enabled;
}
}
pub struct KotoSettings {
pub run_tests: bool,
pub vm_settings: KotoVmSettings,
}
impl KotoSettings {
#[must_use]
pub fn with_execution_limit(self, limit: Duration) -> Self {
Self {
vm_settings: KotoVmSettings {
execution_limit: Some(limit),
..self.vm_settings
},
..self
}
}
#[must_use]
pub fn with_stdin(self, stdin: impl KotoFile + 'static) -> Self {
Self {
vm_settings: KotoVmSettings {
stdin: make_ptr!(stdin),
..self.vm_settings
},
..self
}
}
#[must_use]
pub fn with_stdout(self, stdout: impl KotoFile + 'static) -> Self {
Self {
vm_settings: KotoVmSettings {
stdout: make_ptr!(stdout),
..self.vm_settings
},
..self
}
}
#[must_use]
pub fn with_stderr(self, stderr: impl KotoFile + 'static) -> Self {
Self {
vm_settings: KotoVmSettings {
stderr: make_ptr!(stderr),
..self.vm_settings
},
..self
}
}
#[must_use]
pub fn with_module_imported_callback(
self,
callback: impl ModuleImportedCallback + 'static,
) -> Self {
Self {
vm_settings: KotoVmSettings {
module_imported_callback: Some(Box::new(callback)),
..self.vm_settings
},
..self
}
}
}
impl Default for KotoSettings {
fn default() -> Self {
Self {
run_tests: true,
vm_settings: KotoVmSettings::default(),
}
}
}
pub struct CompileArgs<'a> {
pub script: &'a str,
pub script_path: Option<KString>,
pub compiler_settings: CompilerSettings,
}
impl<'a> CompileArgs<'a> {
pub fn new(script: &'a str) -> Self {
Self {
script,
script_path: None,
compiler_settings: CompilerSettings::default(),
}
}
pub fn script_path(mut self, script_path: impl Into<KString>) -> Self {
self.script_path = Some(script_path.into());
self
}
pub fn enable_type_checks(mut self, enabled: bool) -> Self {
self.compiler_settings.enable_type_checks = enabled;
self
}
pub fn export_top_level_ids(mut self, enabled: bool) -> Self {
self.compiler_settings.export_top_level_ids = enabled;
self
}
}
impl<'a> From<&'a str> for CompileArgs<'a> {
fn from(script: &'a str) -> Self {
Self {
script,
script_path: None,
compiler_settings: Default::default(),
}
}
}
impl<'a> From<&'a String> for CompileArgs<'a> {
fn from(script: &'a String) -> Self {
Self {
script: script.as_str(),
script_path: None,
compiler_settings: Default::default(),
}
}
}