xsd_types/lexical/decimal/
integer.rs

1use crate::lexical::lexical_form;
2
3use super::{Decimal, DecimalBuf, Overflow, Sign};
4use std::borrow::{Borrow, ToOwned};
5use std::cmp::Ordering;
6use std::fmt;
7use std::hash::{Hash, Hasher};
8use std::str::FromStr;
9
10mod non_negative_integer;
11mod non_positive_integer;
12
13pub use non_negative_integer::*;
14pub use non_positive_integer::*;
15
16lexical_form! {
17	/// Integer number.
18	///
19	/// See: <https://www.w3.org/TR/xmlschema-2/#integer>
20	ty: Integer,
21
22	/// Owned integer number.
23	///
24	/// See: <https://www.w3.org/TR/xmlschema-2/#integer>
25	buffer: IntegerBuf,
26
27	/// Creates a new integer from a string.
28	///
29	/// If the input string is ot a [valid XSD integer](https://www.w3.org/TR/xmlschema-2/#integer),
30	/// an [`InvalidInteger`] error is returned.
31	new,
32
33	/// Creates a new integer from a string without checking it.
34	///
35	/// # Safety
36	///
37	/// The input string must be a [valid XSD integer](https://www.w3.org/TR/xmlschema-2/#integer).
38	new_unchecked,
39
40	value: crate::Integer,
41	error: InvalidInteger,
42	as_ref: as_integer,
43	parent_forms: {
44		as_decimal: Decimal, DecimalBuf
45	}
46}
47
48impl Integer {
49	/// Returns `true` if `self` is positive
50	/// and `false` is the number is zero or negative.
51	pub fn is_positive(&self) -> bool {
52		let mut sign_positive = true;
53		for c in &self.0 {
54			match c {
55				b'+' | b'0' => (),
56				b'-' => sign_positive = false,
57				_ => return sign_positive,
58			}
59		}
60
61		false
62	}
63
64	/// Returns `true` if `self` is negative
65	/// and `false` is the number is zero or positive.
66	pub fn is_negative(&self) -> bool {
67		let mut sign_negative = true;
68		for c in &self.0 {
69			match c {
70				b'-' | b'0' => (),
71				b'+' => sign_negative = false,
72				_ => return sign_negative,
73			}
74		}
75
76		false
77	}
78
79	/// Returns `true` if `self` is zero
80	/// and `false` otherwise.
81	pub fn is_zero(&self) -> bool {
82		for c in &self.0 {
83			if !matches!(c, b'+' | b'-' | b'0') {
84				return false;
85			}
86		}
87
88		true
89	}
90
91	/// Returns `true` if `self` is positive or zero
92	/// and `false` is negative.
93	pub fn is_non_negative(&self) -> bool {
94		self.0[0] != b'-'
95	}
96
97	pub fn sign(&self) -> Sign {
98		let mut sign_positive = true;
99		for c in &self.0 {
100			match c {
101				b'+' | b'0' => (),
102				b'-' => sign_positive = false,
103				_ => {
104					if sign_positive {
105						return Sign::Positive;
106					} else {
107						return Sign::Negative;
108					}
109				}
110			}
111		}
112
113		Sign::Zero
114	}
115
116	/// Returns the absolute value of `self`.
117	///
118	/// The returned integer is in canonical form (without leading zeros).
119	pub fn abs(&self) -> &NonNegativeInteger {
120		let mut last_zero = 0;
121		for (i, c) in self.0.iter().enumerate() {
122			match c {
123				b'+' | b'-' => (),
124				b'0' => last_zero = i,
125				_ => return unsafe { NonNegativeInteger::new_unchecked(&self.0[i..]) },
126			}
127		}
128
129		unsafe { NonNegativeInteger::new_unchecked(&self.0[last_zero..]) }
130	}
131
132	/// Returns the canonical form of `self` (without leading zeros).
133	pub fn canonical(&self) -> &Self {
134		if self.is_zero() {
135			unsafe { Self::new_unchecked(&self.0[self.0.len() - 1..]) }
136		} else {
137			let mut last_zero = 0;
138			for (i, c) in self.0.iter().enumerate() {
139				match c {
140					b'+' => (),
141					b'0' => last_zero = i,
142					_ => return unsafe { Self::new_unchecked(&self.0[i..]) },
143				}
144			}
145
146			unsafe { Self::new_unchecked(&self.0[last_zero..]) }
147		}
148	}
149
150	#[inline(always)]
151	fn as_canonical_str(&self) -> &str {
152		self.canonical().as_str()
153	}
154
155	#[inline(always)]
156	pub fn value(&self) -> crate::Integer {
157		crate::Integer::from_str(self.as_str()).unwrap()
158	}
159}
160
161impl PartialEq for Integer {
162	fn eq(&self, other: &Self) -> bool {
163		self.as_canonical_str() == other.as_canonical_str()
164	}
165}
166
167impl Eq for Integer {}
168
169impl Hash for Integer {
170	fn hash<H: Hasher>(&self, h: &mut H) {
171		self.as_canonical_str().hash(h)
172	}
173}
174
175impl Ord for Integer {
176	fn cmp(&self, other: &Self) -> Ordering {
177		let sign = self.sign();
178		let other_sign = other.sign();
179		match sign.cmp(&other_sign) {
180			Ordering::Equal => {
181				let a = self.abs().as_bytes();
182				let b = other.abs().as_bytes();
183
184				match a.len().cmp(&b.len()) {
185					Ordering::Equal => {
186						if sign.is_negative() {
187							a.cmp(b).reverse()
188						} else {
189							a.cmp(b)
190						}
191					}
192					other => {
193						if sign.is_negative() {
194							other.reverse()
195						} else {
196							other
197						}
198					}
199				}
200			}
201			other => other,
202		}
203	}
204}
205
206impl PartialOrd for Integer {
207	fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
208		Some(self.cmp(other))
209	}
210}
211
212impl IntegerBuf {
213	pub fn zero() -> Self {
214		unsafe { Self::new_unchecked("0".to_string()) }
215	}
216
217	pub fn one() -> Self {
218		unsafe { Self::new_unchecked("1".to_string()) }
219	}
220}
221
222impl Default for IntegerBuf {
223	fn default() -> Self {
224		Self::zero()
225	}
226}
227
228impl PartialOrd for IntegerBuf {
229	fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
230		Some(self.cmp(other))
231	}
232}
233
234impl Ord for IntegerBuf {
235	fn cmp(&self, other: &Self) -> Ordering {
236		self.as_integer().cmp(other.as_integer())
237	}
238}
239
240macro_rules! number_conversion {
241	{ $($ty:ty),* } => {
242		$(
243			impl From<$ty> for IntegerBuf {
244				fn from(i: $ty) -> Self {
245					unsafe { IntegerBuf::new_unchecked(i.to_string()) }
246				}
247			}
248
249			impl<'a> TryFrom<&'a Integer> for $ty {
250				type Error = Overflow;
251
252				fn try_from(i: &'a Integer) -> Result<Self, Overflow> {
253					i.as_str().parse().map_err(|_| Overflow)
254				}
255			}
256
257			impl TryFrom<IntegerBuf> for $ty {
258				type Error = Overflow;
259
260				fn try_from(i: IntegerBuf) -> Result<Self, Overflow> {
261					i.as_str().parse().map_err(|_| Overflow)
262				}
263			}
264		)*
265	};
266}
267
268number_conversion! {
269	u8,
270	i8,
271	u16,
272	i16,
273	u32,
274	i32,
275	u64,
276	i64,
277	u128,
278	i128,
279	usize,
280	isize
281}
282
283fn check_bytes(s: &[u8]) -> bool {
284	check(s.iter().copied())
285}
286
287fn check<C: Iterator<Item = u8>>(mut chars: C) -> bool {
288	enum State {
289		Initial,
290		NonEmptyInteger,
291		Integer,
292	}
293
294	let mut state = State::Initial;
295
296	loop {
297		state = match state {
298			State::Initial => match chars.next() {
299				Some(b'+') => State::NonEmptyInteger,
300				Some(b'-') => State::NonEmptyInteger,
301				Some(b'0'..=b'9') => State::Integer,
302				_ => break false,
303			},
304			State::NonEmptyInteger => match chars.next() {
305				Some(b'0'..=b'9') => State::Integer,
306				_ => break false,
307			},
308			State::Integer => match chars.next() {
309				Some(b'0'..=b'9') => State::Integer,
310				Some(_) => break false,
311				None => break true,
312			},
313		}
314	}
315}
316
317#[cfg(test)]
318mod tests {
319	use super::*;
320
321	#[test]
322	fn parse_01() {
323		Integer::new("0").unwrap();
324	}
325
326	#[test]
327	#[should_panic]
328	fn parse_02() {
329		Integer::new("+").unwrap();
330	}
331
332	#[test]
333	#[should_panic]
334	fn parse_03() {
335		Integer::new("-").unwrap();
336	}
337
338	#[test]
339	#[should_panic]
340	fn parse_04() {
341		Integer::new("012+").unwrap();
342	}
343
344	#[test]
345	fn parse_05() {
346		Integer::new("+42").unwrap();
347	}
348
349	#[test]
350	fn parse_06() {
351		Integer::new("-42").unwrap();
352	}
353
354	#[test]
355	fn abs_01() {
356		assert_eq!(Integer::new("01").unwrap().abs().as_str(), "1")
357	}
358
359	#[test]
360	fn abs_02() {
361		assert_eq!(Integer::new("00").unwrap().abs().as_str(), "0")
362	}
363
364	#[test]
365	fn abs_03() {
366		assert_eq!(Integer::new("+00000").unwrap().abs().as_str(), "0")
367	}
368
369	#[test]
370	fn abs_04() {
371		assert_eq!(Integer::new("-00000").unwrap().abs().as_str(), "0")
372	}
373
374	#[test]
375	fn abs_05() {
376		assert_eq!(Integer::new("-00100").unwrap().abs().as_str(), "100")
377	}
378
379	#[test]
380	fn eq_01() {
381		assert_eq!(Integer::new("+001").unwrap(), Integer::new("1").unwrap())
382	}
383
384	#[test]
385	fn eq_02() {
386		assert_ne!(Integer::new("-001").unwrap(), Integer::new("1").unwrap())
387	}
388
389	#[test]
390	fn eq_03() {
391		assert_eq!(Integer::new("-000").unwrap(), Integer::new("+0").unwrap())
392	}
393
394	#[test]
395	fn cmp_01() {
396		assert!(Integer::new("123").unwrap() < Integer::new("456").unwrap())
397	}
398
399	#[test]
400	fn cmp_02() {
401		assert!(Integer::new("1230").unwrap() > Integer::new("456").unwrap())
402	}
403
404	#[test]
405	fn cmp_03() {
406		assert!(Integer::new("-1230").unwrap() < Integer::new("456").unwrap())
407	}
408
409	#[test]
410	fn cmp_04() {
411		assert!(Integer::new("-1230").unwrap() < Integer::new("-456").unwrap())
412	}
413
414	#[test]
415	fn cmp_05() {
416		assert!(Integer::new("-123").unwrap() > Integer::new("-456").unwrap())
417	}
418
419	#[test]
420	fn cmp_06() {
421		assert_eq!(
422			Integer::new("+123456")
423				.unwrap()
424				.cmp(Integer::new("0000123456").unwrap()),
425			Ordering::Equal
426		)
427	}
428}