tomllib/
types.rs

1use std::collections::HashMap;
2use std::hash::Hasher;
3use std::rc::Rc;
4use std::cell::{Cell, RefCell};
5use std::fmt;
6use std::error::Error;
7use std::fmt::Display;
8use std::str::FromStr;
9use std::borrow::Cow;
10use internals::parser::Parser;
11use nom::IResult;
12
13/// Conveys the result of a parse operation on a TOML document
14#[derive(Debug, Eq, PartialEq, Clone)]
15pub enum ParseResult<'a> {
16  /// The entire input was parsed without error.
17  Full,
18  /// The entire input was parsed, but there were errors. Contains an `Rc<RefCell<Vec>>` of `ParseError`s.
19  FullError(Rc<RefCell<Vec<ParseError<'a>>>>),
20  /// Part of the input was parsed successfully without any errors. Contains a `Cow<str>`, with the leftover, unparsed
21  /// input, the line number and column (currently column reporting is unimplemented and will always report `0`) where
22  /// parsing stopped.
23  Partial(Cow<'a, str>, usize, usize),
24  /// Part of the input was parsed successfully with errors. Contains a `Cow<str>`, with the leftover, unparsed input,
25  /// the line number and column (currently column reporting is unimplemented and will always report `0`) where parsing
26  /// stopped, and an `Rc<RefCell<Vec>>` of `ParseError`s.
27  PartialError(Cow<'a, str>, usize, usize, Rc<RefCell<Vec<ParseError<'a>>>>),
28  /// The parser failed to parse any of the input as a complete TOML document. Contains the line number and column
29  /// (currently column reporting is unimplemented and will always report `0`) where parsing stopped.
30  Failure(usize, usize),
31}
32
33/// Represents a non-failure error encountered while parsing a TOML document.
34#[derive(Debug, Eq, PartialEq, Clone)]
35pub enum ParseError<'a> {
36  /// An `Array` containing different types was encountered. Contains the `String` key that points to the `Array` and
37  /// the line number and column (currently column reporting is unimplemented and will always report `0`) where the
38  /// `Array` was found. The `Array` can be retrieved and/or changed by its key using `TOMLParser::get_value` and
39  /// `TOMLParser::set_value` methods.
40  MixedArray(String, usize, usize),
41  /// A duplicate key was encountered. Contains the `String` key that was duplicated in the document, the line number
42  /// and column (currently column reporting is unimplemented and will always report `0`) where the duplicate key was
43  /// found, and the `Value` that the key points to.
44  DuplicateKey(String, usize, usize, Value<'a>),
45  /// An invalid table was encountered. Either the key\[s\] that make up the table are invalid or a duplicate table was
46  /// found. Contains the `String` key of the invalid table, the line number and column (currently column reporting is
47  /// unimplemented and will always report `0`) where the invalid table was found, `RefCell<HashMap<String, Value>>`
48  /// that contains all the keys and values belonging to that table.
49  InvalidTable(String, usize, usize, RefCell<HashMap<String, Value<'a>>>),
50  /// An invalid `DateTime` was encountered. This could be a `DateTime` with:
51  ///
52  /// * 0 for year
53  /// * 0 for month or greater than 12 for month
54  /// * 0 for day or greater than, 28, 29, 30, or 31 for day depending on the month and if the year is a leap year
55  /// * Greater than 23 for hour
56  /// * Greater than 59 for minute
57  /// * Greater than 59 for second
58  /// * Greater than 23 for offset hour
59  /// * Greater than 59 for offset minute
60  ///
61  /// Contains the `String` key of the invalid `DateTime`, the line number and column (currently column reporting is
62  /// unimplemented and will always report `0`) where the invalid `DateTime` was found, and a Cow<str> containing the
63  /// invalid `DateTime` string.
64  InvalidDateTime(String, usize, usize, Cow<'a, str>),
65  /// *Currently unimplemented*. Reserved for future use when an integer overflow is detected.
66  IntegerOverflow(String, usize, usize, Cow<'a, str>),
67  /// *Currently unimplemented*. Reserved for future use when an integer underflow is detected.
68  IntegerUnderflow(String, usize, usize, Cow<'a, str>),
69  /// *Currently unimplemented*. Reserved for future use when an invalid integer representation is detected.
70  InvalidInteger(String, usize, usize, Cow<'a, str>),
71  /// *Currently unimplemented*. Reserved for future use when a float value of infinity is detected.
72  Infinity(String, usize, usize, Cow<'a, str>),
73  /// *Currently unimplemented*. Reserved for future use when a float value of negative infinity is detected.
74  NegativeInfinity(String, usize, usize, Cow<'a, str>),
75  /// *Currently unimplemented*. Reserved for future use when a float string conversion to an `f64` would result in a loss
76  /// of precision.
77  LossOfPrecision(String, usize, usize, Cow<'a, str>),
78  /// *Currently unimplemented*. Reserved for future use when an invalid float representation is detected.
79  InvalidFloat(String, usize, usize, Cow<'a, str>),
80  /// *Currently unimplemented*. Reserved for future use when an invalid `true` or `false` string is detected.
81  InvalidBoolean(String, usize, usize, Cow<'a, str>),
82  /// *Currently unimplemented*. Reserved for future use when an invalid string representation is detected.
83  InvalidString(String, usize, usize, Cow<'a, str>, StrType),
84  /// *Currently unimplemented*. Reserved for future use when new error types are added without resorting to a breaking
85  /// change.
86  GenericError(String, usize, usize, Option<Cow<'a, str>>, String),
87}
88
89// Represents the 7 different types of values that can exist in a TOML document.
90#[derive(Debug, PartialEq, Eq, Clone)]
91pub enum Value<'a> {
92  /// An integer value. Contains a `Cow<str>` representing the integer since integers can contain underscores.
93  Integer(Cow<'a, str>),
94  /// A float value. Contains a `Cow<str>` representing the float since floats can be formatted many different ways and
95  /// can contain underscores.
96  Float(Cow<'a, str>),
97  /// A boolean value. Contains a `bool` value since only `true` and `false` are allowed.
98  Boolean(bool),
99  /// A `DateTime` value. Contains a `DateTime` struct that has a date and optionally a time, fractional seconds, and
100  /// offset from UTC.
101  DateTime(DateTime<'a>),
102  /// A string value. Contains a `Cow<str>` with the string contents (without quotes) and `StrType` indicating whether
103  /// the string is a basic string, multi-line basic string, literal string or multi-line literal string.
104  String(Cow<'a, str>, StrType),
105  /// An array value. Contains an `Rc<Vec>` of `Value`s contained in the `Array`.
106  Array(Rc<Vec<Value<'a>>>),
107  /// An inline table value. Contains an `Rc<Vec>` of tuples that contain a `Cow<str>` representing a key, and `Value`
108  /// that the key points to.
109  InlineTable(Rc<Vec<(Cow<'a, str>, Value<'a>)>>)
110}
111
112/// Represents the 4 different types of strings that are allowed in TOML documents.
113#[derive(Debug, Eq, PartialEq, Copy, Clone)]
114pub enum StrType {
115  /// String is a basic string.
116  Basic,
117  /// String is a multi-line basic string.
118  MLBasic,
119  /// String is a literal string.
120  Literal,
121  /// String is a multi-line literal string.
122  MLLiteral,
123}
124
125/// Represents the child keys of a key in a parsed TOML document.
126#[derive(Debug, Eq, PartialEq, Clone)]
127pub enum Children {
128  /// Contains a `Cell<usize>` with the amount of child keys the key has. The key has children that are indexed with an
129  /// integer starting at 0. `Array`s and array of tables use integer for their child keys. For example:
130  ///
131  /// ```text
132  /// Array = ["A", "B", "C", "D", "E"]
133  /// [[array_of_table]]
134  /// key = "val 1"
135  /// [[array_of_table]]
136  /// key = "val 2"
137  /// [[array_of_table]]
138  /// key = "val 3"
139  /// ```
140  ///
141  /// "Array" has 5 children. The key of "D" is "Array[3]" because it is fourth element in "Array" and indexing starts
142  /// at 0.
143  /// "array_of_table" has 3 children. The key of "val 3" is "array_of_table[2].key" because it is in the third
144  /// sub-table of "array_of_table" and indexing starts at 0.
145  Count(Cell<usize>),
146  /// Contains a `RefCell<Vec>` of `String`s with every sub-key of the key. The key has children that are indexed with a
147  /// sub-key. Tables and inline-tables use sub-keys for their child keys. For example:
148  ///
149  /// ```text
150  /// InlineTable = {subkey1 = "A", subkey2 = "B"}
151  /// [table]
152  /// a_key = "val 1"
153  /// b_key = "val 2"
154  /// c_key = "val 3"
155  /// ```
156  ///
157  /// "InlineTable" has 2 children, "subkey1" and "subkey2". The key of "B" is "InlineTable.subkey2".
158  /// "table" has 3 children, "a_key", "b_key", and "c_key". The key of "val 3" is "table.c_key".
159  Keys(RefCell<Vec<String>>)
160}
161
162/// Contains convenience functions to combine base keys with child keys to make a full key.
163impl Children {
164
165  /// Combines string type `base_key` with a string type `child_key` to form a full key.
166  ///
167  /// # Examples
168  ///
169  /// ```
170  /// use tomllib::TOMLParser;
171  /// use tomllib::types::Children;
172  /// let toml_doc = r#"
173  /// [dependencies]
174  /// nom = {version = "^1.2.0", features = ["regexp"]}
175  /// regex = {version = "^0.1.48"}
176  /// log = {version = "^0.3.5"}
177  /// "#;
178  /// let parser = TOMLParser::new();
179  /// let (parser, result) = parser.parse(toml_doc);
180  /// let deps = parser.get_children("dependencies");
181  /// if let &Children::Keys(ref subkeys) = deps.unwrap() {
182  ///   assert_eq!("dependencies.nom",
183  ///     Children::combine_keys("dependencies", &subkeys.borrow()[0]));
184  /// }
185  /// ```
186  pub fn combine_keys<S>(base_key: S, child_key: S) -> String where S: Into<String> {
187    let mut full_key;
188    let base = base_key.into();
189    let child = child_key.into();
190    if base != "" {
191      full_key = base.clone();
192      full_key.push('.');
193      full_key.push_str(&child);
194    } else {
195      full_key = child.clone();
196    }
197    return full_key;
198  }
199
200  /// Combines string type `base_key` with an integer type `child_key` to form a full key.
201  ///
202  /// # Examples
203  ///
204  /// ```
205  /// use tomllib::TOMLParser;
206  /// use tomllib::types::Children;
207  /// let toml_doc = r#"
208  /// keywords = ["toml", "parser", "encode", "decode", "nom"]
209  /// "#;
210  /// let parser = TOMLParser::new();
211  /// let (parser, result) = parser.parse(toml_doc);
212  /// let kw = parser.get_children("keywords");
213  /// if let &Children::Count(ref subkeys) = kw.unwrap() {
214  ///   assert_eq!("keywords[4]", Children::combine_keys_index("keywords", subkeys.get() - 1));
215  /// }
216  /// # else {
217  /// #  assert!(false, "{:?}", kw.unwrap());
218  /// # }
219  /// ```
220  pub fn combine_keys_index<S>(base_key: S, child_key: usize) -> String where S: Into<String> {
221    return format!("{}[{}]", base_key.into(), child_key);
222  }
223
224  /// Combines string type `base_key` with all subkeys of an instance of `Children` to form a `Vec` of full keys
225  ///
226  /// # Examples
227  ///
228  /// ```
229  /// use tomllib::TOMLParser;
230  /// use tomllib::types::Children;
231  /// let toml_doc = r#"
232  /// keywords = ["toml", "parser"]
233  /// numbers = {first = 1, second = 2}
234  /// "#;
235  /// let parser = TOMLParser::new();
236  /// let (parser, result) = parser.parse(toml_doc);
237  /// let kw = parser.get_children("keywords");
238  /// assert_eq!(vec!["keywords[0]".to_string(), "keywords[1]".to_string()],
239  ///   kw.unwrap().combine_child_keys("keywords"));
240  /// let num = parser.get_children("numbers");
241  /// assert_eq!(vec!["numbers.first".to_string(), "numbers.second".to_string()],
242  ///   num.unwrap().combine_child_keys("numbers"));
243  /// ```
244  pub fn combine_child_keys<S>(&self, base_key: S) -> Vec<String> where S: Into<String> {
245    let mut all_keys = vec![];
246    let base = base_key.into();
247    match self {
248      &Children::Count(ref c) => {
249        for i in 0..c.get() {
250          all_keys.push(format!("{}[{}]", base, i));
251        }
252      },
253      &Children::Keys(ref hs_rc) => {
254        for subkey in hs_rc.borrow().iter() {
255          if base != "" {
256            let mut full_key = base.clone();
257            full_key.push('.');
258            full_key.push_str(&subkey);
259            all_keys.push(full_key);
260          } else {
261            all_keys.push(subkey.clone());
262          }
263        }
264      },
265    }
266    return all_keys;
267  }
268}
269
270/// Formats a `Value` for display. Uses default rust formatting for for `i64` for `Integer`s, `f64` for `Float`s, bool
271/// for `Boolean`s. The default formatting for `Array`s and `InlineTable`s is No whitespace after/before
272/// opening/closing braces, no whitespace before and one space after all commas, no comments on the same line as the
273/// `Array` or `InlineTable`, and one space before and after an equals sign in an `InlineTable`.
274impl<'a> Display for Value<'a> {
275  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
276    match self {
277      &Value::Integer(ref v) | &Value::Float(ref v) =>
278        write!(f, "{}", v),
279      &Value::Boolean(ref b) => write!(f, "{}", b),
280      &Value::DateTime(ref v) => write!(f, "{}", v),
281      &Value::Array(ref arr) => {
282        try!(write!(f, "["));
283        for i in 0..arr.len() - 1 {
284          try!(write!(f, "{}, ", arr[i]));
285        }
286        if arr.len() > 0 {
287          try!(write!(f, "{}", arr[arr.len()-1]));
288        }
289        write!(f, "]")
290      },
291      &Value::String(ref s, ref t) => {
292        match t {
293          &StrType::Basic => write!(f, "\"{}\"", s),
294          &StrType::MLBasic => write!(f, "\"\"\"{}\"\"\"", s),
295          &StrType::Literal => write!(f, "'{}'", s),
296          &StrType::MLLiteral =>  write!(f, "'''{}'''", s),
297        }
298      },
299      &Value::InlineTable(ref it) => {
300        try!(write!(f, "{{"));
301        for i in 0..it.len() - 1 {
302          try!(write!(f, "{} = {}, ", it[i].0, it[i].1));
303        }
304        if it.len() > 0 {
305          try!(write!(f, "{} = {}", it[it.len()-1].0, it[it.len()-1].1));
306        }
307        write!(f, "}}")
308      }
309    }
310  }
311}
312
313impl<'a> Value<'a> {
314
315  /// Convenience function for creating an `Value::Integer` from an `i64`. Cannot fail since `i64` maps directly onto
316  /// TOML integers.
317  ///
318  /// # Examples
319  ///
320  /// ```
321  /// use tomllib::types::Value;
322  ///
323  /// assert_eq!(Value::Integer("100".into()), Value::int(100));
324  /// ```
325  pub fn int(int: i64) -> Value<'a> {
326    Value::Integer(format!("{}", int).into())
327  }
328
329  /// Convenience function for creating an `Value::Integer` from an string type. Returns `Ok(Integer)` on success and
330  /// `Err(TOMLError)` on failure.
331  ///
332  /// # Examples
333  ///
334  /// ```
335  /// use tomllib::types::Value;
336  ///
337  /// assert_eq!(Value::Integer("200".into()), Value::int_from_str("200").unwrap());
338  /// ```
339  pub fn int_from_str<S>(int: S) -> Result<Value<'a>, TOMLError> where S: Into<String> + Clone {
340    let result = Value::Integer(int.clone().into().into());
341    if result.validate() {
342      return Result::Ok(result);
343    } else {
344      return Result::Err(TOMLError::new(format!("Error parsing int. Argument: {}", int.into())));
345    }
346  }
347
348  /// Convenience function for creating a `Value::Float` from a `f64`. Cannot fail since `f64` maps directly onto TOML
349  /// floats.
350  ///
351  /// # Examples
352  ///
353  /// ```
354  /// use tomllib::types::Value;
355  ///
356  /// assert_eq!(Value::Float("300.3".into()), Value::float(300.3));
357  /// ```
358  pub fn float(float: f64) -> Value<'a> {
359    Value::Float(format!("{}", float).into())
360  }
361
362  /// Convenience function for creating a `Value::Float` from an string type. Returns `Ok(Float)` on success and
363  /// `Err(TOMLError)`
364  /// on failure.
365  ///
366  /// # Examples
367  ///
368  /// ```
369  /// use tomllib::types::Value;
370  ///
371  /// assert_eq!(Value::Float("400.4".into()), Value::float_from_str("400.4").unwrap());
372  /// ```
373  pub fn float_from_str<S>(float: S) -> Result<Value<'a>, TOMLError> where S: Into<String> + Clone {
374    let result = Value::Float(float.clone().into().into());
375    if result.validate() {
376      return Result::Ok(result);
377    } else {
378      return Result::Err(TOMLError::new(format!("Error parsing float. Argument: {}", float.into())));
379    }
380  }
381
382  /// Convenience function for creating a `Value::Boolean` from a `bool`. Cannot fail since `bool` maps directly onto
383  /// TOML booleans.
384  ///
385  /// # Examples
386  ///
387  /// ```
388  /// use tomllib::types::Value;
389  ///
390  /// assert_eq!(Value::Boolean(true), Value::bool(true));
391  /// ```
392  pub fn bool(b: bool) -> Value<'a> {
393      Value::Boolean(b)
394  }
395  pub fn bool_from_str<S>(b: S) -> Result<Value<'a>, TOMLError> where S: Into<String> + Clone {
396    let lower = b.clone().into().to_lowercase();
397    if lower == "true" {
398      Result::Ok(Value::Boolean(true))
399    } else if lower == "false" {
400      Result::Ok(Value::Boolean(false))
401    } else {
402      return Result::Err(TOMLError::new(
403        format!("Error parsing bool. Argument: {}", b.into())
404      ))
405    }
406  }
407
408  /// Convenience function for creating a `Value::DateTime` containing only a date from integer values. Returns
409  /// `Ok(DateTime)` on success and `Err(TOMLError)` on failure.
410  ///
411  /// # Examples
412  ///
413  /// ```
414  /// use tomllib::types::{Value, DateTime, Date};
415  ///
416  /// assert_eq!(Value::DateTime(DateTime::new(Date::from_str("2010", "04", "10").unwrap(), None)),
417  ///   Value::date_from_int(2010, 4, 10).unwrap());
418  /// ```
419  pub fn date_from_int(year: usize, month: usize, day: usize) -> Result<Value<'a>, TOMLError> {
420    let y = format!("{:0>4}", year);
421    let m = format!("{:0>2}", month);
422    let d = format!("{:0>2}", day);
423    match Date::from_str(y, m, d) {
424      Ok(date) => {
425        Ok(Value::DateTime(DateTime::new(date, None)))
426      },
427      Err(error) => Err(error),
428    }
429  }
430
431  /// Convenience function for creating a `Value::DateTime` containing only a date from string values. Returns
432  /// `Ok(DateTime)` on success and `Err(TOMLError)` on failure.
433  ///
434  /// # Examples
435  ///
436  /// ```
437  /// use tomllib::types::{Value, DateTime, Date};
438  ///
439  /// assert_eq!(Value::DateTime(DateTime::new(Date::from_str("2011", "05", "11").unwrap(), None)),
440  ///   Value::date_from_str("2011", "05", "11").unwrap());
441  /// ```
442  pub fn date_from_str<S>(year: S, month: S, day: S) -> Result<Value<'a>, TOMLError> where S: Into<String> + Clone {
443    match Date::from_str(year.clone().into(), month.clone().into(), day.clone().into()) {
444      Ok(date) => {
445        Ok(Value::DateTime(DateTime::new(date, None)))
446      },
447      Err(error) => Err(error),
448    }
449  }
450
451  /// Convenience function for creating a `Value::DateTime` containing a date and time from integer values. Returns
452  /// `Ok(DateTime)` on success and `Err(TOMLError)` on failure.
453  ///
454  /// # Examples
455  ///
456  /// ```
457  /// use tomllib::types::{Value, DateTime, Date, Time};
458  ///
459  /// assert_eq!(Value::DateTime(DateTime::new(Date::from_str("2010", "04", "10").unwrap(),
460  ///   Some(Time::from_str("01", "02", "03", None, None).unwrap()))),
461  ///   Value::datetime_from_int(2010, 4, 10, 1, 2, 3).unwrap());
462  /// ```
463  pub fn datetime_from_int(year: usize, month: usize, day: usize, hour: usize, minute: usize, second: usize) -> Result<Value<'a>, TOMLError> {
464    let y = format!("{:0>4}", year);
465    let m = format!("{:0>2}", month);
466    let d = format!("{:0>2}", day);
467    let h = format!("{:0>2}", hour);
468    let min = format!("{:0>2}", minute);
469    let s = format!("{:0>2}", second);
470    match Date::from_str(y, m, d) {
471      Ok(date) => {
472        match Time::from_str(h, min, s, None, None) {
473          Ok(time) => Ok(Value::DateTime(DateTime::new(date, Some(time)))),
474          Err(error) => Err(error),
475        }
476      },
477      Err(error) => Err(error),
478    }
479  }
480
481  /// Convenience function for creating a `Value::DateTime` containing a date and time from string values. Returns
482  /// `Ok(DateTime)` on success and `Err(TOMLError)` on failure.
483  ///
484  /// # Examples
485  ///
486  /// ```
487  /// use tomllib::types::{Value, DateTime, Date, Time};
488  ///
489  /// assert_eq!(Value::DateTime(DateTime::new(Date::from_str("2011", "05", "11").unwrap(),
490  ///   Some(Time::from_str("02", "03", "04", None, None).unwrap()))),
491  ///   Value::datetime_from_str("2011", "05", "11", "02", "03", "04").unwrap());
492  /// ```
493  pub fn datetime_from_str<S>(year: S, month: S, day: S, hour: S, minute: S, second: S) -> Result<Value<'a>, TOMLError> where S: Into<String> + Clone {
494    match Date::from_str(year.clone().into(), month.clone().into(), day.clone().into()) {
495      Ok(date) => {
496        match Time::from_str(hour.clone().into(), minute.clone().into(), second.clone().into(), None, None) {
497          Ok(time) => Ok(Value::DateTime(DateTime::new(date, Some(time)))),
498          Err(error) => Err(error),
499        }
500      },
501      Err(error) => Err(error),
502    }
503  }
504
505  /// Convenience function for creating a `Value::DateTime` containing a date and time with fractional seconds from
506  /// integer values. Returns `Ok(DateTime)` on success and `Err(TOMLError)` on failure. Note, you can't represent
507  /// leading zeros on the fractional part this way for example: `2016-03-15T08:05:22.00055` is not possible using this
508  /// function.
509  ///
510  /// # Examples
511  ///
512  /// ```
513  /// use tomllib::types::{Value, DateTime, Date, Time};
514  ///
515  /// assert_eq!(Value::DateTime(DateTime::new(Date::from_str("2010", "04", "10").unwrap(),
516  ///   Some(Time::from_str("01", "02", "03", Some("5432".into()), None).unwrap()))),
517  ///   Value::datetime_frac_from_int(2010, 4, 10, 1, 2, 3, 5432).unwrap());
518  /// ```
519  pub fn datetime_frac_from_int(year: usize, month: usize, day: usize, hour: usize, minute: usize, second: usize, frac: usize) -> Result<Value<'a>, TOMLError> {
520    let y = format!("{:0>4}", year);
521    let m = format!("{:0>2}", month);
522    let d = format!("{:0>2}", day);
523    let h = format!("{:0>2}", hour);
524    let min = format!("{:0>2}", minute);
525    let s = format!("{:0>2}", second);
526    let f = format!("{}", frac);
527    match Date::from_str(y, m, d) {
528      Ok(date) => {
529        match Time::from_str(h, min, s, Some(f), None) {
530          Ok(time) => Ok(Value::DateTime(DateTime::new(date, Some(time)))),
531          Err(error) => Err(error),
532        }
533      },
534      Err(error) => Err(error),
535    }
536  }
537
538  /// Convenience function for creating a `Value::DateTime` containing a date and time with fractional seconds from
539  /// string values. Returns `Ok(DateTime)` on success and `Err(TOMLError)` on failure.
540  ///
541  /// # Examples
542  ///
543  /// ```
544  /// use tomllib::types::{Value, DateTime, Date, Time};
545  ///
546  /// assert_eq!(Value::DateTime(DateTime::new(Date::from_str("2011", "05", "11").unwrap(),
547  ///   Some(Time::from_str("02", "03", "04", Some("0043".into()), None).unwrap()))),
548  ///   Value::datetime_frac_from_str("2011", "05", "11", "02", "03", "04", "0043").unwrap());
549  /// ```
550  pub fn datetime_frac_from_str<S>(year: S, month: S, day: S, hour: S, minute: S, second: S, frac: S) -> Result<Value<'a>, TOMLError> where S: Into<String> + Clone{
551    match Date::from_str(year.clone().into(), month.clone().into(), day.clone().into()) {
552      Ok(date) => {
553        match Time::from_str(hour.clone().into(), minute.clone().into(), second.clone().into(), Some(frac.clone().into()), None) {
554          Ok(time) => Ok(Value::DateTime(DateTime::new(date, Some(time)))),
555          Err(error) => Err(error),
556        }
557      },
558      Err(error) => Err(error),
559    }
560  }
561
562  /// Convenience function for creating a `Value::DateTime` containing a date and time with a timezone offset from UTC
563  /// from integer values, except for the plus/minus sign which is passed as a char `'+'` or `'-'`. Returns
564  /// `Ok(DateTime)` on success and `Err(TOMLError)` on failure.
565  ///
566  /// # Examples
567  ///
568  /// ```
569  /// use tomllib::types::{Value, DateTime, Date, Time, TimeOffset, TimeOffsetAmount};
570  ///
571  /// assert_eq!(Value::DateTime(DateTime::new(Date::from_str("2010", "04", "10").unwrap(),
572  ///   Some(Time::from_str("01", "02", "03", None, Some(TimeOffset::Time(TimeOffsetAmount::from_str(
573  ///     "+", "08", "00"
574  ///   ).unwrap()))).unwrap()))),
575  ///   Value::datetime_offset_from_int(2010, 4, 10, 1, 2, 3, '+', 8, 0).unwrap());
576  /// ```
577  pub fn datetime_offset_from_int(year: usize, month: usize, day: usize, hour: usize, minute: usize, second: usize, posneg: char, off_hour: usize, off_minute: usize) -> Result<Value<'a>, TOMLError> {
578    let y = format!("{:0>4}", year);
579    let m = format!("{:0>2}", month);
580    let d = format!("{:0>2}", day);
581    let h = format!("{:0>2}", hour);
582    let min = format!("{:0>2}", minute);
583    let s = format!("{:0>2}", second);
584    let oh = format!("{:0>2}", off_hour);
585    let omin = format!("{:0>2}", off_minute);
586    let mut pn = "".to_string();
587    pn.push(posneg);
588    match Date::from_str(y, m, d) {
589      Ok(date) => {
590        match TimeOffsetAmount::from_str(pn, oh, omin) {
591          Ok(offset) => {
592            match Time::from_str(h, min, s, None, Some(TimeOffset::Time(offset))) {
593              Ok(time) => Ok(Value::DateTime(DateTime::new(date, Some(time)))),
594              Err(error) => Err(error),
595            }
596          },
597          Err(error) => Result::Err(error),
598        }
599      },
600      Err(error) => Err(error),
601    }
602  }
603
604  /// Convenience function for creating a `Value::DateTime` containing a date and time with a timezone offset from UTC
605  /// from string values. Returns `Ok(DateTime)` on success and `Err(TOMLError)` on failure.
606  ///
607  /// # Examples
608  ///
609  /// ```
610  /// use tomllib::types::{Value, DateTime, Date, Time, TimeOffset, TimeOffsetAmount};
611  ///
612  /// assert_eq!(Value::DateTime(DateTime::new(Date::from_str("2011", "05", "11").unwrap(),
613  ///   Some(Time::from_str("02", "03", "04", None, Some(TimeOffset::Time(TimeOffsetAmount::from_str(
614  ///     "+", "09", "30"
615  ///   ).unwrap()))).unwrap()))),
616  ///   Value::datetime_offset_from_str("2011", "05", "11", "02", "03", "04", "+", "09", "30").unwrap());
617  /// ```
618  pub fn datetime_offset_from_str<S>(year: S, month: S, day: S, hour: S, minute: S, second: S, posneg: S, off_hour: S, off_minute: S) -> Result<Value<'a>, TOMLError> where S: Into<String> + Clone{
619    match Date::from_str(year.clone().into(), month.clone().into(), day.clone().into()) {
620      Ok(date) => {
621        match TimeOffsetAmount::from_str(posneg.clone().into(), off_hour.clone().into(), off_minute.clone().into()) {
622          Ok(offset) => {
623            match Time::from_str(hour.clone().into(), minute.clone().into(), second.clone().into(), None, Some(
624                    TimeOffset::Time(offset)
625                  )) {
626              Ok(time) => Ok(Value::DateTime(DateTime::new(date, Some(time)))),
627              Err(error) => Err(error),
628            }
629          },
630          Err(error) => Result::Err(error),
631        }
632      },
633      Err(error) => Err(error),
634    }
635  }
636
637  /// Convenience function for creating a `Value::DateTime` containing a date and time with a timezone of Zulu from
638  /// integer values. Returns Ok(DateTime)` on success and `Err(TOMLError)` on failure.
639  ///
640  /// # Examples
641  ///
642  /// ```
643  /// use tomllib::types::{Value, DateTime, Date, Time, TimeOffset};
644  ///
645  /// assert_eq!(Value::DateTime(DateTime::new(Date::from_str("2010", "04", "10").unwrap(),
646  ///   Some(Time::from_str("01", "02", "03", None, Some(TimeOffset::Zulu)).unwrap()))),
647  ///   Value::datetime_zulu_from_int(2010, 4, 10, 1, 2, 3).unwrap());
648  /// ```
649  pub fn datetime_zulu_from_int(year: usize, month: usize, day: usize, hour: usize, minute: usize, second: usize) -> Result<Value<'a>, TOMLError> {
650    let y = format!("{:0>4}", year);
651    let m = format!("{:0>2}", month);
652    let d = format!("{:0>2}", day);
653    let h = format!("{:0>2}", hour);
654    let min = format!("{:0>2}", minute);
655    let s = format!("{:0>2}", second);
656    match Date::from_str(y, m, d) {
657      Ok(date) => {
658        match Time::from_str(h, min, s, None, Some(TimeOffset::Zulu)) {
659          Ok(time) => Ok(Value::DateTime(DateTime::new(date, Some(time)))),
660          Err(error) => Err(error),
661        }
662      },
663      Err(error) => Err(error),
664    }
665  }
666
667  /// Convenience function for creating a `Value::DateTime` containing a date and time with a timezone of Zulu from
668  /// string values. Returns `Ok(DateTime)` on success and `Err(TOMLError)` on failure.
669  ///
670  /// # Examples
671  ///
672  /// ```
673  /// use tomllib::types::{Value, DateTime, Date, Time, TimeOffset};
674  ///
675  /// assert_eq!(Value::DateTime(DateTime::new(Date::from_str("2011", "05", "11").unwrap(),
676  ///   Some(Time::from_str("02", "03", "04", None, Some(TimeOffset::Zulu)).unwrap()))),
677  ///   Value::datetime_zulu_from_str("2011", "05", "11", "02", "03", "04").unwrap());
678  /// ```
679  pub fn datetime_zulu_from_str<S>(year: S, month: S, day: S, hour: S, minute: S, second: S) -> Result<Value<'a>, TOMLError> where S: Into<String> + Clone {
680    match Date::from_str(year.clone().into(), month.clone().into(), day.clone().into()) {
681      Ok(date) => {
682        match Time::from_str(hour.clone().into(), minute.clone().into(), second.clone().into(), None, Some(
683                TimeOffset::Zulu
684              )) {
685          Ok(time) => Ok(Value::DateTime(DateTime::new(date, Some(time)))),
686          Err(error) => Err(error),
687        }
688      },
689      Err(error) => Err(error),
690    }
691  }
692
693  /// Convenience function for creating a `Value::DateTime` containing a date and time with fractional seconds and a
694  /// timezone of Zulu from integer values, except for the plus/minus sign which is passed as a string value `"+"` or
695  /// "-"`. Returns `Ok(DateTime)` on success and `Err(TOMLError)` on failure. Note, you can't represent leading zeros
696  /// on the fractional part this way for example: `2016-03-15T08:05:22.00055Z` is not possible using this function.
697  ///
698  /// # Examples
699  ///
700  /// ```
701  /// use tomllib::types::{Value, DateTime, Date, Time, TimeOffset};
702  ///
703  /// assert_eq!(Value::DateTime(DateTime::new(Date::from_str("2010", "04", "10").unwrap(),
704  ///   Some(Time::from_str("01", "02", "03", Some("5678".into()), Some(TimeOffset::Zulu)).unwrap()))),
705  ///   Value::datetime_full_zulu_from_int(2010, 4, 10, 1, 2, 3, 5678).unwrap());
706  /// ```
707  pub fn datetime_full_zulu_from_int(year: usize, month: usize, day: usize, hour: usize, minute: usize, second: usize, frac: u64) -> Result<Value<'a>, TOMLError> {
708    let y = format!("{:0>4}", year);
709    let m = format!("{:0>2}", month);
710    let d = format!("{:0>2}", day);
711    let h = format!("{:0>2}", hour);
712    let min = format!("{:0>2}", minute);
713    let s = format!("{:0>2}", second);
714    let f = format!("{}", frac);
715    match Date::from_str(y, m, d) {
716      Ok(date) => {
717        match Time::from_str(h, min, s, Some(f), Some(TimeOffset::Zulu)) {
718          Ok(time) => Ok(Value::DateTime(DateTime::new(date, Some(time)))),
719          Err(error) => Err(error),
720        }
721      },
722      Err(error) => Err(error),
723    }
724  }
725
726  /// Convenience function for creating a `Value::DateTime` containing a date and time with fractional seconds and a
727  /// timezone of Zulu from string values. Returns `Ok(DateTime)` on success and `Err(TOMLError)` on failure.
728  ///
729  /// # Examples
730  ///
731  /// ```
732  /// use tomllib::types::{Value, DateTime, Date, Time, TimeOffset};
733  ///
734  /// assert_eq!(Value::DateTime(DateTime::new(Date::from_str("2011", "05", "11").unwrap(),
735  ///   Some(Time::from_str("02", "03", "04", None, Some(TimeOffset::Zulu)).unwrap()))),
736  ///   Value::datetime_zulu_from_str("2011", "05", "11", "02", "03", "04").unwrap());
737  /// ```
738  pub fn datetime_full_zulu_from_str<S>(year: S, month: S, day: S, hour: S, minute: S, second: S, frac: S) -> Result<Value<'a>, TOMLError> where S: Into<String> + Clone {
739    match Date::from_str(year.clone().into(), month.clone().into(), day.clone().into()) {
740      Ok(date) => {
741        match Time::from_str(hour.clone().into(), minute.clone().into(), second.clone().into(), Some(frac.clone().into()), Some(
742            TimeOffset::Zulu
743          )) {
744          Ok(time) => Ok(Value::DateTime(DateTime::new(date, Some(time)))),
745          Err(error) => Err(error),
746        }
747      },
748      Err(error) => Err(error),
749    }
750  }
751
752  /// Convenience function for creating a `Value::DateTime` containing a date and time with fractional seconds and a
753  /// timezone offset from UTC from integer values, except for the plus/minus sign which is passed as a char `"+"` or
754  /// `"-"`. Returns `Ok(DateTime)` on success and `Err(TOMLError)` on failure. Note, you can't represent
755  /// leading zeros on the fractional part this way for example: `2016-03-15T08:05:22.00055-11:00` is not possible using
756  /// this function.
757  ///
758  /// # Examples
759  ///
760  /// ```
761  /// use tomllib::types::{Value, DateTime, Date, Time, TimeOffset, TimeOffsetAmount};
762  ///
763  /// assert_eq!(Value::DateTime(DateTime::new(Date::from_str("2010", "04", "10").unwrap(),
764  ///   Some(Time::from_str("01", "02", "03", Some("135".into()), Some(TimeOffset::Time(TimeOffsetAmount::from_str(
765  ///     "-", "11", "00"
766  ///   ).unwrap()))).unwrap()))),
767  ///   Value::datetime_full_from_int(2010, 4, 10, 1, 2, 3, 135, '-', 11, 0).unwrap());
768  /// ```
769  pub fn datetime_full_from_int(year: usize, month: usize, day: usize, hour: usize, minute: usize, second: usize, frac: u64, posneg: char, off_hour: usize, off_minute: usize) -> Result<Value<'a>, TOMLError> {
770    let y = format!("{:0>4}", year);
771    let m = format!("{:0>2}", month);
772    let d = format!("{:0>2}", day);
773    let h = format!("{:0>2}", hour);
774    let min = format!("{:0>2}", minute);
775    let s = format!("{:0>2}", second);
776    let f = format!("{}", frac);
777    let oh = format!("{:0>2}", off_hour);
778    let omin = format!("{:0>2}", off_minute);
779    let mut pn = "".to_string();
780    pn.push(posneg);
781    match Date::from_str(y, m, d) {
782      Ok(date) => {
783        match TimeOffsetAmount::from_str(pn, oh, omin) {
784          Ok(offset) => {
785            match Time::from_str(h, min, s, Some(f), Some(TimeOffset::Time(offset))) {
786              Ok(time) => Ok(Value::DateTime(DateTime::new(date, Some(time)))),
787              Err(error) => Err(error),
788            }
789          },
790          Err(error) => Err(error),
791        }
792      },
793      Err(error) => Err(error),
794    }
795  }
796
797  /// Convenience function for creating a `Value::DateTime` containing a date and time with fractional seconds and a
798  /// timezone offset from UTC from string values. Returns `Ok(DateTime)` on success and `Err(TOMLError)` on failure.
799  ///
800  /// # Examples
801  ///
802  /// ```
803  /// use tomllib::types::{Value, DateTime, Date, Time, TimeOffset, TimeOffsetAmount};
804  ///
805  /// assert_eq!(Value::DateTime(DateTime::new(Date::from_str("2011", "05", "11").unwrap(),
806  ///   Some(Time::from_str("02", "03", "04", Some("0864".into()), Some(TimeOffset::Time(TimeOffsetAmount::from_str(
807  ///     "+", "09", "30"
808  ///   ).unwrap()))).unwrap()))),
809  ///   Value::datetime_full_from_str("2011", "05", "11", "02", "03", "04", "0864","+", "09", "30").unwrap());
810  /// ```
811  pub fn datetime_full_from_str<S>(year: S, month: S, day: S, hour: S, minute: S, second: S, frac: S, posneg: S, off_hour: S, off_minute: S) -> Result<Value<'a>, TOMLError> where S: Into<String> + Clone {
812    match Date::from_str(year.clone().into(), month.clone().into(), day.clone().into()) {
813      Ok(date) => {
814        match TimeOffsetAmount::from_str(posneg.clone().into(), off_hour.clone().into(), off_minute.clone().into()) {
815          Ok(offset) => {
816            match  Time::from_str(hour.clone().into(), minute.clone().into(), second.clone().into(), Some(frac.clone().into()), Some(
817                TimeOffset::Time(offset)
818              )) {
819              Ok(time) => Ok(Value::DateTime(DateTime::new(date, Some(time)))),
820              Err(error) => Err(error),
821            }
822          },
823          Err(error) => Err(error),
824        }
825      },
826      Err(error) => Err(error),
827    }
828  }
829
830  /// Convenience function for creating a `Value::DateTime` from a sinle string value.
831  ///
832  /// # Examples
833  ///
834  /// ```
835  /// use tomllib::types::{Value, DateTime, Date, Time, TimeOffset, TimeOffsetAmount};
836  ///
837  /// assert_eq!(Value::DateTime(DateTime::new(Date::from_str("2012", "06", "12").unwrap(),
838  ///   Some(Time::from_str("02", "03", "04", Some("0864".into()), Some(TimeOffset::Time(TimeOffsetAmount::from_str(
839  ///     "+", "10", "30"
840  ///   ).unwrap()))).unwrap()))),
841  ///   Value::datetime_parse("2012-06-12T02:03:04.0864+10:30").unwrap());
842  /// ```
843  pub fn datetime_parse<S>(dt: S) -> Result<Value<'a>, TOMLError> where S: Into<&'a str> {
844    let datetime = dt.into();
845    let p = Parser::new();
846    match p.date_time(datetime) {
847      (_, IResult::Done(i, o)) => {
848        let result = Value::DateTime(o);
849        if i.len() > 0 || !result.validate() {
850          return Result::Err(TOMLError::new(format!("Error parsing string as datetime. Argument: {}", datetime)));
851        } else {
852          return Result::Ok(result);
853        }
854      },
855      (_,_) => return Result::Err(TOMLError::new(format!("Error parsing string as datetime. Argument: {}", datetime))),
856    }
857  }
858
859  /// Convenience function for creating a `Value::String` with `StrType::Basic`. Returns Ok() on success and Err() on
860  /// failure.
861  ///
862  /// # Examples
863  ///
864  /// ```
865  /// use tomllib::types::{Value, StrType};
866  ///
867  /// assert_eq!(Value::String("foobar".into(), StrType::Basic), Value::basic_string("foobar").unwrap());
868  /// ```
869  pub fn basic_string<S>(s: S) -> Result<Value<'a>, TOMLError> where S: Into<String> + Clone {
870    let result = Value::String(s.clone().into().into(), StrType::Basic);
871    if result.validate() {
872      return Result::Ok(result);
873    } else {
874      return Result::Err(TOMLError::new(format!("Error parsing string as basic_string. Argument: {}", s.into())));
875    }
876  }
877
878  /// Convenience function for creating a `Value::String` with `StrType::MLBasic`. Returns Ok() on success and Err() on
879  /// failure.
880  ///
881  /// # Examples
882  ///
883  /// ```
884  /// use tomllib::types::{Value, StrType};
885  ///
886  /// assert_eq!(Value::String("foo\nbar".into(), StrType::MLBasic), Value::ml_basic_string("foo\nbar").unwrap());
887  /// ```
888  pub fn ml_basic_string<S>(s: S) -> Result<Value<'a>, TOMLError> where S: Into<String> + Clone {
889    let result = Value::String(s.clone().into().into(), StrType::MLBasic);
890    if result.validate() {
891      return Result::Ok(result);
892    } else {
893      return Result::Err(TOMLError::new(format!("Error parsing string as ml_basic_string. Argument: {}", s.into())));
894    }
895  }
896
897  /// Convenience function for creating a `Value::String` with `StrType::Literal`. Returns Ok() on success and Err() on
898  /// failure.
899  ///
900  /// # Examples
901  ///
902  /// ```
903  /// use tomllib::types::{Value, StrType};
904  ///
905  /// assert_eq!(Value::String("\"foobar\"".into(), StrType::Literal), Value::literal_string("\"foobar\"").unwrap());
906  /// ```
907  pub fn literal_string<S>(s: S) -> Result<Value<'a>, TOMLError> where S: Into<String> + Clone {
908    let result = Value::String(s.clone().into().into(), StrType::Literal);
909    if result.validate() {
910      return Result::Ok(result);
911    } else {
912      return Result::Err(TOMLError::new(format!("Error parsing string as literal_string. Argument: {}", s.into())));
913    }
914  }
915
916  /// Convenience function for creating a `Value::String` with `StrType::MLLiteral`. Returns Ok() on success and Err()
917  /// on failure.
918  ///
919  /// # Examples
920  ///
921  /// ```
922  /// use tomllib::types::{Value, StrType};
923  ///
924  /// assert_eq!(Value::String("\"foo\nbar\"".into(), StrType::MLLiteral),
925  ///   Value::ml_literal_string("\"foo\nbar\"").unwrap());
926  /// ```
927  pub fn ml_literal_string<S>(s: S) -> Result<Value<'a>, TOMLError> where S: Into<String> + Clone {
928    let result = Value::String(s.clone().into().into(), StrType::MLLiteral);
929    if result.validate() {
930      return Result::Ok(result);
931    } else {
932      return Result::Err(TOMLError::new(format!("Error parsing string as ml_literal_string. Argument: {}", s.into())));
933    }
934  }
935
936  /// Parses and validates a `Value`, returns true if the value is valid and false if it is invalid.
937  ///
938  /// # Examples
939  ///
940  /// ```
941  /// use tomllib::types::{Value};
942  ///
943  /// assert!(!Value::Integer("_989_721_".into()).validate()); // Integers may have underscores but they must be
944  ///                                                           // surrounded by digits
945  /// assert!(Value::Float("7.62".into()).validate());
946  /// ```
947  pub fn validate(&self) -> bool{
948    match self {
949      &Value::Integer(ref s) => {
950        let p = Parser::new();
951        match p.integer(s) {
952           (_, IResult::Done(_, _)) => true,
953           (_,_) => false,
954        }
955      },
956      &Value::Float(ref s) => {
957        let p = Parser::new();
958        match p.float(s) {
959           (_, IResult::Done(_, _)) => true,
960           (_,_) => false,
961        }
962      },
963      &Value::DateTime(ref dt) => {dt.validate()},
964      &Value::String(ref s, st) => {
965        match st {
966          StrType::Basic => {
967            match Parser::quoteless_basic_string(s) {
968              IResult::Done(i,_) => i.len() == 0,
969              _ => false,
970            }
971          },
972          StrType::MLBasic => {
973            match Parser::quoteless_ml_basic_string(s) {
974              IResult::Done(i,_) => i.len() == 0,
975              _ => false,
976            }
977          },
978          StrType::Literal => {
979            match Parser::quoteless_literal_string(s) {
980              IResult::Done(i,_) => i.len() == 0,
981              _ => false,
982            }
983          },
984          StrType::MLLiteral => {
985            match Parser::quoteless_ml_literal_string(s) {
986              IResult::Done(i,_) => i.len() == 0,
987              _ => false,
988            }
989          },
990        }
991      },
992      _ => true,
993    }
994  }
995}
996
997/// Error type returned by `Value` creation convenience functions on invalid input.
998#[derive(Debug)]
999pub struct TOMLError {
1000  message: String,
1001}
1002
1003impl Error for TOMLError {
1004
1005  /// Gives a description of the error encountered when validating input to a `Value` creation function.
1006  ///
1007  /// # Examples
1008  ///
1009  /// ```
1010  /// use std::error::Error;
1011  /// use tomllib::types::Value;
1012  ///
1013  /// if let Err(toml_err) = Value::basic_string("foo\n") {
1014  ///   println!("{}", toml_err.description());
1015  /// }
1016  /// # else {
1017  /// #   assert!(false);
1018  /// # }
1019  /// ```
1020  fn description(&self) -> &str {
1021    &self.message
1022  }
1023
1024  /// Returns an `Error` that caused the current `Error`. Always returns `None`.
1025  fn cause(&self) -> Option<&Error> { None }
1026}
1027
1028impl Display for TOMLError {
1029  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1030    write!(f, "{}", self.message)
1031  }
1032}
1033
1034impl TOMLError {
1035  fn new(msg: String) -> TOMLError {
1036    warn!("{}", msg);
1037    TOMLError{message: msg}
1038  }
1039}
1040
1041/// Represents a plus sign or minus sign for positive and negative timezone offsets.
1042#[derive(Debug, Eq, PartialEq, Clone, Copy)]
1043pub enum PosNeg {
1044  /// A plus sign representing a positive timezone offset.
1045  Pos,
1046  /// A minus sign representing a negaive timezone offset.
1047  Neg,
1048}
1049
1050impl Display for PosNeg {
1051  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1052    match self {
1053      &PosNeg::Pos => write!(f, "+"),
1054      &PosNeg::Neg => write!(f, "-"),
1055    }
1056
1057  }
1058}
1059
1060/// Represents either a timezone of Zulu or or hour plus minute timezone offset from UTC.
1061#[derive(Debug, Eq, Clone)]
1062pub enum TimeOffset<'a> {
1063  // Timezone [Zulu](https://en.wikipedia.org/wiki/List_of_military_time_zones), also known as Greenwich Mean Time or
1064  // Coordinated Universal Time (UTC).
1065  Zulu,
1066  // Contains a `TimeOffsetAmount` with the hours and minutes offset from UTC.
1067  Time(TimeOffsetAmount<'a>),
1068}
1069
1070impl<'a> PartialEq for TimeOffset<'a> {
1071  fn eq(&self, other: &TimeOffset<'a>) -> bool {
1072    match (self, other) {
1073      (&TimeOffset::Zulu, &TimeOffset::Zulu) => true,
1074      (&TimeOffset::Time(ref i), &TimeOffset::Time(ref j)) if(i == j) => true,
1075      _ => false
1076    }
1077  }
1078}
1079
1080impl<'a> Display for TimeOffset<'a> {
1081  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1082    match self {
1083      &TimeOffset::Zulu => write!(f, "Z"),
1084      &TimeOffset::Time(ref t) => write!(f, "{}", t),
1085    }
1086  }
1087}
1088
1089impl<'a> TimeOffset<'a> {
1090  pub fn validate(&self) -> bool {
1091    match self {
1092      &TimeOffset::Zulu => return true,
1093      &TimeOffset::Time(ref amount) => return amount.validate(),
1094    }
1095  }
1096}
1097
1098/// A positive or negative amount of hours and minutes offset from UTC.
1099#[derive(Debug, Eq, Clone)]
1100pub struct TimeOffsetAmount<'a> {
1101  /// Represents whether the offset is positive or negative.
1102  pub pos_neg: PosNeg,
1103  /// Represents the number of hours that time is offset from UTC.Must be 2 decimal digits between 0 23 inclusive.
1104  pub hour: Cow<'a, str>,
1105  /// Represents the number of minutes that time is offset from UTC. Must be 2 decimal digits between 0 59 inclusive.
1106  pub minute: Cow<'a, str>,
1107}
1108
1109impl<'a> PartialEq for TimeOffsetAmount<'a> {
1110  fn eq(&self, other: &TimeOffsetAmount<'a>) -> bool {
1111    self.pos_neg == other.pos_neg &&
1112    self.hour == other.hour &&
1113    self.minute == other.minute
1114  }
1115}
1116
1117impl<'a> Display for TimeOffsetAmount<'a> {
1118  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1119    write!(f, "{}{}:{}", self.pos_neg, &self.hour, &self.minute)
1120  }
1121}
1122
1123impl<'a> TimeOffsetAmount<'a> {
1124
1125  /// Create a new `TimeOffsetAmount` from string type values. Returns `Ok()` on success and `Err()` on failure.
1126  ///
1127  /// # Examples
1128  /// ```
1129  /// use tomllib::types::TimeOffsetAmount;
1130  ///
1131  /// let offset = TimeOffsetAmount::from_str("-", "04", "00").unwrap();
1132  /// ```
1133  pub fn from_str<S>(pos_neg: S, hour: S, minute: S) -> Result<TimeOffsetAmount<'a>, TOMLError> where S: Into<String>{
1134    let pn = match pos_neg.into().as_ref() {
1135      "+" => PosNeg::Pos,
1136      "-" => PosNeg::Neg,
1137      _   => return Result::Err(TOMLError::new("pos_neg value is neither a '+' or a '-'.".to_string())),
1138    };
1139    let offset = TimeOffsetAmount{pos_neg: pn, hour: hour.into().into(), minute: minute.into().into()};
1140    if offset.validate() {
1141      return Result::Ok(offset);
1142    } else {
1143      return Result::Err(TOMLError::new("Error validating TimeOffsetAmount.".to_string()));
1144    }
1145  }
1146
1147  /// Validates a created `TimeOffsetAmount`.
1148  ///
1149  /// # Examples
1150  ///
1151  /// ```
1152  /// use tomllib::types::{TimeOffsetAmount, PosNeg};
1153  ///
1154  /// let offset_wrong = TimeOffsetAmount{pos_neg: PosNeg::Pos, hour: "31".into(), minute: "30".into()};
1155  /// let offset_right = TimeOffsetAmount{pos_neg: PosNeg::Pos, hour: "07".into(), minute: "00".into()};
1156  /// assert!(!offset_wrong.validate());
1157  /// assert!(offset_right.validate());
1158  /// ```
1159  pub fn validate(&self) -> bool {
1160    if self.hour.len() != 2 || self.minute.len() != 2 {
1161      return false;
1162    }
1163    return self.validate_numbers();
1164   }
1165
1166  fn validate_numbers(&self) -> bool {
1167    if let Ok(h) = usize::from_str(&self.hour) {
1168      if h > 23 {
1169        return false;
1170      }
1171    } else {
1172      return false;
1173    }
1174    if let Ok(m) = usize::from_str(&self.minute) {
1175      if m > 59 {
1176        return false;
1177      }
1178    } else {
1179      return false;
1180    }
1181    return true;
1182  }
1183}
1184
1185/// Represents a date value.
1186// <year>-<month>-<day>
1187#[derive(Debug, Eq, Clone)]
1188pub struct Date<'a> {
1189  /// Represents the year of a date. Must be 4 decimal digits greater than 0".
1190  pub year: Cow<'a, str>,
1191  /// Represents the month of a date. Must be 2 decimal digits greater than 0 less than 13.
1192  pub month: Cow<'a, str>,
1193  /// Represents the day of a date. Must be 2 decimal digits greater than 0less than 28, 29, 30, or 31 depending on the
1194  /// month and whether the year is a leap year.
1195  pub day: Cow<'a, str>,
1196}
1197
1198impl<'a> PartialEq for Date<'a> {
1199  fn eq(&self, other: &Date<'a>) -> bool {
1200    self.year == other.year &&
1201    self.month == other.month &&
1202    self.day == other.day
1203  }
1204}
1205
1206impl<'a> Display for Date<'a> {
1207  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1208    write!(f, "{}-{}-{}", self.year, self.month, self.day)
1209  }
1210}
1211
1212impl<'a> Date<'a> {
1213
1214  /// Create a new `Date` from string type values. Returns `Ok()` on success and `Err()` on failure.
1215  ///
1216  /// # Examples
1217  /// ```
1218  /// use tomllib::types::Date;
1219  ///
1220  /// let date = Date::from_str("1991", "09", "23").unwrap();
1221  /// ```
1222  pub fn from_str<S>(year: S, month: S, day: S) -> Result<Date<'a>, TOMLError> where S: Into<String> {
1223    let date = Date{year: year.into().into(), month: month.into().into(), day: day.into().into()};
1224    if date.validate() {
1225      Ok(date)
1226    } else {
1227      Err(TOMLError::new("Error validating Date.".to_string()))
1228    }
1229  }
1230
1231  /// Validates a created `Date`.
1232  ///
1233  /// # Examples
1234  ///
1235  /// ```
1236  /// use tomllib::types::Date;
1237  ///
1238  /// let date_wrong = Date{year: "76563".into(), month: "10".into(), day: "20".into()};
1239  /// let date_right = Date{year: "1763".into(), month: "10".into(), day: "20".into()};
1240  /// assert!(!date_wrong.validate());
1241  /// assert!(date_right.validate());
1242  /// ```
1243  pub fn validate(&self) -> bool {
1244    if self.year.len() != 4 || self.month.len() != 2 || self.day.len() != 2 {
1245      return false;
1246    }
1247    return self.validate_numbers();
1248  }
1249
1250  fn validate_numbers(&self) -> bool {
1251    if let Ok(y) = usize::from_str(&self.year) {
1252      if y == 0 || y > 9999{
1253        return false;
1254      }
1255      if let Ok(m) = usize::from_str(&self.month) {
1256        if m < 1 || m > 12 {
1257          return false;
1258        }
1259        if let Ok(d) = usize::from_str(&self.day) {
1260          if d < 1 {
1261            return false;
1262          }
1263          match m {
1264            2 => {
1265              let leap_year;
1266              if y % 4 != 0 {
1267                leap_year = false;
1268              } else if y % 100 != 0 {
1269                leap_year = true;
1270              } else if y % 400 != 0 {
1271                leap_year = false;
1272              } else {
1273                leap_year = true;
1274              }
1275              if leap_year && d > 29 {
1276                return false;
1277              } else if !leap_year && d > 28 {
1278                return false;
1279              }
1280            },
1281            1 | 3 | 5 | 7 | 8 | 10 | 12 => { if d > 31 { return false; } },
1282            _ => { if d > 30 { return false; } },
1283          }
1284        } else {
1285          return false;
1286        }
1287      } else {
1288        return false;
1289      }
1290    } else {
1291      return false;
1292    }
1293    return true;
1294  }
1295}
1296
1297/// Represents the time part of a `DateTime` including optional fractional seconds and timezone offset.
1298#[derive(Debug, Eq, Clone)]
1299pub struct Time<'a> {
1300  /// Represents the hour of the time. Must be 2 decimal digits between 0 and 23 inclusive.
1301  pub hour: Cow<'a, str>,
1302  /// Represents the minute of the time. Must be 2 decimal digits between 0 and 59 inclusive.
1303  pub minute: Cow<'a, str>,
1304  /// Represent the second of the time. Must be 2 decimal digits between 0 and 59 inclusive.
1305  pub second: Cow<'a, str>,
1306  /// Optional fraction of a second of the time. Can be an arbitrary number of decimal digits.
1307  pub fraction: Option<Cow<'a, str>>,
1308  /// Optional time zone offset.
1309  pub offset: Option<TimeOffset<'a>>,
1310}
1311
1312impl<'a> PartialEq for Time<'a> {
1313  fn eq(&self, other: &Time<'a>) -> bool {
1314    self.hour == other.hour &&
1315    self.minute == other.minute &&
1316    self.second == other.second &&
1317    self.fraction == other.fraction &&
1318    self.offset == other.offset
1319  }
1320}
1321
1322impl<'a> Display for Time<'a> {
1323  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1324    match (&self.fraction, &self.offset) {
1325      (&Some(ref frac), &Some(ref offset))  => write!(f, "T{}:{}:{}.{}{}", self.hour, self.minute, self.second, frac, offset),
1326      (&Some(ref frac), &None)              => write!(f, "T{}:{}:{}.{}", self.hour, self.minute, self.second, frac),
1327      (&None, &Some(ref offset))            => write!(f, "T{}:{}:{}{}", self.hour, self.minute, self.second, offset),
1328      (&None, &None)                        => write!(f, "T{}:{}:{}", self.hour, self.minute, self.second),
1329    }
1330  }
1331}
1332
1333impl<'a> Time<'a> {
1334
1335  /// Create a new `Time` from string type values. Returns `Ok()` on success and `Err()` on failure.
1336  ///
1337  /// # Examples
1338  /// ```
1339  /// use tomllib::types::Time;
1340  ///
1341  /// let time = Time::from_str("19", "33", "02", None, None).unwrap();
1342  /// ```
1343  pub fn from_str<S>(hour: S, minute: S, second: S, fraction: Option<S>, offset: Option<TimeOffset<'a>>)
1344    -> Result<Time<'a>, TOMLError> where S: Into<String> {
1345    if let Some(s) = fraction {
1346      let time = Time{hour: hour.into().into(), minute: minute.into().into(), second: second.into().into(),
1347        fraction: Some(s.into().into()), offset: offset};
1348      if time.validate() {
1349        return Ok(time);
1350      } else {
1351        return Err(TOMLError::new("Error validating Time.".to_string()));
1352      }
1353    } else {
1354      let time = Time{hour: hour.into().into(), minute: minute.into().into(), second: second.into().into(),
1355        fraction: None, offset: offset};
1356      if time.validate() {
1357        return Ok(time);
1358      } else {
1359        return Err(TOMLError::new("Error validating Time.".to_string()));
1360      }
1361    }
1362  }
1363
1364  /// Validates a created `Time`.
1365  ///
1366  /// # Examples
1367  ///
1368  /// ```
1369  /// use tomllib::types::Time;
1370  ///
1371  /// let time_wrong = Time{hour: "23".into(), minute: "79".into(), second: "20".into(),
1372  ///   fraction: None, offset: None};
1373  /// let time_right = Time{hour: "11".into(), minute: "53".into(), second: "25".into(),
1374  ///   fraction: None, offset: None};
1375  /// assert!(!time_wrong.validate());
1376  /// assert!(time_right.validate());
1377  /// ```
1378  pub fn validate(&self) -> bool {
1379    if self.hour.len() != 2 || self.minute.len() != 2 || self.second.len() != 2 {
1380      return false;
1381    }
1382    return self.validate_numbers();
1383  }
1384
1385  fn validate_numbers(&self) -> bool {
1386    if let Ok(h) = usize::from_str(&self.hour) {
1387      if h > 23 {
1388        return false;
1389      }
1390    } else {
1391      return false;
1392    }
1393    if let Ok(m) = usize::from_str(&self.minute) {
1394      if m > 59 {
1395        return false;
1396      }
1397    } else {
1398      return false;
1399    }
1400    if let Ok(s) = usize::from_str(&self.second) {
1401      if s > 59 {
1402        return false;
1403      }
1404    } else {
1405      return false;
1406    }
1407    if let Some(ref frac) = self.fraction {
1408      if u64::from_str(frac).is_err() {
1409        return false;
1410      }
1411    }
1412    if let Some(ref off) = self.offset {
1413      if !off.validate() {
1414        return false;
1415      }
1416    }
1417    return true;
1418  }
1419}
1420
1421/// Represents a`DateTime` including the `Date` and optional `Time`
1422#[derive(Debug, Eq, Clone)]
1423pub struct DateTime<'a> {
1424  pub date: Date<'a>,
1425  pub time: Option<Time<'a>>,
1426}
1427
1428impl<'a> PartialEq for DateTime<'a> {
1429  fn eq(&self, other: &DateTime<'a>) -> bool {
1430    self.date == other.date &&
1431    self.time == other.time
1432  }
1433}
1434
1435impl<'a> Display for DateTime<'a> {
1436  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1437    match &self.time {
1438      &Some(ref time) => write!(f, "{}{}", self.date, time),
1439      &None => write!(f, "{}", self.date),
1440    }
1441  }
1442}
1443
1444// <hour>:<minute>:<second>(.<fraction>)?
1445impl<'a> DateTime<'a> {
1446  pub fn new(date: Date<'a>, time: Option<Time<'a>>) -> DateTime<'a> {
1447    DateTime{date: date, time: time}
1448  }
1449
1450  /// Validates a created `DateTime`.
1451  ///
1452  /// # Examples
1453  ///
1454  /// ```
1455  /// use tomllib::types::{DateTime, Date};
1456  ///
1457  /// let dt_wrong = DateTime{ date: Date{ year: "53456".into(), month: "06".into(), day: "20".into() }, time: None};
1458  /// let dt_right = DateTime{ date: Date{ year: "1995".into(), month: "09".into(), day: "13".into() }, time: None};
1459  /// assert!(!dt_wrong.validate());
1460  /// assert!(dt_right.validate());
1461  /// ```
1462  pub fn validate(&self) -> bool {
1463    if self.date.validate() {
1464      if let Some(ref time) = self.time {
1465        return time.validate();
1466      }
1467    } else {
1468      return false;
1469    }
1470    return true;
1471  }
1472}
1473
1474#[cfg(test)]
1475mod test {
1476  use std::cell::{Cell, RefCell};
1477  use std::rc::Rc;
1478  use types::{Children, Value, Date, Time, DateTime, TimeOffset, TimeOffsetAmount, StrType};
1479
1480  #[test]
1481  fn test_combine_keys() {
1482    assert_eq!("foo.bar.baz".to_string(), Children::combine_keys("foo.bar", "baz"));
1483  }
1484
1485  #[test]
1486  fn test_combine_keys_index() {
1487    assert_eq!("foo.bar[9]".to_string(), Children::combine_keys_index("foo.bar", 9));
1488  }
1489
1490  #[test]
1491  fn test_combine_child_keys() {
1492    let kids = Children::Keys(RefCell::new(vec!["baz".to_string(), "qux".to_string(), "plugh".to_string(),
1493      "thud".to_string()]));
1494    assert_eq!(vec!["foo.bar.baz".to_string(), "foo.bar.qux".to_string(), "foo.bar.plugh".to_string(),
1495      "foo.bar.thud".to_string()], kids.combine_child_keys("foo.bar".to_string()));
1496  }
1497
1498  #[test]
1499  fn test_combine_child_keys_empty_base() {
1500    let kids = Children::Keys(RefCell::new(vec!["baz".to_string(), "qux".to_string(), "plugh".to_string(),
1501      "thud".to_string()]));
1502    assert_eq!(vec!["baz".to_string(), "qux".to_string(), "plugh".to_string(),
1503      "thud".to_string()], kids.combine_child_keys("".to_string()));
1504  }
1505
1506  #[test]
1507  fn test_combine_child_keys_index() {
1508    let kids = Children::Count(Cell::new(3));
1509    assert_eq!(vec!["foo.bar[0]".to_string(), "foo.bar[1]".to_string(), "foo.bar[2]".to_string()],
1510      kids.combine_child_keys("foo.bar".to_string()));
1511  }
1512
1513  #[test]
1514  fn test_value_display() {
1515    let val_int = Value::Integer("7778877".into());
1516    let val_float = Value::Float("1929.345".into());
1517    let val_true = Value::Boolean(true);
1518    let val_false = Value::Boolean(false);
1519    let val_datetime = Value::DateTime(DateTime::new(Date::new_str("9999", "12", "31"), Some(Time::new_str(
1520      "23", "59", "59", Some("9999999"), Some(TimeOffset::Time(TimeOffsetAmount::new_str(
1521        "-", "00", "00"
1522      )))
1523    ))));
1524    let val_basic_str = Value::String("foobar1".into(), StrType::Basic);
1525    let val_literal_str = Value::String("foobar2".into(), StrType::Literal);
1526    let val_ml_basic_str = Value::String("foobar3".into(), StrType::MLBasic);
1527    let val_ml_literal_str = Value::String("foobar4".into(), StrType::MLLiteral);
1528    let val_array = Value::Array(Rc::new(vec![Value::Integer("3000".into()),
1529      Value::Array(Rc::new(vec![Value::Integer("40000".into()), Value::Float("50.5".into())])),
1530      Value::String("barbaz".into(), StrType::Literal)]));
1531    let val_inline_table = Value::InlineTable(Rc::new(vec![
1532      ("foo".into(), Value::Boolean(true)), ("bar".into(), Value::InlineTable(Rc::new(vec![
1533        ("baz".into(), Value::Boolean(false)), ("qux".into(), Value::Integer("2016".into())),
1534      ]))), ("plugh".into(), Value::Float("3333.444".into()))
1535    ]));
1536
1537    assert_eq!("7778877", &format!("{}", val_int));
1538    assert_eq!("1929.345", &format!("{}", val_float));
1539    assert_eq!("true", &format!("{}", val_true));
1540    assert_eq!("false", &format!("{}", val_false));
1541    assert_eq!("9999-12-31T23:59:59.9999999-00:00", &format!("{}", val_datetime));
1542    assert_eq!("\"foobar1\"", &format!("{}", val_basic_str));
1543    assert_eq!("'foobar2'", &format!("{}", val_literal_str));
1544    assert_eq!("\"\"\"foobar3\"\"\"", &format!("{}", val_ml_basic_str));
1545    assert_eq!("'''foobar4'''", &format!("{}", val_ml_literal_str));
1546    assert_eq!("[3000, [40000, 50.5], 'barbaz']", &format!("{}", val_array));
1547    assert_eq!("{foo = true, bar = {baz = false, qux = 2016}, plugh = 3333.444}",
1548      &format!("{}", val_inline_table));
1549  }
1550
1551  #[test]
1552  fn test_create_int() {
1553    assert_eq!(Value::Integer("9223372036854775807".into()), Value::int(9223372036854775807));
1554  }
1555
1556  #[test]
1557  fn test_create_int_from_str() {
1558    assert_eq!(Value::Integer("-9223372036854775808".into()), Value::int_from_str("-9223372036854775808").unwrap());
1559  }
1560
1561  #[test]
1562  fn test_create_int_from_str_fail() {
1563    assert!(Value::int_from_str("q-9223$37(203)[]M807").is_err());
1564  }
1565
1566  #[test]
1567  fn test_create_float() {
1568    assert_eq!(Value::Float("179769000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000".into()), Value::float(1.79769e+308));
1569  }
1570
1571  #[test]
1572  fn test_create_float_from_str() {
1573    assert_eq!(Value::Float("2.22507e-308".into()), Value::float_from_str("2.22507e-308").unwrap());
1574  }
1575
1576  #[test]
1577  fn test_create_float_from_str_fail() {
1578    assert!(Value::float_from_str("q2.3e++10eipi").is_err());
1579  }
1580
1581  #[test]
1582  fn test_create_bool() {
1583    assert_eq!(Value::Boolean(false), Value::bool(false));
1584  }
1585
1586  #[test]
1587  fn test_create_bool_from_str() {
1588    assert_eq!(Value::Boolean(true), Value::bool_from_str("TrUe").unwrap());
1589  }
1590
1591  #[test]
1592  fn test_create_bool_from_str_fail() {
1593    assert!(Value::bool_from_str("TFraulese").is_err());
1594  }
1595
1596  #[test]
1597  fn test_create_date_from_int() {
1598    assert_eq!(Value::DateTime(DateTime::new(Date::new_str("2012", "01", "03"), None)),
1599      Value::date_from_int(2012, 1, 3).unwrap());
1600  }
1601
1602  #[test]
1603  fn test_create_date_from_int_fail() {
1604    assert!(Value::date_from_int(0, 2, 20).is_err());
1605    assert!(Value::date_from_int(2016, 0, 20).is_err());
1606    assert!(Value::date_from_int(2016, 1, 0).is_err());
1607    assert!(Value::date_from_int(2016, 1, 32).is_err());
1608    assert!(Value::date_from_int(2016, 4, 31).is_err());
1609    assert!(Value::date_from_int(2016, 2, 30).is_err());
1610    assert!(Value::date_from_int(2015, 2, 29).is_err());
1611    assert!(Value::date_from_int(1900, 2, 29).is_err());
1612    assert!(Value::date_from_int(2000, 2, 30).is_err());
1613  }
1614
1615  #[test]
1616  fn test_create_date_from_str() {
1617    assert_eq!(Value::DateTime(DateTime::new(Date::new_str("2012", "01", "03"), None)),
1618      Value::date_from_str("2012", "01", "03").unwrap());
1619  }
1620
1621  #[test]
1622  fn test_create_date_from_str_fail() {
1623    assert!(Value::date_from_str("12345", "01", "01").is_err());
1624    assert!(Value::date_from_str("2016", "012", "01").is_err());
1625    assert!(Value::date_from_str("2016", "01", "012").is_err());
1626    assert!(Value::date_from_str("201q", "01", "01").is_err());
1627    assert!(Value::date_from_str("2016", "0q", "01").is_err());
1628    assert!(Value::date_from_str("2016", "01", "0q").is_err());
1629    assert!(Value::date_from_str("201", "01", "01").is_err());
1630    assert!(Value::date_from_str("2016", "1", "01").is_err());
1631    assert!(Value::date_from_str("2016", "01", "1").is_err());
1632  }
1633
1634  #[test]
1635  fn test_create_datetime_from_int() {
1636    assert_eq!(Value::DateTime(DateTime::new(Date::new_str("2012", "01", "03"), Some(Time::new_str(
1637      "03", "30", "30", None, None
1638    )))), Value::datetime_from_int(2012, 1, 3, 3, 30, 30).unwrap());
1639  }
1640
1641  #[test]
1642  fn test_create_datetime_from_int_fail() {
1643    assert!(Value::datetime_from_int(2012, 1, 3, 24, 30, 30).is_err());
1644    assert!(Value::datetime_from_int(2012, 1, 3, 3, 60, 30).is_err());
1645    assert!(Value::datetime_from_int(2012, 1, 3, 3, 30, 60).is_err());
1646  }
1647
1648  #[test]
1649  fn test_create_datetime_from_str() {
1650    assert_eq!(Value::DateTime(DateTime::new(Date::new_str("2012", "01", "03"), Some(Time::new_str(
1651      "03", "30", "30", None, None
1652    )))), Value::datetime_from_str("2012", "01", "03", "03", "30", "30").unwrap());
1653  }
1654
1655  #[test]
1656  fn test_create_datetime_from_str_fail() {
1657    assert!(Value::datetime_from_str("2012", "01", "03", "3", "30", "30").is_err());
1658    assert!(Value::datetime_from_str("2012", "01", "03", "03", "3", "30").is_err());
1659    assert!(Value::datetime_from_str("2012", "01", "03", "03", "30", "3").is_err());
1660    assert!(Value::datetime_from_str("2012", "01", "03", "033", "30", "30").is_err());
1661    assert!(Value::datetime_from_str("2012", "01", "03", "03", "303", "303").is_err());
1662    assert!(Value::datetime_from_str("2012", "01", "03", "0q", "30", "30").is_err());
1663    assert!(Value::datetime_from_str("2012", "01", "03", "03", "3q", "30").is_err());
1664    assert!(Value::datetime_from_str("2012", "01", "03", "03", "30", "3q").is_err());
1665  }
1666
1667  #[test]
1668  fn test_create_datetime_frac_from_int() {
1669    assert_eq!(Value::DateTime(DateTime::new(Date::new_str("2012", "01", "03"), Some(Time::new_str(
1670      "03", "30", "30", Some("3030"), None
1671    )))), Value::datetime_frac_from_int(2012, 1, 3, 3, 30, 30, 3030).unwrap());
1672  }
1673
1674  #[test]
1675  fn test_create_datetime_frac_from_int_fail() {
1676    assert!(Value::datetime_frac_from_int(2012, 1, 0, 3, 30, 30, 3030).is_err());
1677  }
1678
1679  #[test]
1680  fn test_create_datetime_frac_from_str() {
1681    assert_eq!(Value::DateTime(DateTime::new(Date::new_str("2012", "01", "03"), Some(Time::new_str(
1682      "03", "30", "30", Some("3030"), None
1683    )))), Value::datetime_frac_from_str("2012", "01", "03", "03", "30", "30", "3030").unwrap());
1684  }
1685
1686  #[test]
1687  fn test_create_datetime_frac_from_str_fail() {
1688    assert!(Value::datetime_frac_from_str("2012", "01", "03", "03", "30", "30", "q3030").is_err());
1689  }
1690
1691  #[test]
1692  fn test_create_datetime_offset_from_int() {
1693    assert_eq!(Value::DateTime(DateTime::new(Date::new_str("2012", "01", "03"), Some(Time::new_str(
1694      "03", "30", "30", None, Some(TimeOffset::Time(TimeOffsetAmount::new_str(
1695        "+", "07", "45"
1696      )))
1697    )))), Value::datetime_offset_from_int(2012, 1, 3, 3, 30, 30, '+', 7, 45).unwrap());
1698  }
1699
1700  #[test]
1701  fn test_create_datetime_offset_from_int_fail() {
1702    assert!(Value::datetime_offset_from_int(2012, 1, 3, 3, 30, 30, 'q', 7, 45).is_err());
1703    assert!(Value::datetime_offset_from_int(2012, 1, 3, 3, 30, 30, '+', 24, 45).is_err());
1704    assert!(Value::datetime_offset_from_int(2012, 1, 3, 3, 30, 30, '+', 7, 60).is_err());
1705  }
1706
1707  #[test]
1708  fn test_create_datetime_offset_from_str() {
1709    assert_eq!(Value::DateTime(DateTime::new(Date::new_str("2012", "01", "03"), Some(Time::new_str(
1710      "03", "30", "30", None, Some(TimeOffset::Time(TimeOffsetAmount::new_str(
1711        "+", "07", "45"
1712      )))
1713    )))), Value::datetime_offset_from_str("2012", "01", "03", "03", "30", "30", "+", "07", "45").unwrap());
1714  }
1715
1716  #[test]
1717  fn test_create_datetime_offset_from_str_fail() {
1718    assert!(Value::datetime_offset_from_str("2012", "01", "03", "03", "30", "30", "+", "077", "45").is_err());
1719    assert!(Value::datetime_offset_from_str("2012", "01", "03", "03", "30", "30", "+", "07", "455").is_err());
1720    assert!(Value::datetime_offset_from_str("2012", "01", "03", "03", "30", "30", "+", "7", "45").is_err());
1721    assert!(Value::datetime_offset_from_str("2012", "01", "03", "03", "30", "30", "+", "07", "5").is_err());
1722    assert!(Value::datetime_offset_from_str("2012", "01", "03", "03", "30", "30", "q", "07", "45").is_err());
1723  }
1724
1725  #[test]
1726  fn test_create_datetime_zulu_from_int() {
1727    assert_eq!(Value::DateTime(DateTime::new(Date::new_str("2012", "01", "03"), Some(Time::new_str(
1728      "03", "30", "30", None, Some(TimeOffset::Zulu)
1729    )))), Value::datetime_zulu_from_int(2012, 1, 3, 3, 30, 30).unwrap());
1730  }
1731
1732  #[test]
1733  fn test_create_datetime_zulu_from_int_fail() {
1734    assert!(Value::datetime_zulu_from_int(2012, 1, 0, 3, 30, 30).is_err());
1735  }
1736
1737  #[test]
1738  fn test_create_datetime_zulu_from_str() {
1739    assert_eq!(Value::DateTime(DateTime::new(Date::new_str("2012", "01", "03"), Some(Time::new_str(
1740      "03", "30", "30", None, Some(TimeOffset::Zulu)
1741    )))), Value::datetime_zulu_from_str("2012", "01", "03", "03", "30", "30").unwrap());
1742  }
1743
1744  #[test]
1745  fn test_create_datetime_zulu_from_str_fail() {
1746    assert!(Value::datetime_zulu_from_str("q2012", "01", "03", "03", "30", "30").is_err());
1747  }
1748
1749  #[test]
1750  fn test_create_datetime_full_zulu_from_int() {
1751    assert_eq!(Value::DateTime(DateTime::new(Date::new_str("2012", "01", "03"), Some(Time::new_str(
1752      "03", "30", "30", Some("3030"), Some(TimeOffset::Zulu)
1753    )))), Value::datetime_full_zulu_from_int(2012, 1, 3, 3, 30, 30, 3030).unwrap());
1754  }
1755
1756  #[test]
1757  fn test_create_datetime_full_zulu_from_int_fail() {
1758    assert!(Value::datetime_full_zulu_from_int(2012, 1, 0, 3, 30, 30, 3030).is_err());
1759  }
1760
1761  #[test]
1762  fn test_create_datetime_full_zulu_from_str() {
1763    assert_eq!(Value::DateTime(DateTime::new(Date::new_str("2012", "01", "03"), Some(Time::new_str(
1764      "03", "30", "30", Some("3030"), Some(TimeOffset::Zulu)
1765    )))), Value::datetime_full_zulu_from_str("2012", "01", "03", "03", "30", "30", "3030").unwrap());
1766  }
1767
1768  #[test]
1769  fn test_create_datetime_full_zulu_from_str_fail() {
1770    assert!(Value::datetime_full_zulu_from_str("q2012", "01", "03", "03", "30", "30", "3030").is_err());
1771  }
1772
1773  #[test]
1774  fn test_create_datetime_full_from_int() {
1775    assert_eq!(Value::DateTime(DateTime::new(Date::new_str("2012", "01", "03"), Some(Time::new_str(
1776      "03", "30", "30", Some("3030"), Some(TimeOffset::Time(TimeOffsetAmount::new_str(
1777        "+", "07", "45"
1778      )))
1779    )))), Value::datetime_full_from_int(2012, 1, 3, 3, 30, 30, 3030, '+', 7, 45).unwrap());
1780  }
1781
1782  #[test]
1783  fn test_create_datetime_full_from_int_fail() {
1784    assert!(Value::datetime_full_from_int(2012, 1, 0, 3, 30, 30, 3030, '+', 7, 45).is_err());
1785    assert!(Value::datetime_full_from_int(2012, 13, 0, 3, 30, 30, 3030, '+', 7, 45).is_err());
1786    assert!(Value::datetime_full_from_int(2012, 1, 0, 3, 61, 30, 3030, '+', 7, 45).is_err());
1787    assert!(Value::datetime_full_from_int(2012, 1, 0, 3, 30, 30, 3030, '+', 25, 45).is_err());
1788    assert!(Value::datetime_full_from_int(2012, 1, 0, 3, 30, 30, 3030, 'q', 7, 45).is_err());
1789  }
1790
1791  #[test]
1792  fn test_create_datetime_full_from_str() {
1793    assert_eq!(Value::DateTime(DateTime::new(Date::new_str("2012", "01", "03"), Some(Time::new_str(
1794      "03", "30", "30", Some("3030"), Some(TimeOffset::Time(TimeOffsetAmount::new_str(
1795        "+", "07", "45"
1796      )))
1797    )))), Value::datetime_full_from_str("2012", "01", "03", "03", "30", "30", "3030", "+", "07", "45").unwrap());
1798  }
1799
1800  #[test]
1801  fn test_create_datetime_full_from_str_fail() {
1802    assert!(Value::datetime_full_from_str("2012", "01", "03", "03", "30", "30", "q3030", "+", "07", "45").is_err());
1803    assert!(Value::datetime_full_from_str("2012", "01", "03", "03", "30", "30", "3030", "q", "07", "45").is_err());
1804  }
1805
1806  #[test]
1807  fn test_datetime_parse() {
1808    assert_eq!(Value::DateTime(DateTime::new(Date::new_str("2012", "01", "03"), Some(Time::new_str(
1809      "03", "30", "30", Some("3030"), Some(TimeOffset::Time(TimeOffsetAmount::new_str(
1810        "+", "07", "45"
1811      )))
1812    )))), Value::datetime_parse("2012-01-03T03:30:30.3030+07:45").unwrap());
1813
1814    assert_eq!(Value::DateTime(DateTime::new(Date::new_str("2012", "01", "03"), Some(Time::new_str(
1815      "03", "30", "30", Some("3030"), Some(TimeOffset::Zulu)
1816    )))), Value::datetime_parse("2012-01-03T03:30:30.3030Z").unwrap());
1817
1818    assert_eq!(Value::DateTime(DateTime::new(Date::new_str("2012", "01", "03"), Some(Time::new_str(
1819      "03", "30", "30", None, Some(TimeOffset::Zulu)
1820    )))), Value::datetime_parse("2012-01-03T03:30:30Z").unwrap());
1821
1822    assert_eq!(Value::DateTime(DateTime::new(Date::new_str("2012", "01", "03"), Some(Time::new_str(
1823      "03", "30", "30", None, Some(TimeOffset::Time(TimeOffsetAmount::new_str(
1824        "+", "07", "45"
1825      )))
1826    )))), Value::datetime_parse("2012-01-03T03:30:30+07:45").unwrap());
1827
1828    assert_eq!(Value::DateTime(DateTime::new(Date::new_str("2012", "01", "03"), Some(Time::new_str(
1829      "03", "30", "30", None, None
1830    )))), Value::datetime_parse("2012-01-03T03:30:30").unwrap());
1831
1832    assert_eq!(Value::DateTime(DateTime::new(Date::new_str("2012", "01", "03"), None
1833    )), Value::datetime_parse("2012-01-03").unwrap());
1834  }
1835
1836  #[test]
1837  fn test_datetime_parse_fail() {
1838    assert!(Value::datetime_parse("012-01-03T03:30:30.3030+07:45").is_err());
1839    assert!(Value::datetime_parse("2012-1-03T03:30:30.3030+07:45").is_err());
1840    assert!(Value::datetime_parse("2012-01-3T03:30:30.3030+07:45").is_err());
1841    assert!(Value::datetime_parse("2012-01-03T3:30:30.3030+07:45").is_err());
1842    assert!(Value::datetime_parse("2012-01-03T03:0:30.3030+07:45").is_err());
1843    assert!(Value::datetime_parse("2012-01-03T03:30:0.3030+07:45").is_err());
1844    assert!(Value::datetime_parse("2012-01-03T03:30:30.+07:45").is_err());
1845    assert!(Value::datetime_parse("2012-01-03T03:30:30.303007:45").is_err());
1846    assert!(Value::datetime_parse("2012-01-03T03:30:30.3030+7:45").is_err());
1847    assert!(Value::datetime_parse("2012-01-03T03:30:30.3030+07:5").is_err());
1848    assert!(Value::datetime_parse("20123-01-03T03:30:30.3030+07:45").is_err());
1849    assert!(Value::datetime_parse("2012-013-03T03:30:30.3030+07:45").is_err());
1850    assert!(Value::datetime_parse("2012-01-033T03:30:30.3030+07:45").is_err());
1851    assert!(Value::datetime_parse("2012-01-03T033:30:30.3030+07:45").is_err());
1852    assert!(Value::datetime_parse("2012-01-03T03:303:30.3030+07:45").is_err());
1853    assert!(Value::datetime_parse("2012-01-03T03:30:303.3030+07:45").is_err());
1854    assert!(Value::datetime_parse("2012-01-03T03:30:30.3030+073:45").is_err());
1855    assert!(Value::datetime_parse("2012-01-03T03:30:30.3030+07:453").is_err());
1856    assert!(Value::datetime_parse("2012q01-03T03:30:30.3030+07:45").is_err());
1857    assert!(Value::datetime_parse("2012-01q03T03:30:30.3030+07:45").is_err());
1858    assert!(Value::datetime_parse("2012-01-03q03:30:30.3030+07:45").is_err());
1859    assert!(Value::datetime_parse("2012-01-03T03q30:30.3030+07:45").is_err());
1860    assert!(Value::datetime_parse("2012-01-03T03:30q30.3030+07:45").is_err());
1861    assert!(Value::datetime_parse("2012-01-03T03:30:30q3030+07:45").is_err());
1862    assert!(Value::datetime_parse("2012-01-03T03:30:30.3030q07:45").is_err());
1863    assert!(Value::datetime_parse("2012-01-03T03:30:30.3030+07q45").is_err());
1864  }
1865
1866  #[test]
1867  fn test_create_basic_string() {
1868    assert_eq!(Value::String("foobar".into(), StrType::Basic), Value::basic_string("foobar").unwrap());
1869  }
1870
1871  #[test]
1872  fn test_create_basic_string_fail() {
1873    assert!(Value::basic_string("foo\nbar").is_err());
1874  }
1875
1876  #[test]
1877  fn test_create_ml_basic_string() {
1878    assert_eq!(Value::String("foobar".into(), StrType::MLBasic), Value::ml_basic_string("foobar").unwrap());
1879  }
1880
1881  #[test]
1882  fn test_create_ml_basic_string_fail() {
1883    assert!(Value::ml_basic_string(r#"foo\qbar"#).is_err());
1884  }
1885
1886  #[test]
1887  fn test_create_literal_string() {
1888    assert_eq!(Value::String("foobar".into(), StrType::Literal), Value::literal_string("foobar").unwrap());
1889  }
1890
1891  #[test]
1892  fn test_create_literal_string_fail() {
1893    assert!(Value::literal_string(r#"foo
1894bar"#).is_err());
1895  }
1896
1897  #[test]
1898  fn test_create_ml_literal_string() {
1899    assert_eq!(Value::String("foobar".into(), StrType::MLLiteral), Value::ml_literal_string("foobar").unwrap());
1900  }
1901
1902  #[test]
1903  fn test_create_ml_literal_string_fail() {
1904    // This string contains an invisible 0xC char between foo and bar. It's visible in
1905    // Sublime Text, but not in VS Code
1906    assert!(Value::ml_literal_string("foobar").is_err());
1907  }
1908
1909}