Compare commits
1 Commits
f0a0593d4c
...
791c20660c
| Author | SHA1 | Date | |
|---|---|---|---|
| 791c20660c |
@@ -32,12 +32,8 @@ export const toJSON = (e: NixValue): NixString => {
|
|||||||
return mkStringWithContext(string, context);
|
return mkStringWithContext(string, context);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const toXML = (e: NixValue): NixString => {
|
export const toXML = (_e: NixValue): never => {
|
||||||
const [xml, context] = Deno.core.ops.op_to_xml(force(e));
|
throw new Error("Not implemented: toXML");
|
||||||
if (context.length === 0) {
|
|
||||||
return xml;
|
|
||||||
}
|
|
||||||
return mkStringWithContext(xml, new Set(context));
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
1
nix-js/runtime-ts/src/types/global.d.ts
vendored
1
nix-js/runtime-ts/src/types/global.d.ts
vendored
@@ -60,7 +60,6 @@ declare global {
|
|||||||
|
|
||||||
function op_from_json(json: string): unknown;
|
function op_from_json(json: string): unknown;
|
||||||
function op_from_toml(toml: string): unknown;
|
function op_from_toml(toml: string): unknown;
|
||||||
function op_to_xml(e: NixValue): [string, string[]];
|
|
||||||
|
|
||||||
function op_finalize_derivation(input: {
|
function op_finalize_derivation(input: {
|
||||||
name: string;
|
name: string;
|
||||||
|
|||||||
@@ -119,14 +119,8 @@ impl Context {
|
|||||||
DERIVATION_NIX.to_string(),
|
DERIVATION_NIX.to_string(),
|
||||||
);
|
);
|
||||||
let code = self.ctx.compile(source, None)?;
|
let code = self.ctx.compile(source, None)?;
|
||||||
self.runtime.eval(
|
self.runtime
|
||||||
format!(
|
.eval(format!("Nix.builtins.derivation = {};Nix.builtins.storeDir=\"{}\"", code, self.get_store_dir()), &mut self.ctx)?;
|
||||||
"Nix.builtins.derivation = {};Nix.builtins.storeDir=\"{}\"",
|
|
||||||
code,
|
|
||||||
self.get_store_dir()
|
|
||||||
),
|
|
||||||
&mut self.ctx,
|
|
||||||
)?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -73,7 +73,6 @@ fn runtime_extension<Ctx: RuntimeContext>() -> Extension {
|
|||||||
op_from_json(),
|
op_from_json(),
|
||||||
op_from_toml(),
|
op_from_toml(),
|
||||||
op_finalize_derivation::<Ctx>(),
|
op_finalize_derivation::<Ctx>(),
|
||||||
op_to_xml(),
|
|
||||||
];
|
];
|
||||||
ops.extend(crate::fetcher::register_ops::<Ctx>());
|
ops.extend(crate::fetcher::register_ops::<Ctx>());
|
||||||
|
|
||||||
@@ -194,8 +193,7 @@ impl<Ctx: RuntimeContext> Runtime<Ctx> {
|
|||||||
|
|
||||||
#[cfg(feature = "inspector")]
|
#[cfg(feature = "inspector")]
|
||||||
pub(crate) fn wait_for_inspector_disconnect(&mut self) {
|
pub(crate) fn wait_for_inspector_disconnect(&mut self) {
|
||||||
let _ = self
|
let _ = self.rt
|
||||||
.rt
|
|
||||||
.block_on(self.js_runtime.run_event_loop(PollEventLoopOptions {
|
.block_on(self.js_runtime.run_event_loop(PollEventLoopOptions {
|
||||||
wait_for_inspector: true,
|
wait_for_inspector: true,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
|
|||||||
@@ -365,7 +365,7 @@ async fn listen_for_new_inspectors(
|
|||||||
);
|
);
|
||||||
eprintln!("Visit chrome://inspect to connect to the debugger.");
|
eprintln!("Visit chrome://inspect to connect to the debugger.");
|
||||||
if info.wait_for_session {
|
if info.wait_for_session {
|
||||||
eprintln!("nix-js is waiting for debugger to connect.");
|
eprintln!("Deno is waiting for debugger to connect.");
|
||||||
}
|
}
|
||||||
if inspector_map.borrow_mut().insert(info.uuid, info).is_some() {
|
if inspector_map.borrow_mut().insert(info.uuid, info).is_some() {
|
||||||
panic!("Inspector UUID already in map");
|
panic!("Inspector UUID already in map");
|
||||||
@@ -456,7 +456,7 @@ impl InspectorInfo {
|
|||||||
let host_listen = format!("{}", self.host);
|
let host_listen = format!("{}", self.host);
|
||||||
let host = host.as_ref().unwrap_or(&host_listen);
|
let host = host.as_ref().unwrap_or(&host_listen);
|
||||||
json!({
|
json!({
|
||||||
"description": "nix-js",
|
"description": "deno",
|
||||||
"devtoolsFrontendUrl": self.get_frontend_url(host),
|
"devtoolsFrontendUrl": self.get_frontend_url(host),
|
||||||
"faviconUrl": "https://deno.land/favicon.ico",
|
"faviconUrl": "https://deno.land/favicon.ico",
|
||||||
"id": self.uuid.to_string(),
|
"id": self.uuid.to_string(),
|
||||||
@@ -480,7 +480,7 @@ impl InspectorInfo {
|
|||||||
|
|
||||||
fn get_title(&self) -> String {
|
fn get_title(&self) -> String {
|
||||||
format!(
|
format!(
|
||||||
"nix-js{} [pid: {}]",
|
"deno{} [pid: {}]",
|
||||||
self.thread_name
|
self.thread_name
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|n| format!(" - {n}"))
|
.map(|n| format!(" - {n}"))
|
||||||
|
|||||||
@@ -3,7 +3,8 @@ use std::str::FromStr;
|
|||||||
|
|
||||||
use hashbrown::hash_map::{Entry, HashMap};
|
use hashbrown::hash_map::{Entry, HashMap};
|
||||||
|
|
||||||
use deno_core::{FromV8, OpState, v8};
|
use deno_core::OpState;
|
||||||
|
use deno_core::v8;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use rust_embed::Embed;
|
use rust_embed::Embed;
|
||||||
|
|
||||||
@@ -11,8 +12,6 @@ use super::{NixRuntimeError, OpStateExt, RuntimeContext};
|
|||||||
use crate::error::Source;
|
use crate::error::Source;
|
||||||
use crate::store::Store as _;
|
use crate::store::Store as _;
|
||||||
|
|
||||||
type Result<T> = std::result::Result<T, NixRuntimeError>;
|
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub(super) struct RegexCache {
|
pub(super) struct RegexCache {
|
||||||
cache: HashMap<String, Regex>,
|
cache: HashMap<String, Regex>,
|
||||||
@@ -25,7 +24,7 @@ impl RegexCache {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_regex(&mut self, pattern: &str) -> std::result::Result<Regex, regex::Error> {
|
fn get_regex(&mut self, pattern: &str) -> Result<Regex, regex::Error> {
|
||||||
Ok(match self.cache.entry(pattern.to_string()) {
|
Ok(match self.cache.entry(pattern.to_string()) {
|
||||||
Entry::Occupied(occupied) => occupied.get().clone(),
|
Entry::Occupied(occupied) => occupied.get().clone(),
|
||||||
Entry::Vacant(vacant) => {
|
Entry::Vacant(vacant) => {
|
||||||
@@ -45,7 +44,7 @@ pub(crate) struct CorePkgs;
|
|||||||
pub(super) fn op_import<Ctx: RuntimeContext>(
|
pub(super) fn op_import<Ctx: RuntimeContext>(
|
||||||
state: &mut OpState,
|
state: &mut OpState,
|
||||||
#[string] path: String,
|
#[string] path: String,
|
||||||
) -> Result<String> {
|
) -> std::result::Result<String, NixRuntimeError> {
|
||||||
let _span = tracing::info_span!("op_import", path = %path).entered();
|
let _span = tracing::info_span!("op_import", path = %path).entered();
|
||||||
let ctx: &mut Ctx = state.get_ctx_mut();
|
let ctx: &mut Ctx = state.get_ctx_mut();
|
||||||
|
|
||||||
@@ -94,7 +93,7 @@ pub(super) fn op_scoped_import<Ctx: RuntimeContext>(
|
|||||||
state: &mut OpState,
|
state: &mut OpState,
|
||||||
#[string] path: String,
|
#[string] path: String,
|
||||||
#[serde] scope: Vec<String>,
|
#[serde] scope: Vec<String>,
|
||||||
) -> Result<String> {
|
) -> std::result::Result<String, NixRuntimeError> {
|
||||||
let _span = tracing::info_span!("op_scoped_import", path = %path).entered();
|
let _span = tracing::info_span!("op_scoped_import", path = %path).entered();
|
||||||
let ctx: &mut Ctx = state.get_ctx_mut();
|
let ctx: &mut Ctx = state.get_ctx_mut();
|
||||||
|
|
||||||
@@ -120,7 +119,7 @@ pub(super) fn op_scoped_import<Ctx: RuntimeContext>(
|
|||||||
|
|
||||||
#[deno_core::op2]
|
#[deno_core::op2]
|
||||||
#[string]
|
#[string]
|
||||||
pub(super) fn op_read_file(#[string] path: String) -> Result<String> {
|
pub(super) fn op_read_file(#[string] path: String) -> std::result::Result<String, NixRuntimeError> {
|
||||||
Ok(std::fs::read_to_string(&path).map_err(|e| format!("Failed to read {}: {}", path, e))?)
|
Ok(std::fs::read_to_string(&path).map_err(|e| format!("Failed to read {}: {}", path, e))?)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -141,7 +140,9 @@ pub(super) fn op_path_exists(#[string] path: String) -> bool {
|
|||||||
|
|
||||||
#[deno_core::op2]
|
#[deno_core::op2]
|
||||||
#[string]
|
#[string]
|
||||||
pub(super) fn op_read_file_type(#[string] path: String) -> Result<String> {
|
pub(super) fn op_read_file_type(
|
||||||
|
#[string] path: String,
|
||||||
|
) -> std::result::Result<String, NixRuntimeError> {
|
||||||
let path = Path::new(&path);
|
let path = Path::new(&path);
|
||||||
let metadata = std::fs::symlink_metadata(path)
|
let metadata = std::fs::symlink_metadata(path)
|
||||||
.map_err(|e| format!("Failed to read file type for {}: {}", path.display(), e))?;
|
.map_err(|e| format!("Failed to read file type for {}: {}", path.display(), e))?;
|
||||||
@@ -164,7 +165,7 @@ pub(super) fn op_read_file_type(#[string] path: String) -> Result<String> {
|
|||||||
#[serde]
|
#[serde]
|
||||||
pub(super) fn op_read_dir(
|
pub(super) fn op_read_dir(
|
||||||
#[string] path: String,
|
#[string] path: String,
|
||||||
) -> Result<std::collections::HashMap<String, String>> {
|
) -> std::result::Result<std::collections::HashMap<String, String>, NixRuntimeError> {
|
||||||
let path = Path::new(&path);
|
let path = Path::new(&path);
|
||||||
|
|
||||||
if !path.is_dir() {
|
if !path.is_dir() {
|
||||||
@@ -209,7 +210,7 @@ pub(super) fn op_read_dir(
|
|||||||
pub(super) fn op_resolve_path(
|
pub(super) fn op_resolve_path(
|
||||||
#[string] current_dir: String,
|
#[string] current_dir: String,
|
||||||
#[string] path: String,
|
#[string] path: String,
|
||||||
) -> Result<String> {
|
) -> std::result::Result<String, NixRuntimeError> {
|
||||||
let _span = tracing::debug_span!("op_resolve_path").entered();
|
let _span = tracing::debug_span!("op_resolve_path").entered();
|
||||||
tracing::debug!(current_dir, path);
|
tracing::debug!(current_dir, path);
|
||||||
|
|
||||||
@@ -260,7 +261,7 @@ pub(super) fn op_make_placeholder(#[string] output: String) -> String {
|
|||||||
pub(super) fn op_decode_span<Ctx: RuntimeContext>(
|
pub(super) fn op_decode_span<Ctx: RuntimeContext>(
|
||||||
state: &mut OpState,
|
state: &mut OpState,
|
||||||
#[string] span_str: String,
|
#[string] span_str: String,
|
||||||
) -> Result<serde_json::Value> {
|
) -> std::result::Result<serde_json::Value, NixRuntimeError> {
|
||||||
let parts: Vec<&str> = span_str.split(':').collect();
|
let parts: Vec<&str> = span_str.split(':').collect();
|
||||||
if parts.len() != 3 {
|
if parts.len() != 3 {
|
||||||
return Ok(serde_json::json!({
|
return Ok(serde_json::json!({
|
||||||
@@ -316,7 +317,7 @@ pub(super) struct ParsedHash {
|
|||||||
pub(super) fn op_parse_hash(
|
pub(super) fn op_parse_hash(
|
||||||
#[string] hash_str: String,
|
#[string] hash_str: String,
|
||||||
#[string] algo: Option<String>,
|
#[string] algo: Option<String>,
|
||||||
) -> Result<ParsedHash> {
|
) -> std::result::Result<ParsedHash, NixRuntimeError> {
|
||||||
use nix_compat::nixhash::{HashAlgo, NixHash};
|
use nix_compat::nixhash::{HashAlgo, NixHash};
|
||||||
|
|
||||||
let hash_algo = algo
|
let hash_algo = algo
|
||||||
@@ -346,7 +347,7 @@ pub(super) fn op_add_path<Ctx: RuntimeContext>(
|
|||||||
#[string] name: Option<String>,
|
#[string] name: Option<String>,
|
||||||
recursive: bool,
|
recursive: bool,
|
||||||
#[string] sha256: Option<String>,
|
#[string] sha256: Option<String>,
|
||||||
) -> Result<String> {
|
) -> std::result::Result<String, NixRuntimeError> {
|
||||||
use nix_compat::nixhash::{HashAlgo, NixHash};
|
use nix_compat::nixhash::{HashAlgo, NixHash};
|
||||||
use sha2::{Digest, Sha256};
|
use sha2::{Digest, Sha256};
|
||||||
use std::fs;
|
use std::fs;
|
||||||
@@ -422,7 +423,7 @@ pub(super) fn op_add_path<Ctx: RuntimeContext>(
|
|||||||
pub(super) fn op_store_path<Ctx: RuntimeContext>(
|
pub(super) fn op_store_path<Ctx: RuntimeContext>(
|
||||||
state: &mut OpState,
|
state: &mut OpState,
|
||||||
#[string] path: String,
|
#[string] path: String,
|
||||||
) -> Result<String> {
|
) -> std::result::Result<String, NixRuntimeError> {
|
||||||
use crate::store::validate_store_path;
|
use crate::store::validate_store_path;
|
||||||
|
|
||||||
let ctx: &Ctx = state.get_ctx();
|
let ctx: &Ctx = state.get_ctx();
|
||||||
@@ -445,7 +446,7 @@ pub(super) fn op_to_file<Ctx: RuntimeContext>(
|
|||||||
#[string] name: String,
|
#[string] name: String,
|
||||||
#[string] contents: String,
|
#[string] contents: String,
|
||||||
#[serde] references: Vec<String>,
|
#[serde] references: Vec<String>,
|
||||||
) -> Result<String> {
|
) -> std::result::Result<String, NixRuntimeError> {
|
||||||
let ctx: &Ctx = state.get_ctx();
|
let ctx: &Ctx = state.get_ctx();
|
||||||
let store = ctx.get_store();
|
let store = ctx.get_store();
|
||||||
let store_path = store
|
let store_path = store
|
||||||
@@ -460,7 +461,7 @@ pub(super) fn op_to_file<Ctx: RuntimeContext>(
|
|||||||
pub(super) fn op_copy_path_to_store<Ctx: RuntimeContext>(
|
pub(super) fn op_copy_path_to_store<Ctx: RuntimeContext>(
|
||||||
state: &mut OpState,
|
state: &mut OpState,
|
||||||
#[string] path: String,
|
#[string] path: String,
|
||||||
) -> Result<String> {
|
) -> std::result::Result<String, NixRuntimeError> {
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
let path_obj = Path::new(&path);
|
let path_obj = Path::new(&path);
|
||||||
@@ -489,7 +490,7 @@ pub(super) fn op_copy_path_to_store<Ctx: RuntimeContext>(
|
|||||||
|
|
||||||
#[deno_core::op2]
|
#[deno_core::op2]
|
||||||
#[string]
|
#[string]
|
||||||
pub(super) fn op_get_env(#[string] key: String) -> Result<String> {
|
pub(super) fn op_get_env(#[string] key: String) -> std::result::Result<String, NixRuntimeError> {
|
||||||
match std::env::var(key) {
|
match std::env::var(key) {
|
||||||
Ok(val) => Ok(val),
|
Ok(val) => Ok(val),
|
||||||
Err(std::env::VarError::NotPresent) => Ok("".into()),
|
Err(std::env::VarError::NotPresent) => Ok("".into()),
|
||||||
@@ -499,12 +500,14 @@ pub(super) fn op_get_env(#[string] key: String) -> Result<String> {
|
|||||||
|
|
||||||
#[deno_core::op2]
|
#[deno_core::op2]
|
||||||
#[serde]
|
#[serde]
|
||||||
pub(super) fn op_walk_dir(#[string] path: String) -> Result<Vec<(String, String)>> {
|
pub(super) fn op_walk_dir(
|
||||||
|
#[string] path: String,
|
||||||
|
) -> std::result::Result<Vec<(String, String)>, NixRuntimeError> {
|
||||||
fn walk_recursive(
|
fn walk_recursive(
|
||||||
base: &Path,
|
base: &Path,
|
||||||
current: &Path,
|
current: &Path,
|
||||||
results: &mut Vec<(String, String)>,
|
results: &mut Vec<(String, String)>,
|
||||||
) -> Result<()> {
|
) -> std::result::Result<(), NixRuntimeError> {
|
||||||
let entries = std::fs::read_dir(current)
|
let entries = std::fs::read_dir(current)
|
||||||
.map_err(|e| NixRuntimeError::from(format!("failed to read directory: {}", e)))?;
|
.map_err(|e| NixRuntimeError::from(format!("failed to read directory: {}", e)))?;
|
||||||
|
|
||||||
@@ -561,7 +564,7 @@ pub(super) fn op_add_filtered_path<Ctx: RuntimeContext>(
|
|||||||
recursive: bool,
|
recursive: bool,
|
||||||
#[string] sha256: Option<String>,
|
#[string] sha256: Option<String>,
|
||||||
#[serde] include_paths: Vec<String>,
|
#[serde] include_paths: Vec<String>,
|
||||||
) -> Result<String> {
|
) -> std::result::Result<String, NixRuntimeError> {
|
||||||
use nix_compat::nixhash::{HashAlgo, NixHash};
|
use nix_compat::nixhash::{HashAlgo, NixHash};
|
||||||
use sha2::{Digest, Sha256};
|
use sha2::{Digest, Sha256};
|
||||||
use std::fs;
|
use std::fs;
|
||||||
@@ -664,7 +667,7 @@ pub(super) fn op_match(
|
|||||||
state: &mut OpState,
|
state: &mut OpState,
|
||||||
#[string] regex: String,
|
#[string] regex: String,
|
||||||
#[string] text: String,
|
#[string] text: String,
|
||||||
) -> Result<Option<Vec<Option<String>>>> {
|
) -> std::result::Result<Option<Vec<Option<String>>>, NixRuntimeError> {
|
||||||
let cache = state.borrow_mut::<RegexCache>();
|
let cache = state.borrow_mut::<RegexCache>();
|
||||||
let re = cache
|
let re = cache
|
||||||
.get_regex(&format!("^{}$", regex))
|
.get_regex(&format!("^{}$", regex))
|
||||||
@@ -689,7 +692,7 @@ pub(super) fn op_split(
|
|||||||
state: &mut OpState,
|
state: &mut OpState,
|
||||||
#[string] regex: String,
|
#[string] regex: String,
|
||||||
#[string] text: String,
|
#[string] text: String,
|
||||||
) -> Result<Vec<SplitResult>> {
|
) -> std::result::Result<Vec<SplitResult>, NixRuntimeError> {
|
||||||
let cache = state.borrow_mut::<RegexCache>();
|
let cache = state.borrow_mut::<RegexCache>();
|
||||||
let re = cache
|
let re = cache
|
||||||
.get_regex(®ex)
|
.get_regex(®ex)
|
||||||
@@ -738,39 +741,41 @@ pub(super) enum NixJsonValue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> deno_core::convert::ToV8<'a> for NixJsonValue {
|
impl<'a> deno_core::convert::ToV8<'a> for NixJsonValue {
|
||||||
type Error = NixRuntimeError;
|
type Error = deno_error::JsErrorBox;
|
||||||
|
|
||||||
fn to_v8<'i>(
|
fn to_v8<'i>(
|
||||||
self,
|
self,
|
||||||
scope: &mut v8::PinScope<'a, 'i>,
|
scope: &mut v8::PinScope<'a, 'i>,
|
||||||
) -> std::result::Result<v8::Local<'a, v8::Value>, Self::Error> {
|
) -> std::result::Result<v8::Local<'a, v8::Value>, Self::Error> {
|
||||||
Ok(match self {
|
match self {
|
||||||
Self::Null => v8::null(scope).into() ,
|
Self::Null => Ok(v8::null(scope).into()),
|
||||||
Self::Bool(b) => v8::Boolean::new(scope, b).into() ,
|
Self::Bool(b) => Ok(v8::Boolean::new(scope, b).into()),
|
||||||
Self::Int(i) => v8::BigInt::new_from_i64(scope, i).into() ,
|
Self::Int(i) => Ok(v8::BigInt::new_from_i64(scope, i).into()),
|
||||||
Self::Float(f) => v8::Number::new(scope, f).into() ,
|
Self::Float(f) => Ok(v8::Number::new(scope, f).into()),
|
||||||
Self::Str(s) => v8::String::new(scope, &s)
|
Self::Str(s) => v8::String::new(scope, &s)
|
||||||
.map(|s| s.into())
|
.map(|s| s.into())
|
||||||
.ok_or("failed to create v8 string")?,
|
.ok_or_else(|| deno_error::JsErrorBox::type_error("failed to create v8 string")),
|
||||||
Self::Arr(arr) => {
|
Self::Arr(arr) => {
|
||||||
let elements = arr
|
let elements = arr
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|v| v.to_v8(scope))
|
.map(|v| v.to_v8(scope))
|
||||||
.collect::<std::result::Result<Vec<_>, _>>()?;
|
.collect::<std::result::Result<Vec<_>, _>>()?;
|
||||||
v8::Array::new_with_elements(scope, &elements).into()
|
Ok(v8::Array::new_with_elements(scope, &elements).into())
|
||||||
}
|
}
|
||||||
Self::Obj(entries) => {
|
Self::Obj(entries) => {
|
||||||
let obj = v8::Object::new(scope);
|
let obj = v8::Object::new(scope);
|
||||||
for (k, v) in entries {
|
for (k, v) in entries {
|
||||||
let key: v8::Local<v8::Value> = v8::String::new(scope, &k)
|
let key: v8::Local<v8::Value> = v8::String::new(scope, &k)
|
||||||
.ok_or("failed to create v8 string")?
|
.ok_or_else(|| {
|
||||||
|
deno_error::JsErrorBox::type_error("failed to create v8 string")
|
||||||
|
})?
|
||||||
.into();
|
.into();
|
||||||
let val = v.to_v8(scope)?;
|
let val = v.to_v8(scope)?;
|
||||||
obj.set(scope, key, val);
|
obj.set(scope, key, val);
|
||||||
}
|
}
|
||||||
obj.into()
|
Ok(obj.into())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -810,7 +815,7 @@ impl DrvHashCache {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn toml_to_nix(value: toml::Value) -> Result<NixJsonValue> {
|
fn toml_to_nix(value: toml::Value) -> std::result::Result<NixJsonValue, NixRuntimeError> {
|
||||||
match value {
|
match value {
|
||||||
toml::Value::String(s) => Ok(NixJsonValue::Str(s)),
|
toml::Value::String(s) => Ok(NixJsonValue::Str(s)),
|
||||||
toml::Value::Integer(i) => Ok(NixJsonValue::Int(i)),
|
toml::Value::Integer(i) => Ok(NixJsonValue::Int(i)),
|
||||||
@@ -834,14 +839,18 @@ fn toml_to_nix(value: toml::Value) -> Result<NixJsonValue> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[deno_core::op2]
|
#[deno_core::op2]
|
||||||
pub(super) fn op_from_json(#[string] json_str: String) -> Result<NixJsonValue> {
|
pub(super) fn op_from_json(
|
||||||
|
#[string] json_str: String,
|
||||||
|
) -> std::result::Result<NixJsonValue, NixRuntimeError> {
|
||||||
let parsed: serde_json::Value = serde_json::from_str(&json_str)
|
let parsed: serde_json::Value = serde_json::from_str(&json_str)
|
||||||
.map_err(|e| NixRuntimeError::from(format!("builtins.fromJSON: {e}")))?;
|
.map_err(|e| NixRuntimeError::from(format!("builtins.fromJSON: {e}")))?;
|
||||||
Ok(json_to_nix(parsed))
|
Ok(json_to_nix(parsed))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[deno_core::op2]
|
#[deno_core::op2]
|
||||||
pub(super) fn op_from_toml(#[string] toml_str: String) -> Result<NixJsonValue> {
|
pub(super) fn op_from_toml(
|
||||||
|
#[string] toml_str: String,
|
||||||
|
) -> std::result::Result<NixJsonValue, NixRuntimeError> {
|
||||||
let parsed: toml::Value = toml::from_str(&toml_str)
|
let parsed: toml::Value = toml::from_str(&toml_str)
|
||||||
.map_err(|e| NixRuntimeError::from(format!("while parsing TOML: {e}")))?;
|
.map_err(|e| NixRuntimeError::from(format!("while parsing TOML: {e}")))?;
|
||||||
toml_to_nix(parsed)
|
toml_to_nix(parsed)
|
||||||
@@ -889,7 +898,7 @@ fn output_path_name(drv_name: &str, output: &str) -> String {
|
|||||||
pub(super) fn op_finalize_derivation<Ctx: RuntimeContext>(
|
pub(super) fn op_finalize_derivation<Ctx: RuntimeContext>(
|
||||||
state: &mut OpState,
|
state: &mut OpState,
|
||||||
#[serde] input: FinalizeDerivationInput,
|
#[serde] input: FinalizeDerivationInput,
|
||||||
) -> Result<FinalizeDerivationOutput> {
|
) -> std::result::Result<FinalizeDerivationOutput, NixRuntimeError> {
|
||||||
use crate::derivation::{DerivationData, OutputInfo};
|
use crate::derivation::{DerivationData, OutputInfo};
|
||||||
use crate::string_context::extract_input_drvs_and_srcs;
|
use crate::string_context::extract_input_drvs_and_srcs;
|
||||||
|
|
||||||
@@ -1095,7 +1104,10 @@ fn op_make_fixed_output_path_impl(
|
|||||||
|
|
||||||
#[deno_core::op2]
|
#[deno_core::op2]
|
||||||
#[string]
|
#[string]
|
||||||
pub(super) fn op_hash_string(#[string] algo: String, #[string] data: String) -> Result<String> {
|
pub(super) fn op_hash_string(
|
||||||
|
#[string] algo: String,
|
||||||
|
#[string] data: String,
|
||||||
|
) -> std::result::Result<String, NixRuntimeError> {
|
||||||
use sha2::{Digest, Sha256, Sha512};
|
use sha2::{Digest, Sha256, Sha512};
|
||||||
|
|
||||||
let hash_bytes: Vec<u8> = match algo.as_str() {
|
let hash_bytes: Vec<u8> = match algo.as_str() {
|
||||||
@@ -1132,7 +1144,10 @@ pub(super) fn op_hash_string(#[string] algo: String, #[string] data: String) ->
|
|||||||
|
|
||||||
#[deno_core::op2]
|
#[deno_core::op2]
|
||||||
#[string]
|
#[string]
|
||||||
pub(super) fn op_hash_file(#[string] algo: String, #[string] path: String) -> Result<String> {
|
pub(super) fn op_hash_file(
|
||||||
|
#[string] algo: String,
|
||||||
|
#[string] path: String,
|
||||||
|
) -> std::result::Result<String, NixRuntimeError> {
|
||||||
let data = std::fs::read(&path)
|
let data = std::fs::read(&path)
|
||||||
.map_err(|e| NixRuntimeError::from(format!("cannot read '{}': {}", path, e)))?;
|
.map_err(|e| NixRuntimeError::from(format!("cannot read '{}': {}", path, e)))?;
|
||||||
|
|
||||||
@@ -1172,7 +1187,9 @@ pub(super) fn op_hash_file(#[string] algo: String, #[string] path: String) -> Re
|
|||||||
|
|
||||||
#[deno_core::op2]
|
#[deno_core::op2]
|
||||||
#[string]
|
#[string]
|
||||||
pub(super) fn op_convert_hash(#[serde] input: ConvertHashInput) -> Result<String> {
|
pub(super) fn op_convert_hash(
|
||||||
|
#[serde] input: ConvertHashInput,
|
||||||
|
) -> std::result::Result<String, NixRuntimeError> {
|
||||||
use nix_compat::nixhash::{HashAlgo, NixHash};
|
use nix_compat::nixhash::{HashAlgo, NixHash};
|
||||||
|
|
||||||
let hash_algo = input
|
let hash_algo = input
|
||||||
@@ -1212,540 +1229,3 @@ pub(super) struct ConvertHashInput {
|
|||||||
#[serde(rename = "toHashFormat")]
|
#[serde(rename = "toHashFormat")]
|
||||||
to_format: String,
|
to_format: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct XmlCtx<'s> {
|
|
||||||
force_fn: v8::Local<'s, v8::Function>,
|
|
||||||
is_thunk: v8::Local<'s, v8::Symbol>,
|
|
||||||
has_context: v8::Local<'s, v8::Symbol>,
|
|
||||||
is_path: v8::Local<'s, v8::Symbol>,
|
|
||||||
primop_meta: v8::Local<'s, v8::Symbol>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'s> XmlCtx<'s> {
|
|
||||||
fn new<'i>(
|
|
||||||
scope: &mut v8::PinScope<'s, 'i>,
|
|
||||||
nix_obj: v8::Local<'s, v8::Object>,
|
|
||||||
) -> Result<Self> {
|
|
||||||
let get_fn = |scope: &v8::PinScope<'s, 'i>, name: &str| {
|
|
||||||
let key = v8::String::new(scope, name).ok_or("v8 string" )?;
|
|
||||||
let val = nix_obj
|
|
||||||
.get(scope, key.into())
|
|
||||||
.ok_or_else(|| format!("no {name}") )?;
|
|
||||||
v8::Local::<v8::Function>::try_from(val)
|
|
||||||
.map_err(|e| format!("{name} not function: {e}") )
|
|
||||||
};
|
|
||||||
let get_sym = |scope: &v8::PinScope<'s, 'i>, name: &str| {
|
|
||||||
let key = v8::String::new(scope, name).ok_or("v8 string" )?;
|
|
||||||
let val = nix_obj
|
|
||||||
.get(scope, key.into())
|
|
||||||
.ok_or_else(|| format!("no {name}") )?;
|
|
||||||
v8::Local::<v8::Symbol>::try_from(val).map_err(|e| format!("{name} not symbol: {e}") )
|
|
||||||
};
|
|
||||||
Ok(Self {
|
|
||||||
force_fn: get_fn(scope, "force")?,
|
|
||||||
is_thunk: get_sym(scope, "IS_THUNK")?,
|
|
||||||
has_context: get_sym(scope, "HAS_CONTEXT")?,
|
|
||||||
is_path: get_sym(scope, "IS_PATH")?,
|
|
||||||
primop_meta: get_sym(scope, "PRIMOP_METADATA")?,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct XmlWriter {
|
|
||||||
buf: String,
|
|
||||||
context: Vec<String>,
|
|
||||||
drvs_seen: hashbrown::HashSet<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl XmlWriter {
|
|
||||||
fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
buf: String::with_capacity(4096),
|
|
||||||
context: Vec::new(),
|
|
||||||
drvs_seen: hashbrown::HashSet::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn indent(&mut self, depth: usize) {
|
|
||||||
for _ in 0..depth {
|
|
||||||
self.buf.push_str(" ");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn escape_attr(&mut self, s: &str) {
|
|
||||||
for ch in s.chars() {
|
|
||||||
match ch {
|
|
||||||
'"' => self.buf.push_str("""),
|
|
||||||
'<' => self.buf.push_str("<"),
|
|
||||||
'>' => self.buf.push_str(">"),
|
|
||||||
'&' => self.buf.push_str("&"),
|
|
||||||
'\n' => self.buf.push_str("
"),
|
|
||||||
'\r' => self.buf.push_str("
"),
|
|
||||||
'\t' => self.buf.push_str("	"),
|
|
||||||
c => self.buf.push(c),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn force<'s>(
|
|
||||||
&self,
|
|
||||||
val: v8::Local<'s, v8::Value>,
|
|
||||||
scope: &mut v8::PinScope<'s, '_>,
|
|
||||||
ctx: &XmlCtx<'s>,
|
|
||||||
) -> Result<v8::Local<'s, v8::Value>> {
|
|
||||||
let undef = v8::undefined(scope);
|
|
||||||
ctx.force_fn
|
|
||||||
.call(scope, undef.into(), &[val])
|
|
||||||
.ok_or_else(|| ("force() threw an exception").into())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn has_sym<'s>(
|
|
||||||
obj: v8::Local<'s, v8::Object>,
|
|
||||||
scope: &mut v8::PinScope<'s, '_>,
|
|
||||||
sym: v8::Local<'s, v8::Symbol>,
|
|
||||||
) -> bool {
|
|
||||||
matches!(obj.get(scope, sym.into()), Some(v) if v.is_true())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn extract_str<'s>(
|
|
||||||
&self,
|
|
||||||
val: v8::Local<'s, v8::Value>,
|
|
||||||
scope: &mut v8::PinScope<'s, '_>,
|
|
||||||
ctx: &XmlCtx<'s>,
|
|
||||||
) -> Option<String> {
|
|
||||||
if val.is_string() {
|
|
||||||
return Some(val.to_rust_string_lossy(scope));
|
|
||||||
}
|
|
||||||
if val.is_object() {
|
|
||||||
let obj = val.to_object(scope)?;
|
|
||||||
if Self::has_sym(obj, scope, ctx.has_context) {
|
|
||||||
let key = v8::String::new(scope, "value")?;
|
|
||||||
let s = obj.get(scope, key.into())?;
|
|
||||||
return Some(s.to_rust_string_lossy(scope));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
fn collect_context<'s>(
|
|
||||||
&mut self,
|
|
||||||
obj: v8::Local<'s, v8::Object>,
|
|
||||||
scope: &mut v8::PinScope<'s, '_>,
|
|
||||||
) {
|
|
||||||
let context_key = match v8::String::new(scope, "context") {
|
|
||||||
Some(k) => k,
|
|
||||||
None => return,
|
|
||||||
};
|
|
||||||
let ctx_val = match obj.get(scope, context_key.into()) {
|
|
||||||
Some(v) => v,
|
|
||||||
None => return,
|
|
||||||
};
|
|
||||||
let global = scope.get_current_context().global(scope);
|
|
||||||
let array_key = match v8::String::new(scope, "Array") {
|
|
||||||
Some(k) => k,
|
|
||||||
None => return,
|
|
||||||
};
|
|
||||||
let array_obj = match global
|
|
||||||
.get(scope, array_key.into())
|
|
||||||
.and_then(|v| v.to_object(scope))
|
|
||||||
{
|
|
||||||
Some(o) => o,
|
|
||||||
None => return,
|
|
||||||
};
|
|
||||||
let from_key = match v8::String::new(scope, "from") {
|
|
||||||
Some(k) => k,
|
|
||||||
None => return,
|
|
||||||
};
|
|
||||||
let from_fn = match array_obj
|
|
||||||
.get(scope, from_key.into())
|
|
||||||
.and_then(|v| v8::Local::<v8::Function>::try_from(v).ok())
|
|
||||||
{
|
|
||||||
Some(f) => f,
|
|
||||||
None => return,
|
|
||||||
};
|
|
||||||
let arr_val = match from_fn.call(scope, array_obj.into(), &[ctx_val]) {
|
|
||||||
Some(v) => v,
|
|
||||||
None => return,
|
|
||||||
};
|
|
||||||
let arr = match v8::Local::<v8::Array>::try_from(arr_val) {
|
|
||||||
Ok(a) => a,
|
|
||||||
Err(_) => return,
|
|
||||||
};
|
|
||||||
for i in 0..arr.length() {
|
|
||||||
if let Some(elem) = arr.get_index(scope, i) {
|
|
||||||
self.context.push(elem.to_rust_string_lossy(scope));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_derivation<'s>(obj: v8::Local<'s, v8::Object>, scope: &mut v8::PinScope<'s, '_>) -> bool {
|
|
||||||
let key = match v8::String::new(scope, "type") {
|
|
||||||
Some(k) => k,
|
|
||||||
None => return false,
|
|
||||||
};
|
|
||||||
match obj.get(scope, key.into()) {
|
|
||||||
Some(v) if v.is_string() => v.to_rust_string_lossy(scope) == "derivation",
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_value<'s>(
|
|
||||||
&mut self,
|
|
||||||
val: v8::Local<'s, v8::Value>,
|
|
||||||
scope: &mut v8::PinScope<'s, '_>,
|
|
||||||
ctx: &XmlCtx<'s>,
|
|
||||||
depth: usize,
|
|
||||||
) -> Result<()> {
|
|
||||||
let val = self.force(val, scope, ctx)?;
|
|
||||||
|
|
||||||
if val.is_null() {
|
|
||||||
self.indent(depth);
|
|
||||||
self.buf.push_str("<null />\n");
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
if val.is_true() {
|
|
||||||
self.indent(depth);
|
|
||||||
self.buf.push_str("<bool value=\"true\" />\n");
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
if val.is_false() {
|
|
||||||
self.indent(depth);
|
|
||||||
self.buf.push_str("<bool value=\"false\" />\n");
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
if val.is_big_int() {
|
|
||||||
let bi = val.to_big_int(scope).ok_or("bigint" )?;
|
|
||||||
let (i, _) = bi.i64_value();
|
|
||||||
self.indent(depth);
|
|
||||||
self.buf.push_str("<int value=\"");
|
|
||||||
self.buf.push_str(&i.to_string());
|
|
||||||
self.buf.push_str("\" />\n");
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
if val.is_number() {
|
|
||||||
let n = val.number_value(scope).ok_or("number" )?;
|
|
||||||
self.indent(depth);
|
|
||||||
self.buf.push_str("<float value=\"");
|
|
||||||
self.buf.push_str(&format!("{}", n));
|
|
||||||
self.buf.push_str("\" />\n");
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
if val.is_string() {
|
|
||||||
let s = val.to_rust_string_lossy(scope);
|
|
||||||
self.indent(depth);
|
|
||||||
self.buf.push_str("<string value=\"");
|
|
||||||
self.escape_attr(&s);
|
|
||||||
self.buf.push_str("\" />\n");
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
if val.is_array() {
|
|
||||||
let arr = v8::Local::<v8::Array>::try_from(val).map_err(|e| e.to_string() )?;
|
|
||||||
self.indent(depth);
|
|
||||||
self.buf.push_str("<list>\n");
|
|
||||||
for i in 0..arr.length() {
|
|
||||||
let elem = arr.get_index(scope, i).ok_or("array elem" )?;
|
|
||||||
self.write_value(elem, scope, ctx, depth + 1)?;
|
|
||||||
}
|
|
||||||
self.indent(depth);
|
|
||||||
self.buf.push_str("</list>\n");
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
if val.is_function() {
|
|
||||||
return self.write_function(val, scope, ctx, depth);
|
|
||||||
}
|
|
||||||
if val.is_object() {
|
|
||||||
let obj = val.to_object(scope).ok_or("to_object" )?;
|
|
||||||
|
|
||||||
if Self::has_sym(obj, scope, ctx.has_context) {
|
|
||||||
let key = v8::String::new(scope, "value").ok_or("v8 str" )?;
|
|
||||||
let s = obj
|
|
||||||
.get(scope, key.into())
|
|
||||||
.ok_or("value" )?
|
|
||||||
.to_rust_string_lossy(scope);
|
|
||||||
self.collect_context(obj, scope);
|
|
||||||
self.indent(depth);
|
|
||||||
self.buf.push_str("<string value=\"");
|
|
||||||
self.escape_attr(&s);
|
|
||||||
self.buf.push_str("\" />\n");
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
if Self::has_sym(obj, scope, ctx.is_path) {
|
|
||||||
let key = v8::String::new(scope, "value").ok_or("v8 str" )?;
|
|
||||||
let s = obj
|
|
||||||
.get(scope, key.into())
|
|
||||||
.ok_or("value" )?
|
|
||||||
.to_rust_string_lossy(scope);
|
|
||||||
self.indent(depth);
|
|
||||||
self.buf.push_str("<path value=\"");
|
|
||||||
self.escape_attr(&s);
|
|
||||||
self.buf.push_str("\" />\n");
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
if Self::has_sym(obj, scope, ctx.is_thunk) {
|
|
||||||
self.indent(depth);
|
|
||||||
self.buf.push_str("<unevaluated />\n");
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
return self.write_attrs(obj, scope, ctx, depth);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.indent(depth);
|
|
||||||
self.buf.push_str("<unevaluated />\n");
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_attrs<'s>(
|
|
||||||
&mut self,
|
|
||||||
obj: v8::Local<'s, v8::Object>,
|
|
||||||
scope: &mut v8::PinScope<'s, '_>,
|
|
||||||
ctx: &XmlCtx<'s>,
|
|
||||||
depth: usize,
|
|
||||||
) -> Result<()> {
|
|
||||||
if Self::is_derivation(obj, scope) {
|
|
||||||
self.indent(depth);
|
|
||||||
self.buf.push_str("<derivation");
|
|
||||||
|
|
||||||
let drv_path_key = v8::String::new(scope, "drvPath").ok_or("v8 str" )?;
|
|
||||||
let drv_str = if let Some(drv_val) = obj.get(scope, drv_path_key.into()) {
|
|
||||||
let forced = self.force(drv_val, scope, ctx)?;
|
|
||||||
let s = self.extract_str(forced, scope, ctx);
|
|
||||||
if let Some(ref s) = s {
|
|
||||||
self.buf.push_str(" drvPath=\"");
|
|
||||||
self.escape_attr(s);
|
|
||||||
self.buf.push('"');
|
|
||||||
}
|
|
||||||
s
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
let out_path_key = v8::String::new(scope, "outPath").ok_or("v8 str" )?;
|
|
||||||
if let Some(out_val) = obj.get(scope, out_path_key.into()) {
|
|
||||||
let forced = self.force(out_val, scope, ctx)?;
|
|
||||||
if let Some(ref s) = self.extract_str(forced, scope, ctx) {
|
|
||||||
self.buf.push_str(" outPath=\"");
|
|
||||||
self.escape_attr(s);
|
|
||||||
self.buf.push('"');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.buf.push_str(">\n");
|
|
||||||
|
|
||||||
let is_repeated = if let Some(ref dp) = drv_str {
|
|
||||||
!self.drvs_seen.insert(dp.clone())
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
};
|
|
||||||
|
|
||||||
if is_repeated {
|
|
||||||
self.indent(depth + 1);
|
|
||||||
self.buf.push_str("<repeated />\n");
|
|
||||||
} else {
|
|
||||||
self.write_attrs_sorted(obj, scope, ctx, depth + 1)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.indent(depth);
|
|
||||||
self.buf.push_str("</derivation>\n");
|
|
||||||
} else {
|
|
||||||
self.indent(depth);
|
|
||||||
self.buf.push_str("<attrs>\n");
|
|
||||||
self.write_attrs_sorted(obj, scope, ctx, depth + 1)?;
|
|
||||||
self.indent(depth);
|
|
||||||
self.buf.push_str("</attrs>\n");
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_attrs_sorted<'s>(
|
|
||||||
&mut self,
|
|
||||||
obj: v8::Local<'s, v8::Object>,
|
|
||||||
scope: &mut v8::PinScope<'s, '_>,
|
|
||||||
ctx: &XmlCtx<'s>,
|
|
||||||
depth: usize,
|
|
||||||
) -> Result<()> {
|
|
||||||
let keys = obj
|
|
||||||
.get_own_property_names(scope, v8::GetPropertyNamesArgsBuilder::new().build())
|
|
||||||
.ok_or("property names" )?;
|
|
||||||
|
|
||||||
let mut key_strings: Vec<String> = Vec::with_capacity(keys.length() as usize);
|
|
||||||
for i in 0..keys.length() {
|
|
||||||
let key = keys.get_index(scope, i).ok_or("key index" )?;
|
|
||||||
key_strings.push(key.to_rust_string_lossy(scope));
|
|
||||||
}
|
|
||||||
key_strings.sort();
|
|
||||||
|
|
||||||
for key_str in &key_strings {
|
|
||||||
let v8_key = v8::String::new(scope, key_str).ok_or("v8 str" )?;
|
|
||||||
let val = obj
|
|
||||||
.get(scope, v8_key.into())
|
|
||||||
.ok_or("attr value" )?;
|
|
||||||
|
|
||||||
self.indent(depth);
|
|
||||||
self.buf.push_str("<attr name=\"");
|
|
||||||
self.escape_attr(key_str);
|
|
||||||
self.buf.push_str("\">\n");
|
|
||||||
|
|
||||||
self.write_value(val, scope, ctx, depth + 1)?;
|
|
||||||
|
|
||||||
self.indent(depth);
|
|
||||||
self.buf.push_str("</attr>\n");
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_function<'s>(
|
|
||||||
&mut self,
|
|
||||||
val: v8::Local<'s, v8::Value>,
|
|
||||||
scope: &mut v8::PinScope<'s, '_>,
|
|
||||||
ctx: &XmlCtx<'s>,
|
|
||||||
depth: usize,
|
|
||||||
) -> Result<()> {
|
|
||||||
let obj = val.to_object(scope).ok_or("fn to_object" )?;
|
|
||||||
|
|
||||||
if let Some(meta) = obj.get(scope, ctx.primop_meta.into())
|
|
||||||
&& meta.is_object()
|
|
||||||
&& !meta.is_null_or_undefined()
|
|
||||||
{
|
|
||||||
self.indent(depth);
|
|
||||||
self.buf.push_str("<unevaluated />\n");
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
let args_key = v8::String::new(scope, "args").ok_or("v8 str" )?;
|
|
||||||
let args_val = obj.get(scope, args_key.into());
|
|
||||||
|
|
||||||
match args_val {
|
|
||||||
Some(args) if args.is_object() && !args.is_null_or_undefined() => {
|
|
||||||
let args_obj = args.to_object(scope).ok_or("args to_object" )?;
|
|
||||||
|
|
||||||
let req_key = v8::String::new(scope, "required").ok_or("v8 str" )?;
|
|
||||||
let opt_key = v8::String::new(scope, "optional").ok_or("v8 str" )?;
|
|
||||||
let ellipsis_key = v8::String::new(scope, "ellipsis").ok_or("v8 str" )?;
|
|
||||||
|
|
||||||
let mut all_formals: Vec<String> = Vec::new();
|
|
||||||
if let Some(req) = args_obj.get(scope, req_key.into())
|
|
||||||
&& let Ok(arr) = v8::Local::<v8::Array>::try_from(req)
|
|
||||||
{
|
|
||||||
for i in 0..arr.length() {
|
|
||||||
if let Some(elem) = arr.get_index(scope, i) {
|
|
||||||
all_formals.push(elem.to_rust_string_lossy(scope));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if let Some(opt) = args_obj.get(scope, opt_key.into())
|
|
||||||
&& let Ok(arr) = v8::Local::<v8::Array>::try_from(opt)
|
|
||||||
{
|
|
||||||
for i in 0..arr.length() {
|
|
||||||
if let Some(elem) = arr.get_index(scope, i) {
|
|
||||||
all_formals.push(elem.to_rust_string_lossy(scope));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
all_formals.sort();
|
|
||||||
|
|
||||||
let has_ellipsis = matches!(
|
|
||||||
args_obj.get(scope, ellipsis_key.into()),
|
|
||||||
Some(v) if v.is_true()
|
|
||||||
);
|
|
||||||
|
|
||||||
self.indent(depth);
|
|
||||||
self.buf.push_str("<function>\n");
|
|
||||||
self.indent(depth + 1);
|
|
||||||
if has_ellipsis {
|
|
||||||
self.buf.push_str("<attrspat ellipsis=\"1\">\n");
|
|
||||||
} else {
|
|
||||||
self.buf.push_str("<attrspat>\n");
|
|
||||||
}
|
|
||||||
for formal in &all_formals {
|
|
||||||
self.indent(depth + 2);
|
|
||||||
self.buf.push_str("<attr name=\"");
|
|
||||||
self.escape_attr(formal);
|
|
||||||
self.buf.push_str("\" />\n");
|
|
||||||
}
|
|
||||||
self.indent(depth + 1);
|
|
||||||
self.buf.push_str("</attrspat>\n");
|
|
||||||
self.indent(depth);
|
|
||||||
self.buf.push_str("</function>\n");
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
let source = val
|
|
||||||
.to_detail_string(scope)
|
|
||||||
.map(|s| s.to_rust_string_lossy(scope));
|
|
||||||
let param = source.as_deref().and_then(extract_lambda_param);
|
|
||||||
self.indent(depth);
|
|
||||||
self.buf.push_str("<function>\n");
|
|
||||||
self.indent(depth + 1);
|
|
||||||
self.buf.push_str("<varpat name=\"");
|
|
||||||
self.buf.push_str(param.unwrap_or("x"));
|
|
||||||
self.buf.push_str("\" />\n");
|
|
||||||
self.indent(depth);
|
|
||||||
self.buf.push_str("</function>\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn extract_lambda_param(source: &str) -> Option<&str> {
|
|
||||||
let s = source.trim();
|
|
||||||
let arrow_pos = s.find("=>")?;
|
|
||||||
let before = s[..arrow_pos].trim();
|
|
||||||
if before.starts_with('(') && before.ends_with(')') {
|
|
||||||
let inner = before[1..before.len() - 1].trim();
|
|
||||||
if !inner.is_empty() && !inner.contains(',') {
|
|
||||||
return Some(inner);
|
|
||||||
}
|
|
||||||
} else if !before.is_empty()
|
|
||||||
&& !before.contains(' ')
|
|
||||||
&& !before.contains('(')
|
|
||||||
&& before
|
|
||||||
.chars()
|
|
||||||
.all(|c| c.is_alphanumeric() || c == '_' || c == '$')
|
|
||||||
{
|
|
||||||
return Some(before);
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) struct ToXmlResult {
|
|
||||||
pub xml: String,
|
|
||||||
pub context: Vec<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> FromV8<'a> for ToXmlResult {
|
|
||||||
type Error = NixRuntimeError;
|
|
||||||
|
|
||||||
fn from_v8(
|
|
||||||
scope: &mut v8::PinScope<'a, '_>,
|
|
||||||
value: v8::Local<'a, v8::Value>,
|
|
||||||
) -> std::result::Result<Self, Self::Error> {
|
|
||||||
let global = scope.get_current_context().global(scope);
|
|
||||||
let nix_key = v8::String::new(scope, "Nix").ok_or("v8 string" )?;
|
|
||||||
let nix_obj = global
|
|
||||||
.get(scope, nix_key.into())
|
|
||||||
.ok_or("no Nix global" )?
|
|
||||||
.to_object(scope)
|
|
||||||
.ok_or("Nix not object" )?;
|
|
||||||
|
|
||||||
let ctx = XmlCtx::new(scope, nix_obj)?;
|
|
||||||
|
|
||||||
let mut writer = XmlWriter::new();
|
|
||||||
writer
|
|
||||||
.buf
|
|
||||||
.push_str("<?xml version='1.0' encoding='utf-8'?>\n<expr>\n");
|
|
||||||
writer.write_value(value, scope, &ctx, 1)?;
|
|
||||||
writer.buf.push_str("</expr>\n");
|
|
||||||
|
|
||||||
Ok(ToXmlResult {
|
|
||||||
xml: writer.buf,
|
|
||||||
context: writer.context,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[deno_core::op2]
|
|
||||||
#[serde]
|
|
||||||
pub(super) fn op_to_xml(#[scoped] value: ToXmlResult) -> (String, Vec<String>) {
|
|
||||||
(value.xml, value.context)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -241,8 +241,14 @@ eval_okay_test!(
|
|||||||
tail_call_1
|
tail_call_1
|
||||||
);
|
);
|
||||||
eval_okay_test!(tojson);
|
eval_okay_test!(tojson);
|
||||||
eval_okay_test!(toxml);
|
eval_okay_test!(
|
||||||
eval_okay_test!(toxml2);
|
#[ignore = "not implemented: toXML"]
|
||||||
|
toxml
|
||||||
|
);
|
||||||
|
eval_okay_test!(
|
||||||
|
#[ignore = "not implemented: toXML"]
|
||||||
|
toxml2
|
||||||
|
);
|
||||||
eval_okay_test!(tryeval);
|
eval_okay_test!(tryeval);
|
||||||
eval_okay_test!(types);
|
eval_okay_test!(types);
|
||||||
eval_okay_test!(versions);
|
eval_okay_test!(versions);
|
||||||
|
|||||||
Reference in New Issue
Block a user