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
use std::{collections::HashSet, fmt};

use fnv::FnvBuildHasher;
use serde::{Deserialize, Deserializer};
use serde_state::DeserializeState;

use necsim_core::lineage::Lineage;
use necsim_impls_std::lineage_file::loader::LineageFileLoader;

use super::{
    super::pause::{Pause, SampleDestiny},
    SampleOrigin,
};

impl fmt::Display for SampleOrigin {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Self::Habitat => fmt.write_str("Habitat"),
            Self::List(_) => fmt.write_str("List"),
            Self::Bincode(_) => fmt.write_str("Bincode"),
        }
    }
}

impl fmt::Debug for SampleOrigin {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        struct VecLineages(usize);

        impl fmt::Debug for VecLineages {
            fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
                write!(fmt, "Vec<Lineage; {}>", self.0)
            }
        }

        match self {
            Self::Habitat => fmt.debug_struct(stringify!(Habitat)).finish(),
            Self::List(lineages) => fmt
                .debug_tuple(stringify!(List))
                .field(&VecLineages(lineages.len()))
                .finish(),
            Self::Bincode(loader) => fmt
                .debug_tuple(stringify!(Bincode))
                .field(&VecLineages(loader.get_lineages().len()))
                .finish(),
        }
    }
}

impl<'de> DeserializeState<'de, &'de Option<Pause>> for SampleOrigin {
    fn deserialize_state<D: Deserializer<'de>>(
        pause: &mut &'de Option<Pause>,
        deserializer: D,
    ) -> Result<Self, D::Error> {
        let raw = SampleOriginRaw::deserialize(deserializer)?;

        if let Some(pause) = pause {
            if matches!(pause.destiny, SampleDestiny::List)
                && !matches!(raw, SampleOriginRaw::List(_))
            {
                return Err(serde::de::Error::custom(format!(
                    "`List` pause destiny requires `List` origin sample, found `{raw}`"
                )));
            }
        }

        let lineages = match &raw {
            SampleOriginRaw::Habitat => return Ok(Self::Habitat),
            SampleOriginRaw::List(lineages) => lineages.iter(),
            SampleOriginRaw::Bincode(loader) => loader.get_lineages().iter(),
        };

        let mut global_references =
            HashSet::with_capacity_and_hasher(lineages.len(), FnvBuildHasher::default());

        for lineage in lineages {
            if !global_references.insert(lineage.global_reference.clone()) {
                return Err(serde::de::Error::custom(format!(
                    "duplicate lineage reference #{}",
                    lineage.global_reference
                )));
            }
        }

        match raw {
            SampleOriginRaw::Habitat => Ok(Self::Habitat),
            SampleOriginRaw::List(lineages) => Ok(Self::List(lineages)),
            SampleOriginRaw::Bincode(loader) => Ok(Self::Bincode(loader)),
        }
    }
}

#[derive(Debug, Deserialize)]
enum SampleOriginRaw {
    Habitat,
    List(Vec<Lineage>),
    Bincode(LineageFileLoader),
}

impl fmt::Display for SampleOriginRaw {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Self::Habitat => fmt.write_str("Habitat"),
            Self::List(_) => fmt.write_str("List"),
            Self::Bincode(_) => fmt.write_str("Bincode"),
        }
    }
}