//! Abstraction for parse-time script validation. //! //! Lives in `shared` so the manager can call into validation logic //! without taking a hard dep on the executor crate. The executor-core //! crate provides the impl by registering `Engine` as a `ScriptValidator`. use thiserror::Error; #[derive(Debug, Error)] pub enum ValidationError { #[error("invalid script source: {0}")] Syntax(String), /// v1.1.3: source compiled but failed the module-shape gate /// (top-level statements other than `fn` / `const` / `import`). #[error("module syntax error: {0}")] ModuleShape(String), } /// Output of a successful validate. v1.1.3 carries the list of literal /// `import ""` paths the script declares — the manager writes /// these into the `script_imports` dep-graph table. Endpoints may also /// have imports; the field is populated unconditionally. #[derive(Debug, Clone, Default)] pub struct ValidatedScript { /// Literal-path imports (in declaration order). Dynamic imports /// `import some_var as y;` are not captured — the resolver still /// honors them at runtime, but the dep graph only tracks names /// known at compile time. pub imports: Vec, } pub trait ScriptValidator: Send + Sync { /// Endpoint-shape validation: parse-only syntax check. Returns the /// declared (literal) imports so the manager can populate the /// dep-graph table on save. fn validate(&self, source: &str) -> Result; /// Module-shape validation: parse + reject any top-level /// statement that isn't `fn` / `const` / `import`. Default impl /// rejects every module so non-engine validators stay simple /// (tests / stubs don't need to know module rules). fn validate_module(&self, _source: &str) -> Result { Err(ValidationError::ModuleShape( "module validation not implemented by this validator".into(), )) } }