<commit_before>//
//   Copyright 2016 Andrew Hunter
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.
//

//!
//! # Matches
//!
//! ```
//! # use ndfa::*;
//! # assert!(matches("abcabc", "abc".repeat_forever(1)).is_some());
//! # assert!(matches("abcabcabc", "abc".repeat_forever(1)).is_some());
//! # assert!(matches("abc", "abc").is_some());
//! # assert!(matches("def", "abc".repeat_forever(1)).is_none());
//! if matches("abcabc", "abc".repeat_forever(1)).is_some() { /* ... */ }
//! ```

use super::symbol_range_dfa::*;
use super::symbol_reader::*;
use super::pattern_matcher::*;
use super::prepare::*;

///
/// Runs a pattern matcher against a stream, and returns the number of characters matching if it accepted the stream
///
fn matches_symbol_range<InputSymbol: PartialOrd, OutputSymbol: 'static>(dfa: &SymbolRangeDfa<InputSymbol, OutputSymbol>, symbol_reader: &mut SymbolReader<InputSymbol>) -> Option<usize> {
    // Run until there are no more states
    let mut current_state = dfa.start();

    while let More(this_state) = current_state {
        let next_state =
            if let Some(next_char) = symbol_reader.next_symbol() {
                this_state.next(next_char)
            } else {
                this_state.finish()
            };

        current_state = next_state;
    }

    if let Accept(count, _) = current_state {
        Some(count)
    } else {
        None
    }
}

///
/// Matches a source stream against a pattern
///
pub fn matches<'a, Symbol, OutputSymbol, Prepare, Reader, Source>(source: Source, pattern: Prepare) -> Option<usize>
where   Prepare: PrepareToMatch<SymbolRangeDfa<Symbol, OutputSymbol>>
,       Reader: SymbolReader<Symbol>+'a
,       Source: SymbolSource<'a, Symbol, SymbolReader=Reader>
,       Symbol: PartialOrd
,       OutputSymbol: 'static {
    let matcher    = pattern.prepare_to_match();
    let mut reader = source.read_symbols();

    matches_symbol_range(&matcher, &mut reader)
}

///
/// Matches a source stream against a pattern
///
pub fn matches_prepared<'a, Symbol, OutputSymbol, Reader, Source>(source: Source, matcher: &SymbolRangeDfa<Symbol, OutputSymbol>) -> Option<usize>
where   Reader: SymbolReader<Symbol>+'a
,       Source: SymbolSource<'a, Symbol, SymbolReader=Reader>
,       Symbol: PartialOrd
,       OutputSymbol: 'static {
    let mut reader = source.read_symbols();

    matches_symbol_range(&matcher, &mut reader)
}
<commit_msg>Now it’s easier to test matching various things, we can start writing more interesting tests<commit_after>//
//   Copyright 2016 Andrew Hunter
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.
//

//!
//! # Matches
//!
//! ```
//! # use ndfa::*;
//! # assert!(matches("abcabc", "abc".repeat_forever(1)).is_some());
//! # assert!(matches("abcabcabc", "abc".repeat_forever(1)).is_some());
//! # assert!(matches("abc", "abc").is_some());
//! # assert!(matches("def", "abc".repeat_forever(1)).is_none());
//! if matches("abcabc", "abc".repeat_forever(1)).is_some() { /* ... */ }
//! ```

use super::symbol_range_dfa::*;
use super::symbol_reader::*;
use super::pattern_matcher::*;
use super::prepare::*;

///
/// Runs a pattern matcher against a stream, and returns the number of characters matching if it accepted the stream
///
fn matches_symbol_range<InputSymbol: PartialOrd, OutputSymbol: 'static>(dfa: &SymbolRangeDfa<InputSymbol, OutputSymbol>, symbol_reader: &mut SymbolReader<InputSymbol>) -> Option<usize> {
    // Run until there are no more states
    let mut current_state = dfa.start();

    while let More(this_state) = current_state {
        let next_state =
            if let Some(next_char) = symbol_reader.next_symbol() {
                this_state.next(next_char)
            } else {
                this_state.finish()
            };

        current_state = next_state;
    }

    if let Accept(count, _) = current_state {
        Some(count)
    } else {
        None
    }
}

///
/// Matches a source stream against a pattern
///
pub fn matches<'a, Symbol, OutputSymbol, Prepare, Reader, Source>(source: Source, pattern: Prepare) -> Option<usize>
where   Prepare: PrepareToMatch<SymbolRangeDfa<Symbol, OutputSymbol>>
,       Reader: SymbolReader<Symbol>+'a
,       Source: SymbolSource<'a, Symbol, SymbolReader=Reader>
,       Symbol: PartialOrd
,       OutputSymbol: 'static {
    let matcher    = pattern.prepare_to_match();
    let mut reader = source.read_symbols();

    matches_symbol_range(&matcher, &mut reader)
}

///
/// Matches a source stream against a pattern
///
pub fn matches_prepared<'a, Symbol, OutputSymbol, Reader, Source>(source: Source, matcher: &SymbolRangeDfa<Symbol, OutputSymbol>) -> Option<usize>
where   Reader: SymbolReader<Symbol>+'a
,       Source: SymbolSource<'a, Symbol, SymbolReader=Reader>
,       Symbol: PartialOrd
,       OutputSymbol: 'static {
    let mut reader = source.read_symbols();

    matches_symbol_range(&matcher, &mut reader)
}

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

    #[test]
    fn match_multiple_repeats() {
        assert!(matches("abcabc", "abc".repeat_forever(1)).is_some());
    }

    #[test]
    fn match_single_repeat() {
        assert!(matches("abc", "abc".repeat_forever(1)).is_some());
    }

    #[test]
    fn match_zero_repeats() {
        assert!(matches("", "abc".repeat_forever(0)).is_some());
    }
}<|endoftext|>


<commit_before>//! Defines the `HttpMessage` trait that serves to encapsulate the operations of a single
//! request-response cycle on any HTTP connection.

use std::fmt::Debug;
use std::any::{Any, TypeId};
use std::io::{Read, Write};

use std::mem;

use typeable::Typeable;

use header::Headers;
use http::RawStatus;
use url::Url;

use method;
use version;
use traitobject;

/// Describes a request.
#[derive(Clone, Debug)]
pub struct RequestHead {
    /// The headers of the request
    pub headers: Headers,
    /// The method of the request
    pub method: method::Method,
    /// The URL of the request
    pub url: Url,
}

/// Describes a response.
#[derive(Clone, Debug)]
pub struct ResponseHead {
    /// The headers of the reponse
    pub headers: Headers,
    /// The raw status line of the response
    pub raw_status: RawStatus,
    /// The HTTP/2 version which generated the response
    pub version: version::HttpVersion,
}

/// The trait provides an API for sending an receiving HTTP messages.
pub trait HttpMessage: Write + Read + Send + Any + Typeable + Debug {
    /// Initiates a new outgoing request.
    ///
    /// Only the request's head is provided (in terms of the `RequestHead` struct).
    ///
    /// After this, the `HttpMessage` instance can be used as an `io::Write` in order to write the
    /// body of the request.
    fn set_outgoing(&mut self, head: RequestHead) -> ::Result<RequestHead>;
    /// Obtains the incoming response and returns its head (i.e. the `ResponseHead` struct)
    ///
    /// After this, the `HttpMessage` instance can be used as an `io::Read` in order to read out
    /// the response body.
    fn get_incoming(&mut self) -> ::Result<ResponseHead>;

    /// Closes the underlying HTTP connection.
    fn close_connection(&mut self) -> ::Result<()>;
}

impl HttpMessage {
    unsafe fn downcast_ref_unchecked<T: 'static>(&self) -> &T {
        mem::transmute(traitobject::data(self))
    }

    unsafe fn downcast_mut_unchecked<T: 'static>(&mut self) -> &mut T {
        mem::transmute(traitobject::data_mut(self))
    }

    unsafe fn downcast_unchecked<T: 'static>(self: Box<HttpMessage>) -> Box<T>  {
        let raw: *mut HttpMessage = mem::transmute(self);
        mem::transmute(traitobject::data_mut(raw))
    }
}

impl HttpMessage {
    /// Is the underlying type in this trait object a T?
    #[inline]
    pub fn is<T: Any>(&self) -> bool {
        (*self).get_type() == TypeId::of::<T>()
    }

    /// If the underlying type is T, get a reference to the contained data.
    #[inline]
    pub fn downcast_ref<T: Any>(&self) -> Option<&T> {
        if self.is::<T>() {
            Some(unsafe { self.downcast_ref_unchecked() })
        } else {
            None
        }
    }

    /// If the underlying type is T, get a mutable reference to the contained
    /// data.
    #[inline]
    pub fn downcast_mut<T: Any>(&mut self) -> Option<&mut T> {
        if self.is::<T>() {
            Some(unsafe { self.downcast_mut_unchecked() })
        } else {
            None
        }
    }

    /// If the underlying type is T, extract it.
    #[inline]
    pub fn downcast<T: Any>(self: Box<HttpMessage>)
            -> Result<Box<T>, Box<HttpMessage>> {
        if self.is::<T>() {
            Ok(unsafe { self.downcast_unchecked() })
        } else {
            Err(self)
        }
    }
}
<commit_msg>feat(client): add `Protocol` trait<commit_after>//! Defines the `HttpMessage` trait that serves to encapsulate the operations of a single
//! request-response cycle on any HTTP connection.

use std::fmt::Debug;
use std::any::{Any, TypeId};
use std::io::{Read, Write};

use std::mem;

use typeable::Typeable;

use header::Headers;
use http::RawStatus;
use url::Url;

use method;
use version;
use traitobject;
use net::ContextVerifier;

/// The trait provides an API for creating new `HttpMessage`s depending on the underlying HTTP
/// protocol.
pub trait Protocol {
    /// Creates a fresh `HttpMessage` bound to the given host, based on the given protocol scheme.
    fn new_message(&self, host: &str, port: u16, scheme: &str) -> ::Result<Box<HttpMessage>>;
    /// Sets the SSL verifier that should be used when establishing TLS-protected connections.
    fn set_ssl_verifier(&mut self, verifier: ContextVerifier);
}

/// Describes a request.
#[derive(Clone, Debug)]
pub struct RequestHead {
    /// The headers of the request
    pub headers: Headers,
    /// The method of the request
    pub method: method::Method,
    /// The URL of the request
    pub url: Url,
}

/// Describes a response.
#[derive(Clone, Debug)]
pub struct ResponseHead {
    /// The headers of the reponse
    pub headers: Headers,
    /// The raw status line of the response
    pub raw_status: RawStatus,
    /// The HTTP/2 version which generated the response
    pub version: version::HttpVersion,
}

/// The trait provides an API for sending an receiving HTTP messages.
pub trait HttpMessage: Write + Read + Send + Any + Typeable + Debug {
    /// Initiates a new outgoing request.
    ///
    /// Only the request's head is provided (in terms of the `RequestHead` struct).
    ///
    /// After this, the `HttpMessage` instance can be used as an `io::Write` in order to write the
    /// body of the request.
    fn set_outgoing(&mut self, head: RequestHead) -> ::Result<RequestHead>;
    /// Obtains the incoming response and returns its head (i.e. the `ResponseHead` struct)
    ///
    /// After this, the `HttpMessage` instance can be used as an `io::Read` in order to read out
    /// the response body.
    fn get_incoming(&mut self) -> ::Result<ResponseHead>;

    /// Closes the underlying HTTP connection.
    fn close_connection(&mut self) -> ::Result<()>;
}

impl HttpMessage {
    unsafe fn downcast_ref_unchecked<T: 'static>(&self) -> &T {
        mem::transmute(traitobject::data(self))
    }

    unsafe fn downcast_mut_unchecked<T: 'static>(&mut self) -> &mut T {
        mem::transmute(traitobject::data_mut(self))
    }

    unsafe fn downcast_unchecked<T: 'static>(self: Box<HttpMessage>) -> Box<T>  {
        let raw: *mut HttpMessage = mem::transmute(self);
        mem::transmute(traitobject::data_mut(raw))
    }
}

impl HttpMessage {
    /// Is the underlying type in this trait object a T?
    #[inline]
    pub fn is<T: Any>(&self) -> bool {
        (*self).get_type() == TypeId::of::<T>()
    }

    /// If the underlying type is T, get a reference to the contained data.
    #[inline]
    pub fn downcast_ref<T: Any>(&self) -> Option<&T> {
        if self.is::<T>() {
            Some(unsafe { self.downcast_ref_unchecked() })
        } else {
            None
        }
    }

    /// If the underlying type is T, get a mutable reference to the contained
    /// data.
    #[inline]
    pub fn downcast_mut<T: Any>(&mut self) -> Option<&mut T> {
        if self.is::<T>() {
            Some(unsafe { self.downcast_mut_unchecked() })
        } else {
            None
        }
    }

    /// If the underlying type is T, extract it.
    #[inline]
    pub fn downcast<T: Any>(self: Box<HttpMessage>)
            -> Result<Box<T>, Box<HttpMessage>> {
        if self.is::<T>() {
            Ok(unsafe { self.downcast_unchecked() })
        } else {
            Err(self)
        }
    }
}
<|endoftext|>


<commit_before>pub fn format_numeral(n: f64, format: &str) -> String {
    if format.contains(":") {
        format_time(n, format)
    } else {
        format_number(n, format)
    }
}

pub fn unformat_numeral(string: &str) -> f64 {
    if string.contains(":") {
        unformat_time(string)
    } else {
        unformat_number(string)
    }
}

#[allow(unused_variables)]
fn format_number(n: f64, format: &str) -> String {
    "TODO".to_string()
}

#[allow(unused_variables)]
fn unformat_number(string: &str) -> f64 {
    0.0
}

#[allow(unused_variables)]
fn format_time(n: f64, format: &str) -> String {
    let hours: f64 = (n/60./60.).floor();
    let minutes = ((n - (hours * 60. * 60.))/60.).floor();
    let seconds = (n - (hours * 60. * 60.) - (minutes * 60.)).round();

    let hours_string = hours.to_string();

    let minutes_string = & if minutes < 10. {
        "0".to_string() + &minutes.to_string()
    } else {
        minutes.to_string()
    };

    let seconds_string = & if seconds < 10. {
        "0".to_string() + &seconds.to_string()
    } else {
        seconds.to_string()
    };

    hours_string + ":" + minutes_string + ":" + seconds_string
}

fn unformat_time(string: &str) -> f64 {
    let split = string.split(":");
    let time_array: Vec<&str> = split.collect();
    let mut seconds = 0.0;

    if time_array.len() == 3 {
        // hours
        seconds = seconds + (time_array[0].parse::<f64>().unwrap() * 60.0 * 60.0);
        // minutes
        seconds = seconds + (time_array[1].parse::<f64>().unwrap() * 60.0);
        // seconds
        seconds = seconds + (time_array[2].parse::<f64>().unwrap());
    } else if time_array.len() == 2 {
        // minutes
        seconds = seconds + (time_array[0].parse::<f64>().unwrap() * 60.0);
        // seconds
        seconds = seconds + (time_array[1].parse::<f64>().unwrap());
    }
    seconds
}

#[test]
fn test_format_time() {
    let time = format_numeral(3600.0, "00:00:00");
    assert_eq!(time, "1:00:00");
}

#[test]
fn test_unformat_time() {
    let number = unformat_numeral("1:00:00");
    assert_eq!(number, 3600.0);
}
<commit_msg>refactor(numeral.rs): unimplemented functions now uses unimplemented!() macro<commit_after>pub fn format_numeral(n: f64, format: &str) -> String {
    if format.contains(":") {
        format_time(n, format)
    } else {
        format_number(n, format)
    }
}

pub fn unformat_numeral(string: &str) -> f64 {
    if string.contains(":") {
        unformat_time(string)
    } else {
        unformat_number(string)
    }
}

#[allow(unused_variables)]
fn format_number(n: f64, format: &str) -> String {
    unimplemented!()
}

#[allow(unused_variables)]
fn unformat_number(string: &str) -> f64 {
    unimplemented!()
}

#[allow(unused_variables)]
fn format_time(n: f64, format: &str) -> String {
    let hours: f64 = (n/60./60.).floor();
    let minutes = ((n - (hours * 60. * 60.))/60.).floor();
    let seconds = (n - (hours * 60. * 60.) - (minutes * 60.)).round();

    let hours_string = hours.to_string();

    let minutes_string = & if minutes < 10. {
        "0".to_string() + &minutes.to_string()
    } else {
        minutes.to_string()
    };

    let seconds_string = & if seconds < 10. {
        "0".to_string() + &seconds.to_string()
    } else {
        seconds.to_string()
    };

    hours_string + ":" + minutes_string + ":" + seconds_string
}

fn unformat_time(string: &str) -> f64 {
    let split = string.split(":");
    let time_array: Vec<&str> = split.collect();
    let mut seconds = 0.0;

    if time_array.len() == 3 {
        // hours
        seconds = seconds + (time_array[0].parse::<f64>().unwrap() * 60.0 * 60.0);
        // minutes
        seconds = seconds + (time_array[1].parse::<f64>().unwrap() * 60.0);
        // seconds
        seconds = seconds + (time_array[2].parse::<f64>().unwrap());
    } else if time_array.len() == 2 {
        // minutes
        seconds = seconds + (time_array[0].parse::<f64>().unwrap() * 60.0);
        // seconds
        seconds = seconds + (time_array[1].parse::<f64>().unwrap());
    }
    seconds
}

#[test]
fn test_format_time() {
    let time = format_numeral(3600.0, "00:00:00");
    assert_eq!(time, "1:00:00");
}

#[test]
fn test_unformat_time() {
    let number = unformat_numeral("1:00:00");
    assert_eq!(number, 3600.0);
}
<|endoftext|>


<commit_before>//! SMTP request, containing one of several commands, and arguments

// FIXME: Add parsing.

use emailaddress::{EmailAddress, AddrError};
use std::io::{Error as IoError};
use std::fmt::{Display, Formatter, Result as FmtResult};
use std::net::{Ipv4Addr, Ipv6Addr};
use std::str::{FromStr};
use tokio_proto::streaming::pipeline::{Frame};
use util::{XText};

/// Client identifier, as sent in `EHLO`.
#[derive(PartialEq,Eq,Clone,Debug)]
pub enum ClientId {
    Domain(String),
    Ipv4(Ipv4Addr),
    Ipv6(Ipv6Addr),
    Other { tag: String, value: String },
}

impl Display for ClientId {
    fn fmt(&self, f: &mut Formatter) -> FmtResult {
        match *self {
            ClientId::Domain(ref value) => f.write_str(value),
            ClientId::Ipv4(ref value) => write!(f, "{}", value),
            ClientId::Ipv6(ref value) => write!(f, "IPv6:{}", value),
            ClientId::Other { ref tag, ref value } => write!(f, "{}:{}", tag, value),
        }
    }
}

/// A mailbox specified in `MAIL FROM` or `RCPT TO`.
#[derive(PartialEq,Clone,Debug)]
pub struct Mailbox(pub Option<EmailAddress>);

impl From<EmailAddress> for Mailbox {
    fn from(addr: EmailAddress) -> Self {
        Mailbox(Some(addr))
    }
}

impl FromStr for Mailbox {
    type Err = AddrError;

    fn from_str(string: &str) -> Result<Mailbox, AddrError> {
        if string.is_empty() {
            Ok(Mailbox(None))
        } else {
            Ok(EmailAddress::new(string)?.into())
        }
    }
}

impl Display for Mailbox {
    fn fmt(&self, f: &mut Formatter) -> FmtResult {
        match self.0 {
            Some(ref email) => write!(f, "<{}>", email),
            None => f.write_str("<>"),
        }
    }
}

/// A `MAIL FROM` extension parameter.
#[derive(PartialEq,Eq,Clone,Debug)]
pub enum MailParam {
    Body(MailBodyParam),
    Size(usize),
    Other { keyword: String, value: Option<String> },
}

impl Display for MailParam {
    fn fmt(&self, f: &mut Formatter) -> FmtResult {
        match *self {
            MailParam::Body(ref value) => write!(f, "BODY={}", value),
            MailParam::Size(size) => write!(f, "SIZE={}", size),
            MailParam::Other { ref keyword, value: Some(ref value) } => {
                write!(f, "{}={}", keyword, XText(value))
            },
            MailParam::Other { ref keyword, value: None } => {
                f.write_str(keyword)
            },
        }
    }
}

/// Values for the `BODY` parameter to `MAIL FROM`.
#[derive(PartialEq,Eq,Clone,Debug)]
pub enum MailBodyParam {
    SevenBit,
    EightBitMime,
}

impl Display for MailBodyParam {
    fn fmt(&self, f: &mut Formatter) -> FmtResult {
        match *self {
            MailBodyParam::SevenBit => f.write_str("7BIT"),
            MailBodyParam::EightBitMime => f.write_str("8BITMIME"),
        }
    }
}

/// A `RCPT TO` extension parameter.
#[derive(PartialEq,Eq,Clone,Debug)]
pub enum RcptParam {
    Other { keyword: String, value: Option<String> },
}

impl Display for RcptParam {
    fn fmt(&self, f: &mut Formatter) -> FmtResult {
        match *self {
            RcptParam::Other { ref keyword, value: Some(ref value) } => {
                write!(f, "{}={}", keyword, XText(value))
            },
            RcptParam::Other { ref keyword, value: None } => {
                f.write_str(keyword)
            },
        }
    }
}

/// A complete SMTP request.
#[derive(PartialEq,Clone,Debug)]
pub enum Request {
    Ehlo(ClientId),
    StartTls,
    Mail { from: Mailbox, params: Vec<MailParam> },
    Rcpt { to: Mailbox, params: Vec<RcptParam> },
    Data,
    Quit,
}

impl Display for Request {
    fn fmt(&self, f: &mut Formatter) -> FmtResult {
        match *self {
            Request::Ehlo(ref id) => write!(f, "EHLO {}\r\n", id),
            Request::StartTls => write!(f, "STARTTLS\r\n"),
            Request::Mail { ref from, ref params } => {
                write!(f, "MAIL FROM:{}", from)?;
                for param in params {
                    write!(f, " {}", param)?;
                }
                f.write_str("\r\n")
            },
            Request::Rcpt { ref to, ref params } => {
                write!(f, "RCPT TO:{}", to)?;
                for param in params {
                    write!(f, " {}", param)?;
                }
                f.write_str("\r\n")
            },
            Request::Data => {
                f.write_str("DATA\r\n")
            },
            Request::Quit => {
                f.write_str("QUIT\r\n")
            },
        }
    }
}

impl From<Request> for Frame<Request, Vec<u8>, IoError> {
    fn from(request: Request) -> Self {
        let has_body = request == Request::Data;
        Frame::Message {
            message: request,
            body: has_body,
        }
    }
}

#[cfg(test)]
mod tests {
    use ::{ClientId, MailBodyParam, MailParam, RcptParam, Request};

    #[test]
    fn test() {
        for (input, expect) in vec![
            (
                Request::Ehlo(
                    ClientId::Domain("foobar.example".to_string())
                ),
                "EHLO foobar.example\r\n",
            ),
            (
                Request::Ehlo(
                    ClientId::Ipv4("127.0.0.1".parse().unwrap())
                ),
                "EHLO 127.0.0.1\r\n",
            ),
            (
                Request::StartTls,
                "STARTTLS\r\n",
            ),
            (
                Request::Mail {
                    from: "".parse().unwrap(),
                    params: vec![],
                },
                "MAIL FROM:<>\r\n",
            ),
            (
                Request::Mail {
                    from: "".parse().unwrap(),
                    params: vec![
                        MailParam::Body(MailBodyParam::EightBitMime),
                        MailParam::Size(1024),
                    ],
                },
                "MAIL FROM:<> BODY=8BITMIME SIZE=1024\r\n",
            ),
            (
                Request::Mail {
                    from: "john@example.test".parse().unwrap(),
                    params: vec![],
                },
                "MAIL FROM:<john@example.test>\r\n",
            ),
            (
                Request::Rcpt {
                    to: "".parse().unwrap(),
                    params: vec![],
                },
                "RCPT TO:<>\r\n",
            ),
            (
                Request::Rcpt {
                    to: "".parse().unwrap(),
                    params: vec![
                        RcptParam::Other { keyword: "FOOBAR".to_string(), value: None },
                    ],
                },
                "RCPT TO:<> FOOBAR\r\n",
            ),
            (
                Request::Rcpt {
                    to: "alice@example.test".parse().unwrap(),
                    params: vec![],
                },
                "RCPT TO:<alice@example.test>\r\n",
            ),
            (
                Request::Data,
                "DATA\r\n",
            ),
            (
                Request::Quit,
                "QUIT\r\n",
            ),
        ] {
            assert_eq!(input.to_string(), expect);
        }
    }
}
<commit_msg>Expand custom parameter tests.<commit_after>//! SMTP request, containing one of several commands, and arguments

// FIXME: Add parsing.

use emailaddress::{EmailAddress, AddrError};
use std::io::{Error as IoError};
use std::fmt::{Display, Formatter, Result as FmtResult};
use std::net::{Ipv4Addr, Ipv6Addr};
use std::str::{FromStr};
use tokio_proto::streaming::pipeline::{Frame};
use util::{XText};

/// Client identifier, as sent in `EHLO`.
#[derive(PartialEq,Eq,Clone,Debug)]
pub enum ClientId {
    Domain(String),
    Ipv4(Ipv4Addr),
    Ipv6(Ipv6Addr),
    Other { tag: String, value: String },
}

impl Display for ClientId {
    fn fmt(&self, f: &mut Formatter) -> FmtResult {
        match *self {
            ClientId::Domain(ref value) => f.write_str(value),
            ClientId::Ipv4(ref value) => write!(f, "{}", value),
            ClientId::Ipv6(ref value) => write!(f, "IPv6:{}", value),
            ClientId::Other { ref tag, ref value } => write!(f, "{}:{}", tag, value),
        }
    }
}

/// A mailbox specified in `MAIL FROM` or `RCPT TO`.
#[derive(PartialEq,Clone,Debug)]
pub struct Mailbox(pub Option<EmailAddress>);

impl From<EmailAddress> for Mailbox {
    fn from(addr: EmailAddress) -> Self {
        Mailbox(Some(addr))
    }
}

impl FromStr for Mailbox {
    type Err = AddrError;

    fn from_str(string: &str) -> Result<Mailbox, AddrError> {
        if string.is_empty() {
            Ok(Mailbox(None))
        } else {
            Ok(EmailAddress::new(string)?.into())
        }
    }
}

impl Display for Mailbox {
    fn fmt(&self, f: &mut Formatter) -> FmtResult {
        match self.0 {
            Some(ref email) => write!(f, "<{}>", email),
            None => f.write_str("<>"),
        }
    }
}

/// A `MAIL FROM` extension parameter.
#[derive(PartialEq,Eq,Clone,Debug)]
pub enum MailParam {
    Body(MailBodyParam),
    Size(usize),
    Other { keyword: String, value: Option<String> },
}

impl Display for MailParam {
    fn fmt(&self, f: &mut Formatter) -> FmtResult {
        match *self {
            MailParam::Body(ref value) => write!(f, "BODY={}", value),
            MailParam::Size(size) => write!(f, "SIZE={}", size),
            MailParam::Other { ref keyword, value: Some(ref value) } => {
                write!(f, "{}={}", keyword, XText(value))
            },
            MailParam::Other { ref keyword, value: None } => {
                f.write_str(keyword)
            },
        }
    }
}

/// Values for the `BODY` parameter to `MAIL FROM`.
#[derive(PartialEq,Eq,Clone,Debug)]
pub enum MailBodyParam {
    SevenBit,
    EightBitMime,
}

impl Display for MailBodyParam {
    fn fmt(&self, f: &mut Formatter) -> FmtResult {
        match *self {
            MailBodyParam::SevenBit => f.write_str("7BIT"),
            MailBodyParam::EightBitMime => f.write_str("8BITMIME"),
        }
    }
}

/// A `RCPT TO` extension parameter.
#[derive(PartialEq,Eq,Clone,Debug)]
pub enum RcptParam {
    Other { keyword: String, value: Option<String> },
}

impl Display for RcptParam {
    fn fmt(&self, f: &mut Formatter) -> FmtResult {
        match *self {
            RcptParam::Other { ref keyword, value: Some(ref value) } => {
                write!(f, "{}={}", keyword, XText(value))
            },
            RcptParam::Other { ref keyword, value: None } => {
                f.write_str(keyword)
            },
        }
    }
}

/// A complete SMTP request.
#[derive(PartialEq,Clone,Debug)]
pub enum Request {
    Ehlo(ClientId),
    StartTls,
    Mail { from: Mailbox, params: Vec<MailParam> },
    Rcpt { to: Mailbox, params: Vec<RcptParam> },
    Data,
    Quit,
}

impl Display for Request {
    fn fmt(&self, f: &mut Formatter) -> FmtResult {
        match *self {
            Request::Ehlo(ref id) => write!(f, "EHLO {}\r\n", id),
            Request::StartTls => write!(f, "STARTTLS\r\n"),
            Request::Mail { ref from, ref params } => {
                write!(f, "MAIL FROM:{}", from)?;
                for param in params {
                    write!(f, " {}", param)?;
                }
                f.write_str("\r\n")
            },
            Request::Rcpt { ref to, ref params } => {
                write!(f, "RCPT TO:{}", to)?;
                for param in params {
                    write!(f, " {}", param)?;
                }
                f.write_str("\r\n")
            },
            Request::Data => {
                f.write_str("DATA\r\n")
            },
            Request::Quit => {
                f.write_str("QUIT\r\n")
            },
        }
    }
}

impl From<Request> for Frame<Request, Vec<u8>, IoError> {
    fn from(request: Request) -> Self {
        let has_body = request == Request::Data;
        Frame::Message {
            message: request,
            body: has_body,
        }
    }
}

#[cfg(test)]
mod tests {
    use ::{ClientId, MailBodyParam, MailParam, RcptParam, Request};

    #[test]
    fn test() {
        for (input, expect) in vec![
            (
                Request::Ehlo(
                    ClientId::Domain("foobar.example".to_string())
                ),
                "EHLO foobar.example\r\n",
            ),
            (
                Request::Ehlo(
                    ClientId::Ipv4("127.0.0.1".parse().unwrap())
                ),
                "EHLO 127.0.0.1\r\n",
            ),
            (
                Request::StartTls,
                "STARTTLS\r\n",
            ),
            (
                Request::Mail {
                    from: "".parse().unwrap(),
                    params: vec![],
                },
                "MAIL FROM:<>\r\n",
            ),
            (
                Request::Mail {
                    from: "".parse().unwrap(),
                    params: vec![
                        MailParam::Body(MailBodyParam::EightBitMime),
                        MailParam::Size(1024),
                        MailParam::Other {
                            keyword: "X-FLAG".to_string(),
                            value: None,
                        },
                        MailParam::Other {
                            keyword: "X-VALUE".to_string(),
                            value: Some("+".to_string()),
                        },
                    ],
                },
                "MAIL FROM:<> BODY=8BITMIME SIZE=1024 X-FLAG X-VALUE=+2B\r\n",
            ),
            (
                Request::Mail {
                    from: "john@example.test".parse().unwrap(),
                    params: vec![],
                },
                "MAIL FROM:<john@example.test>\r\n",
            ),
            (
                Request::Rcpt {
                    to: "".parse().unwrap(),
                    params: vec![],
                },
                "RCPT TO:<>\r\n",
            ),
            (
                Request::Rcpt {
                    to: "".parse().unwrap(),
                    params: vec![
                        RcptParam::Other {
                            keyword: "X-FLAG".to_string(),
                            value: None,
                        },
                        RcptParam::Other {
                            keyword: "X-VALUE".to_string(),
                            value: Some("+".to_string()),
                        },
                    ],
                },
                "RCPT TO:<> X-FLAG X-VALUE=+2B\r\n",
            ),
            (
                Request::Rcpt {
                    to: "alice@example.test".parse().unwrap(),
                    params: vec![],
                },
                "RCPT TO:<alice@example.test>\r\n",
            ),
            (
                Request::Data,
                "DATA\r\n",
            ),
            (
                Request::Quit,
                "QUIT\r\n",
            ),
        ] {
            assert_eq!(input.to_string(), expect);
        }
    }
}
<|endoftext|>


<commit_before>use command::Command;
use vm::VM;

pub struct Program {
    instructions: Vec<Command>,
    instruction_pointer: Option<usize>,
    current_depth: u64,
    pub status: ProgramStatus,
}

#[derive(Clone, Copy, PartialEq, Debug)]
pub enum ProgramStatus {
    Normal,
    Seeking(u64),
}

#[derive(Clone, Copy, Debug, PartialEq)]
pub struct ProgramPatch {
    pub instruction_pointer: usize,
    pub current_depth: u64,
    pub status: ProgramStatus,
}

fn find_by<T, F>(list: &[T], start: Option<usize>, mut predicate: F) -> Option<usize>
where
    F: FnMut(&T) -> bool,
{
    let start = start.unwrap_or(0);
    for (offset, element) in list.iter().enumerate() {
        if predicate(element) {
            return Some(start + offset);
        }
    }

    return None;
}

impl Program {
    pub fn new() -> Program {
        Program {
            instructions: Vec::new(),
            instruction_pointer: None,
            current_depth: 0,
            status: ProgramStatus::Normal,
        }
    }

    pub fn append(&mut self, instructions: &[Command]) {
        self.instructions.extend(instructions.iter().cloned());
        if self.instruction_pointer.is_none() {
            self.instruction_pointer = Some(0);
        }
    }

    pub fn execute(&mut self, vm: &mut VM) {
        match (self.instruction_pointer, self.status) {
            (Some(index), ProgramStatus::Seeking(goal)) if index < self.instructions.len() => {
                let patch = Program::attempt_forward_jump(
                    &self.instructions,
                    index,
                    goal,
                    self.current_depth,
                );
                println!("SEEKING BRANCH\n{:?}\n", patch);
                return self.update(patch).execute(vm);
            }

            (Some(start), ProgramStatus::Normal) if start < self.instructions.len() => {
                for index in start..self.instructions.len() {
                    match self.instructions[index] {
                        Command::JumpBackward => {
                            if vm.cells[vm.data_pointer] == 0 {
                                self.current_depth -= 1;
                                continue;
                            }

                            let patch = Program::backward_jump(
                                &self.instructions,
                                index,
                                self.current_depth,
                            );
                            println!("BACKWARD BRANCH\n{:?}\n", patch);
                            return self.update(patch).execute(vm);
                        }

                        Command::JumpForward => {
                            if vm.cells[vm.data_pointer] != 0 {
                                self.current_depth += 1;
                                continue;
                            }

                            let patch = Program::attempt_forward_jump(
                                &self.instructions,
                                index,
                                self.current_depth,
                                self.current_depth,
                            );
                            println!("FORWARD BRANCH\n{:?}\n", patch);
                            return self.update(patch).execute(vm);
                        }

                        command if start < self.instructions.len() => vm.apply(command),

                        _ => {}
                    }
                }
            }

            _ => {}
        }
    }

    fn update(&mut self, patch: ProgramPatch) -> &mut Self {
        self.instruction_pointer = Some(patch.instruction_pointer);
        self.current_depth = patch.current_depth;
        self.status = patch.status;
        return self;
    }

    fn backward_jump(commands: &Vec<Command>, start: usize, depth: u64) -> ProgramPatch {
        let goal_depth = depth - 1;
        let mut current_depth = depth;
        for index in (0..start).rev() {
            match commands[index] {
                Command::JumpBackward => current_depth += 1,
                Command::JumpForward => current_depth -= 1,
                _ => {}
            }
            if goal_depth == current_depth {
                return ProgramPatch {
                    instruction_pointer: index,
                    status: ProgramStatus::Normal,
                    current_depth,
                };
            }
        }
        panic!("No opening bracket found?");
    }

    fn attempt_forward_jump(
        commands: &Vec<Command>,
        index: usize,
        goal_depth: u64,
        mut current_depth: u64,
    ) -> ProgramPatch {
        let index = find_by(commands, Some(index), |command| {
            match command {
                &Command::JumpBackward => current_depth -= 1,
                &Command::JumpForward => current_depth += 1,
                _ => {}
            };
            return current_depth == goal_depth;
        });

        return match index {
            Some(index) => ProgramPatch {
                instruction_pointer: index + 1,
                status: ProgramStatus::Normal,
                current_depth,
            },

            None => ProgramPatch {
                instruction_pointer: commands.len(),
                status: ProgramStatus::Seeking(goal_depth),
                current_depth,
            },
        };
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn attempt_forward_jump() {
        let commands = vec![Command::JumpForward, Command::JumpForward, Command::JumpBackward];
        let index = 0;
        let goal_depth = 0;
        let current_depth = 0;
        let actual = Program::attempt_forward_jump(&commands, index, goal_depth, current_depth);
        assert_eq!(actual, ProgramPatch {
            instruction_pointer: 3,
            current_depth: 1,
            status: ProgramStatus::Seeking(0),
        })
    }
}
<commit_msg>Fix off by one errors<commit_after>use command::Command;
use vm::VM;

pub struct Program {
    instructions: Vec<Command>,
    instruction_pointer: Option<usize>,
    current_depth: u64,
    pub status: ProgramStatus,
}

#[derive(Clone, Copy, PartialEq, Debug)]
pub enum ProgramStatus {
    Normal,
    Seeking(u64),
}

#[derive(Clone, Copy, Debug, PartialEq)]
pub struct ProgramPatch {
    pub instruction_pointer: usize,
    pub current_depth: u64,
    pub status: ProgramStatus,
}

fn find_by<T, F>(list: &[T], start: Option<usize>, mut predicate: F) -> Option<usize>
where
    F: FnMut(&T) -> bool,
{
    let start = start.unwrap_or(0);
    for (offset, element) in list.iter().enumerate() {
        if predicate(element) {
            return Some(start + offset);
        }
    }

    return None;
}

impl Program {
    pub fn new() -> Program {
        Program {
            instructions: Vec::new(),
            instruction_pointer: None,
            current_depth: 0,
            status: ProgramStatus::Normal,
        }
    }

    pub fn append(&mut self, instructions: &[Command]) {
        self.instructions.extend(instructions.iter().cloned());
        if self.instruction_pointer.is_none() {
            self.instruction_pointer = Some(0);
        }
    }

    pub fn execute(&mut self, vm: &mut VM) {
        match (self.instruction_pointer, self.status) {
            (Some(index), ProgramStatus::Seeking(goal)) if index < self.instructions.len() => {
                let patch = Program::attempt_forward_jump(
                    &self.instructions,
                    index,
                    goal,
                    self.current_depth,
                );
                println!("SEEKING BRANCH\n{:?}\n", patch);
                return self.update(patch).execute(vm);
            }

            (Some(start), ProgramStatus::Normal) if start < self.instructions.len() => {
                for index in start..self.instructions.len() {
                    match self.instructions[index] {
                        Command::JumpBackward => {
                            if vm.cells[vm.data_pointer] == 0 {
                                self.current_depth -= 1;
                                continue;
                            }

                            let patch = Program::backward_jump(
                                &self.instructions,
                                index,
                                self.current_depth,
                            );
                            println!("BACKWARD BRANCH\n{:?}\n", patch);
                            return self.update(patch).execute(vm);
                        }

                        Command::JumpForward => {
                            if vm.cells[vm.data_pointer] != 0 {
                                self.current_depth += 1;
                                continue;
                            }

                            let patch = Program::attempt_forward_jump(
                                &self.instructions,
                                index,
                                self.current_depth,
                                self.current_depth,
                            );
                            println!("FORWARD BRANCH\n{:?}\n", patch);
                            return self.update(patch).execute(vm);
                        }

                        command if start < self.instructions.len() => vm.apply(command),

                        _ => {}
                    }
                }
            }

            _ => {}
        }
    }

    fn update(&mut self, patch: ProgramPatch) -> &mut Self {
        self.instruction_pointer = Some(patch.instruction_pointer);
        self.current_depth = patch.current_depth;
        self.status = patch.status;
        return self;
    }

    fn backward_jump(commands: &Vec<Command>, start: usize, depth: u64) -> ProgramPatch {
        let goal_depth = depth - 1;
        let mut current_depth = depth;
        for index in (0..start).rev() {
            match commands[index] {
                Command::JumpBackward => current_depth += 1,
                Command::JumpForward => current_depth -= 1,
                _ => {}
            }
            if goal_depth == current_depth {
                return ProgramPatch {
                    instruction_pointer: index + 1,
                    status: ProgramStatus::Normal,
                    current_depth: current_depth + 1,
                };
            }
        }
        panic!("No opening bracket found?");
    }

    fn attempt_forward_jump(
        commands: &Vec<Command>,
        index: usize,
        goal_depth: u64,
        mut current_depth: u64,
    ) -> ProgramPatch {
        let index = find_by(commands, Some(index), |command| {
            match command {
                &Command::JumpBackward => current_depth -= 1,
                &Command::JumpForward => current_depth += 1,
                _ => {}
            };
            return current_depth == goal_depth;
        });

        return match index {
            Some(index) => ProgramPatch {
                instruction_pointer: index + 1,
                status: ProgramStatus::Normal,
                current_depth,
            },

            None => ProgramPatch {
                instruction_pointer: commands.len(),
                status: ProgramStatus::Seeking(goal_depth),
                current_depth,
            },
        };
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn attempt_forward_jump() {
        let commands = vec![Command::JumpForward, Command::JumpForward, Command::JumpBackward];
        let index = 0;
        let goal_depth = 0;
        let current_depth = 0;
        let actual = Program::attempt_forward_jump(&commands, index, goal_depth, current_depth);
        assert_eq!(actual, ProgramPatch {
            instruction_pointer: 3,
            current_depth: 1,
            status: ProgramStatus::Seeking(0),
        })
    }
}
<|endoftext|>


<commit_before>use itertools::{multipeek, MultiPeek};
use std::str;

#[derive(Debug, PartialEq)]
pub enum Token {
    LeftParen,
    RightParen,
    LeftBrace,
    RightBrace,
    Comma,
    Dot,
    Minus,
    Plus,
    Semicolon,
    Slash,
    Star,
    Bang,
    BangEqual,
    Equal,
    EqualEqual,
    Greater,
    GreaterEqual,
    Less,
    LessEqual,
    Identifier(String),
    StringLiteral(String),
    NumberLiteral(f64),
    // Keywords
    And,
    Class,
    Else,
    False,
    Fun,
    For,
    If,
    Nil,
    Or,
    Print,
    Return,
    Super,
    This,
    True,
    Var,
    While,

    Eof,
    // The book doesn't have tokens for comments and
    // whitespaces. Introducing them the scanner can
    // deal with them uniformly and in a more
    // functional way.
    Comment,
    Whitespace,
}

#[derive(Debug,Clone,Copy)]
/// Represents a position in the source file.
/// Both line and column are represented by a 1-based
/// index, since this struct exists only to provide
/// context to the user.
struct Position {
    line: usize,
    column: usize,
}

impl Position {
    fn initial() -> Position {
        Position {
            line: 1,
            column: 1,
        }
    }

    fn increment_column(&mut self) -> () {
        self.column += 1;
    }

    fn increment_line(&mut self) -> () {
        self.line += 1;
        self.column = 1;
    }
}

#[derive(Debug)]
pub struct TokenWithContext {
    token: Token,
    // Takes a copy. Tokens can outlive the file they came from
    lexeme: String,
    /// Position of the first character of the token
    position: Position,
}

struct Scanner<'a> {
    current_position: Position,
    current_lexeme: String,
    source: MultiPeek<str::Chars<'a>>,
}

fn is_digit(c: char) -> bool {
    c >= '0' && c <= '9'
}

fn is_alpha(c: char) -> bool {
    (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_'
}

fn is_alphanumeric(c: char) -> bool {
    is_digit(c) || is_alpha(c)
}

fn is_whitespace(c: char) -> bool {
    match c {
        ' ' => true,
        '\r' => true,
        '\t' => true,
        '\n' => true,
        _ => false,
    }
}

impl<'a> Scanner<'a> {
    fn initialize(source: &String) -> Scanner {
        Scanner {
            current_position: Position::initial(),
            current_lexeme: "".into(),
            source: multipeek(source.chars()),
        }
    }

    fn peek_check(&mut self, check: &Fn(char) -> bool) -> bool {
        self.source.reset_peek();
        match self.source.peek() {
            Some(&c) => check(c),
            None => false,
        }
    }

    fn peek_check2(&mut self, check1: &Fn(char) -> bool, check2: &Fn(char) -> bool) -> bool {
        self.source.reset_peek();
        match self.source.peek() {
            Some(&p1) => {
                match self.source.peek() {
                    Some(&p2) => check1(p1) && check2(p2),
                    None => false,
                }
            }
            None => false,
        }
    }

    fn advance(&mut self) -> Option<char> {
        let next = self.source.next();
        match next {
            Some(c) => {
                self.current_lexeme.push(c);
                if c == '\n' {
                    self.current_position.increment_line();
                } else {
                    self.current_position.increment_column();;
                }
            }
            None => (),
        }
        next
    }

    fn advance_if_match(&mut self, expected: char) -> bool {
        if self.peek_check(&|c| c == expected) {
            let _ = self.advance();
            true
        } else {
            false
        }
    }

    fn add_context(&mut self, token: Token, initial_position: Position) -> TokenWithContext {
        let result = TokenWithContext {
            token: token,
            lexeme: self.current_lexeme.clone(),
            position: initial_position,
        };
        self.current_lexeme = "".into();
        result
    }

    fn string(&mut self) -> Result<Token, String> {
        let initial_line = self.current_position.line;
        self.source.reset_peek();
        while self.peek_check(&|c| c != '"') {
            self.advance();
        }
        if !self.advance_if_match('"') {
            return Err(format!("Unterminated string at line {}", initial_line));
        }
        let literal_length = self.current_lexeme.len() - 2;
        // Trims delimiters
        let literal = self.current_lexeme.chars().skip(1).take(literal_length).collect();
        Ok(Token::StringLiteral(literal))
    }

    fn number(&mut self) -> Token {
        while self.peek_check(&is_digit) {
            self.advance();
        }
        if self.peek_check2(&|p1| p1 == '.', &is_digit) {
            self.advance(); // Consume the .
            while self.peek_check(&is_digit) {
                self.advance();
            }
        }
        let value = self.current_lexeme.parse::<f64>().unwrap();
        Token::NumberLiteral(value)
    }

    fn identifier(&mut self) -> Token {
        while self.peek_check(&is_alphanumeric) {
            self.advance();
        }
        match self.current_lexeme.as_ref() {
            "and" => Token::And,
            "class" => Token::Class,
            "else" => Token::Else,
            "false" => Token::False,
            "for" => Token::For,
            "fun" => Token::Fun,
            "if" => Token::If,
            "nil" => Token::Nil,
            "or" => Token::Or,
            "print" => Token::Print,
            "return" => Token::Return,
            "super" => Token::Super,
            "this" => Token::This,
            "true" => Token::True,
            "var" => Token::Var,
            "while" => Token::While,
            identifier => Token::Identifier(identifier.into()),
        }

    }

    fn scan_next(&mut self) -> Result<TokenWithContext, String> {
        let initial_position = self.current_position;
        // Check if there is something left to iterate on.
        // Early return if we're done.
        let next_char = match self.advance() {
            Some(c) => c,
            None => {
                return Ok(TokenWithContext {
                    token: Token::Eof,
                    // Not very meaningful. Is Eof needed at all?
                    lexeme: "".into(),
                    position: initial_position,
                });
            }
        };
        let token = match next_char {
            '(' => Token::LeftParen,
            ')' => Token::RightParen,
            '{' => Token::LeftBrace,
            '}' => Token::RightBrace,
            ',' => Token::Comma,
            '.' => Token::Dot,
            '-' => Token::Minus,
            '+' => Token::Plus,
            ';' => Token::Semicolon,
            '*' => Token::Star,
            '!' => {
                if self.advance_if_match('=') {
                    Token::BangEqual
                } else {
                    Token::Bang
                }
            }
            '=' => {
                if self.advance_if_match('=') {
                    Token::EqualEqual
                } else {
                    Token::Equal
                }
            }
            '<' => {
                if self.advance_if_match('=') {
                    Token::LessEqual
                } else {
                    Token::Less
                }
            }
            '>' => {
                if self.advance_if_match('=') {
                    Token::GreaterEqual
                } else {
                    Token::Greater
                }
            }
            '/' => {
                if self.advance_if_match('/') {
                    // Comments go on till the end of the line
                    while self.peek_check(&|c| c != '\n') {
                        self.advance();
                    }
                    Token::Comment
                } else {
                    Token::Slash
                }
            }
            '"' => try!(self.string()),
            c if is_whitespace(c) => Token::Whitespace,
            c if is_digit(c) => self.number(),
            c if is_alpha(c) => self.identifier(),
            c => {
                return Err(format!("Unexpected character {} at line {}, column {}",
                                   c,
                                   self.current_position.line,
                                   self.current_position.column - 1));
            }
        };
        Ok(self.add_context(token, initial_position))
    }
}

pub fn scan(source: &String) -> Result<Vec<TokenWithContext>, String> {
    let mut scanner = Scanner::initialize(source);
    let mut tokens = Vec::new();
    let mut eof = false;
    while !eof {
        let token_with_context = try!(scanner.scan_next());
        match token_with_context.token {
            // Ignoring tokens we don't care about
            Token::Comment => {}
            Token::Whitespace => {}
            Token::Eof => {
                eof = true;
                tokens.push(token_with_context);
            }
            _ => tokens.push(token_with_context),
        }
    }
    Ok(tokens)
}

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

    #[test]
    fn single_token() {
        let tokens = scan(&"+".into()).unwrap();
        assert_eq!(tokens[0].token, Token::Plus);
    }

    #[test]
    fn expression() {
        let tokens = scan(&"1+2".into()).unwrap();
        assert_eq!(tokens[0].token, Token::NumberLiteral(1.0f64));
        assert_eq!(tokens[1].token, Token::Plus);
        assert_eq!(tokens[2].token, Token::NumberLiteral(2.0f64));
        assert_eq!(tokens[3].token, Token::Eof);
    }

    #[test]
    fn expression_with_whitespaces() {
        let tokens = scan(&"1 + 2".into()).unwrap();
        assert_eq!(tokens[0].token, Token::NumberLiteral(1.0f64));
        assert_eq!(tokens[1].token, Token::Plus);
        assert_eq!(tokens[2].token, Token::NumberLiteral(2.0f64));
        assert_eq!(tokens[3].token, Token::Eof);
    }

    #[test]
    fn assignement_with_comment() {
        let tokens = scan(&"var a = 1.0; // A comment".into()).unwrap();
        assert_eq!(tokens[0].token, Token::Var);
        assert_eq!(tokens[1].token, Token::Identifier("a".into()));
        assert_eq!(tokens[2].token, Token::Equal);
        assert_eq!(tokens[3].token, Token::NumberLiteral(1.0f64));
        assert_eq!(tokens[4].token, Token::Semicolon);
        assert_eq!(tokens[5].token, Token::Eof);
    }

    #[test]
    fn multiline_statements() {
        let tokens = scan(&r#"var a = 1.0;
                              var b = "Hello";"#
                .into())
            .unwrap();
        assert_eq!(tokens[0].token, Token::Var);
        assert_eq!(tokens[1].token, Token::Identifier("a".into()));
        assert_eq!(tokens[2].token, Token::Equal);
        assert_eq!(tokens[3].token, Token::NumberLiteral(1.0f64));
        assert_eq!(tokens[4].token, Token::Semicolon);
        assert_eq!(tokens[5].token, Token::Var);
        assert_eq!(tokens[6].token, Token::Identifier("b".into()));
        assert_eq!(tokens[7].token, Token::Equal);
        assert_eq!(tokens[8].token, Token::StringLiteral("Hello".into()));
        assert_eq!(tokens[9].token, Token::Semicolon);
        assert_eq!(tokens[10].token, Token::Eof);
        assert_eq!(tokens[1].position.line, 1);
        assert_eq!(tokens[1].position.column, 5);
        assert_eq!(tokens[9].position.line, 2);
        assert_eq!(tokens[9].position.column, 46); // There are 30 spaces before the text!
    }
}
<commit_msg>Factored out advance_while<commit_after>use itertools::{multipeek, MultiPeek};
use std::str;

#[derive(Debug, PartialEq)]
pub enum Token {
    LeftParen,
    RightParen,
    LeftBrace,
    RightBrace,
    Comma,
    Dot,
    Minus,
    Plus,
    Semicolon,
    Slash,
    Star,
    Bang,
    BangEqual,
    Equal,
    EqualEqual,
    Greater,
    GreaterEqual,
    Less,
    LessEqual,
    Identifier(String),
    StringLiteral(String),
    NumberLiteral(f64),
    // Keywords
    And,
    Class,
    Else,
    False,
    Fun,
    For,
    If,
    Nil,
    Or,
    Print,
    Return,
    Super,
    This,
    True,
    Var,
    While,

    Eof,
    // The book doesn't have tokens for comments and
    // whitespaces. Introducing them the scanner can
    // deal with them uniformly and in a more
    // functional way.
    Comment,
    Whitespace,
}

#[derive(Debug,Clone,Copy)]
/// Represents a position in the source file.
/// Both line and column are represented by a 1-based
/// index, since this struct exists only to provide
/// context to the user.
struct Position {
    line: usize,
    column: usize,
}

impl Position {
    fn initial() -> Position {
        Position {
            line: 1,
            column: 1,
        }
    }

    fn increment_column(&mut self) -> () {
        self.column += 1;
    }

    fn increment_line(&mut self) -> () {
        self.line += 1;
        self.column = 1;
    }
}

#[derive(Debug)]
pub struct TokenWithContext {
    token: Token,
    // Takes a copy. Tokens can outlive the file they came from
    lexeme: String,
    /// Position of the first character of the token
    position: Position,
}

struct Scanner<'a> {
    current_position: Position,
    current_lexeme: String,
    source: MultiPeek<str::Chars<'a>>,
}

fn is_digit(c: char) -> bool {
    c >= '0' && c <= '9'
}

fn is_alpha(c: char) -> bool {
    (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_'
}

fn is_alphanumeric(c: char) -> bool {
    is_digit(c) || is_alpha(c)
}

fn is_whitespace(c: char) -> bool {
    match c {
        ' ' => true,
        '\r' => true,
        '\t' => true,
        '\n' => true,
        _ => false,
    }
}

impl<'a> Scanner<'a> {
    fn initialize(source: &String) -> Scanner {
        Scanner {
            current_position: Position::initial(),
            current_lexeme: "".into(),
            source: multipeek(source.chars()),
        }
    }

    fn peek_check(&mut self, check: &Fn(char) -> bool) -> bool {
        self.source.reset_peek();
        match self.source.peek() {
            Some(&c) => check(c),
            None => false,
        }
    }

    fn peek_check2(&mut self, check1: &Fn(char) -> bool, check2: &Fn(char) -> bool) -> bool {
        self.source.reset_peek();
        match self.source.peek() {
            Some(&p1) => {
                match self.source.peek() {
                    Some(&p2) => check1(p1) && check2(p2),
                    None => false,
                }
            }
            None => false,
        }
    }

    fn advance(&mut self) -> Option<char> {
        let next = self.source.next();
        match next {
            Some(c) => {
                self.current_lexeme.push(c);
                if c == '\n' {
                    self.current_position.increment_line();
                } else {
                    self.current_position.increment_column();;
                }
            }
            None => (),
        }
        next
    }

    fn advance_if_match(&mut self, expected: char) -> bool {
        if self.peek_check(&|c| c == expected) {
            let _ = self.advance();
            true
        } else {
            false
        }
    }

    fn advance_while(&mut self, condition: &Fn(char) -> bool) -> () {
        while self.peek_check(condition) {
            self.advance();
        }
    }

    fn add_context(&mut self, token: Token, initial_position: Position) -> TokenWithContext {
        let result = TokenWithContext {
            token: token,
            lexeme: self.current_lexeme.clone(),
            position: initial_position,
        };
        self.current_lexeme = "".into();
        result
    }

    fn string(&mut self) -> Result<Token, String> {
        let initial_line = self.current_position.line;
        self.advance_while(&|c| c != '"');
        if !self.advance_if_match('"') {
            return Err(format!("Unterminated string at line {}", initial_line));
        }
        let literal_length = self.current_lexeme.len() - 2;
        // Trims delimiters
        let literal = self.current_lexeme.chars().skip(1).take(literal_length).collect();
        Ok(Token::StringLiteral(literal))
    }

    fn number(&mut self) -> Token {
        self.advance_while(&is_digit);
        if self.peek_check2(&|p1| p1 == '.', &is_digit) {
            self.advance(); // Consume the .
            self.advance_while(&is_digit);
        }
        let value = self.current_lexeme.parse::<f64>().unwrap();
        Token::NumberLiteral(value)
    }

    fn identifier(&mut self) -> Token {
        self.advance_while(&is_alphanumeric);
        match self.current_lexeme.as_ref() {
            "and" => Token::And,
            "class" => Token::Class,
            "else" => Token::Else,
            "false" => Token::False,
            "for" => Token::For,
            "fun" => Token::Fun,
            "if" => Token::If,
            "nil" => Token::Nil,
            "or" => Token::Or,
            "print" => Token::Print,
            "return" => Token::Return,
            "super" => Token::Super,
            "this" => Token::This,
            "true" => Token::True,
            "var" => Token::Var,
            "while" => Token::While,
            identifier => Token::Identifier(identifier.into()),
        }

    }

    fn scan_next(&mut self) -> Result<TokenWithContext, String> {
        let initial_position = self.current_position;
        // Check if there is something left to iterate on.
        // Early return if we're done.
        let next_char = match self.advance() {
            Some(c) => c,
            None => {
                return Ok(TokenWithContext {
                    token: Token::Eof,
                    // Not very meaningful. Is Eof needed at all?
                    lexeme: "".into(),
                    position: initial_position,
                });
            }
        };
        let token = match next_char {
            '(' => Token::LeftParen,
            ')' => Token::RightParen,
            '{' => Token::LeftBrace,
            '}' => Token::RightBrace,
            ',' => Token::Comma,
            '.' => Token::Dot,
            '-' => Token::Minus,
            '+' => Token::Plus,
            ';' => Token::Semicolon,
            '*' => Token::Star,
            '!' => {
                if self.advance_if_match('=') {
                    Token::BangEqual
                } else {
                    Token::Bang
                }
            }
            '=' => {
                if self.advance_if_match('=') {
                    Token::EqualEqual
                } else {
                    Token::Equal
                }
            }
            '<' => {
                if self.advance_if_match('=') {
                    Token::LessEqual
                } else {
                    Token::Less
                }
            }
            '>' => {
                if self.advance_if_match('=') {
                    Token::GreaterEqual
                } else {
                    Token::Greater
                }
            }
            '/' => {
                if self.advance_if_match('/') {
                    // Comments go on till the end of the line
                    self.advance_while(&|c| c != '\n');
                    Token::Comment
                } else {
                    Token::Slash
                }
            }
            '"' => try!(self.string()),
            c if is_whitespace(c) => Token::Whitespace,
            c if is_digit(c) => self.number(),
            c if is_alpha(c) => self.identifier(),
            c => {
                return Err(format!("Unexpected character {} at line {}, column {}",
                                   c,
                                   self.current_position.line,
                                   self.current_position.column - 1));
            }
        };
        Ok(self.add_context(token, initial_position))
    }
}

pub fn scan(source: &String) -> Result<Vec<TokenWithContext>, String> {
    let mut scanner = Scanner::initialize(source);
    let mut tokens = Vec::new();
    let mut eof = false;
    while !eof {
        let token_with_context = try!(scanner.scan_next());
        match token_with_context.token {
            // Ignoring tokens we don't care about
            Token::Comment => {}
            Token::Whitespace => {}
            Token::Eof => {
                eof = true;
                tokens.push(token_with_context);
            }
            _ => tokens.push(token_with_context),
        }
    }
    Ok(tokens)
}

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

    #[test]
    fn single_token() {
        let tokens = scan(&"+".into()).unwrap();
        assert_eq!(tokens[0].token, Token::Plus);
    }

    #[test]
    fn expression() {
        let tokens = scan(&"1+2".into()).unwrap();
        assert_eq!(tokens[0].token, Token::NumberLiteral(1.0f64));
        assert_eq!(tokens[1].token, Token::Plus);
        assert_eq!(tokens[2].token, Token::NumberLiteral(2.0f64));
        assert_eq!(tokens[3].token, Token::Eof);
    }

    #[test]
    fn expression_with_whitespaces() {
        let tokens = scan(&"1 + 2".into()).unwrap();
        assert_eq!(tokens[0].token, Token::NumberLiteral(1.0f64));
        assert_eq!(tokens[1].token, Token::Plus);
        assert_eq!(tokens[2].token, Token::NumberLiteral(2.0f64));
        assert_eq!(tokens[3].token, Token::Eof);
    }

    #[test]
    fn assignement_with_comment() {
        let tokens = scan(&"var a = 1.0; // A comment".into()).unwrap();
        assert_eq!(tokens[0].token, Token::Var);
        assert_eq!(tokens[1].token, Token::Identifier("a".into()));
        assert_eq!(tokens[2].token, Token::Equal);
        assert_eq!(tokens[3].token, Token::NumberLiteral(1.0f64));
        assert_eq!(tokens[4].token, Token::Semicolon);
        assert_eq!(tokens[5].token, Token::Eof);
    }

    #[test]
    fn multiline_statements() {
        let tokens = scan(&r#"var a = 1.0;
                              var b = "Hello";"#
                .into())
            .unwrap();
        assert_eq!(tokens[0].token, Token::Var);
        assert_eq!(tokens[1].token, Token::Identifier("a".into()));
        assert_eq!(tokens[2].token, Token::Equal);
        assert_eq!(tokens[3].token, Token::NumberLiteral(1.0f64));
        assert_eq!(tokens[4].token, Token::Semicolon);
        assert_eq!(tokens[5].token, Token::Var);
        assert_eq!(tokens[6].token, Token::Identifier("b".into()));
        assert_eq!(tokens[7].token, Token::Equal);
        assert_eq!(tokens[8].token, Token::StringLiteral("Hello".into()));
        assert_eq!(tokens[9].token, Token::Semicolon);
        assert_eq!(tokens[10].token, Token::Eof);
        assert_eq!(tokens[1].position.line, 1);
        assert_eq!(tokens[1].position.column, 5);
        assert_eq!(tokens[9].position.line, 2);
        assert_eq!(tokens[9].position.column, 46); // There are 30 spaces before the text!
    }
}
<|endoftext|>


<commit_before>// partially (c) 2016 Google Inc. (Lewin Bormann, lewinb@google.com)
//
// See project root for licensing information.
//

use std::collections::hash_map::DefaultHasher;
use std::error::Error;
use std::fmt;
use std::fs;
use std::hash::{Hash, Hasher};
use std::io;
use std::io::{Read, Write};

use crate::types::Token;
use itertools::Itertools;

/// Implements a specialized storage to set and retrieve `Token` instances.
/// The `scope_hash` represents the signature of the scopes for which the given token
/// should be stored or retrieved.
/// For completeness, the underlying, sorted scopes are provided as well. They might be
/// useful for presentation to the user.
pub trait TokenStorage {
    type Error: 'static + Error + Send + Sync;

    /// If `token` is None, it is invalid or revoked and should be removed from storage.
    /// Otherwise, it should be saved.
    fn set(
        &mut self,
        scope_hash: u64,
        scopes: &Vec<&str>,
        token: Option<Token>,
    ) -> Result<(), Self::Error>;
    /// A `None` result indicates that there is no token for the given scope_hash.
    fn get(&self, scope_hash: u64, scopes: &Vec<&str>) -> Result<Option<Token>, Self::Error>;
}

/// Calculate a hash value describing the scopes, and return a sorted Vec of the scopes.
pub fn hash_scopes<I, T>(scopes: I) -> (u64, Vec<String>)
where
    T: Into<String>,
    I: IntoIterator<Item = T>,
{
    let mut sv: Vec<String> = scopes.into_iter().map(Into::into).collect();
    sv.sort();
    let mut sh = DefaultHasher::new();
    sv.hash(&mut sh);
    (sh.finish(), sv)
}

/// A storage that remembers nothing.
#[derive(Default)]
pub struct NullStorage;

#[derive(Debug)]
pub struct NullError;

impl Error for NullError {
    fn description(&self) -> &str {
        "NULL"
    }
}

impl fmt::Display for NullError {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        "NULL-ERROR".fmt(f)
    }
}

impl TokenStorage for NullStorage {
    type Error = NullError;
    fn set(&mut self, _: u64, _: &Vec<&str>, _: Option<Token>) -> Result<(), NullError> {
        Ok(())
    }
    fn get(&self, _: u64, _: &Vec<&str>) -> Result<Option<Token>, NullError> {
        Ok(None)
    }
}

/// A storage that remembers values for one session only.
#[derive(Debug, Default)]
pub struct MemoryStorage {
    tokens: Vec<JSONToken>,
}

impl MemoryStorage {
    pub fn new() -> MemoryStorage {
        Default::default()
    }
}

impl TokenStorage for MemoryStorage {
    type Error = NullError;

    fn set(
        &mut self,
        scope_hash: u64,
        scopes: &Vec<&str>,
        token: Option<Token>,
    ) -> Result<(), NullError> {
        let matched = self.tokens.iter().find_position(|x| x.hash == scope_hash);
        if let Some((idx, _)) = matched {
            self.tokens.remove(idx);
        }

        match token {
            Some(t) => {
                self.tokens.push(JSONToken {
                    hash: scope_hash,
                    scopes: Some(scopes.iter().map(|x| x.to_string()).collect()),
                    token: t.clone(),
                });
                ()
            }
            None => {}
        };
        Ok(())
    }

    fn get(&self, scope_hash: u64, scopes: &Vec<&str>) -> Result<Option<Token>, NullError> {
        let scopes: Vec<_> = scopes.iter().sorted().unique().collect();

        for t in &self.tokens {
            if let Some(token_scopes) = &t.scopes {
                let matched = token_scopes
                    .iter()
                    .filter(|x| scopes.contains(&&&x[..]))
                    .count();
                if matched >= scopes.len() {
                    return Result::Ok(Some(t.token.clone()));
                }
            } else if scope_hash == t.hash {
                return Result::Ok(Some(t.token.clone()));
            }
        }
        Result::Ok(None)
    }
}

/// A single stored token.
#[derive(Debug, Clone, Serialize, Deserialize)]
struct JSONToken {
    pub hash: u64,
    pub scopes: Option<Vec<String>>,
    pub token: Token,
}

/// List of tokens in a JSON object
#[derive(Serialize, Deserialize)]
struct JSONTokens {
    pub tokens: Vec<JSONToken>,
}

/// Serializes tokens to a JSON file on disk.
#[derive(Default)]
pub struct DiskTokenStorage {
    location: String,
    tokens: Vec<JSONToken>,
}

impl DiskTokenStorage {
    pub fn new<S: AsRef<str>>(location: S) -> Result<DiskTokenStorage, io::Error> {
        let mut dts = DiskTokenStorage {
            location: location.as_ref().to_owned(),
            tokens: Vec::new(),
        };

        // best-effort
        let read_result = dts.load_from_file();

        match read_result {
            Result::Ok(()) => Result::Ok(dts),
            Result::Err(e) => {
                match e.kind() {
                    io::ErrorKind::NotFound => Result::Ok(dts), // File not found; ignore and create new one
                    _ => Result::Err(e),                        // e.g. PermissionDenied
                }
            }
        }
    }

    fn load_from_file(&mut self) -> Result<(), io::Error> {
        let mut f = fs::OpenOptions::new().read(true).open(&self.location)?;
        let mut contents = String::new();

        match f.read_to_string(&mut contents) {
            Result::Err(e) => return Result::Err(e),
            Result::Ok(_sz) => (),
        }

        let tokens: JSONTokens;

        match serde_json::from_str(&contents) {
            Result::Err(e) => return Result::Err(io::Error::new(io::ErrorKind::InvalidData, e)),
            Result::Ok(t) => tokens = t,
        }

        for t in tokens.tokens {
            self.tokens.push(t);
        }
        return Result::Ok(());
    }

    pub fn dump_to_file(&mut self) -> Result<(), io::Error> {
        let mut jsontokens = JSONTokens { tokens: Vec::new() };

        for token in self.tokens.iter() {
            jsontokens.tokens.push((*token).clone());
        }

        let serialized;;

        match serde_json::to_string(&jsontokens) {
            Result::Err(e) => return Result::Err(io::Error::new(io::ErrorKind::InvalidData, e)),
            Result::Ok(s) => serialized = s,
        }

        let mut f = fs::OpenOptions::new()
            .create(true)
            .write(true)
            .truncate(true)
            .open(&self.location)?;
        f.write(serialized.as_ref()).map(|_| ())
    }
}

impl TokenStorage for DiskTokenStorage {
    type Error = io::Error;
    fn set(
        &mut self,
        scope_hash: u64,
        scopes: &Vec<&str>,
        token: Option<Token>,
    ) -> Result<(), Self::Error> {
        let matched = self.tokens.iter().find_position(|x| x.hash == scope_hash);
        if let Some((idx, _)) = matched {
            self.tokens.remove(idx);
        }

        match token {
            None => (),
            Some(t) => {
                self.tokens.push(JSONToken {
                    hash: scope_hash,
                    scopes: Some(scopes.iter().map(|x| x.to_string()).collect()),
                    token: t.clone(),
                });
                ()
            }
        }
        self.dump_to_file()
    }
    fn get(&self, scope_hash: u64, scopes: &Vec<&str>) -> Result<Option<Token>, Self::Error> {
        let scopes: Vec<_> = scopes.iter().sorted().unique().collect();

        for t in &self.tokens {
            if let Some(token_scopes) = &t.scopes {
                let matched = token_scopes
                    .iter()
                    .filter(|x| scopes.contains(&&&x[..]))
                    .count();
                // we may have some of the tokens as denormalized (many namespaces repeated)
                if matched >= scopes.len() {
                    return Result::Ok(Some(t.token.clone()));
                }
            } else if scope_hash == t.hash {
                return Result::Ok(Some(t.token.clone()));
            }
        }
        Result::Ok(None)
    }
}
<commit_msg>cargo fmt --all<commit_after>// partially (c) 2016 Google Inc. (Lewin Bormann, lewinb@google.com)
//
// See project root for licensing information.
//

use std::collections::hash_map::DefaultHasher;
use std::error::Error;
use std::fmt;
use std::fs;
use std::hash::{Hash, Hasher};
use std::io;
use std::io::{Read, Write};

use crate::types::Token;
use itertools::Itertools;

/// Implements a specialized storage to set and retrieve `Token` instances.
/// The `scope_hash` represents the signature of the scopes for which the given token
/// should be stored or retrieved.
/// For completeness, the underlying, sorted scopes are provided as well. They might be
/// useful for presentation to the user.
pub trait TokenStorage {
    type Error: 'static + Error + Send + Sync;

    /// If `token` is None, it is invalid or revoked and should be removed from storage.
    /// Otherwise, it should be saved.
    fn set(
        &mut self,
        scope_hash: u64,
        scopes: &Vec<&str>,
        token: Option<Token>,
    ) -> Result<(), Self::Error>;
    /// A `None` result indicates that there is no token for the given scope_hash.
    fn get(&self, scope_hash: u64, scopes: &Vec<&str>) -> Result<Option<Token>, Self::Error>;
}

/// Calculate a hash value describing the scopes, and return a sorted Vec of the scopes.
pub fn hash_scopes<I, T>(scopes: I) -> (u64, Vec<String>)
where
    T: Into<String>,
    I: IntoIterator<Item = T>,
{
    let mut sv: Vec<String> = scopes.into_iter().map(Into::into).collect();
    sv.sort();
    let mut sh = DefaultHasher::new();
    sv.hash(&mut sh);
    (sh.finish(), sv)
}

/// A storage that remembers nothing.
#[derive(Default)]
pub struct NullStorage;

#[derive(Debug)]
pub struct NullError;

impl Error for NullError {
    fn description(&self) -> &str {
        "NULL"
    }
}

impl fmt::Display for NullError {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        "NULL-ERROR".fmt(f)
    }
}

impl TokenStorage for NullStorage {
    type Error = NullError;
    fn set(&mut self, _: u64, _: &Vec<&str>, _: Option<Token>) -> Result<(), NullError> {
        Ok(())
    }
    fn get(&self, _: u64, _: &Vec<&str>) -> Result<Option<Token>, NullError> {
        Ok(None)
    }
}

/// A storage that remembers values for one session only.
#[derive(Debug, Default)]
pub struct MemoryStorage {
    tokens: Vec<JSONToken>,
}

impl MemoryStorage {
    pub fn new() -> MemoryStorage {
        Default::default()
    }
}

impl TokenStorage for MemoryStorage {
    type Error = NullError;

    fn set(
        &mut self,
        scope_hash: u64,
        scopes: &Vec<&str>,
        token: Option<Token>,
    ) -> Result<(), NullError> {
        let matched = self.tokens.iter().find_position(|x| x.hash == scope_hash);
        if let Some((idx, _)) = matched {
            self.tokens.remove(idx);
        }

        match token {
            Some(t) => {
                self.tokens.push(JSONToken {
                    hash: scope_hash,
                    scopes: Some(scopes.iter().map(|x| x.to_string()).collect()),
                    token: t.clone(),
                });
                ()
            }
            None => {}
        };
        Ok(())
    }

    fn get(&self, scope_hash: u64, scopes: &Vec<&str>) -> Result<Option<Token>, NullError> {
        let scopes: Vec<_> = scopes.iter().sorted().unique().collect();

        for t in &self.tokens {
            if let Some(token_scopes) = &t.scopes {
                let matched = token_scopes
                    .iter()
                    .filter(|x| scopes.contains(&&&x[..]))
                    .count();
                if matched >= scopes.len() {
                    return Result::Ok(Some(t.token.clone()));
                }
            } else if scope_hash == t.hash {
                return Result::Ok(Some(t.token.clone()));
            }
        }
        Result::Ok(None)
    }
}

/// A single stored token.
#[derive(Debug, Clone, Serialize, Deserialize)]
struct JSONToken {
    pub hash: u64,
    pub scopes: Option<Vec<String>>,
    pub token: Token,
}

/// List of tokens in a JSON object
#[derive(Serialize, Deserialize)]
struct JSONTokens {
    pub tokens: Vec<JSONToken>,
}

/// Serializes tokens to a JSON file on disk.
#[derive(Default)]
pub struct DiskTokenStorage {
    location: String,
    tokens: Vec<JSONToken>,
}

impl DiskTokenStorage {
    pub fn new<S: AsRef<str>>(location: S) -> Result<DiskTokenStorage, io::Error> {
        let mut dts = DiskTokenStorage {
            location: location.as_ref().to_owned(),
            tokens: Vec::new(),
        };

        // best-effort
        let read_result = dts.load_from_file();

        match read_result {
            Result::Ok(()) => Result::Ok(dts),
            Result::Err(e) => {
                match e.kind() {
                    io::ErrorKind::NotFound => Result::Ok(dts), // File not found; ignore and create new one
                    _ => Result::Err(e),                        // e.g. PermissionDenied
                }
            }
        }
    }

    fn load_from_file(&mut self) -> Result<(), io::Error> {
        let mut f = fs::OpenOptions::new().read(true).open(&self.location)?;
        let mut contents = String::new();

        match f.read_to_string(&mut contents) {
            Result::Err(e) => return Result::Err(e),
            Result::Ok(_sz) => (),
        }

        let tokens: JSONTokens;

        match serde_json::from_str(&contents) {
            Result::Err(e) => return Result::Err(io::Error::new(io::ErrorKind::InvalidData, e)),
            Result::Ok(t) => tokens = t,
        }

        for t in tokens.tokens {
            self.tokens.push(t);
        }
        return Result::Ok(());
    }

    pub fn dump_to_file(&mut self) -> Result<(), io::Error> {
        let mut jsontokens = JSONTokens { tokens: Vec::new() };

        for token in self.tokens.iter() {
            jsontokens.tokens.push((*token).clone());
        }

        let serialized;

        match serde_json::to_string(&jsontokens) {
            Result::Err(e) => return Result::Err(io::Error::new(io::ErrorKind::InvalidData, e)),
            Result::Ok(s) => serialized = s,
        }

        let mut f = fs::OpenOptions::new()
            .create(true)
            .write(true)
            .truncate(true)
            .open(&self.location)?;
        f.write(serialized.as_ref()).map(|_| ())
    }
}

impl TokenStorage for DiskTokenStorage {
    type Error = io::Error;
    fn set(
        &mut self,
        scope_hash: u64,
        scopes: &Vec<&str>,
        token: Option<Token>,
    ) -> Result<(), Self::Error> {
        let matched = self.tokens.iter().find_position(|x| x.hash == scope_hash);
        if let Some((idx, _)) = matched {
            self.tokens.remove(idx);
        }

        match token {
            None => (),
            Some(t) => {
                self.tokens.push(JSONToken {
                    hash: scope_hash,
                    scopes: Some(scopes.iter().map(|x| x.to_string()).collect()),
                    token: t.clone(),
                });
                ()
            }
        }
        self.dump_to_file()
    }
    fn get(&self, scope_hash: u64, scopes: &Vec<&str>) -> Result<Option<Token>, Self::Error> {
        let scopes: Vec<_> = scopes.iter().sorted().unique().collect();

        for t in &self.tokens {
            if let Some(token_scopes) = &t.scopes {
                let matched = token_scopes
                    .iter()
                    .filter(|x| scopes.contains(&&&x[..]))
                    .count();
                // we may have some of the tokens as denormalized (many namespaces repeated)
                if matched >= scopes.len() {
                    return Result::Ok(Some(t.token.clone()));
                }
            } else if scope_hash == t.hash {
                return Result::Ok(Some(t.token.clone()));
            }
        }
        Result::Ok(None)
    }
}
<|endoftext|>