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
//! API for building objects

// These are new-style builders that build on top of the partial object API.
mod curve;
mod cycle;
mod edge;
mod face;
mod shell;
mod sketch;
mod solid;
mod surface;
mod vertex;

use std::array;

pub use self::{
    curve::CurveBuilder,
    cycle::CycleBuilder,
    edge::{GlobalEdgeBuilder, HalfEdgeBuilder},
    face::FaceBuilder,
    shell::ShellBuilder,
    sketch::SketchBuilder,
    solid::SolidBuilder,
    surface::SurfaceBuilder,
    vertex::{GlobalVertexBuilder, SurfaceVertexBuilder},
};

/// Pass objects to a builder method
///
/// Many builder methods receive objects as arguments, and many builder
/// arguments return objects back, based on their input. In the general case,
/// the number of objects passed and returned is usually arbitrary, but many
/// callers pass a specific number of objects, and expect the same number of
/// objects back.
///
/// This trait can be used to do exactly that. It is implemented for `Vec` and
/// arrays. When passing a `Vec`, a `Vec` is returned. When passing an array, an
/// array of the same size is returned.
pub trait ObjectArgument<T>: IntoIterator<Item = T> {
    /// The value returned, if the implementing type is passed on an argument
    ///
    /// The return value has the same length as the implementing type, but it is
    /// not necessarily of the same type. For this reason, this associated type
    /// is generic.
    type SameSize<R>;

    /// A return value that has one more element than the argument
    type SizePlusOne<R>;

    /// Return the number of objects
    fn num_objects(&self) -> usize;

    /// Create a return value by mapping the implementing type
    fn map<F, R>(self, f: F) -> Self::SameSize<R>
    where
        F: FnMut(T) -> R;

    /// Create a return value with one more element
    fn map_plus_one<F, R>(self, item: R, f: F) -> Self::SizePlusOne<R>
    where
        F: FnMut(T) -> R;
}

impl<T> ObjectArgument<T> for Vec<T> {
    type SameSize<R> = Vec<R>;
    type SizePlusOne<R> = Vec<R>;

    fn num_objects(&self) -> usize {
        self.len()
    }

    fn map<F, R>(self, mut f: F) -> Self::SameSize<R>
    where
        F: FnMut(T) -> R,
    {
        let mut ret = Vec::new();

        for item in self {
            ret.push(f(item));
        }

        ret
    }

    fn map_plus_one<F, R>(self, item: R, f: F) -> Self::SizePlusOne<R>
    where
        F: FnMut(T) -> R,
    {
        let mut ret = self.map(f);
        ret.push(item);
        ret
    }
}

// This macro implements `ObjectArgument` for a number of array types. This
// should just be a single implementation, but while const generic expressions
// are still unstable, this is unfortunately not possible:
// <https://github.com/rust-lang/rust/issues/76560>
macro_rules! impl_object_argument_for_arrays {
    ($($len:expr, $len_plus_one:expr;)*) => {
        $(
            impl<T> ObjectArgument<T> for [T; $len] {
                type SameSize<R> = [R; $len];
                type SizePlusOne<R> = [R; $len_plus_one];

                fn num_objects(&self) -> usize {
                    self.len()
                }

                fn map<F, R>(self, f: F) -> Self::SameSize<R>
                where
                    F: FnMut(T) -> R,
                {
                    self.map(f)
                }

                fn map_plus_one<F, R>(self, item: R, mut f: F)
                    -> Self::SizePlusOne<R>
                where
                    F: FnMut(T) -> R,
                {
                    let mut tmp = array::from_fn(|_| None);
                    for (i, item) in self.into_iter().enumerate() {
                        tmp[i] = Some(f(item));
                    }

                    tmp[tmp.len() - 1] = Some(item);

                    tmp.map(Option::unwrap)
                }
            }
        )*
    };
}

impl_object_argument_for_arrays!(
    0, 1;
    1, 2;
    2, 3;
    3, 4;
    4, 5;
    5, 6;
    6, 7;
    7, 8;
);