aorist_primitives/
lib.rs

1#![allow(non_snake_case)]
2
3mod concept;
4pub use concept::*;
5mod endpoints;
6pub use endpoints::*;
7
8#[macro_export]
9macro_rules! register_ast_nodes {
10    ($name:ident, $($variant: ident,)+) => {
11
12        #[derive(Clone)]
13        pub enum $name {
14            $(
15                $variant(Arc<RwLock<$variant>>),
16            )+
17        }
18        impl $name {
19            pub fn clone_without_ancestors(&self) -> Self {
20                match &self {
21                    $(
22                        Self::$variant(x) => Self::$variant(Arc::new(RwLock::new(x.read().unwrap().clone_without_ancestors()))),
23                    )+
24                }
25            }
26            pub fn set_ancestors(&self, ancestors: Vec<AncestorRecord>) {
27                match &self {
28                    $(
29                        Self::$variant(x) => x.write().unwrap().set_ancestors(ancestors),
30                    )+
31                }
32            }
33            pub fn get_ancestors(&self) -> Option<Vec<AncestorRecord>> {
34                match &self {
35                    $(
36                        Self::$variant(x) => x.read().unwrap().get_ancestors(),
37                    )+
38                }
39            }
40            pub fn get_descendants(&self) -> Vec<AST> {
41                let mut queue = VecDeque::new();
42                queue.push_back(self.clone());
43                let mut current = queue.pop_front();
44                let mut v: Vec<AST> = Vec::new();
45                while let Some(elem) = current {
46                    let direct_descendants = match &elem {
47                        $(
48                            Self::$variant(x) => {
49                            let read = x.read().unwrap();
50                            read.get_direct_descendants()
51                            }
52                        )+
53                    };
54                    for desc in direct_descendants.into_iter() {
55                        queue.push_back(desc);
56                    }
57                    v.push(elem);
58                    current = queue.pop_front();
59                }
60                v
61            }
62            pub fn name(&self) -> String {
63                match &self {
64                    $(
65                        Self::$variant(..) => stringify!($variant),
66                    )+
67                }
68                .to_string()
69            }
70            pub fn optimize_fields(&self) {
71                match &self {
72                    $(
73                        Self::$variant(rw) => rw.write().unwrap().optimize_fields(),
74                    )+
75                }
76            }
77            pub fn to_python_ast_node<'a>(
78                &self,
79                py: Python,
80                ast_module: &'a PyModule,
81                depth: usize,
82            ) -> PyResult<&'a PyAny> {
83                match &self {
84                    $(
85                        Self::$variant(x) => x.read().unwrap().to_python_ast_node(
86                            py,
87                            ast_module,
88                            depth,
89                        ),
90                    )+
91                }
92            }
93            pub fn to_r_ast_node(
94                &self,
95                depth: usize,
96            ) -> Robj {
97                match &self {
98                    $(
99                        Self::$variant(x) => x.read().unwrap().to_r_ast_node(
100                            depth,
101                        ),
102                    )+
103                }
104            }
105        }
106        impl PartialEq for $name {
107            fn eq(&self, other: &Self) -> bool {
108                match (&self, other) {
109                    $(
110                        (Self::$variant(v1), Self::$variant(v2)) => {
111                            v1.read().unwrap().eq(&v2.read().unwrap())
112                        },
113                    )+
114                    (_, _) => false,
115                }
116            }
117        }
118        impl Eq for $name {}
119        impl Hash for $name {
120            fn hash<H: Hasher>(&self, state: &mut H) {
121                match &self {
122                    $(
123                        Self::$variant(x) => x.read().unwrap().hash(state),
124                    )+
125                }
126            }
127        }
128    }
129}
130
131#[macro_export]
132macro_rules! define_task_node {
133    ($name:ident,
134     $descendants:expr,
135     $py_ast_closure:expr,
136     $import_closure:expr,
137     $import_type:ty,
138     $($field: ident : $field_type: ty,)*) => {
139        #[derive(Hash, PartialEq, Clone)]
140        pub struct $name {
141            $(
142                $field: $field_type,
143            )*
144        }
145        impl $name {
146            pub fn new_wrapped($(
147                $field: $field_type,
148            )*) -> Arc<RwLock<Self>> {
149                Arc::new(RwLock::new(Self::new($($field, )*)))
150            }
151            pub fn get_statements<'a>(
152                &self,
153            ) -> Vec<AST> {
154                ($py_ast_closure)(self)
155            }
156            pub fn new($(
157                $field: $field_type,
158            )*) -> Self {
159                Self {
160                    $($field,)*
161                }
162            }
163            $(
164                pub fn $field(&self) -> $field_type {
165                    self.$field.clone()
166                }
167            )*
168            pub fn get_direct_descendants(&self) -> Vec<AST> {
169                $descendants(self)
170            }
171            pub fn get_imports(&self) -> Vec<$import_type> {
172                $import_closure(self)
173            }
174        }
175    };
176}
177
178#[macro_export]
179macro_rules! register_task_nodes {
180    ($name:ident, $import_type: ty, $($variant: ident,)+) => {
181
182        #[derive(Clone)]
183        pub enum $name {
184            $(
185                $variant(Arc<RwLock<$variant>>),
186            )+
187        }
188        impl $name {
189            pub fn get_imports(&self) -> Vec<$import_type>{
190                match &self {
191                    $(
192                        Self::$variant(x) => x.read().unwrap().get_imports(),
193                    )+
194                }
195            }
196            pub fn get_statements(&self) -> Vec<AST> {
197                match &self {
198                    $(
199                        Self::$variant(x) => x.read().unwrap().get_statements(),
200                    )+
201                }
202            }
203        }
204        impl PartialEq for $name {
205            fn eq(&self, other: &Self) -> bool {
206                match (&self, other) {
207                    $(
208                        (Self::$variant(v1), Self::$variant(v2)) => {
209                            v1.read().unwrap().eq(&v2.read().unwrap())
210                        },
211                    )+
212                    (_, _) => false,
213                }
214            }
215        }
216        impl Eq for $name {}
217        impl Hash for $name {
218            fn hash<H: Hasher>(&self, state: &mut H) {
219                match &self {
220                    $(
221                        Self::$variant(x) => x.read().unwrap().hash(state),
222                    )+
223                }
224            }
225        }
226    }
227}
228
229#[macro_export]
230macro_rules! define_ast_node {
231    ($name:ident,
232     $descendants:expr,
233     $py_ast_closure:expr,
234     $r_ast_closure:expr,
235     $($field: ident : $field_type: ty,)*) => {
236        #[derive(Hash, PartialEq, Eq, Clone, Optimizable)]
237        pub struct $name {
238            $(
239                $field: $field_type,
240            )*
241            ancestors: Option<Vec<AncestorRecord>>,
242        }
243        impl $name {
244            pub fn new_wrapped($(
245                $field: $field_type,
246            )*) -> Arc<RwLock<Self>> {
247                Arc::new(RwLock::new(Self::new($($field, )*)))
248            }
249            pub fn to_python_ast_node<'a>(
250                &self,
251                py: Python,
252                ast_module: &'a PyModule,
253                depth: usize,
254            ) -> PyResult<&'a PyAny> {
255                ($py_ast_closure)(self, py, ast_module, depth)
256            }
257            pub fn to_r_ast_node(&self, depth: usize) -> Robj {
258                ($r_ast_closure)(self, depth)
259            }
260            pub fn new($(
261                $field: $field_type,
262            )*) -> Self {
263                Self {
264                    $($field,)*
265                    ancestors: None,
266                }
267            }
268            pub fn clone_without_ancestors(&self) -> Self {
269                Self {
270                    $($field: self.$field.clone(),)*
271                    ancestors: None,
272                }
273            }
274            pub fn set_ancestors(&mut self, ancestors: Vec<AncestorRecord>) {
275                assert!(self.ancestors.is_none());
276                self.ancestors = Some(ancestors);
277            }
278            pub fn get_ancestors(&self) -> Option<Vec<AncestorRecord>> {
279                self.ancestors.clone()
280            }
281            $(
282                pub fn $field(&self) -> $field_type {
283                    self.$field.clone()
284                }
285            )*
286            pub fn get_direct_descendants(&self) -> Vec<AST> {
287                $descendants(self)
288            }
289        }
290    };
291}
292
293#[macro_export]
294macro_rules! define_program {
295    ($name:ident, $root:ident, $constraint:ident, $satisfy_type:ident,
296     $lt: lifetime, $clt: lifetime,
297     $dialect:ident,
298     $preamble:expr, $call:expr, $tuple_call: expr, $dialect_call: expr) => {
299        pub struct $name {}
300        impl<$lt, $clt> ConstraintSatisfactionBase<$lt, $clt> for $name
301        where
302            $lt: $clt,
303        {
304            type RootType = $root;
305            type ConstraintType = $constraint;
306            type Outer = Constraint;
307        }
308        impl<$lt, $clt> $satisfy_type<$lt, $clt> for $name
309        where
310            $lt: $clt,
311        {
312            type Dialect = $dialect;
313            fn compute_parameter_tuple(
314                uuid: Uuid,
315                c: Concept<'a>,
316                ancestry: Arc<ConceptAncestry<'a>>,
317            ) -> ParameterTuple {
318                $tuple_call(uuid, c, ancestry)
319            }
320            fn get_preamble() -> String {
321                $preamble.to_string()
322            }
323            fn get_call() -> String {
324                $call.to_string()
325            }
326            fn get_dialect() -> Dialect {
327                Dialect::$dialect($dialect_call())
328            }
329        }
330    };
331}
332
333#[macro_export]
334macro_rules! register_programs_for_constraint {
335    ($constraint:ident, $root: ident, $lt: lifetime, $clt: lifetime, $ancestry: ty,
336     $($dialect:ident, $element: ident),+) => {
337        impl<$lt, $clt> SatisfiableConstraint<$lt, $clt> for $constraint where $lt : $clt {
338            type TAncestry = $ancestry;
339            fn satisfy(
340                &mut self,
341                c: Concept<$lt>,
342                d: &Dialect,
343                ancestry: Arc<$ancestry>,
344            ) -> Result<Option<(String, String, ParameterTuple, Dialect)>> {
345                match d {
346                    $(
347                        Dialect::$dialect{..} => Ok(Some((
348                            $element::get_preamble(),
349                            $element::get_call(),
350                            $element::compute_parameter_tuple(
351                                self.get_uuid()?.clone(),
352                                c.clone(),
353                                ancestry,
354                            ),
355                            $element::get_dialect(),
356                        ))),
357                    )+
358                    _ => Ok(None),
359                }
360            }
361            fn satisfy_given_preference_ordering(
362                &mut self,
363                c: Concept<$lt>,
364                preferences: &Vec<Dialect>,
365                ancestry: Arc<$ancestry>,
366            ) -> Result<(String, String, ParameterTuple, Dialect)> {
367                match c {
368                    Concept::$root{..} => {
369                        for d in preferences {
370                            if let Some((preamble, call, params, dialect))
371                                = self.satisfy(c.clone(), &d, ancestry.clone())? {
372                                return Ok((preamble, call, params, dialect));
373                            }
374                        }
375                        bail!("Cannot satisfy preference ordering for {}", c.get_uuid())
376                    },
377                    _ => bail!("Wrong type of concept provided: {}", c.get_type())
378                }
379            }
380        }
381    };
382}
383
384#[macro_export]
385macro_rules! register_satisfiable_constraints {
386
387    ($outer: ident, $($constraint:ident),+)  => {
388        impl <'a> SatisfiableOuterConstraint<'a> for $outer {
389            fn satisfy_given_preference_ordering(
390                &mut self,
391                c: AoristRef<Concept>,
392                preferences: &Vec<Dialect>,
393                ancestry: Arc<ConceptAncestry>,
394            ) -> Result<(String, String, ParameterTuple, Dialect)> {
395                match &mut self.inner {
396                    $(
397                        Some(AoristConstraint::$constraint(ref mut x)) =>
398                        x.satisfy_given_preference_ordering(
399                            c, preferences,
400                            ancestry,
401                        ),
402                    )+
403                    _ => bail!("Constraint {} is not satisfiable (no program provided).", self.inner.as_ref().unwrap().get_name())
404                }
405            }
406        }
407    }
408}
409
410#[macro_export]
411macro_rules! define_attribute {
412    (
413      $element:ident,
414      $presto_type:ident,
415      $orc_type:ident,
416      $sql_type:ident,
417      $sqlite_type:ident,
418      $postgres_type:ident,
419      $bigquery_type:ident,
420      $value:ident
421    ) => {
422        aorist_paste::item! {
423            #[cfg_attr(feature = "python", pyclass(module = "aorist"))]
424            #[derive(
425                Hash,
426                PartialEq,
427                Eq,
428                Debug,
429                Serialize,
430                Deserialize,
431                Clone,
432                $presto_type,
433                $orc_type,
434                $sqlite_type,
435                $postgres_type,
436                $bigquery_type,
437            )]
438            #[cfg_attr(feature = "sql", derive($sql_type))]
439            pub struct $element {
440                pub name: String,
441                pub comment: Option<String>,
442                pub nullable: bool,
443            }
444            impl TAttribute for $element {
445                type Value = $value;
446
447                fn get_name(&self) -> &String {
448                    &self.name
449                }
450                fn get_comment(&self) -> &Option<String> {
451                    &self.comment
452                }
453                fn is_nullable(&self) -> bool {
454                    self.nullable
455                }
456            }
457            #[cfg(feature = "python")]
458            #[pymethods]
459            impl $element {
460                #[new]
461                #[args(comment = "None")]
462                #[args(nullable = "false")]
463                pub fn new(
464                    name: String,
465                    comment: Option<String>,
466                    nullable: bool
467                ) -> Self {
468                    Self { name, comment, nullable }
469                }
470                #[getter]
471                pub fn name(&self) -> PyResult<String> {
472                    Ok(self.name.clone())
473                }
474
475            }
476            #[cfg(feature = "python")]
477            #[pyo3::prelude::pyproto]
478            impl pyo3::PyObjectProtocol for $element {
479                fn __repr__(&self) -> pyo3::PyResult<String> {
480                    Ok(format!(
481                        "{} {}",
482                        stringify!($element),
483                        serde_json::to_string_pretty(self).unwrap()
484                    ))
485                }
486                fn __str__(&self) -> pyo3::PyResult<String> {
487                    Ok(format!(
488                        "{} {}",
489                        stringify!($element),
490                        serde_json::to_string_pretty(self).unwrap()
491                    ))
492                }
493            }
494        }
495    };
496}
497
498#[macro_export]
499macro_rules! define_constraint {
500    ($element:ident, $requires_program:expr, $satisfy_type:ident, $root:ident, $outer:ident,
501    $title:expr, $body:expr, $should_add:expr, $get_required:expr $(, $required:ident)*) => {
502        aorist_paste::item! {
503            #[cfg_attr(feature = "python", pyclass(module = "aorist"))]
504            #[derive(Clone)]
505            pub struct $element {
506                id: Uuid,
507                root_uuid: Uuid,
508                $([<$required:snake:lower>] : Vec<Arc<RwLock<$outer>>>,)*
509            }
510            #[cfg(feature = "python")]
511            #[pymethods]
512            impl $element {
513                #[classattr]
514                pub fn name() -> String {
515                    stringify!($element).to_string()
516                }
517            }
518            pub trait $satisfy_type<'a> : ConstraintSatisfactionBase<'a, ConstraintType=$element, RootType=$root> {
519                type Dialect;
520
521                // computes a parameter tuple as a string, e.g. to be called from
522                // Python
523                fn compute_parameter_tuple(
524                    uuid: Uuid,
525                    root: Concept,
526                    ancestry: Arc<ConceptAncestry>,
527                ) -> ParameterTuple;
528                fn get_preamble() -> String;
529                fn get_call() -> String;
530                fn get_dialect() -> Dialect;
531            }
532
533            #[cfg_attr(feature = "python", pyclass(module = "aorist"))]
534            #[derive(Clone)]
535            pub struct [<$element Program>] {
536                pub dialect: Dialect,
537                pub code: String,
538                pub entrypoint: String,
539                pub arg_functions: Vec<(Vec<String>, String)>,
540                pub kwarg_functions: LinkedHashMap<String, (Vec<String>, String)>,
541            }
542            #[cfg(feature = "python")]
543            #[pymethods]
544            impl $element {
545                #[staticmethod]
546                pub fn register_python_program(
547                    code: &str,
548                    entrypoint: &str,
549                    arg_functions: Vec<(Vec<&str>, &str)>,
550                    kwarg_functions: HashMap<&str, (Vec<&str>, &str)>,
551                    pip_requirements: Vec<String>,
552                ) -> PyResult<[<$element Program>]> {
553
554                    let mut funs: LinkedHashMap<String, (Vec<String>, String)> = LinkedHashMap::new();
555                    for (k, (v1, v2)) in kwarg_functions.iter() {
556                        funs.insert(k.to_string(), (v1.iter().map(|x| x.to_string()).collect(), v2.to_string()));
557                    }
558                    Ok([<$element Program>]{
559                        code: code.to_string(),
560                        entrypoint: entrypoint.to_string(),
561                        arg_functions: arg_functions.iter().map(|(x, y)| (x.iter().map(|x| x.to_string()).collect(), y.to_string())).collect(),
562                        kwarg_functions: funs,
563                        dialect: Dialect::Python(aorist_core::Python::new(pip_requirements))
564                    })
565                }
566                #[staticmethod]
567                pub fn register_r_program(
568                    code: &str,
569                    entrypoint: &str,
570                    arg_functions: Vec<(Vec<&str>, &str)>,
571                    kwarg_functions: HashMap<&str, (Vec<&str>, &str)>,
572                ) -> PyResult<[<$element Program>]> {
573
574                    let mut funs: LinkedHashMap<String, (Vec<String>, String)> = LinkedHashMap::new();
575                    for (k, (v1, v2)) in kwarg_functions.iter() {
576                        funs.insert(k.to_string(), (v1.iter().map(|x| x.to_string()).collect(), v2.to_string()));
577                    }
578                    Ok([<$element Program>]{
579                        code: code.to_string(),
580                        entrypoint: entrypoint.to_string(),
581                        arg_functions: arg_functions.iter().map(|(x, y)| (x.iter().map(|x| x.to_string()).collect(), y.to_string())).collect(),
582                        kwarg_functions: funs,
583                        dialect: Dialect::R(aorist_core::R::new())
584                    })
585                }
586                #[staticmethod]
587                pub fn register_presto_program(
588                    code: &str,
589                    entrypoint: &str,
590                    arg_functions: Vec<(Vec<&str>, &str)>,
591                    kwarg_functions: HashMap<&str, (Vec<&str>, &str)>,
592                ) -> PyResult<[<$element Program>]> {
593
594                    let mut funs: LinkedHashMap<String, (Vec<String>, String)> = LinkedHashMap::new();
595                    for (k, (v1, v2)) in kwarg_functions.iter() {
596                        funs.insert(k.to_string(), (v1.iter().map(|x| x.to_string()).collect(), v2.to_string()));
597                    }
598                    Ok([<$element Program>]{
599                        code: code.to_string(),
600                        entrypoint: entrypoint.to_string(),
601                        arg_functions: arg_functions.iter().map(|(x, y)| (x.iter().map(|x| x.to_string()).collect(), y.to_string())).collect(),
602                        kwarg_functions: funs,
603                        dialect: Dialect::Presto(aorist_core::Presto::new())
604                    })
605                }
606                #[staticmethod]
607                pub fn register_bash_program(
608                    code: &str,
609                    entrypoint: &str,
610                    arg_functions: Vec<(Vec<&str>, &str)>,
611                    kwarg_functions: HashMap<&str, (Vec<&str>, &str)>,
612                ) -> PyResult<[<$element Program>]> {
613
614                    let mut funs: LinkedHashMap<String, (Vec<String>, String)> = LinkedHashMap::new();
615                    for (k, (v1, v2)) in kwarg_functions.iter() {
616                        funs.insert(k.to_string(), (v1.iter().map(|x| x.to_string()).collect(), v2.to_string()));
617                    }
618                    Ok([<$element Program>]{
619                        code: code.to_string(),
620                        entrypoint: entrypoint.to_string(),
621                        arg_functions: arg_functions.iter().map(|(x, y)| (x.iter().map(|x| x.to_string()).collect(), y.to_string())).collect(),
622                        kwarg_functions: funs,
623                        dialect: Dialect::Bash(aorist_core::Bash::new())
624                    })
625                }
626            }
627            impl <'a> TProgram<'a, $element> for [<$element Program>] {
628                fn new(
629                    code: String,
630                    entrypoint: String,
631                    arg_functions: Vec<(Vec<String>, String)>,
632                    kwarg_functions: LinkedHashMap<String, (Vec<String>, String)>,
633                    dialect: Dialect,
634                ) -> Self {
635                    Self { code, entrypoint, arg_functions, kwarg_functions, dialect }
636                }
637                fn get_arg_functions(&self) -> Vec<(Vec<String>, String)> {
638                    self.arg_functions.clone()
639                }
640                fn get_code(&self) -> String {
641                    self.code.clone()
642                }
643                fn get_dialect(&self) -> Dialect {
644                    self.dialect.clone()
645                }
646                fn get_entrypoint(&self) -> String {
647                    self.entrypoint.clone()
648                }
649                fn get_kwarg_functions(&self) -> LinkedHashMap<String, (Vec<String>, String)> {
650                    self.kwarg_functions.clone()
651                }
652            }
653            impl $element {
654                // TODO: move any of these functions that should have public visibility
655                // into TConstraint
656                fn get_uuid(&self) -> Result<Uuid> {
657                    Ok(self.id.clone())
658                }
659                fn _should_add(root: AoristRef<Concept>, ancestry: &ConceptAncestry) -> bool {
660                    $should_add(root, ancestry)
661                }
662                fn get_required(root: AoristRef<Concept>, ancestry: &ConceptAncestry) -> Vec<Uuid> {
663                    $get_required(root, ancestry)
664                }
665                fn get_root_uuid(&self) -> Result<Uuid> {
666                    Ok(self.root_uuid.clone())
667                }
668                fn requires_program(&self) -> Result<bool> {
669                    Ok($requires_program)
670                }
671                // these are *all* downstream constraints
672                fn get_downstream_constraints(&self) -> Result<Vec<Arc<RwLock<Constraint>>>> {
673                    let mut downstream: Vec<Arc<RwLock<Constraint>>> = Vec::new();
674                    $(
675                        for arc in &self.[<$required:snake:lower>] {
676                            downstream.push(arc.clone());
677                        }
678                    )*
679                    Ok(downstream)
680                }
681                fn get_title() -> Option<String> {
682                    $title
683                }
684                fn get_body() -> Option<String> {
685                    $body
686                }
687            }
688            impl <'a> TConstraint<'a> for $element {
689                type Root = AoristRef<$root>;
690                type Outer = $outer;
691                type Ancestry = ConceptAncestry;
692
693                fn get_root_type_name() -> Result<String> {
694                    Ok(stringify!($root).into())
695                }
696                fn get_required_constraint_names() -> Vec<String> {
697                    vec![$(
698                        stringify!($required).into()
699                    ),*]
700                }
701                fn should_add(root: AoristRef<Concept>, ancestry: &ConceptAncestry) -> bool {
702                    let read = root.0.read().unwrap();
703                    match *read {
704                        Concept::$root((_, _, _)) => Self::_should_add(root.clone(), ancestry),
705                        _ => panic!("should_add called with unexpected concept."),
706                    }
707                }
708                fn new(root_uuid: Uuid,
709                       potential_child_constraints: Vec<Arc<RwLock<Constraint>>>) -> Result<Self> {
710                    // TODO: we should dedupe potential child constraints
711                    $(
712                        let mut [<$required:snake:lower>]: Vec<Arc<RwLock<Constraint>>> =
713                        Vec::new();
714                    )*
715                    let mut by_uuid: HashMap<Uuid, Arc<RwLock<Constraint>>> = HashMap::new();
716                    for constraint in &potential_child_constraints {
717                        $(
718                            if let Some(AoristConstraint::$required{..}) =
719                            &constraint.read().unwrap().inner
720                            {
721                                by_uuid.insert(
722                                    constraint.read().unwrap().get_uuid()?,
723                                    constraint.clone()
724                                );
725                            }
726                        )*
727                    }
728                    for constraint in by_uuid.values() {
729                        $(
730                            if let Some(AoristConstraint::$required{..}) =
731                            &constraint.read().unwrap().inner {
732                                [<$required:snake:lower>].push(constraint.clone());
733                            }
734                        )*
735                    }
736                    Ok(Self{
737                        id: Uuid::new_v4(),
738                        root_uuid,
739                        $([<$required:snake:lower>],)*
740                    })
741                }
742            }
743        }
744    };
745}
746
747#[macro_export]
748macro_rules! register_attribute_new {
749    ( $name:ident, $($element: ident),+ ) => { paste! {
750        #[derive(Hash, PartialEq, Eq, Debug, Serialize, Deserialize, Clone)]
751        pub enum [<$name Enum>] {
752            $(
753                $element($element),
754            )+
755        }
756        #[cfg(feature = "python")]
757        impl <'a> FromPyObject<'a> for [<$name Enum>] {
758            fn extract(obj: &'a PyAny) -> PyResult<Self> {
759                $(
760                    if let Ok(x) = $element::extract(obj) {
761                        return Ok(Self::$element(x));
762                    }
763                )+
764                Err(PyValueError::new_err("could not convert enum arm."))
765            }
766        }
767        #[cfg(feature = "python")]
768        impl IntoPy<PyObject> for [<$name Enum>] {
769            fn into_py(self, py: Python) -> PyObject {
770                match self {
771                    $(
772                        [<$name Enum>]::$element(x) => x.into_py(py),
773                    )+
774                }
775            }
776        }
777        impl [<$name Enum>] {
778            pub fn get_name(&self) -> &String {
779                match self {
780                    $(
781                        [<$name Enum>]::$element(x) => x.get_name(),
782                    )+
783                }
784            }
785            pub fn get_type(&self) -> String {
786                match self {
787                    $(
788                        [<$name Enum>]::$element(x) => stringify!($element).to_string(),
789                    )+
790                }
791            }
792            pub fn is_nullable(&self) -> bool {
793                match self {
794                    $(
795                        [<$name Enum>]::$element(x) => x.is_nullable(),
796                    )+
797                }
798            }
799
800            pub fn as_predicted_objective(&self) -> Self {
801                match self {
802                    $(
803                        [<$name Enum>]::$element(x) => [<$name Enum>]::$element($element {
804                            name: format!("predicted_{}", x.get_name()).to_string(),
805                            comment: x.get_comment().clone(),
806                            nullable: false,
807                        }),
808                    )+
809                }
810            }
811            pub fn get_comment(&self) -> &Option<String> {
812                match self {
813                    $(
814                        [<$name Enum>]::$element(x) => x.get_comment(),
815                    )+
816                }
817            }
818            #[cfg(feature = "sql")]
819            pub fn get_sql_type(&self) -> sqlparser::ast::DataType {
820                match self {
821                    $(
822                        [<$name Enum>]::$element(x) => x.get_sql_type(),
823                    )+
824                }
825            }
826            pub fn get_presto_type(&self) -> String {
827                match self {
828                    $(
829                        [<$name Enum>]::$element(x) => x.get_presto_type(),
830                    )+
831                }
832            }
833            pub fn get_sqlite_type(&self) -> String {
834                match self {
835                    $(
836                        [<$name Enum>]::$element(x) => x.get_sqlite_type(),
837                    )+
838                }
839            }
840            pub fn get_postgres_type(&self) -> String {
841                match self {
842                    $(
843                        [<$name Enum>]::$element(x) => x.get_postgres_type(),
844                    )+
845                }
846            }
847            pub fn psycopg2_value_json_serializable(&self) -> bool {
848                match self {
849                    $(
850                        [<$name Enum>]::$element(x) => x.psycopg2_value_json_serializable(),
851                    )+
852                }
853            }
854            pub fn get_bigquery_type(&self) -> String {
855                match self {
856                    $(
857                        [<$name Enum>]::$element(x) => x.get_bigquery_type(),
858                    )+
859                }
860            }
861            pub fn get_orc_type(&self) -> String {
862                match self {
863                    $(
864                        [<$name Enum>]::$element(x) => x.get_orc_type(),
865                    )+
866                }
867            }
868        }
869        #[aorist(derivative(Hash))]
870        pub struct $name {
871            pub inner: AttributeOrTransform,
872        }
873        /*#[cfg(feature = "python")]
874        impl<'a> FromPyObject<'a> for $name {
875            fn extract(ob: &'a PyAny) -> PyResult<Self> {
876                let inner = AttributeOrTransform::extract(ob)?;
877                Ok(Self{ inner, tag: None, uuid: None })
878            }
879        }*/
880        impl $name {
881            pub fn get_name(&self) -> &String {
882                self.inner.get_name()
883            }
884            pub fn psycopg2_value_json_serializable(&self) -> bool {
885                self.inner.psycopg2_value_json_serializable()
886            }
887            pub fn get_type(&self) -> String {
888                self.inner.get_type()
889            }
890            pub fn is_nullable(&self) -> bool {
891                self.inner.is_nullable()
892            }
893            pub fn get_comment(&self) -> &Option<String> {
894                self.inner.get_comment()
895            }
896            #[cfg(feature = "sql")]
897            pub fn get_sql_type(&self) -> DataType {
898                self.inner.get_sql_type()
899            }
900            pub fn get_presto_type(&self) -> String {
901                self.inner.get_presto_type()
902            }
903            pub fn get_sqlite_type(&self) -> String {
904                self.inner.get_sqlite_type()
905            }
906            pub fn get_postgres_type(&self) -> String {
907                self.inner.get_postgres_type()
908            }
909            pub fn get_bigquery_type(&self) -> String {
910                self.inner.get_bigquery_type()
911            }
912            pub fn get_orc_type(&self) -> String {
913                self.inner.get_orc_type()
914            }
915        }
916        #[cfg(feature = "python")]
917        aorist_paste::item!(
918            pub fn [<$name:snake:lower>] (m: &PyModule) -> PyResult<()> {
919                $(
920                    m.add_class::<$element>()?;
921                )+
922                Ok(())
923            }
924        );
925        #[cfg(feature = "python")]
926        #[pyo3::prelude::pymethods]
927        impl [<Py $name>] {
928            #[getter]
929            pub fn name(&self) -> pyo3::prelude::PyResult<String> {
930                Ok(self.inner.0.read().unwrap().get_name().clone())
931            }
932            #[getter]
933            pub fn aorist_type(&self) -> pyo3::prelude::PyResult<String> {
934                Ok(self.inner.0.read().unwrap().get_type().clone())
935            }
936            #[getter]
937            pub fn comment(&self) -> pyo3::prelude::PyResult<Option<String>> {
938                Ok(self.inner.0.read().unwrap().get_comment().clone())
939            }
940            #[getter]
941            pub fn orc_type(&self) -> pyo3::prelude::PyResult<String> {
942                Ok(self.inner.0.read().unwrap().get_orc_type().clone())
943            }
944            #[getter]
945            pub fn presto_type(&self) -> pyo3::prelude::PyResult<String> {
946                Ok(self.inner.0.read().unwrap().get_presto_type().clone())
947            }
948            #[getter]
949            pub fn bigquery_type(&self) -> pyo3::prelude::PyResult<String> {
950                Ok(self.inner.0.read().unwrap().get_bigquery_type().clone())
951            }
952            #[getter]
953            pub fn is_nullable(&self) -> pyo3::prelude::PyResult<bool> {
954                Ok(self.inner.0.read().unwrap().is_nullable().clone())
955            }
956            #[getter]
957            pub fn psycopg2_value_json_serializable(&self) -> pyo3::prelude::PyResult<bool> {
958                Ok(self.inner.0.read().unwrap().psycopg2_value_json_serializable().clone())
959            }
960            #[getter]
961            pub fn postgres_type(&self) -> pyo3::prelude::PyResult<String> {
962                Ok(self.inner.0.read().unwrap().get_postgres_type().clone())
963            }
964        }
965    }}
966}
967
968#[macro_export]
969macro_rules! register_attribute {
970    ( $name:ident, $($element: ident),+ ) => { paste! {
971        #[derive(Hash, PartialEq, Eq, Debug, Serialize, Deserialize, Clone)]
972        pub enum [<$name Enum>] {
973            $(
974                $element($element),
975            )+
976        }
977        impl <'a> FromPyObject<'a> for [<$name Enum>] {
978            fn extract(obj: &'a PyAny) -> PyResult<Self> {
979                $(
980                    if let Ok(x) = $element::extract(obj) {
981                        return Ok(Self::$element(x));
982                    }
983                )+
984                Err(PyValueError::new_err("could not convert enum arm."))
985            }
986        }
987        impl [<$name Enum>] {
988            pub fn get_name(&self) -> &String {
989                match self {
990                    $(
991                        [<$name Enum>]::$element(x) => x.get_name(),
992                    )+
993                }
994            }
995            pub fn get_type(&self) -> String {
996                match self {
997                    $(
998                        [<$name Enum>]::$element(x) => stringify!($element).to_string(),
999                    )+
1000                }
1001            }
1002            pub fn is_nullable(&self) -> bool {
1003                match self {
1004                    $(
1005                        [<$name Enum>]::$element(x) => x.is_nullable(),
1006                    )+
1007                }
1008            }
1009
1010            pub fn as_predicted_objective(&self) -> Self {
1011                match self {
1012                    $(
1013                        [<$name Enum>]::$element(x) => [<$name Enum>]::$element($element {
1014                            name: format!("predicted_{}", x.get_name()).to_string(),
1015                            comment: x.get_comment().clone(),
1016                            nullable: false,
1017                        }),
1018                    )+
1019                }
1020            }
1021            pub fn get_comment(&self) -> &Option<String> {
1022                match self {
1023                    $(
1024                        [<$name Enum>]::$element(x) => x.get_comment(),
1025                    )+
1026                }
1027            }
1028            pub fn get_sql_type(&self) -> DataType {
1029                match self {
1030                    $(
1031                        [<$name Enum>]::$element(x) => x.get_sql_type(),
1032                    )+
1033                }
1034            }
1035            pub fn get_presto_type(&self) -> String {
1036                match self {
1037                    $(
1038                        [<$name Enum>]::$element(x) => x.get_presto_type(),
1039                    )+
1040                }
1041            }
1042            pub fn get_sqlite_type(&self) -> String {
1043                match self {
1044                    $(
1045                        [<$name Enum>]::$element(x) => x.get_sqlite_type(),
1046                    )+
1047                }
1048            }
1049            pub fn get_postgres_type(&self) -> String {
1050                match self {
1051                    $(
1052                        [<$name Enum>]::$element(x) => x.get_postgres_type(),
1053                    )+
1054                }
1055            }
1056            pub fn psycopg2_value_json_serializable(&self) -> bool {
1057                match self {
1058                    $(
1059                        [<$name Enum>]::$element(x) => x.psycopg2_value_json_serializable(),
1060                    )+
1061                }
1062            }
1063            pub fn get_bigquery_type(&self) -> String {
1064                match self {
1065                    $(
1066                        [<$name Enum>]::$element(x) => x.get_bigquery_type(),
1067                    )+
1068                }
1069            }
1070            pub fn get_orc_type(&self) -> String {
1071                match self {
1072                    $(
1073                        [<$name Enum>]::$element(x) => x.get_orc_type(),
1074                    )+
1075                }
1076            }
1077        }
1078        #[aorist_concept(derivative(Hash))]
1079        pub struct $name {
1080            pub inner: AttributeOrTransform,
1081        }
1082        impl<'a> FromPyObject<'a> for $name {
1083            fn extract(ob: &'a PyAny) -> PyResult<Self> {
1084                let inner = AttributeOrTransform::extract(ob)?;
1085                Ok(Self{ inner, constraints: Vec::new(), tag: None, uuid: None })
1086            }
1087        }
1088        impl $name {
1089            pub fn get_name(&self) -> &String {
1090                self.inner.get_name()
1091            }
1092            pub fn psycopg2_value_json_serializable(&self) -> bool {
1093                self.inner.psycopg2_value_json_serializable()
1094            }
1095            pub fn get_type(&self) -> String {
1096                self.inner.get_type()
1097            }
1098            pub fn is_nullable(&self) -> bool {
1099                self.inner.is_nullable()
1100            }
1101            pub fn get_comment(&self) -> &Option<String> {
1102                self.inner.get_comment()
1103            }
1104            pub fn get_sql_type(&self) -> DataType {
1105                self.inner.get_sql_type()
1106            }
1107            pub fn get_presto_type(&self) -> String {
1108                self.inner.get_presto_type()
1109            }
1110            pub fn get_sqlite_type(&self) -> String {
1111                self.inner.get_sqlite_type()
1112            }
1113            pub fn get_postgres_type(&self) -> String {
1114                self.inner.get_postgres_type()
1115            }
1116            pub fn get_bigquery_type(&self) -> String {
1117                self.inner.get_bigquery_type()
1118            }
1119            pub fn get_orc_type(&self) -> String {
1120                self.inner.get_orc_type()
1121            }
1122        }
1123        aorist_paste::item!(
1124            pub fn [<$name:snake:lower>] (m: &PyModule) -> PyResult<()> {
1125                $(
1126                    m.add_class::<$element>()?;
1127                )+
1128                Ok(())
1129            }
1130        );
1131    }}
1132}
1133
1134#[macro_export]
1135macro_rules! register_concept {
1136    ( $name:ident, $ancestry:ident, $($element: ident ),* ) => { aorist_paste::item! {
1137        #[derive(Clone, Debug, Serialize, PartialEq)]
1138        pub enum $name {
1139            $(
1140                $element((AoristRef<$element>, usize, Option<(Uuid, String)>)),
1141            )+
1142        }
1143        // note: both Universe and EndpointConfig must exist
1144        impl AoristUniverse for AoristRef<Universe> {
1145            type TEndpoints = EndpointConfig;
1146            fn get_endpoints(&self) -> Self::TEndpoints {
1147                (*self.0.read().unwrap()).endpoints.0.read().unwrap().clone()
1148            }
1149        }
1150        $(
1151            impl [<CanBe $element>] for $name {
1152                fn [<construct_ $element:snake:lower>](
1153                    obj_ref: AoristRef<$element>,
1154                    ix: Option<usize>,
1155                    id: Option<(Uuid, String)>
1156                ) -> AoristRef<Self> {
1157                    AoristRef(Arc::new(RwLock::new($name::$element((
1158                        obj_ref.clone(),
1159                        match ix {
1160                            Some(i) => i,
1161                            None => 0,
1162                        },
1163                        id,
1164                    )))))
1165               }
1166            }
1167        )+
1168
1169        pub trait [<$name Descendants>] {
1170            fn get_descendants(&self) -> Vec<AoristRef<$name>>;
1171        }
1172
1173        $(
1174            impl [<$name Descendants>] for AoristRef<$element> {
1175                fn get_descendants(&self) -> Vec<AoristRef<$name>> {
1176                    let mut concepts = Vec::new();
1177                    for tpl in self.get_children() {
1178                        let wrapped_concept = WrappedConcept::from(tpl);
1179                        concepts.push(wrapped_concept.inner);
1180                    }
1181                    concepts
1182                }
1183            }
1184        )+
1185
1186
1187        impl ConceptEnum for $name {}
1188        impl ConceptEnum for AoristRef<$name> {}
1189
1190        $(
1191            impl TryFrom<AoristRef<$name>> for AoristRef<$element> {
1192                type Error = String;
1193                fn try_from(x: AoristRef<$name>) -> Result<Self, String> {
1194                    let read = x.0.read();
1195                    match read {
1196                        Ok(elem) => match *elem {
1197                            $name::$element((ref y, _, _)) => Ok(y.clone()),
1198                            _ => Err("Cannot convert.".into()),
1199                        },
1200                        _ => Err("Cannot read.".into()),
1201                    }
1202                }
1203            }
1204        )+
1205
1206        #[cfg_attr(feature = "python", pyclass(module = "aorist"))]
1207        pub struct $ancestry {
1208            pub parents: Arc<RwLock<HashMap<(Uuid, String), AoristRef<$name>>>>,
1209        }
1210        impl Ancestry for $ancestry {
1211            type TConcept = AoristRef<$name>;
1212            fn new(parents: Arc<RwLock<HashMap<(Uuid, String), AoristRef<$name>>>>) -> Self {
1213                 Self { parents }
1214            }
1215
1216        }
1217        impl $ancestry {
1218            $(
1219                pub fn [<$element:snake:lower>](
1220                    &self,
1221                    root: AoristRef<$name>,
1222                ) -> Result<AoristRef<$element>, String> {
1223                    if root.get_type() == stringify!($element).to_string(){
1224                        return(Ok(AoristRef::<$element>::try_from(root.clone()).unwrap()));
1225                    }
1226                    let parent_id = root.get_parent_id();
1227                    match parent_id {
1228                        None => Err(
1229                            format!(
1230                                "Cannot find ancestor of type {} for root {}.",
1231                                stringify!($element),
1232                                root.get_type(),
1233                            )
1234                        ),
1235                        Some(id) => {
1236                            let guard = self.parents.read().unwrap();
1237                            let parent = guard.get(&id).unwrap().clone();
1238                            self.[<$element:snake:lower>](parent)
1239                        }
1240                    }
1241                }
1242            )+
1243        }
1244        #[cfg(feature = "python")]
1245        impl $ancestry {
1246            pub fn py_object(&self, ancestor: &str, root: AoristRef<$name>, py: Python) -> PyResult<PyObject> {
1247                match ancestor {
1248                    $(
1249                        stringify!([<$element:snake:lower>]) => match self.[<$element:snake:lower>](root) {
1250                            Ok(x) => x.py_object(py),
1251                            Err(err) => Err(pyo3::exceptions::PyTypeError::new_err(err.clone())),
1252                        }
1253                    )+
1254                    _ => panic!("Unknown ancestor type: {}", ancestor),
1255                }
1256            }
1257        }
1258        #[cfg(feature = "python")]
1259        impl $name {
1260            pub fn py_object(&self, py: Python) -> PyObject {
1261                let object = match self {
1262                    $(
1263                        $name::$element((x, _, _)) => PyObject::from(PyCell::new(py, [<Py $element>] {
1264                            inner: x.clone(),
1265                        }).unwrap()),
1266                    )+
1267                };
1268                object
1269            }
1270        }
1271        impl TConceptEnum for AoristRef<$name> {
1272            type TUniverse = AoristRef<Universe>;
1273            fn get_parent_id(&self) -> Option<(Uuid, String)> {
1274                let read = self.0.read().unwrap();
1275                match *read {
1276                    $(
1277                        $name::$element((_, _, ref id)) => id.clone(),
1278                    )+
1279                }
1280            }
1281            fn from_universe(universe: AoristRef<Universe>) -> Self {
1282                AoristRef(Arc::new(RwLock::new($name::Universe((universe, 0, None)))))
1283            }
1284            fn get_type(&self) -> String {
1285                let read = self.0.read().unwrap();
1286                match *read {
1287                    $(
1288                        $name::$element((ref x, _, _)) => stringify!($element).to_string(),
1289                    )*
1290                }
1291            }
1292            fn get_uuid(&self) -> Uuid {
1293                let read = self.0.read().unwrap();
1294                match *read {
1295                    $(
1296                        $name::$element((ref x, _, _)) => x.get_uuid().unwrap(),
1297                    )*
1298                }
1299            }
1300            fn get_tag(&self) -> Option<String> {
1301                let read = self.0.read().unwrap();
1302                match *read {
1303                    $(
1304                        $name::$element((ref x, _, _)) => x.get_tag(),
1305                    )*
1306                }
1307            }
1308            fn get_index_as_child(&self) -> usize {
1309                let read = self.0.read().unwrap();
1310                match *read {
1311                    $(
1312                        $name::$element((_, idx, _)) => idx,
1313                    )*
1314                }
1315            }
1316            fn get_child_concepts(&self) -> Vec<Self> {
1317                let read = self.0.read().unwrap();
1318                match *read {
1319                    $(
1320                        $name::$element((ref x, _, _)) => x.get_descendants(),
1321                    )*
1322                }
1323            }
1324            fn populate_child_concept_map(&self, concept_map: &mut HashMap<(Uuid, String), Self>) {
1325                let read = self.0.read().unwrap();
1326                match *read {
1327                    $(
1328                        $name::$element((ref x, idx, ref parent)) => {
1329                            debug!("Visiting concept {}: {}", stringify!($element), x.get_uuid().unwrap());
1330                            for child in x.get_descendants() {
1331                                child.populate_child_concept_map(concept_map);
1332                            }
1333                            concept_map.insert(
1334                                (
1335                                    x.get_uuid().unwrap(),
1336                                    stringify!($element).to_string()
1337                                 ),
1338                                 AoristRef(Arc::new(RwLock::new(
1339                                    $name::$element((x.clone(), idx, parent.clone()))
1340                                 ))),
1341                            );
1342                        }
1343                    )*
1344                }
1345            }
1346        }
1347    }
1348    }
1349}
1350#[macro_export]
1351macro_rules! register_constraint_new {
1352    ( $name:ident, $lt: lifetime, $($element: ident),+ ) => { aorist_paste::item! {
1353        #[derive(Clone)]
1354        pub enum $name {
1355            $(
1356                $element($element),
1357            )+
1358        }
1359        #[cfg_attr(feature = "python", pyclass(module = "aorist"))]
1360        #[derive(Clone)]
1361        pub struct [<$name Program>] {
1362            inner: [<$name ProgramEnum>],
1363        }
1364
1365        #[cfg(feature = "python")]
1366        #[pymethods]
1367        impl [<$name Program>] {
1368            #[new]
1369            fn new(inner: [<$name ProgramEnum>]) -> Self {
1370                Self { inner }
1371            }
1372        }
1373        #[cfg(feature = "python")]
1374        impl TOuterProgram for [<$name Program>] {
1375            type TAncestry = ConceptAncestry;
1376            fn get_dialect(&self) -> Dialect {
1377                self.inner.get_dialect()
1378            }
1379            fn compute_args(
1380                &self,
1381                root: <Self::TAncestry as Ancestry>::TConcept,
1382                ancestry: &Self::TAncestry,
1383                context: &mut aorist_core::Context,
1384            ) -> (String, String, ParameterTuple, Dialect) {
1385                let gil = Python::acquire_gil();
1386                let py = gil.python();
1387                //let mut args = Vec::new();
1388                let dill: &PyModule = PyModule::import(py, "dill").unwrap();
1389                let mut args = Vec::new();
1390                let mut kwargs = LinkedHashMap::new();
1391                for (input_types, serialized) in &self.inner.get_arg_functions() {
1392                    let py_arg = PyString::new(py, &serialized);
1393                    let deserialized = dill.getattr("loads").unwrap().call1((py_arg,)).unwrap();
1394                    let mut objects = Vec::new();
1395                    let mut context_pos = None;
1396                    for (i, x) in input_types.iter().enumerate() {
1397                        if x == "context" {
1398                            assert!(context_pos.is_none());
1399                            context_pos = Some(i);
1400                        } else {
1401                            objects.push(
1402                                ancestry.py_object(x, root.clone(), py).unwrap().to_object(py)
1403                            );
1404                        }
1405                    }
1406                    let extracted;
1407                    if let Some(pos) = context_pos {
1408                        let obj = PyObject::from(PyCell::new(py, context.clone()).unwrap());
1409                        objects.insert(pos, obj.to_object(py));
1410                        let returned = deserialized.call1((objects,)).unwrap();
1411                        let (
1412                            extracted_string, extracted_context
1413                        ) : (String, aorist_core::Context) = returned.extract().unwrap();
1414                        context.insert(&extracted_context);
1415                        extracted = extracted_string;
1416                    } else {
1417                        let arg = deserialized.call1((objects,)).unwrap();
1418                        // TODO: add more return types here
1419                        extracted = arg.extract().unwrap();
1420                    }
1421                    let ast = AST::StringLiteral(StringLiteral::new_wrapped(extracted, false));
1422                    args.push(ast);
1423                }
1424                for (key, (input_types, serialized)) in &self.inner.get_kwarg_functions() {
1425                    let py_arg = PyString::new(py, &serialized);
1426                    let py_arg = py_arg.call_method1("encode", ("latin-1",)).unwrap();
1427                    let deserialized = dill.getattr("loads").unwrap().call1((py_arg,)).unwrap();
1428
1429
1430                    let mut objects = Vec::new();
1431                    let mut context_pos = None;
1432                    for (i, x) in input_types.iter().enumerate() {
1433                        if x == "context" {
1434                            assert!(context_pos.is_none());
1435                            context_pos = Some(i);
1436                        } else {
1437                            match ancestry.py_object(x, root.clone(), py) {
1438                                Ok(x) => objects.push(x.to_object(py)),
1439                                Err(err) => panic!(
1440                                    "Error when running program for key {} input_type {} # {}:\n{}",
1441                                    key, i, x, err,
1442                                ),
1443                            }
1444                        }
1445                    }
1446                    let extracted;
1447                    if let Some(pos) = context_pos {
1448                        let obj = PyObject::from(PyCell::new(py, context.clone()).unwrap());
1449                        objects.insert(pos, obj.to_object(py));
1450                        let returned = deserialized.call1((objects,)).unwrap();
1451                        let (
1452                            extracted_string, extracted_context
1453                        ) : (String, aorist_core::Context) = returned.extract().unwrap();
1454                        context.insert(&extracted_context);
1455                        extracted = extracted_string;
1456                    } else {
1457                        extracted = match deserialized.call1((objects,)) {
1458                            Ok(arg) => arg.extract().unwrap(),
1459                            Err(err) => {
1460                                err.print(py);
1461                                panic!("Problem when extracting object. See traceback above");
1462                            }
1463                        };
1464                    }
1465
1466                    let ast = AST::StringLiteral(StringLiteral::new_wrapped(extracted, false));
1467                    kwargs.insert(key.to_string(), ast);
1468                }
1469                (
1470                    self.inner.get_code(),
1471                    self.inner.get_entrypoint(),
1472                    ParameterTuple { args, kwargs },
1473                    // TODO: this should be handled by self.inner.get_dialect()
1474                    self.inner.get_dialect(),
1475                )
1476            }
1477        }
1478        #[cfg_attr(feature = "python", derive(pyo3::prelude::FromPyObject))]
1479        #[derive(Clone)]
1480        pub enum [<$name ProgramEnum>] {
1481            $(
1482                $element([<$element Program>]),
1483            )+
1484        }
1485        impl [<$name ProgramEnum>] {
1486            #[cfg(feature = "python")]
1487            pub fn get_arg_functions(&self) -> Vec<(Vec<String>, String)> {
1488                match self {
1489                    $(
1490                        [<$name ProgramEnum>]::$element(x) => x.get_arg_functions(),
1491                    )+
1492                }
1493            }
1494            pub fn get_dialect(&self) -> Dialect {
1495                match self {
1496                    $(
1497                        [<$name ProgramEnum>]::$element(x) => x.get_dialect(),
1498                    )+
1499                }
1500            }
1501            pub fn get_code(&self) -> String {
1502                match self {
1503                    $(
1504                        [<$name ProgramEnum>]::$element(x) => x.get_code(),
1505                    )+
1506                }
1507            }
1508            pub fn get_entrypoint(&self) -> String {
1509                match self {
1510                    $(
1511                        [<$name ProgramEnum>]::$element(x) => x.get_entrypoint(),
1512                    )+
1513                }
1514            }
1515            pub fn get_kwarg_functions(&self) -> LinkedHashMap<String, (Vec<String>, String)> {
1516                match self {
1517                    $(
1518                        [<$name ProgramEnum>]::$element(x) => x.get_kwarg_functions(),
1519                    )+
1520                }
1521            }
1522        }
1523        pub enum [<$name Builder>]<$lt> {
1524            $(
1525                $element(ConstraintBuilder<$lt, $element>),
1526            )+
1527        }
1528        #[cfg(feature = "python")]
1529        #[pymodule]
1530        fn libaorist_constraint(py: Python, m: &PyModule) -> PyResult<()> {
1531            init_logging();
1532            $(
1533                m.add_class::<$element>()?;
1534                m.add_class::<[<$name Program>]>()?;
1535            )+
1536            Ok(())
1537        }
1538        impl <$lt> TBuilder<$lt> for [<$name Builder>]<$lt> {
1539            type OuterType = Constraint;
1540            type TEnum = AoristRef<Concept>;
1541            type TAncestry = ConceptAncestry;
1542            fn builders() -> Vec<[<$name Builder>]<$lt>> where Self : Sized {
1543                vec![
1544                    $(
1545                        [<$name Builder>]::$element(
1546                            ConstraintBuilder::<$lt, $element>{
1547                                _phantom: std::marker::PhantomData,
1548                                _phantom_lt: std::marker::PhantomData,
1549                            }
1550                        ),
1551                    )+
1552                ]
1553            }
1554            fn get_constraint_name(&self) -> String {
1555                match &self {
1556                    $(
1557                        [<$name Builder>]::$element(_) => stringify!($element).to_string(),
1558                    )+
1559                }
1560            }
1561            fn get_required_constraint_names(&self) -> Vec<String> {
1562                match &self {
1563                    $(
1564                        [<$name Builder>]::$element(_) => $element::get_required_constraint_names(),
1565                    )+
1566                }
1567            }
1568            fn build_constraint(
1569                &self,
1570                root_uuid: Uuid,
1571                potential_child_constraints: Vec<Arc<RwLock<Self::OuterType>>>,
1572            ) -> Result<Self::OuterType> {
1573                match &self {
1574                    $(
1575                        [<$name Builder>]::$element(x) => Ok(Constraint {
1576                            name: self.get_constraint_name(),
1577                            root: self.get_root_type_name()?,
1578                            requires: Some(self.get_required_constraint_names()),
1579                            inner: Some(
1580                                $name::$element(x.build_constraint(
1581                                    root_uuid,
1582                                    potential_child_constraints,
1583                                )?)
1584                            ),
1585                        }),
1586                    )+
1587                }
1588            }
1589            fn get_root_type_name(&self) -> Result<String> {
1590                match &self {
1591                    $(
1592                        [<$name Builder>]::$element(_) => $element::get_root_type_name(),
1593                    )+
1594                }
1595            }
1596            fn get_required(&self, root: AoristRef<Concept>, ancestry:&ConceptAncestry) -> Vec<Uuid> {
1597                match &self {
1598                    $(
1599                        [<$name Builder>]::$element(_) =>
1600                        $element::get_required(root, ancestry),
1601                    )+
1602                }
1603            }
1604            fn should_add(&self, root: AoristRef<Concept>, ancestry:&ConceptAncestry) -> bool {
1605                match &self {
1606                    $(
1607                        [<$name Builder>]::$element(_) =>
1608                        $element::should_add(root, ancestry),
1609                    )+
1610                }
1611            }
1612        }
1613        impl <$lt> TConstraintEnum<$lt> for $name {
1614            fn get_required_constraint_names() -> HashMap<String, Vec<String>> {
1615                hashmap! {
1616                    $(
1617                        stringify!($element).to_string() => $element::get_required_constraint_names(),
1618                    )+
1619                }
1620            }
1621            fn get_explanations() -> HashMap<String, (Option<String>, Option<String>)> {
1622                hashmap! {
1623                    $(
1624                        stringify!($element).to_string() => (
1625                            $element::get_title(),
1626                            $element::get_body(),
1627                        ),
1628                    )+
1629                }
1630            }
1631        }
1632        impl <$lt> $name {
1633            pub fn get_root_type_name(&self) -> Result<String> {
1634                match self {
1635                    $(
1636                        Self::$element(_) => $element::get_root_type_name(),
1637                    )+
1638                }
1639            }
1640            pub fn get_downstream_constraints(&self) -> Result<Vec<Arc<RwLock<Constraint>>>> {
1641                match self {
1642                    $(
1643                        Self::$element(x) => x.get_downstream_constraints(),
1644                    )+
1645                }
1646            }
1647            pub fn requires_program(&self) -> Result<bool> {
1648                match self {
1649                    $(
1650                        Self::$element(x) => x.requires_program(),
1651                    )+
1652                }
1653            }
1654            pub fn get_uuid(&self) -> Result<Uuid> {
1655                match self {
1656                    $(
1657                        Self::$element(x) => x.get_uuid(),
1658                    )+
1659                }
1660            }
1661            pub fn get_title(&self) -> Option<String> {
1662                match self {
1663                    $(
1664                        Self::$element(_) => $element::get_title(),
1665                    )+
1666                }
1667            }
1668            pub fn get_body(&self) -> Option<String> {
1669                match self {
1670                    $(
1671                        Self::$element(_) => $element::get_body(),
1672                    )+
1673                }
1674            }
1675            pub fn get_root_uuid(&self) -> Result<Uuid> {
1676                match self {
1677                    $(
1678                        Self::$element(x) => x.get_root_uuid(),
1679                    )+
1680                }
1681            }
1682            fn get_root_type_names() -> Result<HashMap<String, String>> {
1683                Ok(hashmap! {
1684                    $(
1685                        stringify!($element).to_string() => $element::get_root_type_name()?,
1686                    )+
1687                })
1688            }
1689            pub fn get_name(&self) -> String {
1690                match self {
1691                    $(
1692                        Self::$element(x) => stringify!($element).to_string(),
1693                    )+
1694                }
1695            }
1696            pub fn should_add(
1697                &self,
1698                root: AoristRef<Concept>,
1699                ancestry: &ConceptAncestry,
1700            ) -> bool {
1701                match &self {
1702                    $(
1703                        Self::$element(_) => $element::should_add(root,
1704                        ancestry),
1705                    )+
1706                }
1707            }
1708        }}
1709    }
1710}