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
//! Partial objects
//!
//! This module contains type that represent partial objects. This is useful
//! when building objects, as it's often possible to provide just some the data
//! they own or objects they reference, while computing the rest.
//!
//! More generally speaking, there are situations where different parts of a new
//! objects are available at different times, and provided from different
//! places. Partial objects can be used to represent such partially constructed
//! objects whenever that is required.
//!
//! The API for partial objects follows a specific style:
//!
//! - Partial objects are structs with fields that mirror the fields of the full
//!   object structs, but all fields are optional.
//! - Partial object structs implement [`Default`], but a `partial` method is
//!   also available on the respective full object struct, as a perhaps more
//!   convenient and readable way to construct a partial object.
//! - Partial object structs have `with_*` methods to provide values for each of
//!   their fields.
//! - Partial object structs may have other methods with prefixes like `as_*`,
//!   `from_*`, or similar, if one or more of their fields can be initialized by
//!   providing alternative data.
//! - Partial object structs have a `build` method to build a full object.
//! - All `with_*`, `as_*`, and `build` methods can be chained, to provide a
//!   convenient API.

mod curve;
mod edge;
mod vertex;

pub use self::{
    curve::{PartialCurve, PartialGlobalCurve},
    edge::{PartialGlobalEdge, PartialHalfEdge},
    vertex::{PartialGlobalVertex, PartialSurfaceVertex, PartialVertex},
};

use crate::{
    objects::{
        Curve, GlobalCurve, GlobalEdge, GlobalVertex, HalfEdge, SurfaceVertex,
        Vertex,
    },
    stores::{Handle, Stores},
};

/// Either a partial object or a full one
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
pub enum MaybePartial<T: HasPartialForm> {
    /// A full object
    Full(T),

    /// A partial object
    Partial(T::PartialForm),
}

impl<T: HasPartialForm> MaybePartial<T> {
    /// Return the full object, either directly or by building it
    pub fn into_full(self, stores: &Stores) -> T {
        match self {
            Self::Partial(partial) => T::from_partial(partial, stores),
            Self::Full(full) => full,
        }
    }

    /// Return the partial object, either directly or via conversion
    pub fn into_partial(self) -> T::PartialForm {
        match self {
            Self::Partial(partial) => partial,
            Self::Full(full) => full.into(),
        }
    }
}

/// Implemented for types that are partial objects
///
/// # Implementation Note
///
/// It would be nicer to require a conversion from `&Self` into the partial
/// form, but I think we need a `where` clause on the associated type to specify
/// that, which is unstable. It should become stable soon though, together with
/// generic associated types:
/// <https://github.com/rust-lang/rust/issues/44265>
pub trait HasPartialForm: Into<Self::PartialForm> {
    /// The full version of this partial object
    type PartialForm;

    /// Build a full object from the partial object
    fn from_partial(partial: Self::PartialForm, stores: &Stores) -> Self;
}

macro_rules! impl_traits {
    ($($full:ty, $partial:ty;)*) => {
        $(
            impl HasPartialForm for $full {
                type PartialForm = $partial;

                fn from_partial(partial: Self::PartialForm, stores: &Stores)
                    -> Self
                {
                    partial.build(stores)
                }
            }

            impl From<$full> for MaybePartial<$full> {
                fn from(full: $full) -> Self {
                    Self::Full(full)
                }
            }

            impl From<$partial> for MaybePartial<$full> {
                fn from(partial: $partial) -> Self {
                    Self::Partial(partial)
                }
            }
        )*
    };
}

impl_traits!(
    Curve, PartialCurve;
    GlobalEdge, PartialGlobalEdge;
    GlobalVertex, PartialGlobalVertex;
    HalfEdge, PartialHalfEdge;
    SurfaceVertex, PartialSurfaceVertex;
    Vertex, PartialVertex;

    Handle<GlobalCurve>, PartialGlobalCurve;
);