1use crate::{
8 capability::CapabilityName,
9 effect::Effect,
10 env::Cx,
11 error::{Error, Result},
12 id::{ShapeId, Symbol},
13 ref_id::Ref,
14 term::OpKey,
15 value::Value,
16};
17
18#[derive(Clone, Debug)]
26pub struct OpSpec {
27 pub key: OpKey,
29 pub subject: Ref,
31 pub args_shape: Ref,
33 pub result_shape: Ref,
35 pub effects: Vec<Symbol>,
37 pub requires: Vec<CapabilityName>,
39}
40
41impl OpSpec {
42 pub fn new(key: OpKey, subject: Ref, args_shape: Ref, result_shape: Ref) -> Self {
44 Self {
45 key,
46 subject,
47 args_shape,
48 result_shape,
49 effects: Vec::new(),
50 requires: Vec::new(),
51 }
52 }
53
54 pub fn with_effects(mut self, effects: Vec<Symbol>) -> Self {
56 self.effects = effects;
57 self
58 }
59
60 pub fn requiring(mut self, capability: CapabilityName) -> Self {
62 self.requires.push(capability);
63 self
64 }
65
66 pub fn with_requirements(mut self, requires: Vec<CapabilityName>) -> Self {
68 self.requires = requires;
69 self
70 }
71}
72
73pub trait Op: Send + Sync {
80 fn spec(&self) -> &OpSpec;
82 fn invoke_authorized(&self, cx: &mut Cx, input: Value) -> Result<Step>;
84}
85
86#[derive(Clone, Debug)]
88pub enum Step {
89 Value(Value),
91 Events(Value),
93 Suspended(Box<Effect>),
95}
96
97pub struct ResolvedOp<'a> {
103 inner: ResolvedOpKind<'a>,
104}
105
106impl<'a> ResolvedOp<'a> {
107 pub fn spec(&self) -> &OpSpec {
109 match &self.inner {
110 ResolvedOpKind::Native(op) => op.spec(),
111 ResolvedOpKind::Adapter(adapter) => &adapter.spec,
112 }
113 }
114
115 pub fn invoke_authorized(&self, cx: &mut Cx, input: Value) -> Result<Step> {
117 match &self.inner {
118 ResolvedOpKind::Native(op) => op.invoke_authorized(cx, input),
119 ResolvedOpKind::Adapter(adapter) => adapter.invoke(cx, input),
120 }
121 }
122}
123
124enum ResolvedOpKind<'a> {
125 Native(&'a dyn Op),
126 Adapter(Box<crate::op_adapters::AdapterOp<'a>>),
127}
128
129pub fn invoke_op(cx: &mut Cx, target: Value, key: &OpKey, input: Value) -> Result<Step> {
136 let resolved = resolve_op(cx, &target, key)?;
137 let requires = resolved.spec().requires.clone();
138 cx.require_all(&requires)?;
139 let args_shape = resolved.spec().args_shape.clone();
140 check_shape_if_available(cx, args_shape, input.clone())?;
141 resolved.invoke_authorized(cx, input)
142}
143
144pub fn resolve_op<'a>(cx: &mut Cx, target: &'a Value, key: &OpKey) -> Result<ResolvedOp<'a>> {
150 if let Some(op) = target.object().op(key) {
151 return Ok(ResolvedOp {
152 inner: ResolvedOpKind::Native(op),
153 });
154 }
155
156 let Some(adapter) = crate::op_adapters::resolve_adapter(cx, target, key)? else {
157 return Err(Error::Eval(format!(
158 "operation {} not available",
159 format_op_key(key)
160 )));
161 };
162
163 Ok(ResolvedOp {
164 inner: ResolvedOpKind::Adapter(Box::new(adapter)),
165 })
166}
167
168pub fn check_shape_if_available(cx: &mut Cx, shape_ref: Ref, input: Value) -> Result<()> {
174 let Ref::Symbol(symbol) = shape_ref else {
175 return Ok(());
176 };
177 if is_any_shape_symbol(&symbol) {
178 return Ok(());
179 }
180
181 let Some(shape_value) = cx.registry().shape_by_symbol(&symbol).cloned() else {
182 return Ok(());
183 };
184 let Some(shape) = shape_value.object().as_shape() else {
185 return Ok(());
186 };
187 let matched = shape.check_value(cx, input)?;
188 if matched.accepted {
189 Ok(())
190 } else {
191 Err(Error::WrongShape {
192 expected: shape.id().unwrap_or(ShapeId(0)),
193 diagnostics: matched.diagnostics,
194 })
195 }
196}
197
198pub fn core_call_op_key() -> OpKey {
210 core_op_key("call")
211}
212
213pub fn core_shape_check_value_op_key() -> OpKey {
215 core_op_key("shape-check-value")
216}
217
218pub fn core_shape_check_term_op_key() -> OpKey {
220 core_op_key("shape-check-term")
221}
222
223pub fn core_shape_describe_op_key() -> OpKey {
225 core_op_key("shape-describe")
226}
227
228pub fn core_class_symbol_op_key() -> OpKey {
230 core_op_key("class-symbol")
231}
232
233pub fn core_object_encoding_op_key() -> OpKey {
235 core_op_key("object-encoding")
236}
237
238pub fn core_read_construct_op_key() -> OpKey {
240 core_op_key("read-construct")
241}
242
243pub fn core_number_domain_symbol_op_key() -> OpKey {
245 core_op_key("number-domain-symbol")
246}
247
248pub fn core_number_value_op_key() -> OpKey {
250 core_op_key("number-value")
251}
252
253pub fn core_realize_start_op_key() -> OpKey {
255 core_op_key("realize-start")
256}
257
258pub fn core_force_op_key() -> OpKey {
260 core_op_key("force")
261}
262
263pub fn core_seq_next_op_key() -> OpKey {
265 core_op_key("seq-next")
266}
267
268pub fn core_seq_close_op_key() -> OpKey {
270 core_op_key("seq-close")
271}
272
273pub fn core_list_items_op_key() -> OpKey {
275 core_op_key("list-items")
276}
277
278pub fn core_table_entries_op_key() -> OpKey {
280 core_op_key("table-entries")
281}
282
283pub fn core_dir_is_dir_op_key() -> OpKey {
285 core_op_key("dir-is-dir")
286}
287
288pub fn core_expr_snapshot_op_key() -> OpKey {
290 core_op_key("expr-snapshot")
291}
292
293pub fn core_any_ref() -> Ref {
295 core_ref("Any")
296}
297
298fn is_any_shape_symbol(symbol: &Symbol) -> bool {
299 *symbol == core_symbol("Any") || *symbol == core_symbol("AnyShape")
300}
301
302fn core_op_key(name: &str) -> OpKey {
303 OpKey::new(Symbol::new("core"), Symbol::new(name), 1)
304}
305
306pub(crate) fn core_ref(name: &str) -> Ref {
307 Ref::Symbol(core_symbol(name))
308}
309
310pub(crate) fn core_symbol(name: &str) -> Symbol {
311 Symbol::qualified("core", name)
312}
313
314fn format_op_key(key: &OpKey) -> String {
315 format!("{}:{}@v{}", key.namespace, key.name, key.version)
316}