1use std::fmt;
7use ustr::Ustr;
8
9#[derive(Debug, Clone, PartialEq)]
14#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
15pub enum TypedParameter {
16 Int { default: Option<i32> },
19 Float { default: Option<f32> },
21 String { default: Option<String> },
23
24 Color {
28 default: Option<[f32; 3]>,
29 space: Option<Ustr>, },
31 Point {
33 default: Option<[f32; 3]>,
34 space: Option<Ustr>, },
36 Vector {
38 default: Option<[f32; 3]>,
39 space: Option<Ustr>,
40 },
41 Normal {
43 default: Option<[f32; 3]>,
44 space: Option<Ustr>,
45 },
46 Matrix { default: Option<[f32; 16]> },
48
49 IntArray {
52 size: usize,
53 default: Option<Vec<i32>>,
54 },
55 FloatArray {
57 size: usize,
58 default: Option<Vec<f32>>,
59 },
60 StringArray {
62 size: usize,
63 default: Option<Vec<String>>,
64 },
65 ColorArray {
67 size: usize,
68 default: Option<Vec<[f32; 3]>>,
69 space: Option<Ustr>,
70 },
71 PointArray {
73 size: usize,
74 default: Option<Vec<[f32; 3]>>,
75 space: Option<Ustr>,
76 },
77 VectorArray {
79 size: usize,
80 default: Option<Vec<[f32; 3]>>,
81 space: Option<Ustr>,
82 },
83 NormalArray {
85 size: usize,
86 default: Option<Vec<[f32; 3]>>,
87 space: Option<Ustr>,
88 },
89 MatrixArray {
91 size: usize,
92 default: Option<Vec<[f32; 16]>>,
93 },
94
95 IntDynamicArray { default: Option<Vec<i32>> },
98 FloatDynamicArray { default: Option<Vec<f32>> },
100 StringDynamicArray { default: Option<Vec<String>> },
102 ColorDynamicArray {
104 default: Option<Vec<[f32; 3]>>,
105 space: Option<Ustr>,
106 },
107 PointDynamicArray {
109 default: Option<Vec<[f32; 3]>>,
110 space: Option<Ustr>,
111 },
112 VectorDynamicArray {
114 default: Option<Vec<[f32; 3]>>,
115 space: Option<Ustr>,
116 },
117 NormalDynamicArray {
119 default: Option<Vec<[f32; 3]>>,
120 space: Option<Ustr>,
121 },
122 MatrixDynamicArray { default: Option<Vec<[f32; 16]>> },
124
125 Closure { closure_type: Ustr },
128}
129
130impl TypedParameter {
131 pub fn has_default(&self) -> bool {
133 match self {
134 TypedParameter::Int { default } => default.is_some(),
135 TypedParameter::Float { default } => default.is_some(),
136 TypedParameter::String { default } => default.is_some(),
137 TypedParameter::Color { default, .. } => default.is_some(),
138 TypedParameter::Point { default, .. } => default.is_some(),
139 TypedParameter::Vector { default, .. } => default.is_some(),
140 TypedParameter::Normal { default, .. } => default.is_some(),
141 TypedParameter::Matrix { default } => default.is_some(),
142
143 TypedParameter::IntArray { default, .. } => default.is_some(),
144 TypedParameter::FloatArray { default, .. } => default.is_some(),
145 TypedParameter::StringArray { default, .. } => default.is_some(),
146 TypedParameter::ColorArray { default, .. } => default.is_some(),
147 TypedParameter::PointArray { default, .. } => default.is_some(),
148 TypedParameter::VectorArray { default, .. } => default.is_some(),
149 TypedParameter::NormalArray { default, .. } => default.is_some(),
150 TypedParameter::MatrixArray { default, .. } => default.is_some(),
151
152 TypedParameter::IntDynamicArray { default } => default.is_some(),
153 TypedParameter::FloatDynamicArray { default } => default.is_some(),
154 TypedParameter::StringDynamicArray { default } => default.is_some(),
155 TypedParameter::ColorDynamicArray { default, .. } => default.is_some(),
156 TypedParameter::PointDynamicArray { default, .. } => default.is_some(),
157 TypedParameter::VectorDynamicArray { default, .. } => default.is_some(),
158 TypedParameter::NormalDynamicArray { default, .. } => default.is_some(),
159 TypedParameter::MatrixDynamicArray { default } => default.is_some(),
160
161 TypedParameter::Closure { .. } => false, }
163 }
164
165 pub fn is_array(&self) -> bool {
167 !matches!(
168 self,
169 TypedParameter::Int { .. }
170 | TypedParameter::Float { .. }
171 | TypedParameter::String { .. }
172 | TypedParameter::Color { .. }
173 | TypedParameter::Point { .. }
174 | TypedParameter::Vector { .. }
175 | TypedParameter::Normal { .. }
176 | TypedParameter::Matrix { .. }
177 | TypedParameter::Closure { .. }
178 )
179 }
180
181 pub fn is_dynamic_array(&self) -> bool {
183 matches!(
184 self,
185 TypedParameter::IntDynamicArray { .. }
186 | TypedParameter::FloatDynamicArray { .. }
187 | TypedParameter::StringDynamicArray { .. }
188 | TypedParameter::ColorDynamicArray { .. }
189 | TypedParameter::PointDynamicArray { .. }
190 | TypedParameter::VectorDynamicArray { .. }
191 | TypedParameter::NormalDynamicArray { .. }
192 | TypedParameter::MatrixDynamicArray { .. }
193 )
194 }
195
196 pub fn is_closure(&self) -> bool {
198 matches!(self, TypedParameter::Closure { .. })
199 }
200
201 pub fn type_name(&self) -> &'static str {
203 match self {
204 TypedParameter::Int { .. } => "int",
205 TypedParameter::Float { .. } => "float",
206 TypedParameter::String { .. } => "string",
207 TypedParameter::Color { .. } => "color",
208 TypedParameter::Point { .. } => "point",
209 TypedParameter::Vector { .. } => "vector",
210 TypedParameter::Normal { .. } => "normal",
211 TypedParameter::Matrix { .. } => "matrix",
212
213 TypedParameter::IntArray { .. } => "int[]",
214 TypedParameter::FloatArray { .. } => "float[]",
215 TypedParameter::StringArray { .. } => "string[]",
216 TypedParameter::ColorArray { .. } => "color[]",
217 TypedParameter::PointArray { .. } => "point[]",
218 TypedParameter::VectorArray { .. } => "vector[]",
219 TypedParameter::NormalArray { .. } => "normal[]",
220 TypedParameter::MatrixArray { .. } => "matrix[]",
221
222 TypedParameter::IntDynamicArray { .. } => "int[]",
223 TypedParameter::FloatDynamicArray { .. } => "float[]",
224 TypedParameter::StringDynamicArray { .. } => "string[]",
225 TypedParameter::ColorDynamicArray { .. } => "color[]",
226 TypedParameter::PointDynamicArray { .. } => "point[]",
227 TypedParameter::VectorDynamicArray { .. } => "vector[]",
228 TypedParameter::NormalDynamicArray { .. } => "normal[]",
229 TypedParameter::MatrixDynamicArray { .. } => "matrix[]",
230
231 TypedParameter::Closure { .. } => "closure",
232 }
233 }
234}
235
236impl fmt::Display for TypedParameter {
237 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
238 match self {
239 TypedParameter::IntArray { size, .. } => write!(f, "int[{}]", size),
240 TypedParameter::FloatArray { size, .. } => write!(f, "float[{}]", size),
241 TypedParameter::StringArray { size, .. } => write!(f, "string[{}]", size),
242 TypedParameter::ColorArray { size, .. } => write!(f, "color[{}]", size),
243 TypedParameter::PointArray { size, .. } => write!(f, "point[{}]", size),
244 TypedParameter::VectorArray { size, .. } => write!(f, "vector[{}]", size),
245 TypedParameter::NormalArray { size, .. } => write!(f, "normal[{}]", size),
246 TypedParameter::MatrixArray { size, .. } => write!(f, "matrix[{}]", size),
247
248 TypedParameter::Closure { closure_type } => write!(f, "closure {}", closure_type),
249
250 other => write!(f, "{}", other.type_name()),
251 }
252 }
253}
254
255#[derive(Debug, Clone, PartialEq)]
257#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
258pub struct Metadata {
259 pub name: Ustr,
260 pub value: MetadataValue,
261}
262
263#[derive(Debug, Clone, PartialEq)]
265#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
266pub enum MetadataValue {
267 Int(i32),
268 Float(f32),
269 String(String),
270 IntArray(Vec<i32>),
271 FloatArray(Vec<f32>),
272 StringArray(Vec<String>),
273}
274
275#[derive(Debug, Clone, PartialEq)]
277#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
278pub enum ParameterKind {
279 Input(TypedParameter),
281 Output(TypedParameter),
283}
284
285impl ParameterKind {
286 pub fn is_output(&self) -> bool {
288 matches!(self, ParameterKind::Output(_))
289 }
290
291 pub fn typed_param(&self) -> &TypedParameter {
293 match self {
294 ParameterKind::Input(p) | ParameterKind::Output(p) => p,
295 }
296 }
297}
298
299#[derive(Debug, Clone, PartialEq)]
301#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
302pub struct Parameter {
303 pub name: Ustr,
305 pub kind: ParameterKind,
307 pub metadata: Vec<Metadata>,
309}
310
311impl Parameter {
312 pub fn new_input(name: impl Into<Ustr>, typed_param: TypedParameter) -> Self {
314 Parameter {
315 name: name.into(),
316 kind: ParameterKind::Input(typed_param),
317 metadata: Vec::new(),
318 }
319 }
320
321 pub fn new_output(name: impl Into<Ustr>, mut typed_param: TypedParameter) -> Self {
323 match &mut typed_param {
325 TypedParameter::Int { default } => *default = None,
326 TypedParameter::Float { default } => *default = None,
327 TypedParameter::String { default } => *default = None,
328 TypedParameter::Color { default, .. } => *default = None,
329 TypedParameter::Point { default, .. } => *default = None,
330 TypedParameter::Vector { default, .. } => *default = None,
331 TypedParameter::Normal { default, .. } => *default = None,
332 TypedParameter::Matrix { default } => *default = None,
333
334 TypedParameter::IntArray { default, .. } => *default = None,
335 TypedParameter::FloatArray { default, .. } => *default = None,
336 TypedParameter::StringArray { default, .. } => *default = None,
337 TypedParameter::ColorArray { default, .. } => *default = None,
338 TypedParameter::PointArray { default, .. } => *default = None,
339 TypedParameter::VectorArray { default, .. } => *default = None,
340 TypedParameter::NormalArray { default, .. } => *default = None,
341 TypedParameter::MatrixArray { default, .. } => *default = None,
342
343 TypedParameter::IntDynamicArray { default } => *default = None,
344 TypedParameter::FloatDynamicArray { default } => *default = None,
345 TypedParameter::StringDynamicArray { default } => *default = None,
346 TypedParameter::ColorDynamicArray { default, .. } => *default = None,
347 TypedParameter::PointDynamicArray { default, .. } => *default = None,
348 TypedParameter::VectorDynamicArray { default, .. } => *default = None,
349 TypedParameter::NormalDynamicArray { default, .. } => *default = None,
350 TypedParameter::MatrixDynamicArray { default } => *default = None,
351
352 TypedParameter::Closure { .. } => {} }
354
355 Parameter {
356 name: name.into(),
357 kind: ParameterKind::Output(typed_param),
358 metadata: Vec::new(),
359 }
360 }
361
362 pub fn is_output(&self) -> bool {
364 self.kind.is_output()
365 }
366
367 pub fn typed_param(&self) -> &TypedParameter {
369 self.kind.typed_param()
370 }
371
372 pub fn find_metadata(&self, name: &str) -> Option<&Metadata> {
374 self.metadata.iter().find(|m| m.name.as_str() == name)
375 }
376
377 pub fn add_metadata(&mut self, name: impl Into<Ustr>, value: MetadataValue) {
379 self.metadata.push(Metadata {
380 name: name.into(),
381 value,
382 });
383 }
384}
385
386impl TryFrom<crate::parser::types::ParsedParameter> for Parameter {
388 type Error = String;
389
390 fn try_from(old: crate::parser::types::ParsedParameter) -> Result<Self, Self::Error> {
391 use crate::parser::types::BaseType;
392
393 let typed_param = match old.type_desc.basetype {
395 BaseType::Int => {
396 if old.type_desc.is_array() {
397 if old.type_desc.arraylen == -1 {
398 TypedParameter::IntDynamicArray {
399 default: if old.valid_default && !old.idefault.is_empty() {
400 Some(old.idefault)
401 } else {
402 None
403 },
404 }
405 } else {
406 TypedParameter::IntArray {
407 size: old.type_desc.arraylen as usize,
408 default: if old.valid_default && !old.idefault.is_empty() {
409 Some(old.idefault)
410 } else {
411 None
412 },
413 }
414 }
415 } else {
416 TypedParameter::Int {
417 default: if old.valid_default && !old.idefault.is_empty() {
418 Some(old.idefault[0])
419 } else {
420 None
421 },
422 }
423 }
424 }
425 BaseType::Float => {
426 if old.type_desc.is_array() {
427 if old.type_desc.arraylen == -1 {
428 TypedParameter::FloatDynamicArray {
429 default: if old.valid_default && !old.fdefault.is_empty() {
430 Some(old.fdefault)
431 } else {
432 None
433 },
434 }
435 } else {
436 TypedParameter::FloatArray {
437 size: old.type_desc.arraylen as usize,
438 default: if old.valid_default && !old.fdefault.is_empty() {
439 Some(old.fdefault)
440 } else {
441 None
442 },
443 }
444 }
445 } else {
446 TypedParameter::Float {
447 default: if old.valid_default && !old.fdefault.is_empty() {
448 Some(old.fdefault[0])
449 } else {
450 None
451 },
452 }
453 }
454 }
455 BaseType::String => {
456 if old.type_desc.is_array() {
457 if old.type_desc.arraylen == -1 {
458 TypedParameter::StringDynamicArray {
459 default: if old.valid_default && !old.sdefault.is_empty() {
460 Some(old.sdefault)
461 } else {
462 None
463 },
464 }
465 } else {
466 TypedParameter::StringArray {
467 size: old.type_desc.arraylen as usize,
468 default: if old.valid_default && !old.sdefault.is_empty() {
469 Some(old.sdefault)
470 } else {
471 None
472 },
473 }
474 }
475 } else {
476 TypedParameter::String {
477 default: if old.valid_default && !old.sdefault.is_empty() {
478 Some(old.sdefault[0].clone())
479 } else {
480 None
481 },
482 }
483 }
484 }
485 BaseType::Color => {
486 let space = old.spacename.first().map(|s| Ustr::from(s.as_str()));
487 if old.type_desc.is_array() {
488 let arrays = if old.valid_default && !old.fdefault.is_empty() {
490 Some(
491 old.fdefault
492 .chunks_exact(3)
493 .map(|chunk| [chunk[0], chunk[1], chunk[2]])
494 .collect(),
495 )
496 } else {
497 None
498 };
499
500 if old.type_desc.arraylen == -1 {
501 TypedParameter::ColorDynamicArray {
502 default: arrays,
503 space,
504 }
505 } else {
506 TypedParameter::ColorArray {
507 size: old.type_desc.arraylen as usize,
508 default: arrays,
509 space,
510 }
511 }
512 } else {
513 TypedParameter::Color {
514 default: if old.valid_default && old.fdefault.len() >= 3 {
515 Some([old.fdefault[0], old.fdefault[1], old.fdefault[2]])
516 } else {
517 None
518 },
519 space,
520 }
521 }
522 }
523 BaseType::Point => {
524 let space = old.spacename.first().map(|s| Ustr::from(s.as_str()));
525 if old.type_desc.is_array() {
526 let arrays = if old.valid_default && !old.fdefault.is_empty() {
527 Some(
528 old.fdefault
529 .chunks_exact(3)
530 .map(|chunk| [chunk[0], chunk[1], chunk[2]])
531 .collect(),
532 )
533 } else {
534 None
535 };
536
537 if old.type_desc.arraylen == -1 {
538 TypedParameter::PointDynamicArray {
539 default: arrays,
540 space,
541 }
542 } else {
543 TypedParameter::PointArray {
544 size: old.type_desc.arraylen as usize,
545 default: arrays,
546 space,
547 }
548 }
549 } else {
550 TypedParameter::Point {
551 default: if old.valid_default && old.fdefault.len() >= 3 {
552 Some([old.fdefault[0], old.fdefault[1], old.fdefault[2]])
553 } else {
554 None
555 },
556 space,
557 }
558 }
559 }
560 BaseType::Vector => {
561 let space = old.spacename.first().map(|s| Ustr::from(s.as_str()));
562 if old.type_desc.is_array() {
563 let arrays = if old.valid_default && !old.fdefault.is_empty() {
564 Some(
565 old.fdefault
566 .chunks_exact(3)
567 .map(|chunk| [chunk[0], chunk[1], chunk[2]])
568 .collect(),
569 )
570 } else {
571 None
572 };
573
574 if old.type_desc.arraylen == -1 {
575 TypedParameter::VectorDynamicArray {
576 default: arrays,
577 space,
578 }
579 } else {
580 TypedParameter::VectorArray {
581 size: old.type_desc.arraylen as usize,
582 default: arrays,
583 space,
584 }
585 }
586 } else {
587 TypedParameter::Vector {
588 default: if old.valid_default && old.fdefault.len() >= 3 {
589 Some([old.fdefault[0], old.fdefault[1], old.fdefault[2]])
590 } else {
591 None
592 },
593 space,
594 }
595 }
596 }
597 BaseType::Normal => {
598 let space = old.spacename.first().map(|s| Ustr::from(s.as_str()));
599 if old.type_desc.is_array() {
600 let arrays = if old.valid_default && !old.fdefault.is_empty() {
601 Some(
602 old.fdefault
603 .chunks_exact(3)
604 .map(|chunk| [chunk[0], chunk[1], chunk[2]])
605 .collect(),
606 )
607 } else {
608 None
609 };
610
611 if old.type_desc.arraylen == -1 {
612 TypedParameter::NormalDynamicArray {
613 default: arrays,
614 space,
615 }
616 } else {
617 TypedParameter::NormalArray {
618 size: old.type_desc.arraylen as usize,
619 default: arrays,
620 space,
621 }
622 }
623 } else {
624 TypedParameter::Normal {
625 default: if old.valid_default && old.fdefault.len() >= 3 {
626 Some([old.fdefault[0], old.fdefault[1], old.fdefault[2]])
627 } else {
628 None
629 },
630 space,
631 }
632 }
633 }
634 BaseType::Matrix => {
635 if old.type_desc.is_array() {
636 let arrays = if old.valid_default && !old.fdefault.is_empty() {
637 Some(
638 old.fdefault
639 .chunks_exact(16)
640 .map(|chunk| {
641 let mut arr = [0.0; 16];
642 arr.copy_from_slice(chunk);
643 arr
644 })
645 .collect(),
646 )
647 } else {
648 None
649 };
650
651 if old.type_desc.arraylen == -1 {
652 TypedParameter::MatrixDynamicArray { default: arrays }
653 } else {
654 TypedParameter::MatrixArray {
655 size: old.type_desc.arraylen as usize,
656 default: arrays,
657 }
658 }
659 } else {
660 TypedParameter::Matrix {
661 default: if old.valid_default && old.fdefault.len() >= 16 {
662 let mut arr = [0.0; 16];
663 arr.copy_from_slice(&old.fdefault[..16]);
664 Some(arr)
665 } else {
666 None
667 },
668 }
669 }
670 }
671 BaseType::None => {
672 if old.type_desc.is_closure {
673 TypedParameter::Closure {
674 closure_type: old.structname.unwrap_or_else(|| Ustr::from("closure")),
675 }
676 } else {
677 return Err("Cannot convert BaseType::None that isn't a closure".to_string());
678 }
679 }
680 };
681
682 let mut param = if old.is_output {
684 Parameter::new_output(old.name, typed_param)
685 } else {
686 Parameter::new_input(old.name, typed_param)
687 };
688
689 for meta in old.metadata {
691 let meta_value = if !meta.idefault.is_empty() {
692 if meta.idefault.len() == 1 {
693 MetadataValue::Int(meta.idefault[0])
694 } else {
695 MetadataValue::IntArray(meta.idefault)
696 }
697 } else if !meta.fdefault.is_empty() {
698 if meta.fdefault.len() == 1 {
699 MetadataValue::Float(meta.fdefault[0])
700 } else {
701 MetadataValue::FloatArray(meta.fdefault)
702 }
703 } else if !meta.sdefault.is_empty() {
704 if meta.sdefault.len() == 1 {
705 MetadataValue::String(meta.sdefault[0].clone())
706 } else {
707 MetadataValue::StringArray(meta.sdefault)
708 }
709 } else {
710 continue;
711 };
712 param.add_metadata(meta.name, meta_value);
713 }
714
715 Ok(param)
716 }
717}
718
719#[cfg(test)]
720mod tests {
721 use super::*;
722
723 #[test]
724 fn test_typed_parameter_creation() {
725 let param = TypedParameter::Float { default: Some(0.5) };
727 assert!(param.has_default());
728 assert!(!param.is_array());
729 assert_eq!(param.type_name(), "float");
730
731 let param = TypedParameter::Color {
733 default: None,
734 space: Some(Ustr::from("rgb")),
735 };
736 assert!(!param.has_default());
737 assert!(!param.is_array());
738 assert_eq!(param.type_name(), "color");
739
740 let param = TypedParameter::FloatArray {
742 size: 5,
743 default: Some(vec![1.0, 2.0, 3.0, 4.0, 5.0]),
744 };
745 assert!(param.has_default());
746 assert!(param.is_array());
747 assert!(!param.is_dynamic_array());
748 assert_eq!(param.to_string(), "float[5]");
749
750 let param = TypedParameter::StringDynamicArray {
752 default: Some(vec!["hello".to_string(), "world".to_string()]),
753 };
754 assert!(param.has_default());
755 assert!(param.is_array());
756 assert!(param.is_dynamic_array());
757 assert_eq!(param.type_name(), "string[]");
758 }
759
760 #[test]
761 fn test_output_parameter_strips_defaults() {
762 let typed_param = TypedParameter::Color {
763 default: Some([1.0, 0.0, 0.0]),
764 space: None,
765 };
766
767 let output = Parameter::new_output("result", typed_param);
768 assert!(output.is_output());
769
770 match output.typed_param() {
772 TypedParameter::Color { default, .. } => {
773 assert!(
774 default.is_none(),
775 "Output parameter should not have default"
776 );
777 }
778 _ => panic!("Wrong type"),
779 }
780 }
781
782 #[test]
783 fn test_type_safety() {
784 let color = TypedParameter::Color {
788 default: Some([1.0, 0.5, 0.0]),
789 space: None,
790 };
791
792 match color {
794 TypedParameter::Color {
795 default: Some(rgb), ..
796 } => {
797 assert_eq!(rgb[0], 1.0);
798 assert_eq!(rgb[1], 0.5);
799 assert_eq!(rgb[2], 0.0);
800 }
801 TypedParameter::Int { .. } => {
802 panic!("This branch is impossible - type safety!");
803 }
804 _ => {}
805 }
806 }
807}