use_material_property/
lib.rs1#![forbid(unsafe_code)]
2#[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}