Skip to content

Commit fc72bb6

Browse files
drodileldruin
authored andcommitted
Add functionality to get Temperature from string
This allows creating Temperature unit from string. Supports also degree character and "deg" in between the floating point value of the unit and the unit character (C/K/F/R).
1 parent e3e8e3e commit fc72bb6

File tree

4 files changed

+154
-0
lines changed

4 files changed

+154
-0
lines changed

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ readme = "README.md"
1616

1717
[features]
1818
no_std = []
19+
from_str = ["regex"]
1920

2021
[dependencies]
2122
serde = { version = "1.0", optional = true, features = ["derive"] }
23+
regex = { version = "1", optional = true }

README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,15 @@ fn main() {
8383
}
8484
```
8585

86+
### Features
87+
88+
The crate contains few features to disable or enable certain functionalities:
89+
90+
* no_std
91+
* Removes functionality that Rust std library provides
92+
* from_str
93+
* Allows creating measurement units from string input
94+
8695
--------------------------------------
8796

8897
**References**

src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ use std::time as time;
2222
#[macro_use]
2323
extern crate serde;
2424

25+
#[cfg(feature = "from_str")]
26+
extern crate regex;
27+
2528
use std::f64::consts::PI as PI;
2629

2730
#[macro_use]

src/temperature.rs

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
//! Types and constants for handling temperature.
22
33
use super::measurement::*;
4+
#[cfg(feature = "from_str")]
5+
use std::str::FromStr;
6+
#[cfg(feature = "from_str")]
7+
use regex::Regex;
48

59
/// The `Temperature` struct can be used to deal with absolute temperatures in
610
/// a common way.
@@ -204,6 +208,35 @@ impl ::std::cmp::PartialOrd for Temperature {
204208
}
205209
}
206210

211+
#[cfg(feature = "from_str")]
212+
impl FromStr for Temperature {
213+
type Err = std::num::ParseFloatError;
214+
215+
/// Create a new Temperature from a string
216+
/// Plain numbers in string are considered to be Celsius
217+
fn from_str(val: &str) -> Result<Self, Self::Err> {
218+
if val.is_empty() {
219+
return Ok(Temperature::from_celsius(0.0));
220+
}
221+
222+
let re = Regex::new(r"\s*([0-9.]*)\s?(deg|\u{00B0}|)?\s?([fckrFCKR]{1})\s*$").unwrap();
223+
if let Some(caps) = re.captures(val) {
224+
let float_val = caps.get(1).unwrap().as_str();
225+
return Ok(
226+
match caps.get(3).unwrap().as_str().to_uppercase().as_str() {
227+
"F" => Temperature::from_fahrenheit(float_val.parse::<f64>()?),
228+
"C" => Temperature::from_celsius(float_val.parse::<f64>()?),
229+
"K" => Temperature::from_kelvin(float_val.parse::<f64>()?),
230+
"R" => Temperature::from_rankine(float_val.parse::<f64>()?),
231+
_ => Temperature::from_celsius(val.parse::<f64>()?),
232+
},
233+
);
234+
}
235+
236+
Ok(Temperature::from_celsius(val.parse::<f64>()?))
237+
}
238+
}
239+
207240
implement_display!(Temperature);
208241
implement_measurement!(TemperatureDelta);
209242

@@ -245,6 +278,113 @@ mod test {
245278
assert_almost_eq(o, 180.0);
246279
}
247280

281+
#[test]
282+
#[cfg(feature = "from_str")]
283+
fn empty_str() {
284+
let t = Temperature::from_str("");
285+
assert!(t.is_ok());
286+
287+
let o = t.unwrap().as_celsius();
288+
assert_eq!(o, 0.0);
289+
}
290+
291+
#[test]
292+
#[cfg(feature = "from_str")]
293+
fn celsius_str() {
294+
let t = Temperature::from_str("100C");
295+
assert!(t.is_ok());
296+
297+
let o = t.unwrap().as_celsius();
298+
assert_almost_eq(o, 100.0);
299+
}
300+
301+
#[test]
302+
#[cfg(feature = "from_str")]
303+
fn celsius_space_str() {
304+
let t = Temperature::from_str("100 C");
305+
assert!(t.is_ok());
306+
307+
let o = t.unwrap().as_celsius();
308+
assert_almost_eq(o, 100.0);
309+
}
310+
311+
#[test]
312+
#[cfg(feature = "from_str")]
313+
fn celsius_degree_str() {
314+
let t = Temperature::from_str("100°C");
315+
assert!(t.is_ok());
316+
317+
let o = t.unwrap().as_celsius();
318+
assert_almost_eq(o, 100.0);
319+
}
320+
321+
#[test]
322+
#[cfg(feature = "from_str")]
323+
fn fahrenheit_str() {
324+
let t = Temperature::from_str("100F");
325+
assert!(t.is_ok());
326+
327+
let o = t.unwrap().as_fahrenheit();
328+
assert_almost_eq(o, 100.0);
329+
}
330+
331+
#[test]
332+
#[cfg(feature = "from_str")]
333+
fn fahrenheit_lc_str() {
334+
let t = Temperature::from_str("100 f");
335+
assert!(t.is_ok());
336+
337+
let o = t.unwrap().as_fahrenheit();
338+
assert_almost_eq(o, 100.0);
339+
}
340+
341+
#[test]
342+
#[cfg(feature = "from_str")]
343+
fn fahrenheit_degree_str() {
344+
let t = Temperature::from_str("100 deg f");
345+
assert!(t.is_ok());
346+
347+
let o = t.unwrap().as_fahrenheit();
348+
assert_almost_eq(o, 100.0);
349+
}
350+
351+
#[test]
352+
#[cfg(feature = "from_str")]
353+
fn rankine_str() {
354+
let t = Temperature::from_str("100R");
355+
assert!(t.is_ok());
356+
357+
let o = t.unwrap().as_rankine();
358+
assert_almost_eq(o, 100.0);
359+
}
360+
361+
#[test]
362+
#[cfg(feature = "from_str")]
363+
fn rankine_degree_str() {
364+
let t = Temperature::from_str("100 °R");
365+
assert!(t.is_ok());
366+
367+
let o = t.unwrap().as_rankine();
368+
assert_almost_eq(o, 100.0);
369+
}
370+
371+
#[test]
372+
#[cfg(feature = "from_str")]
373+
fn number_str() {
374+
let t = Temperature::from_str("100.5");
375+
assert!(t.is_ok());
376+
377+
let o = t.unwrap().as_celsius();
378+
assert_almost_eq(o, 100.5);
379+
}
380+
381+
#[test]
382+
#[cfg(feature = "from_str")]
383+
fn invalid_str() {
384+
let t = Temperature::from_str("abcd");
385+
assert!(t.is_err());
386+
}
387+
248388
// Traits
249389
#[test]
250390
fn add() {

0 commit comments

Comments
 (0)