Skip to main content

kdb_plus_fixed/ipc/
format.rs

1//++++++++++++++++++++++++++++++++++++++++++++++++++//
2// >> Load Libraries
3//++++++++++++++++++++++++++++++++++++++++++++++++++//
4
5use super::*;
6use std::fmt;
7
8//++++++++++++++++++++++++++++++++++++++++++++++++++//
9// >> Implementation
10//++++++++++++++++++++++++++++++++++++++++++++++++++//
11
12//%% Display %%//vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv/
13
14impl fmt::Display for K {
15    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
16        match self.0.qtype {
17            qtype::ERROR => write!(f, "'{}", self.get_error_string().unwrap()),
18            _ => {
19                let mut stream = String::new();
20                if let Some(precision) = f.precision() {
21                    put_q(self, &mut stream, precision);
22                } else {
23                    put_q(self, &mut stream, 0);
24                }
25                write!(f, "{}", stream)
26            }
27        }
28    }
29}
30
31//++++++++++++++++++++++++++++++++++++++++++++++++++//
32// >> Private Functions
33//++++++++++++++++++++++++++++++++++++++++++++++++++//
34
35fn put_bool(boolean: G, stream: &mut String) {
36    stream.push(match boolean {
37        0 => '0',
38        _ => '1',
39    });
40}
41
42fn put_guid(guid: U, stream: &mut String) {
43    let strguid = guid
44        .iter()
45        .map(|byte| format!("{:02x}", byte))
46        .collect::<String>();
47    stream.push_str(
48        format!(
49            "{}-{}-{}-{}-{}",
50            &strguid[0..8],
51            &strguid[8..12],
52            &strguid[12..16],
53            &strguid[16..20],
54            &strguid[20..32]
55        )
56        .as_str(),
57    );
58}
59
60fn put_byte(byte: G, stream: &mut String) {
61    stream.push_str(format!("{:02x}", byte).as_str());
62}
63
64fn put_short(short: H, stream: &mut String) {
65    if short == qnull_base::H {
66        stream.push_str("0N")
67    } else if short == qinf_base::H {
68        stream.push_str("0W")
69    } else if short == qninf_base::H {
70        stream.push_str("-0W")
71    } else {
72        stream.push_str(format!("{}", short).as_str())
73    }
74}
75
76fn put_int(int: I, stream: &mut String) {
77    if int == qnull_base::I {
78        stream.push_str("0N")
79    } else if int == qinf_base::I {
80        stream.push_str("0W")
81    } else if int == qninf_base::I {
82        stream.push_str("-0W")
83    } else {
84        stream.push_str(format!("{}", int).as_str())
85    }
86}
87
88fn put_long(long: J, stream: &mut String) {
89    if long == qnull_base::J {
90        stream.push_str("0N")
91    } else if long == qinf_base::J {
92        stream.push_str("0W")
93    } else if long == qninf_base::J {
94        stream.push_str("-0W")
95    } else {
96        stream.push_str(format!("{}", long).as_str())
97    }
98}
99
100fn put_real(real: E, stream: &mut String, precision: usize) {
101    if real.is_nan() {
102        stream.push_str("0N")
103    } else if real == qinf_base::E {
104        stream.push_str("0W")
105    } else if real == qninf_base::E {
106        stream.push_str("-0W")
107    } else if precision != 0 {
108        stream.push_str(format!("{1:.*}", precision, real).as_str())
109    } else {
110        stream.push_str(format!("{}", real).as_str())
111    }
112}
113
114fn put_float(float: F, stream: &mut String, precision: usize) {
115    if float.is_nan() {
116        stream.push_str("0n")
117    } else if float.is_infinite() && float.is_sign_negative() {
118        stream.push_str("-0w")
119    } else if float.is_infinite() {
120        stream.push_str("0w")
121    } else if precision != 0 {
122        stream.push_str(format!("{1:.*}", precision, float).as_str())
123    } else {
124        stream.push_str(format!("{}", float).as_str())
125    }
126}
127
128fn put_symbol(symbol: &str, stream: &mut String) {
129    stream.push('`');
130    stream.push_str(symbol);
131}
132
133/// Put formatted timestamp value to a stream and return if 'p' suffix is necessaery in case of atom.
134fn put_timestamp(nanos: J, stream: &mut String) -> bool {
135    if nanos == qnull_base::J {
136        stream.push_str("0N");
137        true
138    } else if nanos == qinf_base::J {
139        stream.push_str("0W");
140        true
141    } else if nanos == qninf_base::J {
142        stream.push_str("-0W");
143        true
144    } else {
145        stream.push_str(
146            q_timestamp_to_datetime(nanos)
147                .format("%Y.%m.%dD%H:%M:%S%.9f")
148                .to_string()
149                .as_str(),
150        );
151        false
152    }
153}
154
155fn put_month(months: I, stream: &mut String) {
156    if months == qnull_base::I {
157        stream.push_str("0N")
158    } else if months == qinf_base::I {
159        stream.push_str("0W")
160    } else if months == qninf_base::I {
161        stream.push_str("-0W")
162    } else {
163        match months.signum() {
164            -1 => {
165                let year = 2000 + months.signum() * (1 + months.abs() / 12);
166                let month = 12 + (1 + months % 12);
167                stream.push_str(format!("{}.{:02}", year, month).as_str())
168            }
169            _ => {
170                stream.push_str(format!("{}.{:02}", 2000 + months / 12, 1 + (months % 12)).as_str())
171            }
172        }
173    }
174}
175
176/// Put formatted date value to a stream and return if 'd' suffix is necessaery in case of atom.
177fn put_date(days: I, stream: &mut String) -> bool {
178    if days == qnull_base::I {
179        stream.push_str("0N");
180        true
181    } else if days == qinf_base::I {
182        stream.push_str("0W");
183        true
184    } else if days == qninf_base::I {
185        stream.push_str("-0W");
186        true
187    } else {
188        stream.push_str(
189            Utc.timestamp_nanos(ONE_DAY_NANOS * (days + KDB_DAY_OFFSET) as i64)
190                .date_naive()
191                .format("%Y.%m.%d")
192                .to_string()
193                .as_str(),
194        );
195        false
196    }
197}
198
199/// Put formatted datetime value to a stream and return if 'z' suffix is necessaery in case of atom.
200fn put_datetime(days: F, stream: &mut String) -> bool {
201    if days.is_nan() {
202        stream.push_str("0N");
203        true
204    } else if days.is_infinite() && days.is_sign_negative() {
205        stream.push_str("-0W");
206        true
207    } else if days.is_infinite() {
208        stream.push_str("0W");
209        true
210    } else {
211        stream.push_str(
212            q_datetime_to_datetime(days)
213                .format("%Y.%m.%dT%H:%M:%S%.3f")
214                .to_string()
215                .as_str(),
216        );
217        false
218    }
219}
220
221/// Put formatted timespan value to a stream and return if 'n' suffix is necessaery in case of atom.
222fn put_timespan(nanos: J, stream: &mut String) -> bool {
223    if nanos == qnull_base::J {
224        stream.push_str("0N");
225        true
226    } else if nanos == qinf_base::J {
227        stream.push_str("0W");
228        true
229    } else if nanos == qninf_base::J {
230        stream.push_str("-0W");
231        true
232    } else {
233        let duration = q_timespan_to_duration(nanos);
234        if duration.num_nanoseconds().unwrap() < 0 {
235            stream.push_str(
236                format!(
237                    "-{}D{:02}:{:02}:{:02}.{:09}",
238                    duration.num_days().abs(),
239                    duration.num_hours().abs() % 24,
240                    duration.num_minutes().abs() % 60,
241                    duration.num_seconds().abs() % 60,
242                    duration.num_nanoseconds().unwrap_or(0).abs() % 1_000_000_000_i64
243                )
244                .as_str(),
245            );
246        } else {
247            stream.push_str(
248                format!(
249                    "{}D{:02}:{:02}:{:02}.{:09}",
250                    duration.num_days(),
251                    duration.num_hours() % 24,
252                    duration.num_minutes() % 60,
253                    duration.num_seconds() % 60,
254                    duration.num_nanoseconds().unwrap_or(0) % 1_000_000_000_i64
255                )
256                .as_str(),
257            );
258        }
259        false
260    }
261}
262
263/// Put formatted minute value to a stream and return if 'u' suffix is necessaery in case of atom.
264fn put_minute(minutes: I, stream: &mut String) -> bool {
265    if minutes == qnull_base::I {
266        stream.push_str("0N");
267        true
268    } else if minutes == qinf_base::I {
269        stream.push_str("0W");
270        true
271    } else if minutes == qninf_base::I {
272        stream.push_str("-0W");
273        true
274    } else {
275        let duration = q_minute_to_duration(minutes);
276        if duration.num_minutes() < 0 {
277            stream.push_str(
278                format!(
279                    "-{:02}:{:02}",
280                    duration.num_hours().abs() % 24,
281                    duration.num_minutes().abs() % 60
282                )
283                .as_str(),
284            );
285        } else {
286            stream.push_str(
287                format!(
288                    "{:02}:{:02}",
289                    duration.num_hours() % 24,
290                    duration.num_minutes() % 60
291                )
292                .as_str(),
293            );
294        }
295        false
296    }
297}
298
299/// Put formatted second value to a stream and return if 'v' suffix is necessaery in case of atom.
300fn put_second(seconds: I, stream: &mut String) -> bool {
301    if seconds == qnull_base::I {
302        stream.push_str("0N");
303        true
304    } else if seconds == qinf_base::I {
305        stream.push_str("0W");
306        true
307    } else if seconds == qninf_base::I {
308        stream.push_str("-0W");
309        true
310    } else {
311        let duration = q_second_to_duration(seconds);
312        if duration.num_seconds() < 0 {
313            stream.push_str(
314                format!(
315                    "-{:02}:{:02}:{:02}",
316                    duration.num_hours().abs() % 24,
317                    duration.num_minutes().abs() % 60,
318                    duration.num_seconds().abs() % 60
319                )
320                .as_str(),
321            );
322        } else {
323            stream.push_str(
324                format!(
325                    "{:02}:{:02}:{:02}",
326                    duration.num_hours() % 24,
327                    duration.num_minutes() % 60,
328                    duration.num_seconds() % 60
329                )
330                .as_str(),
331            );
332        }
333        false
334    }
335}
336
337/// Put formatted time value to a stream and return if 't' suffix is necessaery in case of atom.
338fn put_time(millis: I, stream: &mut String) -> bool {
339    if millis == qnull_base::I {
340        stream.push_str("0N");
341        true
342    } else if millis == qinf_base::I {
343        stream.push_str("0W");
344        true
345    } else if millis == qninf_base::I {
346        stream.push_str("-0W");
347        true
348    } else {
349        let duration = q_time_to_duration(millis);
350        if duration.num_milliseconds() < 0 {
351            stream.push_str(
352                format!(
353                    "-{:02}:{:02}:{:02}.{:03}",
354                    duration.num_hours().abs() % 24,
355                    duration.num_minutes().abs() % 60,
356                    duration.num_seconds().abs() % 60,
357                    duration.num_milliseconds().abs() % 1000_i64
358                )
359                .as_str(),
360            );
361        } else {
362            stream.push_str(
363                format!(
364                    "{:02}:{:02}:{:02}.{:03}",
365                    duration.num_hours() % 24,
366                    duration.num_minutes() % 60,
367                    duration.num_seconds() % 60,
368                    duration.num_milliseconds() % 1000_i64
369                )
370                .as_str(),
371            );
372        }
373        false
374    }
375}
376
377fn put_attribute(attribute: i8, stream: &mut String) {
378    match attribute {
379        qattribute::SORTED => stream.push_str("`s#"),
380        qattribute::PARTED => stream.push_str("`p#"),
381        qattribute::UNIQUE => stream.push_str("`u#"),
382        qattribute::GROUPED => stream.push_str("`g#"),
383        // Nothing to do
384        _ => {}
385    }
386}
387
388fn put_bool_list(list: &[G], stream: &mut String) {
389    let size = list.len();
390    if size == 0 {
391        stream.push_str("`bool$()");
392    } else {
393        if size == 1 {
394            stream.push(',');
395        }
396        list.iter().for_each(|element| {
397            put_bool(*element, stream);
398        });
399        stream.push('b');
400    }
401}
402
403fn put_guid_list(list: &[U], stream: &mut String) {
404    let size = list.len();
405    if size == 0 {
406        stream.push_str("`guid$()");
407    } else {
408        if size == 1 {
409            stream.push(',');
410        }
411        for item in list.iter().take(size - 1) {
412            put_guid(*item, stream);
413            stream.push(' ');
414        }
415        put_guid(list[size - 1], stream);
416    }
417}
418
419fn put_byte_list(list: &[G], stream: &mut String) {
420    let size = list.len();
421    if size == 0 {
422        stream.push_str("`byte$()");
423    } else {
424        if size == 1 {
425            stream.push(',');
426        }
427        stream.push_str("0x");
428        list.iter().for_each(|element| {
429            put_byte(*element, stream);
430        });
431    }
432}
433
434fn put_short_list(list: &[H], stream: &mut String) {
435    let size = list.len();
436    if size == 0 {
437        stream.push_str("`short$()");
438    } else {
439        if size == 1 {
440            stream.push(',');
441        }
442        for item in list.iter().take(size - 1) {
443            put_short(*item, stream);
444            stream.push(' ');
445        }
446        put_short(list[size - 1], stream);
447        stream.push('h');
448    }
449}
450
451fn put_int_list(list: &[I], stream: &mut String) {
452    let size = list.len();
453    if size == 0 {
454        stream.push_str("`int$()");
455    } else {
456        if size == 1 {
457            stream.push(',');
458        }
459        for item in list.iter().take(size - 1) {
460            put_int(*item, stream);
461            stream.push(' ');
462        }
463        put_int(list[size - 1], stream);
464        stream.push('i');
465    }
466}
467
468fn put_long_list(list: &[J], stream: &mut String) {
469    let size = list.len();
470    if size == 0 {
471        stream.push_str("`long$()");
472    } else {
473        if size == 1 {
474            stream.push(',');
475        }
476        for item in list.iter().take(size - 1) {
477            put_long(*item, stream);
478            stream.push(' ');
479        }
480        put_long(list[size - 1], stream);
481    }
482}
483
484fn put_real_list(list: &[E], stream: &mut String, precision: usize) {
485    let size = list.len();
486    if size == 0 {
487        stream.push_str("`real$()");
488    } else {
489        if size == 1 {
490            stream.push(',');
491        }
492        for item in list.iter().take(size - 1) {
493            put_real(*item, stream, precision);
494            stream.push(' ');
495        }
496        put_real(list[size - 1], stream, precision);
497        stream.push('e');
498    }
499}
500
501fn put_float_list(list: &[F], stream: &mut String, precision: usize) {
502    let size = list.len();
503    if size == 0 {
504        stream.push_str("`float$()");
505    } else {
506        if size == 1 {
507            stream.push(',');
508        }
509        for item in list.iter().take(size - 1) {
510            put_float(*item, stream, precision);
511            stream.push(' ');
512        }
513        put_float(list[size - 1], stream, precision);
514    }
515}
516
517fn put_string(string: &str, stream: &mut String) {
518    let size = string.len();
519    if size == 1 {
520        stream.push(',');
521    }
522    stream.push('"');
523    stream.push_str(string);
524    stream.push('"');
525}
526
527fn put_symbol_list(list: &[S], stream: &mut String) {
528    let size = list.len();
529    if size == 0 {
530        stream.push_str("`symbol$()");
531    } else {
532        if size == 1 {
533            stream.push(',');
534        }
535        for item in list.iter().take(size - 1) {
536            put_symbol(item, stream);
537        }
538        put_symbol(&list[size - 1], stream);
539    }
540}
541
542fn put_timestamp_list(list: &[J], stream: &mut String) {
543    let size = list.len();
544    if size == 0 {
545        stream.push_str("`timestamp$()");
546    } else {
547        if size == 1 {
548            stream.push(',');
549        }
550        for item in list.iter().take(size - 1) {
551            put_timestamp(*item, stream);
552            stream.push(' ');
553        }
554        if put_timestamp(list[size - 1], stream) {
555            stream.push('p');
556        }
557    }
558}
559
560fn put_month_list(list: &[I], stream: &mut String) {
561    let size = list.len();
562    if size == 0 {
563        stream.push_str("`month$()");
564    } else {
565        if size == 1 {
566            stream.push(',');
567        }
568        for item in list.iter().take(size - 1) {
569            put_month(*item, stream);
570            stream.push(' ');
571        }
572        put_month(list[size - 1], stream);
573        stream.push('m');
574    }
575}
576
577fn put_date_list(list: &[I], stream: &mut String) {
578    let size = list.len();
579    if size == 0 {
580        stream.push_str("`date$()");
581    } else {
582        if size == 1 {
583            stream.push(',');
584        }
585        for item in list.iter().take(size - 1) {
586            put_date(*item, stream);
587            stream.push(' ');
588        }
589        if put_date(list[size - 1], stream) {
590            stream.push('d');
591        }
592    }
593}
594
595fn put_datetime_list(list: &[F], stream: &mut String) {
596    let size = list.len();
597    if size == 0 {
598        stream.push_str("`datetime$()");
599    } else {
600        if size == 1 {
601            stream.push(',');
602        }
603        for item in list.iter().take(size - 1) {
604            put_datetime(*item, stream);
605            stream.push(' ');
606        }
607        if put_datetime(list[size - 1], stream) {
608            stream.push('z');
609        }
610    }
611}
612
613fn put_timespan_list(list: &[J], stream: &mut String) {
614    let size = list.len();
615    if size == 0 {
616        stream.push_str("`timespan$()");
617    } else {
618        if size == 1 {
619            stream.push(',');
620        }
621        for item in list.iter().take(size - 1) {
622            put_timespan(*item, stream);
623            stream.push(' ');
624        }
625        if put_timespan(list[size - 1], stream) {
626            stream.push('n');
627        }
628    }
629}
630
631fn put_minute_list(list: &[I], stream: &mut String) {
632    let size = list.len();
633    if size == 0 {
634        stream.push_str("`minute$()");
635    } else {
636        if size == 1 {
637            stream.push(',');
638        }
639        for item in list.iter().take(size - 1) {
640            put_minute(*item, stream);
641            stream.push(' ');
642        }
643        if put_minute(list[size - 1], stream) {
644            stream.push('u');
645        }
646    }
647}
648
649fn put_second_list(list: &[I], stream: &mut String) {
650    let size = list.len();
651    if size == 0 {
652        stream.push_str("`second$()");
653    } else {
654        if size == 1 {
655            stream.push(',');
656        }
657        for item in list.iter().take(size - 1) {
658            put_second(*item, stream);
659            stream.push(' ');
660        }
661        if put_second(list[size - 1], stream) {
662            stream.push('v');
663        }
664    }
665}
666
667fn put_time_list(list: &[I], stream: &mut String) {
668    let size = list.len();
669    if size == 0 {
670        stream.push_str("`time$()");
671    } else {
672        if size == 1 {
673            stream.push(',');
674        }
675        for item in list.iter().take(size - 1) {
676            put_time(*item, stream);
677            stream.push(' ');
678        }
679        if put_time(list[size - 1], stream) {
680            stream.push('t');
681        }
682    }
683}
684
685fn put_compound_list(list: &[K], stream: &mut String, precision: usize) {
686    let size = list.len();
687    if size == 0 {
688        stream.push_str("()");
689    } else if size == 1 {
690        stream.push(',');
691        put_q(&list[0], stream, precision);
692    } else {
693        stream.push('(');
694        for item in list.iter().take(size - 1) {
695            put_q(item, stream, precision);
696            stream.push(';');
697        }
698        put_q(&list[size - 1], stream, precision);
699        stream.push(')');
700    }
701}
702
703fn put_table(table: &K, stream: &mut String, precision: usize) {
704    stream.push('+');
705    put_dictionary(table.get_dictionary().unwrap(), stream, precision);
706}
707
708fn put_dictionary(dictionary: &K, stream: &mut String, precision: usize) {
709    let dictionary_ = dictionary.as_vec::<K>().unwrap();
710    let is_keyed_table = dictionary_[0].get_type() == qtype::TABLE;
711    if is_keyed_table {
712        stream.push('(');
713    }
714    put_q(&dictionary_[0], stream, precision);
715    if is_keyed_table {
716        stream.push(')');
717    }
718    stream.push('!');
719    if is_keyed_table {
720        stream.push('(');
721    }
722    put_q(&dictionary_[1], stream, precision);
723    if is_keyed_table {
724        stream.push(')');
725    }
726}
727
728fn put_q(object: &K, stream: &mut String, precision: usize) {
729    match object.0.qtype {
730        qtype::BOOL_ATOM => {
731            put_bool(object.get_byte().unwrap(), stream);
732            stream.push('b');
733        }
734        qtype::GUID_ATOM => put_guid(object.get_guid().unwrap(), stream),
735        qtype::BYTE_ATOM => {
736            stream.push_str("0x");
737            put_byte(object.get_byte().unwrap(), stream);
738        }
739        qtype::SHORT_ATOM => {
740            put_short(object.get_short().unwrap(), stream);
741            stream.push('h');
742        }
743        qtype::INT_ATOM => {
744            put_int(object.get_int().unwrap(), stream);
745            stream.push('i');
746        }
747        qtype::LONG_ATOM => put_long(object.get_long().unwrap(), stream),
748        qtype::REAL_ATOM => {
749            put_real(object.get_real().unwrap(), stream, precision);
750            stream.push('e');
751        }
752        qtype::FLOAT_ATOM => put_float(object.get_float().unwrap(), stream, precision),
753        qtype::CHAR => {
754            stream.push('"');
755            stream.push(object.get_char().unwrap());
756            stream.push('"');
757        }
758        qtype::SYMBOL_ATOM => put_symbol(object.get_symbol().unwrap(), stream),
759        qtype::TIMESTAMP_ATOM => {
760            if put_timestamp(object.get_long().unwrap(), stream) {
761                stream.push('p');
762            }
763        }
764        qtype::MONTH_ATOM => {
765            put_month(object.get_int().unwrap(), stream);
766            stream.push('m');
767        }
768        qtype::DATE_ATOM => {
769            if put_date(object.get_int().unwrap(), stream) {
770                stream.push('d');
771            }
772        }
773        qtype::DATETIME_ATOM => {
774            if put_datetime(object.get_float().unwrap(), stream) {
775                stream.push('z');
776            }
777        }
778        qtype::TIMESPAN_ATOM => {
779            if put_timespan(object.get_long().unwrap(), stream) {
780                stream.push('n');
781            }
782        }
783        qtype::MINUTE_ATOM => {
784            if put_minute(object.get_int().unwrap(), stream) {
785                stream.push('u');
786            }
787        }
788        qtype::SECOND_ATOM => {
789            if put_second(object.get_int().unwrap(), stream) {
790                stream.push('v');
791            }
792        }
793        qtype::TIME_ATOM => {
794            if put_time(object.get_int().unwrap(), stream) {
795                stream.push('t');
796            }
797        }
798        qtype::COMPOUND_LIST => {
799            // Put an attribute.
800            put_attribute(object.0.attribute, stream);
801            put_compound_list(object.as_vec::<K>().unwrap(), stream, precision)
802        }
803        qtype::BOOL_LIST => {
804            // Put an attribute.
805            put_attribute(object.0.attribute, stream);
806            put_bool_list(object.as_vec::<G>().unwrap(), stream)
807        }
808        qtype::GUID_LIST => {
809            // Put an attribute.
810            put_attribute(object.0.attribute, stream);
811            put_guid_list(object.as_vec::<U>().unwrap(), stream)
812        }
813        qtype::BYTE_LIST => {
814            // Put an attribute.
815            put_attribute(object.0.attribute, stream);
816            put_byte_list(object.as_vec::<G>().unwrap(), stream)
817        }
818        qtype::SHORT_LIST => {
819            // Put an attribute.
820            put_attribute(object.0.attribute, stream);
821            put_short_list(object.as_vec::<H>().unwrap(), stream)
822        }
823        qtype::INT_LIST => {
824            // Put an attribute.
825            put_attribute(object.0.attribute, stream);
826            put_int_list(object.as_vec::<I>().unwrap(), stream)
827        }
828        qtype::LONG_LIST => {
829            // Put an attribute.
830            put_attribute(object.0.attribute, stream);
831            put_long_list(object.as_vec::<J>().unwrap(), stream)
832        }
833        qtype::REAL_LIST => {
834            // Put an attribute.
835            put_attribute(object.0.attribute, stream);
836            put_real_list(object.as_vec::<E>().unwrap(), stream, precision)
837        }
838        qtype::FLOAT_LIST => {
839            // Put an attribute.
840            put_attribute(object.0.attribute, stream);
841            put_float_list(object.as_vec::<F>().unwrap(), stream, precision)
842        }
843        qtype::STRING => {
844            // Put an attribute.
845            put_attribute(object.0.attribute, stream);
846            put_string(object.as_string().unwrap(), stream)
847        }
848        qtype::SYMBOL_LIST => {
849            // Put an attribute.
850            put_attribute(object.0.attribute, stream);
851            put_symbol_list(object.as_vec::<S>().unwrap(), stream)
852        }
853        qtype::TIMESTAMP_LIST => {
854            // Put an attribute.
855            put_attribute(object.0.attribute, stream);
856            put_timestamp_list(object.as_vec::<J>().unwrap(), stream)
857        }
858        qtype::MONTH_LIST => {
859            // Put an attribute.
860            put_attribute(object.0.attribute, stream);
861            put_month_list(object.as_vec::<I>().unwrap(), stream)
862        }
863        qtype::DATE_LIST => {
864            // Put an attribute.
865            put_attribute(object.0.attribute, stream);
866            put_date_list(object.as_vec::<I>().unwrap(), stream)
867        }
868        qtype::DATETIME_LIST => {
869            // Put an attribute.
870            put_attribute(object.0.attribute, stream);
871            put_datetime_list(object.as_vec::<F>().unwrap(), stream)
872        }
873        qtype::TIMESPAN_LIST => {
874            // Put an attribute.
875            put_attribute(object.0.attribute, stream);
876            put_timespan_list(object.as_vec::<J>().unwrap(), stream)
877        }
878        qtype::MINUTE_LIST => {
879            // Put an attribute.
880            put_attribute(object.0.attribute, stream);
881            put_minute_list(object.as_vec::<I>().unwrap(), stream)
882        }
883        qtype::SECOND_LIST => {
884            // Put an attribute.
885            put_attribute(object.0.attribute, stream);
886            put_second_list(object.as_vec::<I>().unwrap(), stream)
887        }
888        qtype::TIME_LIST => {
889            // Put an attribute.
890            put_attribute(object.0.attribute, stream);
891            put_time_list(object.as_vec::<I>().unwrap(), stream)
892        }
893        qtype::TABLE => put_table(object, stream, precision),
894        qtype::DICTIONARY | qtype::SORTED_DICTIONARY => put_dictionary(object, stream, precision),
895        qtype::NULL => stream.push_str("::"),
896        _ => unimplemented!(),
897    }
898}