Skip to main content

pdg_rs/models/
pdgmeasurement.rs

1use std::fmt::Display;
2
3use rusqlite::Row;
4
5use crate::{LimitType, PdgFootnote, PdgId};
6
7/// Bibliographic reference for a PDG measurement.
8#[derive(Clone, Debug)]
9pub struct PdgReference {
10    /// PDG document identifier.
11    pub document_id: String,
12    /// Publication venue or collaboration name, when available.
13    pub publication_name: Option<String>,
14    /// Publication year, when available.
15    pub publication_year: Option<isize>,
16    /// Digital object identifier, when available.
17    pub doi: Option<String>,
18    /// INSPIRE record identifier, when available.
19    pub inspire_id: Option<String>,
20    /// Publication title, when available.
21    pub title: Option<String>,
22}
23
24/// Experimental or observational measurement supporting a PDG data entry.
25#[derive(Clone, Debug)]
26pub struct PdgMeasurement {
27    /// PDG identifier measured by this row.
28    pub pdgid: PdgId,
29    /// Bibliographic reference for the measurement.
30    pub reference: PdgReference,
31    /// Event count reported by the measurement.
32    pub event_count: Option<String>,
33    /// Confidence level associated with the measurement.
34    pub confidence_level: Option<f64>,
35    /// Location or experiment place label.
36    pub place: Option<String>,
37    /// Measurement technique label.
38    pub technique: Option<String>,
39    /// Charge selector or charge state label.
40    pub charge: Option<String>,
41    /// Whether the measurement is marked by a PDG change bar.
42    pub changebar: bool,
43    /// PDG comment attached to the measurement.
44    pub comment: Option<String>,
45    /// Sort key used by the PDG tables.
46    pub sort: isize,
47    /// Values reported by this measurement.
48    pub values: Vec<PdgMeasurementValue>,
49    /// Footnotes attached to this measurement.
50    pub footnotes: Vec<PdgFootnote>,
51    pub(crate) id: isize,
52}
53
54impl TryFrom<&Row<'_>> for PdgMeasurement {
55    type Error = rusqlite::Error;
56
57    fn try_from(row: &Row<'_>) -> Result<Self, Self::Error> {
58        Ok(Self {
59            id: row.get(0)?,
60            pdgid: row.get(1)?,
61            event_count: row.get(2)?,
62            confidence_level: row.get(3)?,
63            place: row.get(4)?,
64            technique: row.get(5)?,
65            charge: row.get(6)?,
66            changebar: row.get(7)?,
67            comment: row.get(8)?,
68            sort: row.get(9)?,
69            reference: PdgReference {
70                document_id: row.get(10)?,
71                publication_name: row.get(11)?,
72                publication_year: row.get(12)?,
73                doi: row.get(13)?,
74                inspire_id: row.get(14)?,
75                title: row.get(15)?,
76            },
77            values: Vec::new(),
78            footnotes: Vec::new(),
79        })
80    }
81}
82
83/// Individual value reported by a [`PdgMeasurement`].
84#[derive(Clone, Debug)]
85pub struct PdgMeasurementValue {
86    /// Column name for multi-column measurement rows.
87    pub column_name: Option<String>,
88    /// Raw value text from the measurement table.
89    pub value_text: Option<String>,
90    /// Unit text from the measurement table.
91    pub unit_text: Option<String>,
92    /// Display-ready value text.
93    pub display_value_text: Option<String>,
94    /// Power-of-ten exponent used when displaying the value.
95    pub display_power_of_ten: Option<isize>,
96    /// Whether the display value should be interpreted as a percentage.
97    pub display_in_percent: Option<bool>,
98    /// Limit or range type for this value.
99    pub limit_type: Option<LimitType>,
100    /// Whether this value is used in the PDG average.
101    pub used_in_average: bool,
102    /// Whether this value is used in a PDG fit.
103    pub used_in_fit: bool,
104    /// Parsed numeric central value.
105    pub value: Option<f64>,
106    /// Positive total uncertainty.
107    pub error_positive: Option<f64>,
108    /// Negative total uncertainty.
109    pub error_negative: Option<f64>,
110    /// Positive statistical uncertainty.
111    pub stat_error_positive: Option<f64>,
112    /// Negative statistical uncertainty.
113    pub stat_error_negative: Option<f64>,
114    /// Positive systematic uncertainty.
115    pub syst_error_positive: Option<f64>,
116    /// Negative systematic uncertainty.
117    pub syst_error_negative: Option<f64>,
118    /// Sort key used by the PDG tables.
119    pub sort: isize,
120}
121
122impl TryFrom<&Row<'_>> for PdgMeasurementValue {
123    type Error = rusqlite::Error;
124
125    fn try_from(row: &Row<'_>) -> Result<Self, Self::Error> {
126        Ok(Self {
127            column_name: row.get(0)?,
128            value_text: row.get(1)?,
129            unit_text: row.get(2)?,
130            display_value_text: row.get(3)?,
131            display_power_of_ten: row.get(4)?,
132            display_in_percent: row.get(5)?,
133            limit_type: row.get(6)?,
134            used_in_average: row.get(7)?,
135            used_in_fit: row.get(8)?,
136            value: row.get(9)?,
137            error_positive: row.get(10)?,
138            error_negative: row.get(11)?,
139            stat_error_positive: row.get(12)?,
140            stat_error_negative: row.get(13)?,
141            syst_error_positive: row.get(14)?,
142            syst_error_negative: row.get(15)?,
143            sort: row.get(16)?,
144        })
145    }
146}
147
148impl Display for PdgMeasurementValue {
149    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
150        write!(
151            f,
152            "{}",
153            self.display_value_text
154                .clone()
155                .unwrap_or_else(|| "NULL".to_string())
156        )?;
157        if self.display_in_percent.unwrap_or_default() {
158            write!(f, "%")?;
159        } else if self.display_power_of_ten.unwrap_or_default() != 0 {
160            write!(f, "E{}", self.display_power_of_ten.unwrap_or_default())?;
161        }
162        if let Some(unit_text) = &self.unit_text
163            && !unit_text.is_empty()
164        {
165            write!(f, " {unit_text}")?;
166        }
167
168        Ok(())
169    }
170}