diff --git a/README.md b/README.md index f82435f..2c179bf 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,9 @@ # Oddsidizer -Rust library converting betting odds between American, Decimal and Fractional formats producing pretty and traditional fractions for display. +Rust library with tools for converting and formatting data around betting: + +1. odds: american, decimal and fractional, formats producing pretty and traditional fractions for display, +2. race distance (miles, furlongs, yards). ## Features diff --git a/src/distance.rs b/src/distance.rs new file mode 100644 index 0000000..6eb9228 --- /dev/null +++ b/src/distance.rs @@ -0,0 +1,114 @@ +use std::fmt; + +/// Represents a race distance in miles, furlongs, and yards. +#[derive(Debug, PartialEq)] +pub struct RaceDistance { + pub miles: u32, + pub furlongs: u32, + pub yards: u32, +} + +impl RaceDistance { + /// Creates a `RaceDistance` instance from a total distance in yards. + pub fn from_yards(total_yards: u32) -> Self { + const YARDS_IN_MILE: u32 = 1760; + const YARDS_IN_FURLONG: u32 = 220; + + let miles = total_yards / YARDS_IN_MILE; + let yards_after_miles = total_yards % YARDS_IN_MILE; + + let furlongs = yards_after_miles / YARDS_IN_FURLONG; + let yards = yards_after_miles % YARDS_IN_FURLONG; + + Self { + miles, + furlongs, + yards, + } + } +} + +impl fmt::Display for RaceDistance { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if self.miles == 0 && self.furlongs == 0 && self.yards == 0 { + return write!(f, "0y"); + } + + let mut parts = Vec::new(); + + if self.miles > 0 { + parts.push(format!("{}m", self.miles)); + } + if self.furlongs > 0 { + parts.push(format!("{}f", self.furlongs)); + } + if self.yards > 0 { + parts.push(format!("{}y", self.yards)); + } + + write!(f, "{}", parts.join(" ")) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_distance_conversions_with_yards() { + let test_cases = [ + // Sprints + (1100, 0, 5, 0), // 5f + (1320, 0, 6, 0), // 6f + (1540, 0, 7, 0), // 7f + // Middle Distances + (1760, 1, 0, 0), // 1m + (1980, 1, 1, 0), // 1m 1f + (2200, 1, 2, 0), // 1m 2f + (2420, 1, 3, 0), // 1m 3f + // Routes / Stayers + (2640, 1, 4, 0), // 1m 4f + (3080, 1, 6, 0), // 1m 6f + (3520, 2, 0, 0), // 2m + // Edge cases with remaining yards + (0, 0, 0, 0), // Zero yards + (219, 0, 0, 219), // Just under one furlong + (1759, 0, 7, 219), // Just under one mile + (2000, 1, 1, 20), + ]; + + for &(input_yards, expected_miles, expected_furlongs, expected_yards) in &test_cases { + let expected = RaceDistance { + miles: expected_miles, + furlongs: expected_furlongs, + yards: expected_yards, + }; + // Updated to use the new associated function syntax. + let result = RaceDistance::from_yards(input_yards); + + assert_eq!(result, expected, "Failed on input of {} yards", input_yards); + } + } + + #[test] + fn test_display_formatting_with_yards() { + fn rd(miles: u32, furlongs: u32, yards: u32) -> RaceDistance { + RaceDistance { + miles, + furlongs, + yards, + } + } + + // Test various combinations to check the formatting logic. + assert_eq!(rd(1, 6, 0).to_string(), "1m 6f"); + assert_eq!(rd(0, 7, 219).to_string(), "7f 219y"); + assert_eq!(rd(2, 0, 50).to_string(), "2m 50y"); + assert_eq!(rd(0, 6, 0).to_string(), "6f"); + assert_eq!(rd(0, 0, 100).to_string(), "100y"); + assert_eq!(rd(0, 0, 0).to_string(), "0y"); + + let converted_dist = RaceDistance::from_yards(2000); + assert_eq!(format!("{}", converted_dist), "1m 1f 20y"); + } +} diff --git a/src/lib.rs b/src/lib.rs index 90bf319..ac42a47 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,6 +4,9 @@ pub use config::*; mod convert; pub use convert::*; +mod distance; +pub use distance::RaceDistance; + mod lookup_tables; mod odds; diff --git a/src/testing_helpers.rs b/src/testing_helpers.rs index c57e851..09f3af3 100644 --- a/src/testing_helpers.rs +++ b/src/testing_helpers.rs @@ -5,7 +5,6 @@ use crate::ConversionError; // --- Helper for comparing Decimals in tests --- pub fn assert_decimal_eq(a: Decimal, b: Decimal) { - use rust_decimal_macros::dec; let tolerance = dec!(0.001); assert!( (a - b).abs() < tolerance,