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
1353pub enum GeneratorMessage {
1355 Yielded(Value, oneshot::Sender<()>),
1357 Complete,
1359}
1360
1361pub struct Generator {
1363 rx: Mutex<mpsc::Receiver<GeneratorMessage>>,
1365 done: AtomicBool,
1367 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 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 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}