valid_toml 0.0.2

Provides the ability to load a TOML file with validation.
Documentation
/* Copyright 2016 Joshua Gentry
 *
 * Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
 * http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
 * <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
 * option. This file may not be copied, modified, or distributed
 * except according to those terms.
 */
use std::collections::{BTreeMap, LinkedList};

use enums::{Error, ExtractResult, Warning};
use issues::Issues;
use item_value::ItemValue;
use notify::Notify;
use toml_data::TomlData;

//*************************************************************************************************
/// This object is used to build up a TomlFile, it tracks processed items in the file as well as
/// any issues encountered durring the processing.
pub struct TomlBuilder
{
    //---------------------------------------------------------------------------------------------
    /// The TOML group we are parsing.
    group : String,

    //---------------------------------------------------------------------------------------------
    /// The items that have been successfully processed.
    items : LinkedList<(String, ItemValue)>,

    //---------------------------------------------------------------------------------------------
    /// The issues that have been encountered.
    issues : Issues
}

impl TomlBuilder
{
    //*********************************************************************************************
    /// Create a new instance of the object.
    pub fn new<T:AsRef<str>>(
        group : T
        ) -> TomlBuilder
    {
        TomlBuilder {
            group  : String::from(group.as_ref()),
            items  : LinkedList::new(),
            issues : Issues::new()
        }
    }

    //*********************************************************************************************
    /// Returns the result of the construction operation. If there are any errors then an error
    /// is returned, otherwise the TomlFile is returned.
    pub fn result(mut self) -> Result<TomlData, Issues>
    {
        if self.issues.has_errors() == false
        {
            let mut result = BTreeMap::new();

            while let Some((key, item)) = self.items.pop_front()
            {
                result.insert(key, item);
            }

            Ok(TomlData::new(result, self.issues.warnings))
        }
        else
        {
            Err(self.issues)
        }
    }

    //*********************************************************************************************
    /// Builds the full name for an item.
    pub fn full_name<T:AsRef<str>>(
        &self,
        name : T
        ) -> String
    {
        if self.group.len() > 0
        {
            let mut full_name = String::with_capacity(name.as_ref().len() + self.group.len() + 1);

            full_name.push_str(&self.group);
            full_name.push('.');
            full_name.push_str(name.as_ref());

            full_name
        }
        else
        {
            String::from(name.as_ref())
        }
    }

    //*********************************************************************************************
    /// An item without a definition was encountered, add a warning to the issues element.
    pub fn undefined_item<T:AsRef<str>>(
        &mut self,
        name : T
        )
    {
        self.issues.warnings.push_back(Warning::Undefined(String::from(name.as_ref())));
    }

    //*********************************************************************************************
    /// An item could not be extracted, add a validation error to the issues structure.  We need to
    /// check if the validation error has multiple issues.  If it does then we need to merge those
    /// issues into the issues structure.
    pub fn add<T:AsRef<str>>(
        &mut self,
        name   : T,
        item   : ExtractResult,
        notify : Option<&Box<Notify>>
        )
    {
        let full_name = self.full_name(name);

        match item
        {
            //-------------------------------------------------------------------------------------
            // Got an item, add it to the builder and notify anyone listening.
            ExtractResult::Item(item) => {
                if let Some(notify) = notify
                {
                    notify.send(&item);
                }
                self.items.push_back((full_name, item));
            },
            //-------------------------------------------------------------------------------------
            // Validation error for a single item.
            ExtractResult::Error(err) => {
                self.issues.errors.push_back(
                    Error::Validation(full_name, err)
                    );
            },
            //-------------------------------------------------------------------------------------
            // A group was processed, merge the results into ours.
            ExtractResult::Group(mut builder) => {
                self.issues.errors.append(&mut builder.issues.errors);
                self.issues.warnings.append(&mut builder.issues.warnings);
                self.items.append(&mut builder.items);
            }
        }
    }

    //*********************************************************************************************
    /// An item was not found.  If this item is not optional then an error is added to the list.
    /// If the item is optional then a warning is added to the warnings.
    pub fn missing_item<T:AsRef<str>>(
        &mut self,
        name     : T,
        optional : bool
        )
    {
        let full_name = self.full_name(name);

        if optional == false
        {
            self.issues.errors.push_back(Error::Missing(full_name));
        }
        else
        {
            self.issues.warnings.push_back(Warning::Missing(full_name));
        }
    }
}