From ad72b71d84645ddd2dc43cabf19f65cc7bc87546 Mon Sep 17 00:00:00 2001 From: Ali Alimohammadi Date: Tue, 27 Jan 2026 16:55:54 -0800 Subject: [PATCH 1/3] feat: add comprehensive pressure unit conversion --- DIRECTORY.md | 1 + src/conversions/mod.rs | 2 + src/conversions/pressure.rs | 390 ++++++++++++++++++++++++++++++++++++ 3 files changed, 393 insertions(+) create mode 100644 src/conversions/pressure.rs diff --git a/DIRECTORY.md b/DIRECTORY.md index c123cf19d26..8ce0e155d16 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -84,6 +84,7 @@ * [Octal to Decimal](https://github.com/TheAlgorithms/Rust/blob/master/src/conversions/octal_to_decimal.rs) * [Octal to Hexadecimal](https://github.com/TheAlgorithms/Rust/blob/master/src/conversions/octal_to_hexadecimal.rs) * [Order of Magnitude Conversion](https://github.com/TheAlgorithms/Rust/blob/master/src/conversions/order_of_magnitude_conversion.rs) + * [Pressure](https://github.com/TheAlgorithms/Rust/blob/master/src/conversions/pressure.rs) * [Rectangular to Polar](https://github.com/TheAlgorithms/Rust/blob/master/src/conversions/rectangular_to_polar.rs) * [RGB-CMYK Conversion](https://github.com/TheAlgorithms/Rust/blob/master/src/conversions/rgb_cmyk_conversion.rs) * [RGB-HSV Conversion](https://github.com/TheAlgorithms/Rust/blob/master/src/conversions/rgb_hsv_conversion.rs) diff --git a/src/conversions/mod.rs b/src/conversions/mod.rs index f8d7706e46b..267f4084204 100644 --- a/src/conversions/mod.rs +++ b/src/conversions/mod.rs @@ -13,6 +13,7 @@ mod octal_to_binary; mod octal_to_decimal; mod octal_to_hexadecimal; mod order_of_magnitude_conversion; +mod pressure; mod rectangular_to_polar; mod rgb_cmyk_conversion; mod rgb_hsv_conversion; @@ -38,6 +39,7 @@ pub use self::octal_to_hexadecimal::octal_to_hexadecimal; pub use self::order_of_magnitude_conversion::{ convert_metric_length, metric_length_conversion, MetricLengthUnit, }; +pub use self::pressure::{convert_pressure, PressureUnit}; pub use self::rectangular_to_polar::rectangular_to_polar; pub use self::rgb_cmyk_conversion::rgb_to_cmyk; pub use self::rgb_hsv_conversion::{hsv_to_rgb, rgb_to_hsv, ColorError, Hsv, Rgb}; diff --git a/src/conversions/pressure.rs b/src/conversions/pressure.rs new file mode 100644 index 00000000000..36b77f428ee --- /dev/null +++ b/src/conversions/pressure.rs @@ -0,0 +1,390 @@ +//! Conversion of pressure units. +//! +//! This module provides conversion between various pressure units including: +//! Pascal (Pa, kPa, MPa, GPa), Bar (bar, mbar), Atmosphere (atm, at, ata), +//! Torr (Torr, mTorr), PSI (psi, ksi), Barad (Ba), Pièze (pz), +//! and manometric units (mmHg, cmHg, inHg, mmH2O, cmH2O, inH2O, msw, fsw). +//! +//! # References +//! - [Units of Pressure](https://msestudent.com/what-are-the-units-of-pressure/) + +use std::fmt; +use std::str::FromStr; + +/// Trait for types that can be converted into a PressureUnit +pub trait IntoPressureUnit { + fn into_pressure_unit(self) -> Result; +} + +impl IntoPressureUnit for PressureUnit { + fn into_pressure_unit(self) -> Result { + Ok(self) + } +} + +impl IntoPressureUnit for &str { + fn into_pressure_unit(self) -> Result { + PressureUnit::from_str(self) + } +} + +impl IntoPressureUnit for String { + fn into_pressure_unit(self) -> Result { + PressureUnit::from_str(&self) + } +} + +/// Supported pressure units +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum PressureUnit { + // SI units (Pascal-based) + Pascal, + Kilopascal, + Megapascal, + Gigapascal, + Hectopascal, + + // Atmosphere units + Atmosphere, + TechnicalAtmosphere, + TotalAtmosphere, + + // Torr units + Torr, + Millitorr, + + // Bar units + Bar, + Millibar, + + // Imperial units + Psi, + Ksi, + OunceForcePerSquareInch, + + // Other metric units + Barad, + Pieze, + + // Manometric units + MillimeterMercury, + CentimeterMercury, + InchMercury, + MillimeterWater, + CentimeterWater, + InchWater, + MeterSeawater, + FootSeawater, +} + +impl fmt::Display for PressureUnit { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let s = match self { + Self::Pascal => "Pa", + Self::Kilopascal => "kPa", + Self::Megapascal => "MPa", + Self::Gigapascal => "GPa", + Self::Hectopascal => "hPa", + Self::Atmosphere => "atm", + Self::TechnicalAtmosphere => "at", + Self::TotalAtmosphere => "ata", + Self::Torr => "Torr", + Self::Millitorr => "mTorr", + Self::Bar => "bar", + Self::Millibar => "mbar", + Self::Psi => "psi", + Self::Ksi => "ksi", + Self::OunceForcePerSquareInch => "ozf/in²", + Self::Barad => "Ba", + Self::Pieze => "pz", + Self::MillimeterMercury => "mmHg", + Self::CentimeterMercury => "cmHg", + Self::InchMercury => "inHg", + Self::MillimeterWater => "mmH₂O", + Self::CentimeterWater => "cmH₂O", + Self::InchWater => "inH₂O", + Self::MeterSeawater => "msw", + Self::FootSeawater => "fsw", + }; + write!(f, "{s}") + } +} + +impl PressureUnit { + /// Get the conversion factor to convert this unit to pascals + fn to_pascal_factor(self) -> f64 { + match self { + // SI units (Pascal-based) + Self::Pascal => 1.0, + Self::Kilopascal | Self::Pieze => 1_000.0, + Self::Megapascal => 1_000_000.0, + Self::Gigapascal => 1_000_000_000.0, + Self::Hectopascal | Self::Millibar => 100.0, + + // Atmosphere units + Self::Atmosphere | Self::TotalAtmosphere => 101_325.0, + Self::TechnicalAtmosphere => 98_070.0, + + // Torr units (1 atm = 760 Torr exactly) + Self::Torr | Self::MillimeterMercury => 101_325.0 / 760.0, + Self::Millitorr => 101_325.0 / 760_000.0, + + // Bar units + Self::Bar => 100_000.0, + + // Imperial units + Self::Psi => 6_894.757_293_168, + Self::Ksi => 6_894_757.293_168, + Self::OunceForcePerSquareInch => 430.922_330_823, + + // Other metric units + Self::Barad => 0.1, + + // Manometric units + Self::CentimeterMercury => 101_325.0 / 76.0, + Self::InchMercury => 3_386.389, + Self::MillimeterWater => 9.806_65, + Self::CentimeterWater => 98.0665, + Self::InchWater => 249.088_908_333, + Self::MeterSeawater => 10_000.0, + Self::FootSeawater => 3_048.0, + } + } + + /// Get all supported units as strings + pub fn supported_units() -> Vec<&'static str> { + vec![ + "Pa", "kPa", "MPa", "GPa", "hPa", "atm", "at", "ata", "Torr", "mTorr", "bar", "mbar", + "psi", "ksi", "ozf/in²", "Ba", "pz", "mmHg", "cmHg", "inHg", "mmH₂O", "cmH₂O", "inH₂O", + "msw", "fsw", + ] + } +} + +impl FromStr for PressureUnit { + type Err = String; + + fn from_str(s: &str) -> Result { + let unit = match s.to_lowercase().as_str() { + "pa" | "pascal" => Self::Pascal, + "kpa" | "kilopascal" => Self::Kilopascal, + "mpa" | "megapascal" => Self::Megapascal, + "gpa" | "gigapascal" => Self::Gigapascal, + "hpa" | "hectopascal" => Self::Hectopascal, + "atm" | "atmosphere" => Self::Atmosphere, + "at" | "technical_atmosphere" | "kgf/cm2" => Self::TechnicalAtmosphere, + "ata" | "total_atmosphere" => Self::TotalAtmosphere, + "torr" => Self::Torr, + "mtorr" | "millitorr" => Self::Millitorr, + "bar" => Self::Bar, + "mbar" | "millibar" => Self::Millibar, + "psi" | "lb/in2" => Self::Psi, + "ksi" => Self::Ksi, + "ozf/in2" | "ounce_force_per_square_inch" => Self::OunceForcePerSquareInch, + "ba" | "barad" => Self::Barad, + "pz" | "pieze" => Self::Pieze, + "mmhg" | "millimeter_mercury" => Self::MillimeterMercury, + "cmhg" | "centimeter_mercury" => Self::CentimeterMercury, + "inhg" | "inch_mercury" => Self::InchMercury, + "mmh2o" | "millimeter_water" => Self::MillimeterWater, + "cmh2o" | "centimeter_water" => Self::CentimeterWater, + "inh2o" | "inch_water" => Self::InchWater, + "msw" | "meter_seawater" => Self::MeterSeawater, + "fsw" | "foot_seawater" => Self::FootSeawater, + _ => return Err(format!("Unknown pressure unit: {s}")), + }; + Ok(unit) + } +} + +/// Convert pressure from one unit to another. +/// +/// This function accepts both `PressureUnit` enums and string identifiers. +/// +/// # Arguments +/// +/// * `value` - The numerical value to convert +/// * `from_unit` - The unit to convert from (can be a `PressureUnit` enum or a string) +/// * `to_unit` - The unit to convert to (can be a `PressureUnit` enum or a string) +/// +/// # Returns +/// +/// The converted value, or an error if the unit is invalid +/// +/// # Examples +/// +/// Using enums (type-safe): +/// ```ignore +/// let result = convert_pressure(100.0, PressureUnit::Psi, PressureUnit::Kilopascal); +/// ``` +/// +/// Using strings (convenient): +/// ```ignore +/// let result = convert_pressure(100.0, "psi", "kpa"); +/// ``` +pub fn convert_pressure(value: f64, from_unit: F, to_unit: T) -> Result +where + F: IntoPressureUnit, + T: IntoPressureUnit, +{ + let from = from_unit.into_pressure_unit().map_err(|_| { + format!( + "Invalid 'from_unit' value. Supported values are:\n{}", + PressureUnit::supported_units().join(", ") + ) + })?; + + let to = to_unit.into_pressure_unit().map_err(|_| { + format!( + "Invalid 'to_unit' value. Supported values are:\n{}", + PressureUnit::supported_units().join(", ") + ) + })?; + + // Convert to pascals first, then to target unit + let pascals = value * from.to_pascal_factor(); + Ok(pascals / to.to_pascal_factor()) +} + +#[cfg(test)] +mod tests { + use super::*; + + const EPSILON: f64 = 1e-3; // Increased tolerance for floating point comparisons + + fn approx_eq(a: f64, b: f64) -> bool { + (a - b).abs() < EPSILON + } + + #[test] + fn test_pressure_conversions() { + // Test basic conversions from Python original (using strings) + assert!(approx_eq( + convert_pressure(4.0, "atm", "pascal").unwrap(), + 405_300.0 + )); + assert!(approx_eq( + convert_pressure(1.0, "pascal", "psi").unwrap(), + 0.000_145_037_738 + )); + assert!(approx_eq( + convert_pressure(1.0, "bar", "atm").unwrap(), + 0.986_923_266_716 + )); + assert!(approx_eq( + convert_pressure(3.0, "kilopascal", "bar").unwrap(), + 0.03 + )); + assert!(approx_eq( + convert_pressure(2.0, "megapascal", "psi").unwrap(), + 290.075_476 + )); + assert!(approx_eq( + convert_pressure(4.0, "psi", "torr").unwrap(), + 206.859_730 + )); + assert!(approx_eq( + convert_pressure(1.0, "inhg", "atm").unwrap(), + 0.033_421_052 + )); + assert!(approx_eq( + convert_pressure(1.0, "torr", "psi").unwrap(), + 0.019_336_775 + )); + + // Test using enums (type-safe) + assert!(approx_eq( + convert_pressure(1.0, PressureUnit::Atmosphere, PressureUnit::Pascal).unwrap(), + 101_325.0 + )); + assert!(approx_eq( + convert_pressure(100.0, PressureUnit::Psi, PressureUnit::Kilopascal).unwrap(), + 689.475_729 + )); + + // Test mixed usage (enum and string) + assert!(approx_eq( + convert_pressure(1.0, PressureUnit::Bar, "atm").unwrap(), + 0.986_923_266_716 + )); + assert!(approx_eq( + convert_pressure(1.0, "bar", PressureUnit::Atmosphere).unwrap(), + 0.986_923_266_716 + )); + + // Test invalid units + assert!(convert_pressure(4.0, "wrongUnit", "atm").is_err()); + assert!(convert_pressure(4.0, "atm", "wrongUnit").is_err()); + + // Test atmospheric pressure conversions + assert!(approx_eq( + convert_pressure(1.0, "atm", "pascal").unwrap(), + 101_325.0 + )); + assert!(approx_eq( + convert_pressure(1.0, "atm", "bar").unwrap(), + 1.01325 + )); + assert!(approx_eq( + convert_pressure(1.0, "atm", "torr").unwrap(), + 760.0 + )); + assert!(approx_eq( + convert_pressure(1.0, "atm", "psi").unwrap(), + 14.695_949 + )); + + // Test roundtrip conversion + let original = 100.0; + let converted = convert_pressure(original, "psi", "kpa").unwrap(); + let back = convert_pressure(converted, "kpa", "psi").unwrap(); + assert!(approx_eq(original, back)); + + // Test manometric units + assert!(approx_eq( + convert_pressure(760.0, "mmhg", "atm").unwrap(), + 1.0 + )); + assert!(approx_eq( + convert_pressure(1.0, "mmh2o", "pascal").unwrap(), + 9.80665 + )); + assert!(approx_eq( + convert_pressure(1.0, "msw", "kpa").unwrap(), + 10.0 + )); + assert!(approx_eq( + convert_pressure(1.0, "fsw", "pascal").unwrap(), + 3_048.0 + )); + + // Test technical atmosphere + assert!(approx_eq( + convert_pressure(1.0, "at", "atm").unwrap(), + 0.967_841_105 + )); + + // Test ksi conversion + assert!(approx_eq( + convert_pressure(1.0, "ksi", "psi").unwrap(), + 1_000.0 + )); + + // Test gigapascal conversion + assert!(approx_eq( + convert_pressure(1.0, "gpa", "mpa").unwrap(), + 1_000.0 + )); + + // Test hectopascal equals millibar + let hpa_to_pa = convert_pressure(1.0, "hpa", "pa").unwrap(); + let mbar_to_pa = convert_pressure(1.0, "mbar", "pa").unwrap(); + assert!(approx_eq(hpa_to_pa, mbar_to_pa)); + + // Test barad conversion + assert!(approx_eq(convert_pressure(1.0, "ba", "pa").unwrap(), 0.1)); + + // Test pieze conversion + assert!(approx_eq(convert_pressure(1.0, "pz", "kpa").unwrap(), 1.0)); + } +} From d0db399c24c2ffdc632df86132b51c0fe168567f Mon Sep 17 00:00:00 2001 From: Ali Alimohammadi <41567902+AliAlimohammadi@users.noreply.github.com> Date: Tue, 27 Jan 2026 17:12:30 -0800 Subject: [PATCH 2/3] Update pressure.rs --- src/conversions/pressure.rs | 82 +++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/src/conversions/pressure.rs b/src/conversions/pressure.rs index 36b77f428ee..179f7f1a815 100644 --- a/src/conversions/pressure.rs +++ b/src/conversions/pressure.rs @@ -387,4 +387,86 @@ mod tests { // Test pieze conversion assert!(approx_eq(convert_pressure(1.0, "pz", "kpa").unwrap(), 1.0)); } + + #[test] + fn test_additional_coverage() { + // Test String (owned) conversion + let unit_string = String::from("kPa"); + assert_eq!( + unit_string.into_pressure_unit().unwrap(), + PressureUnit::Kilopascal + ); + + let invalid_string = String::from("invalid"); + assert!(invalid_string.into_pressure_unit().is_err()); + + // Test Display implementation for all units + assert_eq!(format!("{}", PressureUnit::Pascal), "Pa"); + assert_eq!(format!("{}", PressureUnit::Kilopascal), "kPa"); + assert_eq!(format!("{}", PressureUnit::Megapascal), "MPa"); + assert_eq!(format!("{}", PressureUnit::Gigapascal), "GPa"); + assert_eq!(format!("{}", PressureUnit::Hectopascal), "hPa"); + assert_eq!(format!("{}", PressureUnit::Atmosphere), "atm"); + assert_eq!(format!("{}", PressureUnit::TechnicalAtmosphere), "at"); + assert_eq!(format!("{}", PressureUnit::TotalAtmosphere), "ata"); + assert_eq!(format!("{}", PressureUnit::Torr), "Torr"); + assert_eq!(format!("{}", PressureUnit::Millitorr), "mTorr"); + assert_eq!(format!("{}", PressureUnit::Bar), "bar"); + assert_eq!(format!("{}", PressureUnit::Millibar), "mbar"); + assert_eq!(format!("{}", PressureUnit::Psi), "psi"); + assert_eq!(format!("{}", PressureUnit::Ksi), "ksi"); + assert_eq!(format!("{}", PressureUnit::OunceForcePerSquareInch), "ozf/in²"); + assert_eq!(format!("{}", PressureUnit::Barad), "Ba"); + assert_eq!(format!("{}", PressureUnit::Pieze), "pz"); + assert_eq!(format!("{}", PressureUnit::MillimeterMercury), "mmHg"); + assert_eq!(format!("{}", PressureUnit::CentimeterMercury), "cmHg"); + assert_eq!(format!("{}", PressureUnit::InchMercury), "inHg"); + assert_eq!(format!("{}", PressureUnit::MillimeterWater), "mmH₂O"); + assert_eq!(format!("{}", PressureUnit::CentimeterWater), "cmH₂O"); + assert_eq!(format!("{}", PressureUnit::InchWater), "inH₂O"); + assert_eq!(format!("{}", PressureUnit::MeterSeawater), "msw"); + assert_eq!(format!("{}", PressureUnit::FootSeawater), "fsw"); + + // Test Millitorr conversion factor + assert!(approx_eq( + convert_pressure(1.0, "mtorr", "pa").unwrap(), + 101_325.0 / 760_000.0 + )); + assert!(approx_eq( + convert_pressure(1000.0, "mtorr", "torr").unwrap(), + 1.0 + )); + + // Test OunceForcePerSquareInch conversion factor + assert!(approx_eq( + convert_pressure(1.0, "ozf/in2", "pa").unwrap(), + 430.922_330_823 + )); + assert!(approx_eq( + convert_pressure(16.0, "ozf/in2", "psi").unwrap(), + 1.0 + )); + + // Test CentimeterMercury conversion factor + assert!(approx_eq( + convert_pressure(1.0, "cmhg", "pa").unwrap(), + 101_325.0 / 76.0 + )); + assert!(approx_eq( + convert_pressure(76.0, "cmhg", "atm").unwrap(), + 1.0 + )); + + // Test CentimeterWater conversion factor + assert!(approx_eq( + convert_pressure(1.0, "cmh2o", "pa").unwrap(), + 98.0665 + )); + + // Test InchWater conversion factor + assert!(approx_eq( + convert_pressure(1.0, "inh2o", "pa").unwrap(), + 249.088_908_333 + )); + } } From bead7bd321674df2bfc84d35e5241df6f68d1ca2 Mon Sep 17 00:00:00 2001 From: Ali Alimohammadi <41567902+AliAlimohammadi@users.noreply.github.com> Date: Tue, 27 Jan 2026 17:14:52 -0800 Subject: [PATCH 3/3] Update pressure.rs --- src/conversions/pressure.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/conversions/pressure.rs b/src/conversions/pressure.rs index 179f7f1a815..7f6ba251fd1 100644 --- a/src/conversions/pressure.rs +++ b/src/conversions/pressure.rs @@ -396,7 +396,7 @@ mod tests { unit_string.into_pressure_unit().unwrap(), PressureUnit::Kilopascal ); - + let invalid_string = String::from("invalid"); assert!(invalid_string.into_pressure_unit().is_err()); @@ -415,7 +415,10 @@ mod tests { assert_eq!(format!("{}", PressureUnit::Millibar), "mbar"); assert_eq!(format!("{}", PressureUnit::Psi), "psi"); assert_eq!(format!("{}", PressureUnit::Ksi), "ksi"); - assert_eq!(format!("{}", PressureUnit::OunceForcePerSquareInch), "ozf/in²"); + assert_eq!( + format!("{}", PressureUnit::OunceForcePerSquareInch), + "ozf/in²" + ); assert_eq!(format!("{}", PressureUnit::Barad), "Ba"); assert_eq!(format!("{}", PressureUnit::Pieze), "pz"); assert_eq!(format!("{}", PressureUnit::MillimeterMercury), "mmHg");