mirror of
https://github.com/gorules/zen.git
synced 2026-10-04 00:02:18 +00:00
* feat: implement slots instead of natural language Replace the natural-language projection (`nl`) with engine-owned slot completion: for a cursor position the engine classifies the slot being edited (operand, operator, literal, member access, ...) and returns typed options, literal facts and the expected type, for policies and graphs. - zen-expression: new `slot` module (classification, literals, operators) and a lenient lexer mode for incomplete input; `nl` removed - Intellisense: typed diagnostic codes with arguments, richer inspect for hover, dedicated parser error variants (messages unchanged) - Engine: `Workspace::cursor_scope`, `slot`, `facts`; `rename_from` and `references_from`; editor spans in UTF-16 units; renames reach imported policies, `$nodes` reads, child graphs and output column heads; no `$root` completion in policies - Node bindings: `slot`, `facts`, `cursorScope`, `slotBatch` replace `nl`, `nlTokenize`, `nlEncodeString`, `nlTokenizeBatch` No change to expression evaluation: the VM, compiler, functions and the default lexer path are untouched, and parser error messages keep their text. * fix: harden slot classification, scopes and graph expectations - restore master execution order; slot scopes hide writes of the edited block and its dependents instead of relying on block ranking - token-start editing gated by slot state; operator, list and operand spans no longer splice into neighbouring tokens - interval-aware bracket pairing for half-open and reversed intervals - lenient lexer recovers from unknown characters (slots only) - graph expected type follows switch and pass-through nodes and merges compatible output schemas - sibling inference caps distinct values and analyses lazily - deterministic overload return type display * fix: order policy blocks by entity reads through closures and relationships - record reads for `#.field` in closures so entity fields read through lists become dependencies - link reads through derived lists (filter/map results) to the entity fields they carry and order the list after those fields - evaluate entity blocks on single relationships, and demand entity paths for plain reads through relationships - slot scopes keep the top-level variable when hiding written fields
258 lines
7.6 KiB
Rust
258 lines
7.6 KiB
Rust
pub(crate) mod db;
|
|
pub(crate) mod editor;
|
|
pub(crate) mod graph;
|
|
pub(crate) mod search;
|
|
pub(crate) mod slot;
|
|
pub(crate) mod types;
|
|
|
|
use std::sync::Arc;
|
|
|
|
use crate::model::DecisionContent;
|
|
use crate::policy::evaluator::EvalArtifact;
|
|
use crate::policy::raw::PolicyDocument;
|
|
use db::Db;
|
|
use zen_expression::variable::VariableType;
|
|
|
|
pub use graph::{
|
|
FunctionResolutionRequest, FunctionTypeResolver, GraphAnalysis, GraphNodeAnalysis,
|
|
GraphSignature, GraphTraceMap,
|
|
};
|
|
pub use slot::{CursorScope, ExpressionFacts, SlotResponse};
|
|
pub use types::{
|
|
BlockExecution, BlockRef, BlockTrace, Completion, ConditionTrace, ConditionalSchema, Cursor,
|
|
CursorTarget, DecisionTableExtras, DependencyNode, Diagnostic, DiagnosticCode,
|
|
DiagnosticLocation, Dictionary, DictionaryEntryInfo, DiscriminantVariant, DiscriminatedUnion,
|
|
EngineEdit, Entity, EntityField, EvaluateRequest, EvaluationError, EvaluationResult,
|
|
ExpressionKind, FieldOrigin, GuardedProperty, InputProperty, InputValidationError,
|
|
InspectResult, OutputProperty, PrepareRename, PropertyKind, ReferenceKind, ReferenceSite,
|
|
RenameTarget, SchemaFieldKind, SchemaGroup, ScopeRequest, SearchHit, SearchHitKind, Severity,
|
|
SlotRole, Span, Trace, WriteConflict, WriteTrace,
|
|
};
|
|
|
|
use types::Global;
|
|
|
|
pub struct Workspace {
|
|
db: Db,
|
|
}
|
|
|
|
impl Workspace {
|
|
pub fn new() -> Self {
|
|
Self { db: Db::new() }
|
|
}
|
|
|
|
pub fn set_document(&mut self, path: impl Into<Arc<str>>, document: DecisionContent) {
|
|
self.db.set_document(path.into(), Arc::new(document));
|
|
}
|
|
|
|
pub fn set_document_arc(&mut self, path: impl Into<Arc<str>>, document: Arc<DecisionContent>) {
|
|
self.db.set_document(path.into(), document);
|
|
}
|
|
|
|
pub fn set_policy(&mut self, path: impl Into<Arc<str>>, document: PolicyDocument) {
|
|
self.db.set_policy(path.into(), Arc::new(document));
|
|
}
|
|
|
|
pub fn set_policy_arc(&mut self, path: impl Into<Arc<str>>, document: Arc<PolicyDocument>) {
|
|
self.db.set_policy(path.into(), document);
|
|
}
|
|
|
|
pub fn remove_path(&mut self, path: &str) -> bool {
|
|
self.db.remove_document(path)
|
|
}
|
|
|
|
pub fn paths(&self) -> Vec<Arc<str>> {
|
|
self.db.document_paths()
|
|
}
|
|
|
|
pub fn get_document(&self, path: &str) -> Option<Arc<DecisionContent>> {
|
|
self.db.raw_document(path)
|
|
}
|
|
|
|
pub fn get_policy(&self, path: &str) -> Option<Arc<PolicyDocument>> {
|
|
self.db.raw_policy(path)
|
|
}
|
|
|
|
pub fn is_graph(&self, path: &str) -> bool {
|
|
self.db.is_graph(path)
|
|
}
|
|
|
|
pub fn evaluate(&self, req: &EvaluateRequest) -> Result<EvaluationResult, EvaluationError> {
|
|
self.db.evaluate(req)
|
|
}
|
|
|
|
pub fn enhance_trace(
|
|
&self,
|
|
req: &EvaluateRequest,
|
|
) -> Result<EvaluationResult, EvaluationError> {
|
|
self.db.enhance_trace(req)
|
|
}
|
|
|
|
pub fn enhance_graph_trace(
|
|
&self,
|
|
document: &Arc<str>,
|
|
trace: &GraphTraceMap,
|
|
) -> Result<Trace, EvaluationError> {
|
|
self.db.enhance_graph_trace(document, trace)
|
|
}
|
|
|
|
pub(crate) fn eval_artifact(&self, policy: &str) -> Arc<EvalArtifact> {
|
|
self.db.eval_artifact(policy)
|
|
}
|
|
|
|
pub fn entities(&self, req: &ScopeRequest) -> Vec<Entity> {
|
|
if self.db.is_graph(&req.policy_path) {
|
|
return Vec::new();
|
|
}
|
|
self.db.entities(req)
|
|
}
|
|
|
|
pub fn globals(&self, req: &ScopeRequest) -> Vec<Global> {
|
|
if self.db.is_graph(&req.policy_path) {
|
|
return Vec::new();
|
|
}
|
|
self.db.globals(req)
|
|
}
|
|
|
|
pub fn dictionaries(&self, req: &ScopeRequest) -> Vec<Dictionary> {
|
|
self.db.dictionaries(req)
|
|
}
|
|
|
|
pub fn inputs(&self, req: &ScopeRequest) -> Vec<InputProperty> {
|
|
self.db.inputs(req)
|
|
}
|
|
|
|
pub fn outputs(&self, req: &ScopeRequest) -> Vec<OutputProperty> {
|
|
self.db.outputs(req)
|
|
}
|
|
|
|
pub fn conditional_schema(&self, req: &ScopeRequest) -> ConditionalSchema {
|
|
self.db.conditional_schema(req)
|
|
}
|
|
|
|
pub fn component_members(&self, policy: &str) -> Vec<Arc<str>> {
|
|
self.db.component_members(policy)
|
|
}
|
|
|
|
pub fn cross_component_write_conflicts(&self) -> Vec<WriteConflict> {
|
|
self.db.cross_component_write_conflicts()
|
|
}
|
|
|
|
pub fn diagnostics(&self, path: &str) -> Vec<Diagnostic> {
|
|
let path_arc: Arc<str> = Arc::from(path);
|
|
(*self.db.policy_diagnostics(&path_arc)).clone()
|
|
}
|
|
|
|
pub fn all_diagnostics(&self) -> Vec<Diagnostic> {
|
|
self.db.all_diagnostics()
|
|
}
|
|
|
|
pub(crate) fn imports(&self, importer: &str, policy: &str) -> bool {
|
|
self.db.unit(importer).members.contains(policy)
|
|
}
|
|
|
|
pub(crate) fn evaluation_diagnostics(&self, entry: &str) -> Vec<Diagnostic> {
|
|
self.db.evaluation_diagnostics(&Arc::from(entry))
|
|
}
|
|
|
|
pub fn set_function_resolver(
|
|
&mut self,
|
|
resolver: impl Fn(&str, &VariableType) -> Option<String> + 'static,
|
|
) {
|
|
self.db.set_function_resolver(Some(Box::new(resolver)));
|
|
}
|
|
|
|
pub fn function_resolution_requests(&self) -> Vec<FunctionResolutionRequest> {
|
|
self.db.function_resolution_requests()
|
|
}
|
|
|
|
pub fn set_function_type(&self, source: &str, input: &VariableType, ts_type: Option<&str>) {
|
|
self.db.set_function_type(source, input, ts_type);
|
|
}
|
|
|
|
pub fn graph_analysis(&self, path: &str) -> Option<Arc<GraphAnalysis>> {
|
|
let path_arc: Arc<str> = Arc::from(path);
|
|
self.db.graph_analysis(&path_arc)
|
|
}
|
|
|
|
pub fn unchecked_nodes(&self, path: &str) -> Vec<Arc<str>> {
|
|
self.db.graph_unchecked_nodes(path)
|
|
}
|
|
|
|
pub fn inspect(&self, cursor: &Cursor) -> Option<InspectResult> {
|
|
self.db.inspect(cursor)
|
|
}
|
|
|
|
pub fn completions(&self, cursor: &Cursor) -> Vec<Completion> {
|
|
self.db.completions(cursor)
|
|
}
|
|
|
|
pub fn cursor_scope(&self, cursor: &Cursor) -> Option<CursorScope> {
|
|
self.db.cursor_scope(cursor)
|
|
}
|
|
|
|
pub fn slot(&self, cursor: &Cursor, text: &str) -> Option<SlotResponse> {
|
|
self.db.slot(cursor, text)
|
|
}
|
|
|
|
pub fn facts(&self, policy_path: &str) -> Vec<ExpressionFacts> {
|
|
self.db.facts(policy_path)
|
|
}
|
|
|
|
pub fn prepare_rename(&self, cursor: &Cursor) -> Option<PrepareRename> {
|
|
self.db.prepare_rename(cursor)
|
|
}
|
|
|
|
pub fn rename(&self, target: &RenameTarget, new_name: &str) -> Vec<EngineEdit> {
|
|
self.db.rename(target, new_name)
|
|
}
|
|
|
|
pub fn references(&self, target: &RenameTarget) -> Vec<ReferenceSite> {
|
|
self.db.references(target)
|
|
}
|
|
|
|
pub fn rename_from(
|
|
&self,
|
|
target: &RenameTarget,
|
|
new_name: &str,
|
|
origin: Option<&str>,
|
|
) -> Vec<EngineEdit> {
|
|
self.db.rename_from(target, new_name, origin)
|
|
}
|
|
|
|
pub fn references_from(
|
|
&self,
|
|
target: &RenameTarget,
|
|
origin: Option<&str>,
|
|
) -> Vec<ReferenceSite> {
|
|
self.db.references_from(target, origin)
|
|
}
|
|
|
|
pub fn search(&self, query: &str, limit: Option<u32>) -> Vec<SearchHit> {
|
|
self.db.search(query, limit)
|
|
}
|
|
|
|
pub fn input_skeleton(&self, req: &ScopeRequest) -> serde_json::Value {
|
|
self.db.input_skeleton(req)
|
|
}
|
|
|
|
pub fn dependencies(&self, target: &str) -> DependencyNode {
|
|
self.db.dependencies(target, None)
|
|
}
|
|
|
|
pub fn dependencies_scoped(&self, target: &str, document: Option<&str>) -> DependencyNode {
|
|
if let Some(doc) = document {
|
|
if self.db.is_graph(doc) {
|
|
let doc_arc: Arc<str> = Arc::from(doc);
|
|
return self.db.graph_dependencies(&doc_arc, target);
|
|
}
|
|
}
|
|
self.db.dependencies(target, document)
|
|
}
|
|
}
|
|
|
|
impl Default for Workspace {
|
|
fn default() -> Self {
|
|
Self::new()
|
|
}
|
|
}
|