Skip to main content

ccsds_ndm/
traits.rs

1// SPDX-FileCopyrightText: 2025 Jochim Maene <jochim.maene+github@gmail.com>
2//
3// SPDX-License-Identifier: MPL-2.0
4
5//! Core traits for CCSDS NDM message handling.
6//!
7//! This module defines the primary traits used for parsing and serializing
8//! NDM messages in both KVN and XML formats.
9
10use crate::error::Result;
11use crate::kvn::ser::KvnWriter;
12
13/// Core trait for NDM message types.
14///
15/// All CCSDS message types (OPM, OEM, CDM, etc.) implement this trait,
16/// providing a uniform interface for parsing and serialization.
17///
18/// # Example
19///
20/// ```no_run
21/// use ccsds_ndm::messages::opm::Opm;
22/// use ccsds_ndm::traits::Ndm;
23///
24/// // Parse from KVN
25/// let opm = Opm::from_kvn("CCSDS_OPM_VERS = 3.0\n...").unwrap();
26///
27/// // Serialize to XML
28/// let xml = opm.to_xml().unwrap();
29/// ```
30/// Trait for types that provide semantic validation.
31pub trait Validate {
32    /// Perform semantic validation on the object.
33    ///
34    /// Checks for logical consistency beyond syntactic correctness.
35    /// For example: `START_TIME <= STOP_TIME`, or `MASS >= 0`.
36    ///
37    /// # Returns
38    ///
39    /// `Ok(())` if valid, or a `ValidationError` if invalid.
40    fn validate(&self) -> Result<()> {
41        Ok(())
42    }
43}
44
45/// Core trait for NDM message types.
46///
47/// All CCSDS message types (OPM, OEM, CDM, etc.) implement this trait,
48/// providing a uniform interface for parsing and serialization.
49///
50/// # Example
51///
52/// ```no_run
53/// use ccsds_ndm::messages::opm::Opm;
54/// use ccsds_ndm::traits::Ndm;
55///
56/// // Parse from KVN
57/// let opm = Opm::from_kvn("CCSDS_OPM_VERS = 3.0\n...").unwrap();
58///
59/// // Serialize to XML
60/// let xml = opm.to_xml().unwrap();
61/// ```
62pub trait Ndm: Sized + serde::Serialize + Validate {
63    /// Serialize the message to KVN (Key-Value Notation) format.
64    ///
65    /// # Returns
66    ///
67    /// A string containing the KVN representation of the message.
68    fn to_kvn(&self) -> Result<String>;
69
70    /// Parse a message from KVN (Key-Value Notation) format.
71    ///
72    /// # Arguments
73    ///
74    /// * `kvn` - The KVN content as a string
75    fn from_kvn(kvn: &str) -> Result<Self>;
76
77    /// Serialize the message to XML format.
78    ///
79    /// # Returns
80    ///
81    /// A string containing the XML representation of the message.
82    fn to_xml(&self) -> Result<String>;
83
84    /// Parse a message from XML format.
85    ///
86    /// # Arguments
87    ///
88    /// * `xml` - The XML content as a string
89    fn from_xml(xml: &str) -> Result<Self>;
90}
91
92/// Trait for types that can be parsed from a KVN value string.
93///
94/// This is automatically implemented for any type that implements `FromStr`.
95pub trait FromKvnValue: Sized {
96    /// Parse a value from its KVN string representation.
97    ///
98    /// # Arguments
99    ///
100    /// * `s` - The value string (without key or unit)
101    fn from_kvn_value(s: &str) -> Result<Self>;
102}
103
104/// Trait to check if a value is considered "null" or "empty" in CCSDS context.
105///
106/// This unifies the logic for XML (nil="true" or empty text) and KVN (empty value).
107pub trait CcsdsNullable {
108    /// Returns true if the value represents a null/empty state.
109    fn is_null(&self) -> bool;
110}
111
112impl CcsdsNullable for String {
113    fn is_null(&self) -> bool {
114        self.trim().is_empty() || self.trim() == "n/a"
115    }
116}
117
118impl CcsdsNullable for str {
119    fn is_null(&self) -> bool {
120        self.trim().is_empty() || self.trim() == "n/a"
121    }
122}
123
124impl<T> FromKvnValue for T
125where
126    T: std::str::FromStr,
127    T::Err: Into<crate::error::CcsdsNdmError>,
128{
129    fn from_kvn_value(s: &str) -> Result<Self> {
130        s.parse::<T>().map_err(Into::into)
131    }
132}
133
134/// Trait for types that can be parsed directly from a float and optional unit.
135///
136/// This avoids the overhead of formatting a float to a string and then parsing it back.
137pub trait FromKvnFloat: Sized {
138    /// Create an instance from a float value and optional unit string.
139    ///
140    /// # Arguments
141    ///
142    /// * `value` - The float value
143    /// * `unit` - The optional unit string
144    fn from_kvn_float(value: f64, unit: Option<&str>) -> Result<Self>;
145}
146
147/// Trait for types that can be serialized to KVN format.
148///
149/// Implementors write their KVN representation to the provided [`KvnWriter`].
150pub trait ToKvn {
151    /// Write the KVN representation to the writer.
152    ///
153    /// # Arguments
154    ///
155    /// * `writer` - The KVN writer to output to
156    fn write_kvn(&self, writer: &mut KvnWriter);
157}