Skip to content

Commit

Permalink
Added some documentation. Documnted TOMLParser, ParseResult, ParseErr…
Browse files Browse the repository at this point in the history
…or, Value (declaration only, not implementation), StrType and Children. Still need to do Value implementation, TOMLError, PosNeg, TimeOffset, TimeOffsetAmount, Date, Time, and DateTime.
  • Loading branch information
joelself committed Mar 15, 2016
1 parent 28ce9ff commit ee3750e
Show file tree
Hide file tree
Showing 3 changed files with 284 additions and 4 deletions.
128 changes: 125 additions & 3 deletions src/lib.rs
Expand Up @@ -12,30 +12,152 @@ use std::fmt::Display;
use types::{ParseResult, Value, Children};
use internals::parser::Parser;

/// A parser, manipulator, and outputter of TOML documents.
pub struct TOMLParser<'a> {
parser: Parser<'a>,
}

impl<'a> TOMLParser<'a> {
/// Constructs a new `TOMLParser`
///
/// # Examples
///
/// ```
/// use tomllib::TOMLParser;
///
/// let mut parser = TOMLParser::new();
/// ```
pub fn new() -> TOMLParser<'a> {
TOMLParser{parser: Parser::new()}
}

/// Parses the string slice `input` as a TOML document. The method takes ownership of the parser and then returns it,
/// along with the `ParseResult`, in a tuple.
///
/// # Examples
///
/// ```
/// use tomllib::TOMLParser;
///
/// let parser = TOMLParser::new();
/// let (parser, result) = parser.parse("[table]\nAKey=\"A Value\"");
/// ```
pub fn parse(mut self, input: &'a str) -> (TOMLParser<'a>, ParseResult<'a>) {
let (tmp, result) = self.parser.parse(input);
self.parser = tmp;
(self, result)
}

/// Given a string type `key`, returns the associated `Value` or `None` if the key doesn't exist in the parsed
/// document.
///
/// # Examples
///
/// ```
/// use tomllib::TOMLParser;
///
/// let parser = TOMLParser::new();
/// let toml_doc = r#""A Key" = "A Value"
/// [[tables]]
/// SomeKey = 2010-05-18
/// [tables.subtable]
/// AnotherKey = 5
/// "#;
/// let (parser, result) = parser.parse(toml_doc);
/// let value1 = parser.get_value("\"A Key\"");
/// let value3 = parser.get_value("tables[0].SomeKey");
/// let value3 = parser.get_value("tables[0].subtable.AnotherKey");
/// assert_eq!(value1.unwrap(), Value::basic_string("A Value").unwrap());
/// assert_eq!(value2.unwrap(), Value::date_from_int(2010, 5, 18).unwrap());
/// assert_eq!(value3.unwrap(), Value::int(5));
/// ```
pub fn get_value<S>(self: &TOMLParser<'a>, key: S) -> Option<Value<'a>> where S: Into<String> {
self.parser.get_value(key)
}
pub fn get_children<S>(self: &TOMLParser<'a>, key: S) -> Option<&Children> where S: Into<String> {
self.parser.get_children(key)
}

/// Given a string type `key` and a `Value` `val`, sets `Value` at `key` to `val` and returns true if `key` exists in
/// the parsed document. If `key` doesn't exist in the parsed document returns false. Setting a value does not alter
/// the document's format, including whitespace and comments, unless an `Array` or `InlineTable`'s structure is changed
/// meaning either:
///
/// * The amount of values in an `Array` is changed
/// * The amount of key-value pairs in an `InlineTable` is changed
/// * Any of the keys in an `InlineTable` is changed
///
/// In these cases the `Array` or `InlineTable` will revert to default formatting: No whitespace after/before
/// opening/closing braces, no whitespace before and one space after all commas, no comments on the same line as the
/// `Array` or `InlineTable`, and one space before and after an equals sign in `InlineTable`s.
///
/// # Examples
///
/// ```
/// use tomllib::TOMLParser;
/// use tomllib::types::Value;
///
/// let parser = TOMLParser::new();
/// let (mut parser, result) = parser.parse("[table]\nAKey=\"A Value\"");
/// let success = parser.set_value("table.AKey", Value::Integer("5_000".into()));
/// assert!(success);
/// let value = parser.get_value("AKey");
/// assert_eq!(value.unwrap(), Value::int_from_str(5_000).unwrap());
/// ```
pub fn set_value<S>(self: &mut TOMLParser<'a>, key: S, val: Value<'a>) -> bool where S: Into<String> {
self.parser.set_value(key, val)
}

/// Given a string type `key` returns all the child keys of the `key` if it exists in the parsed document, otherwise
/// returns `None`.
///
/// # Examples
///
/// ```
/// use tomllib::TOMLParser;
///
/// let toml_doc = r#"
/// [table]
/// "A Key" = "A Value"
/// SomeKey = "Some Value"
/// AnotherKey = 5
/// [[array_of_tables]]
/// [[array_of_tables]]
/// [[array_of_tables]]
/// "#;
/// let (parser, result) = parser.parse(toml_doc);
/// let table_child_keys = parser.get_children("table");
/// assert_eq!(table_child_keys.unwrap(), Children::Keys(RefCell::new(vec![
/// "\"A Key\"".to_string(), "SomeKey".to_string(), "AnotherKey".to_string()
/// ])));
/// let aot_child_keys = parser.get_children("array_of_tables");
/// assert_eq!(aot_child_keys.unwrap(), Children::Count(Cell::new(3)));
/// ```
pub fn get_children<S>(self: &TOMLParser<'a>, key: S) -> Option<&Children> where S: Into<String> {
self.parser.get_children(key)
}
}

/// Formats a parsed TOML document for display
///
/// # Examples
///
/// ```
/// use tomllib::TOMLParser;
/// use tomllib::types::Value;
///
/// let parser = TOMLParser::new();
/// let toml_doc = r#"
/// [table] # This is a comment
/// "A Key" = "A Value" # This line is indented
/// SomeKey = "Some Value" # This line is indented twice
/// "#;
/// let (mut parser, result) = parser.parse(toml_doc);
/// parser.set_value("table.\"A Key\"", Value::float(9.876));
/// parser.set_value("table.SomeKey", Value::bool(false));
/// assert_eq(&format!("{}", parser), r#"
/// [table] # This is a comment
/// "A Key" = 9.876 # This line is indented
/// SomeKey = false # This line is indented twice
/// "#);
/// ```
impl<'a> Display for TOMLParser<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.parser)
Expand Down
158 changes: 158 additions & 0 deletions src/types.rs
Expand Up @@ -10,59 +10,179 @@ use std::borrow::Cow;
use internals::parser::Parser;
use nom::IResult;

/// Conveys the result of a parse operation on a TOML document
#[derive(Debug, Eq, PartialEq, Clone)]
pub enum ParseResult<'a> {
/// The entire input was parsed without error.
Full,
/// The entire input was parsed, but there were errors. Contains an `Rc<RefCell<Vec>>` of `ParseError`s.
FullError(Rc<RefCell<Vec<ParseError<'a>>>>),
/// Part of the input was parsed successfully without any errors. Contains a `Cow<str>`, with the leftover, unparsed
/// input, the line number and column (currently column reporting is unimplemented and will always report `0`) where
/// parsing stopped.
Partial(Cow<'a, str>, usize, usize),
/// Part of the input was parsed successfully with errors. Contains a `Cow<str>`, with the leftover, unparsed input,
/// the line number and column (currently column reporting is unimplemented and will always report `0`) where parsing
/// stopped, and an `Rc<RefCell<Vec>>` of `ParseError`s.
PartialError(Cow<'a, str>, usize, usize, Rc<RefCell<Vec<ParseError<'a>>>>),
/// The parser failed to parse any of the input as a complete TOML document. Contains the line number and column
/// (currently column reporting is unimplemented and will always report `0`) where parsing stopped.
Failure(usize, usize),
}

/// Represents a non-failure error encountered while parsing a TOML document.
#[derive(Debug, Eq, PartialEq, Clone)]
pub enum ParseError<'a> {
/// An `Array` containing different types was encountered. Contains the `String` key that points to the `Array` and
/// the line number and column (currently column reporting is unimplemented and will always report `0`) where the
/// `Array` was found. The `Array` can be retrieved and/or changed by its key using `TOMLParser::get_value` and
/// `TOMLParser::set_value` methods.
MixedArray(String, usize, usize),
/// A duplicate key was encountered. Contains the `String` key that was duplicated in the document, the line number
/// and column (currently column reporting is unimplemented and will always report `0`) where the duplicate key was
/// found, and the `Value` that the key points to.
DuplicateKey(String, usize, usize, Value<'a>),
/// An invalid table was encountered. Either the key\[s\] that make up the table are invalid or a duplicate table was
/// found. Contains the `String` key of the invalid table, the line number and column (currently column reporting is
/// unimplemented and will always report `0`) where the invalid table was found, `RefCell<HashMap<String, Value>>`
/// that contains all the keys and values belonging to that table.
InvalidTable(String, usize, usize, RefCell<HashMap<String, Value<'a>>>),
/// An invalid `DateTime` was encountered. This could be a `DateTime` with:
///
/// * 0 for year
/// * 0 for month or greater than 12 for month
/// * 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
/// * Greater than 23 for hour
/// * Greater than 59 for minute
/// * Greater than 59 for second
/// * Greater than 23 for offset hour
/// * Greater than 59 for offset minute
///
/// Contains the `String` key of the invalid `DateTime`, the line number and column (currently column reporting is
/// unimplemented and will always report `0`) where the invalid `DateTime` was found, and a Cow<str> containing the
/// invalid `DateTime` string.
InvalidDateTime(String, usize, usize, Cow<'a, str>),
/// *Currently unimplemented*. Reserved for future use when an integer overflow is detected.
IntegerOverflow(String, usize, usize, Cow<'a, str>),
/// *Currently unimplemented*. Reserved for future use when an integer underflow is detected.
IntegerUnderflow(String, usize, usize, Cow<'a, str>),
/// *Currently unimplemented*. Reserved for future use when an invalid integer representation is detected.
InvalidInteger(String, usize, usize, Cow<'a, str>),
/// *Currently unimplemented*. Reserved for future use when a float value of infinity is detected.
Infinity(String, usize, usize, Cow<'a, str>),
/// *Currently unimplemented*. Reserved for future use when a float value of negative infinity is detected.
NegativeInfinity(String, usize, usize, Cow<'a, str>),
/// *Currently unimplemented*. Reserved for future use when a float string conversion to an `f64` would result in a loss
/// of precision.
LossOfPrecision(String, usize, usize, Cow<'a, str>),
/// *Currently unimplemented*. Reserved for future use when an invalid float representation is detected.
InvalidFloat(String, usize, usize, Cow<'a, str>),
/// *Currently unimplemented*. Reserved for future use when an invalid `true` or `false` string is detected.
InvalidBoolean(String, usize, usize, Cow<'a, str>),
/// *Currently unimplemented*. Reserved for future use when an invalid string representation is detected.
InvalidString(String, usize, usize, Cow<'a, str>, StrType),
/// *Currently unimplemented*. Reserved for future use when new error types are added without resorting to a breaking
/// change.
GenericError(String, usize, usize, Option<Cow<'a, str>>, String),
}

// Represents the 7 different types of values that can exist in a TOML document.
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum Value<'a> {
/// An integer value. Contains a `Cow<str>` representing the integer since integers can contain underscores.
Integer(Cow<'a, str>),
/// A float value. Contains a `Cow<str>` representing the float since floats can be formatted many different ways and
/// can contain underscores.
Float(Cow<'a, str>),
/// A boolean value. Contains a `bool` value since only `true` and `false` are allowed.
Boolean(bool),
/// A `DateTime` value. Contains a `DateTime` struct that has a date and optionally a time, fractional seconds, and
/// offset from UTC.
DateTime(DateTime<'a>),
/// A string value. Contains a `Cow<str>` with the string contents (without quotes) and `StrType` indicating whether
/// the string is a basic string, multi-line basic string, literal string or multi-line literal string.
String(Cow<'a, str>, StrType),
/// An array value. Contains an `Rc<Vec>` of `Value`s contained in the `Array`.
Array(Rc<Vec<Value<'a>>>),
/// An inline table value. Contains an `Rc<Vec>` of tuples that contain a `Cow<str>` representing a key, and `Value`
/// that the key points to.
InlineTable(Rc<Vec<(Cow<'a, str>, Value<'a>)>>)
}

/// Represents the 4 different types of strings that are allowed in TOML documents.
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
pub enum StrType {
/// String is a basic string.
Basic,
/// String is a multi-line basic string.
MLBasic,
/// String is a literal string.
Literal,
/// String is a multi-line literal string.
MLLiteral,
}

/// Represents the child keys of a key in a parsed TOML document.
#[derive(Debug, Eq, PartialEq, Clone)]
pub enum Children {
/// Contains a `Cell<usize>` with the amount of child keys the key has. The key has children that are indexed with an
/// integer starting at 0. `Array`s and array of tables use integer for their child keys. For example:
///
/// ```
/// Array = ["A", "B", "C", "D", "E"]
/// [[array_of_table]]
/// key = "val 1"
/// [[array_of_table]]
/// key = "val 2"
/// [[array_of_table]]
/// key = "val 3"
/// ```
///
/// "Array" has 5 children. The key of "D" is "Array[3]" because it is fourth element in "Array" and indexing starts
/// at 0.
/// "array_of_table" has 3 children. The key of "val 3" is "array_of_table[2].key" because it is in the third
/// sub-table of "array_of_table" and indexing starts at 0.
Count(Cell<usize>),
/// Contains a `RefCell<Vec>` of `String`s with every sub-key of the key. The key has children that are indexed with a
/// sub-key. Tables and inline-tables use sub-keys for their child keys. For example:
///
/// ```
/// InlineTable = {subkey1 = "A", subkey2 = "B"}
/// [table]
/// a_key = "val 1"
/// b_key = "val 2"
/// c_key = "val 3"
/// ```
///
/// "InlineTable" has 2 children, "subkey1" and "subkey2". The key of "B" is "InlineTable.subkey2".
/// "table" has 3 children, "a_key", "b_key", and "c_key". The key of "val 3" is "table.c_key".
Keys(RefCell<Vec<String>>)
}

/// Contains convenience functions to combine base keys with child keys to make a full key.
impl Children {

/// Combines string type `base_key` with a string type `child_key` to form a full key.
///
/// # Examples
///
/// ```
/// use tomllib::TOMLParser;
/// use tomllib::types::Children;
/// let toml_doc = r#"
/// [dependencies]
/// nom = {version = "^1.2.0", features = ["regexp"]}
/// regex = {version = "^0.1.48"}
/// log = {version = "^0.3.5"}
/// "#;
/// let parser = TOMLParser::new();
/// let (parser, result) = parser.parse(toml_doc);
/// let deps = parser.get_children("dependencies");
/// if let Children::Keys(subkeys) = deps.unwrap() {
/// assert_eq!("dependencies.nom",
/// Children::combine_keys("dependencies", &subkeys.borrow()[0]));
/// }
/// ```
pub fn combine_keys<S>(base_key: S, child_key: S) -> String where S: Into<String> {
let mut full_key;
let base = base_key.into();
Expand All @@ -76,9 +196,47 @@ impl Children {
}
return full_key;
}

/// Combines string type `base_key` with an integer type `child_key` to form a full key.
///
/// # Examples
///
/// ```
/// use tomllib::TOMLParser;
/// use tomllib::types::Children;
/// let toml_doc = r#"
/// keywords = ["toml", "parser", "encode", "decode", "nom"]
/// "#;
/// let parser = TOMLParser::new();
/// let (parser, result) = parser.parse(toml_doc);
/// let kw = parser.get_children("keywords");
/// if let Children::Count(subkeys) = kw.unwrap() {
/// assert_eq!("keywords[4]", Children::combine_keys("keywords", subkeys.get() - 1));
/// }
/// # assert!(false);
/// ```
pub fn combine_keys_index<S>(base_key: S, child_key: usize) -> String where S: Into<String> {
return format!("{}[{}]", base_key.into(), child_key);
}

/// Combines string type `base_key` with all subkeys of an instance of `Children` to form a `Vec` of full keys
///
/// # Examples
///
/// ```
/// use tomllib::TOMLParser;
/// use tomllib::types::Children;
/// let toml_doc = r#"
/// keywords = ["toml", "parser"]
/// numbers = {first = 1, second = 2}
/// "#;
/// let parser = TOMLParser::new();
/// let (parser, result) = parser.parse(toml_doc);
/// let kw = parser.get_children("keywords");
/// assert_eq!(vec!["keywords[0]".to_string(), "keywords[1]".to_string()], kw.unwrap().combine_keys("keywords"));
/// let num = parser.get_children("numbers");
/// assert_eq!(vec!["numbers.first".to_string(), "numbers.second".to_string()], num.unwrap().combine_keys("numbers"));
/// ```
pub fn combine_child_keys<S>(&self, base_key: S) -> Vec<String> where S: Into<String> {
let mut all_keys = vec![];
let base = base_key.into();
Expand Down
2 changes: 1 addition & 1 deletion todolist.md
Expand Up @@ -2,7 +2,7 @@
- [x] Switch back to using nom now that the new version has been released
- [x] Logging
- [x] Add line numbers to errors
- [ ] **Add documentation to public enums, structs, functions, methods and macros**
- [ ] **Add documentation to public enums, structs, functions, methods and macros** - *In progress*
- [x] Switch names of TOMLValue and Value
- [x] Rename Parser to TOMLParser

Expand Down

0 comments on commit ee3750e

Please sign in to comment.