vyre_primitives/types/
linear_check.rs1#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
20#[non_exhaustive]
21pub enum LinearDiscipline {
22 Linear,
24 Affine,
26 Relevant,
28 Unrestricted,
30}
31
32impl LinearDiscipline {
33 #[must_use]
36 #[inline]
37 pub const fn forbids_drop(self) -> bool {
38 matches!(self, Self::Linear | Self::Relevant)
39 }
40
41 #[must_use]
44 #[inline]
45 pub const fn forbids_reuse(self) -> bool {
46 matches!(self, Self::Linear | Self::Affine)
47 }
48}
49
50#[derive(Debug, Clone, Copy, PartialEq, Eq)]
52pub enum LinearTypeError {
53 Dropped {
55 discipline: LinearDiscipline,
57 },
58 Reused {
60 discipline: LinearDiscipline,
62 uses: u32,
64 },
65}
66
67pub const fn check_linear_use(
79 discipline: LinearDiscipline,
80 uses: u32,
81) -> Result<(), LinearTypeError> {
82 match discipline {
83 LinearDiscipline::Linear => {
84 if uses == 0 {
85 Err(LinearTypeError::Dropped { discipline })
86 } else if uses > 1 {
87 Err(LinearTypeError::Reused { discipline, uses })
88 } else {
89 Ok(())
90 }
91 }
92 LinearDiscipline::Affine => {
93 if uses > 1 {
94 Err(LinearTypeError::Reused { discipline, uses })
95 } else {
96 Ok(())
97 }
98 }
99 LinearDiscipline::Relevant => {
100 if uses == 0 {
101 Err(LinearTypeError::Dropped { discipline })
102 } else {
103 Ok(())
104 }
105 }
106 LinearDiscipline::Unrestricted => Ok(()),
107 }
108}
109
110#[cfg(test)]
111mod tests {
112 use super::*;
113
114 #[test]
115 fn linear_one_use_is_ok() {
116 assert_eq!(check_linear_use(LinearDiscipline::Linear, 1), Ok(()));
117 }
118
119 #[test]
120 fn linear_zero_uses_is_dropped() {
121 let err = check_linear_use(LinearDiscipline::Linear, 0).unwrap_err();
122 assert_eq!(
123 err,
124 LinearTypeError::Dropped {
125 discipline: LinearDiscipline::Linear
126 }
127 );
128 }
129
130 #[test]
131 fn linear_two_uses_is_reused() {
132 let err = check_linear_use(LinearDiscipline::Linear, 2).unwrap_err();
133 assert_eq!(
134 err,
135 LinearTypeError::Reused {
136 discipline: LinearDiscipline::Linear,
137 uses: 2
138 }
139 );
140 }
141
142 #[test]
143 fn affine_zero_or_one_use_is_ok() {
144 assert_eq!(check_linear_use(LinearDiscipline::Affine, 0), Ok(()));
145 assert_eq!(check_linear_use(LinearDiscipline::Affine, 1), Ok(()));
146 }
147
148 #[test]
149 fn affine_multi_use_is_reused() {
150 let err = check_linear_use(LinearDiscipline::Affine, 3).unwrap_err();
151 assert!(matches!(err, LinearTypeError::Reused { uses: 3, .. }));
152 }
153
154 #[test]
155 fn relevant_zero_uses_is_dropped() {
156 let err = check_linear_use(LinearDiscipline::Relevant, 0).unwrap_err();
157 assert!(matches!(err, LinearTypeError::Dropped { .. }));
158 }
159
160 #[test]
161 fn relevant_any_nonzero_use_is_ok() {
162 assert_eq!(check_linear_use(LinearDiscipline::Relevant, 1), Ok(()));
163 assert_eq!(check_linear_use(LinearDiscipline::Relevant, 5), Ok(()));
164 assert_eq!(
165 check_linear_use(LinearDiscipline::Relevant, u32::MAX),
166 Ok(())
167 );
168 }
169
170 #[test]
171 fn unrestricted_accepts_anything() {
172 for uses in [0u32, 1, 2, 100, u32::MAX] {
173 assert_eq!(
174 check_linear_use(LinearDiscipline::Unrestricted, uses),
175 Ok(())
176 );
177 }
178 }
179
180 #[test]
184 fn helpers_agree_with_checker_at_boundaries() {
185 for d in [
186 LinearDiscipline::Linear,
187 LinearDiscipline::Affine,
188 LinearDiscipline::Relevant,
189 LinearDiscipline::Unrestricted,
190 ] {
191 assert_eq!(
193 d.forbids_drop(),
194 check_linear_use(d, 0).is_err(),
195 "forbids_drop disagrees with checker at uses=0 for {:?}",
196 d
197 );
198 assert_eq!(
200 d.forbids_reuse(),
201 check_linear_use(d, 2).is_err(),
202 "forbids_reuse disagrees with checker at uses=2 for {:?}",
203 d
204 );
205 }
206 }
207}