blueprint_engine_core/
value.rs

1use indexmap::{IndexMap, IndexSet};
2use std::collections::HashMap;
3use std::fmt;
4use std::future::Future;
5use std::hash::{Hash, Hasher};
6use std::pin::Pin;
7use std::sync::Arc;
8use std::sync::atomic::{AtomicBool, Ordering};
9use tokio::sync::{mpsc, oneshot, Mutex, RwLock};
10
11use crate::error::{BlueprintError, Result};
12
13pub type NativeFuture = Pin<Box<dyn Future<Output = Result<Value>> + Send>>;
14pub type NativeFn = Arc<dyn Fn(Vec<Value>, HashMap<String, Value>) -> NativeFuture + Send + Sync>;
15
16#[derive(Clone)]
17pub enum Value {
18    None,
19    Bool(bool),
20    Int(i64),
21    Float(f64),
22    String(Arc<String>),
23    List(Arc<RwLock<Vec<Value>>>),
24    Dict(Arc<RwLock<IndexMap<String, Value>>>),
25    Set(Arc<RwLock<IndexSet<Value>>>),
26    Tuple(Arc<Vec<Value>>),
27    Function(Arc<UserFunction>),
28    Lambda(Arc<LambdaFunction>),
29    NativeFunction(Arc<NativeFunction>),
30    Response(Arc<HttpResponse>),
31    ProcessResult(Arc<ProcessResult>),
32    Iterator(Arc<StreamIterator>),
33    Generator(Arc<Generator>),
34    StructType(Arc<StructType>),
35    StructInstance(Arc<StructInstance>),
36}
37
38impl fmt::Debug for Value {
39    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
40        match self {
41            Value::None => write!(f, "None"),
42            Value::Bool(b) => write!(f, "Bool({b})"),
43            Value::Int(i) => write!(f, "Int({i})"),
44            Value::Float(fl) => write!(f, "Float({fl})"),
45            Value::String(s) => write!(f, "String({s:?})"),
46            Value::List(_) => write!(f, "List([...])"),
47            Value::Dict(_) => write!(f, "Dict({{...}})"),
48            Value::Set(_) => write!(f, "Set({{...}})"),
49            Value::Tuple(t) => write!(f, "Tuple({:?})", t.as_ref()),
50            Value::Function(func) => write!(f, "Function({})", func.name),
51            Value::Lambda(_) => write!(f, "Lambda"),
52            Value::NativeFunction(func) => write!(f, "NativeFunction({})", func.name),
53            Value::Response(r) => write!(f, "Response(status={})", r.status),
54            Value::ProcessResult(r) => write!(f, "ProcessResult(code={})", r.code),
55            Value::Iterator(_) => write!(f, "Iterator"),
56            Value::Generator(_) => write!(f, "Generator"),
57            Value::StructType(s) => write!(f, "StructType({})", s.name),
58            Value::StructInstance(s) => write!(f, "StructInstance({})", s.struct_type.name),
59        }
60    }
61}
62
63impl Value {
64    pub fn type_name(&self) -> &'static str {
65        match self {
66            Value::None => "NoneType",
67            Value::Bool(_) => "bool",
68            Value::Int(_) => "int",
69            Value::Float(_) => "float",
70            Value::String(_) => "string",
71            Value::List(_) => "list",
72            Value::Dict(_) => "dict",
73            Value::Set(_) => "set",
74            Value::Tuple(_) => "tuple",
75            Value::Function(_) => "function",
76            Value::Lambda(_) => "function",
77            Value::NativeFunction(_) => "builtin_function",
78            Value::Response(_) => "Response",
79            Value::ProcessResult(_) => "Result",
80            Value::Iterator(_) => "iterator",
81            Value::Generator(_) => "generator",
82            Value::StructType(_) => "type",
83            Value::StructInstance(_) => "struct",
84        }
85    }
86
87    pub fn is_truthy(&self) -> bool {
88        match self {
89            Value::None => false,
90            Value::Bool(b) => *b,
91            Value::Int(i) => *i != 0,
92            Value::Float(f) => *f != 0.0,
93            Value::String(s) => !s.is_empty(),
94            Value::List(l) => {
95                if let Ok(guard) = l.try_read() {
96                    !guard.is_empty()
97                } else {
98                    true
99                }
100            }
101            Value::Dict(d) => {
102                if let Ok(guard) = d.try_read() {
103                    !guard.is_empty()
104                } else {
105                    true
106                }
107            }
108            Value::Set(s) => {
109                if let Ok(guard) = s.try_read() {
110                    !guard.is_empty()
111                } else {
112                    true
113                }
114            }
115            Value::Tuple(t) => !t.is_empty(),
116            _ => true,
117        }
118    }
119
120    pub async fn is_truthy_async(&self) -> bool {
121        match self {
122            Value::None => false,
123            Value::Bool(b) => *b,
124            Value::Int(i) => *i != 0,
125            Value::Float(f) => *f != 0.0,
126            Value::String(s) => !s.is_empty(),
127            Value::List(l) => {
128                let guard = l.read().await;
129                !guard.is_empty()
130            }
131            Value::Dict(d) => {
132                let guard = d.read().await;
133                !guard.is_empty()
134            }
135            Value::Set(s) => {
136                let guard = s.read().await;
137                !guard.is_empty()
138            }
139            Value::Tuple(t) => !t.is_empty(),
140            _ => true,
141        }
142    }
143
144    pub fn is_none(&self) -> bool {
145        matches!(self, Value::None)
146    }
147
148    pub async fn deep_copy(&self) -> Value {
149        match self {
150            Value::List(l) => {
151                let items = l.read().await;
152                let mut copied = Vec::with_capacity(items.len());
153                for item in items.iter() {
154                    copied.push(Box::pin(item.deep_copy()).await);
155                }
156                Value::List(Arc::new(RwLock::new(copied)))
157            }
158            Value::Dict(d) => {
159                let map = d.read().await;
160                let mut copied = IndexMap::with_capacity(map.len());
161                for (k, v) in map.iter() {
162                    copied.insert(k.clone(), Box::pin(v.deep_copy()).await);
163                }
164                Value::Dict(Arc::new(RwLock::new(copied)))
165            }
166            Value::Tuple(t) => {
167                let mut copied = Vec::with_capacity(t.len());
168                for item in t.iter() {
169                    copied.push(Box::pin(item.deep_copy()).await);
170                }
171                Value::Tuple(Arc::new(copied))
172            }
173            other => other.clone(),
174        }
175    }
176
177    pub fn as_bool(&self) -> Result<bool> {
178        match self {
179            Value::Bool(b) => Ok(*b),
180            _ => Err(BlueprintError::TypeError {
181                expected: "bool".into(),
182                actual: self.type_name().into(),
183            }),
184        }
185    }
186
187    pub fn as_int(&self) -> Result<i64> {
188        match self {
189            Value::Int(i) => Ok(*i),
190            _ => Err(BlueprintError::TypeError {
191                expected: "int".into(),
192                actual: self.type_name().into(),
193            }),
194        }
195    }
196
197    pub fn as_float(&self) -> Result<f64> {
198        match self {
199            Value::Float(f) => Ok(*f),
200            Value::Int(i) => Ok(*i as f64),
201            _ => Err(BlueprintError::TypeError {
202                expected: "float".into(),
203                actual: self.type_name().into(),
204            }),
205        }
206    }
207
208    pub fn as_string(&self) -> Result<String> {
209        match self {
210            Value::String(s) => Ok(s.as_ref().clone()),
211            _ => Err(BlueprintError::TypeError {
212                expected: "string".into(),
213                actual: self.type_name().into(),
214            }),
215        }
216    }
217
218    pub fn as_str(&self) -> Result<&str> {
219        match self {
220            Value::String(s) => Ok(s.as_ref()),
221            _ => Err(BlueprintError::TypeError {
222                expected: "string".into(),
223                actual: self.type_name().into(),
224            }),
225        }
226    }
227
228    pub fn to_display_string(&self) -> String {
229        match self {
230            Value::None => "None".into(),
231            Value::Bool(b) => if *b { "True" } else { "False" }.into(),
232            Value::Int(i) => i.to_string(),
233            Value::Float(f) => {
234                if f.fract() == 0.0 {
235                    format!("{f:.1}")
236                } else {
237                    f.to_string()
238                }
239            }
240            Value::String(s) => s.as_ref().clone(),
241            Value::List(l) => {
242                match l.try_read() {
243                    Ok(guard) => {
244                        let items: Vec<String> = guard.iter().map(|v| v.repr()).collect();
245                        format!("[{}]", items.join(", "))
246                    }
247                    Err(_) => "[<locked>]".into(),
248                }
249            }
250            Value::Dict(d) => {
251                match d.try_read() {
252                    Ok(guard) => {
253                        let items: Vec<String> = guard
254                            .iter()
255                            .map(|(k, v)| format!("{:?}: {}", k, v.repr()))
256                            .collect();
257                        format!("{{{}}}", items.join(", "))
258                    }
259                    Err(_) => "{<locked>}".into(),
260                }
261            }
262            Value::Set(s) => {
263                match s.try_read() {
264                    Ok(guard) => {
265                        let items: Vec<String> = guard.iter().map(|v| v.repr()).collect();
266                        format!("{{{}}}", items.join(", "))
267                    }
268                    Err(_) => "{<locked>}".into(),
269                }
270            }
271            Value::Tuple(t) => {
272                let items: Vec<String> = t.iter().map(|v| v.repr()).collect();
273                if t.len() == 1 {
274                    format!("({},)", items[0])
275                } else {
276                    format!("({})", items.join(", "))
277                }
278            }
279            Value::Function(f) => format!("<function {}>", f.name),
280            Value::Lambda(_) => "<lambda>".into(),
281            Value::NativeFunction(f) => format!("<builtin_function {}>", f.name),
282            Value::Response(r) => format!("<Response status={}>", r.status),
283            Value::ProcessResult(r) => format!("<Result code={}>", r.code),
284            Value::Iterator(_) => "<iterator>".into(),
285            Value::Generator(_) => "<generator>".into(),
286            Value::StructType(s) => format!("<type {}>", s.name),
287            Value::StructInstance(s) => s.to_display_string(),
288        }
289    }
290
291    pub fn repr(&self) -> String {
292        match self {
293            Value::String(s) => format!("{:?}", s.as_ref()),
294            _ => self.to_display_string(),
295        }
296    }
297
298    pub fn get_attr(&self, name: &str) -> Option<Value> {
299        match self {
300            Value::Response(r) => r.get_attr(name),
301            Value::ProcessResult(r) => r.get_attr(name),
302            Value::String(s) => get_string_method(s.clone(), name),
303            Value::List(l) => get_list_method(l.clone(), name),
304            Value::Dict(d) => get_dict_method(d.clone(), name),
305            Value::Set(s) => get_set_method(s.clone(), name),
306            Value::Iterator(it) => it.get_attr(name),
307            Value::StructInstance(s) => s.get_field(name),
308            _ => None,
309        }
310    }
311
312    pub fn has_attr(&self, name: &str) -> bool {
313        self.get_attr(name).is_some()
314    }
315}
316
317impl PartialEq for Value {
318    fn eq(&self, other: &Self) -> bool {
319        match (self, other) {
320            (Value::None, Value::None) => true,
321            (Value::Bool(a), Value::Bool(b)) => a == b,
322            (Value::Int(a), Value::Int(b)) => a == b,
323            (Value::Float(a), Value::Float(b)) => a == b,
324            (Value::Int(a), Value::Float(b)) => (*a as f64) == *b,
325            (Value::Float(a), Value::Int(b)) => *a == (*b as f64),
326            (Value::String(a), Value::String(b)) => a == b,
327            (Value::Tuple(a), Value::Tuple(b)) => a == b,
328            _ => false,
329        }
330    }
331}
332
333impl Eq for Value {}
334
335impl Hash for Value {
336    fn hash<H: Hasher>(&self, state: &mut H) {
337        std::mem::discriminant(self).hash(state);
338        match self {
339            Value::None => {}
340            Value::Bool(b) => b.hash(state),
341            Value::Int(i) => i.hash(state),
342            Value::Float(f) => f.to_bits().hash(state),
343            Value::String(s) => s.hash(state),
344            Value::Tuple(t) => t.hash(state),
345            _ => {}
346        }
347    }
348}
349
350#[derive(Debug, Clone)]
351pub struct Parameter {
352    pub name: String,
353    pub default: Option<Value>,
354    pub kind: ParameterKind,
355}
356
357#[derive(Debug, Clone, Copy, PartialEq)]
358pub enum ParameterKind {
359    Positional,
360    Args,
361    Kwargs,
362}
363
364pub struct UserFunction {
365    pub name: String,
366    pub params: Vec<Parameter>,
367    pub body: Box<dyn std::any::Any + Send + Sync>,
368    pub closure: Option<Arc<dyn std::any::Any + Send + Sync>>,
369}
370
371impl fmt::Debug for UserFunction {
372    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
373        f.debug_struct("UserFunction")
374            .field("name", &self.name)
375            .field("params", &self.params)
376            .finish()
377    }
378}
379
380pub struct LambdaFunction {
381    pub params: Vec<Parameter>,
382    pub body: Box<dyn std::any::Any + Send + Sync>,
383    pub closure: Option<Arc<dyn std::any::Any + Send + Sync>>,
384}
385
386impl fmt::Debug for LambdaFunction {
387    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
388        f.debug_struct("LambdaFunction")
389            .field("params", &self.params)
390            .finish()
391    }
392}
393
394pub struct NativeFunction {
395    pub name: String,
396    pub func: NativeFn,
397}
398
399impl fmt::Debug for NativeFunction {
400    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
401        f.debug_struct("NativeFunction")
402            .field("name", &self.name)
403            .finish()
404    }
405}
406
407impl NativeFunction {
408    pub fn new<F, Fut>(name: impl Into<String>, f: F) -> Self
409    where
410        F: Fn(Vec<Value>, HashMap<String, Value>) -> Fut + Send + Sync + 'static,
411        Fut: Future<Output = Result<Value>> + Send + 'static,
412    {
413        NativeFunction {
414            name: name.into(),
415            func: Arc::new(move |args, kwargs| Box::pin(f(args, kwargs))),
416        }
417    }
418
419    pub fn new_with_state<F>(name: impl Into<String>, f: F) -> Self
420    where
421        F: Fn(Vec<Value>, HashMap<String, Value>) -> NativeFuture + Send + Sync + 'static,
422    {
423        NativeFunction {
424            name: name.into(),
425            func: Arc::new(f),
426        }
427    }
428
429    pub async fn call(&self, args: Vec<Value>, kwargs: HashMap<String, Value>) -> Result<Value> {
430        (self.func)(args, kwargs).await
431    }
432}
433
434#[derive(Debug, Clone)]
435pub struct HttpResponse {
436    pub status: i64,
437    pub body: String,
438    pub headers: HashMap<String, String>,
439}
440
441impl HttpResponse {
442    pub fn get_attr(&self, name: &str) -> Option<Value> {
443        match name {
444            "status" => Some(Value::Int(self.status)),
445            "body" => Some(Value::String(Arc::new(self.body.clone()))),
446            "headers" => {
447                let map: IndexMap<String, Value> = self
448                    .headers
449                    .iter()
450                    .map(|(k, v)| (k.clone(), Value::String(Arc::new(v.clone()))))
451                    .collect();
452                Some(Value::Dict(Arc::new(RwLock::new(map))))
453            }
454            _ => None,
455        }
456    }
457}
458
459#[derive(Debug, Clone)]
460pub struct ProcessResult {
461    pub code: i64,
462    pub stdout: String,
463    pub stderr: String,
464}
465
466impl ProcessResult {
467    pub fn get_attr(&self, name: &str) -> Option<Value> {
468        match name {
469            "code" => Some(Value::Int(self.code)),
470            "stdout" => Some(Value::String(Arc::new(self.stdout.clone()))),
471            "stderr" => Some(Value::String(Arc::new(self.stderr.clone()))),
472            _ => None,
473        }
474    }
475}
476
477fn get_string_method(s: Arc<String>, name: &str) -> Option<Value> {
478    let s_clone = s.clone();
479    match name {
480        "upper" => Some(Value::NativeFunction(Arc::new(NativeFunction::new_with_state(
481            "upper",
482            move |_args, _kwargs| {
483                let result = s_clone.to_uppercase();
484                Box::pin(async move { Ok(Value::String(Arc::new(result))) })
485            },
486        )))),
487        "lower" => {
488            let s = s.clone();
489            Some(Value::NativeFunction(Arc::new(NativeFunction::new_with_state(
490                "lower",
491                move |_args, _kwargs| {
492                    let result = s.to_lowercase();
493                    Box::pin(async move { Ok(Value::String(Arc::new(result))) })
494                },
495            ))))
496        }
497        "strip" => {
498            let s = s.clone();
499            Some(Value::NativeFunction(Arc::new(NativeFunction::new_with_state(
500                "strip",
501                move |_args, _kwargs| {
502                    let result = s.trim().to_string();
503                    Box::pin(async move { Ok(Value::String(Arc::new(result))) })
504                },
505            ))))
506        }
507        "split" => {
508            let s = s.clone();
509            Some(Value::NativeFunction(Arc::new(NativeFunction::new_with_state(
510                "split",
511                move |args, _kwargs| {
512                    let sep = if args.is_empty() {
513                        None
514                    } else {
515                        Some(args[0].to_display_string())
516                    };
517                    let parts: Vec<Value> = match sep {
518                        Some(ref sep) => s.split(sep.as_str()).map(|p| Value::String(Arc::new(p.to_string()))).collect(),
519                        None => s.split_whitespace().map(|p| Value::String(Arc::new(p.to_string()))).collect(),
520                    };
521                    Box::pin(async move { Ok(Value::List(Arc::new(tokio::sync::RwLock::new(parts)))) })
522                },
523            ))))
524        }
525        "join" => {
526            let s = s.clone();
527            Some(Value::NativeFunction(Arc::new(NativeFunction::new_with_state(
528                "join",
529                move |args, _kwargs| {
530                    let s = s.clone();
531                    Box::pin(async move {
532                        if args.is_empty() {
533                            return Err(BlueprintError::ArgumentError {
534                                message: "join() requires 1 argument".into(),
535                            });
536                        }
537                        let items = match &args[0] {
538                            Value::List(l) => l.read().await.clone(),
539                            Value::Tuple(t) => t.as_ref().clone(),
540                            _ => return Err(BlueprintError::TypeError {
541                                expected: "list or tuple".into(),
542                                actual: args[0].type_name().into(),
543                            }),
544                        };
545                        let strings: Vec<String> = items.iter().map(|v| v.to_display_string()).collect();
546                        Ok(Value::String(Arc::new(strings.join(s.as_str()))))
547                    })
548                },
549            ))))
550        }
551        "replace" => {
552            let s = s.clone();
553            Some(Value::NativeFunction(Arc::new(NativeFunction::new_with_state(
554                "replace",
555                move |args, _kwargs| {
556                    let s = s.clone();
557                    Box::pin(async move {
558                        if args.len() < 2 {
559                            return Err(BlueprintError::ArgumentError {
560                                message: "replace() requires 2 arguments".into(),
561                            });
562                        }
563                        let old = args[0].to_display_string();
564                        let new = args[1].to_display_string();
565                        let result = s.replace(&old, &new);
566                        Ok(Value::String(Arc::new(result)))
567                    })
568                },
569            ))))
570        }
571        "startswith" => {
572            let s = s.clone();
573            Some(Value::NativeFunction(Arc::new(NativeFunction::new_with_state(
574                "startswith",
575                move |args, _kwargs| {
576                    let s = s.clone();
577                    Box::pin(async move {
578                        if args.is_empty() {
579                            return Err(BlueprintError::ArgumentError {
580                                message: "startswith() requires 1 argument".into(),
581                            });
582                        }
583                        let prefix = args[0].to_display_string();
584                        Ok(Value::Bool(s.starts_with(&prefix)))
585                    })
586                },
587            ))))
588        }
589        "endswith" => {
590            let s = s.clone();
591            Some(Value::NativeFunction(Arc::new(NativeFunction::new_with_state(
592                "endswith",
593                move |args, _kwargs| {
594                    let s = s.clone();
595                    Box::pin(async move {
596                        if args.is_empty() {
597                            return Err(BlueprintError::ArgumentError {
598                                message: "endswith() requires 1 argument".into(),
599                            });
600                        }
601                        let suffix = args[0].to_display_string();
602                        Ok(Value::Bool(s.ends_with(&suffix)))
603                    })
604                },
605            ))))
606        }
607        "find" => {
608            let s = s.clone();
609            Some(Value::NativeFunction(Arc::new(NativeFunction::new_with_state(
610                "find",
611                move |args, _kwargs| {
612                    let s = s.clone();
613                    Box::pin(async move {
614                        if args.is_empty() {
615                            return Err(BlueprintError::ArgumentError {
616                                message: "find() requires 1 argument".into(),
617                            });
618                        }
619                        let needle = args[0].to_display_string();
620                        let result = s.find(&needle).map(|i| i as i64).unwrap_or(-1);
621                        Ok(Value::Int(result))
622                    })
623                },
624            ))))
625        }
626        "format" => {
627            let s = s.clone();
628            Some(Value::NativeFunction(Arc::new(NativeFunction::new_with_state(
629                "format",
630                move |args, _kwargs| {
631                    let s = s.clone();
632                    Box::pin(async move {
633                        let mut result = s.as_str().to_string();
634                        for arg in args {
635                            if let Some(pos) = result.find("{}") {
636                                result = format!("{}{}{}", &result[..pos], arg.to_display_string(), &result[pos+2..]);
637                            }
638                        }
639                        Ok(Value::String(Arc::new(result)))
640                    })
641                },
642            ))))
643        }
644        _ => None,
645    }
646}
647
648fn get_list_method(l: Arc<RwLock<Vec<Value>>>, name: &str) -> Option<Value> {
649    match name {
650        "append" => {
651            let l_clone = l.clone();
652            Some(Value::NativeFunction(Arc::new(NativeFunction::new_with_state(
653                "append",
654                move |args, _kwargs| {
655                    let l = l_clone.clone();
656                    Box::pin(async move {
657                        if args.len() != 1 {
658                            return Err(BlueprintError::ArgumentError {
659                                message: format!("append() takes exactly 1 argument ({} given)", args.len()),
660                            });
661                        }
662                        let mut list = l.write().await;
663                        list.push(args[0].clone());
664                        Ok(Value::None)
665                    })
666                },
667            ))))
668        }
669        "extend" => {
670            let l_clone = l.clone();
671            Some(Value::NativeFunction(Arc::new(NativeFunction::new_with_state(
672                "extend",
673                move |args, _kwargs| {
674                    let l = l_clone.clone();
675                    Box::pin(async move {
676                        if args.len() != 1 {
677                            return Err(BlueprintError::ArgumentError {
678                                message: format!("extend() takes exactly 1 argument ({} given)", args.len()),
679                            });
680                        }
681                        let items = match &args[0] {
682                            Value::List(other) => other.read().await.clone(),
683                            Value::Tuple(t) => t.as_ref().clone(),
684                            _ => return Err(BlueprintError::TypeError {
685                                expected: "list or tuple".into(),
686                                actual: args[0].type_name().into(),
687                            }),
688                        };
689                        let mut list = l.write().await;
690                        list.extend(items);
691                        Ok(Value::None)
692                    })
693                },
694            ))))
695        }
696        "insert" => {
697            let l_clone = l.clone();
698            Some(Value::NativeFunction(Arc::new(NativeFunction::new_with_state(
699                "insert",
700                move |args, _kwargs| {
701                    let l = l_clone.clone();
702                    Box::pin(async move {
703                        if args.len() != 2 {
704                            return Err(BlueprintError::ArgumentError {
705                                message: format!("insert() takes exactly 2 arguments ({} given)", args.len()),
706                            });
707                        }
708                        let index = args[0].as_int()? as usize;
709                        let mut list = l.write().await;
710                        let len = list.len();
711                        let index = index.min(len);
712                        list.insert(index, args[1].clone());
713                        Ok(Value::None)
714                    })
715                },
716            ))))
717        }
718        "pop" => {
719            let l_clone = l.clone();
720            Some(Value::NativeFunction(Arc::new(NativeFunction::new_with_state(
721                "pop",
722                move |args, _kwargs| {
723                    let l = l_clone.clone();
724                    Box::pin(async move {
725                        if args.len() > 1 {
726                            return Err(BlueprintError::ArgumentError {
727                                message: format!("pop() takes at most 1 argument ({} given)", args.len()),
728                            });
729                        }
730                        let mut list = l.write().await;
731                        if list.is_empty() {
732                            return Err(BlueprintError::IndexError {
733                                message: "pop from empty list".into(),
734                            });
735                        }
736                        let index = if args.is_empty() {
737                            list.len() - 1
738                        } else {
739                            let i = args[0].as_int()?;
740                            if i < 0 {
741                                (list.len() as i64 + i) as usize
742                            } else {
743                                i as usize
744                            }
745                        };
746                        if index >= list.len() {
747                            return Err(BlueprintError::IndexError {
748                                message: format!("pop index {} out of range", index),
749                            });
750                        }
751                        Ok(list.remove(index))
752                    })
753                },
754            ))))
755        }
756        "remove" => {
757            let l_clone = l.clone();
758            Some(Value::NativeFunction(Arc::new(NativeFunction::new_with_state(
759                "remove",
760                move |args, _kwargs| {
761                    let l = l_clone.clone();
762                    Box::pin(async move {
763                        if args.len() != 1 {
764                            return Err(BlueprintError::ArgumentError {
765                                message: format!("remove() takes exactly 1 argument ({} given)", args.len()),
766                            });
767                        }
768                        let mut list = l.write().await;
769                        let pos = list.iter().position(|x| x == &args[0]);
770                        match pos {
771                            Some(i) => {
772                                list.remove(i);
773                                Ok(Value::None)
774                            }
775                            None => Err(BlueprintError::ValueError {
776                                message: "value not in list".into(),
777                            }),
778                        }
779                    })
780                },
781            ))))
782        }
783        "clear" => {
784            let l_clone = l.clone();
785            Some(Value::NativeFunction(Arc::new(NativeFunction::new_with_state(
786                "clear",
787                move |_args, _kwargs| {
788                    let l = l_clone.clone();
789                    Box::pin(async move {
790                        let mut list = l.write().await;
791                        list.clear();
792                        Ok(Value::None)
793                    })
794                },
795            ))))
796        }
797        "index" => {
798            let l_clone = l.clone();
799            Some(Value::NativeFunction(Arc::new(NativeFunction::new_with_state(
800                "index",
801                move |args, _kwargs| {
802                    let l = l_clone.clone();
803                    Box::pin(async move {
804                        if args.is_empty() || args.len() > 3 {
805                            return Err(BlueprintError::ArgumentError {
806                                message: format!("index() takes 1 to 3 arguments ({} given)", args.len()),
807                            });
808                        }
809                        let list = l.read().await;
810                        let start = if args.len() > 1 { args[1].as_int()? as usize } else { 0 };
811                        let end = if args.len() > 2 { args[2].as_int()? as usize } else { list.len() };
812                        for (i, item) in list.iter().enumerate().skip(start).take(end - start) {
813                            if item == &args[0] {
814                                return Ok(Value::Int(i as i64));
815                            }
816                        }
817                        Err(BlueprintError::ValueError {
818                            message: "value not in list".into(),
819                        })
820                    })
821                },
822            ))))
823        }
824        "count" => {
825            let l_clone = l.clone();
826            Some(Value::NativeFunction(Arc::new(NativeFunction::new_with_state(
827                "count",
828                move |args, _kwargs| {
829                    let l = l_clone.clone();
830                    Box::pin(async move {
831                        if args.len() != 1 {
832                            return Err(BlueprintError::ArgumentError {
833                                message: format!("count() takes exactly 1 argument ({} given)", args.len()),
834                            });
835                        }
836                        let list = l.read().await;
837                        let count = list.iter().filter(|x| *x == &args[0]).count();
838                        Ok(Value::Int(count as i64))
839                    })
840                },
841            ))))
842        }
843        "reverse" => {
844            let l_clone = l.clone();
845            Some(Value::NativeFunction(Arc::new(NativeFunction::new_with_state(
846                "reverse",
847                move |_args, _kwargs| {
848                    let l = l_clone.clone();
849                    Box::pin(async move {
850                        let mut list = l.write().await;
851                        list.reverse();
852                        Ok(Value::None)
853                    })
854                },
855            ))))
856        }
857        "copy" => {
858            let l_clone = l.clone();
859            Some(Value::NativeFunction(Arc::new(NativeFunction::new_with_state(
860                "copy",
861                move |_args, _kwargs| {
862                    let l = l_clone.clone();
863                    Box::pin(async move {
864                        let list = l.read().await;
865                        Ok(Value::List(Arc::new(RwLock::new(list.clone()))))
866                    })
867                },
868            ))))
869        }
870        _ => None,
871    }
872}
873
874fn get_dict_method(d: Arc<RwLock<IndexMap<String, Value>>>, name: &str) -> Option<Value> {
875    match name {
876        "get" => {
877            let d_clone = d.clone();
878            Some(Value::NativeFunction(Arc::new(NativeFunction::new_with_state(
879                "get",
880                move |args, _kwargs| {
881                    let d = d_clone.clone();
882                    Box::pin(async move {
883                        if args.is_empty() || args.len() > 2 {
884                            return Err(BlueprintError::ArgumentError {
885                                message: format!("get() takes 1 or 2 arguments ({} given)", args.len()),
886                            });
887                        }
888                        let key = match &args[0] {
889                            Value::String(s) => s.as_ref().clone(),
890                            v => return Err(BlueprintError::TypeError {
891                                expected: "string".into(),
892                                actual: v.type_name().into(),
893                            }),
894                        };
895                        let default = if args.len() == 2 {
896                            args[1].clone()
897                        } else {
898                            Value::None
899                        };
900                        let map = d.read().await;
901                        Ok(map.get(&key).cloned().unwrap_or(default))
902                    })
903                },
904            ))))
905        }
906        "keys" => {
907            let d_clone = d.clone();
908            Some(Value::NativeFunction(Arc::new(NativeFunction::new_with_state(
909                "keys",
910                move |_args, _kwargs| {
911                    let d = d_clone.clone();
912                    Box::pin(async move {
913                        let map = d.read().await;
914                        let keys: Vec<Value> = map.keys().map(|k| Value::String(Arc::new(k.clone()))).collect();
915                        Ok(Value::List(Arc::new(RwLock::new(keys))))
916                    })
917                },
918            ))))
919        }
920        "values" => {
921            let d_clone = d.clone();
922            Some(Value::NativeFunction(Arc::new(NativeFunction::new_with_state(
923                "values",
924                move |_args, _kwargs| {
925                    let d = d_clone.clone();
926                    Box::pin(async move {
927                        let map = d.read().await;
928                        let values: Vec<Value> = map.values().cloned().collect();
929                        Ok(Value::List(Arc::new(RwLock::new(values))))
930                    })
931                },
932            ))))
933        }
934        "items" => {
935            let d_clone = d.clone();
936            Some(Value::NativeFunction(Arc::new(NativeFunction::new_with_state(
937                "items",
938                move |_args, _kwargs| {
939                    let d = d_clone.clone();
940                    Box::pin(async move {
941                        let map = d.read().await;
942                        let items: Vec<Value> = map.iter()
943                            .map(|(k, v)| Value::Tuple(Arc::new(vec![Value::String(Arc::new(k.clone())), v.clone()])))
944                            .collect();
945                        Ok(Value::List(Arc::new(RwLock::new(items))))
946                    })
947                },
948            ))))
949        }
950        _ => None,
951    }
952}
953
954fn get_set_method(s: Arc<RwLock<IndexSet<Value>>>, name: &str) -> Option<Value> {
955    match name {
956        "add" => {
957            let s_clone = s.clone();
958            Some(Value::NativeFunction(Arc::new(NativeFunction::new_with_state(
959                "add",
960                move |args, _kwargs| {
961                    let s = s_clone.clone();
962                    Box::pin(async move {
963                        if args.len() != 1 {
964                            return Err(BlueprintError::ArgumentError {
965                                message: format!("add() takes exactly 1 argument ({} given)", args.len()),
966                            });
967                        }
968                        let mut set = s.write().await;
969                        set.insert(args[0].clone());
970                        Ok(Value::None)
971                    })
972                },
973            ))))
974        }
975        "remove" => {
976            let s_clone = s.clone();
977            Some(Value::NativeFunction(Arc::new(NativeFunction::new_with_state(
978                "remove",
979                move |args, _kwargs| {
980                    let s = s_clone.clone();
981                    Box::pin(async move {
982                        if args.len() != 1 {
983                            return Err(BlueprintError::ArgumentError {
984                                message: format!("remove() takes exactly 1 argument ({} given)", args.len()),
985                            });
986                        }
987                        let mut set = s.write().await;
988                        if !set.shift_remove(&args[0]) {
989                            return Err(BlueprintError::KeyError {
990                                key: args[0].to_display_string(),
991                            });
992                        }
993                        Ok(Value::None)
994                    })
995                },
996            ))))
997        }
998        "discard" => {
999            let s_clone = s.clone();
1000            Some(Value::NativeFunction(Arc::new(NativeFunction::new_with_state(
1001                "discard",
1002                move |args, _kwargs| {
1003                    let s = s_clone.clone();
1004                    Box::pin(async move {
1005                        if args.len() != 1 {
1006                            return Err(BlueprintError::ArgumentError {
1007                                message: format!("discard() takes exactly 1 argument ({} given)", args.len()),
1008                            });
1009                        }
1010                        let mut set = s.write().await;
1011                        set.shift_remove(&args[0]);
1012                        Ok(Value::None)
1013                    })
1014                },
1015            ))))
1016        }
1017        "pop" => {
1018            let s_clone = s.clone();
1019            Some(Value::NativeFunction(Arc::new(NativeFunction::new_with_state(
1020                "pop",
1021                move |_args, _kwargs| {
1022                    let s = s_clone.clone();
1023                    Box::pin(async move {
1024                        let mut set = s.write().await;
1025                        if set.is_empty() {
1026                            return Err(BlueprintError::KeyError {
1027                                key: "pop from an empty set".into(),
1028                            });
1029                        }
1030                        let item = set.iter().next().cloned().unwrap();
1031                        set.shift_remove(&item);
1032                        Ok(item)
1033                    })
1034                },
1035            ))))
1036        }
1037        "clear" => {
1038            let s_clone = s.clone();
1039            Some(Value::NativeFunction(Arc::new(NativeFunction::new_with_state(
1040                "clear",
1041                move |_args, _kwargs| {
1042                    let s = s_clone.clone();
1043                    Box::pin(async move {
1044                        let mut set = s.write().await;
1045                        set.clear();
1046                        Ok(Value::None)
1047                    })
1048                },
1049            ))))
1050        }
1051        "copy" => {
1052            let s_clone = s.clone();
1053            Some(Value::NativeFunction(Arc::new(NativeFunction::new_with_state(
1054                "copy",
1055                move |_args, _kwargs| {
1056                    let s = s_clone.clone();
1057                    Box::pin(async move {
1058                        let set = s.read().await;
1059                        Ok(Value::Set(Arc::new(RwLock::new(set.clone()))))
1060                    })
1061                },
1062            ))))
1063        }
1064        "union" => {
1065            let s_clone = s.clone();
1066            Some(Value::NativeFunction(Arc::new(NativeFunction::new_with_state(
1067                "union",
1068                move |args, _kwargs| {
1069                    let s = s_clone.clone();
1070                    Box::pin(async move {
1071                        if args.len() != 1 {
1072                            return Err(BlueprintError::ArgumentError {
1073                                message: format!("union() takes exactly 1 argument ({} given)", args.len()),
1074                            });
1075                        }
1076                        let set = s.read().await;
1077                        let other = match &args[0] {
1078                            Value::Set(other) => other.read().await.clone(),
1079                            Value::List(l) => l.read().await.iter().cloned().collect(),
1080                            Value::Tuple(t) => t.iter().cloned().collect(),
1081                            _ => return Err(BlueprintError::TypeError {
1082                                expected: "set, list, or tuple".into(),
1083                                actual: args[0].type_name().into(),
1084                            }),
1085                        };
1086                        let result: IndexSet<Value> = set.union(&other).cloned().collect();
1087                        Ok(Value::Set(Arc::new(RwLock::new(result))))
1088                    })
1089                },
1090            ))))
1091        }
1092        "intersection" => {
1093            let s_clone = s.clone();
1094            Some(Value::NativeFunction(Arc::new(NativeFunction::new_with_state(
1095                "intersection",
1096                move |args, _kwargs| {
1097                    let s = s_clone.clone();
1098                    Box::pin(async move {
1099                        if args.len() != 1 {
1100                            return Err(BlueprintError::ArgumentError {
1101                                message: format!("intersection() takes exactly 1 argument ({} given)", args.len()),
1102                            });
1103                        }
1104                        let set = s.read().await;
1105                        let other = match &args[0] {
1106                            Value::Set(other) => other.read().await.clone(),
1107                            Value::List(l) => l.read().await.iter().cloned().collect(),
1108                            Value::Tuple(t) => t.iter().cloned().collect(),
1109                            _ => return Err(BlueprintError::TypeError {
1110                                expected: "set, list, or tuple".into(),
1111                                actual: args[0].type_name().into(),
1112                            }),
1113                        };
1114                        let result: IndexSet<Value> = set.intersection(&other).cloned().collect();
1115                        Ok(Value::Set(Arc::new(RwLock::new(result))))
1116                    })
1117                },
1118            ))))
1119        }
1120        "difference" => {
1121            let s_clone = s.clone();
1122            Some(Value::NativeFunction(Arc::new(NativeFunction::new_with_state(
1123                "difference",
1124                move |args, _kwargs| {
1125                    let s = s_clone.clone();
1126                    Box::pin(async move {
1127                        if args.len() != 1 {
1128                            return Err(BlueprintError::ArgumentError {
1129                                message: format!("difference() takes exactly 1 argument ({} given)", args.len()),
1130                            });
1131                        }
1132                        let set = s.read().await;
1133                        let other = match &args[0] {
1134                            Value::Set(other) => other.read().await.clone(),
1135                            Value::List(l) => l.read().await.iter().cloned().collect(),
1136                            Value::Tuple(t) => t.iter().cloned().collect(),
1137                            _ => return Err(BlueprintError::TypeError {
1138                                expected: "set, list, or tuple".into(),
1139                                actual: args[0].type_name().into(),
1140                            }),
1141                        };
1142                        let result: IndexSet<Value> = set.difference(&other).cloned().collect();
1143                        Ok(Value::Set(Arc::new(RwLock::new(result))))
1144                    })
1145                },
1146            ))))
1147        }
1148        "symmetric_difference" => {
1149            let s_clone = s.clone();
1150            Some(Value::NativeFunction(Arc::new(NativeFunction::new_with_state(
1151                "symmetric_difference",
1152                move |args, _kwargs| {
1153                    let s = s_clone.clone();
1154                    Box::pin(async move {
1155                        if args.len() != 1 {
1156                            return Err(BlueprintError::ArgumentError {
1157                                message: format!("symmetric_difference() takes exactly 1 argument ({} given)", args.len()),
1158                            });
1159                        }
1160                        let set = s.read().await;
1161                        let other = match &args[0] {
1162                            Value::Set(other) => other.read().await.clone(),
1163                            Value::List(l) => l.read().await.iter().cloned().collect(),
1164                            Value::Tuple(t) => t.iter().cloned().collect(),
1165                            _ => return Err(BlueprintError::TypeError {
1166                                expected: "set, list, or tuple".into(),
1167                                actual: args[0].type_name().into(),
1168                            }),
1169                        };
1170                        let result: IndexSet<Value> = set.symmetric_difference(&other).cloned().collect();
1171                        Ok(Value::Set(Arc::new(RwLock::new(result))))
1172                    })
1173                },
1174            ))))
1175        }
1176        "issubset" => {
1177            let s_clone = s.clone();
1178            Some(Value::NativeFunction(Arc::new(NativeFunction::new_with_state(
1179                "issubset",
1180                move |args, _kwargs| {
1181                    let s = s_clone.clone();
1182                    Box::pin(async move {
1183                        if args.len() != 1 {
1184                            return Err(BlueprintError::ArgumentError {
1185                                message: format!("issubset() takes exactly 1 argument ({} given)", args.len()),
1186                            });
1187                        }
1188                        let set = s.read().await;
1189                        let other = match &args[0] {
1190                            Value::Set(other) => other.read().await.clone(),
1191                            Value::List(l) => l.read().await.iter().cloned().collect(),
1192                            Value::Tuple(t) => t.iter().cloned().collect(),
1193                            _ => return Err(BlueprintError::TypeError {
1194                                expected: "set, list, or tuple".into(),
1195                                actual: args[0].type_name().into(),
1196                            }),
1197                        };
1198                        Ok(Value::Bool(set.is_subset(&other)))
1199                    })
1200                },
1201            ))))
1202        }
1203        "issuperset" => {
1204            let s_clone = s.clone();
1205            Some(Value::NativeFunction(Arc::new(NativeFunction::new_with_state(
1206                "issuperset",
1207                move |args, _kwargs| {
1208                    let s = s_clone.clone();
1209                    Box::pin(async move {
1210                        if args.len() != 1 {
1211                            return Err(BlueprintError::ArgumentError {
1212                                message: format!("issuperset() takes exactly 1 argument ({} given)", args.len()),
1213                            });
1214                        }
1215                        let set = s.read().await;
1216                        let other = match &args[0] {
1217                            Value::Set(other) => other.read().await.clone(),
1218                            Value::List(l) => l.read().await.iter().cloned().collect(),
1219                            Value::Tuple(t) => t.iter().cloned().collect(),
1220                            _ => return Err(BlueprintError::TypeError {
1221                                expected: "set, list, or tuple".into(),
1222                                actual: args[0].type_name().into(),
1223                            }),
1224                        };
1225                        Ok(Value::Bool(set.is_superset(&other)))
1226                    })
1227                },
1228            ))))
1229        }
1230        "isdisjoint" => {
1231            let s_clone = s.clone();
1232            Some(Value::NativeFunction(Arc::new(NativeFunction::new_with_state(
1233                "isdisjoint",
1234                move |args, _kwargs| {
1235                    let s = s_clone.clone();
1236                    Box::pin(async move {
1237                        if args.len() != 1 {
1238                            return Err(BlueprintError::ArgumentError {
1239                                message: format!("isdisjoint() takes exactly 1 argument ({} given)", args.len()),
1240                            });
1241                        }
1242                        let set = s.read().await;
1243                        let other = match &args[0] {
1244                            Value::Set(other) => other.read().await.clone(),
1245                            Value::List(l) => l.read().await.iter().cloned().collect(),
1246                            Value::Tuple(t) => t.iter().cloned().collect(),
1247                            _ => return Err(BlueprintError::TypeError {
1248                                expected: "set, list, or tuple".into(),
1249                                actual: args[0].type_name().into(),
1250                            }),
1251                        };
1252                        Ok(Value::Bool(set.is_disjoint(&other)))
1253                    })
1254                },
1255            ))))
1256        }
1257        "update" => {
1258            let s_clone = s.clone();
1259            Some(Value::NativeFunction(Arc::new(NativeFunction::new_with_state(
1260                "update",
1261                move |args, _kwargs| {
1262                    let s = s_clone.clone();
1263                    Box::pin(async move {
1264                        if args.len() != 1 {
1265                            return Err(BlueprintError::ArgumentError {
1266                                message: format!("update() takes exactly 1 argument ({} given)", args.len()),
1267                            });
1268                        }
1269                        let other = match &args[0] {
1270                            Value::Set(other) => other.read().await.clone(),
1271                            Value::List(l) => l.read().await.iter().cloned().collect(),
1272                            Value::Tuple(t) => t.iter().cloned().collect(),
1273                            _ => return Err(BlueprintError::TypeError {
1274                                expected: "set, list, or tuple".into(),
1275                                actual: args[0].type_name().into(),
1276                            }),
1277                        };
1278                        let mut set = s.write().await;
1279                        set.extend(other);
1280                        Ok(Value::None)
1281                    })
1282                },
1283            ))))
1284        }
1285        _ => None,
1286    }
1287}
1288
1289pub struct StreamIterator {
1290    rx: Mutex<mpsc::Receiver<Option<String>>>,
1291    content: Mutex<String>,
1292    done: Mutex<bool>,
1293    result: Mutex<Option<IndexMap<String, Value>>>,
1294}
1295
1296impl StreamIterator {
1297    pub fn new(rx: mpsc::Receiver<Option<String>>) -> Self {
1298        Self {
1299            rx: Mutex::new(rx),
1300            content: Mutex::new(String::new()),
1301            done: Mutex::new(false),
1302            result: Mutex::new(None),
1303        }
1304    }
1305
1306    pub async fn next(&self) -> Option<Value> {
1307        let mut done = self.done.lock().await;
1308        if *done {
1309            return None;
1310        }
1311
1312        let mut rx = self.rx.lock().await;
1313        match rx.recv().await {
1314            Some(Some(chunk)) => {
1315                let mut content = self.content.lock().await;
1316                content.push_str(&chunk);
1317                Some(Value::String(Arc::new(chunk)))
1318            }
1319            Some(None) | None => {
1320                *done = true;
1321                None
1322            }
1323        }
1324    }
1325
1326    pub async fn set_result(&self, result: IndexMap<String, Value>) {
1327        let mut r = self.result.lock().await;
1328        *r = Some(result);
1329    }
1330
1331    pub fn get_attr(&self, name: &str) -> Option<Value> {
1332        match name {
1333            "content" => {
1334                let content = self.content.try_lock().ok()?;
1335                Some(Value::String(Arc::new(content.clone())))
1336            }
1337            "done" => {
1338                let done = self.done.try_lock().ok()?;
1339                Some(Value::Bool(*done))
1340            }
1341            "result" => {
1342                let result = self.result.try_lock().ok()?;
1343                match result.as_ref() {
1344                    Some(map) => Some(Value::Dict(Arc::new(RwLock::new(map.clone())))),
1345                    None => Some(Value::None),
1346                }
1347            }
1348            _ => None,
1349        }
1350    }
1351}
1352
1353/// Message sent from generator to consumer
1354pub enum GeneratorMessage {
1355    /// Generator yielded a value, waiting for resume signal
1356    Yielded(Value, oneshot::Sender<()>),
1357    /// Generator completed (returned or finished)
1358    Complete,
1359}
1360
1361/// A generator that yields values lazily
1362pub struct Generator {
1363    /// Receives yielded values from the generator task
1364    rx: Mutex<mpsc::Receiver<GeneratorMessage>>,
1365    /// Whether the generator has completed
1366    done: AtomicBool,
1367    /// The function that creates this generator (for display)
1368    pub name: String,
1369}
1370
1371impl Generator {
1372    pub fn new(rx: mpsc::Receiver<GeneratorMessage>, name: String) -> Self {
1373        Self {
1374            rx: Mutex::new(rx),
1375            done: AtomicBool::new(false),
1376            name,
1377        }
1378    }
1379
1380    /// Get the next yielded value, or None if generator is exhausted
1381    pub async fn next(&self) -> Option<Value> {
1382        if self.done.load(Ordering::SeqCst) {
1383            return None;
1384        }
1385
1386        let mut rx = self.rx.lock().await;
1387        match rx.recv().await {
1388            Some(GeneratorMessage::Yielded(value, resume_tx)) => {
1389                // Signal the generator to continue after yield
1390                let _ = resume_tx.send(());
1391                Some(value)
1392            }
1393            Some(GeneratorMessage::Complete) | None => {
1394                self.done.store(true, Ordering::SeqCst);
1395                None
1396            }
1397        }
1398    }
1399
1400    pub fn is_done(&self) -> bool {
1401        self.done.load(Ordering::SeqCst)
1402    }
1403}
1404
1405impl fmt::Debug for Generator {
1406    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1407        write!(f, "<generator {}>", self.name)
1408    }
1409}
1410
1411#[derive(Debug, Clone, PartialEq)]
1412pub enum TypeAnnotation {
1413    Simple(String),
1414    Parameterized(String, Vec<TypeAnnotation>),
1415    Optional(Box<TypeAnnotation>),
1416    Any,
1417}
1418
1419impl TypeAnnotation {
1420    pub fn matches(&self, value: &Value) -> bool {
1421        match self {
1422            TypeAnnotation::Any => true,
1423            TypeAnnotation::Simple(name) => match name.as_str() {
1424                "int" => matches!(value, Value::Int(_)),
1425                "float" => matches!(value, Value::Float(_) | Value::Int(_)),
1426                "str" => matches!(value, Value::String(_)),
1427                "bool" => matches!(value, Value::Bool(_)),
1428                "list" => matches!(value, Value::List(_)),
1429                "dict" => matches!(value, Value::Dict(_)),
1430                "tuple" => matches!(value, Value::Tuple(_)),
1431                "None" | "NoneType" => matches!(value, Value::None),
1432                struct_name => {
1433                    if let Value::StructInstance(inst) = value {
1434                        inst.struct_type.name == struct_name
1435                    } else {
1436                        false
1437                    }
1438                }
1439            },
1440            TypeAnnotation::Parameterized(name, _params) => {
1441                match name.as_str() {
1442                    "list" => matches!(value, Value::List(_)),
1443                    "dict" => matches!(value, Value::Dict(_)),
1444                    _ => false,
1445                }
1446            }
1447            TypeAnnotation::Optional(inner) => {
1448                matches!(value, Value::None) || inner.matches(value)
1449            }
1450        }
1451    }
1452
1453    pub fn type_name(&self) -> String {
1454        match self {
1455            TypeAnnotation::Any => "any".to_string(),
1456            TypeAnnotation::Simple(name) => name.clone(),
1457            TypeAnnotation::Parameterized(name, params) => {
1458                let param_strs: Vec<String> = params.iter().map(|p| p.type_name()).collect();
1459                format!("{}[{}]", name, param_strs.join(", "))
1460            }
1461            TypeAnnotation::Optional(inner) => format!("{}?", inner.type_name()),
1462        }
1463    }
1464}
1465
1466#[derive(Debug, Clone)]
1467pub struct StructField {
1468    pub name: String,
1469    pub typ: TypeAnnotation,
1470    pub default: Option<Value>,
1471}
1472
1473#[derive(Debug, Clone)]
1474pub struct StructType {
1475    pub name: String,
1476    pub fields: Vec<StructField>,
1477}
1478
1479impl StructType {
1480    pub fn instantiate(&self, args: Vec<Value>, kwargs: HashMap<String, Value>) -> Result<StructInstance> {
1481        let mut field_values: IndexMap<String, Value> = IndexMap::new();
1482
1483        let mut positional_idx = 0;
1484        for field in &self.fields {
1485            let value = if let Some(v) = kwargs.get(&field.name) {
1486                v.clone()
1487            } else if positional_idx < args.len() {
1488                let v = args[positional_idx].clone();
1489                positional_idx += 1;
1490                v
1491            } else if let Some(default) = &field.default {
1492                default.clone()
1493            } else {
1494                return Err(BlueprintError::ArgumentError {
1495                    message: format!(
1496                        "{}() missing required argument: '{}'",
1497                        self.name, field.name
1498                    ),
1499                });
1500            };
1501
1502            if !field.typ.matches(&value) {
1503                return Err(BlueprintError::TypeError {
1504                    expected: format!(
1505                        "{} for field '{}' in {}()",
1506                        field.typ.type_name(),
1507                        field.name,
1508                        self.name
1509                    ),
1510                    actual: value.type_name().to_string(),
1511                });
1512            }
1513
1514            field_values.insert(field.name.clone(), value);
1515        }
1516
1517        if positional_idx < args.len() {
1518            return Err(BlueprintError::ArgumentError {
1519                message: format!(
1520                    "{}() takes {} positional arguments but {} were given",
1521                    self.name,
1522                    self.fields.len(),
1523                    args.len()
1524                ),
1525            });
1526        }
1527
1528        for key in kwargs.keys() {
1529            if !self.fields.iter().any(|f| &f.name == key) {
1530                return Err(BlueprintError::ArgumentError {
1531                    message: format!("{}() got unexpected keyword argument '{}'", self.name, key),
1532                });
1533            }
1534        }
1535
1536        Ok(StructInstance {
1537            struct_type: Arc::new(self.clone()),
1538            fields: field_values,
1539        })
1540    }
1541}
1542
1543#[derive(Debug, Clone)]
1544pub struct StructInstance {
1545    pub struct_type: Arc<StructType>,
1546    pub fields: IndexMap<String, Value>,
1547}
1548
1549impl StructInstance {
1550    pub fn get_field(&self, name: &str) -> Option<Value> {
1551        self.fields.get(name).cloned()
1552    }
1553
1554    pub fn to_display_string(&self) -> String {
1555        let field_strs: Vec<String> = self
1556            .struct_type
1557            .fields
1558            .iter()
1559            .map(|f| {
1560                let val = self.fields.get(&f.name).map(|v| v.repr()).unwrap_or_default();
1561                format!("{}={}", f.name, val)
1562            })
1563            .collect();
1564        format!("{}({})", self.struct_type.name, field_strs.join(", "))
1565    }
1566}