diff --git a/math/Cargo.toml b/math/Cargo.toml index 375b7f2..2e4fa12 100644 --- a/math/Cargo.toml +++ b/math/Cargo.toml @@ -15,5 +15,7 @@ path = "src/main.rs" fluence = "0.6.9" ethnum = "1.0.3" +thiserror = "1.0.25" + [dev-dependencies] fluence-test = "0.1.9" diff --git a/math/src/errors.rs b/math/src/errors.rs new file mode 100644 index 0000000..f408478 --- /dev/null +++ b/math/src/errors.rs @@ -0,0 +1,28 @@ +/* + * Copyright 2021 Fluence Labs Limited + * + * 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 thiserror::Error as ThisError; + +use std::num::ParseIntError; + +#[derive(ThisError, Debug)] +pub enum U256Error { + #[error("an overflow is occured")] + U256Overflow, + + #[error("{0}")] + ParseError(#[from] ParseIntError), +} diff --git a/math/src/main.rs b/math/src/main.rs index 42aea6d..f093b9d 100644 --- a/math/src/main.rs +++ b/math/src/main.rs @@ -16,7 +16,24 @@ * limitations under the License. */ -use ethnum::*; +#![warn(rust_2018_idioms)] +#![deny( + dead_code, + nonstandard_style, + unused_imports, + unused_mut, + unused_variables, + unused_unsafe, + unreachable_patterns +)] + +mod errors; +mod result; + +use errors::U256Error; +use result::U256Result; + +use ethnum::u256; use fluence::marine; use fluence::module_manifest; @@ -24,104 +41,69 @@ module_manifest!(); pub fn main() {} -/// Result like -#[marine] -pub struct MathResult { - /// u256 string representation - pub u256: String, - pub ret_code: u8, - /// error string representation - pub err_msg: String, -} - -mod math_result { - use ethnum::u256; - - use crate::MathResult; - - pub fn ok(ok: u256) -> MathResult { - MathResult { - u256: ok.to_string(), - ret_code: 0, - err_msg: Default::default(), - } - } - - pub fn err(err: &str) -> MathResult { - MathResult { - u256: Default::default(), - ret_code: 1, - err_msg: err.to_string(), - } - } -} - -use math_result::{err, ok}; +use std::convert::identity; +use U256Error::U256Overflow; /// adds 2 256 bits integers (ETH compatible) /// return number or error (failed to parse input or overflow of output) #[marine] -pub fn add_u256(number_1: String, number_2: String) -> MathResult { - let number_1 = number_1.parse::(); - let number_2 = number_2.parse::(); - if let (Ok(number_1), Ok(number_2)) = (number_1, number_2) { - let number = number_1.checked_add(number_2); - if let Some(number) = number { - return ok(number); - } +pub fn add_u256(lhs: String, rhs: String) -> U256Result { + add_u256_impl(lhs, rhs) + .map_err(Into::into) + .unwrap_or_else(identity) +} - return err("Overflow"); - } +pub fn add_u256_impl(lhs: String, rhs: String) -> Result { + let lhs = lhs.parse::()?; + let rhs = rhs.parse::()?; + let result = lhs.checked_add(rhs).ok_or_else(|| U256Overflow)?; - err("InputNonAU256Number") + Ok(U256Result::from_u256(result)) } #[marine] -pub fn sub_u256(number_1: String, number_2: String) -> MathResult { - let number_1 = number_1.parse::(); - let number_2 = number_2.parse::(); - if let (Ok(number_1), Ok(number_2)) = (number_1, number_2) { - let number = number_1.checked_sub(number_2); - if let Some(number) = number { - return ok(number); - } +pub fn sub_u256(lhs: String, rhs: String) -> U256Result { + sub_u256_impl(lhs, rhs) + .map_err(Into::into) + .unwrap_or_else(identity) +} - return err("Underflow"); - } +pub fn sub_u256_impl(lhs: String, rhs: String) -> Result { + let lhs = lhs.parse::()?; + let rhs = rhs.parse::()?; + let result = lhs.checked_sub(rhs).ok_or_else(|| U256Overflow)?; - err("InputNonAU256Number") + Ok(U256Result::from_u256(result)) } #[marine] -pub fn mul_u256(number_1: String, number_2: String) -> MathResult { - let number_1 = number_1.parse::(); - let number_2 = number_2.parse::(); - if let (Ok(number_1), Ok(number_2)) = (number_1, number_2) { - let number = number_1.checked_mul(number_2); - if let Some(number) = number { - return ok(number); - } +pub fn mul_u256(lhs: String, rhs: String) -> U256Result { + mul_u256_impl(lhs, rhs) + .map_err(Into::into) + .unwrap_or_else(identity) +} - return err("Overflow"); - } +pub fn mul_u256_impl(lhs: String, rhs: String) -> Result { + let lhs = lhs.parse::()?; + let rhs = rhs.parse::()?; + let result = lhs.checked_mul(rhs).ok_or_else(|| U256Overflow)?; - err("InputNonAU256Number") + Ok(U256Result::from_u256(result)) } #[marine] -pub fn div_u256(number_1: String, number_2: String) -> MathResult { - let number_1 = number_1.parse::(); - let number_2 = number_2.parse::(); - if let (Ok(number_1), Ok(number_2)) = (number_1, number_2) { - let number = number_1.checked_div(number_2); - if let Some(number) = number { - return ok(number); - } +pub fn div_u256(lhs: String, rhs: String) -> U256Result { + div_u256_impl(lhs, rhs) + .map_err(Into::into) + .unwrap_or_else(identity) +} - return err("DivisionByZero"); - } +pub fn div_u256_impl(lhs: String, rhs: String) -> Result { + let lhs = lhs.parse::()?; + let rhs = rhs.parse::()?; + let result = lhs.checked_div(rhs).ok_or_else(|| U256Overflow)?; - err("InputNonAU256Number") + Ok(U256Result::from_u256(result)) } #[cfg(test)] diff --git a/math/src/result.rs b/math/src/result.rs new file mode 100644 index 0000000..6c6a8ec --- /dev/null +++ b/math/src/result.rs @@ -0,0 +1,55 @@ +/* + * Copyright 2021 Fluence Labs Limited + * + * 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::errors::U256Error; + +use ethnum::u256; +use fluence::marine; + +#[marine] +#[derive(Default, Debug)] +pub struct U256Result { + /// u256 string representation + pub value: String, + pub ret_code: u8, + /// contains error as a string + pub err_msg: String, +} + +impl U256Result { + pub(crate) fn from_u256(value: u256) -> Self { + Self { + value: value.to_string(), + ret_code: 0, // 0 means success + err_msg: <_>::default(), + } + } +} + +impl From for U256Result { + fn from(e: U256Error) -> Self { + let ret_code = match e { + U256Error::ParseError(_) => 1, + U256Error::U256Overflow => 2, + }; + + Self { + ret_code, + err_msg: e.to_string(), + ..<_>::default() + } + } +}