haystack_types/io/
mod.rs

1use nom::{IResult,Parser};
2use nom::bytes::complete::{tag,take,take_while,take_while1};
3use nom::branch::alt;
4use nom::multi::separated_list1;
5use nom::combinator::{map,opt,verify,recognize,success,value,iterator};
6use nom::character::complete::{digit1,char as nom_char,space1};
7use nom::character::{is_digit,is_alphanumeric};
8use nom::sequence::{preceded,terminated,separated_pair};
9use nom::number::complete::double;
10use nom::error::{Error, ErrorKind};
11
12use crate::{HVal, h_bool::HBool, h_null::HNull, h_na::HNA, h_marker::HMarker,
13    h_remove::HRemove, h_number::{NumTrait,HNumber,HUnit}, h_ref::HRef,
14    h_date::HDate, h_datetime::{HDateTime,HOffset}, h_time::HTime,
15    h_coord::HCoord, h_str::HStr, h_uri::HUri, h_dict::HDict,
16    h_list::HList, h_grid::HGrid};
17
18use crate::common::*;
19
20use std::collections::HashMap;
21use core::fmt::Display;
22use core::str::FromStr;
23use std::rc::Rc;
24use num::Float;
25
26pub type HBox<'a,T> = Rc<dyn HVal<'a,T> + 'a>;
27
28pub mod parse {
29    use nom::sequence::delimited;
30    use nom::combinator::map_res;
31    use super::*;
32
33    macro_rules! into_box {
34        ( $fn: expr, $num_type: ty, $lt: lifetime ) => {
35            map($fn,| hval | { Rc::new(hval) as HBox<$lt, $num_type>})
36        }
37    }
38
39    pub mod zinc {
40        use nom::{character::complete::newline, combinator::all_consuming, error::ParseError, AsChar, Err};
41
42        use super::*;
43
44        pub fn literal<'a,'b,T>(input: &'b str) -> IResult<&'b str, HBox<'a,T>>
45        where
46            T: NumTrait + 'a,
47            'a:'b
48        {
49            alt((
50                into_box!(na,T,'a),
51                into_box!(null,T,'a),
52                into_box!(marker,T,'a),
53                into_box!(remove,T,'a),
54                into_box!(boolean,T,'a),
55                into_box!(reference, T,'a),
56                into_box!(string,T,'a),
57                into_box!(uri,T,'a),
58                into_box!(datetime,T,'a),
59                into_box!(date,T,'a),
60                into_box!(time,T,'a),
61                into_box!(number,T,'a),
62                into_box!(coord,T,'a),
63                // TODO: Implement tests for collection types
64                into_box!(dict,T,'a),
65                into_box!(list,T,'a),
66                into_box!(delimited(tag("<<"),grid::<T>,tag(">>")),T,'a),
67                // TODO: Implement symbol type
68            )).parse(input)
69        }
70
71        pub fn boolean(input: &str) -> IResult<&str, HBool> {
72            alt((
73                value(HBool(true),tag("T")),
74                value(HBool(false),tag("F"))
75            )).parse(input)
76        }
77
78        pub fn null(input: &str) -> IResult<&str, HNull> {
79            use crate::h_null::NULL;
80            map(tag("N"), |_s: &str| { NULL }).parse(input)
81        }
82
83        pub fn na(input: &str) -> IResult<&str, HNA> {
84            use crate::h_na::NA;
85            map(tag("NA"), |_s: &str| { NA }).parse(input)
86        }
87
88        pub fn marker(input: &str) -> IResult<&str, HMarker> {
89            use crate::h_marker::MARKER;
90            map(tag("M"), |_s: &str| { MARKER }).parse(input)
91        }
92
93        pub fn remove(input: &str) -> IResult<&str, HRemove> {
94            use crate::h_remove::REMOVE;
95            map(tag("R"), |_s: &str| { REMOVE }).parse(input)
96        }
97
98        pub fn string(input: &str) -> IResult<&str, HStr> {
99            let (input,_) = tag("\"")(input)?;
100            let mut it = iterator(input, alt((
101                value("\x08", tag("\\b")),
102                value("\x0C", tag("\\f")),
103                value("\n", tag("\\n")),
104                value("\r", tag("\\r")),
105                value("\t", tag("\\t")),
106                value("\"", tag("\\\"")),
107                value("\\", tag("\\\\")),
108                value("$", tag("\\$")),
109                take_while1(unicode_char('"')),
110            )));
111
112            let string_literal = it.by_ref().fold(String::new(),|mut acc, input| { acc.push_str(input); acc });
113            let (input,()) = it.finish()?;
114            let (input,_) = tag("\"")(input)?;
115
116            Ok((input,HStr(string_literal)))
117        }
118
119        pub fn uri(input: &str) -> IResult<&str, HUri> {
120            let (input,_) = tag("`")(input)?;
121            let mut it = iterator(input, alt((
122                value(":", tag("\\:")),
123                value("/", tag("\\/")),
124                value("#", tag("\\#")),
125                value("\"", tag("\\\"")),
126                value("[", tag("\\[")),
127                value("]", tag("\\]")),
128                value("@", tag("\\@")),
129                value("`", tag("\\`")),
130                value("&", tag("\\&")),
131                value("=", tag("\\=")),
132                value(";", tag("\\;")),
133                value("\\", tag("\\\\")),
134                take_while1(unicode_char('`')),
135            )));
136
137            let url_literal = it.by_ref().fold(String::new(),|mut acc, input| { acc.push_str(input); acc });
138            let (input,()) = it.finish()?;
139            let (input,_) = tag("`")(input)?;
140
141            let uri_res = HUri::new(&url_literal);
142            let uri = uri_res.or(Err(nom::Err::Error(Error{ input: input, code: ErrorKind::Digit })))?;
143            Ok((input,uri))
144        }
145
146        pub fn ref_chars_body<I>(prefix:char) -> impl FnMut(I) -> IResult<I,I>
147        where
148            I: nom::Input + Clone,
149            <I as nom::Input>::Item: nom::AsChar + Copy,
150        {
151            
152        move |input| preceded(nom::character::complete::char::<I, nom::error::Error<I>>(prefix), take_while1(|c: <I as nom::Input>::Item| {
153            let c = c.as_char();
154            c.is_ascii_alphanumeric() || match c {
155                '_' | ':' | '-' | '.' | '~' => true,
156                _ => false
157            }
158        })).parse(input)
159        }
160
161        fn reference(input: &str) -> IResult<&str, HRef> {
162            let (input,(ref_str,dis_str)) = ((
163                ref_chars_body('@'),
164                opt(preceded(tag(" "), string)),
165            )).parse(input)?;
166            Ok((input,HRef::new(ref_str.to_owned(), dis_str.map(|s| s.into_string()))))
167        }
168
169        fn get_2_digits(input: &str) -> IResult<&str, &str> {
170            verify(take(2usize),|s: &str| s.chars().all(|c| char::is_digit(c,10))).parse(input)
171        }
172
173        fn get_offset(input: &str) -> IResult<&str, (i32, u32, u32)> {
174            ((
175                alt((value(-1,nom_char('-')),value(1,nom_char('+')))),
176                map(get_2_digits, |s| u32::from_str_radix(s,10).unwrap()),
177                preceded(tag(":"), map(get_2_digits, |s| u32::from_str_radix(s,10).unwrap()))
178            )).parse(input)
179        }
180
181        fn get_named_tz(input: &str) -> IResult<&str, &str> {
182            recognize(((
183                take_while1(|c: char| c.is_ascii_uppercase()),
184                take_while(|c: char| is_alphanumeric(c as u8) || c == '/' || c== '-' || c== '_' || c== '+')
185            ))).parse(input)
186        }
187
188        fn timezone(input: &str) -> IResult<&str, (String, HOffset)> {
189            use chrono_tz::{Tz, UTC};
190            use chrono::offset::FixedOffset;
191
192            let (input, (first, second)) = alt((
193                (recognize(get_offset), preceded(tag(" "), get_named_tz)),
194                (tag("Z"), preceded(tag(" "), get_named_tz)),
195                (tag("Z"), success("")),
196            )).parse(input)?;
197
198            // TODO: Implement with TZ instead of String
199            let timezone: (String, HOffset) = match first {
200                "Z" => match second {
201                    // "" => (UTC, HOffset::Utc),
202                    "" => ("UTC".to_owned(), HOffset::Utc),
203                    _ => {
204                        // let t = Tz::from_str(second).unwrap();
205                        // (t, HOffset::Variable(chrono::Duration::minutes(1 * 60 + 1)))
206                        (second.to_owned(), HOffset::Variable(chrono::Duration::minutes(1 * 60 + 1)))
207                    }
208                },
209                _ => {
210                    let (_,(sign,hours,minutes)) = get_offset(first)?;
211                    // (Tz::from_str(second).unwrap(), HOffset::Fixed(FixedOffset::east(sign * (hours as i32 * 3600 + minutes as i32 * 60))))
212                    (second.to_owned(), HOffset::Fixed(FixedOffset::east(sign * (hours as i32 * 3600 + minutes as i32 * 60))))
213                }
214            };
215
216            Ok((input,timezone))
217        }
218
219        pub fn datetime(input: &str) -> IResult<&str, HDateTime> {
220            let start = input;
221            let (input, (yr, mo, day, hr, min, sec, nano, tz)) = (
222                terminated(map(take(4usize), |s| i32::from_str_radix(s,10)),tag("-")),
223                terminated(map(take(2usize), |s| u32::from_str_radix(s,10)),tag("-")),
224                terminated(map(take(2usize), |s| u32::from_str_radix(s,10)),tag("T")),
225                terminated(map(take(2usize), |s| u32::from_str_radix(s,10)),tag(":")),
226                terminated(map(take(2usize), |s| u32::from_str_radix(s,10)),tag(":")),
227                map(take(2usize), |s| u32::from_str_radix(s,10)),
228                opt(preceded(tag("."),map(digit1, |s| u32::from_str_radix(s,10)))),
229                timezone
230            ).parse(start)?;
231
232            let yr = yr.or(Err(nom::Err::Error(Error{ input: start, code: ErrorKind::Digit })))?;
233            let mo = mo.or(Err(nom::Err::Error(Error{ input: start, code: ErrorKind::Digit })))?;
234            let day = day.or(Err(nom::Err::Error(Error{ input: start, code: ErrorKind::Digit })))?;
235            let hr = hr.or(Err(nom::Err::Error(Error{ input: start, code: ErrorKind::Digit })))?;
236            let min = min.or(Err(nom::Err::Error(Error{ input: start, code: ErrorKind::Digit })))?;
237            let sec = sec.or(Err(nom::Err::Error(Error{ input: start, code: ErrorKind::Digit })))?;
238
239            Ok((input, HDateTime::new(
240                yr, mo, day, hr, min, sec,
241                if let Some(nano) = nano {
242                    nano.or(Err(nom::Err::Error(Error{ input: start, code: ErrorKind::Digit })))?
243                } else { 0 },
244                tz
245            )))
246        }
247
248        fn coord_deg<T: Float + Display + FromStr>(input: &str) -> IResult<&str, T> {
249            map_res(recognize(((opt(tag("-")),digit1,opt(((tag("."),digit1)))))),|s: &str| s.parse::<T>()).parse(input)
250        }
251
252        pub fn coord<T: Float + Display + FromStr>(input: &str) -> IResult<&str, HCoord<T>> {
253            let (input,(lat,long)) = (delimited(tag("C("),coord_deg,tag(",")),terminated(coord_deg,tag(")"))).parse(input)?;
254    
255            Ok((input,HCoord::new(lat,long)))
256        }
257
258        fn tags<'a,'b,T>(input: &'b str) -> IResult<&'b str,Option<HashMap<String,HBox<'a,T>>>>
259        where
260            T: NumTrait + 'a,
261            'a:'b,
262        {
263            let (input,res_opt) = opt(separated_list1(
264                tag(" "),
265                (id, opt(preceded(tag(":"), literal::<'a,'b,T>)))
266            )).parse(input)?;
267
268            let mut map: HashMap<String, HBox<'a,T>> = HashMap::new();
269            let map_opt: Option<_>;
270            if let Some(res) = res_opt {
271                res.into_iter().for_each(|(k,v)| {
272                    map.insert(k.to_owned(), v.unwrap_or(Rc::new(crate::h_marker::MARKER) as HBox<'a, T>));
273                });
274                map_opt = Some(map);
275            } else {
276                map_opt = None;
277            }
278
279            Ok((input,map_opt))
280        }
281
282        fn tags_list<'a,'b,T: NumTrait + 'a>(input: &'b str) -> IResult<&'b str,Option<Vec<HBox<'a,T>>>>
283        where
284            T: NumTrait + 'a,
285            'a:'b,
286        {
287            let (input,res) = opt(separated_list1((take_while(AsChar::is_space),tag(","),take_while(AsChar::is_space)),literal::<'a,'b,T>)).parse(input)?;
288
289            Ok((input,res))
290        }
291
292        pub fn dict<'a,'b,T>(input: &'b str) -> IResult<&'b str, HDict<'a,T>>
293        where
294            T: NumTrait + 'a,
295            'a:'b,
296        {
297            let (input,opt_dict) = delimited(tag("{"),tags::<'a,'b,T>,tag("}")).parse(input)?;
298
299            let dict = match opt_dict {
300                Some(dict) => dict,
301                None => HashMap::new()
302            };
303
304            Ok((input,HDict::from_map(dict)))
305        }
306
307        pub fn list<'a,'b, T>(input: &'b str) -> IResult<&'b str, HList<'a,T>>
308        where
309            T: NumTrait + 'a,
310            'a:'b,
311        {
312            let (input,opt_vec) = delimited(tag("["),tags_list::<'a,'b,T>,tag("]")).parse(input)?;
313
314            let vec = match opt_vec {
315                Some(vec) => vec,
316                None => Vec::new()
317            };
318
319            Ok((input,HList::from_vec(vec)))
320        }
321
322        pub fn grid_meta<'a,'b,T>(input: &'b str) -> IResult<&'b str, HashMap<String,HBox<'a,T>>>
323        where
324            T: NumTrait + 'a,
325            'a:'b,
326        {
327            let (input,opt_dict) = tags::<'a,'b,T>(input)?;
328
329            let dict = match opt_dict {
330                Some(dict) => dict,
331                None => Err(nom::Err::Error(Error{
332                    input: input,
333                    code: ErrorKind::Tag
334                }))?
335            };
336
337            Ok((input,dict))
338        }
339
340        pub fn cols<'a,'b,T>(input: &'b str) -> IResult<&'b str, Vec<(String,Option<HashMap<String,HBox<'a,T>>>)>>
341        where
342            T: NumTrait + 'a,
343            'a:'b,
344        {
345            let (input,columns): (_,Vec<(_, Option<HashMap<_,HBox<'a,T>>>)>) = separated_list1(tag(","), separated_pair(id, space1, tags::<T>)).parse(input)?;
346            let columns = columns.into_iter().map(|(id,meta)| (id.to_owned(),meta));
347            let columns = columns.collect();
348            Ok((input,columns))
349        }
350
351        pub fn grid_err<'a,'b,T>(input: &'b str) -> IResult<&'b str, HGrid<T>>
352        where
353            T: NumTrait + 'a,
354            'a: 'b,
355        {
356            let (input,_) = tag("ver:\"3.0\"")(input)?;
357            let (input,meta) = delimited(space1, grid_meta::<'a,'b,T>, tag("\n")).parse(input)?;
358            let (_, is_empty) = all_consuming(map(terminated(tag("empty"), take_while1(|c| c == '\n')), |_| true)).parse(input)?;
359            if is_empty || meta.contains_key("err") {
360                //let dis = meta.get("dis").unwrap().get_string_val().unwrap().into_string();
361                let dis = match meta.get("dis") {
362                    Some(v) => {
363                        // v.get_string_val().unwrap().into_string()
364                        if let Some(s) = v.get_string_val() {
365                            s.clone_into_string()
366                        } else {
367                            return Err(nom::Err::Error(Error{
368                                input: input,
369                                code: ErrorKind::Tag
370                            }))
371                        }
372                    },
373                    None => return Err(nom::Err::Error(Error{
374                        input: input,
375                        code: ErrorKind::Tag
376                    }))
377                };
378                
379                // meta.get("errTrace").map(|s| s.get_string_val().unwrap().into_string())
380                let errTrace = match meta.get("errTrace") {
381                    Some(v) => {
382                        // v.get_string_val().unwrap().into_string()
383                        if let Some(s) = v.get_string_val() {
384                            Some(s.clone_into_string())
385                        } else {
386                            return Err(nom::Err::Error(Error{
387                                input: input,
388                                code: ErrorKind::Tag
389                            }))
390                        }
391                    },
392                    None => return Err(nom::Err::Error(Error{
393                        input: input,
394                        code: ErrorKind::Tag
395                    }))
396                };
397
398                let err = HGrid::Error {
399                    dis, errTrace,
400                };
401                return Ok((input, err));
402                
403            }
404
405            Err(nom::Err::Error(Error{
406                input: input,
407                code: ErrorKind::Tag
408            }))
409        }
410
411        pub fn grid<'a,'b, T>(input: &'b str) -> IResult<&'b str, HGrid<'a,T>>
412        where 
413            'a: 'b,
414            T: NumTrait + 'a,
415        {
416            let (input,version) = delimited(tag("ver:\""), recognize(double), tag("\"")).parse(input)?;
417
418            // Grid Meta
419            let (input,meta) = opt(preceded(space1, grid_meta::<'a,'b,T>)).parse(input)?;
420            let (_, is_empty) = all_consuming(map(terminated(tag("\nempty"), take_while1(|c| c == '\n')), |_| true)).parse(input)?;
421            if is_empty {
422                return Ok((input, HGrid::Empty { meta }));
423            }
424            
425            // Cols
426            let (input,columns) = terminated(cols::<T>, tag("\n")).parse(input)?;
427
428            // Rows
429            let row_width = columns.len();
430            let (input,rows) = separated_list1(
431                tag("\n"),
432                verify(
433                    separated_list1(tag(","),opt(literal::<T>)),
434                    |v: &Vec<Option<HBox<T>>>| v.len()==row_width
435                )
436            ).parse(input)?;
437
438            let mut grid = HGrid::from_row_vec(columns,rows);
439
440            if let Some(meta) = meta {
441                grid = grid.add_meta(meta).unwrap();
442            }
443
444            Ok((input,grid))
445        }
446
447        #[cfg(test)]
448        mod tests {
449            use super::*;
450            use crate::HCast;
451
452            #[test]
453            fn parse_unicode_char() {
454                let hello: IResult<&str,&str> = take_while(unicode_char('"'))("Hello\n\r\t\"\\");
455                assert_eq!(hello,Ok(("\n\r\t\"\\","Hello")));
456            }
457
458            #[test]
459            fn parse_tags() {
460                let input = "dis:\"Fri 31-Jul-2020\" view:\"chart\" title:\"Line\" chartNoScroll chartLegend:\"hide\" hisStart:2020-07-31T00:00:00-04:00 New_York hisEnd:2020-08-01T00:00:00-04:00 New_York hisLimit:10000";
461
462                let res = tags::<f64>(input);
463                if let Ok((_,e)) = res {
464                    let e1_ref = e.as_ref();
465                    let mut buf = String::new();
466                    let boxed_element = e1_ref.unwrap();
467                    
468                    let v = boxed_element.get("dis").unwrap();
469                    v.to_zinc(&mut buf).unwrap();
470                    let rhs = Rc::new(HStr("Fri 31-Jul-2020".to_owned())) as HBox<f64>;
471                    assert_eq!(v,&rhs)
472                } else {
473                    panic!("Failed to parse separated list")
474                }
475            }
476
477            #[test]
478            fn parse_string_02() {
479                assert_eq!(string("\"He\\tllo\""),Ok(("",HStr("He\tllo".to_owned()))));
480            }
481
482            #[test]
483            fn parse_bool() {
484                assert_eq!(boolean("T").unwrap(),("",HBool(true)));
485                assert_eq!(boolean("F").unwrap(),("",HBool(false)),);
486            }
487
488            #[test]
489            fn parse_null() {
490                assert_eq!(null("N").unwrap(),("",crate::h_null::NULL));
491            }
492
493            #[test]
494            fn parse_na() {
495                assert_eq!(na("NA").unwrap(),("",crate::h_na::NA));
496            }
497
498            #[test]
499            fn parse_marker() {
500                assert_eq!(marker("M").unwrap(),("",crate::h_marker::MARKER));
501            }
502
503            #[test]
504            fn parse_remove() {
505                assert_eq!(remove("R").unwrap(),("",crate::h_remove::REMOVE));
506            }
507
508            #[test]
509            fn parse_datetime() {
510                // TODO: Implement with Haystack Timezones so they're valid with the `chrono` library
511                // let tz_obj = (chrono_tz::Tz::from_str("America/Los_Angeles").unwrap(), HOffset::Fixed(chrono::offset::FixedOffset::east(-1 * 8 * 3600)));
512                let tz_obj = ("America/Los_Angeles".to_owned(), HOffset::Fixed(chrono::offset::FixedOffset::east(-1 * 8 * 3600)));
513                assert_eq!(datetime("2010-11-28T07:23:02.773-08:00 America/Los_Angeles").unwrap(),("",HDateTime::new(2010,11,28,7,23,2,773,tz_obj)));
514            }
515
516            #[test]
517            fn parse_coord() {
518                assert_eq!(coord("C(1.5,-9)").unwrap(),("",HCoord::new(1.5,-9f64)));
519            }
520
521            #[test]
522            fn coerce_na2hval() {
523                use crate::h_na::NA;
524                let (_,v) = literal::<f64>("NA").unwrap();
525                let lhs = v.get_na();
526                assert_eq!(lhs,Some(&NA))
527            }
528
529            macro_rules! assert_literal {
530                ( $val: literal, $get: ident, $rhs: expr ) => {
531                    let v = literal::<f64>($val).unwrap();
532                    let lhs = v.1.$get();
533                    assert_eq!(lhs,Some(&$rhs));
534                }
535            }
536
537            #[test]
538            fn coerce_hval() {
539                use crate::h_null::NULL;
540                use crate::h_marker::MARKER;
541                use crate::h_remove::REMOVE;
542                use crate::h_bool::HBool;
543                use crate::h_na::NA;
544                use crate::h_str::HStr;
545                use crate::h_uri::HUri;
546
547                assert_literal!("N",get_null,NULL);
548                assert_literal!("M",get_marker,MARKER);
549                assert_literal!("R",get_remove,REMOVE);
550                assert_literal!("T",get_bool,HBool(true));
551                assert_literal!("F",get_bool,HBool(false));
552                assert_literal!("NA",get_na,NA);
553                assert_literal!(r#""Hello\nSmidgen\"""#,get_string,HStr("Hello\nSmidgen\"".to_owned()));
554                assert_literal!("`http://www.google.com`",get_uri,HUri::new("http://www.google.com").unwrap());
555                assert_literal!("1.5kWh",get_number,HNumber::new(1.5f64,Some(HUnit::new("kWh".to_owned()))));
556            }
557            
558            #[test]
559            fn parse_literal_na() {
560                assert_eq!(literal::<f64>("NA").unwrap().1.get_na(), Some(&crate::h_na::NA));
561            }
562
563            #[test]
564            fn parse_literal_null() {
565                assert_eq!(literal::<f64>("N").unwrap().1.get_null(), Some(&crate::h_null::NULL));
566            }
567
568            #[test]
569            fn parse_literal_marker() {
570                assert_eq!(literal::<f64>("M").unwrap().1.get_marker(), Some(&crate::h_marker::MARKER));
571            }
572
573            #[test]
574            fn parse_literal_remove() {
575                assert_eq!(literal::<f64>("R").unwrap().1.get_remove(), Some(&crate::h_remove::REMOVE));
576            }
577
578            #[test]
579            fn parse_literal_bool() {
580                assert_eq!(literal::<f64>("T").unwrap().1.get_bool(), Some(&HBool(true)));
581                assert_eq!(literal::<f64>("F").unwrap().1.get_bool(), Some(&HBool(false)));
582            }
583
584            #[test]
585            fn parse_literal_string() {
586                assert_eq!(
587                    literal::<f64>(r#""Hello\nWorld""#).unwrap().1.get_string(),
588                    Some(&HStr("Hello\nWorld".to_owned()))
589                );
590            }
591
592            #[test]
593            fn parse_literal_uri() {
594                assert_eq!(
595                    literal::<f64>("`http://example.com`").unwrap().1.get_uri(),
596                    Some(&HUri::new("http://example.com").unwrap())
597                );
598            }
599
600            #[test]
601            fn parse_literal_number() {
602                assert_eq!(
603                    literal::<f64>("42.5").unwrap().1.get_number(),
604                    Some(&HNumber::new(42.5, None))
605                );
606                assert_eq!(
607                    literal::<f64>("1.5kWh").unwrap().1.get_number(),
608                    Some(&HNumber::new(1.5, Some(HUnit::new("kWh".to_owned()))))
609                );
610            }
611
612            #[test]
613            fn parse_literal_coord() {
614                assert_eq!(
615                    literal::<f64>("C(12.34,-56.78)").unwrap().1.get_coord_val(),
616                    Some(&HCoord::new(12.34, -56.78))
617                );
618            }
619
620            #[test]
621            fn parse_literal_datetime() {
622                let tz_obj = ("America/New_York".to_owned(), HOffset::Fixed(chrono::offset::FixedOffset::east(-5 * 3600)));
623                assert_eq!(
624                    literal::<f64>("2023-03-15T12:34:56.789-05:00 America/New_York")
625                        .unwrap()
626                        .1
627                        .get_datetime(),
628                    Some(&HDateTime::new(2023, 3, 15, 12, 34, 56, 789, tz_obj))
629                );
630            }
631
632            #[test]
633            fn parse_literal_dict() {
634                let input = r#"{key1:"value1" key2:42 key3:T}"#;
635                let result = dict::<f64>(input).unwrap().1;
636                assert_eq!(result.get("key1").unwrap().get_string(), Some(&HStr("value1".to_owned())));
637                assert_eq!(result.get("key2").unwrap().get_number(), Some(&HNumber::new(42.0, None)));
638                assert_eq!(result.get("key3").unwrap().get_bool(), Some(&HBool(true)));
639            }
640
641            #[test]
642            fn parse_literal_list() {
643                let input = r#"[42,"hello" , T]"#;
644                let result = list::<f64>(input).unwrap().1;
645                assert_eq!(result[0].get_number(), Some(&HNumber::new(42.0, None)));
646                assert_eq!(result[1].get_string(), Some(&HStr("hello".to_owned())));
647                assert_eq!(result[2].get_bool(), Some(&HBool(true)));
648            }
649
650            #[test]
651            fn parse_grid_empty() {
652                let input = "ver:\"3.0\"\nempty\n";
653                let empty_grid = grid::<f64>(input).unwrap().1;
654                if let HGrid::Empty { meta } = empty_grid {
655                    assert!(meta.is_none());
656                } else {
657                    panic!("Expected an empty grid");
658                }
659                //assert_eq!(grid::<f64>(input).unwrap().1, HGrid::<f64>::Empty);
660            }
661
662            #[test]
663            fn parse_empty_grid_with_meta() {
664                let input = "ver:\"3.0\" dis:\"Example Grid\"\nempty\n";
665                let grid: HGrid<'_, f64> = grid::<f64>(input).unwrap().1;
666                match grid {
667                    HGrid::Empty { meta } => {
668                        assert_eq!(meta.unwrap().get("dis").unwrap().get_string(), Some(&HStr("Example Grid".to_owned())));
669                    },
670                    _ => panic!("Expected an empty grid with metadata"),
671                }
672            }
673
674            /*
675            #[test]
676            fn parse_grid_with_rows() {
677                let input = r#"ver:"3.0" dis:"Example Grid"
678                                    col1 col2
679                                    42,"hello"
680                                    T,F
681                                    "#;
682                let grid = grid::<f64>(input).unwrap().1;
683                assert_eq!(grid.meta().get("dis").unwrap().get_string(), Some(&HStr("Example Grid".to_owned())));
684                assert_eq!(grid.rows()[0][0].get_number(), Some(&HNumber::new(42.0, None)));
685                assert_eq!(grid.rows()[0][1].get_string(), Some(&HStr("hello".to_owned())));
686                assert_eq!(grid.rows()[1][0].get_bool(), Some(&HBool(true)));
687                assert_eq!(grid.rows()[1][1].get_bool(), Some(&HBool(false)));
688            }
689            */
690        }
691    }
692
693    pub fn is_digits(chr: char) -> bool {
694        is_digit(chr as u8) && chr=='_'
695    }
696
697    pub fn digits(input: &str) -> IResult<&str, (&str, &str)> {
698        use nom::branch::permutation;
699
700        permutation((digit1, take_while(is_digits))).parse(input)
701    }
702
703    pub fn exp(input: &str) -> IResult<&str, (bool, (&str, &str))> {
704        use nom::sequence::pair;
705
706        preceded(alt((tag("e"),tag("E"))),pair(
707            opt(alt((tag("+"),tag("-"))))
708                .map(|sign| if let Some(c) = sign { c!="-" } else { true } ),
709            digits
710        )).parse(input)
711    }
712
713    pub fn is_unit(c: char) -> bool {
714        c.is_ascii_alphabetic() || c>=(128 as char) || match c {
715            '%' | '_' | '/' | '$' => true,
716            _ => false
717        }
718    }
719
720    pub fn unit(input: &str) -> IResult<&str, HUnit> {
721        let (input,unit_str) = take_while1(is_unit)(input)?;
722        Ok((input,HUnit::new(unit_str.to_owned())))
723    }
724
725    pub fn number<T: Float + Display + FromStr>(input: &str) -> IResult<&str, HNumber<T>> {
726        use std::slice;
727
728        let start = input;
729        let (input, is_positive) = map(opt(tag("-")),|d| d.is_none()).parse(start)?;
730        let (input, integer) = digits(input)?;
731        let (input, decimals) = opt(preceded(tag("."),digits)).parse(input)?;
732        let (input, exponent) = opt(exp).parse(input)?;
733        let number_slice = unsafe {
734            slice::from_raw_parts(start.as_ptr(),
735            (input.as_ptr() as usize) - (start.as_ptr() as usize))
736        };
737
738        let number_ty: T;
739
740        if let Ok(number_str) = std::str::from_utf8(number_slice) {
741            let number_res = number_str.parse::<T>();
742            if let Ok(number_ty_ok) = number_res {
743                number_ty = number_ty_ok;
744            } else {
745                return Err(nom::Err::Error(nom::error::Error{ input: number_str, code: nom::error::ErrorKind::Float }));
746            }
747        } else {
748            // TODO: Handle numbers with '_' in the digits
749            return Err(nom::Err::Error(nom::error::Error{
750                input: unsafe { std::str::from_utf8_unchecked(number_slice) },
751                code: nom::error::ErrorKind::Float }));
752        }
753
754        let (input, unit_opt) = opt(unit).parse(input)?;
755
756        Ok((input, HNumber::new(number_ty, unit_opt)))
757    }
758
759    pub fn date(input: &str) -> IResult<&str, HDate> {
760        let (input, year) = map(take(4usize), |s| i32::from_str_radix(s, 10) ).parse(input)?;
761        let year = year.or(Err(nom::Err::Error(Error{ input: input, code: ErrorKind::Digit })))?;
762
763        let (input, _) = tag("-")(input)?;
764        let (input, month) = map(take(2usize), |s| u32::from_str_radix(s, 10) ).parse(input)?;
765        let month = month.or(Err(nom::Err::Error(Error{ input: input, code: ErrorKind::Digit })))?;
766
767        let (input, _) = tag("-")(input)?;
768        let (input, day) = map(take(2usize), |s| u32::from_str_radix(s, 10) ).parse(input)?;
769        let day = day.or(Err(nom::Err::Error(Error{ input: input, code: ErrorKind::Digit })))?;
770
771        Ok((input,HDate::new(year, month, day)))
772    }
773
774    pub fn time(input: &str) -> IResult<&str, HTime> {
775        let (input, hour) = map(take(2usize), |s| {u32::from_str_radix(s, 10)}).parse(input)?;
776        let hour = hour.or(Err(nom::Err::Error(Error{ input: input, code: ErrorKind::Digit })))?;
777
778        let (input, _) = tag(":")(input)?;
779        let (input, min) = map(take(2usize), |s| {u32::from_str_radix(s, 10)}).parse(input)?;
780        let min = min.or(Err(nom::Err::Error(Error{ input: input, code: ErrorKind::Digit })))?;
781
782        let (input, _) = tag(":")(input)?;
783        let (input, sec) = map(take(2usize), |s| {u32::from_str_radix(s, 10)}).parse(input)?;
784        let sec = sec.or(Err(nom::Err::Error(Error{ input: input, code: ErrorKind::Digit })))?;
785
786        let (input, _) = tag(".")(input)?;
787        let (input, nano) = map(opt(digit1), |s| {if let Some(s) = s {u32::from_str_radix(s, 10)} else {Ok(0)}}).parse(input)?;
788        let nano = nano.or(Err(nom::Err::Error(Error{ input: input, code: ErrorKind::Digit })))?;
789
790        Ok((input, HTime::new(hour, min, sec, nano)))
791    }
792
793    #[cfg(test)]
794    mod tests {
795        use super::*;
796
797        #[test]
798        fn parse_id() {
799            assert_eq!(id("asdasd1223_").unwrap(),("","asdasd1223_"));
800        }
801
802        #[test]
803        fn parse_date() {
804            assert_eq!(date("2010-11-28").unwrap(),("",HDate::new(2010,11,28)));
805        }
806
807        #[test]
808        fn parse_time() {
809            assert_eq!(time("07:23:02.773").unwrap(),("",HTime::new(07,23,02,773)));
810        }
811    }
812}