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
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
use std::borrow::Cow;
use std::fmt;
use std::slice;
use std::vec;

use variant_name::VariantName;

/// Static string that identifies a transformation.
pub type TransformId = &'static str;
/// Static string that identifies a type of a input/output variable.
pub type TypeId = &'static str;

/// Algorithm that defines the transformation
#[derive(Clone)]
pub enum Algorithm<T: Clone, E> {
    /// A rust function with a vector of input variables as argument.
    /// Returns a vector of [`Result`], one result for each output.
    Function(fn(Vec<Cow<T>>) -> Vec<Result<T, E>>),
    /// Use this variant for algorithms with no input. Such algorithm will
    /// always return this constant.
    Constant(Vec<T>),
}

impl<T: Clone + fmt::Debug, E> fmt::Debug for Algorithm<T, E> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            &Algorithm::Function(ref fun) => write!(f, "Function({:p})", fun),
            &Algorithm::Constant(ref vec) => write!(f, "Constant({:?})", vec),
        }
    }
}

/// A transformation defined by an [`Algorithm`], with a determined number of
/// inputs and outputs.
#[derive(Clone, Debug)]
pub struct Transformation<T: Clone, E> {
    /// Transformation name
    pub name: TransformId,
    pub description: Cow<'static, str>,
    /// Inputs of the transformation, may include a default value
    pub input: Vec<(TypeId, Option<T>)>,
    /// Outputs of the transformation
    pub output: Vec<TypeId>,
    /// Algorithm defining the transformation
    pub algorithm: Algorithm<T, E>,
}

/// Result of [`Transformation::start`].
///
/// Stores the state of the transformation just before it is called.
pub struct TransformationCaller<'a, 'b, T: 'a + 'b + Clone, E: 'a> {
    expected_input_types: slice::Iter<'a, (TypeId, Option<T>)>,
    algorithm: &'a Algorithm<T, E>,
    input: Vec<Cow<'b, T>>,
}

impl<T, E> Transformation<T, E>
where
    T: Clone + VariantName,
{
    /// Create a new Transformation always returning a single constant
    pub fn new_constant(t: T) -> Self {
        Self {
            name: t.variant_name(),
            description: Cow::Owned(format!("Constant variable of type '{}'", t.variant_name())),
            input: vec![],
            output: vec![t.variant_name()],
            algorithm: Algorithm::Constant(vec![t]),
        }
    }

    /// Set this transformation to the given constant value.
    pub fn set_constant(&mut self, t: T) {
        self.name = t.variant_name();
        self.input = vec![];
        self.output = vec![t.variant_name()];
        self.algorithm = Algorithm::Constant(vec![t])
    }
}

impl<T, E> Transformation<T, E>
where
    T: Clone,
{
    /// Ready the transformation to be called.
    pub fn start(&self) -> TransformationCaller<T, E> {
        TransformationCaller {
            expected_input_types: self.input.iter(),
            algorithm: &self.algorithm,
            input: Vec::new(),
        }
    }

    /// Check that output exists for the transform
    pub fn output_exists(&self, output_i: usize) -> bool {
        output_i < self.output.len()
    }

    /// Check that input exists for the transform
    pub fn input_exists(&self, input_i: usize) -> bool {
        input_i < self.input.len()
    }

    /// Return nth output type. Panic if output_i > self.output.len()
    pub fn nth_output_type(&self, output_i: usize) -> TypeId {
        self.output[output_i]
    }

    /// Return nth input type. Panic if input_i > self.input.len()
    pub fn nth_input_type(&self, input_i: usize) -> TypeId {
        self.input[input_i].0
    }
}

impl<'a, 'b, T, E> TransformationCaller<'a, 'b, T, E>
where
    T: Clone + VariantName,
{
    /// Feed next argument to transformation.
    pub fn feed(&mut self, input: T) {
        self.check_type(&input);
        self.input.push(Cow::Owned(input));
    }

    /// Feed next argument to transformation. Expect a reference as input.
    pub fn feed_ref(&mut self, input: &'b T) {
        self.check_type(input);
        self.input.push(Cow::Borrowed(input));
    }

    /// Panic if expected type is not provided or if too many arguments are supplied.
    fn check_type(&mut self, input: &T) {
        let expected_type = self
            .expected_input_types
            .next()
            .expect("Not all type consumed")
            .0;
        if input.variant_name() != expected_type {
            panic!("Wrong type on feeding algorithm!");
        }
    }
}

impl<'a, 'b, T, E> TransformationCaller<'a, 'b, T, E>
where
    T: Clone,
{
    /// Compute the transformation with the provided arguments
    pub fn call(mut self) -> TransformationResult<Result<T, E>> {
        if self.expected_input_types.next().is_some() {
            panic!("Missing input arguments!");
        } else {
            TransformationResult {
                output: match self.algorithm {
                    &Algorithm::Function(f) => f(self.input).into_iter(),
                    &Algorithm::Constant(ref c) => c
                        .clone()
                        .into_iter()
                        .map(Ok)
                        .collect::<Vec<_>>()
                        .into_iter(),
                },
            }
        }
    }
}

/// Represents the result of a transformation.
pub struct TransformationResult<T> {
    output: vec::IntoIter<T>,
}

impl<T> Iterator for TransformationResult<T> {
    type Item = T;
    fn next(&mut self) -> Option<Self::Item> {
        self.output.next()
    }
}