Skip to main content

grib/encoder/
message.rs

1pub use multigrid::*;
2pub use multiproduct::*;
3pub use single::*;
4
5use super::GpvEncoder;
6use crate::WriteToBuffer;
7
8const SECT0_LEN: usize = 16;
9
10/// A functionality to write an entire GRIB2 message. This trait works in
11/// conjunction with [`WriteGrib2MessageIterL1`], [`WriteGrib2MessageIterL2`],
12/// and [`WriteGrib2MessageIterL3`] to write the message.
13pub trait WriteGrib2Message {
14    type S1<'a>: WriteGrib2Ident
15    where
16        Self: 'a;
17
18    type Item<'a>: WriteGrib2MessageIterL1
19    where
20        Self: 'a;
21
22    type Iter<'a>: Iterator<Item = Self::Item<'a>>
23    where
24        Self: 'a;
25
26    fn discipline(&self) -> u8;
27    fn reserved(&self) -> [u8; 2] {
28        [0xff, 0xff]
29    }
30    fn section1(&self) -> &Self::S1<'_>;
31    fn iter(&self) -> Self::Iter<'_>;
32
33    fn num_octets(&self) -> usize {
34        SECT0_LEN
35            + self.section1().section1_len()
36            + self.iter().map(|m| m.num_octets()).sum::<usize>()
37            + crate::SECT8_ES_SIZE
38    }
39
40    fn write(&self, buf: &mut [u8]) -> Result<usize, &'static str> {
41        let total_len = self.num_octets();
42        if buf.len() < total_len {
43            return Err("destination buffer is too small");
44        }
45
46        let mut pos = 0;
47        pos += write_section0(
48            self.discipline(),
49            self.reserved(),
50            total_len,
51            &mut buf[pos..],
52        )?;
53        pos += self.section1().write_section1(&mut buf[pos..])?;
54        for m in self.iter() {
55            pos += m.write(&mut buf[pos..])?;
56        }
57        pos += write_section8(&mut buf[pos..])?;
58        Ok(pos)
59    }
60}
61
62/// A functionality to write elements of the L1 iterator in a GRIB2 message.
63/// This trait works in conjunction with [`WriteGrib2Message`],
64/// [`WriteGrib2MessageIterL2`], and [`WriteGrib2MessageIterL3`] to write the
65/// message.
66pub trait WriteGrib2MessageIterL1 {
67    type S2<'a>: WriteGrib2LocalUse
68    where
69        Self: 'a;
70
71    type Item<'a>: WriteGrib2MessageIterL2
72    where
73        Self: 'a;
74
75    type Iter<'a>: Iterator<Item = Self::Item<'a>>
76    where
77        Self: 'a;
78
79    fn section2(&self) -> Option<&Self::S2<'_>>;
80    fn iter(&self) -> Self::Iter<'_>;
81
82    fn num_octets(&self) -> usize {
83        self.section2().map_or(0, |m| m.section2_len())
84            + self.iter().map(|m| m.num_octets()).sum::<usize>()
85    }
86
87    fn write(&self, buf: &mut [u8]) -> Result<usize, &'static str> {
88        let mut pos = 0;
89        if let Some(section2) = self.section2() {
90            pos += section2.write_section2(&mut buf[pos..])?;
91        }
92        for m in self.iter() {
93            pos += m.write(&mut buf[pos..])?;
94        }
95        Ok(pos)
96    }
97}
98
99/// A functionality to write elements of the L2 iterator in a GRIB2 message.
100/// This trait works in conjunction with [`WriteGrib2Message`],
101/// [`WriteGrib2MessageIterL1`], and [`WriteGrib2MessageIterL3`] to write the
102/// message.
103pub trait WriteGrib2MessageIterL2 {
104    type S3<'a>: WriteGrib2GridDef
105    where
106        Self: 'a;
107
108    type Item<'a>: WriteGrib2MessageIterL3
109    where
110        Self: 'a;
111
112    type Iter<'a>: Iterator<Item = Self::Item<'a>>
113    where
114        Self: 'a;
115
116    fn section3(&self) -> &Self::S3<'_>;
117    fn iter(&self) -> Self::Iter<'_>;
118
119    fn num_octets(&self) -> usize {
120        self.section3().section3_len() + self.iter().map(|m| m.num_octets()).sum::<usize>()
121    }
122
123    fn write(&self, buf: &mut [u8]) -> Result<usize, &'static str> {
124        let mut pos = 0;
125        pos += self.section3().write_section3(&mut buf[pos..])?;
126        for m in self.iter() {
127            pos += m.write(&mut buf[pos..])?;
128        }
129        Ok(pos)
130    }
131}
132
133/// A functionality to write elements of the L3 iterator in a GRIB2 message.
134/// This trait works in conjunction with [`WriteGrib2Message`],
135/// [`WriteGrib2MessageIterL1`], and [`WriteGrib2MessageIterL2`] to write the
136/// message.
137pub trait WriteGrib2MessageIterL3 {
138    type S4<'a>: WriteGrib2ProductDef
139    where
140        Self: 'a;
141    type SD<'a>: WriteGrib2PointValues
142    where
143        Self: 'a;
144
145    fn section4(&self) -> &Self::S4<'_>;
146    fn data_sections(&self) -> &Self::SD<'_>;
147
148    fn num_octets(&self) -> usize {
149        self.section4().section4_len() + self.data_sections().data_sections_len()
150    }
151
152    fn write(&self, buf: &mut [u8]) -> Result<usize, &'static str> {
153        let mut pos = 0;
154        pos += self.section4().write_section4(&mut buf[pos..])?;
155        pos += self.data_sections().write_data_sections(&mut buf[pos..])?;
156        Ok(pos)
157    }
158}
159
160/// A functionality to write the byte sequence of Section 1 (Identification
161/// Section) of a GRIB2 message.
162///
163/// # Examples
164///
165/// This trait is implemented for the payload struct of Section 1
166/// ([`def::grib2::Section1Payload`](crate::def::grib2::Section1Payload)).
167///
168/// ```
169/// use grib::{TryFromSlice, def::grib2, encoder::WriteGrib2Ident};
170///
171/// fn main() -> Result<(), Box<dyn std::error::Error>> {
172///     let sect = grib2::Section1 {
173///         header: grib2::SectionHeader {
174///             len: 21,
175///             sect_num: 1,
176///         },
177///         payload: grib2::Section1Payload {
178///             centre_id: 0xffff,
179///             subcentre_id: 0,
180///             master_table_version: 29,
181///             local_table_version: 0,
182///             ref_time_significance: 0,
183///             ref_time: grib2::RefTime {
184///                 year: 2026,
185///                 month: 1,
186///                 day: 2,
187///                 hour: 3,
188///                 minute: 4,
189///                 second: 5,
190///             },
191///             prod_status: 0,
192///             data_type: 0,
193///             optional: None,
194///         },
195///     };
196///     let mut buf = vec![0; sect.payload.section1_len()];
197///     sect.payload.write_section1(&mut buf)?;
198///     let decoded = grib2::Section1::try_from_slice(&buf, &mut 0);
199///     assert_eq!(decoded, Ok(sect));
200///
201///     Ok(())
202/// }
203/// ```
204///
205/// This trait is also implemented for the byte sequence of the payload of
206/// Section 1.
207///
208/// ```
209/// use grib::{TryFromSlice, def::grib2, encoder::WriteGrib2Ident};
210///
211/// fn main() -> Result<(), Box<dyn std::error::Error>> {
212///     let mut input = [
213///         0xff, 0xff, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x07, 0xea, 0x01, 0x02, 0x03, 0x04, 0x05,
214///         0x00, 0x00,
215///     ];
216///     let mut buf = vec![0; input.section1_len()];
217///     input.write_section1(&mut buf)?;
218///
219///     // The output is simply the input byte sequence with a section header appended to it.
220///     assert_eq!(&buf[5..], input);
221///
222///     let decoded = grib2::Section1::try_from_slice(&buf, &mut 0);
223///     let expected = Ok(grib2::Section1 {
224///         header: grib2::SectionHeader {
225///             len: 21,
226///             sect_num: 1,
227///         },
228///         payload: grib2::Section1Payload {
229///             centre_id: 0xffff,
230///             subcentre_id: 0,
231///             master_table_version: 29,
232///             local_table_version: 0,
233///             ref_time_significance: 0,
234///             ref_time: grib2::RefTime {
235///                 year: 2026,
236///                 month: 1,
237///                 day: 2,
238///                 hour: 3,
239///                 minute: 4,
240///                 second: 5,
241///             },
242///             prod_status: 0,
243///             data_type: 0,
244///             optional: None,
245///         },
246///     });
247///     assert_eq!(decoded, expected);
248///
249///     Ok(())
250/// }
251/// ```
252///
253/// Since no constraints can be placed on the byte sequence, `write_section1`
254/// can, of course, be executed on any byte sequence; however, if it is executed
255/// on an inappropriate byte sequence, the resulting byte sequence will not be
256/// correctly interpreted as Section 1.
257///
258/// ```
259/// use grib::{TryFromSlice, def::grib2, encoder::WriteGrib2Ident};
260///
261/// fn main() -> Result<(), Box<dyn std::error::Error>> {
262///     let mut input = [0xff, 0xff];
263///     let mut buf = vec![0; input.section1_len()];
264///     input.write_section1(&mut buf)?;
265///
266///     // The output is simply the input byte sequence with a section header appended to it.
267///     assert_eq!(&buf[5..], input);
268///
269///     // The output cannot be correctly interpreted as Section 1.
270///     let decoded = grib2::Section1::try_from_slice(&buf, &mut 0);
271///     let expected = Err("slice length is too short");
272///     assert_eq!(decoded, expected);
273///
274///     Ok(())
275/// }
276/// ```
277pub trait WriteGrib2Ident {
278    fn section1_len(&self) -> usize;
279
280    fn write_section1(&self, buf: &mut [u8]) -> Result<usize, &'static str>;
281}
282
283/// A functionality to write the byte sequence of Section 2 (Local Use Section)
284/// of a GRIB2 message.
285pub trait WriteGrib2LocalUse {
286    fn section2_len(&self) -> usize;
287
288    fn write_section2(&self, buf: &mut [u8]) -> Result<usize, &'static str>;
289}
290
291/// A functionality to write the byte sequence of Section 3 (Grid Definition
292/// Section) of a GRIB2 message.
293pub trait WriteGrib2GridDef {
294    fn section3_len(&self) -> usize;
295
296    fn write_section3(&self, buf: &mut [u8]) -> Result<usize, &'static str>;
297}
298
299/// A functionality to write the byte sequence of Section 4 (Product Definition
300/// Section) of a GRIB2 message.
301pub trait WriteGrib2ProductDef {
302    fn section4_len(&self) -> usize;
303
304    fn write_section4(&self, buf: &mut [u8]) -> Result<usize, &'static str>;
305}
306
307/// A functionality to write the byte sequence of Section 5 (Data
308/// Representation Section), Section 6 (Bit-map section), and Section 7 (Data
309/// Section) of a GRIB2 message.
310pub trait WriteGrib2PointValues {
311    fn data_sections_len(&self) -> usize;
312
313    fn write_data_sections(&self, buf: &mut [u8]) -> Result<usize, &'static str>;
314}
315
316macro_rules! add_impl_for_u8_slices {
317    ($(($trait:ty,$len_method:ident,$write_method:ident,$sect_num:expr),)*) => ($(
318        impl<T: AsRef<[u8]>> $trait for T {
319            fn $len_method(&self) -> usize {
320                self.as_ref().len() + 5
321            }
322
323            fn $write_method(&self, buf: &mut [u8]) -> Result<usize, &'static str> {
324                let len = self.$len_method();
325                if buf.len() < len {
326                    return Err("destination buffer is too small");
327                }
328
329                let mut pos = 0;
330                pos += write_section_header(len as u32, $sect_num, &mut buf[pos..])?;
331                buf[pos..len].copy_from_slice(self.as_ref());
332                Ok(len)
333            }
334        }
335    )*);
336}
337
338add_impl_for_u8_slices![
339    (WriteGrib2Ident, section1_len, write_section1, 1),
340    (WriteGrib2LocalUse, section2_len, write_section2, 2),
341    (WriteGrib2GridDef, section3_len, write_section3, 3),
342    (WriteGrib2ProductDef, section4_len, write_section4, 4),
343];
344
345macro_rules! add_impl_for_payload_structs {
346    ($(($trait:ty,$ty:ty,$len_method:ident,$write_method:ident,$sect_num:expr),)*) => ($(
347        impl $trait for $ty {
348            fn $len_method(&self) -> usize {
349                self.num_bytes_required() + 5
350            }
351
352            fn $write_method(&self, buf: &mut [u8]) -> Result<usize, &'static str> {
353                let len = self.$len_method();
354                if buf.len() < len {
355                    return Err("destination buffer is too small");
356                }
357
358                let mut pos = 0;
359                pos += write_section_header(len as u32, $sect_num, &mut buf[pos..])?;
360                pos += self.write_to_buffer(&mut buf[pos..])?;
361                Ok(pos)
362            }
363        }
364    )*);
365}
366
367add_impl_for_payload_structs![
368    (
369        WriteGrib2Ident,
370        crate::def::grib2::Section1Payload,
371        section1_len,
372        write_section1,
373        1
374    ),
375    (
376        WriteGrib2GridDef,
377        crate::def::grib2::Section3Payload,
378        section3_len,
379        write_section3,
380        3
381    ),
382];
383
384/// A serializer that writes the byte sequence of sections concerning GPV data
385/// to the output buffer.
386pub trait WriteGrib2DataSections {
387    /// Returns the length of the byte sequence in Section 5.
388    fn section5_len(&self) -> usize;
389
390    /// Writes the byte sequence of Section 5 to the output buffer.
391    fn write_section5(&self, buf: &mut [u8]) -> Result<usize, &'static str>;
392
393    /// Returns the length of the byte sequence in Section 6.
394    fn section6_len(&self) -> usize;
395
396    /// Writes the byte sequence of Section 6 to the output buffer.
397    fn write_section6(&self, buf: &mut [u8]) -> Result<usize, &'static str>;
398
399    /// Returns the length of the byte sequence in Section 7.
400    fn section7_len(&self) -> usize;
401
402    /// Writes the byte sequence of Section 7 to the output buffer.
403    fn write_section7(&self, buf: &mut [u8]) -> Result<usize, &'static str>;
404}
405
406fn write_section0(
407    discipline: u8,
408    reserved: [u8; 2],
409    len: usize,
410    buf: &mut [u8],
411) -> Result<usize, &'static str> {
412    if buf.len() < SECT0_LEN {
413        return Err("destination buffer is too small");
414    }
415
416    let sect = crate::def::grib2::Section0 {
417        identifier: [0x47, 0x52, 0x49, 0x42],
418        reserved,
419        discipline,
420        edition_num: 2,
421        total_len: len as u64,
422    };
423
424    let mut pos = 0;
425    pos += sect.write_to_buffer(&mut buf[pos..])?;
426    Ok(pos)
427}
428
429fn write_section8(buf: &mut [u8]) -> Result<usize, &'static str> {
430    const SIGNATURE: [u8; 4] = [0x37, 0x37, 0x37, 0x37];
431    if buf.len() < SIGNATURE.num_bytes_required() {
432        return Err("destination buffer is too small");
433    }
434    SIGNATURE.write_to_buffer(buf)
435}
436
437pub(crate) fn write_section_header(
438    len: u32,
439    sect_num: u8,
440    buf: &mut [u8],
441) -> Result<usize, &'static str> {
442    crate::def::grib2::SectionHeader { len, sect_num }.write_to_buffer(buf)
443}
444
445impl<'a, 'd, P> WriteGrib2MessageIterL3 for (&'a P, &'a GpvEncoder<'d>)
446where
447    P: WriteGrib2ProductDef,
448{
449    type S4<'s>
450        = P
451    where
452        Self: 's;
453
454    type SD<'s>
455        = GpvEncoder<'d>
456    where
457        Self: 's;
458
459    fn section4(&self) -> &Self::S4<'_> {
460        self.0
461    }
462
463    fn data_sections(&self) -> &Self::SD<'_> {
464        self.1
465    }
466}
467
468mod multigrid;
469mod multiproduct;
470mod single;