1pub mod contracts;
17pub mod host_capabilities;
18pub mod llm_options;
19pub mod runtime_type_tags;
20pub mod shapes;
21pub mod signatures;
22
23pub use contracts::{
24 BuiltinContract, BuiltinExposure, CapabilityId, EffectAccess, EffectKind, EffectSpec,
25 ResourceSelector,
26};
27
28#[derive(Debug, Clone, Copy, PartialEq, Eq)]
32pub struct BuiltinSignature {
33 pub name: &'static str,
35 pub params: &'static [Param],
39 pub returns: Ty,
42 pub type_params: &'static [&'static str],
45 pub has_rest: bool,
49 pub where_clauses: &'static [(&'static str, &'static str)],
52}
53
54impl BuiltinSignature {
55 pub const fn with_name(self, name: &'static str) -> Self {
57 Self { name, ..self }
58 }
59}
60
61#[derive(Debug, Clone, Copy, PartialEq, Eq)]
63pub struct Param {
64 pub name: &'static str,
65 pub ty: Ty,
66 pub optional: bool,
69}
70
71impl Param {
72 pub const fn new(name: &'static str, ty: Ty) -> Self {
73 Self {
74 name,
75 ty,
76 optional: false,
77 }
78 }
79
80 pub const fn optional(name: &'static str, ty: Ty) -> Self {
81 Self {
82 name,
83 ty,
84 optional: true,
85 }
86 }
87}
88
89#[derive(Debug, Clone, Copy, PartialEq, Eq)]
94pub enum Ty {
95 Named(&'static str),
99 Generic(&'static str),
102 Any,
105 Optional(&'static Ty),
107 Apply(&'static str, &'static [Ty]),
111 Union(&'static [Ty]),
114 Fn(&'static [Ty], &'static Ty),
117 Shape(&'static [ShapeFieldDescriptor]),
119 SchemaOf(&'static str),
123 Never,
125 LitInt(i64),
127 LitString(&'static str),
129}
130
131#[derive(Debug, Clone, Copy, PartialEq, Eq)]
132pub struct ShapeFieldDescriptor {
133 pub name: &'static str,
134 pub ty: Ty,
135 pub optional: bool,
136}
137
138impl ShapeFieldDescriptor {
139 pub const fn new(name: &'static str, ty: Ty) -> Self {
140 Self {
141 name,
142 ty,
143 optional: false,
144 }
145 }
146
147 pub const fn optional(name: &'static str, ty: Ty) -> Self {
148 Self {
149 name,
150 ty,
151 optional: true,
152 }
153 }
154}
155
156impl Ty {
157 pub const fn is_any(&self) -> bool {
159 matches!(self, Ty::Any)
160 }
161}
162
163impl core::fmt::Display for Ty {
164 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
170 match self {
171 Ty::Named(s) | Ty::Generic(s) => f.write_str(s),
172 Ty::Any => f.write_str("any"),
173 Ty::Never => f.write_str("never"),
174 Ty::Optional(inner) => write!(f, "{inner}?"),
175 Ty::Apply(name, args) => {
176 f.write_str(name)?;
177 f.write_str("<")?;
178 for (i, a) in args.iter().enumerate() {
179 if i > 0 {
180 f.write_str(", ")?;
181 }
182 write!(f, "{a}")?;
183 }
184 f.write_str(">")
185 }
186 Ty::Union(parts) => {
187 if let [inner, Ty::Named("nil")] = parts {
191 if !matches!(inner, Ty::Named("nil")) {
192 return write!(f, "{inner}?");
193 }
194 }
195 if let [Ty::Named("int"), Ty::Named("float")] = parts {
196 return f.write_str("number");
197 }
198 for (i, p) in parts.iter().enumerate() {
199 if i > 0 {
200 f.write_str(" | ")?;
201 }
202 write!(f, "{p}")?;
203 }
204 Ok(())
205 }
206 Ty::Fn(params, ret) => {
207 f.write_str("(")?;
208 for (i, p) in params.iter().enumerate() {
209 if i > 0 {
210 f.write_str(", ")?;
211 }
212 write!(f, "{p}")?;
213 }
214 write!(f, ") -> {ret}")
215 }
216 Ty::Shape(fields) => {
217 f.write_str("{")?;
218 for (i, fld) in fields.iter().enumerate() {
219 if i > 0 {
220 f.write_str(", ")?;
221 }
222 let name = fld.name;
223 let ty = &fld.ty;
224 write!(f, "{name}: {ty}")?;
225 if fld.optional {
226 f.write_str("?")?;
227 }
228 }
229 f.write_str("}")
230 }
231 Ty::SchemaOf(t) => write!(f, "Schema<{t}>"),
232 Ty::LitInt(n) => write!(f, "{n}"),
233 Ty::LitString(s) => write!(f, "\"{s}\""),
234 }
235 }
236}
237
238impl BuiltinSignature {
239 pub const fn simple(name: &'static str, params: &'static [Param], returns: Ty) -> Self {
243 Self {
244 name,
245 params,
246 returns,
247 type_params: &[],
248 has_rest: false,
249 where_clauses: &[],
250 }
251 }
252
253 pub const fn variadic(name: &'static str, params: &'static [Param], returns: Ty) -> Self {
256 Self {
257 name,
258 params,
259 returns,
260 type_params: &[],
261 has_rest: true,
262 where_clauses: &[],
263 }
264 }
265
266 pub const fn generic(
270 name: &'static str,
271 type_params: &'static [&'static str],
272 params: &'static [Param],
273 returns: Ty,
274 ) -> Self {
275 Self {
276 name,
277 params,
278 returns,
279 type_params,
280 has_rest: false,
281 where_clauses: &[],
282 }
283 }
284
285 pub fn required_params(&self) -> usize {
287 self.params.iter().filter(|p| !p.optional).count()
288 }
289
290 pub fn is_type_param(&self, name: &str) -> bool {
293 self.type_params.contains(&name)
294 }
295
296 pub fn is_generic(&self) -> bool {
298 !self.type_params.is_empty()
299 }
300
301 pub fn type_param_names(&self) -> Vec<String> {
305 self.type_params.iter().map(|s| (*s).to_string()).collect()
306 }
307
308 pub fn where_clause_strings(&self) -> Vec<(String, String)> {
310 self.where_clauses
311 .iter()
312 .map(|(tp, iface)| ((*tp).to_string(), (*iface).to_string()))
313 .collect()
314 }
315}
316
317impl core::fmt::Display for BuiltinSignature {
318 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
323 if !self.type_params.is_empty() {
324 f.write_str("<")?;
325 for (i, tp) in self.type_params.iter().enumerate() {
326 if i > 0 {
327 f.write_str(", ")?;
328 }
329 f.write_str(tp)?;
330 }
331 if !self.where_clauses.is_empty() {
332 f.write_str(" where ")?;
333 for (i, (tp, iface)) in self.where_clauses.iter().enumerate() {
334 if i > 0 {
335 f.write_str(", ")?;
336 }
337 write!(f, "{tp}: {iface}")?;
338 }
339 }
340 f.write_str("> ")?;
341 }
342 f.write_str(self.name)?;
343 f.write_str("(")?;
344 let last_idx = self.params.len().saturating_sub(1);
345 for (i, p) in self.params.iter().enumerate() {
346 if i > 0 {
347 f.write_str(", ")?;
348 }
349 if self.has_rest && i == last_idx {
350 f.write_str("...")?;
351 }
352 f.write_str(p.name)?;
353 if p.optional {
354 f.write_str("?")?;
355 }
356 let ty = &p.ty;
357 write!(f, ": {ty}")?;
358 }
359 let ret = &self.returns;
360 write!(f, ") -> {ret}")
361 }
362}
363
364#[derive(Debug, Clone, Copy, PartialEq, Eq)]
367pub struct BuiltinMetadata {
368 pub name: &'static str,
369 pub return_types: &'static [&'static str],
370}
371
372pub const TY_ANY: Ty = Ty::Any;
379pub const TY_BOOL: Ty = Ty::Named("bool");
380pub const TY_BYTES: Ty = Ty::Named("bytes");
381pub const TY_CLOSURE: Ty = Ty::Named("closure");
382pub const TY_DECIMAL: Ty = Ty::Named("decimal");
383pub const TY_DICT: Ty = Ty::Named("dict");
384pub const TY_DURATION: Ty = Ty::Named("duration");
385pub const TY_FLOAT: Ty = Ty::Named("float");
386pub const TY_INT: Ty = Ty::Named("int");
387pub const TY_LIST: Ty = Ty::Named("list");
388pub const TY_NEVER: Ty = Ty::Never;
389pub const TY_NIL: Ty = Ty::Named("nil");
390pub const TY_RESOURCE: Ty = Ty::Named("resource");
391pub const TY_STRING: Ty = Ty::Named("string");
392
393pub const TY_STRING_OR_NIL: Ty = Ty::Union(&[TY_STRING, TY_NIL]);
395pub const TY_INT_OR_NIL: Ty = Ty::Union(&[TY_INT, TY_NIL]);
397pub const TY_DICT_OR_NIL: Ty = Ty::Union(&[TY_DICT, TY_NIL]);
399pub const TY_BYTES_OR_NIL: Ty = Ty::Union(&[TY_BYTES, TY_NIL]);
401pub const TY_NUMBER: Ty = Ty::Union(&[TY_INT, TY_FLOAT]);
403
404#[cfg(test)]
405mod tests {
406 use super::*;
407
408 const APPLY_ARGS: &[Ty] = &[TY_DICT];
409 const FN_PARAMS: &[Ty] = &[TY_INT, TY_STRING];
410 const SHAPE_FIELDS: &[ShapeFieldDescriptor] = &[
411 ShapeFieldDescriptor::new("name", TY_STRING),
412 ShapeFieldDescriptor::optional("age", TY_INT),
413 ];
414
415 #[test]
416 fn ty_display_atomic_and_compound() {
417 assert_eq!(format!("{TY_INT}"), "int");
418 assert_eq!(format!("{TY_ANY}"), "any");
419 assert_eq!(format!("{TY_NEVER}"), "never");
420 assert_eq!(format!("{TY_STRING_OR_NIL}"), "string?");
423 let opt_int = Ty::Optional(&TY_INT);
424 assert_eq!(format!("{opt_int}"), "int?");
425 assert_eq!(format!("{TY_NUMBER}"), "number");
427 let list_dict = Ty::Apply("list", APPLY_ARGS);
428 assert_eq!(format!("{list_dict}"), "list<dict>");
429 let lit_int = Ty::LitInt(42);
430 assert_eq!(format!("{lit_int}"), "42");
431 let lit_str = Ty::LitString("pass");
432 assert_eq!(format!("{lit_str}"), "\"pass\"");
433 let schema_t = Ty::SchemaOf("T");
434 assert_eq!(format!("{schema_t}"), "Schema<T>");
435 let fn_ty = Ty::Fn(FN_PARAMS, &TY_BOOL);
436 assert_eq!(format!("{fn_ty}"), "(int, string) -> bool");
437 let shape = Ty::Shape(SHAPE_FIELDS);
438 assert_eq!(format!("{shape}"), "{name: string, age: int?}");
439 }
440
441 const BASIC_PARAMS: &[Param] = &[Param::new("a", TY_DICT), Param::new("b", TY_DICT)];
442 const REST_PARAMS: &[Param] = &[Param::new("prefix", TY_STRING), Param::new("args", TY_ANY)];
443 const OPT_PARAMS: &[Param] = &[
444 Param::new("receipt", TY_DICT),
445 Param::optional("candidate", TY_ANY),
446 ];
447 const GENERIC_PARAMS: &[Param] = &[Param::new("schema", Ty::SchemaOf("T"))];
448
449 #[test]
450 fn signature_display_basic() {
451 let sig = BuiltinSignature::simple("deep_merge", BASIC_PARAMS, TY_DICT);
452 assert_eq!(format!("{sig}"), "deep_merge(a: dict, b: dict) -> dict");
453 }
454
455 #[test]
456 fn signature_display_with_optional_and_rest() {
457 let sig = BuiltinSignature {
458 name: "io_println",
459 params: REST_PARAMS,
460 returns: TY_NIL,
461 type_params: &[],
462 has_rest: true,
463 where_clauses: &[],
464 };
465 assert_eq!(
466 format!("{sig}"),
467 "io_println(prefix: string, ...args: any) -> nil"
468 );
469
470 let opt_sig =
471 BuiltinSignature::simple("lifecycle_replay_resume_input", OPT_PARAMS, TY_DICT);
472 assert_eq!(
473 format!("{opt_sig}"),
474 "lifecycle_replay_resume_input(receipt: dict, candidate?: any) -> dict"
475 );
476 }
477
478 #[test]
479 fn signature_display_with_generics_and_where() {
480 let sig = BuiltinSignature {
481 name: "schema_parse",
482 params: GENERIC_PARAMS,
483 returns: Ty::Generic("T"),
484 type_params: &["T"],
485 has_rest: false,
486 where_clauses: &[("T", "Decode")],
487 };
488 assert_eq!(
489 format!("{sig}"),
490 "<T where T: Decode> schema_parse(schema: Schema<T>) -> T"
491 );
492 }
493}