Skip to main content

graphrecords_query/capabilities/numeric/
clip.rs

1use crate::{
2    AttributeName, Failure, IndexValue, Positional, QueryResult, Scalar, ValueDomain,
3    error::{
4        comparison::IncomparableValues,
5        numeric::{InvalidClipBounds, NonNumericValue},
6    },
7};
8use graphrecords_core::graphrecord::{
9    EdgeIndex, GraphRecordAttribute, GraphRecordValue, NodeIndex,
10};
11use std::{
12    cmp::Ordering,
13    fmt::{Debug, Display},
14};
15
16pub trait ValueClip: ValueDomain {
17    fn clip<'a>(
18        label: &'static str,
19        value: Self::Value<'a>,
20        lower: Self::Value<'a>,
21        upper: Self::Value<'a>,
22    ) -> QueryResult<Self::Value<'a>>;
23}
24
25fn clip_ordered<T>(label: &'static str, value: T, lower: T, upper: T) -> QueryResult<T>
26where
27    T: Debug + Display + PartialOrd + Send + Sync + 'static,
28{
29    match lower.partial_cmp(&upper) {
30        Some(Ordering::Greater) => {
31            return Err(Failure::new(label, InvalidClipBounds::new(lower, upper)));
32        }
33        None => {
34            return Err(Failure::new(label, IncomparableValues::new(lower, upper)));
35        }
36        Some(Ordering::Less | Ordering::Equal) => {}
37    }
38
39    match value.partial_cmp(&lower) {
40        Some(Ordering::Less) => return Ok(lower),
41        None => {
42            return Err(Failure::new(label, IncomparableValues::new(value, lower)));
43        }
44        Some(Ordering::Equal | Ordering::Greater) => {}
45    }
46
47    match value.partial_cmp(&upper) {
48        Some(Ordering::Greater) => Ok(upper),
49        Some(Ordering::Less | Ordering::Equal) => Ok(value),
50        None => Err(Failure::new(label, IncomparableValues::new(value, upper))),
51    }
52}
53
54fn clip_graphrecord_attribute(
55    label: &'static str,
56    value: GraphRecordAttribute,
57    lower: GraphRecordAttribute,
58    upper: GraphRecordAttribute,
59) -> QueryResult<GraphRecordAttribute> {
60    let value = match value {
61        GraphRecordAttribute::Int(_) => value,
62        value @ GraphRecordAttribute::String(_) => {
63            return Err(Failure::new(label, NonNumericValue::new(value)));
64        }
65    };
66    let lower = match lower {
67        GraphRecordAttribute::Int(_) => lower,
68        lower @ GraphRecordAttribute::String(_) => {
69            return Err(Failure::new(label, NonNumericValue::new(lower)));
70        }
71    };
72    let upper = match upper {
73        GraphRecordAttribute::Int(_) => upper,
74        upper @ GraphRecordAttribute::String(_) => {
75            return Err(Failure::new(label, NonNumericValue::new(upper)));
76        }
77    };
78
79    clip_ordered(label, value, lower, upper)
80}
81
82fn clip_graphrecord_value(
83    label: &'static str,
84    value: GraphRecordValue,
85    lower: GraphRecordValue,
86    upper: GraphRecordValue,
87) -> QueryResult<GraphRecordValue> {
88    let value = match value {
89        GraphRecordValue::Int(_)
90        | GraphRecordValue::Float(_)
91        | GraphRecordValue::DateTime(_)
92        | GraphRecordValue::Duration(_) => value,
93        value => return Err(Failure::new(label, NonNumericValue::new(value))),
94    };
95    let lower = match lower {
96        GraphRecordValue::Int(_)
97        | GraphRecordValue::Float(_)
98        | GraphRecordValue::DateTime(_)
99        | GraphRecordValue::Duration(_) => lower,
100        lower => return Err(Failure::new(label, NonNumericValue::new(lower))),
101    };
102    let upper = match upper {
103        GraphRecordValue::Int(_)
104        | GraphRecordValue::Float(_)
105        | GraphRecordValue::DateTime(_)
106        | GraphRecordValue::Duration(_) => upper,
107        upper => return Err(Failure::new(label, NonNumericValue::new(upper))),
108    };
109
110    clip_ordered(label, value, lower, upper)
111}
112
113impl ValueClip for Scalar {
114    fn clip<'a>(
115        label: &'static str,
116        value: Self::Value<'a>,
117        lower: Self::Value<'a>,
118        upper: Self::Value<'a>,
119    ) -> QueryResult<Self::Value<'a>> {
120        clip_graphrecord_value(label, value, lower, upper)
121    }
122}
123
124impl ValueClip for AttributeName {
125    fn clip<'a>(
126        label: &'static str,
127        value: Self::Value<'a>,
128        lower: Self::Value<'a>,
129        upper: Self::Value<'a>,
130    ) -> QueryResult<Self::Value<'a>> {
131        clip_graphrecord_attribute(label, value, lower, upper)
132    }
133}
134
135impl ValueClip for IndexValue<Positional> {
136    fn clip<'a>(
137        label: &'static str,
138        value: Self::Value<'a>,
139        lower: Self::Value<'a>,
140        upper: Self::Value<'a>,
141    ) -> QueryResult<Self::Value<'a>> {
142        clip_ordered(label, value, lower, upper)
143    }
144}
145
146impl ValueClip for IndexValue<NodeIndex> {
147    fn clip<'a>(
148        label: &'static str,
149        value: Self::Value<'a>,
150        lower: Self::Value<'a>,
151        upper: Self::Value<'a>,
152    ) -> QueryResult<Self::Value<'a>> {
153        clip_graphrecord_attribute(label, value, lower, upper)
154    }
155}
156
157impl ValueClip for IndexValue<AttributeName> {
158    fn clip<'a>(
159        label: &'static str,
160        value: Self::Value<'a>,
161        lower: Self::Value<'a>,
162        upper: Self::Value<'a>,
163    ) -> QueryResult<Self::Value<'a>> {
164        clip_graphrecord_attribute(label, value, lower, upper)
165    }
166}
167
168impl ValueClip for IndexValue<EdgeIndex> {
169    fn clip<'a>(
170        label: &'static str,
171        value: Self::Value<'a>,
172        lower: Self::Value<'a>,
173        upper: Self::Value<'a>,
174    ) -> QueryResult<Self::Value<'a>> {
175        clip_ordered(label, value, lower, upper)
176    }
177}
178
179impl ValueClip for IndexValue<GraphRecordValue> {
180    fn clip<'a>(
181        label: &'static str,
182        value: Self::Value<'a>,
183        lower: Self::Value<'a>,
184        upper: Self::Value<'a>,
185    ) -> QueryResult<Self::Value<'a>> {
186        clip_graphrecord_value(label, value, lower, upper)
187    }
188}