<commit_before>#[cfg(feature = "parsing")]
use crate::buffer::Cursor;
use crate::thread::ThreadBound;
use proc_macro2::{
    Delimiter, Group, Ident, LexError, Literal, Punct, Spacing, Span, TokenStream, TokenTree,
};
#[cfg(feature = "printing")]
use quote::ToTokens;
use std::fmt::{self, Debug, Display};
use std::iter::FromIterator;
use std::slice;
use std::vec;

/// The result of a Syn parser.
pub type Result<T> = std::result::Result<T, Error>;

/// Error returned when a Syn parser cannot parse the input tokens.
///
/// # Error reporting in proc macros
///
/// The correct way to report errors back to the compiler from a procedural
/// macro is by emitting an appropriately spanned invocation of
/// [`compile_error!`] in the generated code. This produces a better diagnostic
/// message than simply panicking the macro.
///
/// [`compile_error!`]: std::compile_error!
///
/// When parsing macro input, the [`parse_macro_input!`] macro handles the
/// conversion to `compile_error!` automatically.
///
/// [`parse_macro_input!`]: crate::parse_macro_input!
///
/// ```
/// # extern crate proc_macro;
/// #
/// use proc_macro::TokenStream;
/// use syn::{parse_macro_input, AttributeArgs, ItemFn};
///
/// # const IGNORE: &str = stringify! {
/// #[proc_macro_attribute]
/// # };
/// pub fn my_attr(args: TokenStream, input: TokenStream) -> TokenStream {
///     let args = parse_macro_input!(args as AttributeArgs);
///     let input = parse_macro_input!(input as ItemFn);
///
///     /* ... */
///     # TokenStream::new()
/// }
/// ```
///
/// For errors that arise later than the initial parsing stage, the
/// [`.to_compile_error()`] or [`.into_compile_error()`] methods can be used to
/// perform an explicit conversion to `compile_error!`.
///
/// [`.to_compile_error()`]: Error::to_compile_error
/// [`.into_compile_error()`]: Error::into_compile_error
///
/// ```
/// # extern crate proc_macro;
/// #
/// # use proc_macro::TokenStream;
/// # use syn::{parse_macro_input, DeriveInput};
/// #
/// # const IGNORE: &str = stringify! {
/// #[proc_macro_derive(MyDerive)]
/// # };
/// pub fn my_derive(input: TokenStream) -> TokenStream {
///     let input = parse_macro_input!(input as DeriveInput);
///
///     // fn(DeriveInput) -> syn::Result<proc_macro2::TokenStream>
///     expand::my_derive(input)
///         .unwrap_or_else(syn::Error::into_compile_error)
///         .into()
/// }
/// #
/// # mod expand {
/// #     use proc_macro2::TokenStream;
/// #     use syn::{DeriveInput, Result};
/// #
/// #     pub fn my_derive(input: DeriveInput) -> Result<TokenStream> {
/// #         unimplemented!()
/// #     }
/// # }
/// ```
pub struct Error {
    messages: Vec<ErrorMessage>,
}

struct ErrorMessage {
    // Span is implemented as an index into a thread-local interner to keep the
    // size small. It is not safe to access from a different thread. We want
    // errors to be Send and Sync to play nicely with the Failure crate, so pin
    // the span we're given to its original thread and assume it is
    // Span::call_site if accessed from any other thread.
    start_span: ThreadBound<Span>,
    end_span: ThreadBound<Span>,
    message: String,
}

#[cfg(test)]
struct _Test
where
    Error: Send + Sync;

impl Error {
    /// Usually the [`ParseStream::error`] method will be used instead, which
    /// automatically uses the correct span from the current position of the
    /// parse stream.
    ///
    /// Use `Error::new` when the error needs to be triggered on some span other
    /// than where the parse stream is currently positioned.
    ///
    /// [`ParseStream::error`]: crate::parse::ParseBuffer::error
    ///
    /// # Example
    ///
    /// ```
    /// use syn::{Error, Ident, LitStr, Result, Token};
    /// use syn::parse::ParseStream;
    ///
    /// // Parses input that looks like `name = "string"` where the key must be
    /// // the identifier `name` and the value may be any string literal.
    /// // Returns the string literal.
    /// fn parse_name(input: ParseStream) -> Result<LitStr> {
    ///     let name_token: Ident = input.parse()?;
    ///     if name_token != "name" {
    ///         // Trigger an error not on the current position of the stream,
    ///         // but on the position of the unexpected identifier.
    ///         return Err(Error::new(name_token.span(), "expected `name`"));
    ///     }
    ///     input.parse::<Token![=]>()?;
    ///     let s: LitStr = input.parse()?;
    ///     Ok(s)
    /// }
    /// ```
    pub fn new<T: Display>(span: Span, message: T) -> Self {
        Error {
            messages: vec![ErrorMessage {
                start_span: ThreadBound::new(span),
                end_span: ThreadBound::new(span),
                message: message.to_string(),
            }],
        }
    }

    /// Creates an error with the specified message spanning the given syntax
    /// tree node.
    ///
    /// Unlike the `Error::new` constructor, this constructor takes an argument
    /// `tokens` which is a syntax tree node. This allows the resulting `Error`
    /// to attempt to span all tokens inside of `tokens`. While you would
    /// typically be able to use the `Spanned` trait with the above `Error::new`
    /// constructor, implementation limitations today mean that
    /// `Error::new_spanned` may provide a higher-quality error message on
    /// stable Rust.
    ///
    /// When in doubt it's recommended to stick to `Error::new` (or
    /// `ParseStream::error`)!
    #[cfg(feature = "printing")]
    pub fn new_spanned<T: ToTokens, U: Display>(tokens: T, message: U) -> Self {
        let mut iter = tokens.into_token_stream().into_iter();
        let start = iter.next().map_or_else(Span::call_site, |t| t.span());
        let end = iter.last().map_or(start, |t| t.span());
        Error {
            messages: vec![ErrorMessage {
                start_span: ThreadBound::new(start),
                end_span: ThreadBound::new(end),
                message: message.to_string(),
            }],
        }
    }

    /// The source location of the error.
    ///
    /// Spans are not thread-safe so this function returns `Span::call_site()`
    /// if called from a different thread than the one on which the `Error` was
    /// originally created.
    pub fn span(&self) -> Span {
        let start = match self.messages[0].start_span.get() {
            Some(span) => *span,
            None => return Span::call_site(),
        };
        let end = match self.messages[0].end_span.get() {
            Some(span) => *span,
            None => return Span::call_site(),
        };
        start.join(end).unwrap_or(start)
    }

    /// Render the error as an invocation of [`compile_error!`].
    ///
    /// The [`parse_macro_input!`] macro provides a convenient way to invoke
    /// this method correctly in a procedural macro.
    ///
    /// [`compile_error!`]: std::compile_error!
    /// [`parse_macro_input!`]: crate::parse_macro_input!
    pub fn to_compile_error(&self) -> TokenStream {
        self.messages
            .iter()
            .map(ErrorMessage::to_compile_error)
            .collect()
    }

    /// Render the error as an invocation of [`compile_error!`].
    ///
    /// [`compile_error!`]: std::compile_error!
    ///
    /// # Example
    ///
    /// ```
    /// # extern crate proc_macro;
    /// #
    /// use proc_macro::TokenStream;
    /// use syn::{parse_macro_input, DeriveInput, Error};
    ///
    /// # const _: &str = stringify! {
    /// #[proc_macro_derive(MyTrait)]
    /// # };
    /// pub fn derive_my_trait(input: TokenStream) -> TokenStream {
    ///     let input = parse_macro_input!(input as DeriveInput);
    ///     my_trait::expand(input)
    ///         .unwrap_or_else(Error::into_compile_error)
    ///         .into()
    /// }
    ///
    /// mod my_trait {
    ///     use proc_macro2::TokenStream;
    ///     use syn::{DeriveInput, Result};
    ///
    ///     pub(crate) fn expand(input: DeriveInput) -> Result<TokenStream> {
    ///         /* ... */
    ///         # unimplemented!()
    ///     }
    /// }
    /// ```
    pub fn into_compile_error(self) -> TokenStream {
        self.to_compile_error()
    }

    /// Add another error message to self such that when `to_compile_error()` is
    /// called, both errors will be emitted together.
    pub fn combine(&mut self, another: Error) {
        self.messages.extend(another.messages);
    }
}

impl ErrorMessage {
    fn to_compile_error(&self) -> TokenStream {
        let start = self
            .start_span
            .get()
            .cloned()
            .unwrap_or_else(Span::call_site);
        let end = self.end_span.get().cloned().unwrap_or_else(Span::call_site);

        // compile_error!($message)
        TokenStream::from_iter(vec![
            TokenTree::Ident(Ident::new("compile_error", start)),
            TokenTree::Punct({
                let mut punct = Punct::new('!', Spacing::Alone);
                punct.set_span(start);
                punct
            }),
            TokenTree::Group({
                let mut group = Group::new(Delimiter::Brace, {
                    TokenStream::from_iter(vec![TokenTree::Literal({
                        let mut string = Literal::string(&self.message);
                        string.set_span(end);
                        string
                    })])
                });
                group.set_span(end);
                group
            }),
        ])
    }
}

#[cfg(feature = "parsing")]
pub fn new_at<T: Display>(scope: Span, cursor: Cursor, message: T) -> Error {
    if cursor.eof() {
        Error::new(scope, format!("unexpected end of input, {}", message))
    } else {
        let span = crate::buffer::open_span_of_group(cursor);
        Error::new(span, message)
    }
}

#[cfg(all(feature = "parsing", any(feature = "full", feature = "derive")))]
pub fn new2<T: Display>(start: Span, end: Span, message: T) -> Error {
    Error {
        messages: vec![ErrorMessage {
            start_span: ThreadBound::new(start),
            end_span: ThreadBound::new(end),
            message: message.to_string(),
        }],
    }
}

impl Debug for Error {
    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        if self.messages.len() == 1 {
            formatter
                .debug_tuple("Error")
                .field(&self.messages[0])
                .finish()
        } else {
            formatter
                .debug_tuple("Error")
                .field(&self.messages)
                .finish()
        }
    }
}

impl Debug for ErrorMessage {
    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        Debug::fmt(&self.message, formatter)
    }
}

impl Display for Error {
    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        formatter.write_str(&self.messages[0].message)
    }
}

impl Clone for Error {
    fn clone(&self) -> Self {
        Error {
            messages: self.messages.clone(),
        }
    }
}

impl Clone for ErrorMessage {
    fn clone(&self) -> Self {
        let start = self
            .start_span
            .get()
            .cloned()
            .unwrap_or_else(Span::call_site);
        let end = self.end_span.get().cloned().unwrap_or_else(Span::call_site);
        ErrorMessage {
            start_span: ThreadBound::new(start),
            end_span: ThreadBound::new(end),
            message: self.message.clone(),
        }
    }
}

impl std::error::Error for Error {}

impl From<LexError> for Error {
    fn from(err: LexError) -> Self {
        Error::new(err.span(), "lex error")
    }
}

impl IntoIterator for Error {
    type Item = Error;
    type IntoIter = IntoIter;

    fn into_iter(self) -> Self::IntoIter {
        IntoIter {
            messages: self.messages.into_iter(),
        }
    }
}

pub struct IntoIter {
    messages: vec::IntoIter<ErrorMessage>,
}

impl Iterator for IntoIter {
    type Item = Error;

    fn next(&mut self) -> Option<Self::Item> {
        Some(Error {
            messages: vec![self.messages.next()?],
        })
    }
}

impl<'a> IntoIterator for &'a Error {
    type Item = Error;
    type IntoIter = Iter<'a>;

    fn into_iter(self) -> Self::IntoIter {
        Iter {
            messages: self.messages.iter(),
        }
    }
}

pub struct Iter<'a> {
    messages: slice::Iter<'a, ErrorMessage>,
}

impl<'a> Iterator for Iter<'a> {
    type Item = Error;

    fn next(&mut self) -> Option<Self::Item> {
        Some(Error {
            messages: vec![self.messages.next()?.clone()],
        })
    }
}

impl Extend<Error> for Error {
    fn extend<T: IntoIterator<Item = Error>>(&mut self, iter: T) {
        for err in iter {
            self.combine(err);
        }
    }
}
<commit_msg>Move code of Error functions to non-generic functions<commit_after>#[cfg(feature = "parsing")]
use crate::buffer::Cursor;
use crate::thread::ThreadBound;
use proc_macro2::{
    Delimiter, Group, Ident, LexError, Literal, Punct, Spacing, Span, TokenStream, TokenTree,
};
#[cfg(feature = "printing")]
use quote::ToTokens;
use std::fmt::{self, Debug, Display};
use std::iter::FromIterator;
use std::slice;
use std::vec;

/// The result of a Syn parser.
pub type Result<T> = std::result::Result<T, Error>;

/// Error returned when a Syn parser cannot parse the input tokens.
///
/// # Error reporting in proc macros
///
/// The correct way to report errors back to the compiler from a procedural
/// macro is by emitting an appropriately spanned invocation of
/// [`compile_error!`] in the generated code. This produces a better diagnostic
/// message than simply panicking the macro.
///
/// [`compile_error!`]: std::compile_error!
///
/// When parsing macro input, the [`parse_macro_input!`] macro handles the
/// conversion to `compile_error!` automatically.
///
/// [`parse_macro_input!`]: crate::parse_macro_input!
///
/// ```
/// # extern crate proc_macro;
/// #
/// use proc_macro::TokenStream;
/// use syn::{parse_macro_input, AttributeArgs, ItemFn};
///
/// # const IGNORE: &str = stringify! {
/// #[proc_macro_attribute]
/// # };
/// pub fn my_attr(args: TokenStream, input: TokenStream) -> TokenStream {
///     let args = parse_macro_input!(args as AttributeArgs);
///     let input = parse_macro_input!(input as ItemFn);
///
///     /* ... */
///     # TokenStream::new()
/// }
/// ```
///
/// For errors that arise later than the initial parsing stage, the
/// [`.to_compile_error()`] or [`.into_compile_error()`] methods can be used to
/// perform an explicit conversion to `compile_error!`.
///
/// [`.to_compile_error()`]: Error::to_compile_error
/// [`.into_compile_error()`]: Error::into_compile_error
///
/// ```
/// # extern crate proc_macro;
/// #
/// # use proc_macro::TokenStream;
/// # use syn::{parse_macro_input, DeriveInput};
/// #
/// # const IGNORE: &str = stringify! {
/// #[proc_macro_derive(MyDerive)]
/// # };
/// pub fn my_derive(input: TokenStream) -> TokenStream {
///     let input = parse_macro_input!(input as DeriveInput);
///
///     // fn(DeriveInput) -> syn::Result<proc_macro2::TokenStream>
///     expand::my_derive(input)
///         .unwrap_or_else(syn::Error::into_compile_error)
///         .into()
/// }
/// #
/// # mod expand {
/// #     use proc_macro2::TokenStream;
/// #     use syn::{DeriveInput, Result};
/// #
/// #     pub fn my_derive(input: DeriveInput) -> Result<TokenStream> {
/// #         unimplemented!()
/// #     }
/// # }
/// ```
pub struct Error {
    messages: Vec<ErrorMessage>,
}

struct ErrorMessage {
    // Span is implemented as an index into a thread-local interner to keep the
    // size small. It is not safe to access from a different thread. We want
    // errors to be Send and Sync to play nicely with the Failure crate, so pin
    // the span we're given to its original thread and assume it is
    // Span::call_site if accessed from any other thread.
    start_span: ThreadBound<Span>,
    end_span: ThreadBound<Span>,
    message: String,
}

#[cfg(test)]
struct _Test
where
    Error: Send + Sync;

impl Error {
    /// Usually the [`ParseStream::error`] method will be used instead, which
    /// automatically uses the correct span from the current position of the
    /// parse stream.
    ///
    /// Use `Error::new` when the error needs to be triggered on some span other
    /// than where the parse stream is currently positioned.
    ///
    /// [`ParseStream::error`]: crate::parse::ParseBuffer::error
    ///
    /// # Example
    ///
    /// ```
    /// use syn::{Error, Ident, LitStr, Result, Token};
    /// use syn::parse::ParseStream;
    ///
    /// // Parses input that looks like `name = "string"` where the key must be
    /// // the identifier `name` and the value may be any string literal.
    /// // Returns the string literal.
    /// fn parse_name(input: ParseStream) -> Result<LitStr> {
    ///     let name_token: Ident = input.parse()?;
    ///     if name_token != "name" {
    ///         // Trigger an error not on the current position of the stream,
    ///         // but on the position of the unexpected identifier.
    ///         return Err(Error::new(name_token.span(), "expected `name`"));
    ///     }
    ///     input.parse::<Token![=]>()?;
    ///     let s: LitStr = input.parse()?;
    ///     Ok(s)
    /// }
    /// ```
    pub fn new<T: Display>(span: Span, message: T) -> Self {
        return new(span, message.to_string());

        fn new(span: Span, message: String) -> Error {
            Error {
                messages: vec![ErrorMessage {
                    start_span: ThreadBound::new(span),
                    end_span: ThreadBound::new(span),
                    message,
                }],
            }
        }
    }

    /// Creates an error with the specified message spanning the given syntax
    /// tree node.
    ///
    /// Unlike the `Error::new` constructor, this constructor takes an argument
    /// `tokens` which is a syntax tree node. This allows the resulting `Error`
    /// to attempt to span all tokens inside of `tokens`. While you would
    /// typically be able to use the `Spanned` trait with the above `Error::new`
    /// constructor, implementation limitations today mean that
    /// `Error::new_spanned` may provide a higher-quality error message on
    /// stable Rust.
    ///
    /// When in doubt it's recommended to stick to `Error::new` (or
    /// `ParseStream::error`)!
    #[cfg(feature = "printing")]
    pub fn new_spanned<T: ToTokens, U: Display>(tokens: T, message: U) -> Self {
        return new_spanned(tokens.into_token_stream(), message.to_string());

        fn new_spanned(tokens: TokenStream, message: String) -> Error {
            let mut iter = tokens.into_iter();
            let start = iter.next().map_or_else(Span::call_site, |t| t.span());
            let end = iter.last().map_or(start, |t| t.span());
            Error {
                messages: vec![ErrorMessage {
                    start_span: ThreadBound::new(start),
                    end_span: ThreadBound::new(end),
                    message,
                }],
            }
        }
    }

    /// The source location of the error.
    ///
    /// Spans are not thread-safe so this function returns `Span::call_site()`
    /// if called from a different thread than the one on which the `Error` was
    /// originally created.
    pub fn span(&self) -> Span {
        let start = match self.messages[0].start_span.get() {
            Some(span) => *span,
            None => return Span::call_site(),
        };
        let end = match self.messages[0].end_span.get() {
            Some(span) => *span,
            None => return Span::call_site(),
        };
        start.join(end).unwrap_or(start)
    }

    /// Render the error as an invocation of [`compile_error!`].
    ///
    /// The [`parse_macro_input!`] macro provides a convenient way to invoke
    /// this method correctly in a procedural macro.
    ///
    /// [`compile_error!`]: std::compile_error!
    /// [`parse_macro_input!`]: crate::parse_macro_input!
    pub fn to_compile_error(&self) -> TokenStream {
        self.messages
            .iter()
            .map(ErrorMessage::to_compile_error)
            .collect()
    }

    /// Render the error as an invocation of [`compile_error!`].
    ///
    /// [`compile_error!`]: std::compile_error!
    ///
    /// # Example
    ///
    /// ```
    /// # extern crate proc_macro;
    /// #
    /// use proc_macro::TokenStream;
    /// use syn::{parse_macro_input, DeriveInput, Error};
    ///
    /// # const _: &str = stringify! {
    /// #[proc_macro_derive(MyTrait)]
    /// # };
    /// pub fn derive_my_trait(input: TokenStream) -> TokenStream {
    ///     let input = parse_macro_input!(input as DeriveInput);
    ///     my_trait::expand(input)
    ///         .unwrap_or_else(Error::into_compile_error)
    ///         .into()
    /// }
    ///
    /// mod my_trait {
    ///     use proc_macro2::TokenStream;
    ///     use syn::{DeriveInput, Result};
    ///
    ///     pub(crate) fn expand(input: DeriveInput) -> Result<TokenStream> {
    ///         /* ... */
    ///         # unimplemented!()
    ///     }
    /// }
    /// ```
    pub fn into_compile_error(self) -> TokenStream {
        self.to_compile_error()
    }

    /// Add another error message to self such that when `to_compile_error()` is
    /// called, both errors will be emitted together.
    pub fn combine(&mut self, another: Error) {
        self.messages.extend(another.messages);
    }
}

impl ErrorMessage {
    fn to_compile_error(&self) -> TokenStream {
        let start = self
            .start_span
            .get()
            .cloned()
            .unwrap_or_else(Span::call_site);
        let end = self.end_span.get().cloned().unwrap_or_else(Span::call_site);

        // compile_error!($message)
        TokenStream::from_iter(vec![
            TokenTree::Ident(Ident::new("compile_error", start)),
            TokenTree::Punct({
                let mut punct = Punct::new('!', Spacing::Alone);
                punct.set_span(start);
                punct
            }),
            TokenTree::Group({
                let mut group = Group::new(Delimiter::Brace, {
                    TokenStream::from_iter(vec![TokenTree::Literal({
                        let mut string = Literal::string(&self.message);
                        string.set_span(end);
                        string
                    })])
                });
                group.set_span(end);
                group
            }),
        ])
    }
}

#[cfg(feature = "parsing")]
pub fn new_at<T: Display>(scope: Span, cursor: Cursor, message: T) -> Error {
    if cursor.eof() {
        Error::new(scope, format!("unexpected end of input, {}", message))
    } else {
        let span = crate::buffer::open_span_of_group(cursor);
        Error::new(span, message)
    }
}

#[cfg(all(feature = "parsing", any(feature = "full", feature = "derive")))]
pub fn new2<T: Display>(start: Span, end: Span, message: T) -> Error {
    return new2(start, end, message.to_string());

    fn new2(start: Span, end: Span, message: String) -> Error {
        Error {
            messages: vec![ErrorMessage {
                start_span: ThreadBound::new(start),
                end_span: ThreadBound::new(end),
                message,
            }],
        }
    }
}

impl Debug for Error {
    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        if self.messages.len() == 1 {
            formatter
                .debug_tuple("Error")
                .field(&self.messages[0])
                .finish()
        } else {
            formatter
                .debug_tuple("Error")
                .field(&self.messages)
                .finish()
        }
    }
}

impl Debug for ErrorMessage {
    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        Debug::fmt(&self.message, formatter)
    }
}

impl Display for Error {
    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        formatter.write_str(&self.messages[0].message)
    }
}

impl Clone for Error {
    fn clone(&self) -> Self {
        Error {
            messages: self.messages.clone(),
        }
    }
}

impl Clone for ErrorMessage {
    fn clone(&self) -> Self {
        let start = self
            .start_span
            .get()
            .cloned()
            .unwrap_or_else(Span::call_site);
        let end = self.end_span.get().cloned().unwrap_or_else(Span::call_site);
        ErrorMessage {
            start_span: ThreadBound::new(start),
            end_span: ThreadBound::new(end),
            message: self.message.clone(),
        }
    }
}

impl std::error::Error for Error {}

impl From<LexError> for Error {
    fn from(err: LexError) -> Self {
        Error::new(err.span(), "lex error")
    }
}

impl IntoIterator for Error {
    type Item = Error;
    type IntoIter = IntoIter;

    fn into_iter(self) -> Self::IntoIter {
        IntoIter {
            messages: self.messages.into_iter(),
        }
    }
}

pub struct IntoIter {
    messages: vec::IntoIter<ErrorMessage>,
}

impl Iterator for IntoIter {
    type Item = Error;

    fn next(&mut self) -> Option<Self::Item> {
        Some(Error {
            messages: vec![self.messages.next()?],
        })
    }
}

impl<'a> IntoIterator for &'a Error {
    type Item = Error;
    type IntoIter = Iter<'a>;

    fn into_iter(self) -> Self::IntoIter {
        Iter {
            messages: self.messages.iter(),
        }
    }
}

pub struct Iter<'a> {
    messages: slice::Iter<'a, ErrorMessage>,
}

impl<'a> Iterator for Iter<'a> {
    type Item = Error;

    fn next(&mut self) -> Option<Self::Item> {
        Some(Error {
            messages: vec![self.messages.next()?.clone()],
        })
    }
}

impl Extend<Error> for Error {
    fn extend<T: IntoIterator<Item = Error>>(&mut self, iter: T) {
        for err in iter {
            self.combine(err);
        }
    }
}
<|endoftext|>


<commit_before>extern crate clap;
extern crate libc;

use std::any::Any;
use std::env;
use std::ffi::OsString;

use catfs::flags::DiskSpace;

pub struct Flag<'a, 'b> {
    pub arg: clap::Arg<'a, 'a>,
    pub value: &'b mut Any,
}

pub fn parse_options<'a, 'b>(mut app: clap::App<'a, 'a>, flags: &'b mut [Flag<'a, 'b>]) {
    for f in flags.iter() {
        app = app.arg(f.arg.clone());
    }
    let mut argv = env::args_os().collect::<Vec<OsString>>();
    if argv.len() == 5 && argv[3] == OsString::from("-o") {
	// looks like it's coming from fstab!
        // [0]: catfs, [1] = src_dir#cache_dir, [2] = mnt
        // [3]: -o, [4] = opt1,opt2
        // XXX str has split but OsString doesn't

        // XXX2 this is more convoluted than necessary because paths
        // immutably borrows from argv and we can't modify elements of
        // argv again because that requires another mutable borrow

        // XXX3 need to initialize src/cache because the compiler is
        // not smart enough
        let mut src: OsString = Default::default();
        let mut cache: OsString = Default::default();
        let mut is_fstab = false;

        {
            let paths = argv[1].to_str().unwrap().splitn(2, '#').collect::<Vec<&str>>();
            if paths.len() == 2 {
                src = OsString::from(paths[0]);
                cache = OsString::from(paths[1]);
                is_fstab = true;
            }
        }

        if is_fstab {
            argv[1] = src;
            argv.insert(2, cache);
        }
    }

    let matches = app.get_matches_from(argv);

    for f in flags.iter_mut() {
        let name = f.arg.b.name;

        if matches.is_present(name) {
            // cannot use else if here or rust would claim double
            // mutable borrow because apparently a borrow sends with
            // the last else if
            if let Some(v) = f.value.downcast_mut::<String>() {
                let s = matches.value_of(name).unwrap();
                *v = String::from(s);
                continue;
            }
            if let Some(v) = f.value.downcast_mut::<OsString>() {
                let s = matches.value_of_os(name).unwrap();
                *v = s.to_os_string();
                continue;
            }
            if let Some(v) = f.value.downcast_mut::<bool>() {
                *v = true;
                continue;
            }
            if let Some(v) = f.value.downcast_mut::<Vec<OsString>>() {
                let options = matches.values_of(name).unwrap();
                for s in options {
                    for s in s.split(',') {
                        v.push(OsString::from("-o"));
                        v.push(OsString::from(s));
                    }
                }
                continue;
            }
            if let Some(v) = f.value.downcast_mut::<DiskSpace>() {
                let s = matches.value_of(name).unwrap();
                *v = s.parse().unwrap();
                continue;
            }
            if let Some(v) = f.value.downcast_mut::<libc::uid_t>() {
                let s = matches.value_of(name).unwrap();
                *v = s.parse().unwrap();
                continue;
            }
            if let Some(v) = f.value.downcast_mut::<libc::gid_t>() {
                let s = matches.value_of(name).unwrap();
                *v = s.parse().unwrap();
                continue;
            }

            panic!("unknown type for {}", name);
        }
    }
}
<commit_msg>turn options that start with '-' into normal cmd line args<commit_after>extern crate clap;
extern crate libc;

use std::any::Any;
use std::env;
use std::ffi::OsString;

use catfs::flags::DiskSpace;

pub struct Flag<'a, 'b> {
    pub arg: clap::Arg<'a, 'a>,
    pub value: &'b mut Any,
}

pub fn parse_options<'a, 'b>(mut app: clap::App<'a, 'a>, flags: &'b mut [Flag<'a, 'b>]) {
    for f in flags.iter() {
        app = app.arg(f.arg.clone());
    }
    let mut argv = env::args_os().collect::<Vec<OsString>>();
    if argv.len() == 5 && argv[3] == OsString::from("-o") {
	// looks like it's coming from fstab!
        // [0]: catfs, [1] = src_dir#cache_dir, [2] = mnt
        // [3]: -o, [4] = opt1,opt2
        // XXX str has split but OsString doesn't

        // XXX2 this is more convoluted than necessary because paths
        // immutably borrows from argv and we can't modify elements of
        // argv again because that requires another mutable borrow

        // XXX3 need to initialize src/cache because the compiler is
        // not smart enough
        let mut src: OsString = Default::default();
        let mut cache: OsString = Default::default();
        let mut is_fstab = false;

        {
            let paths = argv[1].to_str().unwrap().splitn(2, '#').collect::<Vec<&str>>();
            if paths.len() == 2 {
                src = OsString::from(paths[0]);
                cache = OsString::from(paths[1]);
                is_fstab = true;
            }
        }

        if is_fstab {
            argv[1] = src;
            argv.insert(2, cache);
            let mut options = String::new();
            let mut arguments: Vec<OsString> = Default::default();

            // options are now pushed down
            for opt in argv[5].to_str().unwrap().split(',') {
                if opt.starts_with("-") {
                    arguments.push(OsString::from(opt));
                } else {
                    options += opt;
                    options += ",";
                }
            }

            if options.len() != 0 {
                options.pop();
                argv[5] = OsString::from(options);
            } else {
                // no options, pop -o and empty string
                argv.pop();
                argv.pop();
            }
            for arg in arguments {
                argv.insert(3, arg);
            }
        }
    }

    let matches = app.get_matches_from(argv);

    for f in flags.iter_mut() {
        let name = f.arg.b.name;

        if matches.is_present(name) {
            // cannot use else if here or rust would claim double
            // mutable borrow because apparently a borrow sends with
            // the last else if
            if let Some(v) = f.value.downcast_mut::<String>() {
                let s = matches.value_of(name).unwrap();
                *v = String::from(s);
                continue;
            }
            if let Some(v) = f.value.downcast_mut::<OsString>() {
                let s = matches.value_of_os(name).unwrap();
                *v = s.to_os_string();
                continue;
            }
            if let Some(v) = f.value.downcast_mut::<bool>() {
                *v = true;
                continue;
            }
            if let Some(v) = f.value.downcast_mut::<Vec<OsString>>() {
                let options = matches.values_of(name).unwrap();
                for s in options {
                    for s in s.split(',') {
                        v.push(OsString::from("-o"));
                        v.push(OsString::from(s));
                    }
                }
                continue;
            }
            if let Some(v) = f.value.downcast_mut::<DiskSpace>() {
                let s = matches.value_of(name).unwrap();
                *v = s.parse().unwrap();
                continue;
            }
            if let Some(v) = f.value.downcast_mut::<libc::uid_t>() {
                let s = matches.value_of(name).unwrap();
                *v = s.parse().unwrap();
                continue;
            }
            if let Some(v) = f.value.downcast_mut::<libc::gid_t>() {
                let s = matches.value_of(name).unwrap();
                *v = s.parse().unwrap();
                continue;
            }

            panic!("unknown type for {}", name);
        }
    }
}
<|endoftext|>


<commit_before>use std::cmp::PartialEq;
use std::fmt;
use std::borrow::Cow;

use quoted_string::{self, ContentChars, AsciiCaseInsensitiveEq};

/// a `Value` usable for a charset parameter.
///
/// # Example
/// ```
/// let mime = mime::TEXT_PLAIN_UTF_8;
/// assert_eq!(mime.param(mime::CHARSET), Some(mime::UTF_8));
/// ```
pub const UTF_8: Value = Value {
    source: "utf-8",
    ascii_case_insensitive: true,
};

/// A parameter value section of a `MediaType` or `MediaRange`.
///
/// Except for the `charset` parameter, parameters
/// are compared case sensitive
#[derive(Clone, Copy, Eq, PartialOrd, Ord, Hash)]
pub struct Value<'a> {
    source: &'a str,
    ascii_case_insensitive: bool
}

impl<'a> Value<'a> {
    #[inline]
    pub(crate) fn new(source: &'a str) -> Self {
        Value {
            source,
            ascii_case_insensitive: false,
        }
    }

    #[inline]
    pub(crate) fn for_name(self, name: &str) -> Self {
        Value {
            source: self.source,
            ascii_case_insensitive: name == crate::CHARSET,
        }
    }

    /// Returns the underlying representation.
    ///
    /// The underlying representation differs from the content,
    /// as it can contain quotes surrounding the content and
    /// quoted-pairs, even if non of them are necessary to
    /// represent the content.
    ///
    /// For example the representation `r#""a\"\ b""#` corresponds
    /// to the content `r#""a" b"#`. Another semantically  equivalent
    /// (i.e. with the same content) representation  is `r#""a\" b""`
    ///
    /// # Example
    ///
    /// ```
    /// let mime = r#"text/plain; param="abc def""#.parse::<mime::MediaType>().unwrap();
    /// let param = mime.param("param").unwrap();
    /// assert_eq!(param.as_str_repr(), r#""abc def""#);
    /// ```
    pub fn as_str_repr(&self) -> &'a str {
        self.source
    }

    /// Returns the content of this instance.
    ///
    /// It differs to the representation in that it will remove the
    /// quotation marks from the quoted string and will "unquote"
    /// quoted pairs.
    ///
    /// If the underlying representation is a quoted string containing
    /// quoted-pairs `Cow::Owned` is returned.
    ///
    /// If the underlying representation is a quoted-string without
    /// quoted-pairs `Cow::Borrowed` is returned as normal
    /// str slicing can be used to strip the surrounding double quoted.
    ///
    /// If the underlying representation is not a quoted-string
    /// `Cow::Borrowed` is returned, too.
    ///
    /// # Example
    ///
    /// ```
    /// let raw_mime = r#"text/plain; p1="char is \""; p2="simple"; p3=simple2"#;
    /// let mime = raw_mime.parse::<mime::MediaType>().unwrap();
    ///
    /// let param1 = mime.param("p1").unwrap();
    /// let expected = r#"char is ""#;
    /// assert_eq!(param1.to_content(), expected);
    ///
    /// let param2 = mime.param("p2").unwrap();
    /// assert_eq!(param2.to_content(), "simple");
    ///
    /// let param3 = mime.param("p3").unwrap();
    /// assert_eq!(param3.to_content(), "simple2");
    /// ```
    ///
    pub fn to_content(&self) -> Cow<'a, str> {
        quoted_string::unquote_unchecked(self.source)
    }

}

impl<'a, 'b> PartialEq<Value<'b>> for Value<'a> {
    #[inline]
    fn eq(&self, other: &Value<'b>) -> bool {
        let left_content_chars = ContentChars::from_string_unchecked(self.source);
        let right_content_chars = ContentChars::from_string_unchecked(other.source);

        if self.ascii_case_insensitive || other.ascii_case_insensitive {
            left_content_chars.eq_ignore_ascii_case(&right_content_chars)
        } else {
            left_content_chars == right_content_chars
        }
    }
}

impl<'a> PartialEq<str> for Value<'a> {
    fn eq(&self, other: &str) -> bool {
        if self.source.starts_with('"') {
            let content_chars = ContentChars::from_string_unchecked(self.source);
            if self.ascii_case_insensitive {
                content_chars.eq_ignore_ascii_case(other)
            } else {
                content_chars == other
            }
        } else if self.ascii_case_insensitive {
            self.source.eq_ignore_ascii_case(other)
        } else {
            self.source == other
        }
    }
}

impl<'a, 'b> PartialEq<&'b str> for Value<'a> {
    #[inline]
    fn eq(&self, other: & &'b str) -> bool {
        self == *other
    }
}

impl<'a, 'b> PartialEq<Value<'b>> for &'a str {
    #[inline]
    fn eq(&self, other: &Value<'b>) -> bool {
        other == self
    }
}

impl<'a> PartialEq<Value<'a>> for str {
    #[inline]
    fn eq(&self, other: &Value<'a>) -> bool {
        other == self
    }
}

impl<'a> From<Value<'a>> for Cow<'a, str> {
    #[inline]
    fn from(value: Value<'a>) -> Self {
        value.to_content()
    }
}

impl<'a> fmt::Debug for Value<'a> {
    #[inline]
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Debug::fmt(self.source, f)
    }
}

impl<'a> fmt::Display for Value<'a> {
    #[inline]
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Display::fmt(self.source, f)
    }
}

#[cfg(test)]
mod test {
    use std::borrow::Cow;
    use std::cmp::PartialEq;
    use std::fmt::Debug;

    use super::Value;

    fn bidi_eq<A: Debug+PartialEq<B>, B: Debug+PartialEq<A>>(left: A, right: B) {
        assert_eq!(left, right);
        assert_eq!(right, left);
    }

    fn bidi_ne<A: Debug+PartialEq<B>, B: Debug+PartialEq<A>>(left: A, right: B) {
        assert_ne!(left, right);
        assert_ne!(right, left);
    }

    #[test]
    fn test_value_eq_str() {
        let value = Value {
            source: "abc",
            ascii_case_insensitive: false
        };
        let value_quoted = Value {
            source: "\"abc\"",
            ascii_case_insensitive: false
        };
        let value_quoted_with_esacpes = Value {
            source: "\"a\\bc\"",
            ascii_case_insensitive: false
        };

        bidi_eq(value, "abc");
        bidi_ne(value, "\"abc\"");
        bidi_ne(value, "\"a\\bc\"");

        bidi_eq(value_quoted, "abc");
        bidi_ne(value_quoted, "\"abc\"");
        bidi_ne(value_quoted, "\"a\\bc\"");

        bidi_eq(value_quoted_with_esacpes, "abc");
        bidi_ne(value_quoted_with_esacpes, "\"abc\"");
        bidi_ne(value_quoted_with_esacpes, "\"a\\bc\"");

        assert_ne!(value, "aBc");
        assert_ne!(value_quoted, "aBc");
        assert_ne!(value_quoted_with_esacpes, "aBc");
    }

    #[test]
    fn test_value_eq_str_ascii_case_insensitive() {
        let value = Value {
            source: "abc",
            ascii_case_insensitive: true
        };
        let value_quoted = Value {
            source: "\"abc\"",
            ascii_case_insensitive: true
        };
        let value_quoted_with_esacpes = Value {
            source: "\"a\\bc\"",
            ascii_case_insensitive: true
        };

        //1st. all case sensitive checks which still apply
        bidi_eq(value, "abc");
        bidi_ne(value, "\"abc\"");
        bidi_ne(value, "\"a\\bc\"");

        bidi_eq(value_quoted, "abc");
        bidi_ne(value_quoted, "\"abc\"");
        bidi_ne(value_quoted, "\"a\\bc\"");

        bidi_eq(value_quoted_with_esacpes, "abc");
        bidi_ne(value_quoted_with_esacpes, "\"abc\"");
        bidi_ne(value_quoted_with_esacpes, "\"a\\bc\"");

        //2nd the case insensitive check
        bidi_eq(value, "aBc");
        bidi_ne(value, "\"aBc\"");
        bidi_ne(value, "\"a\\Bc\"");

        bidi_eq(value_quoted, "aBc");
        bidi_ne(value_quoted, "\"aBc\"");
        bidi_ne(value_quoted, "\"a\\Bc\"");

        bidi_eq(value_quoted_with_esacpes, "aBc");
        bidi_ne(value_quoted_with_esacpes, "\"aBc\"");
        bidi_ne(value_quoted_with_esacpes, "\"a\\Bc\"");
    }

    #[test]
    fn test_value_eq_value() {
        let value = Value {
            source: "abc",
            ascii_case_insensitive: false
        };
        let value_quoted = Value {
            source: "\"abc\"",
            ascii_case_insensitive: false
        };
        let value_quoted_with_esacpes = Value {
            source: "\"a\\bc\"",
            ascii_case_insensitive: false
        };
        assert_eq!(value, value);
        assert_eq!(value_quoted, value_quoted);
        assert_eq!(value_quoted_with_esacpes, value_quoted_with_esacpes);

        bidi_eq(value, value_quoted);
        bidi_eq(value, value_quoted_with_esacpes);
        bidi_eq(value_quoted, value_quoted_with_esacpes);
    }

    #[test]
    fn test_value_eq_value_case_insensitive() {
        let value = Value {
            source: "Abc",
            ascii_case_insensitive: true
        };
        let value_quoted = Value {
            source: "\"aBc\"",
            ascii_case_insensitive: true
        };
        let value_quoted_with_esacpes = Value {
            source: "\"a\\bC\"",
            ascii_case_insensitive: true
        };
        assert_eq!(value, value);
        assert_eq!(value_quoted, value_quoted);
        assert_eq!(value_quoted_with_esacpes, value_quoted_with_esacpes);

        bidi_eq(value, value_quoted);
        bidi_eq(value, value_quoted_with_esacpes);
        bidi_eq(value_quoted, value_quoted_with_esacpes);
    }

    #[test]
    fn test_value_eq_value_mixed_case_sensitivity() {
        let value = Value {
            source: "Abc",
            ascii_case_insensitive: true
        };
        let value_quoted = Value {
            source: "\"aBc\"",
            ascii_case_insensitive: false
        };
        let value_quoted_with_esacpes = Value {
            source: "\"a\\bC\"",
            ascii_case_insensitive: false
        };

        bidi_eq(value, value_quoted);
        bidi_eq(value, value_quoted_with_esacpes);

        //both are ascii case insensitive
        bidi_ne(value_quoted, value_quoted_with_esacpes);
    }

    #[test]
    fn test_as_str_repr() {
        let value = Value::new("\"ab cd\"");
        assert_eq!(value, "ab cd");
        assert_eq!(value.as_str_repr(), "\"ab cd\"");
    }

    #[test]
    fn test_to_content_not_quoted() {
        let value = Value::new("abc");
        assert_eq!(value.to_content(), Cow::Borrowed("abc"));
    }

    #[test]
    fn test_to_content_quoted_simple() {
        let value = Value::new("\"ab cd\"");
        assert_eq!(value.to_content(), Cow::Borrowed("ab cd"));
    }

    #[test]
    fn test_to_content_with_quoted_pair() {
        let value = Value::new("\"ab\\\"cd\"");
        assert_eq!(value, "ab\"cd");
        let expected: Cow<'static, str> = Cow::Owned("ab\"cd".into());
        assert_eq!(value.to_content(), expected);
    }

}
<commit_msg>remove Eq, Ord, and Hash from Value<commit_after>use std::cmp::PartialEq;
use std::fmt;
use std::borrow::Cow;

use quoted_string::{self, ContentChars, AsciiCaseInsensitiveEq};

/// a `Value` usable for a charset parameter.
///
/// # Example
/// ```
/// let mime = mime::TEXT_PLAIN_UTF_8;
/// assert_eq!(mime.param(mime::CHARSET), Some(mime::UTF_8));
/// ```
pub const UTF_8: Value = Value {
    source: "utf-8",
    ascii_case_insensitive: true,
};

/// A parameter value section of a `MediaType` or `MediaRange`.
///
/// Except for the `charset` parameter, parameters are compared case-sensitive.
#[derive(Clone, Copy)]
pub struct Value<'a> {
    source: &'a str,
    ascii_case_insensitive: bool
}

impl<'a> Value<'a> {
    #[inline]
    pub(crate) fn new(source: &'a str) -> Self {
        Value {
            source,
            ascii_case_insensitive: false,
        }
    }

    #[inline]
    pub(crate) fn for_name(self, name: &str) -> Self {
        Value {
            source: self.source,
            ascii_case_insensitive: name == crate::CHARSET,
        }
    }

    /// Returns the underlying representation.
    ///
    /// The underlying representation differs from the content,
    /// as it can contain quotes surrounding the content and
    /// quoted-pairs, even if non of them are necessary to
    /// represent the content.
    ///
    /// For example the representation `r#""a\"\ b""#` corresponds
    /// to the content `r#""a" b"#`. Another semantically  equivalent
    /// (i.e. with the same content) representation  is `r#""a\" b""`
    ///
    /// # Example
    ///
    /// ```
    /// let mime = r#"text/plain; param="abc def""#.parse::<mime::MediaType>().unwrap();
    /// let param = mime.param("param").unwrap();
    /// assert_eq!(param.as_str_repr(), r#""abc def""#);
    /// ```
    pub fn as_str_repr(&self) -> &'a str {
        self.source
    }

    /// Returns the content of this instance.
    ///
    /// It differs to the representation in that it will remove the
    /// quotation marks from the quoted string and will "unquote"
    /// quoted pairs.
    ///
    /// If the underlying representation is a quoted string containing
    /// quoted-pairs `Cow::Owned` is returned.
    ///
    /// If the underlying representation is a quoted-string without
    /// quoted-pairs `Cow::Borrowed` is returned as normal
    /// str slicing can be used to strip the surrounding double quoted.
    ///
    /// If the underlying representation is not a quoted-string
    /// `Cow::Borrowed` is returned, too.
    ///
    /// # Example
    ///
    /// ```
    /// let raw_mime = r#"text/plain; p1="char is \""; p2="simple"; p3=simple2"#;
    /// let mime = raw_mime.parse::<mime::MediaType>().unwrap();
    ///
    /// let param1 = mime.param("p1").unwrap();
    /// let expected = r#"char is ""#;
    /// assert_eq!(param1.to_content(), expected);
    ///
    /// let param2 = mime.param("p2").unwrap();
    /// assert_eq!(param2.to_content(), "simple");
    ///
    /// let param3 = mime.param("p3").unwrap();
    /// assert_eq!(param3.to_content(), "simple2");
    /// ```
    ///
    pub fn to_content(&self) -> Cow<'a, str> {
        quoted_string::unquote_unchecked(self.source)
    }

}

impl<'a, 'b> PartialEq<Value<'b>> for Value<'a> {
    #[inline]
    fn eq(&self, other: &Value<'b>) -> bool {
        let left_content_chars = ContentChars::from_string_unchecked(self.source);
        let right_content_chars = ContentChars::from_string_unchecked(other.source);

        if self.ascii_case_insensitive || other.ascii_case_insensitive {
            left_content_chars.eq_ignore_ascii_case(&right_content_chars)
        } else {
            left_content_chars == right_content_chars
        }
    }
}

impl<'a> PartialEq<str> for Value<'a> {
    fn eq(&self, other: &str) -> bool {
        if self.source.starts_with('"') {
            let content_chars = ContentChars::from_string_unchecked(self.source);
            if self.ascii_case_insensitive {
                content_chars.eq_ignore_ascii_case(other)
            } else {
                content_chars == other
            }
        } else if self.ascii_case_insensitive {
            self.source.eq_ignore_ascii_case(other)
        } else {
            self.source == other
        }
    }
}

impl<'a, 'b> PartialEq<&'b str> for Value<'a> {
    #[inline]
    fn eq(&self, other: & &'b str) -> bool {
        self == *other
    }
}

impl<'a, 'b> PartialEq<Value<'b>> for &'a str {
    #[inline]
    fn eq(&self, other: &Value<'b>) -> bool {
        other == self
    }
}

impl<'a> PartialEq<Value<'a>> for str {
    #[inline]
    fn eq(&self, other: &Value<'a>) -> bool {
        other == self
    }
}

impl<'a> From<Value<'a>> for Cow<'a, str> {
    #[inline]
    fn from(value: Value<'a>) -> Self {
        value.to_content()
    }
}

impl<'a> fmt::Debug for Value<'a> {
    #[inline]
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Debug::fmt(self.source, f)
    }
}

impl<'a> fmt::Display for Value<'a> {
    #[inline]
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Display::fmt(self.source, f)
    }
}

#[cfg(test)]
mod test {
    use std::borrow::Cow;
    use std::cmp::PartialEq;
    use std::fmt::Debug;

    use super::Value;

    fn bidi_eq<A: Debug+PartialEq<B>, B: Debug+PartialEq<A>>(left: A, right: B) {
        assert_eq!(left, right);
        assert_eq!(right, left);
    }

    fn bidi_ne<A: Debug+PartialEq<B>, B: Debug+PartialEq<A>>(left: A, right: B) {
        assert_ne!(left, right);
        assert_ne!(right, left);
    }

    #[test]
    fn test_value_eq_str() {
        let value = Value {
            source: "abc",
            ascii_case_insensitive: false
        };
        let value_quoted = Value {
            source: "\"abc\"",
            ascii_case_insensitive: false
        };
        let value_quoted_with_esacpes = Value {
            source: "\"a\\bc\"",
            ascii_case_insensitive: false
        };

        bidi_eq(value, "abc");
        bidi_ne(value, "\"abc\"");
        bidi_ne(value, "\"a\\bc\"");

        bidi_eq(value_quoted, "abc");
        bidi_ne(value_quoted, "\"abc\"");
        bidi_ne(value_quoted, "\"a\\bc\"");

        bidi_eq(value_quoted_with_esacpes, "abc");
        bidi_ne(value_quoted_with_esacpes, "\"abc\"");
        bidi_ne(value_quoted_with_esacpes, "\"a\\bc\"");

        assert_ne!(value, "aBc");
        assert_ne!(value_quoted, "aBc");
        assert_ne!(value_quoted_with_esacpes, "aBc");
    }

    #[test]
    fn test_value_eq_str_ascii_case_insensitive() {
        let value = Value {
            source: "abc",
            ascii_case_insensitive: true
        };
        let value_quoted = Value {
            source: "\"abc\"",
            ascii_case_insensitive: true
        };
        let value_quoted_with_esacpes = Value {
            source: "\"a\\bc\"",
            ascii_case_insensitive: true
        };

        //1st. all case sensitive checks which still apply
        bidi_eq(value, "abc");
        bidi_ne(value, "\"abc\"");
        bidi_ne(value, "\"a\\bc\"");

        bidi_eq(value_quoted, "abc");
        bidi_ne(value_quoted, "\"abc\"");
        bidi_ne(value_quoted, "\"a\\bc\"");

        bidi_eq(value_quoted_with_esacpes, "abc");
        bidi_ne(value_quoted_with_esacpes, "\"abc\"");
        bidi_ne(value_quoted_with_esacpes, "\"a\\bc\"");

        //2nd the case insensitive check
        bidi_eq(value, "aBc");
        bidi_ne(value, "\"aBc\"");
        bidi_ne(value, "\"a\\Bc\"");

        bidi_eq(value_quoted, "aBc");
        bidi_ne(value_quoted, "\"aBc\"");
        bidi_ne(value_quoted, "\"a\\Bc\"");

        bidi_eq(value_quoted_with_esacpes, "aBc");
        bidi_ne(value_quoted_with_esacpes, "\"aBc\"");
        bidi_ne(value_quoted_with_esacpes, "\"a\\Bc\"");
    }

    #[test]
    fn test_value_eq_value() {
        let value = Value {
            source: "abc",
            ascii_case_insensitive: false
        };
        let value_quoted = Value {
            source: "\"abc\"",
            ascii_case_insensitive: false
        };
        let value_quoted_with_esacpes = Value {
            source: "\"a\\bc\"",
            ascii_case_insensitive: false
        };
        assert_eq!(value, value);
        assert_eq!(value_quoted, value_quoted);
        assert_eq!(value_quoted_with_esacpes, value_quoted_with_esacpes);

        bidi_eq(value, value_quoted);
        bidi_eq(value, value_quoted_with_esacpes);
        bidi_eq(value_quoted, value_quoted_with_esacpes);
    }

    #[test]
    fn test_value_eq_value_case_insensitive() {
        let value = Value {
            source: "Abc",
            ascii_case_insensitive: true
        };
        let value_quoted = Value {
            source: "\"aBc\"",
            ascii_case_insensitive: true
        };
        let value_quoted_with_esacpes = Value {
            source: "\"a\\bC\"",
            ascii_case_insensitive: true
        };
        assert_eq!(value, value);
        assert_eq!(value_quoted, value_quoted);
        assert_eq!(value_quoted_with_esacpes, value_quoted_with_esacpes);

        bidi_eq(value, value_quoted);
        bidi_eq(value, value_quoted_with_esacpes);
        bidi_eq(value_quoted, value_quoted_with_esacpes);
    }

    #[test]
    fn test_value_eq_value_mixed_case_sensitivity() {
        let value = Value {
            source: "Abc",
            ascii_case_insensitive: true
        };
        let value_quoted = Value {
            source: "\"aBc\"",
            ascii_case_insensitive: false
        };
        let value_quoted_with_esacpes = Value {
            source: "\"a\\bC\"",
            ascii_case_insensitive: false
        };

        bidi_eq(value, value_quoted);
        bidi_eq(value, value_quoted_with_esacpes);

        //both are ascii case insensitive
        bidi_ne(value_quoted, value_quoted_with_esacpes);
    }

    #[test]
    fn test_as_str_repr() {
        let value = Value::new("\"ab cd\"");
        assert_eq!(value, "ab cd");
        assert_eq!(value.as_str_repr(), "\"ab cd\"");
    }

    #[test]
    fn test_to_content_not_quoted() {
        let value = Value::new("abc");
        assert_eq!(value.to_content(), Cow::Borrowed("abc"));
    }

    #[test]
    fn test_to_content_quoted_simple() {
        let value = Value::new("\"ab cd\"");
        assert_eq!(value.to_content(), Cow::Borrowed("ab cd"));
    }

    #[test]
    fn test_to_content_with_quoted_pair() {
        let value = Value::new("\"ab\\\"cd\"");
        assert_eq!(value, "ab\"cd");
        let expected: Cow<'static, str> = Cow::Owned("ab\"cd".into());
        assert_eq!(value.to_content(), expected);
    }

}
<|endoftext|>


<commit_before>use std::ops::Deref;
use std::collections::BTreeMap;

use component::Component;
use component::parse_component;
use property::Property;

use std::result::Result as RResult;
use error::*;

pub struct Vcard(Component);

/// The Vcard object.
///
/// This type simply holds data and offers functions to access this data. It does not compute
/// anything.
impl Vcard {

    /// Parse a string to a Vcard object
    ///
    /// Returns an error if the parsed text is not a Vcard (that means that an error is returned
    /// also if this is a valid icalendar!)
    ///
    pub fn build(s: &str) -> Result<Vcard> {
        parse_component(s)
            .and_then(|c| {
                Self::from_component(c)
                    .map_err(|_| {
                        let kind = VObjectErrorKind::NotAVCard;
                        VObjectError::from_kind(kind)
                    })
            })
    }

    /// Wrap a Component into a Vcard object, or don't do it if the Component is not a Vcard.
    pub fn from_component(c: Component)-> RResult<Vcard, Component> {
        if c.name == "VCARD" {
            Ok(Vcard(c))
        } else {
            Err(c)
        }
    }

    make_getter_function_for_values!(adr            , "ADR"          , Adr);
    make_getter_function_for_optional!(anniversary  , "ANNIVERSARY"  , Anniversary);
    make_getter_function_for_optional!(bday         , "BDAY"         , BDay);
    make_getter_function_for_values!(categories     , "CATEGORIES"   , Category);
    make_getter_function_for_optional!(clientpidmap , "CLIENTPIDMAP" , ClientPidMap);
    make_getter_function_for_values!(email          , "EMAIL"        , Email);
    make_getter_function_for_values!(fullname       , "FN"           , FullName);
    make_getter_function_for_optional!(gender       , "GENDER"       , Gender);
    make_getter_function_for_values!(geo            , "GEO"          , Geo);
    make_getter_function_for_values!(impp           , "IMPP"         , IMPP);
    make_getter_function_for_values!(key            , "KEY"          , Key);
    make_getter_function_for_values!(lang           , "LANG"         , Lang);
    make_getter_function_for_values!(logo           , "LOGO"         , Logo);
    make_getter_function_for_values!(member         , "MEMBER"       , Member);
    make_getter_function_for_optional!(name         , "N"            , Name);
    make_getter_function_for_values!(nickname       , "NICKNAME"     , NickName);
    make_getter_function_for_values!(note           , "NOTE"         , Note);
    make_getter_function_for_values!(org            , "ORG"          , Organization);
    make_getter_function_for_values!(photo          , "PHOTO"        , Photo);
    make_getter_function_for_optional!(proid        , "PRIOD"        , Proid);
    make_getter_function_for_values!(related        , "RELATED"      , Related);
    make_getter_function_for_optional!(rev          , "REV"          , Rev);
    make_getter_function_for_values!(role           , "ROLE"         , Title);
    make_getter_function_for_values!(sound          , "SOUND"        , Sound);
    make_getter_function_for_values!(tel            , "TEL"          , Tel);
    make_getter_function_for_values!(title          , "TITLE"        , Title);
    make_getter_function_for_values!(tz             , "TZ"           , Tz);
    make_getter_function_for_optional!(uid          , "UID"          , Uid);
    make_getter_function_for_values!(url            , "URL"          , Url);
    make_getter_function_for_optional!(version      , "VERSION"      , Version);

    make_builder_fn!(fn with_adr building "ADR" with_params,
                     |o| o.unwrap_or(String::from("")) =>
                     pobox    : Option<String>,
                     ext      : Option<String>,
                     street   : Option<String>,
                     locality : Option<String>,
                     region   : Option<String>,
                     code     : Option<String>,
                     country  : Option<String>);

    make_builder_fn!(fn with_anniversary  building "ANNIVERSARY"        , |o| o => value: String);
    make_builder_fn!(fn with_bday         building "BDAY" with_params   , |o| o => value: String);
    make_builder_fn!(fn with_categories   building "CATEGORIES"         , |o| o.join(";") => org: Vec<String>);
    make_builder_fn!(fn with_clientpidmap building "CLIENTPIDMAP"       , |o| o => raw: String);
    make_builder_fn!(fn with_email        building "EMAIL"              , |o| o => email: String);
    make_builder_fn!(fn with_fullname     building "FN"                 , |o| o => fullname: String);
    make_builder_fn!(fn with_gender       building "GENDER" with_params , |o| o => value: String);
    make_builder_fn!(fn with_geo          building "GEO"                , |o| o => uri: String);
    make_builder_fn!(fn with_impp         building "IMPP"               , |o| o => uri: String);
    make_builder_fn!(fn with_key          building "KEY"                , |o| o => uri: String);
    make_builder_fn!(fn with_lang         building "LANG"               , |o| o => lang: String);
    make_builder_fn!(fn with_logo         building "LOGO"               , |o| o => uri: String);
    make_builder_fn!(fn with_member       building "MEMBER"             , |o| o => uri: String);

    make_builder_fn!(fn with_name building "N" with_params,
                     |o| o.unwrap_or(String::from("")) =>
                     surname            : Option<String>,
                     given_name         : Option<String>,
                     additional_name    : Option<String>,
                     honorific_prefixes : Option<String>,
                     honorific_suffixes : Option<String>);

    make_builder_fn!(fn with_nickname building "NICKNAME" with_params , |o| o => name: String);
    make_builder_fn!(fn with_note     building "NOTE"                 , |o| o => text: String);
    make_builder_fn!(fn with_org      building "ORG"                  , |o| o.join(";") => org: Vec<String>);
    make_builder_fn!(fn with_photo    building "PHOTO" with_params    , |o| o => param: String);
    make_builder_fn!(fn with_proid    building "PRODID"               , |o| o => param: String);
    make_builder_fn!(fn with_related  building "RELATED"              , |o| o => uri: String);
    make_builder_fn!(fn with_rev      building "REV"                  , |o| o => timestamp: String);
    make_builder_fn!(fn with_role     building "ROLE"                 , |o| o => role: String);
    make_builder_fn!(fn with_sound    building "SOUND"                , |o| o => uri: String);
    make_builder_fn!(fn with_tel      building "TEL" with_params      , |o| o => value: String);
    make_builder_fn!(fn with_title    building "TITLE"                , |o| o => title: String);
    make_builder_fn!(fn with_tz       building "TZ"                   , |o| o => tz: String);
    make_builder_fn!(fn with_uid      building "UID"                  , |o| o => uri: String);
    make_builder_fn!(fn with_url      building "URL"                  , |o| o => uri: String);
    make_builder_fn!(fn with_version  building "VERSION"              , |o| o => version: String);

}

impl Default for Vcard {
    fn default() -> Self {
        Vcard(Component::new(String::from("VCARD")))
    }
}

impl Deref for Vcard {
    type Target = Component;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

create_data_type!(Adr);
create_data_type!(Anniversary);
create_data_type!(BDay);
create_data_type!(Category);
create_data_type!(ClientPidMap);
create_data_type!(Email);
create_data_type!(FullName);
create_data_type!(Gender);
create_data_type!(Geo);
create_data_type!(IMPP);
create_data_type!(Key);
create_data_type!(Lang);
create_data_type!(Logo);
create_data_type!(Member);
create_data_type!(Name);
create_data_type!(NickName);
create_data_type!(Note);
create_data_type!(Organization);
create_data_type!(PhoneNumber);
create_data_type!(Photo);
create_data_type!(Proid);
create_data_type!(Related);
create_data_type!(Rev);
create_data_type!(Sound);
create_data_type!(Tel);
create_data_type!(Title);
create_data_type!(Tz);
create_data_type!(Uid);
create_data_type!(Url);
create_data_type!(Version);

/// A Name type
///
/// offers functionality to get firstname, middlenames and lastname.
///
/// The parsing behaviour is implemented in a way that splits at whitespace, following these rules:
///
/// * If there is only one element after splitting, this is considered the lastname
/// * If there are two elements, this is firstname and lastname
/// * If there are more than two elements, firstname and lastname are the first and last elements
/// respectively, all others are middlenames.
///
impl Name {

    pub fn plain(&self) -> String {
        self.0.clone()
    }

    pub fn surname(&self) -> Option<String> {
        self.0.split(";").nth(0).map(String::from)
    }

    pub fn given_name(&self) -> Option<String> {
        self.0.split(";").nth(1).map(String::from)
    }

    pub fn additional_names(&self) -> Option<String> {
        self.0.split(";").nth(2).map(String::from)
    }

    pub fn honorific_prefixes(&self) -> Option<String> {
        self.0.split(";").nth(3).map(String::from)
    }

    pub fn honorific_suffixes(&self) -> Option<String> {
        self.0.split(";").nth(4).map(String::from)
    }

    /// Alias for Name::surname()
    #[inline]
    pub fn family_name(&self) -> Option<String> {
        self.surname()
    }

}

pub struct VcardBuilder(Component);

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

    #[test]
    fn test_vcard_basic() {
        let item = Vcard::build(
            "BEGIN:VCARD\n\
            VERSION:2.1\n\
            N:Mustermann;Erika\n\
            FN:Erika Mustermann\n\
            ORG:Wikipedia\n\
            TITLE:Oberleutnant\n\
            PHOTO;JPEG:http://commons.wikimedia.org/wiki/File:Erika_Mustermann_2010.jpg\n\
            TEL;WORK;VOICE:(0221) 9999123\n\n\n\
            TEL;HOME;VOICE:(0221) 1234567\n\
            ADR;HOME:;;Heidestrasse 17;Koeln;;51147;Deutschland\n\
            EMAIL;PREF;INTERNET:erika@mustermann.de\n\
            REV:20140301T221110Z\n\
            END:VCARD\n\r\n\n").unwrap();

        assert_eq!(item.adr()[0].raw(), ";;Heidestrasse 17;Koeln;;51147;Deutschland");
        assert_eq!(item.fullname()[0].raw(), "Erika Mustermann");
        assert_eq!(item.name().unwrap().plain(), "Mustermann;Erika");
        assert_eq!(item.name().unwrap().surname().unwrap()    , "Mustermann");
        assert_eq!(item.name().unwrap().given_name().unwrap() , "Erika");
        assert_eq!(item.org()[0].raw() , "Wikipedia");
        assert_eq!(item.title()[0].raw() , "Oberleutnant");
    }

    #[test]
    fn test_vcard_builder() {
        use component::write_component;

        let build = Vcard::default()
            .with_name(parameters!(),
                       None,
                       Some("Mustermann".into()),
                       None,
                       Some("Erika".into()),
                       None)
            .with_fullname("Erika Mustermann".into())
            .with_org(vec!["Wikipedia".into()])
            .with_title("Oberleutnant".into())
            .with_tel(parameters!("TYPE" => "WORK"), "(0221) 9999123".into())
            .with_tel(parameters!("TYPE" => "HOME"), "(0221) 1234567".into())
            .with_adr(parameters!("TYPE" => "HOME"),
                      None,
                      None,
                      Some("Heidestrasse 17".into()),
                      Some("Koeln".into()),
                      None,
                      Some("51147".into()),
                      Some("Deutschland".into()))
            .with_email("erika@mustermann.de".into())
            .with_rev("20140301T221110Z".into());

        let build_string = write_component(&build);

        let expected =
            "BEGIN:VCARD\r\n\
            ADR;TYPE=HOME:;;Heidestrasse 17;Koeln;;51147;Deutschland\r\n\
            EMAIL:erika@mustermann.de\r\n\
            FN:Erika Mustermann\r\n\
            N:;Mustermann;;Erika;\r\n\
            ORG:Wikipedia\r\n\
            REV:20140301T221110Z\r\n\
            TEL;TYPE=WORK:(0221) 9999123\r\n\
            TEL;TYPE=HOME:(0221) 1234567\r\n\
            TITLE:Oberleutnant\r\n\
            END:VCARD\r\n";

        assert_eq!(expected, build_string);
    }

}

<commit_msg>Let Vcard derive Debug (#20)<commit_after>use std::ops::Deref;
use std::collections::BTreeMap;

use component::Component;
use component::parse_component;
use property::Property;

use std::result::Result as RResult;
use error::*;

#[derive(Debug)]
pub struct Vcard(Component);

/// The Vcard object.
///
/// This type simply holds data and offers functions to access this data. It does not compute
/// anything.
impl Vcard {

    /// Parse a string to a Vcard object
    ///
    /// Returns an error if the parsed text is not a Vcard (that means that an error is returned
    /// also if this is a valid icalendar!)
    ///
    pub fn build(s: &str) -> Result<Vcard> {
        parse_component(s)
            .and_then(|c| {
                Self::from_component(c)
                    .map_err(|_| {
                        let kind = VObjectErrorKind::NotAVCard;
                        VObjectError::from_kind(kind)
                    })
            })
    }

    /// Wrap a Component into a Vcard object, or don't do it if the Component is not a Vcard.
    pub fn from_component(c: Component)-> RResult<Vcard, Component> {
        if c.name == "VCARD" {
            Ok(Vcard(c))
        } else {
            Err(c)
        }
    }

    make_getter_function_for_values!(adr            , "ADR"          , Adr);
    make_getter_function_for_optional!(anniversary  , "ANNIVERSARY"  , Anniversary);
    make_getter_function_for_optional!(bday         , "BDAY"         , BDay);
    make_getter_function_for_values!(categories     , "CATEGORIES"   , Category);
    make_getter_function_for_optional!(clientpidmap , "CLIENTPIDMAP" , ClientPidMap);
    make_getter_function_for_values!(email          , "EMAIL"        , Email);
    make_getter_function_for_values!(fullname       , "FN"           , FullName);
    make_getter_function_for_optional!(gender       , "GENDER"       , Gender);
    make_getter_function_for_values!(geo            , "GEO"          , Geo);
    make_getter_function_for_values!(impp           , "IMPP"         , IMPP);
    make_getter_function_for_values!(key            , "KEY"          , Key);
    make_getter_function_for_values!(lang           , "LANG"         , Lang);
    make_getter_function_for_values!(logo           , "LOGO"         , Logo);
    make_getter_function_for_values!(member         , "MEMBER"       , Member);
    make_getter_function_for_optional!(name         , "N"            , Name);
    make_getter_function_for_values!(nickname       , "NICKNAME"     , NickName);
    make_getter_function_for_values!(note           , "NOTE"         , Note);
    make_getter_function_for_values!(org            , "ORG"          , Organization);
    make_getter_function_for_values!(photo          , "PHOTO"        , Photo);
    make_getter_function_for_optional!(proid        , "PRIOD"        , Proid);
    make_getter_function_for_values!(related        , "RELATED"      , Related);
    make_getter_function_for_optional!(rev          , "REV"          , Rev);
    make_getter_function_for_values!(role           , "ROLE"         , Title);
    make_getter_function_for_values!(sound          , "SOUND"        , Sound);
    make_getter_function_for_values!(tel            , "TEL"          , Tel);
    make_getter_function_for_values!(title          , "TITLE"        , Title);
    make_getter_function_for_values!(tz             , "TZ"           , Tz);
    make_getter_function_for_optional!(uid          , "UID"          , Uid);
    make_getter_function_for_values!(url            , "URL"          , Url);
    make_getter_function_for_optional!(version      , "VERSION"      , Version);

    make_builder_fn!(fn with_adr building "ADR" with_params,
                     |o| o.unwrap_or(String::from("")) =>
                     pobox    : Option<String>,
                     ext      : Option<String>,
                     street   : Option<String>,
                     locality : Option<String>,
                     region   : Option<String>,
                     code     : Option<String>,
                     country  : Option<String>);

    make_builder_fn!(fn with_anniversary  building "ANNIVERSARY"        , |o| o => value: String);
    make_builder_fn!(fn with_bday         building "BDAY" with_params   , |o| o => value: String);
    make_builder_fn!(fn with_categories   building "CATEGORIES"         , |o| o.join(";") => org: Vec<String>);
    make_builder_fn!(fn with_clientpidmap building "CLIENTPIDMAP"       , |o| o => raw: String);
    make_builder_fn!(fn with_email        building "EMAIL"              , |o| o => email: String);
    make_builder_fn!(fn with_fullname     building "FN"                 , |o| o => fullname: String);
    make_builder_fn!(fn with_gender       building "GENDER" with_params , |o| o => value: String);
    make_builder_fn!(fn with_geo          building "GEO"                , |o| o => uri: String);
    make_builder_fn!(fn with_impp         building "IMPP"               , |o| o => uri: String);
    make_builder_fn!(fn with_key          building "KEY"                , |o| o => uri: String);
    make_builder_fn!(fn with_lang         building "LANG"               , |o| o => lang: String);
    make_builder_fn!(fn with_logo         building "LOGO"               , |o| o => uri: String);
    make_builder_fn!(fn with_member       building "MEMBER"             , |o| o => uri: String);

    make_builder_fn!(fn with_name building "N" with_params,
                     |o| o.unwrap_or(String::from("")) =>
                     surname            : Option<String>,
                     given_name         : Option<String>,
                     additional_name    : Option<String>,
                     honorific_prefixes : Option<String>,
                     honorific_suffixes : Option<String>);

    make_builder_fn!(fn with_nickname building "NICKNAME" with_params , |o| o => name: String);
    make_builder_fn!(fn with_note     building "NOTE"                 , |o| o => text: String);
    make_builder_fn!(fn with_org      building "ORG"                  , |o| o.join(";") => org: Vec<String>);
    make_builder_fn!(fn with_photo    building "PHOTO" with_params    , |o| o => param: String);
    make_builder_fn!(fn with_proid    building "PRODID"               , |o| o => param: String);
    make_builder_fn!(fn with_related  building "RELATED"              , |o| o => uri: String);
    make_builder_fn!(fn with_rev      building "REV"                  , |o| o => timestamp: String);
    make_builder_fn!(fn with_role     building "ROLE"                 , |o| o => role: String);
    make_builder_fn!(fn with_sound    building "SOUND"                , |o| o => uri: String);
    make_builder_fn!(fn with_tel      building "TEL" with_params      , |o| o => value: String);
    make_builder_fn!(fn with_title    building "TITLE"                , |o| o => title: String);
    make_builder_fn!(fn with_tz       building "TZ"                   , |o| o => tz: String);
    make_builder_fn!(fn with_uid      building "UID"                  , |o| o => uri: String);
    make_builder_fn!(fn with_url      building "URL"                  , |o| o => uri: String);
    make_builder_fn!(fn with_version  building "VERSION"              , |o| o => version: String);

}

impl Default for Vcard {
    fn default() -> Self {
        Vcard(Component::new(String::from("VCARD")))
    }
}

impl Deref for Vcard {
    type Target = Component;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

create_data_type!(Adr);
create_data_type!(Anniversary);
create_data_type!(BDay);
create_data_type!(Category);
create_data_type!(ClientPidMap);
create_data_type!(Email);
create_data_type!(FullName);
create_data_type!(Gender);
create_data_type!(Geo);
create_data_type!(IMPP);
create_data_type!(Key);
create_data_type!(Lang);
create_data_type!(Logo);
create_data_type!(Member);
create_data_type!(Name);
create_data_type!(NickName);
create_data_type!(Note);
create_data_type!(Organization);
create_data_type!(PhoneNumber);
create_data_type!(Photo);
create_data_type!(Proid);
create_data_type!(Related);
create_data_type!(Rev);
create_data_type!(Sound);
create_data_type!(Tel);
create_data_type!(Title);
create_data_type!(Tz);
create_data_type!(Uid);
create_data_type!(Url);
create_data_type!(Version);

/// A Name type
///
/// offers functionality to get firstname, middlenames and lastname.
///
/// The parsing behaviour is implemented in a way that splits at whitespace, following these rules:
///
/// * If there is only one element after splitting, this is considered the lastname
/// * If there are two elements, this is firstname and lastname
/// * If there are more than two elements, firstname and lastname are the first and last elements
/// respectively, all others are middlenames.
///
impl Name {

    pub fn plain(&self) -> String {
        self.0.clone()
    }

    pub fn surname(&self) -> Option<String> {
        self.0.split(";").nth(0).map(String::from)
    }

    pub fn given_name(&self) -> Option<String> {
        self.0.split(";").nth(1).map(String::from)
    }

    pub fn additional_names(&self) -> Option<String> {
        self.0.split(";").nth(2).map(String::from)
    }

    pub fn honorific_prefixes(&self) -> Option<String> {
        self.0.split(";").nth(3).map(String::from)
    }

    pub fn honorific_suffixes(&self) -> Option<String> {
        self.0.split(";").nth(4).map(String::from)
    }

    /// Alias for Name::surname()
    #[inline]
    pub fn family_name(&self) -> Option<String> {
        self.surname()
    }

}

pub struct VcardBuilder(Component);

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

    #[test]
    fn test_vcard_basic() {
        let item = Vcard::build(
            "BEGIN:VCARD\n\
            VERSION:2.1\n\
            N:Mustermann;Erika\n\
            FN:Erika Mustermann\n\
            ORG:Wikipedia\n\
            TITLE:Oberleutnant\n\
            PHOTO;JPEG:http://commons.wikimedia.org/wiki/File:Erika_Mustermann_2010.jpg\n\
            TEL;WORK;VOICE:(0221) 9999123\n\n\n\
            TEL;HOME;VOICE:(0221) 1234567\n\
            ADR;HOME:;;Heidestrasse 17;Koeln;;51147;Deutschland\n\
            EMAIL;PREF;INTERNET:erika@mustermann.de\n\
            REV:20140301T221110Z\n\
            END:VCARD\n\r\n\n").unwrap();

        assert_eq!(item.adr()[0].raw(), ";;Heidestrasse 17;Koeln;;51147;Deutschland");
        assert_eq!(item.fullname()[0].raw(), "Erika Mustermann");
        assert_eq!(item.name().unwrap().plain(), "Mustermann;Erika");
        assert_eq!(item.name().unwrap().surname().unwrap()    , "Mustermann");
        assert_eq!(item.name().unwrap().given_name().unwrap() , "Erika");
        assert_eq!(item.org()[0].raw() , "Wikipedia");
        assert_eq!(item.title()[0].raw() , "Oberleutnant");
    }

    #[test]
    fn test_vcard_builder() {
        use component::write_component;

        let build = Vcard::default()
            .with_name(parameters!(),
                       None,
                       Some("Mustermann".into()),
                       None,
                       Some("Erika".into()),
                       None)
            .with_fullname("Erika Mustermann".into())
            .with_org(vec!["Wikipedia".into()])
            .with_title("Oberleutnant".into())
            .with_tel(parameters!("TYPE" => "WORK"), "(0221) 9999123".into())
            .with_tel(parameters!("TYPE" => "HOME"), "(0221) 1234567".into())
            .with_adr(parameters!("TYPE" => "HOME"),
                      None,
                      None,
                      Some("Heidestrasse 17".into()),
                      Some("Koeln".into()),
                      None,
                      Some("51147".into()),
                      Some("Deutschland".into()))
            .with_email("erika@mustermann.de".into())
            .with_rev("20140301T221110Z".into());

        let build_string = write_component(&build);

        let expected =
            "BEGIN:VCARD\r\n\
            ADR;TYPE=HOME:;;Heidestrasse 17;Koeln;;51147;Deutschland\r\n\
            EMAIL:erika@mustermann.de\r\n\
            FN:Erika Mustermann\r\n\
            N:;Mustermann;;Erika;\r\n\
            ORG:Wikipedia\r\n\
            REV:20140301T221110Z\r\n\
            TEL;TYPE=WORK:(0221) 9999123\r\n\
            TEL;TYPE=HOME:(0221) 1234567\r\n\
            TITLE:Oberleutnant\r\n\
            END:VCARD\r\n";

        assert_eq!(expected, build_string);
    }

}

<|endoftext|>


<commit_before>use Model;
use linear_algebra::Vector;
use num::{Float, One};

/// Constant Model
///
/// This model predicts a number. The cost function used during training decides
/// wether this number is a mean, median or something else.
#[derive(Debug, Clone)]
pub struct Constant{
    /// Any prediction made by this model will have the value of `c`
    pub c : f64
}

impl Model for Constant{
    type Input = ();
    type Target = f64;

    fn predict(&self, _: &()) -> f64{
        self.c
    }

    fn num_coefficents(&self) -> usize{
        1
    }

    fn gradient(&self, coefficent : usize, _ : &()) -> f64{
        match coefficent{
            0 => 1.0,
            _ => panic!("coefficent index out of range")
        }
    }

    fn coefficent(& mut self, coefficent : usize) -> & mut f64{
        match coefficent{
            0 => & mut self.c,
            _ => panic!("coefficent index out of range")
        }
    }
}

/// Linear model
///
/// Models the target as `y = m * x + c`
#[derive(Debug, Clone)]
pub struct Linear<V : Vector>{
    /// Slope
    pub m : V,
    /// Offset
    pub c : V::Scalar
}

impl<V : Vector> Model for Linear<V> where V::Scalar : Float{

    type Input = V;
    type Target = V::Scalar;

    fn predict(&self, input : &V) -> V::Scalar{
        self.m.dot(input) + self.c
    }

    fn num_coefficents(&self) -> usize{
        self.m.dimension() + 1
    }

    fn gradient(&self, coefficent : usize, input : &V) -> V::Scalar{

        use num::One;

        if coefficent == self.m.dimension(){
            V::Scalar::one() //c
        } else {
            input.at(coefficent)
        }
    }

    fn coefficent(& mut self, coefficent : usize) -> & mut V::Scalar{
        if coefficent == self.m.dimension(){
            & mut self.c
        } else {
            self.m.mut_at(coefficent)
        }
    }
}

/// Models target as `y = 1/1+e^(m * x + c)`
#[derive(Clone)]
pub struct Logicstic<V: Vector>{
    /// Linear term of `Logistic` model
    pub linear : Linear<V>
}

impl<V : Vector> Model for Logicstic<V> where V::Scalar : Float{
    type Input = V;
    type Target = V::Scalar;

    fn predict(&self, input : &V) -> V::Scalar{
        V::Scalar::one() / (V::Scalar::one() + self.linear.predict(input).exp())
    }

    fn num_coefficents(&self) -> usize{
        self.linear.num_coefficents()
    }

    fn gradient(&self, coefficent : usize, input : &V) -> V::Scalar{
        let p = self.predict(input);
        - p * (V::Scalar::one() - p) * self.linear.gradient(coefficent, input)
    }

    fn coefficent(& mut self, coefficent : usize) -> & mut V::Scalar{
        self.linear.coefficent(coefficent)
    }
}<commit_msg>Constant model now supports arbitrary input<commit_after>use Model;
use linear_algebra::Vector;
use num::{Float, One};
use std::marker::PhantomData;

/// Models the target as `c`
///
/// This model predicts a number. The cost function used during training decides
/// wether this number is a mean, median or something else.
#[derive(Debug)]
pub struct Constant<Input>{
    /// Any prediction made by this model will have the value of `c`
    pub c: f64,
    _phantom: PhantomData<Input>
}

impl<I> Constant<I> {
    fn new(c: f64) -> Constant<I>{
        Constant{c: c, _phantom: PhantomData::<I>{}}
    }
}

impl<I> Clone for Constant<I>{
    fn clone(&self) -> Self{
        Constant::new(self.c)
    }
}

impl<I> Model for Constant<I>{
    type Input = I;
    type Target = f64;

    fn predict(&self, _: &I) -> f64{
        self.c
    }

    fn num_coefficents(&self) -> usize{
        1
    }

    fn gradient(&self, coefficent : usize, _ : &I) -> f64{
        match coefficent{
            0 => 1.0,
            _ => panic!("coefficent index out of range")
        }
    }

    fn coefficent(& mut self, coefficent : usize) -> & mut f64{
        match coefficent{
            0 => & mut self.c,
            _ => panic!("coefficent index out of range")
        }
    }
}

/// Models the target as `y = m * x + c`
#[derive(Debug, Clone)]
pub struct Linear<V : Vector>{
    /// Slope
    pub m : V,
    /// Offset
    pub c : V::Scalar
}

impl<V : Vector> Model for Linear<V> where V::Scalar : Float{

    type Input = V;
    type Target = V::Scalar;

    fn predict(&self, input : &V) -> V::Scalar{
        self.m.dot(input) + self.c
    }

    fn num_coefficents(&self) -> usize{
        self.m.dimension() + 1
    }

    fn gradient(&self, coefficent : usize, input : &V) -> V::Scalar{

        use num::One;

        if coefficent == self.m.dimension(){
            V::Scalar::one() //c
        } else {
            input.at(coefficent)
        }
    }

    fn coefficent(& mut self, coefficent : usize) -> & mut V::Scalar{
        if coefficent == self.m.dimension(){
            & mut self.c
        } else {
            self.m.mut_at(coefficent)
        }
    }
}

/// Models target as `y = 1/1+e^(m * x + c)`
#[derive(Clone)]
pub struct Logicstic<V: Vector>{
    /// Linear term of `Logistic` model
    pub linear : Linear<V>
}

impl<V : Vector> Model for Logicstic<V> where V::Scalar : Float{
    type Input = V;
    type Target = V::Scalar;

    fn predict(&self, input : &V) -> V::Scalar{
        V::Scalar::one() / (V::Scalar::one() + self.linear.predict(input).exp())
    }

    fn num_coefficents(&self) -> usize{
        self.linear.num_coefficents()
    }

    fn gradient(&self, coefficent : usize, input : &V) -> V::Scalar{
        let p = self.predict(input);
        - p * (V::Scalar::one() - p) * self.linear.gradient(coefficent, input)
    }

    fn coefficent(& mut self, coefficent : usize) -> & mut V::Scalar{
        self.linear.coefficent(coefficent)
    }
}<|endoftext|>