From 375bd9a44229797503e948239eefaae97b536f7b Mon Sep 17 00:00:00 2001 From: "n.timofeenko" Date: Mon, 9 Dec 2024 20:26:45 +0300 Subject: [PATCH 1/2] rust impl --- Cargo.toml | 6 +++ src/main.rs | 150 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 156 insertions(+) create mode 100644 Cargo.toml create mode 100644 src/main.rs diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..a6a4234 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "Functional-error-handling-rs" +version = "0.1.0" +edition = "2021" + +[dependencies] diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..8bce7a0 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,150 @@ +fn parse_int(input: &str) -> ParseIntResult { + let chars: Vec = input.chars().collect(); + + if chars.is_empty() { + return ParseIntResult::Failure { + position: 0, + character: '\0', + }; + } + + let (sign, start_pos) = match chars[0] { + '-' => (-1, 1), + _ => (1, 0), + }; + + if start_pos == 1 && chars.get(1) == Some(&'-') { + return ParseIntResult::Failure { + position: 1, + character: '-', + }; + } + + let parsing_result = + chars[start_pos..] + .iter() + .enumerate() + .try_fold((0_i64, false), |(acc, _), (i, &ch)| { + ch.to_digit(10) + .map(|d| { + acc.checked_mul(10) + .and_then(|n| n.checked_add(d as i64)) + .map(|n| (n, true)) + .unwrap_or((0, false)) + }) + .filter(|&(_, success)| success) + .ok_or((i + start_pos, ch)) + }); + + parsing_result + .and_then(|(num, _)| { + num.checked_mul(sign) + .filter(|&n| n >= i64::from(i32::MIN) && n <= i64::from(i32::MAX)) + .map(|n| n as i32) + .ok_or((0, '0')) + }) + .map(ParseIntResult::Success) + .unwrap_or_else(|(pos, c)| ParseIntResult::Failure { + position: pos, + character: c, + }) +} + +#[derive(Debug, PartialEq)] +enum ParseIntResult { + Success(i32), + Failure { position: usize, character: char }, +} + +fn main() { + let result = parse_int("1"); + println!("{:?}", result); +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn should_parse_zero() { + // Given + let str = "0"; + + // When + let result = parse_int(str); + + // Then + assert_eq!(result, ParseIntResult::Success(0)); + } + + #[test] + fn should_detect_invalid_chars() { + // Given + let str = "1o1"; + + // When + let result = parse_int(str); + + // Then + assert!(matches!(result, ParseIntResult::Failure { .. })); + if let ParseIntResult::Failure { + position: pos, + character: char, + } = result + { + assert_eq!(pos, 1); + assert_eq!(char, 'o'); + } + } + + #[test] + fn should_detect_double_minus() { + // Given + let str = "--1"; + + // When + let result = parse_int(str); + + // Then + assert!(matches!(result, ParseIntResult::Failure { .. })); + if let ParseIntResult::Failure { + position: pos, + character: char, + } = result + { + assert_eq!(pos, 1); + assert_eq!(char, '-'); + } + } + + #[test] + fn should_parse_positive_integer() { + assert_eq!(parse_int("1"), ParseIntResult::Success(1)); + } + + #[test] + fn should_parse_negative_integer() { + assert_eq!(parse_int("-1"), ParseIntResult::Success(-1)); + } + + #[test] + fn should_parse_max_int() { + let str = i32::MAX.to_string(); + assert_eq!(parse_int(&str), ParseIntResult::Success(i32::MAX)); + } + + #[test] + fn should_parse_min_int() { + let str = i32::MIN.to_string(); + assert_eq!(parse_int(&str), ParseIntResult::Success(i32::MIN)); + } + + #[test] + fn should_parse_arbitrary_integer() { + let test_values = [-100, 0, 100]; + for value in test_values { + let str = value.to_string(); + assert_eq!(parse_int(&str), ParseIntResult::Success(value)); + } + } +} From 1fe60358a0efebe7bd3e6fef1eb6153b2510325e Mon Sep 17 00:00:00 2001 From: "n.timofeenko" Date: Mon, 9 Dec 2024 20:30:03 +0300 Subject: [PATCH 2/2] rust impl v2 --- src/main.rs | 67 ++++++++++++++++++----------------------------------- 1 file changed, 23 insertions(+), 44 deletions(-) diff --git a/src/main.rs b/src/main.rs index 8bce7a0..9203742 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,53 +1,32 @@ fn parse_int(input: &str) -> ParseIntResult { - let chars: Vec = input.chars().collect(); - - if chars.is_empty() { - return ParseIntResult::Failure { - position: 0, - character: '\0', - }; - } - - let (sign, start_pos) = match chars[0] { - '-' => (-1, 1), - _ => (1, 0), - }; - - if start_pos == 1 && chars.get(1) == Some(&'-') { + if input.starts_with("--") { return ParseIntResult::Failure { position: 1, character: '-', }; } - - let parsing_result = - chars[start_pos..] - .iter() - .enumerate() - .try_fold((0_i64, false), |(acc, _), (i, &ch)| { - ch.to_digit(10) - .map(|d| { - acc.checked_mul(10) - .and_then(|n| n.checked_add(d as i64)) - .map(|n| (n, true)) - .unwrap_or((0, false)) - }) - .filter(|&(_, success)| success) - .ok_or((i + start_pos, ch)) - }); - - parsing_result - .and_then(|(num, _)| { - num.checked_mul(sign) - .filter(|&n| n >= i64::from(i32::MIN) && n <= i64::from(i32::MAX)) - .map(|n| n as i32) - .ok_or((0, '0')) - }) - .map(ParseIntResult::Success) - .unwrap_or_else(|(pos, c)| ParseIntResult::Failure { - position: pos, - character: c, - }) + match input.parse::() { + Ok(num) => ParseIntResult::Success(num), + Err(_) => { + let pos = input + .chars() + .enumerate() + .find(|(i, c)| { + if *i == 0 && *c == '-' { + false + } else { + !c.is_ascii_digit() + } + }) + .map(|(i, c)| (i, c)) + .unwrap_or((0, '0')); + + ParseIntResult::Failure { + position: pos.0, + character: pos.1, + } + } + } } #[derive(Debug, PartialEq)]