Files
commonlibrary_rust_ylong_json/src/value/number.rs
T
薛磊 714640966b ylong_json code init
Signed-off-by: 薛磊 <xuelei3@huawei.com>
2023-06-19 17:44:09 +08:00

349 lines
9.2 KiB
Rust

/*
* Copyright (c) 2023 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
use crate::Error;
use core::fmt::{Display, Formatter};
use std::fmt::Debug;
/// Numerical type
///
/// # Examples
/// ```
/// use ylong_json::Number;
///
/// let number: Number = 0.0.into();
/// assert_eq!(number.is_float(), true);
/// ```
#[derive(Clone)]
pub enum Number {
/// Unsigned integer
Unsigned(u64),
/// Signed integer
Signed(i64),
/// Floating point number
Float(f64),
}
impl Number {
/// Determines whether the number is an unsigned integer.
///
/// # Examples
/// ```
/// use ylong_json::Number;
///
/// let number: Number = 1u8.into();
/// assert_eq!(number.is_unsigned(), true);
///
/// let number: Number = 1i8.into();
/// assert_eq!(number.is_unsigned(), false);
/// ```
pub fn is_unsigned(&self) -> bool {
matches!(*self, Self::Unsigned(_))
}
/// Determines whether the number is a signed integer.
///
/// # Examples
/// ```
/// use ylong_json::Number;
///
/// let number: Number = 1i8.into();
/// assert_eq!(number.is_signed(), true);
///
/// let number: Number = 1u8.into();
/// assert_eq!(number.is_signed(), false);
/// ```
pub fn is_signed(&self) -> bool {
matches!(*self, Self::Signed(_))
}
/// Determines whether the number is a floating point number.
///
/// # Examples
/// ```
/// use ylong_json::Number;
///
/// let number: Number = 0.0.into();
/// assert_eq!(number.is_float(), true);
///
/// let number: Number = 1i8.into();
/// assert_eq!(number.is_float(), false);
/// ```
pub fn is_float(&self) -> bool {
matches!(*self, Self::Float(_))
}
/// Trys converting the number to u64. If conversion fails, returns Error.
///
/// Only Unsigned case means success, other cases return Error.
///
/// # Examples
/// ```
/// use ylong_json::Number;
///
/// let number: Number = 1u8.into();
/// assert_eq!(number.try_as_u64().unwrap(), 1u64);
///
/// let number: Number = 1i8.into();
/// assert_eq!(number.try_as_u64().is_err(), true);
/// ```
pub fn try_as_u64(&self) -> Result<u64, Error> {
match self {
Self::Unsigned(u) => Ok(*u),
_ => Err(Error::TypeTransform),
}
}
/// Trys converting the number to i64. If conversion fails, returns Error.
///
/// Only in 164 range unsigned numbers can be converted. Signed numbers can be converted.
/// Otherwise, returns Error.
///
/// # Examples
/// ```
/// use ylong_json::Number;
///
/// let number: Number = 1i8.into();
/// assert_eq!(number.try_as_i64().unwrap(), 1i64);
///
/// let number: Number = u64::MAX.into();
/// assert_eq!(number.try_as_i64().is_err(), true);
/// ```
pub fn try_as_i64(&self) -> Result<i64, Error> {
match self {
Self::Unsigned(u) => {
if *u <= i64::MAX as u64 {
Ok(*u as i64)
} else {
Err(Error::TypeTransform)
}
}
Self::Signed(i) => Ok(*i),
Self::Float(_) => Err(Error::TypeTransform),
}
}
/// Trys converting the number to f64. If conversion fails, returns Error.
///
/// All types can be converted to f64.
///
/// # Examples
/// ```
/// use ylong_json::Number;
///
/// let number: Number = 0.0.into();
/// assert_eq!(number.try_as_f64().unwrap(), 0.0f64);
/// ```
pub fn try_as_f64(&self) -> Result<f64, Error> {
match self {
Self::Unsigned(u) => Ok(*u as f64),
Self::Signed(i) => Ok(*i as f64),
Self::Float(f) => Ok(*f),
}
}
}
impl PartialEq for Number {
fn eq(&self, other: &Self) -> bool {
let a = self.try_as_f64().unwrap();
let b = other.try_as_f64().unwrap();
a == b
}
}
impl Display for Number {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
match self {
Number::Unsigned(x) => write!(f, "{x}"),
Number::Signed(x) => write!(f, "{x}"),
Number::Float(x) => write!(f, "{x:?}"),
}
}
}
impl Debug for Number {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
Display::fmt(self, f)
}
}
macro_rules! number_from_unsigned {
($($u: tt),* $(,)?) => {
$(
impl From<$u> for Number {
fn from(u: $u) -> Self {
Self::Unsigned(u as u64)
}
}
)*
}
}
macro_rules! number_from_signed {
($($i: tt),* $(,)?) => {
$(
impl From<$i> for Number {
fn from(i: $i) -> Self {
Self::Signed(i as i64)
}
}
)*
}
}
macro_rules! number_from_float {
($($f: tt),* $(,)?) => {
$(
impl From<$f> for Number {
fn from(f: $f) -> Self {
Self::Float(f as f64)
}
}
)*
}
}
number_from_unsigned!(u8, u16, u32, u64, usize);
number_from_signed!(i8, i16, i32, i64, isize);
number_from_float!(f32, f64);
#[cfg(test)]
mod ut_number {
use crate::Number;
/// UT test for `Number::fmt`.
///
/// # Title
/// ut_number_fmt
///
/// # Brief
/// 1. Creates some `Number`s.
/// 2. Calls `Number::fmt`.
/// 3. Checks if the test results are correct.
#[test]
fn ut_number_fmt() {
assert_eq!(format!("{}", Number::Unsigned(1)), "1");
assert_eq!(format!("{:?}", Number::Unsigned(1)), "1");
assert_eq!(format!("{}", Number::Signed(1)), "1");
assert_eq!(format!("{:?}", Number::Signed(1)), "1");
assert_eq!(format!("{}", Number::Float(1.0)), "1.0");
assert_eq!(format!("{:?}", Number::Float(1.0)), "1.0");
}
/// UT test for `Number::clone`.
///
/// # Title
/// ut_number_clone
///
/// # Brief
/// 1. Creates some `Number`s.
/// 2. Calls `Number::clone`.
/// 3. Checks if the test results are correct.
#[test]
fn ut_number_clone() {
let number1 = Number::Unsigned(1);
assert_eq!(number1, number1.clone());
let number1 = Number::Signed(1);
assert_eq!(number1, number1.clone());
let number1 = Number::Float(1.0);
assert_eq!(number1, number1.clone());
}
/// UT test for `Number::is_unsigned`.
///
/// # Title
/// ut_number_is_unsigned
///
/// # Brief
/// 1. Creates some `Number`s.
/// 2. Calls `Number::is_unsigned`.
/// 3. Checks if the test results are correct.
#[test]
fn ut_number_is_unsigned() {
assert!(Number::Unsigned(1).is_unsigned());
assert!(!Number::Signed(1).is_unsigned());
assert!(!Number::Float(1.0).is_unsigned());
}
/// UT test for `Number::is_signed`.
///
/// # Title
/// ut_number_is_signed
///
/// # Brief
/// 1. Creates some `Number`s.
/// 2. Calls `Number::is_signed`.
/// 3. Checks if the test results are correct.
#[test]
fn ut_number_is_signed() {
assert!(!Number::Unsigned(1).is_signed());
assert!(Number::Signed(1).is_signed());
assert!(!Number::Float(1.0).is_signed());
}
/// UT test for `Number::is_float`.
///
/// # Title
/// ut_number_is_float
///
/// # Brief
/// 1. Creates some `Number`s.
/// 2. Calls `Number::is_float`.
/// 3. Checks if the test results are correct.
#[test]
fn ut_number_is_float() {
assert!(!Number::Unsigned(1).is_float());
assert!(!Number::Signed(1).is_float());
assert!(Number::Float(1.0).is_float());
}
/// UT test for `Number::try_as_u64`.
///
/// # Title
/// ut_number_try_as_u64
///
/// # Brief
/// 1. Creates some `Number`s.
/// 2. Calls `Number::try_as_u64`.
/// 3. Checks if the test results are correct.
#[test]
fn ut_number_try_as_u64() {
assert!(Number::Unsigned(1).try_as_u64().is_ok());
assert!(Number::Signed(1).try_as_u64().is_err());
assert!(Number::Float(1.0).try_as_u64().is_err());
}
/// UT test for `Number::try_as_i64`.
///
/// # Title
/// ut_number_try_as_i64
///
/// # Brief
/// 1. Creates some `Number`s.
/// 2. Calls `Number::try_as_i64`.
/// 3. Checks if the test results are correct.
#[test]
fn ut_number_try_as_i64() {
assert!(Number::Unsigned(1).try_as_i64().is_ok());
assert!(Number::Unsigned(u64::MAX).try_as_i64().is_err());
assert!(Number::Signed(1).try_as_i64().is_ok());
assert!(Number::Float(1.0).try_as_i64().is_err());
}
}