Skip to main content

tds_driver/tds/
xml.rs

1//! The XML containers
2use super::codec::{put_plp_utf16, Encode};
3use bytes::BytesMut;
4use std::sync::Arc;
5
6/// Provides information of the location for the schema.
7#[derive(Debug, Clone, PartialEq, Eq)]
8#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
9pub struct XmlSchema {
10    db_name: String,
11    owner: String,
12    collection: String,
13}
14
15impl XmlSchema {
16    pub(crate) fn new(
17        db_name: impl ToString,
18        owner: impl ToString,
19        collection: impl ToString,
20    ) -> Self {
21        Self {
22            db_name: db_name.to_string(),
23            owner: owner.to_string(),
24            collection: collection.to_string(),
25        }
26    }
27
28    /// Specifies the name of the database where the schema collection is defined.
29    pub fn db_name(&self) -> &str {
30        &self.db_name
31    }
32
33    /// Specifies the name of the relational schema containing the schema collection.
34    pub fn owner(&self) -> &str {
35        &self.owner
36    }
37
38    /// Specifies the name of the XML schema collection to which the type is
39    /// bound.
40    pub fn collection(&self) -> &str {
41        &self.collection
42    }
43}
44
45/// A representation of XML data in TDS. Holds the data as a UTF-8 string and
46/// and optional information about the schema.
47#[derive(Debug, Clone, PartialEq, Eq)]
48#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
49pub struct XmlData {
50    data: String,
51    schema: Option<Arc<XmlSchema>>,
52}
53
54impl XmlData {
55    /// Create a new XmlData with the given string. Validation of the XML data
56    /// happens in the database.
57    pub fn new(data: impl ToString) -> Self {
58        Self {
59            data: data.to_string(),
60            schema: None,
61        }
62    }
63
64    pub(crate) fn set_schema(&mut self, schema: Arc<XmlSchema>) {
65        self.schema = Some(schema);
66    }
67
68    /// Returns information about the schema of the XML file, if existing.
69    #[allow(clippy::option_as_ref_deref)]
70    pub fn schema(&self) -> Option<&XmlSchema> {
71        self.schema.as_ref().map(|s| &**s)
72    }
73
74    /// Takes the XML string out from the struct.
75    pub fn into_string(self) -> String {
76        self.data
77    }
78}
79
80impl std::fmt::Display for XmlData {
81    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
82        write!(f, "{}", self.data)
83    }
84}
85
86impl AsRef<str> for XmlData {
87    fn as_ref(&self) -> &str {
88        self.data.as_ref()
89    }
90}
91
92impl Encode<BytesMut> for XmlData {
93    fn encode(self, dst: &mut BytesMut) -> crate::Result<()> {
94        put_plp_utf16(dst, &self.data)
95    }
96}
97
98#[cfg(test)]
99mod tests {
100    use super::*;
101
102    #[test]
103    fn xml_schema_accessors() {
104        let schema = XmlSchema::new("db", "owner", "collection");
105        assert_eq!(schema.db_name(), "db");
106        assert_eq!(schema.owner(), "owner");
107        assert_eq!(schema.collection(), "collection");
108    }
109
110    #[test]
111    fn xml_schema_eq_and_clone() {
112        let a = XmlSchema::new("db", "owner", "collection");
113        let b = a.clone();
114        assert_eq!(a, b);
115    }
116
117    #[test]
118    fn xml_data_without_schema() {
119        let data = XmlData::new("<root/>");
120        assert!(data.schema().is_none());
121        assert_eq!(data.as_ref(), "<root/>");
122        assert_eq!(format!("{}", data), "<root/>");
123        assert_eq!(data.into_string(), "<root/>");
124    }
125
126    #[test]
127    fn xml_data_with_schema() {
128        let schema = Arc::new(XmlSchema::new("db", "owner", "collection"));
129        let mut data = XmlData::new("<a>1</a>");
130        data.set_schema(schema.clone());
131
132        let stored = data.schema().expect("schema present");
133        assert_eq!(stored.db_name(), "db");
134        assert_eq!(stored.owner(), "owner");
135        assert_eq!(stored.collection(), "collection");
136    }
137
138    #[test]
139    fn encode_writes_plp_header_and_backpatches_length() {
140        let mut buf = BytesMut::new();
141        XmlData::new("ab")
142            .encode(&mut buf)
143            .expect("encode succeeds");
144
145        // 8 (unknown-size marker) + 4 (length) + 2*2 (utf16 chars) + 4 (terminator)
146        assert_eq!(buf.len(), 8 + 4 + 4 + 4);
147
148        // unknown size marker
149        assert_eq!(&buf[0..8], &0xfffffffffffffffe_u64.to_le_bytes());
150        // backpatched length is number of chars * 2 bytes
151        assert_eq!(&buf[8..12], &(4u32).to_le_bytes());
152        // 'a' then 'b' as UTF-16LE
153        assert_eq!(&buf[12..16], &[b'a', 0, b'b', 0]);
154        // PLP terminator
155        assert_eq!(&buf[16..20], &(0u32).to_le_bytes());
156    }
157
158    #[test]
159    fn encode_empty_is_the_terminator_alone() {
160        let mut buf = BytesMut::new();
161        XmlData::new("").encode(&mut buf).unwrap();
162
163        let mut expected = 0xfffffffffffffffe_u64.to_le_bytes().to_vec();
164        expected.extend_from_slice(&0u32.to_le_bytes());
165        assert_eq!(&buf[..], &expected[..]);
166    }
167}