1- // crates/powerlink-rs-xdc/src/builder/mod.rs
1+ //! Provides functionality to serialize `XdcFile` structs back into XDC-compliant XML strings.
2+ //!
3+ //! This module implements the conversion from the user-friendly public `types`
4+ //! back to the internal `model` structs required by `quick-xml` for correct serialization
5+ //! according to the EPSG DS 311 schema.
26
3- // Declare the new modules for serialization logic
47pub mod app_process;
58pub mod device_function;
69pub mod device_manager;
@@ -19,13 +22,22 @@ use alloc::vec::Vec;
1922use core:: fmt:: Write ;
2023use serde:: Serialize ;
2124
22- // Import new model paths
2325use crate :: model:: app_layers:: { Object , ObjectAccessType , ObjectList , ObjectPdoMapping , SubObject } ;
2426use crate :: model:: common:: ReadOnlyString ;
2527use crate :: model:: header:: ProfileHeader ;
2628use crate :: model:: identity:: { DeviceIdentity , Version } ;
2729
28- /// Serializes `XdcFile` data into a standard XDC XML `String`.
30+ /// Serializes an `XdcFile` data structure into a standard XDC XML string.
31+ ///
32+ /// This function generates a complete XML document, including the standard header
33+ /// and the ISO 15745 container structure. It handles both the Device Profile
34+ /// (identity, functions) and the Communication Profile (Object Dictionary, Network Management).
35+ ///
36+ /// # Arguments
37+ /// * `file` - The `XdcFile` structure containing the device configuration.
38+ ///
39+ /// # Returns
40+ /// * `Result<String, XdcError>` - The formatted XML string or a serialization error.
2941pub fn save_xdc_to_string ( file : & XdcFile ) -> Result < String , XdcError > {
3042 // 1. Convert Identity, DeviceManager, and AppProcess to Device Profile
3143 let device_profile = build_device_profile (
@@ -50,7 +62,7 @@ pub fn save_xdc_to_string(file: &XdcFile) -> Result<String, XdcError> {
5062 ..Default :: default ( )
5163 } ;
5264
53- // 4. Serialize
65+ // 4. Serialize to string
5466 let mut buffer = String :: new ( ) ;
5567 write ! (
5668 & mut buffer,
@@ -65,8 +77,6 @@ pub fn save_xdc_to_string(file: &XdcFile) -> Result<String, XdcError> {
6577 Ok ( buffer)
6678}
6779
68- // ... (Rest of the builder logic remains the same) ...
69-
7080/// Helper to build the `model::ProfileHeader` from the `types::ProfileHeader`.
7181fn build_model_header ( header : & types:: ProfileHeader ) -> ProfileHeader {
7282 model:: header:: ProfileHeader {
@@ -79,7 +89,7 @@ fn build_model_header(header: &types::ProfileHeader) -> ProfileHeader {
7989 }
8090}
8191
82- /// Builds the Device Profile model from the public types .
92+ /// Constructs the internal `Iso15745Profile` model representing the Device Profile .
8393fn build_device_profile (
8494 header : & types:: ProfileHeader ,
8595 identity : & types:: Identity ,
@@ -193,7 +203,7 @@ fn build_device_profile(
193203 }
194204}
195205
196- /// Builds the Communication Profile model from the public Object Dictionary .
206+ /// Constructs the internal `Iso15745Profile` model representing the Communication Profile .
197207fn build_comm_profile (
198208 header : & types:: ProfileHeader ,
199209 od : & types:: ObjectDictionary ,
@@ -295,18 +305,12 @@ fn format_hex_u8(val: u8) -> String {
295305 format ! ( "{:02X}" , val)
296306}
297307
298- /// Formats a byte slice into a string.
299- /// Since `types.rs` now stores data as `String`, this function simply constructs
300- /// the string from bytes. If it was already a string (UTF-8), it returns it.
308+ /// Helper to format a value into a string for serialization.
309+ /// Currently assumes input bytes are already valid UTF-8 strings.
301310fn format_value_to_string ( data : & [ u8 ] , _data_type_id : Option < & str > ) -> Result < String , XdcError > {
302- // Try to interpret as UTF-8 string first
303311 if let Ok ( s) = core:: str:: from_utf8 ( data) {
304312 return Ok ( s. to_string ( ) ) ;
305313 }
306- // If not valid UTF-8, shouldn't happen with our current String storage,
307- // but as a fallback we could hex encode it.
308- // However, since `types::Object` stores `Option<String>`, `data` here
309- // comes from `d.as_bytes()`, so it SHOULD be valid UTF-8.
310314 Err ( XdcError :: FmtError ( core:: fmt:: Error ) )
311315}
312316
@@ -338,12 +342,10 @@ mod tests {
338342 use crate :: model:: Iso15745ProfileContainer ;
339343 use crate :: types:: { self , Object , ObjectDictionary , SubObject } ;
340344 use alloc:: string:: ToString ;
341- use alloc:: vec; // Fix: Import ToString trait
345+ use alloc:: vec;
342346
343- /// Test for Task 10.2: Verifies serialization of a basic XdcFile.
344347 #[ test]
345348 fn test_save_xdc_to_string ( ) {
346- // 1. Create a public `XdcFile` struct
347349 let xdc_file = types:: XdcFile {
348350 header : types:: ProfileHeader {
349351 identification : "Test XDC" . to_string ( ) ,
@@ -405,50 +407,39 @@ mod tests {
405407 ..Default :: default ( )
406408 } ;
407409
408- // 2. Call `save_xdc_to_string`
409410 let xml_string = save_xdc_to_string ( & xdc_file) . unwrap ( ) ;
410411
411- // 3. Parse the string back using the internal models
412+ // Parse back to verify integrity
412413 let container: Iso15745ProfileContainer =
413414 quick_xml:: de:: from_str ( & xml_string) . expect ( "Serialized XML should be valid" ) ;
414415
415- // 4. Assert key fields
416416 assert_eq ! ( container. profile. len( ) , 2 ) ;
417417
418- // Check Device Profile
419418 let dev_profile = container. profile . get ( 0 ) . unwrap ( ) ;
420419 assert_eq ! ( dev_profile. profile_header. profile_name, "My Test Device" ) ;
421420
422421 let identity = dev_profile. profile_body . device_identity . as_ref ( ) . unwrap ( ) ;
423422 assert_eq ! ( identity. vendor_name. value, "MyVendor" ) ;
424423 assert_eq ! ( identity. vendor_id. as_ref( ) . unwrap( ) . value, "0x12345678" ) ;
425424 assert_eq ! ( identity. product_name. value, "MyProduct" ) ;
426- // UPDATED EXPECTATION: "0xABCD" to match serialization format
427425 assert_eq ! ( identity. product_id. as_ref( ) . unwrap( ) . value, "0xABCD" ) ;
428426 assert_eq ! ( identity. version[ 0 ] . value, "1.2" ) ;
429427
430- // Check Communication Profile
431428 let comm_profile = container. profile . get ( 1 ) . unwrap ( ) ;
432- assert_eq ! ( comm_profile. profile_header. profile_name, "My Test Device" ) ;
433-
434429 let app_layers = comm_profile
435430 . profile_body
436431 . application_layers
437432 . as_ref ( )
438433 . unwrap ( ) ;
439434 let obj_list = & app_layers. object_list . object ;
440435 assert_eq ! ( obj_list. len( ) , 2 ) ;
441-
442- // Check Object 0x1000
443436 assert_eq ! ( obj_list[ 0 ] . index, "1000" ) ;
444437 assert_eq ! ( obj_list[ 0 ] . name, "Device Type" ) ;
445438 assert_eq ! ( obj_list[ 0 ] . actual_value, Some ( "0x91010F00" . to_string( ) ) ) ;
446439 assert_eq ! (
447440 obj_list[ 0 ] . access_type,
448441 Some ( model:: app_layers:: ObjectAccessType :: Constant )
449442 ) ;
450-
451- // Check Object 0x1018
452443 assert_eq ! ( obj_list[ 1 ] . index, "1018" ) ;
453444 assert_eq ! ( obj_list[ 1 ] . sub_object. len( ) , 2 ) ;
454445 assert_eq ! ( obj_list[ 1 ] . sub_object[ 1 ] . name, "VendorID" ) ;
@@ -480,8 +471,6 @@ mod tests {
480471 map_access_type_to_model( PublicAccess :: Constant ) ,
481472 ModelAccess :: Constant
482473 ) ;
483-
484- // Test the non-obvious mappings
485474 assert_eq ! (
486475 map_access_type_to_model( PublicAccess :: ReadWriteInput ) ,
487476 ModelAccess :: ReadWrite
@@ -513,4 +502,4 @@ mod tests {
513502 assert_eq ! ( map_pdo_mapping_to_model( PublicPdo :: Tpdo ) , ModelPdo :: Tpdo ) ;
514503 assert_eq ! ( map_pdo_mapping_to_model( PublicPdo :: Rpdo ) , ModelPdo :: Rpdo ) ;
515504 }
516- }
505+ }
0 commit comments