siderite_validation/types/
int.rs1use serde::{Deserialize, Serialize};
4
5use crate::context::ValidationContext;
6use crate::dump::Dump;
7use crate::error::FieldError;
8use crate::rules;
9use crate::schema::{Schema, SchemaObject, SchemaRegistry};
10use crate::validate::Validate;
11use serde_json::Value;
12
13#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
15#[serde(try_from = "i64", into = "i64")]
16pub struct NonNegativeInt(i64);
17
18impl NonNegativeInt {
19 pub fn new(value: i64) -> Result<Self, FieldError> {
21 rules::ge(&value, &0)?;
22 Ok(Self(value))
23 }
24}
25
26crate::types::impl_wrapper!(NonNegativeInt => i64);
27
28impl TryFrom<i64> for NonNegativeInt {
29 type Error = FieldError;
30
31 fn try_from(value: i64) -> Result<Self, Self::Error> {
32 Self::new(value)
33 }
34}
35
36impl Validate for NonNegativeInt {
37 fn prepare(input: &mut Value, ctx: &mut ValidationContext) {
38 let before = ctx.error_count();
39 i64::prepare(input, ctx);
40 if ctx.error_count() == before {
41 ctx.check(rules::ge(&input.as_i64().unwrap_or_default(), &0));
42 }
43 }
44}
45
46impl Schema for NonNegativeInt {
47 fn schema(_: &mut SchemaRegistry) -> SchemaObject {
48 SchemaObject::of_type("integer").with("minimum", 0_i64)
49 }
50}
51
52impl Dump for NonNegativeInt {}
53
54#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
56#[serde(try_from = "i64", into = "i64")]
57pub struct NegativeInt(i64);
58
59impl NegativeInt {
60 pub fn new(value: i64) -> Result<Self, FieldError> {
62 rules::lt(&value, &0)?;
63 Ok(Self(value))
64 }
65}
66
67crate::types::impl_wrapper!(NegativeInt => i64);
68
69impl TryFrom<i64> for NegativeInt {
70 type Error = FieldError;
71
72 fn try_from(value: i64) -> Result<Self, Self::Error> {
73 Self::new(value)
74 }
75}
76
77impl Validate for NegativeInt {
78 fn prepare(input: &mut Value, ctx: &mut ValidationContext) {
79 let before = ctx.error_count();
80 i64::prepare(input, ctx);
81 if ctx.error_count() == before {
82 ctx.check(rules::lt(&input.as_i64().unwrap_or_default(), &0));
83 }
84 }
85}
86
87impl Schema for NegativeInt {
88 fn schema(_: &mut SchemaRegistry) -> SchemaObject {
89 SchemaObject::of_type("integer").with("exclusiveMaximum", 0_i64)
90 }
91}
92
93impl Dump for NegativeInt {}
94
95#[cfg(test)]
96mod tests {
97 #![allow(clippy::unwrap_used)]
98
99 use super::*;
100 use crate::context::ValidationContext;
101 use crate::dump::{Dump, DumpOptions};
102 use crate::schema::schema_for;
103 use crate::types::prepare_codes;
104 use serde_json::json;
105
106 #[test]
107 fn non_negative_int_construction() {
108 assert_eq!(NonNegativeInt::new(0).unwrap().into_inner(), 0);
109 assert_eq!(NonNegativeInt::new(3).unwrap().into_inner(), 3);
110 assert_eq!(
111 NonNegativeInt::new(-1).unwrap_err().code,
112 "greater_than_equal"
113 );
114 }
115
116 #[test]
117 fn negative_int_construction() {
118 assert_eq!(NegativeInt::new(-1).unwrap().into_inner(), -1);
119 assert_eq!(NegativeInt::new(0).unwrap_err().code, "less_than");
120 assert_eq!(NegativeInt::new(2).unwrap_err().code, "less_than");
121 }
122
123 #[test]
124 fn int_serde_round_trip_and_reject() {
125 let value: NonNegativeInt = serde_json::from_str("0").unwrap();
126 assert_eq!(serde_json::to_string(&value).unwrap(), "0");
127 assert!(serde_json::from_str::<NonNegativeInt>("-1").is_err());
128 let value: NegativeInt = serde_json::from_str("-4").unwrap();
129 assert_eq!(serde_json::to_string(&value).unwrap(), "-4");
130 assert!(serde_json::from_str::<NegativeInt>("0").is_err());
131 }
132
133 #[test]
134 fn int_prepare_lax_and_strict() {
135 assert_eq!(
136 prepare_codes::<NonNegativeInt>(json!("7"), ValidationContext::new()),
137 (json!(7), vec![])
138 );
139 assert_eq!(
140 prepare_codes::<NonNegativeInt>(json!("7"), ValidationContext::new().with_strict(true))
141 .1,
142 ["int_type"]
143 );
144 assert_eq!(
145 prepare_codes::<NonNegativeInt>(json!(-1), ValidationContext::new()).1,
146 ["greater_than_equal"]
147 );
148 assert_eq!(
149 prepare_codes::<NegativeInt>(json!("-2"), ValidationContext::new()),
150 (json!(-2), vec![])
151 );
152 assert_eq!(
153 prepare_codes::<NegativeInt>(json!(0), ValidationContext::new()).1,
154 ["less_than"]
155 );
156 }
157
158 #[test]
159 fn int_schema_dump_and_parse_value() {
160 assert_eq!(
161 schema_for::<NonNegativeInt>().0.into_value(),
162 json!({"type": "integer", "minimum": 0})
163 );
164 assert_eq!(
165 schema_for::<NegativeInt>().0.into_value(),
166 json!({"type": "integer", "exclusiveMaximum": 0})
167 );
168 let n = NonNegativeInt::new(4).unwrap();
169 assert_eq!(n.dump(&DumpOptions::new()).unwrap(), json!(4));
170 let parsed =
171 crate::parse_value::<NegativeInt>(json!("-9"), ValidationContext::new()).unwrap();
172 assert_eq!(parsed.into_inner(), -9);
173 let err =
174 crate::parse_value::<NonNegativeInt>(json!(-3), ValidationContext::new()).unwrap_err();
175 assert_eq!(err.errors[0].code, "greater_than_equal");
176 }
177}