<commit_before>/// # What is Validator
/// It useful for Form and Model validation.
/// Implementation for Validation via BTreeMap structure.
/// It consist basic validations rule, validatior, and
/// validation Result and validation Errors.
///
/// ## How to use
/// Most common examples you can find at src/admin/models/*
/// ```ignore
/// fn validate(values: &Map) -> ValidateResults {
///    ValidateResults(vec!(
///        // Validator declaration
///        // `::<String>::` meen Generic type for Default value
///        Validator::<String>::new(btreemap! {
///            // Validator type
///            "requiered".to_string() => true.to_json(),
///            // Value type (from POST/GET/.. request)
///            // We'll get validation error if value type wrong.
///            // It is requiered field for validations rule
///            "vtype".to_string() => "string".to_json(),
///        }).validate("title".to_string(), values.find(&["pages", "title"])),
///
///        // Validator declaration
///        // `::<bool>::` meen Generic type for Default value
///        // and it should be same type as default value
///        Validator::<bool>::new(btreemap! {
///            // Validator type declaration should be same
///            // as default value type
///            "default".to_string() => false.to_json(),
///            "vtype".to_string() => "bool".to_json(),
///        }).validate("published".to_string(), values.find(&["published"])),
///    ))
/// }
/// ```

pub use rustc_serialize::json::{self, Json, ToJson};
pub use rustc_serialize::json::DecoderError::*;
pub use rustc_serialize::Decodable;
use params::{Value, FromValue};
use super::render::{BaseDataMap, BaseDataMapDecoder};
use std::collections::BTreeMap;
use std::string::String;

/// Base Validator struct
#[derive(RustcDecodable, Debug)]
pub struct Validator<T> {
    /// Type of validator
    /// For example: string, bool, i64 etc.
    pub vtype: String,
    pub requiered: Option<bool>,
    pub not_empty: Option<bool>,
    pub min: Option<i64>,
    pub max: Option<u64>,
    pub default: Option<T>,
    errors: Option<ErrorValidator>,
}

/// Validation result enum.
/// Consist Values and Errors
#[derive(Debug)]
pub struct ValidateResult(BaseDataMap, ErrorValidator);
/// Array of Validatopn Results for all validatior
#[derive(Debug)]
pub struct ValidateResults(pub Vec<ValidateResult>);
// Error result aggregator
pub type ErrorsResult = Option<Vec<ErrorValidator>>;

/// Validation results methods
impl ValidateResults {
    /// Get Validation Errors result
    pub fn get_errors(&self) -> ErrorsResult {
        let &ValidateResults(ref results) = self;
        let mut errors = vec!();
        for &ValidateResult(_, ref err) in results {
            if err.errors_count.is_some() {
                errors.push(err.to_owned());
            }
        }
        if errors.len() > 0 { Some(errors) } else { None }
    }

    /// Get Validation Values result
    pub fn get_values(&self) -> BaseDataMap {
        let &ValidateResults(ref results) = self;
        let mut values: BaseDataMap = BTreeMap::new();
        for &ValidateResult(ref val, _) in results {
            values.append(&mut val.clone());
        }
        values
    }
}

/// Convert Validation Results to Json
impl ToJson for ValidateResults {
    fn to_json(&self) -> Json {
        let mut data: Vec<Json> = vec!();
        let &ValidateResults(ref results) = self;
        let mut i = 0;
        for &ValidateResult(ref val, ref err) in results {
            let mut d = BTreeMap::new();
            // Set `field` attribute
            d.insert("field".to_string(), err.field.to_json());
            // Set `errors` attribute
            d.insert("errors".to_string(), err.errors.to_json());
            // Set `values` attribute
            d.insert("values".to_string(), val.to_json());

            data.push(d.to_json());

            //data.append(&mut d);
            i = i + 1;
        }
        Json::Array(data)
    }
}

/// Validator methods
/// It depends from various additional **traits**.
impl<T: FromValue + ToJson + Decodable> Validator<T> {
    /// Init Validation rule
    pub fn new(validator_rules: BaseDataMap) -> Validator<T> {
        validator_rules.decode()
    }

    /// Main validor for all validations rules
    pub fn validate(&mut self, field: String, value: Option<&Value>) -> ValidateResult {
        let mut value: Option<Value> = if let Some(val) = value {
            Some(val.to_owned())
        } else { None };
        // Init Errors
        self.errors = Some(ErrorValidator::new(&field));

        // Invoke validators
        self.requiered(&value);
        self.not_empty(&value);
        self.max(&value);
        self.min(&value);
        value = self.default(&value);

        let json_value: Json = match self.type_cast(&value) {
            Some(ref json_value) => json_value.to_owned(),
            None => {
                if let Some(ref mut error) = self.errors {
                    if value.is_some() {
                        let msg = format!("Field wrong type: {}", error.field);
                        error.add(msg);
                    }
                }
                Json::Null
            }
        };

        let mut err = ErrorValidator::new(&field);
        if let Some(ref err_results) = self.errors {
            err = err_results.to_owned();
        }

        ValidateResult(btreemap! {
            field.to_owned() => json_value
        }, err)
    }

    /// Requered validator
    fn requiered(&mut self, value: &Option<Value>) {
        if self.requiered.is_some() {
            if value.is_some() {
                let check_value = match *value {
                    Some(Value::String(ref value)) => {
                        !value.trim().is_empty()
                    },
                    _ => true
                };
                if check_value {
                    return ()
                }
            }
            if let Some(ref mut error) = self.errors {
                let msg = format!("Field requiered: {}", error.field);
                error.add(msg);
            }
        }
    }

    /// Not Empty validator
    fn not_empty(&mut self, value: &Option<Value>) {
        // We apply validarot onlu if values is set
        // Requered validator analize if value was set
        if self.not_empty.is_some() && value.is_some() {
            let is_empty = match *value {
                // We check only strings
                Some(Value::String(ref value)) => {
                    value.trim().is_empty()
                },
                _ => false
            };
            if is_empty {
                if let Some(ref mut error) = self.errors {
                    let msg = format!("Field can't be empty: {}", error.field);
                    error.add(msg);
                }
            }
        }
    }

    /// Max value validator
    /// Multitype
    fn max(&mut self, value: &Option<Value>) {
        if self.max.is_some() && value.is_some() {
            let mut requiered_value: u64 = 0;
            if let Some(max) = self.max {
                if max == 0 {
                    if let Some(ref mut error) = self.errors {
                        let msg = format!("Validation value can't be equal: {}", max);
                        error.add(msg);
                    }
                    return ()
                }
                requiered_value = max;
            }
            let is_valid = match *value {
                Some(Value::String(ref value)) => {
                    value.chars().count() as u64 <= requiered_value
                },
                Some(Value::U64(value)) => {
                    value <= requiered_value
                },
                Some(Value::I64(value)) => {
                    value as u64 <= requiered_value
                },
                Some(Value::F64(value)) => {
                    value as u64 <= requiered_value
                },
                Some(Value::Boolean(value)) => {
                    value as u64 <= requiered_value
                },
                _ => false
            };
            if !is_valid {
                if let Some(ref mut error) = self.errors {
                    let msg = format!("Field {} can't be min then: {}", error.field, requiered_value);
                    error.add(msg);
                }
            }
        }
    }

    /// Min value validator
    /// Multitype
    fn min(&mut self, value: &Option<Value>) {
        if self.min.is_some() && value.is_some() {
            let mut requiered_value: i64 = 0;
            if let Some(min) = self.min {
                if let Some(max) = self.max {
                    if min >= max as i64 {
                        if let Some(ref mut error) = self.errors {
                            let msg = format!("Validation rule {} can't be greater or equal max rule: {}", min, max);
                            error.add(msg);
                        }
                        return ()
                    }
                }
                requiered_value = min;
            }
            let is_valid = match *value {
                Some(Value::String(ref value)) => {
                    value.chars().count() as i64 >= requiered_value
                },
                Some(Value::U64(value)) => {
                    value as i64>= requiered_value
                },
                Some(Value::I64(value)) => {
                    value >= requiered_value
                },
                Some(Value::F64(value)) => {
                    value as i64 >= requiered_value
                },
                Some(Value::Boolean(value)) => {
                    value as i64 >= requiered_value
                },
                _ => false
            };
            if !is_valid {
                if let Some(ref mut error) = self.errors {
                    let msg = format!("Field {} can't be min then: {}", error.field, requiered_value);
                    error.add(msg);
                }
            }
        }
    }

    /// Default value validator
    /// Validate by type and default field
    fn default(&mut self, value: &Option<Value>) -> Option<Value> {
        if let Some(ref default_value) = self.default {
            if value.is_none() {
                return self.to_value(default_value.to_json());
            }
        }
        value.to_owned()
    }

    /// Convert value based ot specific type to Value
    pub fn to_value(&self, json_value: Json) -> Option<Value> {
        match json_value {
            Json::I64(value) => Some(Value::I64(value)),
            Json::U64(value) => Some(Value::U64(value)),
            Json::F64(value) => Some(Value::F64(value)),
            Json::String( ref value) => Some(Value::String(value.clone())),
            Json::Boolean(value) => Some(Value::Boolean(value)),
            Json::Null => Some(Value::Null),
            _ => None,
        }
    }

    // Type cast for Value.
    // Returned as Json
    fn type_cast(&self, value: &Option<Value>) -> Option<Json> {
        let val: Value;
        if let Some(name) = value.as_ref() {
            val = name.to_owned();
        } else {
            return None
        }

        match &self.vtype.as_ref() as &str {
            "array" | "vec" => {
                if let Some(val) = <Vec<String> as FromValue>::from_value(&val) {
                    Some(val.to_json())
                } else {
                    None
                }
            },
            "bool" | "boolean" => {
                if let Some(val) = <bool as FromValue>::from_value(&val) {
                    Some(Json::Boolean(val))
                } else {
                    None
                }
            },
            "string" | "str" => {
                if let Some(val) = <String as FromValue>::from_value(&val) {
                    Some(Json::String(val))
                } else {
                    None
                }
            },
            "u8" | "u16" | "u32" | "u64" | "usize" => {
                if let Some(val) = <u64 as FromValue>::from_value(&val) {
                    Some(Json::U64(val))
                } else {
                    None
                }
            },
            "i8" | "i16" | "i32" | "i64" | "isize" => {
                if let Some(val) = <i64 as FromValue>::from_value(&val) {
                    Some(Json::I64(val))
                } else {
                    None
                }
            },
            "f32" | "f64" => {
                if let Some(val) = <f64 as FromValue>::from_value(&val) {
                    Some(Json::F64(val))
                } else {
                    None
                }
            },
            _ => None,
        }
    }
}

/// Validator Errors
#[derive(RustcDecodable, Debug, Clone)]
pub struct ErrorValidator {
    pub errors: Vec<String>,
    pub errors_count: Option<u32>,
    pub field : String,
}

/// Validator Errors methods
impl ErrorValidator {
    /// Init error
    fn new(field: &String) -> Self {
        ErrorValidator {
            field: field.to_owned(),
            errors: vec!(),
            errors_count: None,
        }
    }

    /// Add error
    fn add(&mut self, error: String) {
        if self.errors_count.is_none() {
            self.errors = vec!(error);
            self.errors_count = Some(1);
        } else {
            if let Some(count) = self.errors_count {
                self.errors.push(error);
                self.errors_count = Some(count + 1);
            }
        }
    }
}
<commit_msg>alidator example - fix broken test<commit_after>//! # What is Validator
//! It useful for Form and Model validation.
//! Implementation for Validation via BTreeMap structure.
//! It consist basic validations rule, validatior, and
//! validation Result and validation Errors.
//!
//! ## How to use
//! Most common examples you can find at src/admin/models/*
//!
//! ```ignore
//! fn validate(values: &Map) -> ValidateResults {
//!    ValidateResults(vec!(
//!        // Validator declaration
//!        // `::<String>::` meen Generic type for Default value
//!        Validator::<String>::new(btreemap! {
//!            // Validator type
//!            "requiered".to_string() => true.to_json(),
//!            // Value type (from POST/GET/.. request)
//!            // We'll get validation error if value type wrong.
//!            // It is requiered field for validations rule
//!            "vtype".to_string() => "string".to_json(),
//!        }).validate("title".to_string(), values.find(&["pages", "title"])),
//!
//!        // Validator declaration
//!        // `::<bool>::` meen Generic type for Default value
//!        // and it should be same type as default value
//!        Validator::<bool>::new(btreemap! {
//!            // Validator type declaration should be same
//!            // as default value type
//!            "default".to_string() => false.to_json(),
//!            "vtype".to_string() => "bool".to_json(),
//!        }).validate("published".to_string(), values.find(&["published"])),
//!    ))
//! }
//! ```

pub use rustc_serialize::json::{self, Json, ToJson};
pub use rustc_serialize::json::DecoderError::*;
pub use rustc_serialize::Decodable;
use params::{Value, FromValue};
use super::render::{BaseDataMap, BaseDataMapDecoder};
use std::collections::BTreeMap;
use std::string::String;

/// Base Validator struct
#[derive(RustcDecodable, Debug)]
pub struct Validator<T> {
    /// Type of validator
    /// For example: string, bool, i64 etc.
    pub vtype: String,
    pub requiered: Option<bool>,
    pub not_empty: Option<bool>,
    pub min: Option<i64>,
    pub max: Option<u64>,
    pub default: Option<T>,
    errors: Option<ErrorValidator>,
}

/// Validation result enum.
/// Consist Values and Errors
#[derive(Debug)]
pub struct ValidateResult(BaseDataMap, ErrorValidator);
/// Array of Validatopn Results for all validatior
#[derive(Debug)]
pub struct ValidateResults(pub Vec<ValidateResult>);
// Error result aggregator
pub type ErrorsResult = Option<Vec<ErrorValidator>>;

/// Validation results methods
impl ValidateResults {
    /// Get Validation Errors result
    pub fn get_errors(&self) -> ErrorsResult {
        let &ValidateResults(ref results) = self;
        let mut errors = vec!();
        for &ValidateResult(_, ref err) in results {
            if err.errors_count.is_some() {
                errors.push(err.to_owned());
            }
        }
        if errors.len() > 0 { Some(errors) } else { None }
    }

    /// Get Validation Values result
    pub fn get_values(&self) -> BaseDataMap {
        let &ValidateResults(ref results) = self;
        let mut values: BaseDataMap = BTreeMap::new();
        for &ValidateResult(ref val, _) in results {
            values.append(&mut val.clone());
        }
        values
    }
}

/// Convert Validation Results to Json
impl ToJson for ValidateResults {
    fn to_json(&self) -> Json {
        let mut data: Vec<Json> = vec!();
        let &ValidateResults(ref results) = self;
        let mut i = 0;
        for &ValidateResult(ref val, ref err) in results {
            let mut d = BTreeMap::new();
            // Set `field` attribute
            d.insert("field".to_string(), err.field.to_json());
            // Set `errors` attribute
            d.insert("errors".to_string(), err.errors.to_json());
            // Set `values` attribute
            d.insert("values".to_string(), val.to_json());

            data.push(d.to_json());

            //data.append(&mut d);
            i = i + 1;
        }
        Json::Array(data)
    }
}

/// Validator methods
/// It depends from various additional **traits**.
impl<T: FromValue + ToJson + Decodable> Validator<T> {
    /// Init Validation rule
    pub fn new(validator_rules: BaseDataMap) -> Validator<T> {
        validator_rules.decode()
    }

    /// Main validor for all validations rules
    pub fn validate(&mut self, field: String, value: Option<&Value>) -> ValidateResult {
        let mut value: Option<Value> = if let Some(val) = value {
            Some(val.to_owned())
        } else { None };
        // Init Errors
        self.errors = Some(ErrorValidator::new(&field));

        // Invoke validators
        self.requiered(&value);
        self.not_empty(&value);
        self.max(&value);
        self.min(&value);
        value = self.default(&value);

        let json_value: Json = match self.type_cast(&value) {
            Some(ref json_value) => json_value.to_owned(),
            None => {
                if let Some(ref mut error) = self.errors {
                    if value.is_some() {
                        let msg = format!("Field wrong type: {}", error.field);
                        error.add(msg);
                    }
                }
                Json::Null
            }
        };

        let mut err = ErrorValidator::new(&field);
        if let Some(ref err_results) = self.errors {
            err = err_results.to_owned();
        }

        ValidateResult(btreemap! {
            field.to_owned() => json_value
        }, err)
    }

    /// Requered validator
    fn requiered(&mut self, value: &Option<Value>) {
        if self.requiered.is_some() {
            if value.is_some() {
                let check_value = match *value {
                    Some(Value::String(ref value)) => {
                        !value.trim().is_empty()
                    },
                    _ => true
                };
                if check_value {
                    return ()
                }
            }
            if let Some(ref mut error) = self.errors {
                let msg = format!("Field requiered: {}", error.field);
                error.add(msg);
            }
        }
    }

    /// Not Empty validator
    fn not_empty(&mut self, value: &Option<Value>) {
        // We apply validarot onlu if values is set
        // Requered validator analize if value was set
        if self.not_empty.is_some() && value.is_some() {
            let is_empty = match *value {
                // We check only strings
                Some(Value::String(ref value)) => {
                    value.trim().is_empty()
                },
                _ => false
            };
            if is_empty {
                if let Some(ref mut error) = self.errors {
                    let msg = format!("Field can't be empty: {}", error.field);
                    error.add(msg);
                }
            }
        }
    }

    /// Max value validator
    /// Multitype
    fn max(&mut self, value: &Option<Value>) {
        if self.max.is_some() && value.is_some() {
            let mut requiered_value: u64 = 0;
            if let Some(max) = self.max {
                if max == 0 {
                    if let Some(ref mut error) = self.errors {
                        let msg = format!("Validation value can't be equal: {}", max);
                        error.add(msg);
                    }
                    return ()
                }
                requiered_value = max;
            }
            let is_valid = match *value {
                Some(Value::String(ref value)) => {
                    value.chars().count() as u64 <= requiered_value
                },
                Some(Value::U64(value)) => {
                    value <= requiered_value
                },
                Some(Value::I64(value)) => {
                    value as u64 <= requiered_value
                },
                Some(Value::F64(value)) => {
                    value as u64 <= requiered_value
                },
                Some(Value::Boolean(value)) => {
                    value as u64 <= requiered_value
                },
                _ => false
            };
            if !is_valid {
                if let Some(ref mut error) = self.errors {
                    let msg = format!("Field {} can't be min then: {}", error.field, requiered_value);
                    error.add(msg);
                }
            }
        }
    }

    /// Min value validator
    /// Multitype
    fn min(&mut self, value: &Option<Value>) {
        if self.min.is_some() && value.is_some() {
            let mut requiered_value: i64 = 0;
            if let Some(min) = self.min {
                if let Some(max) = self.max {
                    if min >= max as i64 {
                        if let Some(ref mut error) = self.errors {
                            let msg = format!("Validation rule {} can't be greater or equal max rule: {}", min, max);
                            error.add(msg);
                        }
                        return ()
                    }
                }
                requiered_value = min;
            }
            let is_valid = match *value {
                Some(Value::String(ref value)) => {
                    value.chars().count() as i64 >= requiered_value
                },
                Some(Value::U64(value)) => {
                    value as i64>= requiered_value
                },
                Some(Value::I64(value)) => {
                    value >= requiered_value
                },
                Some(Value::F64(value)) => {
                    value as i64 >= requiered_value
                },
                Some(Value::Boolean(value)) => {
                    value as i64 >= requiered_value
                },
                _ => false
            };
            if !is_valid {
                if let Some(ref mut error) = self.errors {
                    let msg = format!("Field {} can't be min then: {}", error.field, requiered_value);
                    error.add(msg);
                }
            }
        }
    }

    /// Default value validator
    /// Validate by type and default field
    fn default(&mut self, value: &Option<Value>) -> Option<Value> {
        if let Some(ref default_value) = self.default {
            if value.is_none() {
                return self.to_value(default_value.to_json());
            }
        }
        value.to_owned()
    }

    /// Convert value based ot specific type to Value
    pub fn to_value(&self, json_value: Json) -> Option<Value> {
        match json_value {
            Json::I64(value) => Some(Value::I64(value)),
            Json::U64(value) => Some(Value::U64(value)),
            Json::F64(value) => Some(Value::F64(value)),
            Json::String( ref value) => Some(Value::String(value.clone())),
            Json::Boolean(value) => Some(Value::Boolean(value)),
            Json::Null => Some(Value::Null),
            _ => None,
        }
    }

    // Type cast for Value.
    // Returned as Json
    fn type_cast(&self, value: &Option<Value>) -> Option<Json> {
        let val: Value;
        if let Some(name) = value.as_ref() {
            val = name.to_owned();
        } else {
            return None
        }

        match &self.vtype.as_ref() as &str {
            "array" | "vec" => {
                if let Some(val) = <Vec<String> as FromValue>::from_value(&val) {
                    Some(val.to_json())
                } else {
                    None
                }
            },
            "bool" | "boolean" => {
                if let Some(val) = <bool as FromValue>::from_value(&val) {
                    Some(Json::Boolean(val))
                } else {
                    None
                }
            },
            "string" | "str" => {
                if let Some(val) = <String as FromValue>::from_value(&val) {
                    Some(Json::String(val))
                } else {
                    None
                }
            },
            "u8" | "u16" | "u32" | "u64" | "usize" => {
                if let Some(val) = <u64 as FromValue>::from_value(&val) {
                    Some(Json::U64(val))
                } else {
                    None
                }
            },
            "i8" | "i16" | "i32" | "i64" | "isize" => {
                if let Some(val) = <i64 as FromValue>::from_value(&val) {
                    Some(Json::I64(val))
                } else {
                    None
                }
            },
            "f32" | "f64" => {
                if let Some(val) = <f64 as FromValue>::from_value(&val) {
                    Some(Json::F64(val))
                } else {
                    None
                }
            },
            _ => None,
        }
    }
}

/// Validator Errors
#[derive(RustcDecodable, Debug, Clone)]
pub struct ErrorValidator {
    pub errors: Vec<String>,
    pub errors_count: Option<u32>,
    pub field : String,
}

/// Validator Errors methods
impl ErrorValidator {
    /// Init error
    fn new(field: &String) -> Self {
        ErrorValidator {
            field: field.to_owned(),
            errors: vec!(),
            errors_count: None,
        }
    }

    /// Add error
    fn add(&mut self, error: String) {
        if self.errors_count.is_none() {
            self.errors = vec!(error);
            self.errors_count = Some(1);
        } else {
            if let Some(count) = self.errors_count {
                self.errors.push(error);
                self.errors_count = Some(count + 1);
            }
        }
    }
}
<|endoftext|>


<commit_before>extern crate xml;

use std::collections::HashMap;
use std::str;

use xml::writer::{EmitterConfig, XmlEvent};

pub fn characters_to_xml(characters: HashMap<String, String>) -> String {
    let mut output: Vec<u8> = Vec::new();
    let mut writer = EmitterConfig::new()
        .perform_indent(true)
        .create_writer(&mut output);

    writer
        .write(XmlEvent::start_element("CharacterRemarks"))
        .unwrap();

    for (character, line) in &characters {
        let element = XmlEvent::start_element("Character").attr("name", character);
        writer.write(element).unwrap();
        writer.write(XmlEvent::characters(line)).unwrap();
        writer.write(XmlEvent::end_element()).unwrap();
    }

    writer.write(XmlEvent::end_element()).unwrap();
    str::from_utf8(&output).unwrap().to_string()
}

#[cfg(test)]
mod tests {
    use super::characters_to_xml;
    use std::collections::HashMap;

    #[test]
    fn test_xml_output() {
        let mut input = HashMap::new();
        input.insert(
            "April".to_string(),
            "Bubbly: I'm > Tam and <= Emily".to_string(),
        );
        input.insert(
            "Tam O'Shanter".to_string(),
            "Burns: \"When chapman billies leave the street ...\"".to_string(),
        );
        input.insert("Emily".to_string(), "Short & shrift".to_string());

        let output = characters_to_xml(input);

        println!("{}", output);
        assert!(output.contains(
            "<Character name=\"Tam O'Shanter\">Burns: \"When chapman \
             billies leave the street ...\"</Character>"
        ));
        assert!(output
            .contains("<Character name=\"April\">Bubbly: I'm > Tam and <= Emily</Character>"));
        assert!(output.contains("<Character name=\"Emily\">Short & shrift</Character>"));
    }
}
<commit_msg>polish xml-output example<commit_after>use std::collections::HashMap;

use xml::writer::{EmitterConfig, XmlEvent};

pub fn characters_to_xml(characters: HashMap<String, String>) -> String {
    let mut output = Vec::new();
    let mut writer = EmitterConfig::new()
        .perform_indent(true)
        .create_writer(&mut output);

    writer
        .write(XmlEvent::start_element("CharacterRemarks"))
        .unwrap();

    for (character, line) in &characters {
        let element = XmlEvent::start_element("Character").attr("name", character);
        writer.write(element).unwrap();
        writer.write(XmlEvent::characters(line)).unwrap();
        writer.write(XmlEvent::end_element()).unwrap();
    }

    writer.write(XmlEvent::end_element()).unwrap();
    String::from_utf8(output).unwrap()
}

#[cfg(test)]
mod tests {
    use super::characters_to_xml;
    use std::collections::HashMap;

    #[test]
    fn test_xml_output() {
        let mut input = HashMap::new();
        input.insert(
            "April".to_string(),
            "Bubbly: I'm > Tam and <= Emily".to_string(),
        );
        input.insert(
            "Tam O'Shanter".to_string(),
            // Using raw string literals let's us use double quotes without escaping
            r#"Burns: "When chapman billies leave the street ...""#.to_string(),
        );
        input.insert("Emily".to_string(), "Short & shrift".to_string());

        let output = characters_to_xml(input);

        println!("{}", output);
        assert!(output.contains(
            "<Character name=\"Tam O'Shanter\">Burns: \"When chapman \
             billies leave the street ...\"</Character>"
        ));
        assert!(output
            .contains(r#"<Character name="April">Bubbly: I'm > Tam and <= Emily</Character>"#));
        assert!(output.contains(r#"<Character name="Emily">Short & shrift</Character>"#));
    }
}
<|endoftext|>


<commit_before>use crate::utils::{snippet_with_applicability, span_lint, span_lint_and_sugg};
use rustc::lint::{EarlyContext, EarlyLintPass, LintArray, LintPass};
use rustc::{declare_tool_lint, lint_array};
use rustc_errors::Applicability;
use std::borrow::Cow;
use syntax::ast::*;
use syntax::parse::{parser, token};
use syntax::tokenstream::TokenStream;

/// **What it does:** This lint warns when you use `println!("")` to
/// print a newline.
///
/// **Why is this bad?** You should use `println!()`, which is simpler.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// println!("");
/// ```
declare_clippy_lint! {
    pub PRINTLN_EMPTY_STRING,
    style,
    "using `println!(\"\")` with an empty string"
}

/// **What it does:** This lint warns when you use `print!()` with a format
/// string that
/// ends in a newline.
///
/// **Why is this bad?** You should use `println!()` instead, which appends the
/// newline.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// print!("Hello {}!\n", name);
/// ```
/// use println!() instead
/// ```rust
/// println!("Hello {}!", name);
/// ```
declare_clippy_lint! {
    pub PRINT_WITH_NEWLINE,
    style,
    "using `print!()` with a format string that ends in a single newline"
}

/// **What it does:** Checks for printing on *stdout*. The purpose of this lint
/// is to catch debugging remnants.
///
/// **Why is this bad?** People often print on *stdout* while debugging an
/// application and might forget to remove those prints afterward.
///
/// **Known problems:** Only catches `print!` and `println!` calls.
///
/// **Example:**
/// ```rust
/// println!("Hello world!");
/// ```
declare_clippy_lint! {
    pub PRINT_STDOUT,
    restriction,
    "printing on stdout"
}

/// **What it does:** Checks for use of `Debug` formatting. The purpose of this
/// lint is to catch debugging remnants.
///
/// **Why is this bad?** The purpose of the `Debug` trait is to facilitate
/// debugging Rust code. It should not be used in in user-facing output.
///
/// **Example:**
/// ```rust
/// println!("{:?}", foo);
/// ```
declare_clippy_lint! {
    pub USE_DEBUG,
    restriction,
    "use of `Debug`-based formatting"
}

/// **What it does:** This lint warns about the use of literals as `print!`/`println!` args.
///
/// **Why is this bad?** Using literals as `println!` args is inefficient
/// (c.f., https://github.com/matthiaskrgr/rust-str-bench) and unnecessary
/// (i.e., just put the literal in the format string)
///
/// **Known problems:** Will also warn with macro calls as arguments that expand to literals
/// -- e.g., `println!("{}", env!("FOO"))`.
///
/// **Example:**
/// ```rust
/// println!("{}", "foo");
/// ```
/// use the literal without formatting:
/// ```rust
/// println!("foo");
/// ```
declare_clippy_lint! {
    pub PRINT_LITERAL,
    style,
    "printing a literal with a format string"
}

/// **What it does:** This lint warns when you use `writeln!(buf, "")` to
/// print a newline.
///
/// **Why is this bad?** You should use `writeln!(buf)`, which is simpler.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// writeln!("");
/// ```
declare_clippy_lint! {
    pub WRITELN_EMPTY_STRING,
    style,
    "using `writeln!(\"\")` with an empty string"
}

/// **What it does:** This lint warns when you use `write!()` with a format
/// string that
/// ends in a newline.
///
/// **Why is this bad?** You should use `writeln!()` instead, which appends the
/// newline.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// write!(buf, "Hello {}!\n", name);
/// ```
declare_clippy_lint! {
    pub WRITE_WITH_NEWLINE,
    style,
    "using `write!()` with a format string that ends in a single newline"
}

/// **What it does:** This lint warns about the use of literals as `write!`/`writeln!` args.
///
/// **Why is this bad?** Using literals as `writeln!` args is inefficient
/// (c.f., https://github.com/matthiaskrgr/rust-str-bench) and unnecessary
/// (i.e., just put the literal in the format string)
///
/// **Known problems:** Will also warn with macro calls as arguments that expand to literals
/// -- e.g., `writeln!(buf, "{}", env!("FOO"))`.
///
/// **Example:**
/// ```rust
/// writeln!(buf, "{}", "foo");
/// ```
declare_clippy_lint! {
    pub WRITE_LITERAL,
    style,
    "writing a literal with a format string"
}

#[derive(Copy, Clone, Debug)]
pub struct Pass;

impl LintPass for Pass {
    fn get_lints(&self) -> LintArray {
        lint_array!(
            PRINT_WITH_NEWLINE,
            PRINTLN_EMPTY_STRING,
            PRINT_STDOUT,
            USE_DEBUG,
            PRINT_LITERAL,
            WRITE_WITH_NEWLINE,
            WRITELN_EMPTY_STRING,
            WRITE_LITERAL
        )
    }

    fn name(&self) -> &'static str {
        "Write"
    }
}

impl EarlyLintPass for Pass {
    fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &Mac) {
        if mac.node.path == "println" {
            span_lint(cx, PRINT_STDOUT, mac.span, "use of `println!`");
            if let Some(fmtstr) = check_tts(cx, &mac.node.tts, false).0 {
                if fmtstr == "" {
                    span_lint_and_sugg(
                        cx,
                        PRINTLN_EMPTY_STRING,
                        mac.span,
                        "using `println!(\"\")`",
                        "replace it with",
                        "println!()".to_string(),
                        Applicability::MachineApplicable,
                    );
                }
            }
        } else if mac.node.path == "print" {
            span_lint(cx, PRINT_STDOUT, mac.span, "use of `print!`");
            if let Some(fmtstr) = check_tts(cx, &mac.node.tts, false).0 {
                if check_newlines(&fmtstr) {
                    span_lint(
                        cx,
                        PRINT_WITH_NEWLINE,
                        mac.span,
                        "using `print!()` with a format string that ends in a \
                         single newline, consider using `println!()` instead",
                    );
                }
            }
        } else if mac.node.path == "write" {
            if let Some(fmtstr) = check_tts(cx, &mac.node.tts, true).0 {
                if check_newlines(&fmtstr) {
                    span_lint(
                        cx,
                        WRITE_WITH_NEWLINE,
                        mac.span,
                        "using `write!()` with a format string that ends in a \
                         single newline, consider using `writeln!()` instead",
                    );
                }
            }
        } else if mac.node.path == "writeln" {
            let check_tts = check_tts(cx, &mac.node.tts, true);
            if let Some(fmtstr) = check_tts.0 {
                if fmtstr == "" {
                    let mut applicability = Applicability::MachineApplicable;
                    let suggestion = check_tts.1.map_or_else(
                        move || {
                            applicability = Applicability::HasPlaceholders;
                            Cow::Borrowed("v")
                        },
                        move |expr| snippet_with_applicability(cx, expr.span, "v", &mut applicability),
                    );

                    span_lint_and_sugg(
                        cx,
                        WRITELN_EMPTY_STRING,
                        mac.span,
                        format!("using `writeln!({}, \"\")`", suggestion).as_str(),
                        "replace it with",
                        format!("writeln!({})", suggestion),
                        applicability,
                    );
                }
            }
        }
    }
}

/// Checks the arguments of `print[ln]!` and `write[ln]!` calls. It will return a tuple of two
/// options. The first part of the tuple is `format_str` of the macros. The second part of the tuple
/// is in the `write[ln]!` case the expression the `format_str` should be written to.
///
/// Example:
///
/// Calling this function on
/// ```rust,ignore
/// writeln!(buf, "string to write: {}", something)
/// ```
/// will return
/// ```rust,ignore
/// (Some("string to write: {}"), Some(buf))
/// ```
fn check_tts<'a>(cx: &EarlyContext<'a>, tts: &TokenStream, is_write: bool) -> (Option<String>, Option<Expr>) {
    use fmt_macros::*;
    let tts = tts.clone();
    let mut parser = parser::Parser::new(&cx.sess.parse_sess, tts, None, false, false);
    let mut expr: Option<Expr> = None;
    if is_write {
        expr = match parser.parse_expr().map_err(|mut err| err.cancel()) {
            Ok(p) => Some(p.into_inner()),
            Err(_) => return (None, None),
        };
        // might be `writeln!(foo)`
        if parser.expect(&token::Comma).map_err(|mut err| err.cancel()).is_err() {
            return (None, expr);
        }
    }

    let fmtstr = match parser.parse_str().map_err(|mut err| err.cancel()) {
        Ok(token) => token.0.to_string(),
        Err(_) => return (None, expr),
    };
    let tmp = fmtstr.clone();
    let mut args = vec![];
    let mut fmt_parser = Parser::new(&tmp, None, Vec::new(), false);
    while let Some(piece) = fmt_parser.next() {
        if !fmt_parser.errors.is_empty() {
            return (None, expr);
        }
        if let Piece::NextArgument(arg) = piece {
            if arg.format.ty == "?" {
                // FIXME: modify rustc's fmt string parser to give us the current span
                span_lint(cx, USE_DEBUG, parser.prev_span, "use of `Debug`-based formatting");
            }
            args.push(arg);
        }
    }
    let lint = if is_write { WRITE_LITERAL } else { PRINT_LITERAL };
    let mut idx = 0;
    loop {
        const SIMPLE: FormatSpec<'_> = FormatSpec {
            fill: None,
            align: AlignUnknown,
            flags: 0,
            precision: CountImplied,
            width: CountImplied,
            ty: "",
        };
        if !parser.eat(&token::Comma) {
            return (Some(fmtstr), expr);
        }
        let token_expr = match parser.parse_expr().map_err(|mut err| err.cancel()) {
            Ok(expr) => expr,
            Err(_) => return (Some(fmtstr), None),
        };
        match &token_expr.node {
            ExprKind::Lit(_) => {
                let mut all_simple = true;
                let mut seen = false;
                for arg in &args {
                    match arg.position {
                        ArgumentImplicitlyIs(n) | ArgumentIs(n) => {
                            if n == idx {
                                all_simple &= arg.format == SIMPLE;
                                seen = true;
                            }
                        },
                        ArgumentNamed(_) => {},
                    }
                }
                if all_simple && seen {
                    span_lint(cx, lint, token_expr.span, "literal with an empty format string");
                }
                idx += 1;
            },
            ExprKind::Assign(lhs, rhs) => {
                if let ExprKind::Lit(_) = rhs.node {
                    if let ExprKind::Path(_, p) = &lhs.node {
                        let mut all_simple = true;
                        let mut seen = false;
                        for arg in &args {
                            match arg.position {
                                ArgumentImplicitlyIs(_) | ArgumentIs(_) => {},
                                ArgumentNamed(name) => {
                                    if *p == name {
                                        seen = true;
                                        all_simple &= arg.format == SIMPLE;
                                    }
                                },
                            }
                        }
                        if all_simple && seen {
                            span_lint(cx, lint, rhs.span, "literal with an empty format string");
                        }
                    }
                }
            },
            _ => idx += 1,
        }
    }
}

// Checks if `s` constains a single newline that terminates it
fn check_newlines(s: &str) -> bool {
    if s.len() < 2 {
        return false;
    }

    let bytes = s.as_bytes();
    if bytes[bytes.len() - 2] != b'\\' || bytes[bytes.len() - 1] != b'n' {
        return false;
    }

    let mut escaping = false;
    for (index, &byte) in bytes.iter().enumerate() {
        if escaping {
            if byte == b'n' {
                return index == bytes.len() - 1;
            }
            escaping = false;
        } else if byte == b'\\' {
            escaping = true;
        }
    }

    false
}
<commit_msg>Don't fail for raw string ending in \n<commit_after>use crate::utils::{snippet_with_applicability, span_lint, span_lint_and_sugg};
use rustc::lint::{EarlyContext, EarlyLintPass, LintArray, LintPass};
use rustc::{declare_tool_lint, lint_array};
use rustc_errors::Applicability;
use std::borrow::Cow;
use syntax::ast::*;
use syntax::parse::{parser, token};
use syntax::tokenstream::{TokenStream, TokenTree};

/// **What it does:** This lint warns when you use `println!("")` to
/// print a newline.
///
/// **Why is this bad?** You should use `println!()`, which is simpler.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// println!("");
/// ```
declare_clippy_lint! {
    pub PRINTLN_EMPTY_STRING,
    style,
    "using `println!(\"\")` with an empty string"
}

/// **What it does:** This lint warns when you use `print!()` with a format
/// string that
/// ends in a newline.
///
/// **Why is this bad?** You should use `println!()` instead, which appends the
/// newline.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// print!("Hello {}!\n", name);
/// ```
/// use println!() instead
/// ```rust
/// println!("Hello {}!", name);
/// ```
declare_clippy_lint! {
    pub PRINT_WITH_NEWLINE,
    style,
    "using `print!()` with a format string that ends in a single newline"
}

/// **What it does:** Checks for printing on *stdout*. The purpose of this lint
/// is to catch debugging remnants.
///
/// **Why is this bad?** People often print on *stdout* while debugging an
/// application and might forget to remove those prints afterward.
///
/// **Known problems:** Only catches `print!` and `println!` calls.
///
/// **Example:**
/// ```rust
/// println!("Hello world!");
/// ```
declare_clippy_lint! {
    pub PRINT_STDOUT,
    restriction,
    "printing on stdout"
}

/// **What it does:** Checks for use of `Debug` formatting. The purpose of this
/// lint is to catch debugging remnants.
///
/// **Why is this bad?** The purpose of the `Debug` trait is to facilitate
/// debugging Rust code. It should not be used in in user-facing output.
///
/// **Example:**
/// ```rust
/// println!("{:?}", foo);
/// ```
declare_clippy_lint! {
    pub USE_DEBUG,
    restriction,
    "use of `Debug`-based formatting"
}

/// **What it does:** This lint warns about the use of literals as `print!`/`println!` args.
///
/// **Why is this bad?** Using literals as `println!` args is inefficient
/// (c.f., https://github.com/matthiaskrgr/rust-str-bench) and unnecessary
/// (i.e., just put the literal in the format string)
///
/// **Known problems:** Will also warn with macro calls as arguments that expand to literals
/// -- e.g., `println!("{}", env!("FOO"))`.
///
/// **Example:**
/// ```rust
/// println!("{}", "foo");
/// ```
/// use the literal without formatting:
/// ```rust
/// println!("foo");
/// ```
declare_clippy_lint! {
    pub PRINT_LITERAL,
    style,
    "printing a literal with a format string"
}

/// **What it does:** This lint warns when you use `writeln!(buf, "")` to
/// print a newline.
///
/// **Why is this bad?** You should use `writeln!(buf)`, which is simpler.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// writeln!("");
/// ```
declare_clippy_lint! {
    pub WRITELN_EMPTY_STRING,
    style,
    "using `writeln!(\"\")` with an empty string"
}

/// **What it does:** This lint warns when you use `write!()` with a format
/// string that
/// ends in a newline.
///
/// **Why is this bad?** You should use `writeln!()` instead, which appends the
/// newline.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// write!(buf, "Hello {}!\n", name);
/// ```
declare_clippy_lint! {
    pub WRITE_WITH_NEWLINE,
    style,
    "using `write!()` with a format string that ends in a single newline"
}

/// **What it does:** This lint warns about the use of literals as `write!`/`writeln!` args.
///
/// **Why is this bad?** Using literals as `writeln!` args is inefficient
/// (c.f., https://github.com/matthiaskrgr/rust-str-bench) and unnecessary
/// (i.e., just put the literal in the format string)
///
/// **Known problems:** Will also warn with macro calls as arguments that expand to literals
/// -- e.g., `writeln!(buf, "{}", env!("FOO"))`.
///
/// **Example:**
/// ```rust
/// writeln!(buf, "{}", "foo");
/// ```
declare_clippy_lint! {
    pub WRITE_LITERAL,
    style,
    "writing a literal with a format string"
}

#[derive(Copy, Clone, Debug)]
pub struct Pass;

impl LintPass for Pass {
    fn get_lints(&self) -> LintArray {
        lint_array!(
            PRINT_WITH_NEWLINE,
            PRINTLN_EMPTY_STRING,
            PRINT_STDOUT,
            USE_DEBUG,
            PRINT_LITERAL,
            WRITE_WITH_NEWLINE,
            WRITELN_EMPTY_STRING,
            WRITE_LITERAL
        )
    }

    fn name(&self) -> &'static str {
        "Write"
    }
}

impl EarlyLintPass for Pass {
    fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &Mac) {
        if mac.node.path == "println" {
            span_lint(cx, PRINT_STDOUT, mac.span, "use of `println!`");
            if let Some(fmtstr) = check_tts(cx, &mac.node.tts, false).0 {
                if fmtstr == "" {
                    span_lint_and_sugg(
                        cx,
                        PRINTLN_EMPTY_STRING,
                        mac.span,
                        "using `println!(\"\")`",
                        "replace it with",
                        "println!()".to_string(),
                        Applicability::MachineApplicable,
                    );
                }
            }
        } else if mac.node.path == "print" {
            span_lint(cx, PRINT_STDOUT, mac.span, "use of `print!`");
            if let (Some(fmtstr), _, is_raw) = check_tts(cx, &mac.node.tts, false) {
                if !is_raw && check_newlines(&fmtstr) {
                    span_lint(
                        cx,
                        PRINT_WITH_NEWLINE,
                        mac.span,
                        "using `print!()` with a format string that ends in a \
                         single newline, consider using `println!()` instead",
                    );
                }
            }
        } else if mac.node.path == "write" {
            if let (Some(fmtstr), _, is_raw) = check_tts(cx, &mac.node.tts, true) {
                if !is_raw && check_newlines(&fmtstr) {
                    span_lint(
                        cx,
                        WRITE_WITH_NEWLINE,
                        mac.span,
                        "using `write!()` with a format string that ends in a \
                         single newline, consider using `writeln!()` instead",
                    );
                }
            }
        } else if mac.node.path == "writeln" {
            let check_tts = check_tts(cx, &mac.node.tts, true);
            if let Some(fmtstr) = check_tts.0 {
                if fmtstr == "" {
                    let mut applicability = Applicability::MachineApplicable;
                    let suggestion = check_tts.1.map_or_else(
                        move || {
                            applicability = Applicability::HasPlaceholders;
                            Cow::Borrowed("v")
                        },
                        move |expr| snippet_with_applicability(cx, expr.span, "v", &mut applicability),
                    );

                    span_lint_and_sugg(
                        cx,
                        WRITELN_EMPTY_STRING,
                        mac.span,
                        format!("using `writeln!({}, \"\")`", suggestion).as_str(),
                        "replace it with",
                        format!("writeln!({})", suggestion),
                        applicability,
                    );
                }
            }
        }
    }
}

/// Checks the arguments of `print[ln]!` and `write[ln]!` calls. It will return a tuple of two
/// options and a bool. The first part of the tuple is `format_str` of the macros. The second part
/// of the tuple is in the `write[ln]!` case the expression the `format_str` should be written to.
/// The final part is a boolean flag indicating if the string is a raw string.
///
/// Example:
///
/// Calling this function on
/// ```rust,ignore
/// writeln!(buf, "string to write: {}", something)
/// ```
/// will return
/// ```rust,ignore
/// (Some("string to write: {}"), Some(buf), false)
/// ```
fn check_tts<'a>(cx: &EarlyContext<'a>, tts: &TokenStream, is_write: bool) -> (Option<String>, Option<Expr>, bool) {
    use fmt_macros::*;
    let tts = tts.clone();
    let mut is_raw = false;
    if let TokenStream(Some(tokens)) = &tts {
        for token in tokens.iter() {
            if let (TokenTree::Token(_, token::Token::Literal(lit, _)), _) = token {
                match lit {
                    token::Lit::Str_(_) => break,
                    token::Lit::StrRaw(_, _) => {
                        is_raw = true;
                        break;
                    },
                    _ => {},
                }
            }
        }
    }
    let mut parser = parser::Parser::new(&cx.sess.parse_sess, tts, None, false, false);
    let mut expr: Option<Expr> = None;
    if is_write {
        expr = match parser.parse_expr().map_err(|mut err| err.cancel()) {
            Ok(p) => Some(p.into_inner()),
            Err(_) => return (None, None, is_raw),
        };
        // might be `writeln!(foo)`
        if parser.expect(&token::Comma).map_err(|mut err| err.cancel()).is_err() {
            return (None, expr, is_raw);
        }
    }

    let fmtstr = match parser.parse_str().map_err(|mut err| err.cancel()) {
        Ok(token) => token.0.to_string(),
        Err(_) => return (None, expr, is_raw),
    };
    let tmp = fmtstr.clone();
    let mut args = vec![];
    let mut fmt_parser = Parser::new(&tmp, None, Vec::new(), false);
    while let Some(piece) = fmt_parser.next() {
        if !fmt_parser.errors.is_empty() {
            return (None, expr, is_raw);
        }
        if let Piece::NextArgument(arg) = piece {
            if arg.format.ty == "?" {
                // FIXME: modify rustc's fmt string parser to give us the current span
                span_lint(cx, USE_DEBUG, parser.prev_span, "use of `Debug`-based formatting");
            }
            args.push(arg);
        }
    }
    let lint = if is_write { WRITE_LITERAL } else { PRINT_LITERAL };
    let mut idx = 0;
    loop {
        const SIMPLE: FormatSpec<'_> = FormatSpec {
            fill: None,
            align: AlignUnknown,
            flags: 0,
            precision: CountImplied,
            width: CountImplied,
            ty: "",
        };
        if !parser.eat(&token::Comma) {
            return (Some(fmtstr), expr, is_raw);
        }
        let token_expr = match parser.parse_expr().map_err(|mut err| err.cancel()) {
            Ok(expr) => expr,
            Err(_) => return (Some(fmtstr), None, is_raw),
        };
        match &token_expr.node {
            ExprKind::Lit(_) => {
                let mut all_simple = true;
                let mut seen = false;
                for arg in &args {
                    match arg.position {
                        ArgumentImplicitlyIs(n) | ArgumentIs(n) => {
                            if n == idx {
                                all_simple &= arg.format == SIMPLE;
                                seen = true;
                            }
                        },
                        ArgumentNamed(_) => {},
                    }
                }
                if all_simple && seen {
                    span_lint(cx, lint, token_expr.span, "literal with an empty format string");
                }
                idx += 1;
            },
            ExprKind::Assign(lhs, rhs) => {
                if let ExprKind::Lit(_) = rhs.node {
                    if let ExprKind::Path(_, p) = &lhs.node {
                        let mut all_simple = true;
                        let mut seen = false;
                        for arg in &args {
                            match arg.position {
                                ArgumentImplicitlyIs(_) | ArgumentIs(_) => {},
                                ArgumentNamed(name) => {
                                    if *p == name {
                                        seen = true;
                                        all_simple &= arg.format == SIMPLE;
                                    }
                                },
                            }
                        }
                        if all_simple && seen {
                            span_lint(cx, lint, rhs.span, "literal with an empty format string");
                        }
                    }
                }
            },
            _ => idx += 1,
        }
    }
}

// Checks if `s` constains a single newline that terminates it
fn check_newlines(s: &str) -> bool {
    if s.len() < 2 {
        return false;
    }

    let bytes = s.as_bytes();
    if bytes[bytes.len() - 2] != b'\\' || bytes[bytes.len() - 1] != b'n' {
        return false;
    }

    let mut escaping = false;
    for (index, &byte) in bytes.iter().enumerate() {
        if escaping {
            if byte == b'n' {
                return index == bytes.len() - 1;
            }
            escaping = false;
        } else if byte == b'\\' {
            escaping = true;
        }
    }

    false
}
<|endoftext|>


<commit_before>// Copyright (C) 2018, 2019, 2020 O.S. Systems Sofware LTDA
//
// SPDX-License-Identifier: Apache-2.0

use crate::{Error, Result};
use async_std::{
    io::BufReader,
    os::unix::net::{UnixListener, UnixStream},
    prelude::*,
};
use log::warn;
use std::{env, fs, path::Path, pin::Pin};

const SDK_TRIGGER_FILENAME: &str =
    "/usr/share/updatehub/state-change-callbacks.d/10-updatehub-sdk-statechange-trigger";
const SOCKET_PATH: &str = "/run/updatehub-statechange.sock";

type CallbackFn = dyn Fn(Handler) -> Pin<Box<dyn Future<Output = Result<()>>>>;

#[derive(Default)]
pub struct StateChangeListener {
    download_callbacks: Vec<Box<CallbackFn>>,
    install_callbacks: Vec<Box<CallbackFn>>,
    reboot_callbacks: Vec<Box<CallbackFn>>,
    error_callbacks: Vec<Box<CallbackFn>>,
}

#[derive(Debug)]
pub enum State {
    Download,
    Install,
    Reboot,
    Error,
}

pub struct Handler {
    stream: UnixStream,
}

impl Handler {
    // Cancels the current action on the agent.
    pub async fn cancel(&mut self) -> Result<()> {
        self.stream.write_all(b"cancel").await.map_err(Error::Io)
    }

    // Tell the agent to proceed with the transition.
    pub async fn proceed(&self) -> Result<()> {
        // No message need to be sent to the connection in order to the
        // agent to proceed handling the current state.
        Ok(())
    }
}

impl StateChangeListener {
    #[inline]
    pub fn new() -> Self {
        StateChangeListener::default()
    }

    pub fn on_state<F, Fut>(&mut self, state: State, f: F)
    where
        F: Fn(Handler) -> Fut + 'static,
        Fut: Future<Output = Result<()>> + 'static,
    {
        match state {
            State::Download => self.download_callbacks.push(Box::new(move |d| Box::pin(f(d)))),
            State::Install => self.install_callbacks.push(Box::new(move |d| Box::pin(f(d)))),
            State::Reboot => self.reboot_callbacks.push(Box::new(move |d| Box::pin(f(d)))),
            State::Error => self.error_callbacks.push(Box::new(move |d| Box::pin(f(d)))),
        }
    }

    pub async fn listen(&self) -> Result<()> {
        let sdk_trigger = Path::new(SDK_TRIGGER_FILENAME);
        if !sdk_trigger.exists() {
            warn!("WARNING: updatehub-sdk-statechange-trigger not found on {:?}", sdk_trigger);
        }

        let socket_path = env::var("UH_LISTENER_TEST").unwrap_or_else(|_| SOCKET_PATH.to_string());
        let socket_path = Path::new(&socket_path);
        if socket_path.exists() {
            fs::remove_file(&socket_path)?;
        }

        let listener = UnixListener::bind(socket_path).await?;
        loop {
            let (socket, ..) = listener.accept().await?;
            self.handle_connection(socket).await?;
        }
    }

    async fn handle_connection(&self, stream: UnixStream) -> Result<()> {
        let mut reader = BufReader::new(&stream);
        let mut line = String::new();

        reader.read_line(&mut line).await?;

        self.emit(stream, &line.trim()).await
    }

    async fn emit(&self, stream: UnixStream, input: &str) -> Result<()> {
        let callbacks = match input {
            "download" => &self.download_callbacks,
            "install" => &self.install_callbacks,
            "reboot" => &self.reboot_callbacks,
            "error" => &self.error_callbacks,
            _ => unreachable!("the input is not valid"),
        };

        for f in callbacks {
            let stream = stream.clone();
            f(Handler { stream }).await?;
        }

        Ok(())
    }
}
<commit_msg>updatehub-sdk: listener: Add support for Probe callbacks<commit_after>// Copyright (C) 2018, 2019, 2020 O.S. Systems Sofware LTDA
//
// SPDX-License-Identifier: Apache-2.0

use crate::{Error, Result};
use async_std::{
    io::BufReader,
    os::unix::net::{UnixListener, UnixStream},
    prelude::*,
};
use log::warn;
use std::{env, fs, path::Path, pin::Pin};

const SDK_TRIGGER_FILENAME: &str =
    "/usr/share/updatehub/state-change-callbacks.d/10-updatehub-sdk-statechange-trigger";
const SOCKET_PATH: &str = "/run/updatehub-statechange.sock";

type CallbackFn = dyn Fn(Handler) -> Pin<Box<dyn Future<Output = Result<()>>>>;

#[derive(Default)]
pub struct StateChangeListener {
    probe_callbacks: Vec<Box<CallbackFn>>,
    download_callbacks: Vec<Box<CallbackFn>>,
    install_callbacks: Vec<Box<CallbackFn>>,
    reboot_callbacks: Vec<Box<CallbackFn>>,
    error_callbacks: Vec<Box<CallbackFn>>,
}

#[derive(Debug)]
pub enum State {
    Probe,
    Download,
    Install,
    Reboot,
    Error,
}

pub struct Handler {
    stream: UnixStream,
}

impl Handler {
    // Cancels the current action on the agent.
    pub async fn cancel(&mut self) -> Result<()> {
        self.stream.write_all(b"cancel").await.map_err(Error::Io)
    }

    // Tell the agent to proceed with the transition.
    pub async fn proceed(&self) -> Result<()> {
        // No message need to be sent to the connection in order to the
        // agent to proceed handling the current state.
        Ok(())
    }
}

impl StateChangeListener {
    #[inline]
    pub fn new() -> Self {
        StateChangeListener::default()
    }

    pub fn on_state<F, Fut>(&mut self, state: State, f: F)
    where
        F: Fn(Handler) -> Fut + 'static,
        Fut: Future<Output = Result<()>> + 'static,
    {
        match state {
            State::Probe => self.probe_callbacks.push(Box::new(move |d| Box::pin(f(d)))),
            State::Download => self.download_callbacks.push(Box::new(move |d| Box::pin(f(d)))),
            State::Install => self.install_callbacks.push(Box::new(move |d| Box::pin(f(d)))),
            State::Reboot => self.reboot_callbacks.push(Box::new(move |d| Box::pin(f(d)))),
            State::Error => self.error_callbacks.push(Box::new(move |d| Box::pin(f(d)))),
        }
    }

    pub async fn listen(&self) -> Result<()> {
        let sdk_trigger = Path::new(SDK_TRIGGER_FILENAME);
        if !sdk_trigger.exists() {
            warn!("WARNING: updatehub-sdk-statechange-trigger not found on {:?}", sdk_trigger);
        }

        let socket_path = env::var("UH_LISTENER_TEST").unwrap_or_else(|_| SOCKET_PATH.to_string());
        let socket_path = Path::new(&socket_path);
        if socket_path.exists() {
            fs::remove_file(&socket_path)?;
        }

        let listener = UnixListener::bind(socket_path).await?;
        loop {
            let (socket, ..) = listener.accept().await?;
            self.handle_connection(socket).await?;
        }
    }

    async fn handle_connection(&self, stream: UnixStream) -> Result<()> {
        let mut reader = BufReader::new(&stream);
        let mut line = String::new();

        reader.read_line(&mut line).await?;

        self.emit(stream, &line.trim()).await
    }

    async fn emit(&self, stream: UnixStream, input: &str) -> Result<()> {
        let callbacks = match input {
            "probe" => &self.probe_callbacks,
            "download" => &self.download_callbacks,
            "install" => &self.install_callbacks,
            "reboot" => &self.reboot_callbacks,
            "error" => &self.error_callbacks,
            _ => unreachable!("the input is not valid"),
        };

        for f in callbacks {
            let stream = stream.clone();
            f(Handler { stream }).await?;
        }

        Ok(())
    }
}
<|endoftext|>


<commit_before>// The Computer Language Benchmarks Game
// http://benchmarksgame.alioth.debian.org/
//
// contributed by the Rust Project Developers

// Copyright (c) 2013-2014 The Rust Project Developers
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// - Redistributions of source code must retain the above copyright
//   notice, this list of conditions and the following disclaimer.
//
// - Redistributions in binary form must reproduce the above copyright
//   notice, this list of conditions and the following disclaimer in
//   the documentation and/or other materials provided with the
//   distribution.
//
// - Neither the name of "The Computer Language Benchmarks Game" nor
//   the name of "The Computer Language Shootout Benchmarks" nor the
//   names of its contributors may be used to endorse or promote
//   products derived from this software without specific prior
//   written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
// COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.

extern crate num;
extern crate test;

use std::str::FromStr;
use std::num::{FromPrimitive, ToPrimitive};

use test::Bencher;

use num::{BigInt, Integer, One, Zero};

struct Context {
    numer: BigInt,
    accum: BigInt,
    denom: BigInt,
}

impl Context {
    fn new() -> Context {
        Context {
            numer: One::one(),
            accum: Zero::zero(),
            denom: One::one(),
        }
    }

    fn from_int(i: int) -> BigInt {
        FromPrimitive::from_int(i).unwrap()
    }

    fn extract_digit(&self) -> int {
        if self.numer > self.accum {return -1;}
        let (q, r) =
            (&self.numer * Context::from_int(3) + &self.accum)
            .div_rem(&self.denom);
        if r + &self.numer >= self.denom {return -1;}
        q.to_int().unwrap()
    }

    fn next_term(&mut self, k: int) {
        let y2 = Context::from_int(k * 2 + 1);
        self.accum = (&self.accum + (&self.numer << 1)) * &y2;
        self.numer = &self.numer * Context::from_int(k);
        self.denom = &self.denom * y2;
    }

    fn eliminate_digit(&mut self, d: int) {
        let d = Context::from_int(d);
        let ten = Context::from_int(10);
        self.accum = (&self.accum - &self.denom * d) * &ten;
        self.numer = &self.numer * ten;
    }
}

fn pidigits(n: int) {
    let mut k = 0;
    let mut context = Context::new();

    for i in range(1, n + 1) {
        let mut d;
        loop {
            k += 1;
            context.next_term(k);
            d = context.extract_digit();
            if d != -1 {break;}
        }

        print!("{}", d);
        if i % 10 == 0 {print!("\t:{}\n", i);}

        context.eliminate_digit(d);
    }

    let m = n % 10;
    if m != 0 {
        for _ in range(m, 10) { print!(" "); }
        print!("\t:{}\n", n);
    }
}

static DEFAULT_DIGITS: int = 512;

#[bench]
fn use_bencher(b: &mut Bencher) {
    b.iter(|| pidigits(DEFAULT_DIGITS))
}

fn main() {
    let args = std::os::args();
    let args = args.as_slice();
    let n = if args.len() < 2 {
        DEFAULT_DIGITS
    } else {
        FromStr::from_str(args[1].as_slice()).unwrap()
    };
    pidigits(n);
}
<commit_msg>Replace deprecated int references with i32/isize.<commit_after>// The Computer Language Benchmarks Game
// http://benchmarksgame.alioth.debian.org/
//
// contributed by the Rust Project Developers

// Copyright (c) 2013-2014 The Rust Project Developers
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// - Redistributions of source code must retain the above copyright
//   notice, this list of conditions and the following disclaimer.
//
// - Redistributions in binary form must reproduce the above copyright
//   notice, this list of conditions and the following disclaimer in
//   the documentation and/or other materials provided with the
//   distribution.
//
// - Neither the name of "The Computer Language Benchmarks Game" nor
//   the name of "The Computer Language Shootout Benchmarks" nor the
//   names of its contributors may be used to endorse or promote
//   products derived from this software without specific prior
//   written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
// COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.

extern crate num;
extern crate test;

use std::str::FromStr;
use std::num::{FromPrimitive, ToPrimitive};

use test::Bencher;

use num::{BigInt, Integer, One, Zero};

struct Context {
    numer: BigInt,
    accum: BigInt,
    denom: BigInt,
}

impl Context {
    fn new() -> Context {
        Context {
            numer: One::one(),
            accum: Zero::zero(),
            denom: One::one(),
        }
    }

    fn from_i32(i: i32) -> BigInt {
        FromPrimitive::from_i32(i).unwrap()
    }

    fn extract_digit(&self) -> i32 {
        if self.numer > self.accum {return -1;}
        let (q, r) =
            (&self.numer * Context::from_i32(3) + &self.accum)
            .div_rem(&self.denom);
        if r + &self.numer >= self.denom {return -1;}
        q.to_i32().unwrap()
    }

    fn next_term(&mut self, k: i32) {
        let y2 = Context::from_i32(k * 2 + 1);
        self.accum = (&self.accum + (&self.numer << 1)) * &y2;
        self.numer = &self.numer * Context::from_i32(k);
        self.denom = &self.denom * y2;
    }

    fn eliminate_digit(&mut self, d: i32) {
        let d = Context::from_i32(d);
        let ten = Context::from_i32(10);
        self.accum = (&self.accum - &self.denom * d) * &ten;
        self.numer = &self.numer * ten;
    }
}

fn pidigits(n: isize) {
    let mut k = 0;
    let mut context = Context::new();

    for i in range(1, n + 1) {
        let mut d;
        loop {
            k += 1;
            context.next_term(k);
            d = context.extract_digit();
            if d != -1 {break;}
        }

        print!("{}", d);
        if i % 10 == 0 {print!("\t:{}\n", i);}

        context.eliminate_digit(d);
    }

    let m = n % 10;
    if m != 0 {
        for _ in range(m, 10) { print!(" "); }
        print!("\t:{}\n", n);
    }
}

static DEFAULT_DIGITS: isize = 512;

#[bench]
fn use_bencher(b: &mut Bencher) {
    b.iter(|| pidigits(DEFAULT_DIGITS))
}

fn main() {
    let args = std::os::args();
    let args = args.as_slice();
    let n = if args.len() < 2 {
        DEFAULT_DIGITS
    } else {
        FromStr::from_str(args[1].as_slice()).unwrap()
    };
    pidigits(n);
}
<|endoftext|>


<commit_before>// Copyright 2013 The rust-url developers.
//
// 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 crate::test::TestFn;
use idna::punycode::{decode, encode_str};
use serde_json::map::Map;
use serde_json::Value;
use std::str::FromStr;

fn one_test(decoded: &str, encoded: &str) {
    match decode(encoded) {
        None => panic!("Decoding {} failed.", encoded),
        Some(result) => {
            let result = result.into_iter().collect::<String>();
            assert!(
                result == decoded,
                format!(
                    "Incorrect decoding of \"{}\":\n   \"{}\"\n!= \"{}\"\n",
                    encoded, result, decoded
                )
            )
        }
    }

    match encode_str(decoded) {
        None => panic!("Encoding {} failed.", decoded),
        Some(result) => assert!(
            result == encoded,
            format!(
                "Incorrect encoding of \"{}\":\n   \"{}\"\n!= \"{}\"\n",
                decoded, result, encoded
            )
        ),
    }
}

fn get_string<'a>(map: &'a Map<String, Value>, key: &str) -> &'a str {
    match map.get(&key.to_string()) {
        Some(&Value::String(ref s)) => s,
        None => "",
        _ => panic!(),
    }
}

pub fn collect_tests<F: FnMut(String, TestFn)>(add_test: &mut F) {
    match Value::from_str(include_str!("punycode_tests.json")) {
        Ok(Value::Array(tests)) => {
            for (i, test) in tests.into_iter().enumerate() {
                match test {
                    Value::Object(o) => {
                        let test_name = {
                            let desc = get_string(&o, "description");
                            if desc.is_empty() {
                                format!("Punycode {}", i + 1)
                            } else {
                                format!("Punycode {}: {}", i + 1, desc)
                            }
                        };
                        add_test(
                            test_name,
                            TestFn::dyn_test_fn(move || {
                                one_test(get_string(&o, "decoded"), get_string(&o, "encoded"))
                            }),
                        )
                    }
                    _ => panic!(),
                }
            }
        }
        other => panic!("{:?}", other),
    }
}
<commit_msg>Use assert!()'s built-in formatting mechanics<commit_after>// Copyright 2013 The rust-url developers.
//
// 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 crate::test::TestFn;
use idna::punycode::{decode, encode_str};
use serde_json::map::Map;
use serde_json::Value;
use std::str::FromStr;

fn one_test(decoded: &str, encoded: &str) {
    match decode(encoded) {
        None => panic!("Decoding {} failed.", encoded),
        Some(result) => {
            let result = result.into_iter().collect::<String>();
            assert!(
                result == decoded,
                "Incorrect decoding of \"{}\":\n   \"{}\"\n!= \"{}\"\n",
                encoded,
                result,
                decoded
            )
        }
    }

    match encode_str(decoded) {
        None => panic!("Encoding {} failed.", decoded),
        Some(result) => assert!(
            result == encoded,
            "Incorrect encoding of \"{}\":\n   \"{}\"\n!= \"{}\"\n",
            decoded,
            result,
            encoded
        ),
    }
}

fn get_string<'a>(map: &'a Map<String, Value>, key: &str) -> &'a str {
    match map.get(&key.to_string()) {
        Some(&Value::String(ref s)) => s,
        None => "",
        _ => panic!(),
    }
}

pub fn collect_tests<F: FnMut(String, TestFn)>(add_test: &mut F) {
    match Value::from_str(include_str!("punycode_tests.json")) {
        Ok(Value::Array(tests)) => {
            for (i, test) in tests.into_iter().enumerate() {
                match test {
                    Value::Object(o) => {
                        let test_name = {
                            let desc = get_string(&o, "description");
                            if desc.is_empty() {
                                format!("Punycode {}", i + 1)
                            } else {
                                format!("Punycode {}: {}", i + 1, desc)
                            }
                        };
                        add_test(
                            test_name,
                            TestFn::dyn_test_fn(move || {
                                one_test(get_string(&o, "decoded"), get_string(&o, "encoded"))
                            }),
                        )
                    }
                    _ => panic!(),
                }
            }
        }
        other => panic!("{:?}", other),
    }
}
<|endoftext|>


<commit_before>#[macro_use] extern crate log;
#[macro_use] extern crate version;
extern crate clap;
extern crate chrono;

extern crate libimagdiary;
extern crate libimagentrylist;
extern crate libimaginteraction;
extern crate libimagrt;
extern crate libimagstore;
extern crate libimagutil;
extern crate libimagtimeui;
#[macro_use] extern crate libimagerror;

use std::path::PathBuf;
use std::process::exit;
use chrono::naive::datetime::NaiveDateTime;

use libimagdiary::diary::Diary;
use libimagdiary::diaryid::DiaryId;
use libimagdiary::error::DiaryError as DE;
use libimagdiary::error::DiaryErrorKind as DEK;
use libimagentrylist::listers::core::CoreLister;
use libimagentrylist::lister::Lister;
use libimagrt::edit::Edit;
use libimagrt::runtime::Runtime;
use libimagstore::storeid::StoreId;
use libimagerror::trace::trace_error;
use libimagtimeui::datetime::DateTime;
use libimagtimeui::parse::Parse;

mod ui;

use ui::build_ui;

fn main() {
    let name = "imag-diary";
    let version = &version!()[..];
    let about = "Personal Diary/Diaries";
    let ui = build_ui(Runtime::get_default_cli_builder(name, version, about));
    let rt = {
        let rt = Runtime::new(ui);
        if rt.is_ok() {
            rt.unwrap()
        } else {
            println!("Could not set up Runtime");
            println!("{:?}", rt.err().unwrap());
            exit(1);
        }
    };

    rt.cli()
        .subcommand_name()
        .map(|name| {
            debug!("Call {}", name);
            match name {
                "create" => create(&rt),
                // "delete" => delete(&rt),
                "edit" => edit(&rt),
                "list" => list(&rt),
                "diary" => diary(&rt),
                _        => {
                    debug!("Unknown command"); // More error handling
                },
            }
        });
}

fn create(rt: &Runtime) {
    let diaryname = get_diary_name(rt);
    if diaryname.is_none() {
        warn!("No diary selected. Use either the configuration file or the commandline option");
        exit(1);
    }
    let diaryname = diaryname.unwrap();

    let prevent_edit = rt.cli().subcommand_matches("create").unwrap().is_present("no-edit");

    let diary = Diary::open(rt.store(), &diaryname[..]);
    let res = diary.new_entry_today()
        .and_then(|mut entry| {
            if prevent_edit {
                debug!("Not editing new diary entry");
                Ok(())
            } else {
                debug!("Editing new diary entry");
                entry.edit_content(rt)
                    .map_err(|e| DE::new(DEK::DiaryEditError, Some(Box::new(e))))
            }
        });

    if let Err(e) = res {
        trace_error(&e);
    } else {
        info!("Ok!");
    }
}

fn list(rt: &Runtime) {
    let diaryname = get_diary_name(rt);
    if diaryname.is_none() {
        warn!("No diary selected. Use either the configuration file or the commandline option");
        exit(1);
    }
    let diaryname = diaryname.unwrap();

    fn location_to_listing_string(id: &StoreId, base: &PathBuf) -> String {
        id.strip_prefix(base)
            .map_err(|e| trace_error(&e))
            .ok()
            .and_then(|p| p.to_str().map(String::from))
            .unwrap_or(String::from("<<Path Parsing Error>>"))
    }

    let diary = Diary::open(rt.store(), &diaryname[..]);
    debug!("Diary opened: {:?}", diary);
    diary.entries()
        .and_then(|es| {
            debug!("Iterator for listing: {:?}", es);

            let es = es.filter_map(|a| {
                debug!("Filtering: {:?}", a);
                a.ok()
            }).map(|e| e.into());

            let base = rt.store().path();

            CoreLister::new(&move |e| location_to_listing_string(e.get_location(), base))
                .list(es) // TODO: Do not ignore non-ok()s
                .map_err(|e| DE::new(DEK::IOError, Some(Box::new(e))))
        })
        .map(|_| debug!("Ok"))
        .map_err(|e| trace_error(&e))
        .ok();
}

fn delete(rt: &Runtime) {
    unimplemented!()
}

fn edit(rt: &Runtime) {
    let diaryname = get_diary_name(rt);
    if diaryname.is_none() {
        warn!("No diary name");
        exit(1);
    }
    let diaryname = diaryname.unwrap();
    let diary = Diary::open(rt.store(), &diaryname[..]);

    let datetime : Option<NaiveDateTime> = rt
        .cli()
        .subcommand_matches("edit")
        .unwrap()
        .value_of("datetime")
        .and_then(DateTime::parse)
        .map(|dt| dt.into());

    let to_edit = match datetime {
        Some(dt) => Some(diary.retrieve(DiaryId::from_datetime(diaryname.clone(), dt))),
        None     => diary.get_youngest_entry(),
    };

    match to_edit {
        Some(Ok(mut e)) => e.edit_content(rt).map_err(|e| DE::new(DEK::IOError, Some(Box::new(e)))),

        Some(Err(e)) => Err(e),
        None => Err(DE::new(DEK::EntryNotInDiary, None)),
    }
    .map_err(|e| trace_error(&e)).ok();
}

fn diary(rt: &Runtime) {
    unimplemented!()
}

fn get_diary_name(rt: &Runtime) -> Option<String> {
    use libimagdiary::config::get_default_diary_name;

    get_default_diary_name(rt)
        .or(rt.cli().value_of("diaryname").map(String::from))
}
<commit_msg>Implement delete()<commit_after>#[macro_use] extern crate log;
#[macro_use] extern crate version;
extern crate clap;
extern crate chrono;

extern crate libimagdiary;
extern crate libimagentrylist;
extern crate libimaginteraction;
extern crate libimagrt;
extern crate libimagstore;
extern crate libimagutil;
extern crate libimagtimeui;
#[macro_use] extern crate libimagerror;

use std::path::PathBuf;
use std::process::exit;
use chrono::naive::datetime::NaiveDateTime;

use libimagdiary::diary::Diary;
use libimagdiary::diaryid::DiaryId;
use libimagdiary::error::DiaryError as DE;
use libimagdiary::error::DiaryErrorKind as DEK;
use libimagentrylist::listers::core::CoreLister;
use libimagentrylist::lister::Lister;
use libimagrt::edit::Edit;
use libimagrt::runtime::Runtime;
use libimagstore::storeid::StoreId;
use libimagerror::trace::trace_error;
use libimagtimeui::datetime::DateTime;
use libimagtimeui::parse::Parse;

mod ui;

use ui::build_ui;

fn main() {
    let name = "imag-diary";
    let version = &version!()[..];
    let about = "Personal Diary/Diaries";
    let ui = build_ui(Runtime::get_default_cli_builder(name, version, about));
    let rt = {
        let rt = Runtime::new(ui);
        if rt.is_ok() {
            rt.unwrap()
        } else {
            println!("Could not set up Runtime");
            println!("{:?}", rt.err().unwrap());
            exit(1);
        }
    };

    rt.cli()
        .subcommand_name()
        .map(|name| {
            debug!("Call {}", name);
            match name {
                "create" => create(&rt),
                "delete" => delete(&rt),
                "edit" => edit(&rt),
                "list" => list(&rt),
                "diary" => diary(&rt),
                _        => {
                    debug!("Unknown command"); // More error handling
                },
            }
        });
}

fn create(rt: &Runtime) {
    let diaryname = get_diary_name(rt);
    if diaryname.is_none() {
        warn!("No diary selected. Use either the configuration file or the commandline option");
        exit(1);
    }
    let diaryname = diaryname.unwrap();

    let prevent_edit = rt.cli().subcommand_matches("create").unwrap().is_present("no-edit");

    let diary = Diary::open(rt.store(), &diaryname[..]);
    let res = diary.new_entry_today()
        .and_then(|mut entry| {
            if prevent_edit {
                debug!("Not editing new diary entry");
                Ok(())
            } else {
                debug!("Editing new diary entry");
                entry.edit_content(rt)
                    .map_err(|e| DE::new(DEK::DiaryEditError, Some(Box::new(e))))
            }
        });

    if let Err(e) = res {
        trace_error(&e);
    } else {
        info!("Ok!");
    }
}

fn list(rt: &Runtime) {
    let diaryname = get_diary_name(rt);
    if diaryname.is_none() {
        warn!("No diary selected. Use either the configuration file or the commandline option");
        exit(1);
    }
    let diaryname = diaryname.unwrap();

    fn location_to_listing_string(id: &StoreId, base: &PathBuf) -> String {
        id.strip_prefix(base)
            .map_err(|e| trace_error(&e))
            .ok()
            .and_then(|p| p.to_str().map(String::from))
            .unwrap_or(String::from("<<Path Parsing Error>>"))
    }

    let diary = Diary::open(rt.store(), &diaryname[..]);
    debug!("Diary opened: {:?}", diary);
    diary.entries()
        .and_then(|es| {
            debug!("Iterator for listing: {:?}", es);

            let es = es.filter_map(|a| {
                debug!("Filtering: {:?}", a);
                a.ok()
            }).map(|e| e.into());

            let base = rt.store().path();

            CoreLister::new(&move |e| location_to_listing_string(e.get_location(), base))
                .list(es) // TODO: Do not ignore non-ok()s
                .map_err(|e| DE::new(DEK::IOError, Some(Box::new(e))))
        })
        .map(|_| debug!("Ok"))
        .map_err(|e| trace_error(&e))
        .ok();
}

fn delete(rt: &Runtime) {
    use libimaginteraction::ask::ask_bool;

    let diaryname = get_diary_name(rt);
    if diaryname.is_none() {
        warn!("No diary selected. Use either the configuration file or the commandline option");
        exit(1);
    }
    let diaryname = diaryname.unwrap();

    let diary = Diary::open(rt.store(), &diaryname[..]);
    debug!("Diary opened: {:?}", diary);

    let datetime : Option<NaiveDateTime> = rt
        .cli()
        .subcommand_matches("delete")
        .unwrap()
        .value_of("datetime")
        .map(|dt| { debug!("DateTime = {:?}", dt); dt })
        .and_then(DateTime::parse)
        .map(|dt| dt.into());

    let to_del = match datetime {
        Some(dt) => Some(diary.retrieve(DiaryId::from_datetime(diaryname.clone(), dt))),
        None     => diary.get_youngest_entry(),
    };

    let to_del = match to_del {
        Some(Ok(e)) => e,

        Some(Err(e)) => {
            trace_error(&e);
            exit(1);
        },
        None => {
            warn!("No entry");
            exit(1);
        },
    };

    if !ask_bool(&format!("Deleting {:?}", to_del.get_location())[..], Some(true)) {
        info!("Aborting delete action");
        return;
    }

    match diary.delete_entry(to_del) {
        Ok(_) => info!("Ok"),
        Err(e) => {
            trace_error(&e);
            exit(1);
        },
    }
}

fn edit(rt: &Runtime) {
    let diaryname = get_diary_name(rt);
    if diaryname.is_none() {
        warn!("No diary name");
        exit(1);
    }
    let diaryname = diaryname.unwrap();
    let diary = Diary::open(rt.store(), &diaryname[..]);

    let datetime : Option<NaiveDateTime> = rt
        .cli()
        .subcommand_matches("edit")
        .unwrap()
        .value_of("datetime")
        .and_then(DateTime::parse)
        .map(|dt| dt.into());

    let to_edit = match datetime {
        Some(dt) => Some(diary.retrieve(DiaryId::from_datetime(diaryname.clone(), dt))),
        None     => diary.get_youngest_entry(),
    };

    match to_edit {
        Some(Ok(mut e)) => e.edit_content(rt).map_err(|e| DE::new(DEK::IOError, Some(Box::new(e)))),

        Some(Err(e)) => Err(e),
        None => Err(DE::new(DEK::EntryNotInDiary, None)),
    }
    .map_err(|e| trace_error(&e)).ok();
}

fn diary(rt: &Runtime) {
    unimplemented!()
}

fn get_diary_name(rt: &Runtime) -> Option<String> {
    use libimagdiary::config::get_default_diary_name;

    get_default_diary_name(rt)
        .or(rt.cli().value_of("diaryname").map(String::from))
}
<|endoftext|>


<commit_before>use nom::{self, IResult};

use std::str;

/// list-wildcards = "%" / "*"
pub fn list_wildcards(c: u8) -> bool {
    c == b'%' || c == b'*'
}

/// quoted-specials = DQUOTE / "\"
pub fn quoted_specials(c: u8) -> bool {
    c == b'"' || c == b'\\'
}

/// resp-specials = "]"
pub fn resp_specials(c: u8) -> bool {
    c == b']'
}

/// atom-specials = "(" / ")" / "{" / SP / CTL / list-wildcards / quoted-specials / resp-specials
pub fn atom_specials(c: u8) -> bool {
    c == b'('
        || c == b')'
        || c == b'{'
        || c == b' '
        || c < 32
        || list_wildcards(c)
        || quoted_specials(c)
        || resp_specials(c)
}

/// ATOM-CHAR = <any CHAR except atom-specials>
pub fn atom_char(c: u8) -> bool {
    !atom_specials(c)
}

/// nil = "NIL"
named!(pub nil, tag_s!("NIL"));

/// ASTRING-CHAR = ATOM-CHAR / resp-specials
pub fn astring_char(c: u8) -> bool {
    atom_char(c) || resp_specials(c)
}

/// QUOTED-CHAR = <any TEXT-CHAR except quoted-specials> / "\" quoted-specials
pub fn quoted_data(i: &[u8]) -> IResult<&[u8], &[u8]> {
    // Ideally this should use nom's `escaped` macro, but it suffers from broken
    // type inference unless compiled with the verbose-errors feature enabled.
    let mut escape = false;
    let mut len = 0;
    for c in i {
        if *c == b'"' && !escape {
            break;
        }
        len += 1;
        if *c == b'\\' && !escape {
            escape = true
        } else if escape {
            escape = false;
        }
    }
    Ok((&i[len..], &i[..len]))
}

/// quoted = DQUOTE *QUOTED-CHAR DQUOTE
named!(pub quoted<&[u8]>, delimited!(
    char!('"'),
    quoted_data,
    char!('"')
));

/// quoted bytes as as utf8
named!(pub quoted_utf8<&str>, map_res!(quoted, str::from_utf8));

/// literal = "{" number "}" CRLF *CHAR8
///            ; Number represents the number of CHAR8s
named!(pub literal<&[u8]>, do_parse!(
    tag_s!("{") >>
    len: number >>
    tag_s!("}") >>
    tag_s!("\r\n") >>
    data: take!(len) >>
    (data)
));

/// string = quoted / literal
named!(pub string<&[u8]>, alt!(quoted | literal));

/// string bytes as as utf8
named!(pub string_utf8<&str>, map_res!(string, str::from_utf8));

/// nstring = string / nil
named!(pub nstring<Option<&[u8]>>, alt!(
    map!(nil, |_| None) |
    map!(string, |s| Some(s))
));

/// nstring bytes as utf8
named!(pub nstring_utf8<Option<&str>>, alt!(
    map!(nil, |_| None) |
    map!(string_utf8, |s| Some(s))
));

/// number          = 1*DIGIT
///                    ; Unsigned 32-bit integer
///                    ; (0 <= n < 4,294,967,296)
named!(pub number<u32>, flat_map!(nom::digit, parse_to!(u32)));

/// same as `number` but 64-bit
named!(pub number_64<u64>, flat_map!(nom::digit, parse_to!(u64)));

/// atom = 1*ATOM-CHAR
named!(pub atom<&str>, map_res!(take_while1_s!(atom_char),
    str::from_utf8
));

/// astring = 1*ASTRING-CHAR / string
named!(pub astring<&[u8]>, alt!(
    take_while1!(astring_char) |
    string
));

/// astring bytes as as utf8
named!(pub astring_utf8<&str>, map_res!(astring, str::from_utf8));

/// text = 1*TEXT-CHAR
named!(pub text<&str>, map_res!(take_while_s!(text_char),
    str::from_utf8
));

/// TEXT-CHAR = <any CHAR except CR and LF>
pub fn text_char(c: u8) -> bool {
    c != b'\r' && c != b'\n'
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_string_literal() {
        match string(b"{3}\r\nXYZ") {
            Ok((_, value)) => {
                assert_eq!(value, "XYZ".as_bytes());
            }
            rsp => panic!("unexpected response {:?}", rsp),
        }
    }

    #[test]
    fn test_astring() {
        match astring(b"text ") {
            Ok((_, value)) => {
                assert_eq!(value, "text".as_bytes());
            }
            rsp => panic!("unexpected response {:?}", rsp),
        }
    }
}
<commit_msg>Replace doc comments with normal comments<commit_after>use nom::{self, IResult};

use std::str;

// list-wildcards = "%" / "*"
pub fn list_wildcards(c: u8) -> bool {
    c == b'%' || c == b'*'
}

// quoted-specials = DQUOTE / "\"
pub fn quoted_specials(c: u8) -> bool {
    c == b'"' || c == b'\\'
}

// resp-specials = "]"
pub fn resp_specials(c: u8) -> bool {
    c == b']'
}

// atom-specials = "(" / ")" / "{" / SP / CTL / list-wildcards / quoted-specials / resp-specials
pub fn atom_specials(c: u8) -> bool {
    c == b'('
        || c == b')'
        || c == b'{'
        || c == b' '
        || c < 32
        || list_wildcards(c)
        || quoted_specials(c)
        || resp_specials(c)
}

// ATOM-CHAR = <any CHAR except atom-specials>
pub fn atom_char(c: u8) -> bool {
    !atom_specials(c)
}

// nil = "NIL"
named!(pub nil, tag_s!("NIL"));

// ASTRING-CHAR = ATOM-CHAR / resp-specials
pub fn astring_char(c: u8) -> bool {
    atom_char(c) || resp_specials(c)
}

// QUOTED-CHAR = <any TEXT-CHAR except quoted-specials> / "\" quoted-specials
pub fn quoted_data(i: &[u8]) -> IResult<&[u8], &[u8]> {
    // Ideally this should use nom's `escaped` macro, but it suffers from broken
    // type inference unless compiled with the verbose-errors feature enabled.
    let mut escape = false;
    let mut len = 0;
    for c in i {
        if *c == b'"' && !escape {
            break;
        }
        len += 1;
        if *c == b'\\' && !escape {
            escape = true
        } else if escape {
            escape = false;
        }
    }
    Ok((&i[len..], &i[..len]))
}

// quoted = DQUOTE *QUOTED-CHAR DQUOTE
named!(pub quoted<&[u8]>, delimited!(
    char!('"'),
    quoted_data,
    char!('"')
));

// quoted bytes as as utf8
named!(pub quoted_utf8<&str>, map_res!(quoted, str::from_utf8));

// literal = "{" number "}" CRLF *CHAR8
//            ; Number represents the number of CHAR8s
named!(pub literal<&[u8]>, do_parse!(
    tag_s!("{") >>
    len: number >>
    tag_s!("}") >>
    tag_s!("\r\n") >>
    data: take!(len) >>
    (data)
));

// string = quoted / literal
named!(pub string<&[u8]>, alt!(quoted | literal));

// string bytes as as utf8
named!(pub string_utf8<&str>, map_res!(string, str::from_utf8));

// nstring = string / nil
named!(pub nstring<Option<&[u8]>>, alt!(
    map!(nil, |_| None) |
    map!(string, |s| Some(s))
));

// nstring bytes as utf8
named!(pub nstring_utf8<Option<&str>>, alt!(
    map!(nil, |_| None) |
    map!(string_utf8, |s| Some(s))
));

// number          = 1*DIGIT
//                    ; Unsigned 32-bit integer
//                    ; (0 <= n < 4,294,967,296)
named!(pub number<u32>, flat_map!(nom::digit, parse_to!(u32)));

// same as `number` but 64-bit
named!(pub number_64<u64>, flat_map!(nom::digit, parse_to!(u64)));

// atom = 1*ATOM-CHAR
named!(pub atom<&str>, map_res!(take_while1_s!(atom_char),
    str::from_utf8
));

// astring = 1*ASTRING-CHAR / string
named!(pub astring<&[u8]>, alt!(
    take_while1!(astring_char) |
    string
));

// astring bytes as as utf8
named!(pub astring_utf8<&str>, map_res!(astring, str::from_utf8));

// text = 1*TEXT-CHAR
named!(pub text<&str>, map_res!(take_while_s!(text_char),
    str::from_utf8
));

// TEXT-CHAR = <any CHAR except CR and LF>
pub fn text_char(c: u8) -> bool {
    c != b'\r' && c != b'\n'
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_string_literal() {
        match string(b"{3}\r\nXYZ") {
            Ok((_, value)) => {
                assert_eq!(value, "XYZ".as_bytes());
            }
            rsp => panic!("unexpected response {:?}", rsp),
        }
    }

    #[test]
    fn test_astring() {
        match astring(b"text ") {
            Ok((_, value)) => {
                assert_eq!(value, "text".as_bytes());
            }
            rsp => panic!("unexpected response {:?}", rsp),
        }
    }
}
<|endoftext|>