rustedbytes_nmea/message.rs
1//! NMEA message representation and field parsing
2//!
3//! This module provides the core data structures for representing parsed NMEA messages
4//! and fields. Message-specific parsing implementations are included in separate
5//! submodules for each message type.
6
7use crate::types::*;
8
9// Message type implementations
10mod gga;
11mod gll;
12mod gns;
13mod gsa;
14mod gsv;
15mod rmc;
16mod vtg;
17
18// Re-export message data structures
19pub use gga::GgaData;
20pub use gll::GllData;
21pub use gns::GnsData;
22pub use gsa::GsaData;
23pub use gsv::{GsvData, SatelliteInfo};
24pub use rmc::RmcData;
25pub use vtg::VtgData;
26
27/// Maximum number of fields in an NMEA sentence
28pub(crate) const MAX_FIELDS: usize = 20;
29
30/// Parsed NMEA sentence data (internal representation)
31///
32/// Represents a single parsed NMEA sentence with its type, fields, and metadata.
33/// This is an internal structure used during parsing.
34#[derive(Debug, Clone)]
35pub(crate) struct ParsedSentence {
36 pub message_type: MessageType,
37 pub talker_id: TalkerId,
38 pub fields: [Option<Field>; MAX_FIELDS],
39 pub field_count: usize,
40}
41
42impl ParsedSentence {
43 /// Helper to get a field as a string slice
44 pub(crate) fn get_field_str(&self, index: usize) -> Option<&str> {
45 if index < self.field_count {
46 self.fields[index].as_ref()?.as_str()
47 } else {
48 None
49 }
50 }
51
52 /// Generic helper to parse a field using FromStr trait
53 pub(crate) fn parse_field<T>(&self, index: usize) -> Option<T>
54 where
55 T: core::str::FromStr,
56 {
57 self.get_field_str(index)?.parse().ok()
58 }
59
60 /// Helper to parse a field as char (first character)
61 pub(crate) fn parse_field_char(&self, index: usize) -> Option<char> {
62 self.get_field_str(index)?.chars().next()
63 }
64}
65
66/// Represents a field value in an NMEA message
67///
68/// A field is a single data element within an NMEA sentence, stored as a
69/// fixed-size byte array with length tracking. This provides `no_std` compatible
70/// string storage without heap allocation.
71#[derive(Debug, Clone, Copy)]
72pub struct Field {
73 data: [u8; 16], // Reduced from 32 to 16 bytes - sufficient for most NMEA fields
74 len: u8, // Changed from usize to u8 for memory efficiency
75}
76
77impl Field {
78 /// Create a field from a byte slice
79 ///
80 /// Copies up to 16 bytes from the input slice.
81 pub(crate) fn from_bytes(bytes: &[u8]) -> Self {
82 let copy_len = bytes.len().min(16);
83 let mut data = [0; 16];
84 data[..copy_len].copy_from_slice(&bytes[..copy_len]);
85
86 Field {
87 data,
88 len: copy_len as u8,
89 }
90 }
91
92 /// Get the field as a string slice
93 ///
94 /// Returns `None` if the field contains invalid UTF-8.
95 pub fn as_str(&self) -> Option<&str> {
96 core::str::from_utf8(&self.data[..self.len as usize]).ok()
97 }
98
99 /// Get the field as a byte slice
100 pub fn as_bytes(&self) -> &[u8] {
101 &self.data[..self.len as usize]
102 }
103}