libhaystack/haystack/encoding/zinc/
encode.rs1use crate::haystack::val::{
6 Bool, Column, Coord, Date, DateTime, Dict, GRID_FORMAT_VERSION, Grid, List, Marker, Na, Number,
7 Ref, Remove, Str, Symbol, Time, Uri, Value, XStr,
8};
9use chrono::SecondsFormat;
10use std::fmt::Display;
11
12pub trait ToZinc {
14 fn to_zinc<W: std::io::Write>(&self, writer: &mut W) -> Result<()>;
15
16 fn to_zinc_string(&self) -> Result<String> {
27 let mut output = Vec::new();
28 self.to_zinc(&mut output)?;
29 Ok(String::from_utf8(output)?)
30 }
31}
32
33pub fn to_zinc_string(value: &Value) -> Result<String> {
44 let mut output = Vec::new();
45 value.to_zinc(&mut output)?;
46 Ok(String::from_utf8(output)?)
47}
48
49#[derive(PartialEq, PartialOrd, Clone, Debug)]
50enum InnerGrid {
51 Yes,
52 No,
53}
54
55trait ZincEncode: ToZinc {
57 fn zinc_encode<W: std::io::Write>(&self, writer: &mut W, in_grid: InnerGrid) -> Result<()>;
58}
59
60pub type Result<T> = std::result::Result<T, Error>;
61
62#[derive(Clone, Debug, PartialEq, Eq)]
63pub enum Error {
64 Message(String),
65}
66
67impl Display for Error {
68 fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
69 match self {
70 Error::Message(msg) => formatter.write_str(msg),
71 }
72 }
73}
74
75impl std::error::Error for Error {}
76
77impl From<std::fmt::Error> for Error {
78 fn from(_: std::fmt::Error) -> Self {
79 Error::from("Format error.")
80 }
81}
82
83impl From<std::io::Error> for Error {
84 fn from(_: std::io::Error) -> Self {
85 Error::from("IO error.")
86 }
87}
88
89impl From<&str> for Error {
90 fn from(msg: &str) -> Self {
91 Error::Message(String::from(msg))
92 }
93}
94
95impl From<std::string::FromUtf8Error> for Error {
96 fn from(_: std::string::FromUtf8Error) -> Self {
97 Error::from("Utf8 encoding error.")
98 }
99}
100
101fn write_str<W: std::io::Write>(writer: &mut W, s: &str) -> Result<()> {
102 let bytes = s.as_bytes();
103 writer.write_all(bytes)?;
104 Ok(())
105}
106
107impl ToZinc for Marker {
108 fn to_zinc<W: std::io::Write>(&self, writer: &mut W) -> Result<()> {
109 writer.write_all(b"M")?;
110 Ok(())
111 }
112}
113
114impl ToZinc for Remove {
115 fn to_zinc<W: std::io::Write>(&self, writer: &mut W) -> Result<()> {
116 writer.write_all(b"R")?;
117 Ok(())
118 }
119}
120
121impl ToZinc for Na {
122 fn to_zinc<W: std::io::Write>(&self, writer: &mut W) -> Result<()> {
123 writer.write_all(b"NA")?;
124 Ok(())
125 }
126}
127
128impl ToZinc for Bool {
129 fn to_zinc<W: std::io::Write>(&self, writer: &mut W) -> Result<()> {
130 if self.value {
131 writer.write_all(b"T")?
132 } else {
133 writer.write_all(b"F")?
134 }
135 Ok(())
136 }
137}
138
139impl ToZinc for Number {
140 fn to_zinc<W: std::io::Write>(&self, writer: &mut W) -> Result<()> {
141 if self.value.is_nan() {
142 writer.write_all(b"NaN")?
143 } else if self.value.is_infinite() {
144 let sign = if self.value.is_sign_negative() {
145 "-"
146 } else {
147 ""
148 };
149 writer.write_fmt(format_args!("{sign}INF"))?
150 } else if let Some(unit) = &self.unit {
151 writer.write_fmt(format_args!("{value}{unit}", value = self.value))?
152 } else {
153 writer.write_fmt(format_args!("{}", self.value))?
154 }
155 Ok(())
156 }
157}
158
159impl ToZinc for Date {
160 fn to_zinc<W: std::io::Write>(&self, writer: &mut W) -> Result<()> {
161 write_str(writer, &self.to_string())?;
162 Ok(())
163 }
164}
165
166impl ToZinc for Time {
167 fn to_zinc<W: std::io::Write>(&self, writer: &mut W) -> Result<()> {
168 write_str(writer, &self.to_string())?;
169 Ok(())
170 }
171}
172
173impl ToZinc for DateTime {
174 fn to_zinc<W: std::io::Write>(&self, writer: &mut W) -> Result<()> {
175 if self.is_utc() {
176 write_str(writer, &self.to_rfc3339_opts(SecondsFormat::AutoSi, true))?;
177 } else {
178 writer.write_fmt(format_args!(
179 "{} {}",
180 &self.to_rfc3339_opts(SecondsFormat::AutoSi, true),
181 &self.timezone_short_name()
182 ))?
183 }
184 Ok(())
185 }
186}
187
188impl ToZinc for Str {
189 fn to_zinc<W: std::io::Write>(&self, writer: &mut W) -> Result<()> {
190 writer.write_all(b"\"")?;
191 let mut buf = [0; 4];
192 for c in self.value.chars() {
193 if c < ' ' || c == '"' || c == '\\' {
194 match c {
195 '"' => writer.write_all(br#"\""#)?,
196 '\t' => writer.write_all(br"\t")?,
197 '\r' => writer.write_all(br"\r")?,
198 '\n' => writer.write_all(br"\n")?,
199 '\\' => writer.write_all(br"\\")?,
200 _ => writer.write_fmt(format_args!("\\u{:04x}", c as u32))?,
201 }
202 } else if c == '$' {
203 writer.write_all(br"\$")?
204 } else {
205 let chunk = c.encode_utf8(&mut buf);
206 writer.write_fmt(format_args!("{chunk}"))?
207 }
208 }
209 writer.write_all(b"\"")?;
210 Ok(())
211 }
212}
213
214impl ToZinc for Ref {
215 fn to_zinc<W: std::io::Write>(&self, writer: &mut W) -> Result<()> {
216 if let Some(dis) = &self.dis {
217 writer.write_fmt(format_args!("@{} \"{}\"", self.value, dis))?
218 } else {
219 writer.write_fmt(format_args!("@{}", self.value))?
220 }
221 Ok(())
222 }
223}
224
225impl ToZinc for Symbol {
226 fn to_zinc<W: std::io::Write>(&self, writer: &mut W) -> Result<()> {
227 writer.write_fmt(format_args!("^{}", self.value))?;
228 Ok(())
229 }
230}
231
232impl ToZinc for Uri {
233 fn to_zinc<W: std::io::Write>(&self, writer: &mut W) -> Result<()> {
234 writer.write_all(b"`")?;
235 for c in self.value.chars() {
236 if c < ' ' {
237 continue;
238 }
239 match c {
240 '`' => writer.write_all(br"\`")?,
241 '\\' => writer.write_all(br"\\")?,
242 '\x20'..='\x7e' => writer.write_all(&[c as u8])?,
243 _ => writer.write_fmt(format_args!("\\u{:04x}", c as u32))?,
244 }
245 }
246 writer.write_all(b"`")?;
247 Ok(())
248 }
249}
250
251impl ToZinc for XStr {
252 fn to_zinc<W: std::io::Write>(&self, writer: &mut W) -> Result<()> {
253 writer.write_fmt(format_args!(
254 "{}{}(\"{}\")",
255 self.r#type[0..1].to_uppercase(),
256 &self.r#type[1..],
257 self.value
258 ))?;
259 Ok(())
260 }
261}
262
263impl ToZinc for Coord {
264 fn to_zinc<W: std::io::Write>(&self, writer: &mut W) -> Result<()> {
265 writer.write_fmt(format_args!("C({},{})", self.lat, self.long))?;
266 Ok(())
267 }
268}
269
270impl ToZinc for Grid {
271 fn to_zinc<W: std::io::Write>(&self, writer: &mut W) -> Result<()> {
272 self.zinc_encode(writer, InnerGrid::No)
273 }
274}
275
276impl ZincEncode for Grid {
277 fn zinc_encode<W: std::io::Write>(&self, writer: &mut W, in_grid: InnerGrid) -> Result<()> {
278 if in_grid == InnerGrid::Yes {
279 writer.write_all(b"<<\n")?;
280 }
281
282 writer.write_fmt(format_args!("ver:\"{GRID_FORMAT_VERSION}\"\n"))?;
283
284 if let Some(meta) = &self.meta {
286 write_dict_tags(writer, meta, b" ")?;
287 }
288
289 if self.is_empty() {
290 writer.write_all(b"empty\n")?;
292 } else {
293 for (i, col) in self.columns.iter().enumerate() {
295 col.to_zinc(writer)?;
296 if i < self.columns.len() - 1 {
297 writer.write_all(b",")?;
298 }
299 }
300 writer.write_all(b"\n")?;
301 for row in &self.rows {
303 for (i, col) in self.columns.iter().enumerate() {
305 if let Some(tag) = row.get(&col.name) {
306 tag.zinc_encode(writer, InnerGrid::Yes)?;
307 }
308 if i < self.columns.len() - 1 {
309 writer.write_all(b",")?;
310 }
311 }
312 writer.write_all(b"\n")?;
313 }
314 }
315 if in_grid == InnerGrid::Yes {
316 writer.write_all(b">>")?;
317 } else {
318 writer.write_all(b"\n")?;
319 }
320 Ok(())
321 }
322}
323
324impl ToZinc for List {
325 fn to_zinc<W: std::io::Write>(&self, writer: &mut W) -> Result<()> {
326 writer.write_all(b"[")?;
327 for (i, el) in self.iter().enumerate() {
328 el.zinc_encode(writer, InnerGrid::Yes)?;
329 if i < self.len() - 1 {
330 writer.write_all(b",")?;
331 }
332 }
333 writer.write_all(b"]")?;
334 Ok(())
335 }
336}
337
338impl ToZinc for Dict {
339 fn to_zinc<W: std::io::Write>(&self, writer: &mut W) -> Result<()> {
340 writer.write_all(b"{")?;
341 write_dict_tags(writer, self, b",")?;
342 writer.write_all(b"}")?;
343 Ok(())
344 }
345}
346
347impl ToZinc for Value {
349 fn to_zinc<W: std::io::Write>(&self, writer: &mut W) -> Result<()> {
350 self.zinc_encode(writer, InnerGrid::No)
351 }
352}
353
354impl ZincEncode for Value {
357 fn zinc_encode<W: std::io::Write>(&self, writer: &mut W, in_grid: InnerGrid) -> Result<()> {
358 match self {
359 Value::Null => writer.write_all(b"N")?,
360
361 Value::Remove => Remove.to_zinc(writer)?,
362
363 Value::Marker => Marker.to_zinc(writer)?,
364
365 Value::Bool(val) => val.to_zinc(writer)?,
366
367 Value::Na => Na.to_zinc(writer)?,
368
369 Value::Number(val) => val.to_zinc(writer)?,
370
371 Value::Str(val) => val.to_zinc(writer)?,
372
373 Value::Ref(val) => val.to_zinc(writer)?,
374
375 Value::Uri(val) => val.to_zinc(writer)?,
376
377 Value::Symbol(val) => val.to_zinc(writer)?,
378
379 Value::Date(val) => val.to_zinc(writer)?,
380
381 Value::Time(val) => val.to_zinc(writer)?,
382
383 Value::DateTime(val) => val.to_zinc(writer)?,
384
385 Value::Coord(val) => val.to_zinc(writer)?,
386
387 Value::XStr(val) => val.to_zinc(writer)?,
388
389 Value::List(val) => val.to_zinc(writer)?,
390
391 Value::Dict(val) => val.to_zinc(writer)?,
392
393 Value::Grid(val) => val.zinc_encode(writer, in_grid)?,
394 }
395 Ok(())
396 }
397}
398impl ToZinc for Column {
400 fn to_zinc<W: std::io::Write>(&self, writer: &mut W) -> Result<()> {
401 write_str(writer, &self.name)?;
402 if let Some(meta) = &self.meta {
403 write_dict_tags(writer, meta, b" ")?;
404 }
405 Ok(())
406 }
407}
408
409fn write_dict_tags<W: std::io::Write>(
410 writer: &mut W,
411 dict: &Dict,
412 separator: &[u8; 1],
413) -> Result<()> {
414 for (pos, (k, v)) in dict.iter().enumerate() {
415 write_str(writer, k)?;
416 if !v.is_marker() {
417 writer.write_all(b":")?;
418 v.zinc_encode(writer, InnerGrid::Yes)?;
419 }
420 if pos < dict.len() - 1 {
421 writer.write_all(separator)?;
422 }
423 }
424 Ok(())
425}