1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
use dagger_core::introspection::{FullType, FullTypeFields, FullTypeFieldsArgs};
use genco::prelude::rust;
use genco::quote;
use itertools::Itertools;

use crate::functions::{CommonFunctions};
use crate::rust::functions::{
    field_options_struct_name, format_function, format_name, format_optional_args,
    format_struct_comment, format_struct_name,
};
use crate::utility::OptionExt;

pub fn render_object(funcs: &CommonFunctions, t: &FullType) -> eyre::Result<rust::Tokens> {
    let selection = rust::import("crate::querybuilder", "Selection");
    let child = rust::import("std::process", "Child");
    let conn = rust::import("dagger_core::connect_params", "ConnectParams");
    let arc = rust::import("std::sync", "Arc");

    Ok(quote! {
        pub struct $(t.name.pipe(|s| format_name(s))) {
            pub proc: $arc<$child>,
            pub selection: $selection,
            pub conn: $conn,
        }

        $(t.fields.pipe(|f| render_optional_args(funcs, f)))

        impl $(t.name.pipe(|s| format_name(s))) {
            $(t.fields.pipe(|f| render_functions(funcs, f)))
        }
    })
}

fn render_optional_args(
    funcs: &CommonFunctions,
    fields: &Vec<FullTypeFields>,
) -> Option<rust::Tokens> {
    let rendered_fields = fields
        .iter()
        .map(|f| render_optional_arg(funcs, f))
        .flatten()
        .collect::<Vec<_>>();

    if rendered_fields.len() == 0 {
        None
    } else {
        Some(quote! {
            $(for field in rendered_fields join ($['\r']) => $field)
        })
    }
}

fn render_optional_arg(funcs: &CommonFunctions, field: &FullTypeFields) -> Option<rust::Tokens> {
    let output_type = field_options_struct_name(field);
    let fields = format_optional_args(funcs, field);

    let builder = rust::import("derive_builder", "Builder");
    let _phantom_data = rust::import("std::marker", "PhantomData");

    if let Some((fields, contains_lifetime)) = fields {
        Some(quote! {
            #[derive($builder, Debug, PartialEq)]
            pub struct $output_type$(if contains_lifetime => <'a>) {
                //#[builder(default, setter(skip))]
                //pub marker: $(phantom_data)<&'a ()>,
                $fields
            }
        })
    } else {
        None
    }
}

pub fn render_optional_field_args(
    funcs: &CommonFunctions,
    args: &Vec<&FullTypeFieldsArgs>,
) -> Option<(rust::Tokens, bool)> {
    if args.len() == 0 {
        return None;
    }
    let mut contains_lifetime = false;
    let rendered_args = args.into_iter().map(|a| &a.input_value).map(|a| {
        let type_ = funcs.format_immutable_input_type(&a.type_);
        if type_.contains("str") {
            contains_lifetime = true;
        }
        quote! {
            $(a.description.pipe(|d| format_struct_comment(d)))
            #[builder(setter(into, strip_option))]
            pub $(format_struct_name(&a.name)): Option<$(type_)>,
        }
    });

    Some((
        quote! {
            $(for arg in rendered_args join ($['\r']) => $arg)
        },
        contains_lifetime,
    ))
}

fn render_functions(funcs: &CommonFunctions, fields: &Vec<FullTypeFields>) -> Option<rust::Tokens> {
    let rendered_functions = fields
        .iter()
        .map(|f| render_function(funcs, f))
        .collect::<Vec<_>>();

    if rendered_functions.len() > 0 {
        Some(quote! {
            $(for func in rendered_functions join ($['\r']) => $func)
        })
    } else {
        None
    }
}

fn render_function(funcs: &CommonFunctions, field: &FullTypeFields) -> Option<rust::Tokens> {
    Some(quote! {
        $(format_function(funcs, field))
    })
}