1pub fn completion_symbols() -> &'static [&'static str] {
2 fiber::completion_symbols()
3}
4
5#[cfg_attr(not(test), allow(dead_code))]
8pub(crate) const LANGUAGE_BUILTINS: &[(&str, &[&str])] = &[
9 (
10 "evaluation",
11 &[
12 "quote",
13 "syntax-quote",
14 "do",
15 "if",
16 "let",
17 "letfn",
18 "binding",
19 "loop",
20 "recur",
21 "throw",
22 "try",
23 "fn",
24 ],
25 ),
26 (
27 "definitions",
28 &[
29 "def",
30 "declare",
31 "var",
32 "set!",
33 "defmacro",
34 ],
35 ),
36 ("namespaces", &["ns", "ns+", "require", "alias"]),
37 ("interop", &["new", "field", "."]),
38];
39
40pub(crate) fn invoke_function_sync(
41 function: Rc<Function>,
42 arguments: Vec<Value>,
43) -> Result<Value, String> {
44 fiber::invoke_function_sync(function, arguments)
45}
46
47#[derive(Debug, Clone, PartialEq, Eq)]
48pub struct ExtensionValue {
49 pub provider: String,
50 pub type_name: String,
51 pub handle: u64,
52}
53
54#[derive(Debug, Clone, PartialEq)]
58pub struct NamedField {
59 pub name: String,
60 pub properties: Option<Form>,
61 pub schema: Form,
62}
63
64impl NamedField {
65 pub(crate) fn from_form(form: &Form, kind: &str) -> Result<Self, String> {
66 let Form::Vector(parts) = form else {
67 return Err(format!("{kind} fields must be symbols or [name schema] vectors"));
68 };
69 let (name, properties, schema) = match parts.as_slice() {
70 [Form::Symbol(name), schema] => (name, None, schema),
71 [Form::Symbol(name), Form::Map(properties), schema] => {
72 (name, Some(Form::Map(properties.clone())), schema)
73 }
74 _ => {
75 return Err(format!(
76 "{kind} fields must be [name schema] or [name properties schema]"
77 ))
78 }
79 };
80 if name.is_empty() || name.contains('/') {
81 return Err(format!("{kind} field names must be unqualified symbols"));
82 }
83 Ok(Self {
84 name: name.clone(),
85 properties,
86 schema: schema.clone(),
87 })
88 }
89
90 pub(crate) fn legacy(name: &str) -> Self {
91 Self {
92 name: name.to_owned(),
93 properties: None,
94 schema: Form::Keyword("any".into()),
95 }
96 }
97
98 pub(crate) fn from_value(value: &Value, kind: &str) -> Result<Self, String> {
99 match value {
100 Value::String(name) if !name.is_empty() && !name.contains('/') => {
101 Ok(Self::legacy(name))
102 }
103 Value::Vector(_) | Value::Tuple(_) => {
104 let form = value_to_form(value)?;
105 Self::from_form(&form, kind)
106 }
107 _ => Err(format!(
108 "{kind} fields must contain field names or field specification vectors"
109 )),
110 }
111 }
112
113 pub(crate) fn schema_form(&self) -> Form {
114 let mut parts = vec![Form::Keyword(self.name.clone())];
115 if let Some(properties) = &self.properties {
116 parts.push(properties.clone());
117 }
118 parts.push(self.schema.clone());
119 Form::Vector(parts)
120 }
121}
122
123#[derive(Debug, Clone, PartialEq)]
127pub struct NamedDeclaration {
128 pub name: String,
129 pub mutable: bool,
130 pub fields: Vec<NamedField>,
131 pub schema: Form,
132 pub positional_constructor: String,
133 pub map_constructor: String,
134}
135
136impl NamedDeclaration {
137 pub(crate) fn new(name: String, mutable: bool, fields: Vec<NamedField>, schema: Form) -> Self {
138 let local_name = name.rsplit('/').next().unwrap_or(&name).to_owned();
139 Self {
140 name,
141 mutable,
142 fields,
143 schema,
144 positional_constructor: format!("->{local_name}"),
145 map_constructor: format!("map->{local_name}"),
146 }
147 }
148}
149
150pub(crate) fn named_value_schema_form(
151 type_name: &str,
152 mutable: bool,
153 fields: &[NamedField],
154) -> Form {
155 let mut parts = vec![Form::Keyword("struct".into())];
156 if mutable {
157 parts.push(Form::Map(vec![(
158 Form::Keyword("mutable?".into()),
159 Form::Bool(true),
160 )]));
161 }
162 parts.push(Form::List(vec![
163 Form::Symbol("var".into()),
164 Form::Symbol(type_name.to_owned()),
165 ]));
166 parts.extend(fields.iter().map(NamedField::schema_form));
167 Form::Vector(parts)
168}
169
170#[derive(Debug, Clone)]
171pub struct StructType {
172 pub name: String,
173 pub fields: Vec<String>,
174 pub declaration: Option<Rc<NamedDeclaration>>,
175}
176
177impl StructType {
178 pub(crate) fn detached(name: String, fields: Vec<String>) -> Self {
179 Self {
180 name,
181 fields,
182 declaration: None,
183 }
184 }
185}
186
187#[derive(Debug, Clone)]
188pub struct MutableType {
189 pub name: String,
190 pub fields: Vec<String>,
191 pub declaration: Option<Rc<NamedDeclaration>>,
192}
193
194impl MutableType {
195 #[cfg(test)]
196 pub(crate) fn detached(name: String, fields: Vec<String>) -> Self {
197 Self {
198 name,
199 fields,
200 declaration: None,
201 }
202 }
203}
204
205#[derive(Debug, Clone)]
206pub struct StructValue {
207 pub ty: Rc<StructType>,
208 pub values: POrderedMap<Value, Value>,
209 pub metadata: Option<Rc<Metadata>>,
210}
211
212#[derive(Debug, Clone)]
213pub struct MutableValue {
214 pub ty: Rc<MutableType>,
215 pub values: Rc<RefCell<Vec<Value>>>,
216 pub metadata: Option<Rc<Metadata>>,
217}
218
219#[derive(Debug, Clone)]
220pub struct GuestProtocol {
221 pub name: String,
222 pub methods: HashMap<String, usize>,
223 pub parents: Vec<String>,
224}
225
226#[derive(Debug, Clone)]
227pub struct NativeType {
228 pub name: String,
229 pub methods: Vec<String>,
230 pub availability: NativeAvailability,
231 pub capability: Option<String>,
232 pub metadata: Option<Rc<Metadata>>,
233}
234
235#[derive(Debug, Clone)]
236pub struct RuntimeSchema {
237 pub form: Form,
238 pub ast: crate::kernel::SchemaType,
239 pub origin: Option<KernelVar<Value>>,
240}
241
242#[derive(Clone)]
243struct PackageCatalogEntry {
244 descriptor: Value,
245 name: Option<String>,
246 namespaces: Vec<String>,
247 state: String,
248 pending: Option<Promise>,
249}
250
251#[derive(Clone, Default)]
252pub struct PackageCatalog {
253 entries: Rc<RefCell<HashMap<String, PackageCatalogEntry>>>,
254}
255
256impl PackageCatalog {
257 pub fn register(
258 &self,
259 coordinate: String,
260 name: Option<String>,
261 descriptor: Value,
262 namespaces: Vec<String>,
263 ) {
264 self.entries.borrow_mut().insert(
265 coordinate,
266 PackageCatalogEntry {
267 descriptor,
268 name,
269 namespaces,
270 state: "available".into(),
271 pending: None,
272 },
273 );
274 }
275
276 fn catalog_value(&self) -> Value {
277 let mut entries = self
278 .entries
279 .borrow()
280 .iter()
281 .map(|(coordinate, entry)| {
282 (
283 Value::String(coordinate.clone()),
284 package_descriptor_state(&entry.descriptor, &entry.state),
285 )
286 })
287 .collect::<Vec<_>>();
288 entries.sort_by(|(left, _), (right, _)| left.display().cmp(&right.display()));
289 Value::OrderedMap(Box::new(POrderedMap::from_iter(entries)))
290 }
291
292 fn find(&self, target: &str) -> Option<(String, Value)> {
293 self.entries
294 .borrow()
295 .iter()
296 .find_map(|(coordinate, entry)| {
297 (coordinate == target
298 || entry.name.as_deref() == Some(target)
299 || entry.namespaces.iter().any(|namespace| namespace == target))
300 .then(|| {
301 (
302 coordinate.clone(),
303 package_descriptor_state(&entry.descriptor, &entry.state),
304 )
305 })
306 })
307 }
308
309 pub fn contains_namespace(&self, namespace: &str) -> bool {
310 self.entries
311 .borrow()
312 .values()
313 .any(|entry| entry.namespaces.iter().any(|name| name == namespace))
314 }
315
316 fn coordinate_for_namespace(&self, namespace: &str) -> Option<String> {
317 self.entries
318 .borrow()
319 .iter()
320 .find_map(|(coordinate, entry)| {
321 entry
322 .namespaces
323 .iter()
324 .any(|name| name == namespace)
325 .then(|| coordinate.clone())
326 })
327 }
328
329 fn state(&self, coordinate: &str) -> Option<String> {
330 self.entries
331 .borrow()
332 .get(coordinate)
333 .map(|entry| entry.state.clone())
334 }
335
336 fn set_state(&self, coordinate: &str, state: &str) {
337 if let Some(entry) = self.entries.borrow_mut().get_mut(coordinate) {
338 entry.state = state.into();
339 }
340 }
341
342 fn pending(&self, coordinate: &str) -> Option<Promise> {
343 self.entries
344 .borrow()
345 .get(coordinate)
346 .and_then(|entry| entry.pending.clone())
347 }
348
349 fn set_pending(&self, coordinate: &str, pending: Option<Promise>) {
350 if let Some(entry) = self.entries.borrow_mut().get_mut(coordinate) {
351 entry.pending = pending;
352 }
353 }
354}
355
356fn package_descriptor_state(descriptor: &Value, state: &str) -> Value {
357 let Value::OrderedMap(values) = descriptor else {
358 return descriptor.clone();
359 };
360 Value::OrderedMap(Box::new(POrderedMap::from_iter(
361 values
362 .iter()
363 .map(|(key, value)| (key.clone(), value.clone()))
364 .chain(std::iter::once((
365 Value::Keyword("package/state".into()),
366 Value::Keyword(state.into()),
367 ))),
368 )))
369}
370
371fn package_descriptor_coordinate(descriptor: &Value) -> Option<String> {
372 let Value::OrderedMap(values) = descriptor else {
373 return None;
374 };
375 match values.get(&Value::Keyword("package/coordinate".into())) {
376 Some(Value::String(coordinate)) => Some(coordinate.clone()),
377 Some(Value::Symbol(coordinate)) => Some(coordinate.as_str().to_owned()),
378 _ => None,
379 }
380}
381
382#[derive(Debug, Clone, Copy, PartialEq, Eq)]
383pub enum NativeAvailability {
384 Portable,
385 CapabilityGated,
386 InventoryOnly,
387}
388
389#[derive(Debug, Clone, Copy, PartialEq, Eq)]
390pub struct NativeOperationDeclaration {
391 pub name: &'static str,
392 pub arity: u16,
393}
394
395pub type NativeProvider = fn(&str, &str) -> Result<Value, String>;
396
397#[derive(Debug, Clone, Copy)]
398pub struct NativeDeclaration {
399 pub namespace: &'static str,
400 pub name: &'static str,
401 pub methods: &'static [&'static str],
402 pub whole_wasm_methods: &'static [NativeOperationDeclaration],
403 pub provider: NativeProvider,
404 pub availability: NativeAvailability,
405 pub capability: Option<&'static str>,
406}
407
408impl NativeDeclaration {
409 pub fn qualified_name(self) -> String {
410 format!("{}.{}", self.namespace, self.name)
411 }
412
413 pub fn method(self, name: &str) -> bool {
414 self.methods.iter().any(|method| *method == name)
415 }
416
417 pub fn whole_wasm_method(self, name: &str) -> Option<NativeOperationDeclaration> {
418 self.whole_wasm_methods
419 .iter()
420 .copied()
421 .find(|method| method.name == name)
422 }
423}
424
425pub const NATIVE_DECLARATIONS: &[NativeDeclaration] = DECLARATIONS_DECLARATIONS;
426
427pub fn native_declarations() -> &'static [NativeDeclaration] {
428 NATIVE_DECLARATIONS
429}
430
431pub(crate) fn native_descriptor_value(declaration: NativeDeclaration) -> Value {
432 Value::NativeType(Rc::new(NativeType {
433 name: declaration.qualified_name(),
434 methods: declaration
435 .methods
436 .iter()
437 .map(|method| (*method).to_owned())
438 .collect(),
439 availability: declaration.availability,
440 capability: declaration.capability.map(str::to_owned),
441 metadata: None,
442 }))
443}
444
445pub fn native_type_values() -> Vec<(String, Value)> {
446 NATIVE_DECLARATIONS
447 .iter()
448 .map(|declaration| {
449 (declaration.name.to_owned(), native_descriptor_value(*declaration))
450 })
451 .collect()
452}
453
454pub fn native_manifest() -> Vec<String> {
460 let mut manifest = NATIVE_DECLARATIONS
461 .iter()
462 .map(|declaration| {
463 let mut methods = declaration
464 .methods
465 .iter()
466 .map(|method| format!("std.native.{}/{}", declaration.name, method))
467 .collect::<Vec<_>>();
468 methods.sort();
469 format!(
470 "native|std.native.{}|{}|{}|annotation|{}",
471 declaration.name,
472 native_availability_name(declaration.availability),
473 declaration.capability.unwrap_or_default(),
474 methods.join(",")
475 )
476 })
477 .collect::<Vec<_>>();
478 manifest.sort();
479 manifest
480}
481
482pub fn protocol_manifest() -> Vec<String> {
488 let mut manifest = protocol_declarations()
489 .iter()
490 .map(|declaration| {
491 let mut parents = declaration
492 .parents
493 .iter()
494 .map(|parent| (*parent).to_owned())
495 .collect::<Vec<_>>();
496 parents.sort();
497 let mut methods = declaration
498 .methods
499 .iter()
500 .map(|method| {
501 format!(
502 "{}/{}:{}",
503 declaration.runtime_name(),
504 method.name,
505 protocol_arity_name(method.arity)
506 )
507 })
508 .collect::<Vec<_>>();
509 methods.sort();
510 format!(
511 "protocol|{}|{}|{}|{}|annotation|{}|{}",
512 declaration.runtime_name(),
513 declaration.name,
514 protocol_availability_name(declaration.availability),
515 declaration.capability.unwrap_or_default(),
516 parents.join(","),
517 methods.join(",")
518 )
519 })
520 .collect::<Vec<_>>();
521 manifest.sort();
522 manifest
523}
524
525fn native_availability_name(availability: NativeAvailability) -> &'static str {
526 match availability {
527 NativeAvailability::Portable => "portable",
528 NativeAvailability::CapabilityGated => "capability-gated",
529 NativeAvailability::InventoryOnly => "inventory-only",
530 }
531}
532
533fn protocol_availability_name(
534 availability: crate::lang::protocol::ProtocolAvailability,
535) -> &'static str {
536 match availability {
537 crate::lang::protocol::ProtocolAvailability::Portable => "portable",
538 crate::lang::protocol::ProtocolAvailability::CapabilityGated => "capability-gated",
539 crate::lang::protocol::ProtocolAvailability::InventoryOnly => "inventory-only",
540 }
541}
542
543fn protocol_arity_name(arity: crate::lang::protocol::ProtocolArity) -> String {
544 match arity {
545 crate::lang::protocol::ProtocolArity::Fixed(value) => value.to_string(),
546 crate::lang::protocol::ProtocolArity::Variadic { .. } => "-1".into(),
547 }
548}
549
550pub(crate) fn protocol_declarations() -> &'static [crate::lang::protocol::ProtocolDeclaration] {
551 crate::lang::protocol::protocol_declarations()
552}
553
554pub fn builtin_protocol_namespace(protocol: &str) -> String {
555 let simple = protocol.strip_prefix("std.foundation/").unwrap_or(protocol);
556 crate::lang::protocol::find_protocol(simple)
557 .map(|declaration| declaration.runtime_name())
558 .unwrap_or_else(|| {
559 if simple.starts_with("std.protocol.") {
560 simple.to_owned()
561 } else {
562 format!("std.protocol.{}.{}", simple.to_ascii_lowercase(), simple)
563 }
564 })
565}
566
567pub(crate) fn builtin_protocol_name(protocol: &str) -> String {
568 let simple = protocol.strip_prefix("std.foundation/").unwrap_or(protocol);
569 crate::lang::protocol::find_protocol(simple)
570 .map(|declaration| declaration.runtime_name())
571 .unwrap_or_else(|| protocol.to_owned())
572}
573
574pub(crate) fn canonical_protocol_name(protocol: &str) -> String {
575 builtin_protocol_name(protocol)
576}
577
578pub(crate) fn canonical_intrinsic_protocol_symbol(symbol: &str) -> Option<String> {
579 let (protocol, method) = symbol.rsplit_once('/')?;
580 let canonical = canonical_protocol_name(protocol);
581 (canonical != protocol).then(|| format!("{canonical}/{method}"))
582}
583
584pub(crate) fn canonical_native_symbol(symbol: &str) -> Option<String> {
589 if let Some(method) = symbol.strip_prefix("file/") {
594 return Some(format!("std.native.File/{method}"));
595 }
596 if let Some(method) = symbol.strip_prefix("os/") {
597 return Some(format!("std.native.OS/{method}"));
598 }
599 if NATIVE_DECLARATIONS
600 .iter()
601 .any(|declaration| declaration.name == symbol)
602 {
603 return Some(format!("std.native.{symbol}"));
604 }
605 let (native_type, method) = symbol.rsplit_once('/')?;
606 NATIVE_DECLARATIONS
607 .iter()
608 .any(|declaration| declaration.name == native_type)
609 .then(|| format!("std.native.{native_type}/{method}"))
610}
611
612pub(crate) fn canonical_intrinsic_symbol(symbol: &str) -> Option<String> {
613 canonical_intrinsic_protocol_symbol(symbol).or_else(|| canonical_native_symbol(symbol))
614}
615
616pub(crate) fn canonical_intrinsic_callable_symbol(symbol: &str) -> Option<String> {
621 let canonical = canonical_intrinsic_symbol(symbol).unwrap_or_else(|| symbol.to_owned());
622 if let Some(native) = canonical.strip_prefix("std.native.") {
623 let (native_type, method) = native.split_once('/')?;
624 if NATIVE_DECLARATIONS.iter().any(|declaration| {
625 declaration.name == native_type && declaration.method(method)
626 }) {
627 return Some(canonical);
628 }
629 }
630 let (namespace, method) = canonical.split_once('/')?;
631 protocol_declarations()
632 .iter()
633 .find(|declaration| declaration.runtime_name() == namespace)
634 .filter(|declaration| declaration.methods.iter().any(|candidate| candidate.name == method))
635 .map(|_| canonical)
636}
637
638pub(crate) fn bytecode_callable_value(name: &str) -> Result<Value, String> {
642 let canonical = canonical_intrinsic_callable_symbol(name)
643 .ok_or_else(|| format!("unknown canonical builtin: {name}"))?;
644 let registry = namespace_registry()?;
645 registry
646 .resolve(&crate::lang::data::Symbol::parse(&canonical))
647 .map(|var| var.deref_value())
648 .ok_or_else(|| format!("unbound canonical builtin: {canonical}"))
649}
650
651pub fn foundation_protocol_values() -> Vec<(String, Value)> {
652 protocol_declarations()
653 .iter()
654 .filter(|declaration| declaration.availability.is_guest_visible())
655 .map(|declaration| {
656 (
657 declaration.name.to_owned(),
658 Value::Protocol(Rc::new(guest_protocol(*declaration))),
659 )
660 })
661 .collect()
662}
663
664pub fn builtin_protocol_method_values() -> Vec<(String, String, Value)> {
665 protocol_declarations()
666 .iter()
667 .filter(|declaration| declaration.availability.is_guest_visible())
668 .flat_map(|declaration| {
669 declaration.methods.iter().map(move |method| {
670 let protocol_name = declaration.runtime_name();
671 let namespace = protocol_name.clone();
672 let method_name = method.name.to_owned();
673 let display_name = format!("{namespace}/{}", method.name);
674 let arity_display_name = display_name.clone();
675 let (minimum_arity, maximum_arity) = method.arity.range();
676 let value = if protocol_name == "std.protocol.ideref.IDeref" && method.name == "deref" {
677 native_protocol_fiber_function(
678 &display_name,
679 &protocol_name,
680 &method_name,
681 minimum_arity,
682 maximum_arity.is_none(),
683 {
684 let protocol_name = protocol_name.clone();
685 let method_name = method_name.clone();
686 move |arguments| protocol_call(&protocol_name, &method_name, &arguments)
687 },
688 protocol_deref_fiber,
689 )
690 } else if protocol_name == "std.protocol.icoroutine.ICoroutine"
691 && method.name == "resume"
692 {
693 native_protocol_fiber_function(
694 &display_name,
695 &protocol_name,
696 &method_name,
697 minimum_arity,
698 maximum_arity.is_none(),
699 {
700 let protocol_name = protocol_name.clone();
701 let method_name = method_name.clone();
702 move |arguments| protocol_call(&protocol_name, &method_name, &arguments)
703 },
704 protocol_coroutine_resume_fiber,
705 )
706 } else {
707 native_variadic_function(&display_name, move |arguments| {
708 if arguments.len() < minimum_arity
709 || maximum_arity.is_some_and(|maximum| arguments.len() > maximum)
710 {
711 let expected = match maximum_arity {
712 Some(maximum) if maximum == minimum_arity => {
713 minimum_arity.to_string()
714 }
715 Some(maximum) => format!("{minimum_arity} to {maximum}"),
716 None => format!("at least {minimum_arity}"),
717 };
718 return Err(format!(
719 "protocol/arity: {arity_display_name} expects {expected} arguments, received {}",
720 arguments.len()
721 ));
722 }
723 protocol_call(&protocol_name, &method_name, &arguments)
724 })
725 };
726 (
727 namespace,
728 method.name.to_owned(),
729 value,
730 )
731 })
732 })
733 .collect()
734}
735
736fn guest_protocol(declaration: crate::lang::protocol::ProtocolDeclaration) -> GuestProtocol {
737 GuestProtocol {
738 name: declaration.runtime_name(),
739 methods: declaration
740 .methods
741 .iter()
742 .map(|method| (method.name.to_owned(), method.arity.guest_arity()))
743 .collect(),
744 parents: declaration
745 .parents
746 .iter()
747 .map(|parent| {
748 crate::lang::protocol::find_protocol(parent)
749 .map(|declaration| declaration.runtime_name())
750 .unwrap_or_else(|| (*parent).to_owned())
751 })
752 .collect(),
753 }
754}
755
756#[cfg(test)]
757mod native_work_protocol_tests {
758 use super::*;
759
760 fn methods(name: &str) -> Vec<(&'static str, usize)> {
761 protocol_declarations()
762 .iter()
763 .find(|declaration| declaration.name == name)
764 .map(|declaration| {
765 declaration
766 .methods
767 .iter()
768 .map(|method| (method.name, method.arity.guest_arity()))
769 .collect()
770 })
771 .expect("protocol must exist")
772 }
773
774 fn protocol(name: &str) -> Rc<GuestProtocol> {
775 foundation_protocol_values()
776 .into_iter()
777 .find(|(candidate, _)| candidate == name)
778 .and_then(|(_, value)| match value {
779 Value::Protocol(protocol) => Some(protocol),
780 _ => None,
781 })
782 .expect("protocol value must exist")
783 }
784
785 #[test]
786 fn protocol_aliases_resolve_to_annotation_owned_namespaces() {
787 let namespaces = crate::core::minimal_namespace_registry();
788 let assoc = namespaces
789 .resolve(&crate::lang::data::Symbol::parse("IAssoc/assoc"))
790 .expect("annotated protocol alias");
791 assert_eq!(
792 assoc.symbol().as_str(),
793 "std.protocol.iassoc.IAssoc/assoc"
794 );
795 assert_eq!(
796 crate::lang::protocol::find_protocol("IAssoc")
797 .expect("annotated protocol")
798 .runtime_name(),
799 "std.protocol.iassoc.IAssoc"
800 );
801 assert_eq!(
802 canonical_native_symbol("Base/vec"),
803 Some("std.native.Base/vec".into())
804 );
805 assert_eq!(canonical_native_symbol("std.native/Base"), None);
806 assert_eq!(
807 canonical_native_symbol("Coroutine"),
808 Some("std.native.Coroutine".into())
809 );
810 assert_eq!(
811 canonical_native_symbol("file/join"),
812 Some("std.native.File/join".into())
813 );
814 assert_eq!(
815 canonical_native_symbol("os/cwd"),
816 Some("std.native.OS/cwd".into())
817 );
818 }
819
820 #[test]
821 fn native_registry_rejects_unknown_annotated_methods() {
822 let error = crate::core::native_type_function_value("String", "missing").unwrap_err();
823 assert_eq!(
824 error,
825 "unknown annotated native method: std.native.String/missing"
826 );
827 }
828
829 #[test]
830 fn native_work_protocol_methods_are_stable() {
831 assert_eq!(methods("IWork"), vec![("work-spec", 1)]);
832 assert_eq!(methods("IWorkExecutor"), vec![("work-execute", 2)]);
833 assert_eq!(
834 methods("IWorkStore"),
835 vec![("work-query", 2), ("work-transact", 2)]
836 );
837 assert_eq!(methods("IWorkRef"), vec![("work-id", 1)]);
838 assert_eq!(
839 methods("IWorkHost"),
840 vec![("work-submit", 4), ("work-resolve", 2)]
841 );
842 assert_eq!(
843 methods("IWorkRun"),
844 vec![
845 ("work-status", 1),
846 ("work-result", 1),
847 ("work-events", 2),
848 ("work-cancel", 2),
849 ]
850 );
851 }
852
853 #[test]
854 fn native_work_protocol_parents_match_the_lifecycle_contract() {
855 assert!(protocol("IWorkExecutor").parents.is_empty());
856 assert!(protocol("IWorkStore").parents.is_empty());
857 assert_eq!(
858 protocol("IWorkHost").parents,
859 vec![crate::lang::protocol::find_protocol("IComponent")
860 .expect("annotated protocol")
861 .runtime_name()]
862 );
863 assert_eq!(
864 protocol("IWorkRun").parents,
865 vec![
866 crate::lang::protocol::find_protocol("IWorkRef")
867 .expect("annotated protocol")
868 .runtime_name(),
869 crate::lang::protocol::find_protocol("IClosed")
870 .expect("annotated protocol")
871 .runtime_name(),
872 ]
873 );
874 }
875}