Skip to main content

grib_reader/
indicator.rs

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