Skip to main content

use_material_property/
lib.rs

1#![forbid(unsafe_code)]
2//! Lightweight material-property metadata helpers.
3//!
4//! This crate intentionally stays small. It models a property kind, a numeric
5//! value, and a display unit without becoming a full material database.
6//!
7//! # Examples
8//!
9//! ```rust
10//! use use_material_property::{
11//!     MaterialProperty, MaterialPropertyKind, filter_by_kind, property_values,
12//! };
13//!
14//! let properties = [
15//!     MaterialProperty::new(MaterialPropertyKind::Density, 7_850.0, "kg/m^3").unwrap(),
16//!     MaterialProperty::new(MaterialPropertyKind::Stress, 250_000_000.0, "Pa").unwrap(),
17//!     MaterialProperty::new(MaterialPropertyKind::Density, 1_000.0, "kg/m^3").unwrap(),
18//! ];
19//!
20//! assert_eq!(filter_by_kind(&properties, MaterialPropertyKind::Density).len(), 2);
21//! assert_eq!(property_values(&properties, MaterialPropertyKind::Density), vec![7_850.0, 1_000.0]);
22//! ```
23
24#[derive(Debug, Clone, Copy, PartialEq, Eq)]
25pub enum MaterialPropertyKind {
26    Density,
27    Stress,
28    Strain,
29    ElasticModulus,
30    Hardness,
31    ThermalConductivity,
32    ElectricalConductivity,
33    ThermalExpansion,
34}
35
36#[derive(Debug, Clone, Copy, PartialEq)]
37pub struct MaterialProperty {
38    kind: MaterialPropertyKind,
39    value: f64,
40    unit: &'static str,
41}
42
43#[derive(Debug, Clone, Copy, PartialEq, Eq)]
44pub enum MaterialPropertyError {
45    InvalidValue,
46    InvalidUnit,
47}
48
49impl MaterialProperty {
50    pub fn new(
51        kind: MaterialPropertyKind,
52        value: f64,
53        unit: &'static str,
54    ) -> Result<Self, MaterialPropertyError> {
55        if !value.is_finite() {
56            return Err(MaterialPropertyError::InvalidValue);
57        }
58
59        if unit.trim().is_empty() {
60            return Err(MaterialPropertyError::InvalidUnit);
61        }
62
63        Ok(Self { kind, value, unit })
64    }
65
66    #[must_use]
67    pub fn kind(&self) -> MaterialPropertyKind {
68        self.kind
69    }
70
71    #[must_use]
72    pub fn value(&self) -> f64 {
73        self.value
74    }
75
76    #[must_use]
77    pub fn unit(&self) -> &'static str {
78        self.unit
79    }
80}
81
82#[must_use]
83pub fn filter_by_kind(
84    properties: &[MaterialProperty],
85    kind: MaterialPropertyKind,
86) -> Vec<MaterialProperty> {
87    properties
88        .iter()
89        .copied()
90        .filter(|property| property.kind == kind)
91        .collect()
92}
93
94#[must_use]
95pub fn property_values(properties: &[MaterialProperty], kind: MaterialPropertyKind) -> Vec<f64> {
96    filter_by_kind(properties, kind)
97        .into_iter()
98        .map(|property| property.value)
99        .collect()
100}
101
102#[cfg(test)]
103mod tests {
104    use super::{
105        MaterialProperty, MaterialPropertyError, MaterialPropertyKind, filter_by_kind,
106        property_values,
107    };
108
109    #[test]
110    fn filters_properties_by_kind() {
111        let properties = [
112            MaterialProperty::new(MaterialPropertyKind::Density, 7_850.0, "kg/m^3").unwrap(),
113            MaterialProperty::new(MaterialPropertyKind::Stress, 250_000_000.0, "Pa").unwrap(),
114            MaterialProperty::new(MaterialPropertyKind::Density, 1_000.0, "kg/m^3").unwrap(),
115        ];
116
117        assert_eq!(
118            filter_by_kind(&properties, MaterialPropertyKind::Density).len(),
119            2
120        );
121        assert_eq!(
122            property_values(&properties, MaterialPropertyKind::Density),
123            vec![7_850.0, 1_000.0]
124        );
125    }
126
127    #[test]
128    fn exposes_property_fields() {
129        let property =
130            MaterialProperty::new(MaterialPropertyKind::ElasticModulus, 200.0, "GPa").unwrap();
131
132        assert_eq!(property.kind(), MaterialPropertyKind::ElasticModulus);
133        assert_eq!(property.value(), 200.0);
134        assert_eq!(property.unit(), "GPa");
135    }
136
137    #[test]
138    fn rejects_invalid_material_property_inputs() {
139        assert_eq!(
140            MaterialProperty::new(MaterialPropertyKind::Density, f64::NAN, "kg/m^3"),
141            Err(MaterialPropertyError::InvalidValue)
142        );
143        assert_eq!(
144            MaterialProperty::new(MaterialPropertyKind::Density, 1.0, "   "),
145            Err(MaterialPropertyError::InvalidUnit)
146        );
147    }
148}