From 1d928b325d482d5493c7379fbeb594284952d130 Mon Sep 17 00:00:00 2001 From: Andy Gayton Date: Fri, 24 Jan 2025 15:33:32 -0500 Subject: [PATCH] fix: surface parse errors when registering handlers --- src/handlers/handler.rs | 28 ++++++++++++++++++++++++ src/handlers/tests.rs | 48 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+) diff --git a/src/handlers/handler.rs b/src/handlers/handler.rs index bc35690..430c4e9 100644 --- a/src/handlers/handler.rs +++ b/src/handlers/handler.rs @@ -406,13 +406,41 @@ use nu_protocol::debugger::WithoutDebug; use nu_protocol::engine::{Closure, Stack, StateWorkingSet}; use nu_protocol::PipelineData; +use nu_protocol::format_shell_error; +use nu_protocol::ShellError; + fn parse_handler_configuration_script( engine: &mut nu::Engine, script: &str, ) -> Result<(Closure, HandlerConfig), Error> { let mut working_set = StateWorkingSet::new(&engine.state); + let block = parse(&mut working_set, None, script.as_bytes(), false); + // Handle parse errors + if let Some(err) = working_set.parse_errors.first() { + let shell_error = ShellError::GenericError { + error: "Parse error".into(), + msg: format!("{:?}", err), + span: Some(err.span()), + help: None, + inner: vec![], + }; + return Err(Error::from(format_shell_error(&working_set, &shell_error))); + } + + // Handle compile errors + if let Some(err) = working_set.compile_errors.first() { + let shell_error = ShellError::GenericError { + error: "Compile error".into(), + msg: format!("{:?}", err), + span: None, + help: None, + inner: vec![], + }; + return Err(Error::from(format_shell_error(&working_set, &shell_error))); + } + engine.state.merge_delta(working_set.render())?; let mut stack = Stack::new(); diff --git a/src/handlers/tests.rs b/src/handlers/tests.rs index 35220da..db9b310 100644 --- a/src/handlers/tests.rs +++ b/src/handlers/tests.rs @@ -140,6 +140,54 @@ async fn test_register_invalid_closure() { assert_no_more_frames(&mut recver).await; } +#[tokio::test] +async fn test_register_parse_error() { + let (store, _temp_dir) = setup_test_environment().await; + let options = ReadOptions::builder().follow(FollowOption::On).build(); + let mut recver = store.read(options).await; + + assert_eq!( + recver.recv().await.unwrap().topic, + "xs.threshold".to_string() + ); + + // Attempt to register a closure which should fail to parse + let frame_handler = store.append( + Frame::with_topic("invalid.register") + .hash( + store + .cas_insert( + r#" + { + process: {|frame| + .head index.html | .cas + } + } + "#, + ) + .await + .unwrap(), + ) + .build(), + ); + + // Ensure the register frame is processed + assert_eq!( + recver.recv().await.unwrap().topic, + "invalid.register".to_string() + ); + + // Expect an unregistered frame to be appended + validate_frame!( + recver.recv().await.unwrap(), { + topic: "invalid.unregistered", + handler: frame_handler, + error: "MissingPositional", + }); + + assert_no_more_frames(&mut recver).await; +} + #[tokio::test] // This test is to ensure that a handler does not process its own output async fn test_no_self_loop() {