1- // crates/powerlink-rs-xdc/src/parser.rs
2-
3- //! The internal XML parser and helper functions for parsing hex strings.
1+ //! The internal XML parser entry point.
2+ //!
3+ //! This module handles the raw XML deserialization using `quick-xml` and forwards
4+ //! the data to the `resolver` module for business logic processing.
45
56use crate :: error:: XdcError ;
67use crate :: model;
7- use crate :: resolver; // This module's functions are now called by `load_...`
8- use crate :: resolver:: ValueMode ; // Import the new ValueMode enum
8+ use crate :: resolver;
9+ use crate :: resolver:: ValueMode ;
910use crate :: types:: XdcFile ;
1011use alloc:: string:: String ;
1112use alloc:: vec:: Vec ;
@@ -15,62 +16,69 @@ use quick_xml::de::from_str;
1516
1617// --- Public API Functions ---
1718
18- /// Loads XDC data (using `actualValue`) from an XML string.
19+ /// Loads XDC data from an XML string, prioritizing `actualValue` attributes.
20+ ///
21+ /// This function is intended for loading **Configuration** files (XDC). It will
22+ /// resolve object dictionary values by looking for `@actualValue` first. If not found,
23+ /// it falls back to `@defaultValue`.
1924///
20- /// This function parses the XML and resolves the data model by prioritizing
21- /// the `actualValue` attributes, which is standard for XDC (Configuration) files.
25+ /// # Arguments
26+ /// * `s` - The raw XML string content.
27+ ///
28+ /// # Returns
29+ /// * `Result<XdcFile, XdcError>` - The parsed and resolved file structure.
2230pub fn load_xdc_from_str ( s : & str ) -> Result < XdcFile , XdcError > {
2331 let container = parse_xml_str ( s) ?;
24- // Call the resolver with ValueMode::Actual
2532 resolver:: resolve_data ( container, ValueMode :: Actual )
2633}
2734
28- /// Loads XDD default data (using `defaultValue`) from an XML string.
35+ /// Loads XDD data from an XML string, prioritizing `defaultValue` attributes.
36+ ///
37+ /// This function is intended for loading **Device Description** files (XDD). It will
38+ /// resolve object dictionary values by looking for `@defaultValue` first.
2939///
30- /// This function parses the XML and resolves the data model by prioritizing
31- /// the `defaultValue` attributes, which is standard for XDD (Device Description) files.
40+ /// # Arguments
41+ /// * `s` - The raw XML string content.
42+ ///
43+ /// # Returns
44+ /// * `Result<XdcFile, XdcError>` - The parsed and resolved file structure.
3245pub fn load_xdd_defaults_from_str ( s : & str ) -> Result < XdcFile , XdcError > {
3346 let container = parse_xml_str ( s) ?;
34- // Call the resolver with ValueMode::Default
3547 resolver:: resolve_data ( container, ValueMode :: Default )
3648}
3749
3850// --- Internal XML Deserialization ---
3951
40- /// The core internal function that uses `quick-xml` to deserialize the string
41- /// into the raw `model` structs.
52+ /// Deserializes the raw XML string into the internal `model` structs.
4253pub ( crate ) fn parse_xml_str ( s : & str ) -> Result < model:: Iso15745ProfileContainer , XdcError > {
43- // quick-xml's deserializer is very efficient.
44- // It maps the XML structure directly to our `model` structs.
4554 from_str ( s) . map_err ( XdcError :: from)
4655}
4756
4857// --- Hex String Parsing Helpers ---
49- // These are used by the resolver.
5058
51- /// Parses a "0x..." or "..." hex string into a `u32`.
59+ /// Parses a "0x..." or raw hex string into a `u32`.
5260pub ( crate ) fn parse_hex_u32 ( s : & str ) -> Result < u32 , ParseIntError > {
5361 let s_no_prefix = s. strip_prefix ( "0x" ) . unwrap_or ( s) ;
5462 u32:: from_str_radix ( s_no_prefix, 16 )
5563}
5664
57- /// Parses a "0x..." or "..." hex string into a `u16`.
65+ /// Parses a "0x..." or raw hex string into a `u16`.
5866pub ( crate ) fn parse_hex_u16 ( s : & str ) -> Result < u16 , ParseIntError > {
5967 let s_no_prefix = s. strip_prefix ( "0x" ) . unwrap_or ( s) ;
6068 u16:: from_str_radix ( s_no_prefix, 16 )
6169}
6270
63- /// Parses a "0x..." or "..." hex string into a `u8`.
71+ /// Parses a "0x..." or raw hex string into a `u8`.
6472pub ( crate ) fn parse_hex_u8 ( s : & str ) -> Result < u8 , ParseIntError > {
6573 let s_no_prefix = s. strip_prefix ( "0x" ) . unwrap_or ( s) ;
6674 u8:: from_str_radix ( s_no_prefix, 16 )
6775}
6876
69- /// Parses a "0x..." or "..." hex string into a byte vector.
77+ /// Parses a "0x..." or raw hex string into a byte vector.
78+ /// Handles odd-length strings by padding with a leading zero.
7079pub ( crate ) fn parse_hex_string ( s : & str ) -> Result < Vec < u8 > , FromHexError > {
7180 let s_no_prefix = s. strip_prefix ( "0x" ) . unwrap_or ( s) ;
7281
73- // Handle odd-length strings by padding with a leading zero
7482 if s_no_prefix. len ( ) % 2 != 0 {
7583 let mut padded_s = String :: with_capacity ( s_no_prefix. len ( ) + 1 ) ;
7684 padded_s. push ( '0' ) ;
@@ -86,9 +94,8 @@ mod tests {
8694 use super :: * ;
8795 use crate :: error:: XdcError ;
8896 use alloc:: vec;
89- use hex:: FromHexError ; // Import for invalid char test
97+ use hex:: FromHexError ;
9098
91- // A minimal but complete XDC structure for testing.
9299 const MINIMAL_GOOD_XDC : & str = r#"<?xml version="1.0" encoding="UTF-8"?>
93100<ISO15745ProfileContainer xmlns="http://www.ethernet-powerlink.org" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.ethernet-powerlink.org Powerlink_Main.xsd">
94101 <ISO15745Profile>
@@ -145,12 +152,10 @@ mod tests {
145152 let result = load_xdc_from_str ( MINIMAL_GOOD_XDC ) ;
146153 assert ! ( result. is_ok( ) ) ;
147154 let xdc_file = result. unwrap ( ) ;
148- // Fix: Use `name` field, not `profile_name`
149155 assert_eq ! ( xdc_file. header. name, "Test Profile" ) ;
150156 assert_eq ! ( xdc_file. identity. vendor_name, "TestVendor" ) ;
151157 assert_eq ! ( xdc_file. object_dictionary. objects. len( ) , 1 ) ;
152158 assert_eq ! ( xdc_file. object_dictionary. objects[ 0 ] . index, 0x1000 ) ;
153- // FIX: The value "0x1234" (U16) is parsed as LE, so it becomes [0x34, 0x12].
154159 assert_eq ! (
155160 xdc_file. object_dictionary. objects[ 0 ] . data. as_deref( ) ,
156161 Some ( "0x1234" )
@@ -164,7 +169,6 @@ mod tests {
164169 assert ! ( result. is_ok( ) ) ;
165170 let xdd_file = result. unwrap ( ) ;
166171 assert_eq ! ( xdd_file. identity. vendor_name, "TestVendor" ) ;
167- // FIX: The value "0x1234" (U16) is parsed as LE, so it becomes [0x34, 0x12].
168172 assert_eq ! (
169173 xdd_file. object_dictionary. objects[ 0 ] . data. as_deref( ) ,
170174 Some ( "0x1234" )
@@ -173,7 +177,7 @@ mod tests {
173177
174178 #[ test]
175179 fn test_load_xdc_malformed_xml ( ) {
176- let malformed_xml = "<ISO15745ProfileContainer><ProfileHeader>" ; // Missing closing tags
180+ let malformed_xml = "<ISO15745ProfileContainer><ProfileHeader>" ;
177181 let result = load_xdc_from_str ( malformed_xml) ;
178182 assert ! ( matches!( result, Err ( XdcError :: XmlParsing ( _) ) ) ) ;
179183 }
@@ -225,8 +229,6 @@ mod tests {
225229 ) ) ;
226230 }
227231
228- // --- Unit Tests for Helpers ---
229-
230232 #[ test]
231233 fn test_parse_hex_u32 ( ) {
232234 assert_eq ! ( parse_hex_u32( "0x1A2B3C4D" ) . unwrap( ) , 0x1A2B3C4D ) ;
@@ -265,11 +267,8 @@ mod tests {
265267
266268 #[ test]
267269 fn test_parse_hex_string_odd_length ( ) {
268- // Odd length string "1" becomes "01"
269270 assert_eq ! ( parse_hex_string( "1" ) . unwrap( ) , vec![ 0x01 ] ) ;
270- // Odd length string "0xABC" becomes "0ABC"
271271 assert_eq ! ( parse_hex_string( "0xABC" ) . unwrap( ) , vec![ 0x0A , 0xBC ] ) ;
272- // Odd length string "123" becomes "0123"
273272 assert_eq ! ( parse_hex_string( "123" ) . unwrap( ) , vec![ 0x01 , 0x23 ] ) ;
274273 }
275274
@@ -287,4 +286,4 @@ mod tests {
287286 Err ( FromHexError :: InvalidHexCharacter { .. } )
288287 ) ) ;
289288 }
290- }
289+ }
0 commit comments