serde_llsd/ser/
notation.rs1use crate::LLSDValue;
15use anyhow::Error;
16use chrono::{TimeZone};
17use base64::Engine;
18pub const LLSDNOTATIONPREFIX: &str = "<? llsd/notation ?>\n";
23pub const LLSDNOTATIONSENTINEL: &str = LLSDNOTATIONPREFIX;
25
26pub fn to_string(val: &LLSDValue) -> Result<String, Error> {
28 let mut writer = String::new();
29 writer.push_str(LLSDNOTATIONPREFIX); generate_value(&mut writer, val)?;
31 Ok(writer)
32}
33
34fn generate_value(writer: &mut String, val: &LLSDValue) -> Result<(), Error> {
48 match val {
50 LLSDValue::Undefined => writer.push('!'),
51 LLSDValue::Boolean(v) => writer.push(if *v { 'T' } else { 'F' }),
52 LLSDValue::String(v) => {
53 writer.push('"');
54 writer.push_str(&escape_quotes(v));
55 writer.push('"');
56 }
57 LLSDValue::URI(v) => {
58 writer.push('l');
59 writer.push('"');
60 writer.push_str(&escape_url(v));
61 writer.push('"');
62 }
63 LLSDValue::Integer(v) => {
64 writer.push('i');
65 writer.push_str(&format!("{}",v));
66 }
67 LLSDValue::Real(v) => {
68 writer.push('r');
69 writer.push_str(&format!("{}",v));
70 }
71 LLSDValue::UUID(v) => {
72 writer.push('u');
73 writer.push_str(&v.to_string());
74 }
75 LLSDValue::Binary(v) => {
76 writer.push('b');
77 writer.push('6');
78 writer.push('4');
79 writer.push('"');
80 writer.push_str(&base64::engine::general_purpose::STANDARD.encode(v));
81 writer.push('"');
82 }
83 LLSDValue::Date(v) => {
84 writer.push('d');
85 writer.push_str(&chrono::Utc
86 .timestamp_opt(*v, 0)
87 .earliest()
88 .unwrap() .to_rfc3339_opts(chrono::SecondsFormat::Secs, true))
90 }
91
92 LLSDValue::Map(v) => {
94 writer.push('{');
96 let mut first: bool = true;
98 for (key, value) in v {
99 if !first {
100 writer.push(',');
101 writer.push('\n');
102 }
103 first = false;
104 writer.push('\'');
105 writer.push_str(key);
106 writer.push('\'');
107 writer.push(':');
108 generate_value(writer, value)?;
109 }
110 writer.push('}');
111 }
112 LLSDValue::Array(v) => {
114 writer.push('[');
116 let mut first: bool = true;
118 for value in v {
119 if !first {
120 writer.push(',');
121 writer.push('\n');
122 }
123 first = false;
124 generate_value(writer, value)?;
125 }
126 writer.push(']');
127 }
128 };
129 Ok(())
130}
131
132fn escape_quotes(s: &str) -> String {
134 let mut writer = String::new();
135 for ch in s.chars() {
136 match ch {
137 '"' | '\\' => { writer.push('\\'); writer.push(ch) }
138 _ => writer.push(ch)
139 }
140 }
141 writer
142}
143
144fn escape_url(s: &str) -> String {
146 urlencoding::encode(s).to_string()
147}
148
149#[test]
151fn notationgentest1() {
152 const TESTXMLNAN: &str = r#"
153<?xml version="1.0" encoding="UTF-8"?>
154<llsd>
155<array>
156<real>nan</real>
157<real>0</real>
158<undef />
159</array>
160</llsd>
161"#;
162
163 const TESTXML1: &str = r#"
164<?xml version="1.0" encoding="UTF-8"?>
165<llsd>
166<map>
167 <key>region_id</key>
168 <uuid>67153d5b-3659-afb4-8510-adda2c034649</uuid>
169 <key>scale</key>
170 <string>one minute</string>
171 <key>simulator statistics</key>
172 <map>
173 <key>time dilation</key><real>0.9878624</real>
174 <key>sim fps</key><real>44.38898</real>
175 <key>pysics fps</key><real>44.38906</real>
176 <key>lsl instructions per second</key><real>0</real>
177 <key>total task count</key><real>4</real>
178 <key>active task count</key><real>0</real>
179 <key>active script count</key><real>4</real>
180 <key>main agent count</key><real>0</real>
181 <key>child agent count</key><real>0</real>
182 <key>inbound packets per second</key><real>1.228283</real>
183 <key>outbound packets per second</key><real>1.277508</real>
184 <key>pending downloads</key><real>0</real>
185 <key>pending uploads</key><real>0.0001096525</real>
186 <key>frame ms</key><real>0.7757886</real>
187 <key>net ms</key><real>0.3152919</real>
188 <key>sim other ms</key><real>0.1826937</real>
189 <key>sim physics ms</key><real>0.04323055</real>
190 <key>agent ms</key><real>0.01599029</real>
191 <key>image ms</key><real>0.01865955</real>
192 <key>script ms</key><real>0.1338836</real>
193 <!-- Comment - some additional test values -->
194 <key>hex number</key><binary encoding="base16">0fa1</binary>
195 <key>base64 number</key><binary>SGVsbG8gd29ybGQ=</binary>
196 <key>date</key><date>2006-02-01T14:29:53Z</date>
197 <key>array</key>
198 <array>
199 <boolean>false</boolean>
200 <integer>42</integer>
201 <undef/>
202 <uuid/>
203 <boolean>1</boolean>
204 </array>
205 </map>
206</map>
207</llsd>
208"#;
209
210 fn trytestcase(teststr: &str) {
211 let parsed1 = crate::de::xml::from_str(teststr).unwrap();
215 println!("Parse of {}: \n{:#?}", teststr, parsed1);
216 let generated = crate::ser::notation::to_string(&parsed1).unwrap();
218 println!("Generated Notation format:\n{}", generated);
219 }
227 trytestcase(TESTXML1);
228 {
230 let parsed1 = crate::de::xml::from_str(TESTXMLNAN).unwrap();
231 println!("Parse of {}: \n{:#?}", TESTXMLNAN, parsed1);
232 let generated = crate::ser::notation::to_string(&parsed1).unwrap();
234 println!("Generated Notation format:\n{}", generated);
235 }
236}