Skip to main content

grib_reader/
indicator.rs

1//! Section 0: Indicator Section parsing for GRIB1 and GRIB2.
2
3/// Parsed Indicator Section (Section 0).
4#[derive(Debug, Clone)]
5pub struct Indicator {
6    /// GRIB edition number (1 or 2).
7    pub edition: u8,
8    /// Discipline (GRIB2 only): 0=Meteorological, 1=Hydrological, 2=Land surface, etc.
9    pub discipline: u8,
10    /// Total length of the GRIB message in bytes.
11    pub total_length: u64,
12}
13
14impl Indicator {
15    /// Parse from the first bytes of a GRIB message.
16    pub fn parse(data: &[u8]) -> Option<Self> {
17        if data.len() < 8 || &data[0..4] != b"GRIB" {
18            return None;
19        }
20
21        let edition = data[7];
22        match edition {
23            1 => {
24                let length = ((data[4] as u64) << 16) | ((data[5] as u64) << 8) | (data[6] as u64);
25                Some(Self {
26                    edition,
27                    discipline: 0,
28                    total_length: length,
29                })
30            }
31            2 => {
32                if data.len() < 16 {
33                    return None;
34                }
35                let discipline = data[6];
36                let length = u64::from_be_bytes(data[8..16].try_into().ok()?);
37                Some(Self {
38                    edition,
39                    discipline,
40                    total_length: length,
41                })
42            }
43            _ => None,
44        }
45    }
46}
47
48#[cfg(test)]
49mod tests {
50    use super::*;
51
52    #[test]
53    fn parse_grib2_indicator() {
54        let mut data = Vec::new();
55        data.extend_from_slice(b"GRIB");
56        data.extend_from_slice(&[0, 0]);
57        data.push(0); // discipline
58        data.push(2); // edition
59        data.extend_from_slice(&100u64.to_be_bytes());
60        let ind = Indicator::parse(&data).unwrap();
61        assert_eq!(ind.edition, 2);
62        assert_eq!(ind.discipline, 0);
63        assert_eq!(ind.total_length, 100);
64    }
65
66    #[test]
67    fn parse_grib1_indicator() {
68        let mut data = vec![0u8; 8];
69        data[0..4].copy_from_slice(b"GRIB");
70        data[4] = 0;
71        data[5] = 3;
72        data[6] = 232; // 1000
73        data[7] = 1;
74        let ind = Indicator::parse(&data).unwrap();
75        assert_eq!(ind.edition, 1);
76        assert_eq!(ind.total_length, 1000);
77    }
78
79    #[test]
80    fn reject_invalid_magic() {
81        assert!(Indicator::parse(b"NOPE1234").is_none());
82    }
83}