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
use std::{
    error::Error,
    fmt::{Debug, Display},
};

/// Error observed while using a `NodeIndex` to access a node in a `SelfRefCol`.
#[derive(PartialEq, Eq, Clone, Copy)]
pub enum NodeIndexError {
    /// RemovedNode => Referenced node is removed from the collection. Node index can only be used if the corresponding node still belongs to the collection.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use orx_selfref_col::*;
    ///
    /// #[derive(Clone, Copy, Debug)]
    /// struct Var;
    /// impl<'a> Variant<'a, char> for Var {
    ///     type Storage = NodeDataLazyClose<char>;
    ///     type Prev = NodeRefSingle<'a, Self, char>;
    ///     type Next = NodeRefsVec<'a, Self, char>;
    ///     type Ends = NodeRefsArray<'a, 2, Self, char>;
    ///     type MemoryReclaim = MemoryReclaimOnThreshold<2>;
    /// }
    ///
    /// let mut col = SelfRefCol::<Var, _>::new();
    /// let [a, b, c, d, e, f, g] = col
    ///     .mutate_take(['a', 'b', 'c', 'd', 'e', 'f', 'g'], |x, values| {
    ///         values.map(|val| x.push_get_ref(val).index(&x))
    ///     });
    ///
    /// let removed_b = col.mutate_take(b, |x, b| x.as_node_ref(b).close_node_take_data(&x)); // does not trigger reclaim yet
    /// assert_eq!(removed_b, 'b');
    ///
    /// assert_eq!(a.invalidity_reason_for_collection(&col), None);
    /// assert_eq!(c.invalidity_reason_for_collection(&col), None);
    ///
    /// assert_eq!(b.invalidity_reason_for_collection(&col), Some(NodeIndexError::RemovedNode));
    /// ```
    RemovedNode,
    /// WrongCollection => Node index is used on a collection different than the collection it is created for.  Node indices can only be used for the collection they belong to.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use orx_selfref_col::*;
    ///
    /// #[derive(Clone, Copy, Debug)]
    /// struct Var;
    /// impl<'a> Variant<'a, char> for Var {
    ///     type Storage = NodeDataLazyClose<char>;
    ///     type Prev = NodeRefSingle<'a, Self, char>;
    ///     type Next = NodeRefsVec<'a, Self, char>;
    ///     type Ends = NodeRefsArray<'a, 2, Self, char>;
    ///     type MemoryReclaim = MemoryReclaimOnThreshold<2>;
    /// }
    ///
    /// let mut col1 = SelfRefCol::<Var, _>::new();
    /// let a = col1.mutate_take('a', |x, a| x.push_get_ref(a).index(&x));
    ///
    /// let col2 = SelfRefCol::<Var, _>::new();
    ///
    /// assert_eq!(a.invalidity_reason_for_collection(&col1), None);
    /// assert_eq!(a.invalidity_reason_for_collection(&col2), Some(NodeIndexError::WrongCollection));
    /// ```
    WrongCollection,
    /// ReorganizedCollection => All nodes of the containing collection is re-organized in order to reclaim memory of closed nodes. Such a reorganization happens only if the collection uses `MemoryReclaimOnThreshold` policy and utilization level of memory drops below the threshold due to pop and remove operations. It is never observed if the list only grows or if `MemoryReclaimNever` policy is used. In this case, the references need to be recreated.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use orx_selfref_col::*;
    ///
    /// #[derive(Clone, Copy, Debug)]
    /// struct Var;
    /// impl<'a> Variant<'a, char> for Var {
    ///     type Storage = NodeDataLazyClose<char>;
    ///     type Prev = NodeRefSingle<'a, Self, char>;
    ///     type Next = NodeRefsVec<'a, Self, char>;
    ///     type Ends = NodeRefsArray<'a, 2, Self, char>;
    ///     type MemoryReclaim = MemoryReclaimOnThreshold<2>;
    /// }
    ///
    /// let mut col = SelfRefCol::<Var, _>::new();
    /// let [a, b, c] = col.mutate_take(['a', 'b', 'c'], |x, values| {
    ///     values.map(|val| x.push_get_ref(val).index(&x))
    /// });
    ///
    /// // triggers memory reclaim, invalidating all prior node indices
    /// let removed_b = col.mutate_take(b, |x, b| x.as_node_ref(b).close_node_take_data(&x));
    /// assert_eq!(removed_b, 'b');
    ///
    /// assert_eq!(a.invalidity_reason_for_collection(&col), Some(NodeIndexError::ReorganizedCollection));
    /// assert_eq!(b.invalidity_reason_for_collection(&col), Some(NodeIndexError::ReorganizedCollection));
    /// assert_eq!(c.invalidity_reason_for_collection(&col), Some(NodeIndexError::ReorganizedCollection));
    ReorganizedCollection,
}

impl Error for NodeIndexError {}

impl Debug for NodeIndexError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::RemovedNode => write!(f,"RemovedNode => Referenced node is removed from the collection. Node index can only be used if the corresponding node still belongs to the collection."),
            Self::WrongCollection => write!(f, "WrongCollection => Node index is used on a collection different than the collection it is created for.  Node indices can only be used for the collection they belong to."),
            Self::ReorganizedCollection => write!(f, "ReorganizedCollection => All nodes of the containing collection is re-organized in order to reclaim memory of closed nodes. Such a reorganization happens only if the collection uses `MemoryReclaimOnThreshold` policy and utilization level of memory drops below the threshold due to pop and remove operations. It is never observed if the list only grows or if `MemoryReclaimNever` policy is used. In this case, the references need to be recreated."),
        }
    }
}

impl Display for NodeIndexError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        <NodeIndexError as Debug>::fmt(self, f)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn debug() {
        assert_eq!(&format!("{:?}", NodeIndexError::RemovedNode),
            "RemovedNode => Referenced node is removed from the collection. Node index can only be used if the corresponding node still belongs to the collection.");
        assert_eq!(&format!("{}", NodeIndexError::RemovedNode),
            "RemovedNode => Referenced node is removed from the collection. Node index can only be used if the corresponding node still belongs to the collection.");

        assert_eq!(&format!("{:?}", NodeIndexError::WrongCollection),
            "WrongCollection => Node index is used on a collection different than the collection it is created for.  Node indices can only be used for the collection they belong to.");
        assert_eq!(&format!("{}", NodeIndexError::WrongCollection),
            "WrongCollection => Node index is used on a collection different than the collection it is created for.  Node indices can only be used for the collection they belong to.");

        assert_eq!(&format!("{:?}", NodeIndexError::ReorganizedCollection),
            "ReorganizedCollection => All nodes of the containing collection is re-organized in order to reclaim memory of closed nodes. Such a reorganization happens only if the collection uses `MemoryReclaimOnThreshold` policy and utilization level of memory drops below the threshold due to pop and remove operations. It is never observed if the list only grows or if `MemoryReclaimNever` policy is used. In this case, the references need to be recreated.");
        assert_eq!(&format!("{}", NodeIndexError::ReorganizedCollection),
            "ReorganizedCollection => All nodes of the containing collection is re-organized in order to reclaim memory of closed nodes. Such a reorganization happens only if the collection uses `MemoryReclaimOnThreshold` policy and utilization level of memory drops below the threshold due to pop and remove operations. It is never observed if the list only grows or if `MemoryReclaimNever` policy is used. In this case, the references need to be recreated.");
    }
}