Compare commits
	
		
			11 Commits
		
	
	
		
			177fa8e5a7
			...
			353558737f
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 353558737f | ||
|  | 5cd09186fa | ||
|  | 8c7926f199 | ||
|  | 890fdb5629 | ||
|  | e783c29c47 | ||
|  | b89a55632b | ||
|  | a067b85ee2 | ||
|  | e4aff84acb | ||
|  | 3e416bedcc | ||
|  | f58d1d0de5 | ||
|  | 58d81c7a87 | 
| @ -1,6 +1,6 @@ | |||||||
| [package] | [package] | ||||||
| name = "yaserde-examples" | name = "yaserde-examples" | ||||||
| version = "0.10.0" | version = "0.11.1" | ||||||
| authors = ["Marc-Antoine Arnaud <maarnaud@media-io.com>"] | authors = ["Marc-Antoine Arnaud <maarnaud@media-io.com>"] | ||||||
| license = "MIT" | license = "MIT" | ||||||
| edition = "2018" | edition = "2018" | ||||||
| @ -8,4 +8,4 @@ description = "Examples for YaSerDe project" | |||||||
| documentation = "https://docs.rs/yaserde" | documentation = "https://docs.rs/yaserde" | ||||||
| 
 | 
 | ||||||
| [dependencies] | [dependencies] | ||||||
| yaserde = {version = "0.10.0", path = "../yaserde", features = ["yaserde_derive"] } | yaserde = {version = "0.11.1", path = "../yaserde", features = ["yaserde_derive"] } | ||||||
|  | |||||||
| @ -10,7 +10,7 @@ use yaserde::*; | |||||||
|   namespace = "html: http://www.w3.org/TR/REC-html40" |   namespace = "html: http://www.w3.org/TR/REC-html40" | ||||||
| )] | )] | ||||||
| struct Workbook { | struct Workbook { | ||||||
|   #[yaserde(rename = "Worksheet")] |   #[yaserde(rename = "Worksheet", prefix = "ss")] | ||||||
|   worksheet: Worksheet, |   worksheet: Worksheet, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -23,7 +23,7 @@ struct Workbook { | |||||||
|   namespace = "html: http://www.w3.org/TR/REC-html40" |   namespace = "html: http://www.w3.org/TR/REC-html40" | ||||||
| )] | )] | ||||||
| struct Worksheet { | struct Worksheet { | ||||||
|   #[yaserde(rename = "Table")] |   #[yaserde(rename = "Table", prefix = "ss")] | ||||||
|   table: Table, |   table: Table, | ||||||
|   #[yaserde(attribute, rename = "Name", prefix = "ss")] |   #[yaserde(attribute, rename = "Name", prefix = "ss")] | ||||||
|   ws_name: String, |   ws_name: String, | ||||||
| @ -53,7 +53,7 @@ struct Table { | |||||||
|   #[yaserde(attribute, rename = "DefaultRowHeight", prefix = "ss")] |   #[yaserde(attribute, rename = "DefaultRowHeight", prefix = "ss")] | ||||||
|   default_column_height: f32, |   default_column_height: f32, | ||||||
| 
 | 
 | ||||||
|   #[yaserde(rename = "Row")] |   #[yaserde(rename = "Row", prefix = "ss")] | ||||||
|   rows: Vec<Row>, |   rows: Vec<Row>, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										102
									
								
								examples/src/generic.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										102
									
								
								examples/src/generic.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,102 @@ | |||||||
|  | use yaserde::*; | ||||||
|  | 
 | ||||||
|  | #[derive(YaSerialize, YaDeserialize, Debug, Default, Clone, Eq, PartialEq)] | ||||||
|  | pub struct Header {} | ||||||
|  | 
 | ||||||
|  | #[derive(YaSerialize, YaDeserialize, Debug, Default, Clone, Eq, PartialEq)] | ||||||
|  | #[yaserde(
 | ||||||
|  |   rename = "Envelope", | ||||||
|  |   namespace = "s: http://schemas.xmlsoap.org/soap/envelope/", | ||||||
|  |   prefix = "s" | ||||||
|  | )] | ||||||
|  | pub struct SoapEnvelope<BODY> | ||||||
|  | where | ||||||
|  |   BODY: YaSerialize + YaDeserialize + Default, | ||||||
|  | { | ||||||
|  |   #[yaserde(rename = "encodingStyle", prefix = "s", attribute)] | ||||||
|  |   pub encoding_style: String, | ||||||
|  |   #[yaserde(rename = "u", prefix = "xmlns", attribute)] | ||||||
|  |   pub tnsattr: Option<String>, | ||||||
|  |   #[yaserde(rename = "urn", prefix = "xmlns", attribute)] | ||||||
|  |   pub urnattr: Option<String>, | ||||||
|  |   #[yaserde(rename = "xsi", prefix = "xmlns", attribute)] | ||||||
|  |   pub xsiattr: Option<String>, | ||||||
|  |   #[yaserde(rename = "Header", prefix = "s")] | ||||||
|  |   pub header: Option<Header>, | ||||||
|  |   #[yaserde(rename = "Body", prefix = "s")] | ||||||
|  |   pub body: BODY, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(YaSerialize, YaDeserialize, Debug, Default, Clone, Eq, PartialEq)] | ||||||
|  | #[yaserde(namespace = "u: urn:schemas-upnp-org:service:AVTransport:1")] | ||||||
|  | pub struct SoapPlay { | ||||||
|  |   #[yaserde(rename = "Play", prefix = "u", default)] | ||||||
|  |   pub body: Play, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(YaSerialize, YaDeserialize, Debug, Default, Clone, Eq, PartialEq)] | ||||||
|  | #[yaserde(rename = "Play", prefix = "u")] | ||||||
|  | pub struct Play { | ||||||
|  |   #[yaserde(flatten, default)] | ||||||
|  |   pub parameters: Play2, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(YaSerialize, YaDeserialize, Debug, Default, Clone, Eq, PartialEq)] | ||||||
|  | #[yaserde(
 | ||||||
|  |   rename = "Play", | ||||||
|  |   namespace = "u: urn:schemas-upnp-org:service:AVTransport:1", | ||||||
|  |   prefix = "u" | ||||||
|  | )] | ||||||
|  | pub struct Play2 { | ||||||
|  |   #[yaserde(rename = "InstanceID", default)] | ||||||
|  |   pub instance_id: i32, | ||||||
|  |   #[yaserde(rename = "Speed", default)] | ||||||
|  |   pub speed: i32, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(PrimitiveYaSerde, Debug, Default, Eq, PartialEq)] | ||||||
|  | struct Meters(i32); | ||||||
|  | 
 | ||||||
|  | #[test] | ||||||
|  | fn test_for_generic_newtype() { | ||||||
|  |   let a = SoapEnvelope { | ||||||
|  |     encoding_style: "".to_string(), | ||||||
|  |     tnsattr: None, | ||||||
|  |     urnattr: None, | ||||||
|  |     xsiattr: None, | ||||||
|  |     header: None, | ||||||
|  |     body: Meters(10), | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   let s = ser::to_string(&a).unwrap(); | ||||||
|  |   let b: SoapEnvelope<Meters> = de::from_str(&s).unwrap(); | ||||||
|  | 
 | ||||||
|  |   assert_eq!(a, b); | ||||||
|  |   println!("{:#?}", b); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[test] | ||||||
|  | fn test_for_generic_nested_struct() { | ||||||
|  |   let a = SoapEnvelope { | ||||||
|  |     encoding_style: "".to_string(), | ||||||
|  |     tnsattr: None, | ||||||
|  |     urnattr: None, | ||||||
|  |     xsiattr: None, | ||||||
|  |     header: None, | ||||||
|  |     body: SoapPlay { | ||||||
|  |       body: Play { | ||||||
|  |         parameters: Play2 { | ||||||
|  |           instance_id: 20, | ||||||
|  |           speed: 1, | ||||||
|  |         }, | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   let s = ser::to_string(&a).unwrap(); | ||||||
|  |   println!("{s}"); | ||||||
|  |   let b: SoapEnvelope<SoapPlay> = de::from_str(&s).unwrap(); | ||||||
|  | 
 | ||||||
|  |   assert_eq!(a, b); | ||||||
|  |   println!("{:#?}", b); | ||||||
|  | } | ||||||
| @ -1,4 +1,6 @@ | |||||||
| mod bbigras_namespace; | mod bbigras_namespace; | ||||||
| mod boscop; | mod boscop; | ||||||
|  | mod generic; | ||||||
| mod ln_dom; | mod ln_dom; | ||||||
|  | mod same_element_different_namespaces; | ||||||
| mod svd; | mod svd; | ||||||
|  | |||||||
							
								
								
									
										37
									
								
								examples/src/same_element_different_namespaces.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								examples/src/same_element_different_namespaces.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,37 @@ | |||||||
|  | // related to issue https://github.com/media-io/yaserde/issues/186
 | ||||||
|  | use yaserde::*; | ||||||
|  | 
 | ||||||
|  | #[derive(YaDeserialize, Debug, PartialEq)] | ||||||
|  | #[yaserde(
 | ||||||
|  |   namespace = "myns: http://my_namespace_1/", | ||||||
|  |   namespace = "ext: http://my_namespace_2/", | ||||||
|  |   prefix = "myns" | ||||||
|  | )] | ||||||
|  | pub struct ErrorType { | ||||||
|  |   #[yaserde(rename = "reasonCode", prefix = "myns")] | ||||||
|  |   pub reason_code: Option<u16>, | ||||||
|  |   #[yaserde(rename = "reasonCode", prefix = "ext")] | ||||||
|  |   pub ext_reason_code: Option<u16>, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[test] | ||||||
|  | fn same_element_different_namespaces() { | ||||||
|  |   use yaserde::de::from_str; | ||||||
|  | 
 | ||||||
|  |   let content = r#" | ||||||
|  |     <error_type xmlns="http://my_namespace_1/" xmlns:ext="http://my_namespace_2/"> | ||||||
|  |       <reasonCode>12</reasonCode> | ||||||
|  |       <ext:reasonCode>32</ext:reasonCode> | ||||||
|  |     </error_type> | ||||||
|  |   "#;
 | ||||||
|  | 
 | ||||||
|  |   let loaded: ErrorType = from_str(content).unwrap(); | ||||||
|  |   println!("{:?}", loaded); | ||||||
|  | 
 | ||||||
|  |   let reference = ErrorType { | ||||||
|  |     reason_code: Some(12), | ||||||
|  |     ext_reason_code: Some(32), | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   assert_eq!(loaded, reference); | ||||||
|  | } | ||||||
| @ -1,124 +1,123 @@ | |||||||
| use yaserde::YaSerialize; |  | ||||||
| 
 |  | ||||||
| #[derive(PartialEq, Debug, YaSerialize)] |  | ||||||
| struct CpuDef { |  | ||||||
|   #[yaserde(child)] |  | ||||||
|   name: String, |  | ||||||
|   #[yaserde(child)] |  | ||||||
|   revision: String, |  | ||||||
|   #[yaserde(child)] |  | ||||||
|   endian: String, // enum {LE, BE, ME}
 |  | ||||||
|   #[yaserde(child)] |  | ||||||
|   mpupresent: bool, |  | ||||||
|   #[yaserde(child)] |  | ||||||
|   fpupresent: bool, |  | ||||||
|   //#[yaserde(child)]
 |  | ||||||
|   //nvicpriobits: enum {8, 16, 32, 64, 128},
 |  | ||||||
|   #[yaserde(child)] |  | ||||||
|   vendorsystickconfig: bool, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #[derive(PartialEq, Debug, YaSerialize)] |  | ||||||
| struct Field { |  | ||||||
|   name: String, |  | ||||||
|   #[yaserde(child)] |  | ||||||
|   description: String, |  | ||||||
|   #[yaserde(child)] |  | ||||||
|   bitrange: String, |  | ||||||
|   #[yaserde(child)] |  | ||||||
|   access: String, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #[derive(PartialEq, Debug, YaSerialize)] |  | ||||||
| struct Register { |  | ||||||
|   #[yaserde(child)] |  | ||||||
|   name: String, |  | ||||||
|   #[yaserde(child)] |  | ||||||
|   description: String, |  | ||||||
|   #[yaserde(child)] |  | ||||||
|   addressoffset: String, |  | ||||||
|   #[yaserde(child)] |  | ||||||
|   size: u8, |  | ||||||
|   #[yaserde(child)] |  | ||||||
|   access: String, |  | ||||||
|   #[yaserde(child)] |  | ||||||
|   resetvalue: String, |  | ||||||
|   #[yaserde(child)] |  | ||||||
|   resetmask: String, |  | ||||||
|   #[yaserde(child)] |  | ||||||
|   fields: Vec<Field>, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #[derive(PartialEq, Debug, YaSerialize)] |  | ||||||
| struct Peripheral { |  | ||||||
|   #[yaserde(child)] |  | ||||||
|   name: String, |  | ||||||
|   #[yaserde(child)] |  | ||||||
|   version: String, |  | ||||||
|   #[yaserde(child)] |  | ||||||
|   description: String, |  | ||||||
|   #[yaserde(child)] |  | ||||||
|   groupname: String, |  | ||||||
|   #[yaserde(child)] |  | ||||||
|   baseaddress: String, |  | ||||||
|   #[yaserde(child)] |  | ||||||
|   size: u8, |  | ||||||
|   #[yaserde(child)] |  | ||||||
|   access: String, |  | ||||||
|   #[yaserde(child)] |  | ||||||
|   registers: Vec<Register>, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #[derive(PartialEq, Debug, YaSerialize)] |  | ||||||
| struct DevAttrs { |  | ||||||
|   #[yaserde(child)] |  | ||||||
|   vendor: String, |  | ||||||
|   #[yaserde(child)] |  | ||||||
|   vendorid: String, |  | ||||||
|   #[yaserde(child)] |  | ||||||
|   name: String, |  | ||||||
|   #[yaserde(child)] |  | ||||||
|   series: String, |  | ||||||
|   #[yaserde(child)] |  | ||||||
|   version: String, |  | ||||||
|   #[yaserde(child)] |  | ||||||
|   description: String, |  | ||||||
|   #[yaserde(child)] |  | ||||||
|   licensetext: String, |  | ||||||
|   #[yaserde(child)] |  | ||||||
|   cpu: CpuDef, |  | ||||||
|   #[yaserde(child)] |  | ||||||
|   addressunitbits: u8, |  | ||||||
|   #[yaserde(child)] |  | ||||||
|   width: u8, |  | ||||||
|   #[yaserde(child)] |  | ||||||
|   size: u8, |  | ||||||
|   #[yaserde(child)] |  | ||||||
|   access: String, |  | ||||||
|   #[yaserde(child)] |  | ||||||
|   resetvalue: String, |  | ||||||
|   #[yaserde(child)] |  | ||||||
|   resetmask: String, |  | ||||||
|   #[yaserde(child)] |  | ||||||
|   peripherals: Vec<Peripheral>, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #[derive(PartialEq, Debug, YaSerialize)] |  | ||||||
| #[yaserde(rename = "device")] |  | ||||||
| struct Device { |  | ||||||
|   #[yaserde(attribute)] |  | ||||||
|   schemaversion: String, |  | ||||||
|   #[yaserde(attribute)] |  | ||||||
|   xmlns: String, |  | ||||||
|   #[yaserde(attribute)] |  | ||||||
|   xsnonamespaceschemalocation: String, |  | ||||||
|   #[yaserde(child)] |  | ||||||
|   devattributes: DevAttrs, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #[test] | #[test] | ||||||
| fn parsing_svd() { | fn parsing_svd() { | ||||||
|   use std::fs; |   use std::fs; | ||||||
|  |   use yaserde::YaSerialize; | ||||||
|  | 
 | ||||||
|  |   #[derive(PartialEq, Debug, YaSerialize)] | ||||||
|  |   struct CpuDef { | ||||||
|  |     #[yaserde(child)] | ||||||
|  |     name: String, | ||||||
|  |     #[yaserde(child)] | ||||||
|  |     revision: String, | ||||||
|  |     #[yaserde(child)] | ||||||
|  |     endian: String, // enum {LE, BE, ME}
 | ||||||
|  |     #[yaserde(child)] | ||||||
|  |     mpupresent: bool, | ||||||
|  |     #[yaserde(child)] | ||||||
|  |     fpupresent: bool, | ||||||
|  |     //#[yaserde(child)]
 | ||||||
|  |     //nvicpriobits: enum {8, 16, 32, 64, 128},
 | ||||||
|  |     #[yaserde(child)] | ||||||
|  |     vendorsystickconfig: bool, | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   #[derive(PartialEq, Debug, YaSerialize)] | ||||||
|  |   struct Field { | ||||||
|  |     name: String, | ||||||
|  |     #[yaserde(child)] | ||||||
|  |     description: String, | ||||||
|  |     #[yaserde(child)] | ||||||
|  |     bitrange: String, | ||||||
|  |     #[yaserde(child)] | ||||||
|  |     access: String, | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   #[derive(PartialEq, Debug, YaSerialize)] | ||||||
|  |   struct Register { | ||||||
|  |     #[yaserde(child)] | ||||||
|  |     name: String, | ||||||
|  |     #[yaserde(child)] | ||||||
|  |     description: String, | ||||||
|  |     #[yaserde(child)] | ||||||
|  |     addressoffset: String, | ||||||
|  |     #[yaserde(child)] | ||||||
|  |     size: u8, | ||||||
|  |     #[yaserde(child)] | ||||||
|  |     access: String, | ||||||
|  |     #[yaserde(child)] | ||||||
|  |     resetvalue: String, | ||||||
|  |     #[yaserde(child)] | ||||||
|  |     resetmask: String, | ||||||
|  |     #[yaserde(child)] | ||||||
|  |     fields: Vec<Field>, | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   #[derive(PartialEq, Debug, YaSerialize)] | ||||||
|  |   struct Peripheral { | ||||||
|  |     #[yaserde(child)] | ||||||
|  |     name: String, | ||||||
|  |     #[yaserde(child)] | ||||||
|  |     version: String, | ||||||
|  |     #[yaserde(child)] | ||||||
|  |     description: String, | ||||||
|  |     #[yaserde(child)] | ||||||
|  |     groupname: String, | ||||||
|  |     #[yaserde(child)] | ||||||
|  |     baseaddress: String, | ||||||
|  |     #[yaserde(child)] | ||||||
|  |     size: u8, | ||||||
|  |     #[yaserde(child)] | ||||||
|  |     access: String, | ||||||
|  |     #[yaserde(child)] | ||||||
|  |     registers: Vec<Register>, | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   #[derive(PartialEq, Debug, YaSerialize)] | ||||||
|  |   struct DevAttrs { | ||||||
|  |     #[yaserde(child)] | ||||||
|  |     vendor: String, | ||||||
|  |     #[yaserde(child)] | ||||||
|  |     vendorid: String, | ||||||
|  |     #[yaserde(child)] | ||||||
|  |     name: String, | ||||||
|  |     #[yaserde(child)] | ||||||
|  |     series: String, | ||||||
|  |     #[yaserde(child)] | ||||||
|  |     version: String, | ||||||
|  |     #[yaserde(child)] | ||||||
|  |     description: String, | ||||||
|  |     #[yaserde(child)] | ||||||
|  |     licensetext: String, | ||||||
|  |     #[yaserde(child)] | ||||||
|  |     cpu: CpuDef, | ||||||
|  |     #[yaserde(child)] | ||||||
|  |     addressunitbits: u8, | ||||||
|  |     #[yaserde(child)] | ||||||
|  |     width: u8, | ||||||
|  |     #[yaserde(child)] | ||||||
|  |     size: u8, | ||||||
|  |     #[yaserde(child)] | ||||||
|  |     access: String, | ||||||
|  |     #[yaserde(child)] | ||||||
|  |     resetvalue: String, | ||||||
|  |     #[yaserde(child)] | ||||||
|  |     resetmask: String, | ||||||
|  |     #[yaserde(child)] | ||||||
|  |     peripherals: Vec<Peripheral>, | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   #[derive(PartialEq, Debug, YaSerialize)] | ||||||
|  |   #[yaserde(rename = "device")] | ||||||
|  |   struct Device { | ||||||
|  |     #[yaserde(attribute)] | ||||||
|  |     schemaversion: String, | ||||||
|  |     #[yaserde(attribute)] | ||||||
|  |     xmlns: String, | ||||||
|  |     #[yaserde(attribute)] | ||||||
|  |     xsnonamespaceschemalocation: String, | ||||||
|  |     #[yaserde(child)] | ||||||
|  |     devattributes: DevAttrs, | ||||||
|  |   } | ||||||
| 
 | 
 | ||||||
|   let register = Register { |   let register = Register { | ||||||
|         name: "PRCMD".to_string(), |         name: "PRCMD".to_string(), | ||||||
|  | |||||||
| @ -1,6 +1,6 @@ | |||||||
| [package] | [package] | ||||||
| name = "yaserde" | name = "yaserde" | ||||||
| version = "0.10.0" | version = "0.11.1" | ||||||
| authors = ["Marc-Antoine Arnaud <arnaud.marcantoine@gmail.com>"] | authors = ["Marc-Antoine Arnaud <arnaud.marcantoine@gmail.com>"] | ||||||
| description = "Serialization and deserialization library" | description = "Serialization and deserialization library" | ||||||
| keywords = ["Serialization", "Deserialization", "XML"] | keywords = ["Serialization", "Deserialization", "XML"] | ||||||
| @ -12,13 +12,13 @@ readme = "../README.md" | |||||||
| edition = "2018" | edition = "2018" | ||||||
| 
 | 
 | ||||||
| [dependencies] | [dependencies] | ||||||
| yaserde_derive = { version = "0.10.0", path = "../yaserde_derive", optional = true } | yaserde_derive = { version = "0.11.1", path = "../yaserde_derive", optional = true } | ||||||
| xml-rs = "0.8.3" | xml-rs = "0.8.3" | ||||||
| log = "0.4" | log = "0.4" | ||||||
| 
 | 
 | ||||||
| [dev-dependencies] | [dev-dependencies] | ||||||
| env_logger = "0.11.0" | env_logger = "0.11.0" | ||||||
| yaserde_derive = { version = "0.10.0", path = "../yaserde_derive" } | yaserde_derive = { version = "0.11.1", path = "../yaserde_derive" } | ||||||
| 
 | 
 | ||||||
| [badges] | [badges] | ||||||
| travis-ci = { repository = "media-io/yaserde" } | travis-ci = { repository = "media-io/yaserde" } | ||||||
|  | |||||||
| @ -94,6 +94,7 @@ use std::io::{Read, Write}; | |||||||
| use xml::writer::XmlEvent; | use xml::writer::XmlEvent; | ||||||
| 
 | 
 | ||||||
| pub mod de; | pub mod de; | ||||||
|  | pub mod primitives; | ||||||
| pub mod ser; | pub mod ser; | ||||||
| 
 | 
 | ||||||
| /// A **data structure** that can be deserialized from any data format supported by YaSerDe.
 | /// A **data structure** that can be deserialized from any data format supported by YaSerDe.
 | ||||||
|  | |||||||
							
								
								
									
										51
									
								
								yaserde/src/primitives.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								yaserde/src/primitives.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,51 @@ | |||||||
|  | use std::{io::Read, io::Write}; | ||||||
|  | 
 | ||||||
|  | use crate::{de, ser}; | ||||||
|  | 
 | ||||||
|  | pub fn serialize_primitives<S, W: Write>( | ||||||
|  |   self_bypass: &S, | ||||||
|  |   default_name: &str, | ||||||
|  |   writer: &mut ser::Serializer<W>, | ||||||
|  |   serialize_function: impl FnOnce(&S) -> String, | ||||||
|  | ) -> Result<(), String> { | ||||||
|  |   let name = writer | ||||||
|  |     .get_start_event_name() | ||||||
|  |     .unwrap_or_else(|| default_name.to_string()); | ||||||
|  | 
 | ||||||
|  |   if !writer.skip_start_end() { | ||||||
|  |     writer | ||||||
|  |       .write(xml::writer::XmlEvent::start_element(name.as_str())) | ||||||
|  |       .map_err(|_e| format!("Start element {name:?} write failed"))?; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   writer | ||||||
|  |     .write(xml::writer::XmlEvent::characters( | ||||||
|  |       serialize_function(self_bypass).as_str(), | ||||||
|  |     )) | ||||||
|  |     .map_err(|_e| format!("Element value {name:?} write failed"))?; | ||||||
|  | 
 | ||||||
|  |   if !writer.skip_start_end() { | ||||||
|  |     writer | ||||||
|  |       .write(xml::writer::XmlEvent::end_element()) | ||||||
|  |       .map_err(|_e| format!("End element {name:?} write failed"))?; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   Ok(()) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pub fn deserialize_primitives<S, R: Read>( | ||||||
|  |   reader: &mut de::Deserializer<R>, | ||||||
|  |   deserialize_function: impl FnOnce(&str) -> Result<S, String>, | ||||||
|  | ) -> Result<S, String> { | ||||||
|  |   if let Ok(xml::reader::XmlEvent::StartElement { .. }) = reader.peek() { | ||||||
|  |     reader.next_event()?; | ||||||
|  |   } else { | ||||||
|  |     return Err("Start element not found".to_string()); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   if let Ok(xml::reader::XmlEvent::Characters(ref text)) = reader.peek() { | ||||||
|  |     deserialize_function(text) | ||||||
|  |   } else { | ||||||
|  |     deserialize_function("") | ||||||
|  |   } | ||||||
|  | } | ||||||
| @ -1117,3 +1117,20 @@ fn de_nested_macro_rules() { | |||||||
| 
 | 
 | ||||||
|   float_attrs!(f32); |   float_attrs!(f32); | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | #[test] | ||||||
|  | fn de_strict() { | ||||||
|  |   init(); | ||||||
|  | 
 | ||||||
|  |   #[derive(PartialEq, Debug, YaDeserialize)] | ||||||
|  |   pub struct Struct { | ||||||
|  |     id: i32, | ||||||
|  |   } | ||||||
|  |   let xml_content = r#"<?xml version="1.0" encoding="utf-8"?>
 | ||||||
|  |       <Struct> | ||||||
|  |           <id>123</id> | ||||||
|  |           <NonExistentAttrShouldCrash></NonExistentAttrShouldCrash> | ||||||
|  |         </Struct>"#;
 | ||||||
|  |   let load: Result<Struct, String> = from_str(xml_content); | ||||||
|  |   assert!(load.is_err()); | ||||||
|  | } | ||||||
|  | |||||||
							
								
								
									
										41
									
								
								yaserde/tests/generic.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								yaserde/tests/generic.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,41 @@ | |||||||
|  | #[macro_use] | ||||||
|  | extern crate yaserde; | ||||||
|  | 
 | ||||||
|  | use yaserde::{YaDeserialize, YaSerialize}; | ||||||
|  | 
 | ||||||
|  | fn init() { | ||||||
|  |   let _ = env_logger::builder().is_test(true).try_init(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[test] | ||||||
|  | fn generic() { | ||||||
|  |   init(); | ||||||
|  | 
 | ||||||
|  |   #[derive(Debug, PartialEq, YaDeserialize, YaSerialize)] | ||||||
|  |   #[yaserde(rename = "base")] | ||||||
|  |   pub struct Base<G> | ||||||
|  |   where | ||||||
|  |     G: YaSerialize + YaDeserialize + Default, | ||||||
|  |   { | ||||||
|  |     background: G, | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   #[derive(Debug, Default, PartialEq, YaDeserialize, YaSerialize)] | ||||||
|  |   pub struct Generic { | ||||||
|  |     #[yaserde(attribute)] | ||||||
|  |     color: String, | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   let content = r#"<base><background color="blue" /></base>"#; | ||||||
|  |   let model = Base { | ||||||
|  |     background: Generic { | ||||||
|  |       color: "blue".to_string(), | ||||||
|  |     }, | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   serialize_and_validate!(model, content); | ||||||
|  | 
 | ||||||
|  |   log::debug!("deserialize_and_validate @ {}:{}", file!(), line!()); | ||||||
|  |   let loaded: Result<Base<Generic>, String> = yaserde::de::from_str(content); | ||||||
|  |   assert_eq!(loaded, Ok(model)); | ||||||
|  | } | ||||||
| @ -47,7 +47,7 @@ fn skip_serializing_if_for_struct() { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fn check_f32_function(&self, value: &f32) -> bool { |     fn check_f32_function(&self, value: &f32) -> bool { | ||||||
|       (value - 0.0).abs() < std::f32::EPSILON |       (value - 0.0).abs() < f32::EPSILON | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
| @ -104,7 +104,7 @@ fn skip_serializing_if_for_struct_attributes() { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fn check_f32_function(&self, value: &f32) -> bool { |     fn check_f32_function(&self, value: &f32) -> bool { | ||||||
|       (value - 0.0).abs() < std::f32::EPSILON |       (value - 0.0).abs() < f32::EPSILON | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,6 +1,6 @@ | |||||||
| [package] | [package] | ||||||
| name = "yaserde_derive" | name = "yaserde_derive" | ||||||
| version = "0.10.0" | version = "0.11.1" | ||||||
| authors = ["Marc-Antoine Arnaud <arnaud.marcantoine@gmail.com>"] | authors = ["Marc-Antoine Arnaud <arnaud.marcantoine@gmail.com>"] | ||||||
| description = "Serialization and deserialization macros" | description = "Serialization and deserialization macros" | ||||||
| keywords = ["Serialization", "Deserialization"] | keywords = ["Serialization", "Deserialization"] | ||||||
| @ -12,7 +12,7 @@ readme = "../README.md" | |||||||
| edition = "2018" | edition = "2018" | ||||||
| 
 | 
 | ||||||
| [dependencies] | [dependencies] | ||||||
| heck = "0.4.0" | heck = "0.5" | ||||||
| syn = { version = "~1.0", features = ["visit", "extra-traits"] } | syn = { version = "~1.0", features = ["visit", "extra-traits"] } | ||||||
| proc-macro2 = "~1.0" | proc-macro2 = "~1.0" | ||||||
| quote = "~1.0" | quote = "~1.0" | ||||||
|  | |||||||
| @ -90,6 +90,13 @@ impl YaSerdeField { | |||||||
|       }, |       }, | ||||||
|     ); |     ); | ||||||
| 
 | 
 | ||||||
|  |     let prefix = self | ||||||
|  |       .attributes | ||||||
|  |       .prefix | ||||||
|  |       .clone() | ||||||
|  |       .map(|p| format!("{}_", p.to_upper_camel_case())) | ||||||
|  |       .unwrap_or_default(); | ||||||
|  | 
 | ||||||
|     let attribute = self |     let attribute = self | ||||||
|       .attributes |       .attributes | ||||||
|       .attribute |       .attribute | ||||||
| @ -98,7 +105,8 @@ impl YaSerdeField { | |||||||
| 
 | 
 | ||||||
|     Ident::new( |     Ident::new( | ||||||
|       &format!( |       &format!( | ||||||
|         "__Visitor_{attribute}{}_{}", |         "__Visitor_{attribute}{}{}_{}", | ||||||
|  |         prefix, | ||||||
|         label.replace('.', "_").to_upper_camel_case(), |         label.replace('.', "_").to_upper_camel_case(), | ||||||
|         struct_id |         struct_id | ||||||
|       ), |       ), | ||||||
| @ -130,6 +138,20 @@ impl YaSerdeField { | |||||||
|       .map(|skip_serializing_if| Ident::new(skip_serializing_if, self.get_span())) |       .map(|skip_serializing_if| Ident::new(skip_serializing_if, self.get_span())) | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   pub fn prefix_namespace(&self, root_attributes: &YaSerdeAttribute) -> String { | ||||||
|  |     root_attributes | ||||||
|  |       .namespaces | ||||||
|  |       .iter() | ||||||
|  |       .find_map(|(prefix, namespace)| { | ||||||
|  |         if self.attributes.prefix.eq(prefix) { | ||||||
|  |           Some(namespace.clone()) | ||||||
|  |         } else { | ||||||
|  |           None | ||||||
|  |         } | ||||||
|  |       }) | ||||||
|  |       .unwrap_or_default() | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   pub fn get_namespace_matching( |   pub fn get_namespace_matching( | ||||||
|     &self, |     &self, | ||||||
|     root_attributes: &YaSerdeAttribute, |     root_attributes: &YaSerdeAttribute, | ||||||
| @ -261,7 +283,7 @@ impl From<&syn::PathSegment> for Field { | |||||||
|           return Field::from(&path.path); |           return Field::from(&path.path); | ||||||
|         } |         } | ||||||
|         Some(syn::GenericArgument::Type(syn::Type::Group(syn::TypeGroup { elem, .. }))) => { |         Some(syn::GenericArgument::Type(syn::Type::Group(syn::TypeGroup { elem, .. }))) => { | ||||||
|           if let syn::Type::Path(ref group) = elem.as_ref() { |           if let Path(ref group) = elem.as_ref() { | ||||||
|             return Field::from(&group.path); |             return Field::from(&group.path); | ||||||
|           } |           } | ||||||
|         } |         } | ||||||
|  | |||||||
| @ -1,13 +1,14 @@ | |||||||
| use crate::common::{Field, YaSerdeAttribute, YaSerdeField}; | use crate::common::{Field, YaSerdeAttribute, YaSerdeField}; | ||||||
| use proc_macro2::{Span, TokenStream}; | use proc_macro2::{Span, TokenStream}; | ||||||
| use quote::quote; | use quote::quote; | ||||||
| use syn::{DataEnum, Fields, Ident}; | use syn::{DataEnum, Fields, Generics, Ident}; | ||||||
| 
 | 
 | ||||||
| pub fn parse( | pub fn parse( | ||||||
|   data_enum: &DataEnum, |   data_enum: &DataEnum, | ||||||
|   name: &Ident, |   name: &Ident, | ||||||
|   root: &str, |   root: &str, | ||||||
|   root_attributes: &YaSerdeAttribute, |   root_attributes: &YaSerdeAttribute, | ||||||
|  |   generics: &Generics, | ||||||
| ) -> TokenStream { | ) -> TokenStream { | ||||||
|   let namespaces_matching = root_attributes.get_namespace_matching( |   let namespaces_matching = root_attributes.get_namespace_matching( | ||||||
|     &None, |     &None, | ||||||
| @ -23,6 +24,7 @@ pub fn parse( | |||||||
|     .collect(); |     .collect(); | ||||||
| 
 | 
 | ||||||
|   let flatten = root_attributes.flatten; |   let flatten = root_attributes.flatten; | ||||||
|  |   let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); | ||||||
| 
 | 
 | ||||||
|   let element_name = if let Some(tag) = &root_attributes.tag { |   let element_name = if let Some(tag) = &root_attributes.tag { | ||||||
|     quote! { |     quote! { | ||||||
| @ -39,7 +41,7 @@ pub fn parse( | |||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   quote! { |   quote! { | ||||||
|     impl ::yaserde::YaDeserialize for #name { |     impl #impl_generics ::yaserde::YaDeserialize for #name #ty_generics #where_clause { | ||||||
|       #[allow(unused_variables)] |       #[allow(unused_variables)] | ||||||
|       fn deserialize<R: ::std::io::Read>( |       fn deserialize<R: ::std::io::Read>( | ||||||
|         reader: &mut ::yaserde::de::Deserializer<R>, |         reader: &mut ::yaserde::de::Deserializer<R>, | ||||||
|  | |||||||
| @ -2,13 +2,15 @@ use super::build_default_value::{build_default_value, build_default_vec_value}; | |||||||
| use crate::common::{Field, YaSerdeAttribute, YaSerdeField}; | use crate::common::{Field, YaSerdeAttribute, YaSerdeField}; | ||||||
| use proc_macro2::{Span, TokenStream}; | use proc_macro2::{Span, TokenStream}; | ||||||
| use quote::quote; | use quote::quote; | ||||||
| use syn::{DataStruct, Ident}; | use syn::{DataStruct, Generics, Ident}; | ||||||
| 
 | 
 | ||||||
| pub fn parse( | pub fn parse( | ||||||
|   data_struct: &DataStruct, |   data_struct: &DataStruct, | ||||||
|   name: &Ident, |   name: &Ident, | ||||||
|  |   root_namespace: &str, | ||||||
|   root: &str, |   root: &str, | ||||||
|   root_attributes: &YaSerdeAttribute, |   root_attributes: &YaSerdeAttribute, | ||||||
|  |   generics: &Generics, | ||||||
| ) -> TokenStream { | ) -> TokenStream { | ||||||
|   let namespaces_matching = root_attributes.get_namespace_matching( |   let namespaces_matching = root_attributes.get_namespace_matching( | ||||||
|     &None, |     &None, | ||||||
| @ -49,6 +51,17 @@ pub fn parse( | |||||||
|     .fields |     .fields | ||||||
|     .iter() |     .iter() | ||||||
|     .map(|field| YaSerdeField::new(field.clone())) |     .map(|field| YaSerdeField::new(field.clone())) | ||||||
|  |     .filter(|field| { | ||||||
|  |       if field.is_attribute() { | ||||||
|  |         return true; | ||||||
|  |       }; | ||||||
|  |       match field.get_type() { | ||||||
|  |         Field::FieldVec { data_type } => !matches!(*data_type, Field::FieldStruct { .. }), | ||||||
|  |         Field::FieldOption { data_type } => !matches!(*data_type, Field::FieldStruct { .. }), | ||||||
|  |         Field::FieldStruct { .. } => false, | ||||||
|  |         _ => true, | ||||||
|  |       } | ||||||
|  |     }) | ||||||
|     .filter_map(|field| { |     .filter_map(|field| { | ||||||
|       let struct_visitor = |struct_name: syn::Path| { |       let struct_visitor = |struct_name: syn::Path| { | ||||||
|         let struct_id: String = struct_name |         let struct_id: String = struct_name | ||||||
| @ -136,9 +149,11 @@ pub fn parse( | |||||||
|       let value_label = field.get_value_label(); |       let value_label = field.get_value_label(); | ||||||
|       let label_name = field.renamed_label_without_namespace(); |       let label_name = field.renamed_label_without_namespace(); | ||||||
| 
 | 
 | ||||||
|  |       let namespace = field.prefix_namespace(root_attributes); | ||||||
|  | 
 | ||||||
|       let visit_struct = |struct_name: syn::Path, action: TokenStream| { |       let visit_struct = |struct_name: syn::Path, action: TokenStream| { | ||||||
|         Some(quote! { |         Some(quote! { | ||||||
|           #label_name => { |           (#namespace, #label_name) => { | ||||||
|             if depth == 0 { |             if depth == 0 { | ||||||
|               // Don't count current struct's StartElement as substruct's StartElement
 |               // Don't count current struct's StartElement as substruct's StartElement
 | ||||||
|               let _root = reader.next_event(); |               let _root = reader.next_event(); | ||||||
| @ -369,9 +384,10 @@ pub fn parse( | |||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   let flatten = root_attributes.flatten; |   let flatten = root_attributes.flatten; | ||||||
|  |   let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); | ||||||
| 
 | 
 | ||||||
|   quote! { |   quote! { | ||||||
|     impl ::yaserde::YaDeserialize for #name { |     impl #impl_generics ::yaserde::YaDeserialize for #name #ty_generics #where_clause { | ||||||
|       #[allow(unused_variables)] |       #[allow(unused_variables)] | ||||||
|       fn deserialize<R: ::std::io::Read>( |       fn deserialize<R: ::std::io::Read>( | ||||||
|         reader: &mut ::yaserde::de::Deserializer<R>, |         reader: &mut ::yaserde::de::Deserializer<R>, | ||||||
| @ -404,15 +420,20 @@ pub fn parse( | |||||||
|           ); |           ); | ||||||
|           match event { |           match event { | ||||||
|             ::yaserde::__xml::reader::XmlEvent::StartElement{ref name, ref attributes, ..} => { |             ::yaserde::__xml::reader::XmlEvent::StartElement{ref name, ref attributes, ..} => { | ||||||
|               if depth == 0 && name.local_name == #root { |               let namespace = name.namespace.clone().unwrap_or_default(); | ||||||
|  |               if depth == 0 && name.local_name == #root && namespace.as_str() == #root_namespace { | ||||||
|                 // Consume root element. We must do this first. In the case it shares a name with a child element, we don't
 |                 // Consume root element. We must do this first. In the case it shares a name with a child element, we don't
 | ||||||
|                 // want to prematurely match the child element below.
 |                 // want to prematurely match the child element below.
 | ||||||
|                 let event = reader.next_event()?; |                 let event = reader.next_event()?; | ||||||
|                 #write_unused |                 #write_unused | ||||||
|               } else { |               } else { | ||||||
|                 match name.local_name.as_str() { | 
 | ||||||
|  |                 match (namespace.as_str(), name.local_name.as_str()) { | ||||||
|                   #call_visitors |                   #call_visitors | ||||||
|                   _ => { |                   _ => { | ||||||
|  |                     ::yaserde::__derive_trace!("SKIPPINGSKIPPING  Skipping element {:?}", name.local_name); | ||||||
|  |                     return Err(format!("Found unauthorized element {}", name.local_name)); | ||||||
|  | 
 | ||||||
|                     let event = reader.next_event()?; |                     let event = reader.next_event()?; | ||||||
|                     #write_unused |                     #write_unused | ||||||
| 
 | 
 | ||||||
| @ -480,8 +501,10 @@ fn build_call_visitor( | |||||||
|     quote!(name.local_name.as_str()), |     quote!(name.local_name.as_str()), | ||||||
|   ); |   ); | ||||||
| 
 | 
 | ||||||
|  |   let namespace = field.prefix_namespace(root_attributes); | ||||||
|  | 
 | ||||||
|   Some(quote! { |   Some(quote! { | ||||||
|     #label_name => { |     (#namespace, #label_name) => { | ||||||
|       let visitor = #visitor_label{}; |       let visitor = #visitor_label{}; | ||||||
| 
 | 
 | ||||||
|       #namespaces_matching |       #namespaces_matching | ||||||
|  | |||||||
| @ -10,21 +10,34 @@ pub fn expand_derive_deserialize(ast: &syn::DeriveInput) -> Result<TokenStream, | |||||||
|   let name = &ast.ident; |   let name = &ast.ident; | ||||||
|   let attrs = &ast.attrs; |   let attrs = &ast.attrs; | ||||||
|   let data = &ast.data; |   let data = &ast.data; | ||||||
|  |   let generics = &ast.generics; | ||||||
| 
 | 
 | ||||||
|   let root_attributes = YaSerdeAttribute::parse(attrs); |   let root_attributes = YaSerdeAttribute::parse(attrs); | ||||||
| 
 | 
 | ||||||
|   let root_name = format!( |   let root_name = root_attributes.xml_element_name(name); | ||||||
|     "{}{}", |   let root_namespace = root_attributes | ||||||
|     root_attributes.prefix_namespace(), |     .namespaces | ||||||
|     root_attributes.xml_element_name(name) |     .iter() | ||||||
|   ); |     .find_map(|(prefix, namespace)| { | ||||||
|  |       if root_attributes.prefix.eq(prefix) { | ||||||
|  |         Some(namespace.clone()) | ||||||
|  |       } else { | ||||||
|  |         None | ||||||
|  |       } | ||||||
|  |     }) | ||||||
|  |     .unwrap_or_default(); | ||||||
| 
 | 
 | ||||||
|   let impl_block = match *data { |   let impl_block = match *data { | ||||||
|     syn::Data::Struct(ref data_struct) => { |     syn::Data::Struct(ref data_struct) => expand_struct::parse( | ||||||
|       expand_struct::parse(data_struct, name, &root_name, &root_attributes) |       data_struct, | ||||||
|     } |       name, | ||||||
|  |       &root_namespace, | ||||||
|  |       &root_name, | ||||||
|  |       &root_attributes, | ||||||
|  |       generics, | ||||||
|  |     ), | ||||||
|     syn::Data::Enum(ref data_enum) => { |     syn::Data::Enum(ref data_enum) => { | ||||||
|       expand_enum::parse(data_enum, name, &root_name, &root_attributes) |       expand_enum::parse(data_enum, name, &root_name, &root_attributes, generics) | ||||||
|     } |     } | ||||||
|     syn::Data::Union(ref _data_union) => unimplemented!(), |     syn::Data::Union(ref _data_union) => unimplemented!(), | ||||||
|   }; |   }; | ||||||
|  | |||||||
| @ -5,9 +5,13 @@ extern crate proc_macro; | |||||||
| 
 | 
 | ||||||
| mod common; | mod common; | ||||||
| mod de; | mod de; | ||||||
|  | mod primitives; | ||||||
| mod ser; | mod ser; | ||||||
| 
 | 
 | ||||||
|  | use primitives::{hexbinary_serde, primitive_serde, primitive_yaserde}; | ||||||
| use proc_macro::TokenStream; | use proc_macro::TokenStream; | ||||||
|  | use proc_macro2::TokenStream as TokenStream2; | ||||||
|  | use quote::quote; | ||||||
| 
 | 
 | ||||||
| #[proc_macro_derive(YaDeserialize, attributes(yaserde))] | #[proc_macro_derive(YaDeserialize, attributes(yaserde))] | ||||||
| pub fn derive_deserialize(input: TokenStream) -> TokenStream { | pub fn derive_deserialize(input: TokenStream) -> TokenStream { | ||||||
| @ -26,3 +30,37 @@ pub fn derive_serialize(input: TokenStream) -> TokenStream { | |||||||
|     Err(msg) => panic!("{}", msg), |     Err(msg) => panic!("{}", msg), | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | // Serialize & Deserialize a struct using it's UpperHex implementation
 | ||||||
|  | #[proc_macro_derive(HexBinaryYaSerde)] | ||||||
|  | pub fn derive_hexbinary(input: TokenStream) -> TokenStream { | ||||||
|  |   let serde: TokenStream2 = hexbinary_serde(input.clone()).into(); | ||||||
|  |   let yaserde: TokenStream2 = primitive_yaserde(input).into(); | ||||||
|  | 
 | ||||||
|  |   quote! { | ||||||
|  |       use ::std::str::FromStr as _; | ||||||
|  |       #serde | ||||||
|  |       #yaserde | ||||||
|  |   } | ||||||
|  |   .into() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Serialize & Deserialize a primitive newtype by generating a FromStr & Display implementation
 | ||||||
|  | #[proc_macro_derive(PrimitiveYaSerde)] | ||||||
|  | pub fn derive_primitive(input: TokenStream) -> TokenStream { | ||||||
|  |   let serde: TokenStream2 = primitive_serde(input.clone()).into(); | ||||||
|  |   let yaserde: TokenStream2 = primitive_yaserde(input).into(); | ||||||
|  | 
 | ||||||
|  |   quote! { | ||||||
|  |       use ::std::str::FromStr as _; | ||||||
|  |       #serde | ||||||
|  |       #yaserde | ||||||
|  |   } | ||||||
|  |   .into() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Serialize & Deserialize a type using it's existing FromStr & Display implementation
 | ||||||
|  | #[proc_macro_derive(DefaultYaSerde)] | ||||||
|  | pub fn derive_default(input: TokenStream) -> TokenStream { | ||||||
|  |   primitive_yaserde(input) | ||||||
|  | } | ||||||
|  | |||||||
							
								
								
									
										118
									
								
								yaserde_derive/src/primitives.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										118
									
								
								yaserde_derive/src/primitives.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,118 @@ | |||||||
|  | // Adds YaSerialize and YaDeserialize implementations for types that support FromStr and Display traits.
 | ||||||
|  | // Code originally from `xsd-parser-rs`
 | ||||||
|  | 
 | ||||||
|  | use proc_macro::TokenStream; | ||||||
|  | use proc_macro2::TokenStream as TokenStream2; | ||||||
|  | use quote::quote; | ||||||
|  | use syn::{parse_macro_input, DeriveInput}; | ||||||
|  | 
 | ||||||
|  | pub fn primitive_yaserde(input: TokenStream) -> TokenStream { | ||||||
|  |   let ast = parse_macro_input!(input as DeriveInput); | ||||||
|  | 
 | ||||||
|  |   let struct_name = &ast.ident; | ||||||
|  |   let struct_name_literal = &ast.ident.to_string(); | ||||||
|  | 
 | ||||||
|  |   let serde = quote! { | ||||||
|  |       impl ::yaserde::YaSerialize for #struct_name { | ||||||
|  |           fn serialize<W: ::std::io::Write>( | ||||||
|  |               &self, | ||||||
|  |               writer: &mut ::yaserde::ser::Serializer<W>, | ||||||
|  |           ) -> ::std::result::Result<(), ::std::string::String> { | ||||||
|  |             ::yaserde::primitives::serialize_primitives( | ||||||
|  |                   self, | ||||||
|  |                   #struct_name_literal, | ||||||
|  |                   writer, |s| s.to_string(), | ||||||
|  |               ) | ||||||
|  |           } | ||||||
|  | 
 | ||||||
|  |           fn serialize_attributes( | ||||||
|  |               &self, | ||||||
|  |               attributes: ::std::vec::Vec<::yaserde::__xml::attribute::OwnedAttribute>, | ||||||
|  |               namespace: ::yaserde::__xml::namespace::Namespace, | ||||||
|  |           ) -> ::std::result::Result< | ||||||
|  |               ( | ||||||
|  |                   ::std::vec::Vec<::yaserde::__xml::attribute::OwnedAttribute>, | ||||||
|  |                   ::yaserde::__xml::namespace::Namespace, | ||||||
|  |               ), | ||||||
|  |               ::std::string::String, | ||||||
|  |           > { | ||||||
|  |               Ok((attributes, namespace)) | ||||||
|  |           } | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       impl ::yaserde::YaDeserialize for #struct_name { | ||||||
|  |           fn deserialize<R: ::std::io::Read>( | ||||||
|  |               reader: &mut ::yaserde::de::Deserializer<R>, | ||||||
|  |           ) -> ::std::result::Result<Self, ::std::string::String> { | ||||||
|  |               ::yaserde::primitives::deserialize_primitives( | ||||||
|  |                   reader, | ||||||
|  |                   |s| #struct_name::from_str(s).map_err(|e| e.to_string()), | ||||||
|  |               ) | ||||||
|  |           } | ||||||
|  |       } | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   serde.into() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pub fn hexbinary_serde(input: TokenStream) -> TokenStream { | ||||||
|  |   let first = input.clone(); | ||||||
|  |   let DeriveInput { ident, .. } = parse_macro_input!(first); | ||||||
|  |   // Calculate number digits to determine whether leading zero should be added
 | ||||||
|  |   quote! { | ||||||
|  |     impl std::fmt::Display for #ident { | ||||||
|  |       fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||||||
|  |         write!(f, "{:02X}", self.0) | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     impl ::std::str::FromStr for #ident { | ||||||
|  |       type Err = ::std::string::String; | ||||||
|  | 
 | ||||||
|  |       fn from_str(s: &::std::primitive::str) -> ::std::result::Result<Self, Self::Err> { | ||||||
|  |         Self::from_bits( | ||||||
|  |           s.parse() | ||||||
|  |               .map_err(|_| String::from("Failed to parse Bitflag integer"))?, | ||||||
|  |       ) | ||||||
|  |       .ok_or(String::from("Unknown bits were set in Bitflag")) | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   .into() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pub fn primitive_serde(input: TokenStream) -> TokenStream { | ||||||
|  |   let first = input.clone(); | ||||||
|  |   let ref di @ DeriveInput { ref ident, .. } = parse_macro_input!(first); | ||||||
|  |   let fromstr = extract_full_path(di).unwrap(); | ||||||
|  |   quote! { | ||||||
|  |     impl std::fmt::Display for #ident { | ||||||
|  |       fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||||||
|  |         write!(f, "{}", self.0) | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     impl ::std::str::FromStr for #ident { | ||||||
|  |       type Err = ::std::string::String; | ||||||
|  | 
 | ||||||
|  |       fn from_str(s: &::std::primitive::str) -> ::std::result::Result<Self, Self::Err> { | ||||||
|  |         Ok(#ident(#fromstr)) | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   .into() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn extract_full_path(ast: &syn::DeriveInput) -> Result<TokenStream2, syn::Error> { | ||||||
|  |   if let syn::Data::Struct(data_struct) = &ast.data { | ||||||
|  |     if let syn::Fields::Unnamed(fields) = &data_struct.fields { | ||||||
|  |       if let Some(syn::Type::Path(path)) = &fields.unnamed.first().map(|f| &f.ty) { | ||||||
|  |         return Ok( | ||||||
|  |           quote! { <#path as ::std::str::FromStr>::from_str(s).map_err(|e| e.to_string())? }, | ||||||
|  |         ); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   Err(syn::Error::new_spanned(ast, "Unable to extract full path")) | ||||||
|  | } | ||||||
| @ -2,15 +2,16 @@ use crate::common::{Field, YaSerdeAttribute, YaSerdeField}; | |||||||
| use crate::ser::{implement_serializer::implement_serializer, label::build_label_name}; | use crate::ser::{implement_serializer::implement_serializer, label::build_label_name}; | ||||||
| use proc_macro2::TokenStream; | use proc_macro2::TokenStream; | ||||||
| use quote::quote; | use quote::quote; | ||||||
| use syn::DataEnum; |  | ||||||
| use syn::Fields; | use syn::Fields; | ||||||
| use syn::Ident; | use syn::Ident; | ||||||
|  | use syn::{DataEnum, Generics}; | ||||||
| 
 | 
 | ||||||
| pub fn serialize( | pub fn serialize( | ||||||
|   data_enum: &DataEnum, |   data_enum: &DataEnum, | ||||||
|   name: &Ident, |   name: &Ident, | ||||||
|   root: &str, |   root: &str, | ||||||
|   root_attributes: &YaSerdeAttribute, |   root_attributes: &YaSerdeAttribute, | ||||||
|  |   generics: &Generics, | ||||||
| ) -> TokenStream { | ) -> TokenStream { | ||||||
|   let inner_enum_inspector = inner_enum_inspector(data_enum, name, root_attributes); |   let inner_enum_inspector = inner_enum_inspector(data_enum, name, root_attributes); | ||||||
| 
 | 
 | ||||||
| @ -108,6 +109,7 @@ pub fn serialize( | |||||||
|     quote!(match self { |     quote!(match self { | ||||||
|       #inner_enum_inspector |       #inner_enum_inspector | ||||||
|     }), |     }), | ||||||
|  |     generics, | ||||||
|   ) |   ) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -3,14 +3,15 @@ use crate::common::{Field, YaSerdeAttribute, YaSerdeField}; | |||||||
| use crate::ser::{element::*, implement_serializer::implement_serializer}; | use crate::ser::{element::*, implement_serializer::implement_serializer}; | ||||||
| use proc_macro2::TokenStream; | use proc_macro2::TokenStream; | ||||||
| use quote::quote; | use quote::quote; | ||||||
| use syn::DataStruct; |  | ||||||
| use syn::Ident; | use syn::Ident; | ||||||
|  | use syn::{DataStruct, Generics}; | ||||||
| 
 | 
 | ||||||
| pub fn serialize( | pub fn serialize( | ||||||
|   data_struct: &DataStruct, |   data_struct: &DataStruct, | ||||||
|   name: &Ident, |   name: &Ident, | ||||||
|   root: &str, |   root: &str, | ||||||
|   root_attributes: &YaSerdeAttribute, |   root_attributes: &YaSerdeAttribute, | ||||||
|  |   generics: &Generics, | ||||||
| ) -> TokenStream { | ) -> TokenStream { | ||||||
|   let append_attributes: TokenStream = data_struct |   let append_attributes: TokenStream = data_struct | ||||||
|     .fields |     .fields | ||||||
| @ -348,5 +349,6 @@ pub fn serialize( | |||||||
|     root_attributes, |     root_attributes, | ||||||
|     append_attributes, |     append_attributes, | ||||||
|     struct_inspector, |     struct_inspector, | ||||||
|  |     generics, | ||||||
|   ) |   ) | ||||||
| } | } | ||||||
|  | |||||||
| @ -3,6 +3,7 @@ use crate::ser::namespace::generate_namespaces_definition; | |||||||
| use proc_macro2::Ident; | use proc_macro2::Ident; | ||||||
| use proc_macro2::TokenStream; | use proc_macro2::TokenStream; | ||||||
| use quote::quote; | use quote::quote; | ||||||
|  | use syn::Generics; | ||||||
| 
 | 
 | ||||||
| pub fn implement_serializer( | pub fn implement_serializer( | ||||||
|   name: &Ident, |   name: &Ident, | ||||||
| @ -10,12 +11,15 @@ pub fn implement_serializer( | |||||||
|   attributes: &YaSerdeAttribute, |   attributes: &YaSerdeAttribute, | ||||||
|   append_attributes: TokenStream, |   append_attributes: TokenStream, | ||||||
|   inner_inspector: TokenStream, |   inner_inspector: TokenStream, | ||||||
|  |   generics: &Generics, | ||||||
| ) -> TokenStream { | ) -> TokenStream { | ||||||
|   let namespaces_definition = generate_namespaces_definition(attributes); |   let namespaces_definition = generate_namespaces_definition(attributes); | ||||||
|   let flatten = attributes.flatten; |   let flatten = attributes.flatten; | ||||||
| 
 | 
 | ||||||
|  |   let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); | ||||||
|  | 
 | ||||||
|   quote! { |   quote! { | ||||||
|     impl ::yaserde::YaSerialize for #name { |     impl #impl_generics ::yaserde::YaSerialize for #name #ty_generics #where_clause { | ||||||
|       #[allow(unused_variables)] |       #[allow(unused_variables)] | ||||||
|       fn serialize<W: ::std::io::Write>( |       fn serialize<W: ::std::io::Write>( | ||||||
|         &self, |         &self, | ||||||
|  | |||||||
| @ -13,6 +13,7 @@ pub fn expand_derive_serialize(ast: &syn::DeriveInput) -> Result<TokenStream, St | |||||||
|   let name = &ast.ident; |   let name = &ast.ident; | ||||||
|   let attrs = &ast.attrs; |   let attrs = &ast.attrs; | ||||||
|   let data = &ast.data; |   let data = &ast.data; | ||||||
|  |   let generics = &ast.generics; | ||||||
| 
 | 
 | ||||||
|   let root_attributes = YaSerdeAttribute::parse(attrs); |   let root_attributes = YaSerdeAttribute::parse(attrs); | ||||||
| 
 | 
 | ||||||
| @ -24,10 +25,10 @@ pub fn expand_derive_serialize(ast: &syn::DeriveInput) -> Result<TokenStream, St | |||||||
| 
 | 
 | ||||||
|   let impl_block = match *data { |   let impl_block = match *data { | ||||||
|     syn::Data::Struct(ref data_struct) => { |     syn::Data::Struct(ref data_struct) => { | ||||||
|       expand_struct::serialize(data_struct, name, &root_name, &root_attributes) |       expand_struct::serialize(data_struct, name, &root_name, &root_attributes, generics) | ||||||
|     } |     } | ||||||
|     syn::Data::Enum(ref data_enum) => { |     syn::Data::Enum(ref data_enum) => { | ||||||
|       expand_enum::serialize(data_enum, name, &root_name, &root_attributes) |       expand_enum::serialize(data_enum, name, &root_name, &root_attributes, generics) | ||||||
|     } |     } | ||||||
|     syn::Data::Union(ref _data_union) => unimplemented!(), |     syn::Data::Union(ref _data_union) => unimplemented!(), | ||||||
|   }; |   }; | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user