Skip to main content

scientific_method/
measurement.rs

1// Copyright © 2026 James Hunt. All rights reserved.
2
3/// A scalar value observed by an experiment.
4#[derive(Debug, Clone, PartialEq)]
5#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
6#[cfg_attr(
7    feature = "serde",
8    serde(tag = "type", content = "value", rename_all = "snake_case")
9)]
10pub enum Scalar {
11    Number(f64),
12    Boolean(bool),
13    Text(String),
14}
15
16impl From<f64> for Scalar {
17    fn from(value: f64) -> Self {
18        Self::Number(value)
19    }
20}
21
22impl From<bool> for Scalar {
23    fn from(value: bool) -> Self {
24        Self::Boolean(value)
25    }
26}
27
28impl From<String> for Scalar {
29    fn from(value: String) -> Self {
30        Self::Text(value)
31    }
32}
33
34impl From<&str> for Scalar {
35    fn from(value: &str) -> Self {
36        Self::Text(value.to_owned())
37    }
38}
39
40/// One named measurement and its optional unit.
41#[derive(Debug, Clone, PartialEq)]
42#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
43pub struct Measurement {
44    pub name: String,
45    pub value: Scalar,
46    pub unit: Option<String>,
47}
48
49impl Measurement {
50    pub fn new(name: impl Into<String>, value: impl Into<Scalar>) -> Self {
51        Self {
52            name: name.into(),
53            value: value.into(),
54            unit: None,
55        }
56    }
57
58    pub fn with_unit(mut self, unit: impl Into<String>) -> Self {
59        self.unit = Some(unit.into());
60        self
61    }
62}