sbrd_gen/builder/
bound.rs

1//! Module for value boundary
2
3use rand::distributions::uniform::{SampleRange, SampleUniform, UniformSampler};
4use rand::distributions::{Distribution, Standard};
5use rand::{Rng, RngCore};
6use serde::{Deserialize, Serialize};
7use std::ops::RangeBounds;
8
9/// Value boundary option
10#[derive(Deserialize, Serialize, Debug, PartialEq, Eq, Clone, Copy)]
11pub struct ValueBound<T> {
12    #[serde(skip_serializing_if = "Option::is_none")]
13    start: Option<T>,
14    #[serde(
15        skip_serializing_if = "skip_serialize_include_end",
16        default = "default_include_end"
17    )]
18    include_end: bool,
19    #[serde(skip_serializing_if = "Option::is_none")]
20    end: Option<T>,
21}
22
23fn skip_serialize_include_end(b: &bool) -> bool {
24    b == &default_include_end()
25}
26
27fn default_include_end() -> bool {
28    true
29}
30
31impl<T> Default for ValueBound<T> {
32    fn default() -> Self {
33        ValueBound::<T>::new_full()
34    }
35}
36
37impl<T> ValueBound<T> {
38    /// Create as [`RangeFull`]
39    ///
40    /// [`RangeFull`]: https://doc.rust-lang.org/nightly/core/ops/struct.RangeFull.html
41    pub fn new_full() -> Self {
42        Self::new(None, None)
43    }
44
45    /// Create value boundary from start to end.
46    /// If end is `Some((true, _))`, then include end.
47    pub fn new(start: Option<T>, end: Option<(bool, T)>) -> Self {
48        let (_include_end, _end): (bool, Option<T>) = match end {
49            None => (false, None),
50            Some((_include_end, _end)) => (_include_end, Some(_end)),
51        };
52
53        Self {
54            start,
55            end: _end,
56            include_end: _include_end,
57        }
58    }
59
60    /// Get start value
61    pub fn get_start(&self) -> &Option<T> {
62        &self.start
63    }
64
65    /// Get end value
66    pub fn get_end(&self) -> &Option<T> {
67        &self.end
68    }
69
70    /// Check include end
71    pub fn is_include_end(&self) -> bool {
72        self.include_end
73    }
74
75    /// Convert into other with into-method
76    pub fn convert_into<U>(self) -> ValueBound<U>
77    where
78        T: Into<U>,
79    {
80        self.convert_with(|v| v.into())
81    }
82
83    /// Convert into other with custom-method
84    pub fn convert_with<F, U>(self, mut convert: F) -> ValueBound<U>
85    where
86        F: FnMut(T) -> U,
87    {
88        let Self {
89            start,
90            include_end,
91            end,
92        } = self;
93
94        ValueBound {
95            start: start.map(|s| {
96                #[allow(clippy::redundant_closure)]
97                convert(s)
98            }),
99            include_end,
100            end: end.map(|e| {
101                #[allow(clippy::redundant_closure)]
102                convert(e)
103            }),
104        }
105    }
106
107    /// Try convert into other with custom-method
108    pub fn try_convert_with<F, U, E>(self, mut convert: F) -> Result<ValueBound<U>, E>
109    where
110        F: FnMut(T) -> Result<U, E>,
111    {
112        let Self {
113            start,
114            include_end,
115            end,
116        } = self;
117
118        let _start = match start {
119            None => None,
120            Some(s) => Some(convert(s)?),
121        };
122
123        let _end = match end {
124            None => None,
125            Some(e) => Some(convert(e)?),
126        };
127
128        Ok(ValueBound {
129            start: _start,
130            include_end,
131            end: _end,
132        })
133    }
134
135    /// Replace from no bound with other's bound each start and end
136    pub fn without_no_bound_from_other(self, other: ValueBound<T>) -> ValueBound<T> {
137        let Self {
138            start,
139            include_end,
140            end,
141        } = self;
142        let Self {
143            start: other_start,
144            include_end: other_include_end,
145            end: other_end,
146        } = other;
147
148        ValueBound {
149            start: start.or(other_start),
150            include_end: if end.is_some() {
151                include_end
152            } else {
153                other_include_end
154            },
155            end: end.or(other_end),
156        }
157    }
158}
159
160impl<T: std::cmp::PartialOrd> ValueBound<T> {
161    /// Check contains value
162    pub fn contains(&self, v: &T) -> bool {
163        let Self {
164            start,
165            include_end,
166            end,
167        } = &self;
168
169        match (start, include_end, end) {
170            (None, _, None) => (..).contains(&v),
171            (None, true, Some(e)) => (..=e).contains(&v),
172            (None, false, Some(e)) => (..e).contains(&v),
173            (Some(s), _, None) => (s..).contains(&v),
174            (Some(s), true, Some(e)) => (s..=e).contains(&v),
175            (Some(s), false, Some(e)) => (s..e).contains(&v),
176        }
177    }
178
179    /// Check range is empty.
180    pub fn is_empty(&self) -> bool {
181        let Self {
182            start,
183            include_end,
184            end,
185        } = &self;
186        match (start, include_end, end) {
187            (None, _, None) => false,
188            (None, true, Some(_)) => false,
189            // endのとる値で範囲が空か決まるが、最少を指定することはめったにないのであきらめて最小以外を指定していることにする
190            (None, false, Some(_)) => true,
191            // include start
192            (Some(_), _, None) => false,
193            (Some(s), true, Some(e)) => (s..=e).is_empty(),
194            (Some(s), false, Some(e)) => (s..e).is_empty(),
195        }
196    }
197}
198
199impl<T: std::fmt::Display> std::fmt::Display for ValueBound<T> {
200    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
201        let Self {
202            start,
203            include_end,
204            end,
205        } = &self;
206
207        match (start, end, include_end) {
208            (Some(s), Some(e), true) => write!(f, "{}..={}", s, e),
209            (Some(s), Some(e), false) => write!(f, "{}..{}", s, e),
210            (Some(s), None, _) => write!(f, "{}..", s),
211            (None, Some(e), true) => write!(f, "..={}", e),
212            (None, Some(e), false) => write!(f, "..{}", e),
213            (None, None, _) => write!(f, "..",),
214        }
215    }
216}
217
218impl<T: PartialOrd> From<std::ops::RangeFull> for ValueBound<T> {
219    fn from(_range: std::ops::RangeFull) -> Self {
220        ValueBound::new(None, None)
221    }
222}
223
224impl<T: PartialOrd> From<std::ops::Range<T>> for ValueBound<T> {
225    fn from(range: std::ops::Range<T>) -> Self {
226        let std::ops::Range { start, end } = range;
227        ValueBound::new(Some(start), Some((false, end)))
228    }
229}
230
231impl<T: PartialOrd> From<std::ops::RangeFrom<T>> for ValueBound<T> {
232    fn from(range: std::ops::RangeFrom<T>) -> Self {
233        let std::ops::RangeFrom { start } = range;
234        ValueBound::new(Some(start), None)
235    }
236}
237
238impl<T: PartialOrd> From<std::ops::RangeInclusive<T>> for ValueBound<T> {
239    fn from(range: std::ops::RangeInclusive<T>) -> Self {
240        let (start, end) = range.into_inner();
241        ValueBound::new(Some(start), Some((true, end)))
242    }
243}
244
245impl<T: PartialOrd> From<std::ops::RangeTo<T>> for ValueBound<T> {
246    fn from(range: std::ops::RangeTo<T>) -> Self {
247        let std::ops::RangeTo { end } = range;
248        ValueBound::new(None, Some((false, end)))
249    }
250}
251
252impl<T: PartialOrd> From<std::ops::RangeToInclusive<T>> for ValueBound<T> {
253    fn from(range: std::ops::RangeToInclusive<T>) -> Self {
254        let std::ops::RangeToInclusive { end } = range;
255        ValueBound::new(None, Some((true, end)))
256    }
257}
258
259impl<T: SampleUniform + PartialOrd> SampleRange<T> for ValueBound<T>
260where
261    Standard: Distribution<T>,
262{
263    #[inline]
264    fn sample_single<R: RngCore + ?Sized>(self, rng: &mut R) -> T {
265        if self.start.is_none() || self.end.is_none() {
266            loop {
267                let s = rng.gen::<T>();
268                if self.contains(&s) {
269                    return s;
270                }
271            }
272        }
273
274        match (self.start, self.end) {
275            (Some(s), Some(e)) => {
276                if self.include_end {
277                    T::Sampler::sample_single_inclusive(s, e, rng)
278                } else {
279                    T::Sampler::sample_single(s, e, rng)
280                }
281            }
282            (_, _) => unreachable!(),
283        }
284    }
285
286    #[inline]
287    fn is_empty(&self) -> bool {
288        self.is_empty()
289    }
290}