From 16a8480d29a983cb05c7fdb8f030dab1aee80b7a Mon Sep 17 00:00:00 2001 From: imxyy_soope_ Date: Mon, 16 Feb 2026 21:48:46 +0800 Subject: [PATCH] feat(cli): support eval file --- Justfile | 6 +- nix-js/src/context.rs | 4 +- nix-js/src/main.rs | 33 +++++++-- nix-js/src/runtime/inspector.rs | 117 ++++++++++++++++---------------- nix-js/src/store/daemon.rs | 4 ++ 5 files changed, 96 insertions(+), 68 deletions(-) diff --git a/Justfile b/Justfile index 5b148dc..3b74586 100644 --- a/Justfile +++ b/Justfile @@ -4,7 +4,7 @@ [no-exit-message] @eval expr: - cargo run -- eval '{{expr}}' + cargo run -- eval --expr '{{expr}}' [no-exit-message] @replr: @@ -12,7 +12,7 @@ [no-exit-message] @evalr expr: - cargo run --release -- eval '{{expr}}' + cargo run --release -- eval --expr '{{expr}}' [no-exit-message] @repli: @@ -20,4 +20,4 @@ [no-exit-message] @evali expr: - cargo run --release --features inspector -- --inspect-brk 127.0.0.1:9229 eval '{{expr}}' + cargo run --release --features inspector -- --inspect-brk 127.0.0.1:9229 eval --expr '{{expr}}' diff --git a/nix-js/src/context.rs b/nix-js/src/context.rs index 2597051..0deecc0 100644 --- a/nix-js/src/context.rs +++ b/nix-js/src/context.rs @@ -12,6 +12,8 @@ use crate::ir::{ Arg, ArgId, Bool, Builtin, ExprId, Ir, Null, ReplBinding, ScopedImportBinding, SymId, Thunk, ToIr as _, WithLookup, }; +#[cfg(feature = "inspector")] +use crate::runtime::inspector::InspectorServer; use crate::runtime::{Runtime, RuntimeContext}; use crate::store::{DaemonStore, Store, StoreConfig}; use crate::value::{Symbol, Value}; @@ -48,7 +50,7 @@ pub struct Context { ctx: Ctx, runtime: Runtime, #[cfg(feature = "inspector")] - _inspector_server: Option, + _inspector_server: Option, } macro_rules! eval { diff --git a/nix-js/src/main.rs b/nix-js/src/main.rs index 6b1c3f4..82df1db 100644 --- a/nix-js/src/main.rs +++ b/nix-js/src/main.rs @@ -1,7 +1,8 @@ +use std::path::PathBuf; use std::process::exit; use anyhow::Result; -use clap::{Parser, Subcommand}; +use clap::{Parser, Subcommand, Args}; use hashbrown::HashSet; use nix_js::context::Context; use nix_js::error::Source; @@ -25,10 +26,22 @@ struct Cli { #[derive(Subcommand)] enum Command { - Eval { expr: String }, + Eval { + #[clap(flatten)] + source: ExprSource + }, Repl, } +#[derive(Args)] +#[group(required = true, multiple = false)] +struct ExprSource { + #[clap(short, long)] + expr: Option, + #[clap(short, long)] + file: Option +} + fn create_context(#[cfg(feature = "inspector")] cli: &Cli) -> Result { #[cfg(feature = "inspector")] { @@ -50,9 +63,15 @@ fn create_context(#[cfg(feature = "inspector")] cli: &Cli) -> Result { Ok(Context::new()?) } -fn run_eval(context: &mut Context, expr: String) -> Result<()> { - let src = Source::new_eval(expr)?; - match context.eval(src) { +fn run_eval(context: &mut Context, src: ExprSource) -> Result<()> { + let src = if let Some(expr) = src.expr { + Source::new_eval(expr)? + } else if let Some(file) = src.file { + Source::new_file(file)? + } else { + unreachable!() + }; + match context.eval_shallow(src) { Ok(value) => { println!("{value}"); } @@ -131,7 +150,9 @@ fn main() -> Result<()> { )?; match cli.command { - Command::Eval { expr } => run_eval(&mut context, expr), + Command::Eval { source } => { + run_eval(&mut context, source) + } Command::Repl => run_repl(&mut context), } } diff --git a/nix-js/src/runtime/inspector.rs b/nix-js/src/runtime/inspector.rs index 0e87bd6..c939e17 100644 --- a/nix-js/src/runtime/inspector.rs +++ b/nix-js/src/runtime/inspector.rs @@ -248,9 +248,9 @@ async fn server( .boxed_local(); let json_version_response = json!({ - "Browser": name, - "Protocol-Version": "1.3", - "V8-Version": deno_core::v8::VERSION_STRING, + "Browser": name, + "Protocol-Version": "1.3", + "V8-Version": deno_core::v8::VERSION_STRING, }); // Create the server manually so it can use the Local Executor @@ -269,19 +269,20 @@ async fn server( let mut accept = pin!(listener.accept()); let stream = tokio::select! { - accept_result = &mut accept => { - match accept_result { - Ok((s, _)) => s, - Err(err) => { - eprintln!("Failed to accept inspector connection: {:?}", err); - continue; - } - } - }, + accept_result = + &mut accept => { + match accept_result { + Ok((s, _)) => s, + Err(err) => { + eprintln!("Failed to accept inspector connection: {:?}", err); + continue; + } + } + }, - _ = &mut shutdown_rx => { - break; - } + _ = &mut shutdown_rx => { + break; + } }; let io = TokioIo::new(stream); @@ -332,15 +333,15 @@ async fn server( let mut shutdown_rx = pin!(shutdown_server_rx.recv()); tokio::select! { - result = conn.as_mut() => { - if let Err(err) = result { - eprintln!("Failed to serve connection: {:?}", err); + result = conn.as_mut() => { + if let Err(err) = result { + eprintln!("Failed to serve connection: {:?}", err); + } + }, + _ = &mut shutdown_rx => { + conn.as_mut().graceful_shutdown(); + let _ = conn.await; } - }, - _ = &mut shutdown_rx => { - conn.as_mut().graceful_shutdown(); - let _ = conn.await; - } } }); } @@ -348,9 +349,9 @@ async fn server( .boxed_local(); tokio::select! { - _ = register_inspector_handler => {}, - _ = deregister_inspector_handler => unreachable!(), - _ = server_handler => {}, + _ = register_inspector_handler => {}, + _ = deregister_inspector_handler => unreachable!(), + _ = server_handler => {}, } } @@ -392,31 +393,31 @@ async fn pump_websocket_messages( ) { 'pump: loop { tokio::select! { - Some(msg) = outbound_rx.next() => { - let msg = Frame::text(msg.content.into_bytes().into()); - let _ = websocket.write_frame(msg).await; - } - Ok(msg) = websocket.read_frame() => { - match msg.opcode { - OpCode::Text => { - if let Ok(s) = String::from_utf8(msg.payload.to_vec()) { - let _ = inbound_tx.unbounded_send(s); - } - } - OpCode::Close => { - // Users don't care if there was an error coming from debugger, - // just about the fact that debugger did disconnect. - eprintln!("Debugger session ended"); - break 'pump; - } - _ => { - // Ignore other messages. - } + Some(msg) = outbound_rx.next() => { + let msg = Frame::text(msg.content.into_bytes().into()); + let _ = websocket.write_frame(msg).await; + } + Ok(msg) = websocket.read_frame() => { + match msg.opcode { + OpCode::Text => { + if let Ok(s) = String::from_utf8(msg.payload.to_vec()) { + let _ = inbound_tx.unbounded_send(s); + } + } + OpCode::Close => { + // Users don't care if there was an error coming from debugger, + // just about the fact that debugger did disconnect. + eprintln!("Debugger session ended"); + break 'pump; + } + _ => { + // Ignore other messages. + } + } + } + else => { + break 'pump; } - } - else => { - break 'pump; - } } } } @@ -456,14 +457,14 @@ impl InspectorInfo { let host_listen = format!("{}", self.host); let host = host.as_ref().unwrap_or(&host_listen); json!({ - "description": "nix-js", - "devtoolsFrontendUrl": self.get_frontend_url(host), - "faviconUrl": "https://deno.land/favicon.ico", - "id": self.uuid.to_string(), - "title": self.get_title(), - "type": "node", - "url": self.url.to_string(), - "webSocketDebuggerUrl": self.get_websocket_debugger_url(host), + "description": "nix-js", + "devtoolsFrontendUrl": self.get_frontend_url(host), + "faviconUrl": "https://deno.land/favicon.ico", + "id": self.uuid.to_string(), + "title": self.get_title(), + "type": "node", + "url": self.url.to_string(), + "webSocketDebuggerUrl": self.get_websocket_debugger_url(host), }) } diff --git a/nix-js/src/store/daemon.rs b/nix-js/src/store/daemon.rs index 3a58b8f..eed6213 100644 --- a/nix-js/src/store/daemon.rs +++ b/nix-js/src/store/daemon.rs @@ -540,6 +540,7 @@ impl NixDaemonClient { } /// Query information about a store path + #[allow(dead_code)] pub async fn query_path_info(&mut self, path: &str) -> IoResult> { let store_path = StorePath::::from_absolute_path(path.as_bytes()) .map_err(|e| IoError::new(IoErrorKind::InvalidInput, e.to_string()))?; @@ -613,6 +614,7 @@ impl NixDaemonClient { } /// Query which paths are valid + #[allow(dead_code)] pub async fn query_valid_paths(&mut self, paths: Vec) -> IoResult> { let store_paths: IoResult>> = paths .iter() @@ -717,6 +719,7 @@ impl NixDaemonConnection { } /// Query information about a store path + #[allow(dead_code)] pub async fn query_path_info(&self, path: &str) -> IoResult> { let mut client = self.client.lock().await; client.query_path_info(path).await @@ -729,6 +732,7 @@ impl NixDaemonConnection { } /// Query which paths are valid + #[allow(dead_code)] pub async fn query_valid_paths(&self, paths: Vec) -> IoResult> { let mut client = self.client.lock().await; client.query_valid_paths(paths).await