<commit_before>// A variation on cares-event-loop.rs.
//
// Here we:
//
// - Have the event loop take an `Arc<Mutex<Channel>>`, so that even after it
// is running we still have the ability to submit new queries
//
// - Have the event loop be run by a `Resolver`, hiding away the event loop
// details from the writer of `main()`.
//
// - Show one way of transforming the asynchronous c-ares interface into
// a synchronous, blocking interface by using a std::sync::mpsc::channel.
extern crate c_ares;
extern crate mio;

use std::collections::HashSet;
use std::error::Error;
use std::mem;
use std::os::unix::io;
use std::sync::{
    Arc,
    Mutex,
    mpsc,
};
use std::thread;

// Messages for the event loop.
enum CAresHandlerMessage {
    // 'Notify me when this file descriptor becomes readable, or writable'
    // The first bool is for 'readable' and the second is for 'writable'.  It's
    // allowed to set both of these - or neither, meaning 'I am no longer
    // interested in this file descriptor'.
    RegisterInterest(io::RawFd, bool, bool),

    // 'Shut down'.
    ShutDown,
}

struct CAresEventHandler {
    ares_channel: Arc<Mutex<c_ares::Channel>>,
    tracked_fds: HashSet<io::RawFd>,
}

impl CAresEventHandler {
    fn new(ares_channel: Arc<Mutex<c_ares::Channel>>) -> CAresEventHandler {
        CAresEventHandler {
            ares_channel: ares_channel,
            tracked_fds: HashSet::new(),
        }
    }
}

impl mio::Handler for CAresEventHandler {
    type Timeout = ();
    type Message = CAresHandlerMessage;

    // mio notifies us that a file descriptor is readable or writable, so we
    // tell the Channel the same.
    fn ready(
        &mut self,
        _event_loop: &mut mio::EventLoop<CAresEventHandler>,
        token: mio::Token,
        events: mio::EventSet) {
        let fd = token.as_usize() as io::RawFd;
        let read_fd = if events.is_readable() {
            fd
        } else {
            c_ares::INVALID_FD
        };
        let write_fd = if events.is_writable() {
            fd
        } else {
            c_ares::INVALID_FD
        };
        let mut ares_channel = self.ares_channel.lock().unwrap();
        ares_channel.process_fd(read_fd, write_fd);
    }

    // Process received messages.  Either:
    // - we're asked to register interest (or non-interest) in a file
    // descriptor
    // - we're asked to shut down the event loop.
    fn notify(
        &mut self,
        event_loop:&mut mio::EventLoop<CAresEventHandler>,
        msg: Self::Message) {
        match msg {
            CAresHandlerMessage::RegisterInterest(fd, readable, writable) => {
                let io = mio::Io::from(fd);
                if !readable && !writable {
                    self.tracked_fds.remove(&fd);
                    event_loop
                        .deregister(&io)
                        .ok()
                        .expect("failed to deregister interest");
                } else {
                    let mut interest = mio::EventSet::none();
                    if readable {
                        interest = interest | mio::EventSet::readable();
                    }
                    if writable {
                        interest = interest | mio::EventSet::writable();
                    }
                    let token = mio::Token(fd as usize);
                    let register_result = if !self.tracked_fds.insert(fd) {
                        event_loop
                            .reregister(
                                &io,
                                token,
                                interest,
                                mio::PollOpt::edge())
                   } else {
                        event_loop
                            .register_opt(
                                &io,
                                token,
                                interest,
                                mio::PollOpt::edge())
                    };
                    register_result.ok().expect("failed to register interest");
                }

                // Don't accidentally close the file descriptor by dropping io!
                mem::forget(io);
            },

            CAresHandlerMessage::ShutDown => event_loop.shutdown(),
        }
    }

    // We run a recurring timer so that we can spot non-responsive servers.
    //
    // In that case we won't get a callback saying that anything is happening
    // on any file descriptor, but nevertheless need to give the Channel an
    // opportunity to notice that it has timed-out requests pending.
    fn timeout(
        &mut self,
        event_loop: &mut mio::EventLoop<CAresEventHandler>,
        _timeout: Self::Timeout) {
        event_loop.timeout_ms((), 500).unwrap();
        let mut ares_channel = self.ares_channel.lock().unwrap();
        ares_channel.process_fd(c_ares::INVALID_FD, c_ares::INVALID_FD);
    }
}

struct Resolver {
    ares_channel: Arc<Mutex<c_ares::Channel>>,
    event_loop_channel: mio::Sender<CAresHandlerMessage>,
    event_loop_handle: Option<thread::JoinHandle<()>>,
}

impl Resolver {
    // Create a new Resolver.
    pub fn new() -> Resolver {
        // Create an event loop.
        let mut event_loop = mio::EventLoop::new()
            .ok()
            .expect("failed to create event loop");
        let event_loop_channel = event_loop.channel();

        // Socket state callback for the c_ares::Channel will be to kick the
        // event loop.
        let event_loop_channel_clone = event_loop_channel.clone();
        let sock_callback =
            move |fd: io::RawFd, readable: bool, writable: bool| {
                let _ = event_loop_channel_clone
                    .send(
                        CAresHandlerMessage::RegisterInterest(
                            fd,
                            readable,
                            writable));
            };

        // Create a c_ares::Channel.
        let mut options = c_ares::Options::new();
        options
            .set_socket_state_callback(sock_callback)
            .set_flags(c_ares::flags::STAYOPEN | c_ares::flags::EDNS)
            .set_timeout(500)
            .set_tries(3);
        let ares_channel = c_ares::Channel::new(options)
            .ok()
            .expect("Failed to create channel");
        let locked_channel = Arc::new(Mutex::new(ares_channel));

        // Set the first instance of the recurring timer on the event loop.
        event_loop.timeout_ms((), 500).unwrap();

        // Kick off the event loop.
        let mut event_handler = CAresEventHandler::new(locked_channel.clone());
        let event_loop_handle = thread::spawn(move || {
            event_loop
                .run(&mut event_handler)
                .ok()
                .expect("failed to run event loop")
        });

        Resolver {
            ares_channel: locked_channel,
            event_loop_channel: event_loop_channel,
            event_loop_handle: Some(event_loop_handle),
        }
    }

    // A blocking CNAME query.  Achieve this by having the callback send the
    // result to a std::sync::mpsc::channel, and waiting on that channel.
    pub fn query_cname(&self, name: &str)
        -> Result<c_ares::CNameResult, c_ares::AresError> {
        let (tx, rx) = mpsc::channel();
        {
            let mut ares_channel = self.ares_channel.lock().unwrap();
            ares_channel.query_cname(name, move |result| {
                tx.send(result).unwrap();
            });
        }
        rx.recv().unwrap()
    }

    // A blocking MX query.
    pub fn query_mx(&self, name: &str)
        -> Result<c_ares::MXResults, c_ares::AresError> {
        let (tx, rx) = mpsc::channel();
        {
            let mut ares_channel = self.ares_channel.lock().unwrap();
            ares_channel.query_mx(name, move |result| {
                tx.send(result).unwrap();
            });
        }
        rx.recv().unwrap()
    }

    // A blocking NAPTR query.
    pub fn query_naptr(&self, name: &str)
        -> Result<c_ares::NAPTRResults, c_ares::AresError> {
        let (tx, rx) = mpsc::channel();
        {
            let mut ares_channel = self.ares_channel.lock().unwrap();
            ares_channel.query_naptr(name, move |result| {
                tx.send(result).unwrap();
            });
        }
        rx.recv().unwrap()
    }
}

impl Drop for Resolver {
    fn drop(&mut self) {
        // Shut down the event loop and wait for it to finish.
        self.event_loop_channel
            .send(CAresHandlerMessage::ShutDown)
            .ok()
            .expect("failed to shut down event loop");
       for handle in self.event_loop_handle.take() {
           handle.join().unwrap();
       }
    }
}

fn print_cname_result(result: Result<c_ares::CNameResult, c_ares::AresError>) {
    match result {
        Err(e) => {
            println!("CNAME lookup failed with error '{}'", e.description());
        }
        Ok(cname_result) => {
            println!("Successful CNAME lookup...");
            println!("{}", cname_result.cname());
        }
    }
}

fn print_mx_results(result: Result<c_ares::MXResults, c_ares::AresError>) {
    match result {
        Err(e) => {
            println!("MX lookup failed with error '{}'", e.description());
        }
        Ok(mx_results) => {
            println!("Successful MX lookup...");
            for mx_result in &mx_results {
                println!(
                    "host {}, priority {}",
                    mx_result.host(),
                    mx_result.priority());
            }
        }
    }
}

fn print_naptr_results(
    result: Result<c_ares::NAPTRResults, c_ares::AresError>) {
    match result {
        Err(e) => {
            println!("NAPTR lookup failed with error '{}'", e.description());
        }
        Ok(naptr_results) => {
            println!("Successful NAPTR lookup...");
            for naptr_result in &naptr_results {
                println!("flags: {}", naptr_result.flags());
                println!("service name: {}", naptr_result.service_name());
                println!("regular expression: {}", naptr_result.reg_exp());
                println!(
                    "replacement pattern: {}",
                    naptr_result.replacement_pattern());
                println!("order: {}", naptr_result.order());
                println!("preference: {}", naptr_result.preference());
            }
        }
    }
}

fn main() {
    // Create a Resolver.  Then make some requests.
    let resolver = Resolver::new();
    let result = resolver.query_cname("dimbleby.github.io");
    println!("");
    print_cname_result(result);

    let result = resolver.query_mx("gmail.com");
    println!("");
    print_mx_results(result);

    let result = resolver.query_naptr("4.3.2.1.5.5.5.0.0.8.1.e164.arpa");
    println!("");
    print_naptr_results(result);
}
<commit_msg>Slight code tidying<commit_after>// A variation on cares-event-loop.rs.
//
// Here we:
//
// - Have the event loop take an `Arc<Mutex<Channel>>`, so that even after it
// is running we still have the ability to submit new queries
//
// - Have the event loop be run by a `Resolver`, hiding away the event loop
// details from the writer of `main()`.
//
// - Show one way of transforming the asynchronous c-ares interface into
// a synchronous, blocking interface by using a std::sync::mpsc::channel.
extern crate c_ares;
extern crate mio;

use std::collections::HashSet;
use std::error::Error;
use std::mem;
use std::os::unix::io;
use std::sync::{
    Arc,
    Mutex,
    mpsc,
};
use std::thread;

// Messages for the event loop.
enum CAresHandlerMessage {
    // 'Notify me when this file descriptor becomes readable, or writable'
    // The first bool is for 'readable' and the second is for 'writable'.  It's
    // allowed to set both of these - or neither, meaning 'I am no longer
    // interested in this file descriptor'.
    RegisterInterest(io::RawFd, bool, bool),

    // 'Shut down'.
    ShutDown,
}

struct CAresEventHandler {
    ares_channel: Arc<Mutex<c_ares::Channel>>,
    tracked_fds: HashSet<io::RawFd>,
}

impl CAresEventHandler {
    fn new(ares_channel: Arc<Mutex<c_ares::Channel>>) -> CAresEventHandler {
        CAresEventHandler {
            ares_channel: ares_channel,
            tracked_fds: HashSet::new(),
        }
    }
}

impl mio::Handler for CAresEventHandler {
    type Timeout = ();
    type Message = CAresHandlerMessage;

    // mio notifies us that a file descriptor is readable or writable, so we
    // tell the Channel the same.
    fn ready(
        &mut self,
        _event_loop: &mut mio::EventLoop<CAresEventHandler>,
        token: mio::Token,
        events: mio::EventSet) {
        let fd = token.as_usize() as io::RawFd;
        let read_fd = if events.is_readable() {
            fd
        } else {
            c_ares::INVALID_FD
        };
        let write_fd = if events.is_writable() {
            fd
        } else {
            c_ares::INVALID_FD
        };
        self.ares_channel.lock().unwrap().process_fd(read_fd, write_fd);
    }

    // Process received messages.  Either:
    // - we're asked to register interest (or non-interest) in a file
    // descriptor
    // - we're asked to shut down the event loop.
    fn notify(
        &mut self,
        event_loop:&mut mio::EventLoop<CAresEventHandler>,
        msg: Self::Message) {
        match msg {
            CAresHandlerMessage::RegisterInterest(fd, readable, writable) => {
                let io = mio::Io::from(fd);
                if !readable && !writable {
                    self.tracked_fds.remove(&fd);
                    event_loop
                        .deregister(&io)
                        .ok()
                        .expect("failed to deregister interest");
                } else {
                    let mut interest = mio::EventSet::none();
                    if readable {
                        interest = interest | mio::EventSet::readable();
                    }
                    if writable {
                        interest = interest | mio::EventSet::writable();
                    }
                    let token = mio::Token(fd as usize);
                    let register_result = if !self.tracked_fds.insert(fd) {
                        event_loop
                            .reregister(
                                &io,
                                token,
                                interest,
                                mio::PollOpt::edge())
                   } else {
                        event_loop
                            .register_opt(
                                &io,
                                token,
                                interest,
                                mio::PollOpt::edge())
                    };
                    register_result.ok().expect("failed to register interest");
                }

                // Don't accidentally close the file descriptor by dropping io!
                mem::forget(io);
            },

            CAresHandlerMessage::ShutDown => event_loop.shutdown(),
        }
    }

    // We run a recurring timer so that we can spot non-responsive servers.
    //
    // In that case we won't get a callback saying that anything is happening
    // on any file descriptor, but nevertheless need to give the Channel an
    // opportunity to notice that it has timed-out requests pending.
    fn timeout(
        &mut self,
        event_loop: &mut mio::EventLoop<CAresEventHandler>,
        _timeout: Self::Timeout) {
        event_loop.timeout_ms((), 500).unwrap();
        self.ares_channel
            .lock()
            .unwrap()
            .process_fd(c_ares::INVALID_FD, c_ares::INVALID_FD);
    }
}

struct Resolver {
    ares_channel: Arc<Mutex<c_ares::Channel>>,
    event_loop_channel: mio::Sender<CAresHandlerMessage>,
    event_loop_handle: Option<thread::JoinHandle<()>>,
}

impl Resolver {
    // Create a new Resolver.
    pub fn new() -> Resolver {
        // Create an event loop.
        let mut event_loop = mio::EventLoop::new()
            .ok()
            .expect("failed to create event loop");
        let event_loop_channel = event_loop.channel();

        // Socket state callback for the c_ares::Channel will be to kick the
        // event loop.
        let event_loop_channel_clone = event_loop_channel.clone();
        let sock_callback =
            move |fd: io::RawFd, readable: bool, writable: bool| {
                let _ = event_loop_channel_clone
                    .send(
                        CAresHandlerMessage::RegisterInterest(
                            fd,
                            readable,
                            writable));
            };

        // Create a c_ares::Channel.
        let mut options = c_ares::Options::new();
        options
            .set_socket_state_callback(sock_callback)
            .set_flags(c_ares::flags::STAYOPEN | c_ares::flags::EDNS)
            .set_timeout(500)
            .set_tries(3);
        let ares_channel = c_ares::Channel::new(options)
            .ok()
            .expect("Failed to create channel");
        let locked_channel = Arc::new(Mutex::new(ares_channel));

        // Set the first instance of the recurring timer on the event loop.
        event_loop.timeout_ms((), 500).unwrap();

        // Kick off the event loop.
        let mut event_handler = CAresEventHandler::new(locked_channel.clone());
        let event_loop_handle = thread::spawn(move || {
            event_loop
                .run(&mut event_handler)
                .ok()
                .expect("failed to run event loop")
        });

        Resolver {
            ares_channel: locked_channel,
            event_loop_channel: event_loop_channel,
            event_loop_handle: Some(event_loop_handle),
        }
    }

    // A blocking CNAME query.  Achieve this by having the callback send the
    // result to a std::sync::mpsc::channel, and waiting on that channel.
    pub fn query_cname(&self, name: &str)
        -> Result<c_ares::CNameResult, c_ares::AresError> {
        let (tx, rx) = mpsc::channel();
        self.ares_channel.lock().unwrap().query_cname(name, move |result| {
            tx.send(result).unwrap();
        });
        rx.recv().unwrap()
    }

    // A blocking MX query.
    pub fn query_mx(&self, name: &str)
        -> Result<c_ares::MXResults, c_ares::AresError> {
        let (tx, rx) = mpsc::channel();
        self.ares_channel.lock().unwrap().query_mx(name, move |result| {
            tx.send(result).unwrap();
        });
        rx.recv().unwrap()
    }

    // A blocking NAPTR query.
    pub fn query_naptr(&self, name: &str)
        -> Result<c_ares::NAPTRResults, c_ares::AresError> {
        let (tx, rx) = mpsc::channel();
        self.ares_channel.lock().unwrap().query_naptr(name, move |result| {
            tx.send(result).unwrap();
        });
        rx.recv().unwrap()
    }
}

impl Drop for Resolver {
    fn drop(&mut self) {
        // Shut down the event loop and wait for it to finish.
        self.event_loop_channel
            .send(CAresHandlerMessage::ShutDown)
            .ok()
            .expect("failed to shut down event loop");
       for handle in self.event_loop_handle.take() {
           handle.join().unwrap();
       }
    }
}

fn print_cname_result(result: Result<c_ares::CNameResult, c_ares::AresError>) {
    match result {
        Err(e) => {
            println!("CNAME lookup failed with error '{}'", e.description());
        }
        Ok(cname_result) => {
            println!("Successful CNAME lookup...");
            println!("{}", cname_result.cname());
        }
    }
}

fn print_mx_results(result: Result<c_ares::MXResults, c_ares::AresError>) {
    match result {
        Err(e) => {
            println!("MX lookup failed with error '{}'", e.description());
        }
        Ok(mx_results) => {
            println!("Successful MX lookup...");
            for mx_result in &mx_results {
                println!(
                    "host {}, priority {}",
                    mx_result.host(),
                    mx_result.priority());
            }
        }
    }
}

fn print_naptr_results(
    result: Result<c_ares::NAPTRResults, c_ares::AresError>) {
    match result {
        Err(e) => {
            println!("NAPTR lookup failed with error '{}'", e.description());
        }
        Ok(naptr_results) => {
            println!("Successful NAPTR lookup...");
            for naptr_result in &naptr_results {
                println!("flags: {}", naptr_result.flags());
                println!("service name: {}", naptr_result.service_name());
                println!("regular expression: {}", naptr_result.reg_exp());
                println!(
                    "replacement pattern: {}",
                    naptr_result.replacement_pattern());
                println!("order: {}", naptr_result.order());
                println!("preference: {}", naptr_result.preference());
            }
        }
    }
}

fn main() {
    // Create a Resolver.  Then make some requests.
    let resolver = Resolver::new();
    let result = resolver.query_cname("dimbleby.github.io");
    println!("");
    print_cname_result(result);

    let result = resolver.query_mx("gmail.com");
    println!("");
    print_mx_results(result);

    let result = resolver.query_naptr("4.3.2.1.5.5.5.0.0.8.1.e164.arpa");
    println!("");
    print_naptr_results(result);
}
<|endoftext|>


<commit_before>#[derive(Debug, PartialEq, Copy, Clone)]
pub enum TokenError {
    // If a token not a valid number
    // For example "+0a" starts as a number, but contains invalid characters.
    InvalidNumber,

    // missing terminating quote "
    MissingQuoteEnd,

    InvalidUnquotedString,

    InvalidEscape,
}

#[derive(Debug, PartialEq, Clone)]
pub enum Token<'a> {
    Error((&'a str, TokenError)),
    Whitespace(&'a str),

    Str(&'a str),
    QStr(String),

    OpenBracket,
    CloseBracket,
    OpenBrace,
    CloseBrace,
    OpenCurly,
    CloseCurly,

    UInt(u64),
    SInt(i64),
    Float(f64),
}

#[inline]
fn scan<F: Fn(char) -> bool>(s: &str, cond: F) -> (&str, &str) {
    // split at the first non-"cond" character
    match s.find(|c: char| !cond(c)) {
        None => (s, ""),
        Some(pos) => s.split_at(pos),
    }
}

#[inline]
pub fn is_token_delim(c: char) -> bool {
    c.is_whitespace() || c == '(' || c == ')' || c == '[' || c == ']' || c == '{' || c == '}'
}

fn is_valid_unquoted_string(s: &str) -> bool {
    !(s.contains('\\') || s.contains('"'))
}

// TODO ; comment
fn next_token<'a>(s: &'a str) -> Option<(Token<'a>, &'a str)> {
    match s.slice_shift_char() {
        None => None,
        Some((c, s2)) => {
            match c {
                '(' => Some((Token::OpenBrace, s2)),
                ')' => Some((Token::CloseBrace, s2)),

                '[' => Some((Token::OpenBracket, s2)),
                ']' => Some((Token::CloseBracket, s2)),

                '{' => Some((Token::OpenCurly, s2)),
                '}' => Some((Token::CloseCurly, s2)),

                '"' => {
                    let mut rest = s2;
                    let mut unquoted_string = String::new();

                    loop {
                        match rest.slice_shift_char() {
                            None => {
                                // Error. No terminating quoting character found
                                return Some((Token::Error((rest, TokenError::MissingQuoteEnd)), s));
                            }
                            Some((ch, rem)) => {
                                match ch {
                                    '"' => {
                                        rest = rem;
                                        // found good string
                                        break;
                                    }
                                    '\\' => {
                                        // next character is escaped
                                        rest = rem;
                                        match rest.slice_shift_char() {
                                            Some(('\\', r)) => {
                                                rest = r;
                                                unquoted_string.push('\\');
                                            }
                                            Some(('"', r)) => {
                                                rest = r;
                                                unquoted_string.push('"');
                                            }
                                            _ => {
                                                return Some((Token::Error((rest, TokenError::InvalidEscape)), s));
                                            }
                                        }
                                    }
                                    _ => {
                                        rest = rem;
                                        unquoted_string.push(ch);
                                    }
                                }
                            }
                        }
                    }

                    Some((Token::QStr(unquoted_string), rest))
                }

                c if char::is_whitespace(c) => {
                    let (ws, rest) = scan(s, char::is_whitespace);
                    Some((Token::Whitespace(ws), rest))
                }

                '+' | '-' => {
                    let (string, rest) = scan(s, |ch| !is_token_delim(ch));
                    assert!(string.len() > 0);

                    // parse +|- always as a signed integer first.
                    // this is the way to distinguish a positive signed
                    // integer from a positive unsigned (+1 == SInt, while 1 == UInt).

                    // if it is followed by a digit, this is should be a number.
                    match s2.slice_shift_char() {
                        Some((ch, _)) if char::is_digit(ch, 10) => {
                            // +|- followed by [0-9]. This must be a number!

                            if let Ok(i) = string.parse::<i64>() {
                                Some((Token::SInt(i), rest))
                            } else if let Ok(i) = string.parse::<f64>() {
                                Some((Token::Float(i), rest))
                            } else {
                                Some((Token::Error((string, TokenError::InvalidNumber)), rest))
                            }
                        }
                        _ => {
                            // If it is followed by any other character (or none) it is a valid
                            // string token.
                            if is_valid_unquoted_string(string) {
                                Some((Token::Str(string), rest))
                            } else {
                                Some((Token::Error((string, TokenError::InvalidUnquotedString)),
                                      rest))
                            }
                        }
                    }
                }

                '0'...'9' => {
                    // this should be either a unsigned integer of a floating point number. If not,
                    // it's invalid.
                    let (string, rest) = scan(s, |ch| !is_token_delim(ch));
                    assert!(string.len() > 0);

                    if let Ok(i) = string.parse::<u64>() {
                        Some((Token::UInt(i), rest))
                    } else if let Ok(i) = string.parse::<f64>() {
                        Some((Token::Float(i), rest))
                    } else {
                        Some((Token::Error((string, TokenError::InvalidNumber)), rest))
                    }
                }

                _ => {
                    // this neither starts with '+' or '-' or 'digit'. this is definitively a
                    // string.

                    let (string, rest) = scan(s, |ch| !is_token_delim(ch));
                    assert!(string.len() > 0);

                    if is_valid_unquoted_string(string) {
                        Some((Token::Str(string), rest))
                    } else {
                        Some((Token::Error((string, TokenError::InvalidUnquotedString)),
                              rest))
                    }
                }
            }
        }
    }
}

pub struct Tokenizer<'a> {
    current: &'a str,
    ignore_ws: bool,
}

impl<'a> Tokenizer<'a> {
    pub fn new(s: &'a str, ignore_ws: bool) -> Tokenizer<'a> {
        Tokenizer {
            current: s,
            ignore_ws: ignore_ws,
        }
    }
}

impl<'a> Iterator for Tokenizer<'a> {
    type Item = Token<'a>;

    fn next(&mut self) -> Option<Self::Item> {
        loop {
            match next_token(self.current) {
                Some((tok, rest)) => {
                    self.current = rest;
                    if self.ignore_ws {
                        if let Token::Whitespace(_) = tok {
                            continue;
                        }
                    }
                    return Some(tok);
                }
                None => {
                    return None;
                }
            }
        }
    }
}

enum State {
    Begin,
    Inner,
    End,
}

pub struct CurlyAroundTokenizer<'a> {
    inner: Tokenizer<'a>,
    state: State,
}

impl<'a> CurlyAroundTokenizer<'a> {
    pub fn new(inner: Tokenizer<'a>) -> CurlyAroundTokenizer<'a> {
        CurlyAroundTokenizer {
            inner: inner,
            state: State::Begin,
        }
    }
}

impl<'a> Iterator for CurlyAroundTokenizer<'a> {
    type Item = Token<'a>;

    fn next(&mut self) -> Option<Self::Item> {
        match self.state {
            State::Begin => {
                self.state = State::Inner;
                Some(Token::OpenCurly)
            }
            State::Inner => {
                if let Some(tok) = self.inner.next() {
                    return Some(tok);
                }
                self.state = State::End;
                Some(Token::CloseCurly)
            }
            State::End => None,
        }
    }
}

#[test]
fn test_tokenizer_whitespace() {
    let t = Tokenizer::new(" (abc 123)", false);
    let tokens: Vec<_> = t.into_iter().collect();
    assert_eq!(vec![Token::Whitespace(" "),
                    Token::OpenBrace,
                    Token::Str("abc"),
                    Token::Whitespace(" "),
                    Token::UInt(123),
                    Token::CloseBrace],
               tokens);
}

#[test]
fn test_tokenizer_curly_around() {
    let t = CurlyAroundTokenizer::new(Tokenizer::new(" (abc 123)", true));
    let tokens: Vec<_> = t.into_iter().collect();
    assert_eq!(vec![Token::OpenCurly,
                    Token::OpenBrace,
                    Token::Str("abc"),
                    Token::UInt(123),
                    Token::CloseBrace,
                    Token::CloseCurly],
               tokens);
}

#[test]
fn test_tokenizer_no_whitespace() {
    let t = Tokenizer::new(" (abc 123)", true);
    let tokens: Vec<_> = t.into_iter().collect();
    assert_eq!(vec![Token::OpenBrace, Token::Str("abc"), Token::UInt(123), Token::CloseBrace],
               tokens);
}

#[test]
fn test_token() {
    assert_eq!(Some((Token::Whitespace("  "), "abc")), next_token("  abc"));
    assert_eq!(Some((Token::Str("abc"), "")), next_token("abc"));
    assert_eq!(Some((Token::Str("abc"), "(")), next_token("abc("));

    assert_eq!(Some((Token::OpenBrace, ")")), next_token("()"));

    assert_eq!(Some((Token::UInt(12345), "")), next_token("12345"));
    assert_eq!(Some((Token::UInt(12345), " ")), next_token("12345 "));
    assert_eq!(Some((Token::SInt(12345), " ")), next_token("+12345 "));
    assert_eq!(Some((Token::SInt(-12345), " ")), next_token("-12345 "));
    assert_eq!(Some((Token::Str("-a"), " ")), next_token("-a "));
    assert_eq!(Some((Token::Str("+a"), " ")), next_token("+a "));
    assert_eq!(Some((Token::Str("+a"), "(")), next_token("+a("));

    assert_eq!(Some((Token::Error(("12345+", TokenError::InvalidNumber)), "")),
               next_token("12345+"));
    assert_eq!(Some((Token::UInt(12345), " +")), next_token("12345 +"));
    assert_eq!(Some((Token::Float(12345.123), "")), next_token("12345.123"));
    assert_eq!(Some((Token::Float(12345.123), "(")),
               next_token("12345.123("));

    assert_eq!(Some((Token::Error(("abc\\", TokenError::InvalidUnquotedString)),
                     " test")),
               next_token("abc\\ test"));
    assert_eq!(Some((Token::Error(("abc\"", TokenError::InvalidUnquotedString)),
                     " test")),
               next_token("abc\" test"));

    assert_eq!(Some((Token::QStr("".to_string()), "(")),
               next_token("\"\"("));
    assert_eq!(Some((Token::QStr("abc".to_string()), "(")),
               next_token("\"abc\"("));
    assert_eq!(Some((Token::QStr("a\"b".to_string()), "(")),
               next_token("\"a\\\"b\"("));
    assert_eq!(Some((Token::QStr("a\\b".to_string()), "(")),
               next_token("\"a\\\\b\"("));

    assert_eq!(Some((Token::Error(("", TokenError::MissingQuoteEnd)), "\"abc ")),
               next_token("\"abc "));

    assert_eq!(Some((Token::Error(("n ", TokenError::InvalidEscape)), "\"abc\\n ")),
               next_token("\"abc\\n "));

}
<commit_msg>Ignore comments<commit_after>#[derive(Debug, PartialEq, Copy, Clone)]
pub enum TokenError {
    // If a token not a valid number
    // For example "+0a" starts as a number, but contains invalid characters.
    InvalidNumber,

    // missing terminating quote "
    MissingQuoteEnd,

    InvalidUnquotedString,

    InvalidEscape,
}

#[derive(Debug, PartialEq, Clone)]
pub enum Token<'a> {
    Error((&'a str, TokenError)),
    Whitespace(&'a str),
    Comment(&'a str),

    Str(&'a str),
    QStr(String),

    OpenBracket,
    CloseBracket,
    OpenBrace,
    CloseBrace,
    OpenCurly,
    CloseCurly,

    UInt(u64),
    SInt(i64),
    Float(f64),
}

#[inline]
fn scan<F: Fn(char) -> bool>(s: &str, cond: F) -> (&str, &str) {
    // split at the first non-"cond" character
    match s.find(|c: char| !cond(c)) {
        None => (s, ""),
        Some(pos) => s.split_at(pos),
    }
}

#[inline]
pub fn is_token_delim(c: char) -> bool {
    c.is_whitespace() || c == '(' || c == ')' || c == '[' || c == ']' || c == '{' || c == '}' ||
    c == ';'
}

fn is_valid_unquoted_string(s: &str) -> bool {
    !(s.contains('\\') || s.contains('"'))
}

// TODO ; comment
fn next_token<'a>(s: &'a str) -> Option<(Token<'a>, &'a str)> {
    match s.slice_shift_char() {
        None => None,
        Some((c, s2)) => {
            match c {
                '(' => Some((Token::OpenBrace, s2)),
                ')' => Some((Token::CloseBrace, s2)),

                '[' => Some((Token::OpenBracket, s2)),
                ']' => Some((Token::CloseBracket, s2)),

                '{' => Some((Token::OpenCurly, s2)),
                '}' => Some((Token::CloseCurly, s2)),

                ';' => {
                    // comment
                    let (comment, rest) = scan(s2, |ch| ch != '\n');
                    Some((Token::Comment(comment), rest))
                }

                '"' => {
                    let mut rest = s2;
                    let mut unquoted_string = String::new();

                    loop {
                        match rest.slice_shift_char() {
                            None => {
                                // Error. No terminating quoting character found
                                return Some((Token::Error((rest, TokenError::MissingQuoteEnd)), s));
                            }
                            Some((ch, rem)) => {
                                match ch {
                                    '"' => {
                                        rest = rem;
                                        // found good string
                                        break;
                                    }
                                    '\\' => {
                                        // next character is escaped
                                        rest = rem;
                                        match rest.slice_shift_char() {
                                            Some(('\\', r)) => {
                                                rest = r;
                                                unquoted_string.push('\\');
                                            }
                                            Some(('"', r)) => {
                                                rest = r;
                                                unquoted_string.push('"');
                                            }
                                            _ => {
                                                return Some((Token::Error((rest, TokenError::InvalidEscape)), s));
                                            }
                                        }
                                    }
                                    _ => {
                                        rest = rem;
                                        unquoted_string.push(ch);
                                    }
                                }
                            }
                        }
                    }

                    Some((Token::QStr(unquoted_string), rest))
                }

                c if char::is_whitespace(c) => {
                    let (ws, rest) = scan(s, char::is_whitespace);
                    Some((Token::Whitespace(ws), rest))
                }

                '+' | '-' => {
                    let (string, rest) = scan(s, |ch| !is_token_delim(ch));
                    assert!(string.len() > 0);

                    // parse +|- always as a signed integer first.
                    // this is the way to distinguish a positive signed
                    // integer from a positive unsigned (+1 == SInt, while 1 == UInt).

                    // if it is followed by a digit, this is should be a number.
                    match s2.slice_shift_char() {
                        Some((ch, _)) if char::is_digit(ch, 10) => {
                            // +|- followed by [0-9]. This must be a number!

                            if let Ok(i) = string.parse::<i64>() {
                                Some((Token::SInt(i), rest))
                            } else if let Ok(i) = string.parse::<f64>() {
                                Some((Token::Float(i), rest))
                            } else {
                                Some((Token::Error((string, TokenError::InvalidNumber)), rest))
                            }
                        }
                        _ => {
                            // If it is followed by any other character (or none) it is a valid
                            // string token.
                            if is_valid_unquoted_string(string) {
                                Some((Token::Str(string), rest))
                            } else {
                                Some((Token::Error((string, TokenError::InvalidUnquotedString)),
                                      rest))
                            }
                        }
                    }
                }

                '0'...'9' => {
                    // this should be either a unsigned integer of a floating point number. If not,
                    // it's invalid.
                    let (string, rest) = scan(s, |ch| !is_token_delim(ch));
                    assert!(string.len() > 0);

                    if let Ok(i) = string.parse::<u64>() {
                        Some((Token::UInt(i), rest))
                    } else if let Ok(i) = string.parse::<f64>() {
                        Some((Token::Float(i), rest))
                    } else {
                        Some((Token::Error((string, TokenError::InvalidNumber)), rest))
                    }
                }

                _ => {
                    // this neither starts with '+' or '-' or 'digit'. this is definitively a
                    // string.

                    let (string, rest) = scan(s, |ch| !is_token_delim(ch));
                    assert!(string.len() > 0);

                    if is_valid_unquoted_string(string) {
                        Some((Token::Str(string), rest))
                    } else {
                        Some((Token::Error((string, TokenError::InvalidUnquotedString)),
                              rest))
                    }
                }
            }
        }
    }
}

pub struct Tokenizer<'a> {
    current: &'a str,
    ignore_ws: bool,
}

impl<'a> Tokenizer<'a> {
    pub fn new(s: &'a str, ignore_ws: bool) -> Tokenizer<'a> {
        Tokenizer {
            current: s,
            ignore_ws: ignore_ws,
        }
    }
}

impl<'a> Iterator for Tokenizer<'a> {
    type Item = Token<'a>;

    fn next(&mut self) -> Option<Self::Item> {
        loop {
            match next_token(self.current) {
                Some((tok, rest)) => {
                    self.current = rest;
                    if self.ignore_ws {
                        match tok {
                            Token::Whitespace(_) | Token::Comment(_) => continue,
                            _ => {}
                        }
                    }
                    return Some(tok);
                }
                None => {
                    return None;
                }
            }
        }
    }
}

enum State {
    Begin,
    Inner,
    End,
}

pub struct CurlyAroundTokenizer<'a> {
    inner: Tokenizer<'a>,
    state: State,
}

impl<'a> CurlyAroundTokenizer<'a> {
    pub fn new(inner: Tokenizer<'a>) -> CurlyAroundTokenizer<'a> {
        CurlyAroundTokenizer {
            inner: inner,
            state: State::Begin,
        }
    }
}

impl<'a> Iterator for CurlyAroundTokenizer<'a> {
    type Item = Token<'a>;

    fn next(&mut self) -> Option<Self::Item> {
        match self.state {
            State::Begin => {
                self.state = State::Inner;
                Some(Token::OpenCurly)
            }
            State::Inner => {
                if let Some(tok) = self.inner.next() {
                    return Some(tok);
                }
                self.state = State::End;
                Some(Token::CloseCurly)
            }
            State::End => None,
        }
    }
}

#[test]
fn test_tokenizer_whitespace() {
    let t = Tokenizer::new(" (abc 123)", false);
    let tokens: Vec<_> = t.into_iter().collect();
    assert_eq!(vec![Token::Whitespace(" "),
                    Token::OpenBrace,
                    Token::Str("abc"),
                    Token::Whitespace(" "),
                    Token::UInt(123),
                    Token::CloseBrace],
               tokens);
}

#[test]
fn test_tokenizer_comment() {
    let t = Tokenizer::new(" (abc;comment\n 123)", false);
    let tokens: Vec<_> = t.into_iter().collect();
    assert_eq!(vec![Token::Whitespace(" "),
                    Token::OpenBrace,
                    Token::Str("abc"),
                    Token::Comment("comment"),
                    Token::Whitespace("\n "),
                    Token::UInt(123),
                    Token::CloseBrace],
               tokens);
}

#[test]
fn test_tokenizer_curly_around() {
    let t = CurlyAroundTokenizer::new(Tokenizer::new(" (abc 123)", true));
    let tokens: Vec<_> = t.into_iter().collect();
    assert_eq!(vec![Token::OpenCurly,
                    Token::OpenBrace,
                    Token::Str("abc"),
                    Token::UInt(123),
                    Token::CloseBrace,
                    Token::CloseCurly],
               tokens);
}

#[test]
fn test_tokenizer_no_whitespace() {
    let t = Tokenizer::new(" (abc 123)", true);
    let tokens: Vec<_> = t.into_iter().collect();
    assert_eq!(vec![Token::OpenBrace, Token::Str("abc"), Token::UInt(123), Token::CloseBrace],
               tokens);
}

#[test]
fn test_token() {
    assert_eq!(Some((Token::Whitespace("  "), "abc")), next_token("  abc"));
    assert_eq!(Some((Token::Str("abc"), "")), next_token("abc"));
    assert_eq!(Some((Token::Str("abc"), "(")), next_token("abc("));

    assert_eq!(Some((Token::OpenBrace, ")")), next_token("()"));

    assert_eq!(Some((Token::UInt(12345), "")), next_token("12345"));
    assert_eq!(Some((Token::UInt(12345), " ")), next_token("12345 "));
    assert_eq!(Some((Token::SInt(12345), " ")), next_token("+12345 "));
    assert_eq!(Some((Token::SInt(-12345), " ")), next_token("-12345 "));
    assert_eq!(Some((Token::Str("-a"), " ")), next_token("-a "));
    assert_eq!(Some((Token::Str("+a"), " ")), next_token("+a "));
    assert_eq!(Some((Token::Str("+a"), "(")), next_token("+a("));

    assert_eq!(Some((Token::Error(("12345+", TokenError::InvalidNumber)), "")),
               next_token("12345+"));
    assert_eq!(Some((Token::UInt(12345), " +")), next_token("12345 +"));
    assert_eq!(Some((Token::Float(12345.123), "")), next_token("12345.123"));
    assert_eq!(Some((Token::Float(12345.123), "(")),
               next_token("12345.123("));

    assert_eq!(Some((Token::Error(("abc\\", TokenError::InvalidUnquotedString)),
                     " test")),
               next_token("abc\\ test"));
    assert_eq!(Some((Token::Error(("abc\"", TokenError::InvalidUnquotedString)),
                     " test")),
               next_token("abc\" test"));

    assert_eq!(Some((Token::QStr("".to_string()), "(")),
               next_token("\"\"("));
    assert_eq!(Some((Token::QStr("abc".to_string()), "(")),
               next_token("\"abc\"("));
    assert_eq!(Some((Token::QStr("a\"b".to_string()), "(")),
               next_token("\"a\\\"b\"("));
    assert_eq!(Some((Token::QStr("a\\b".to_string()), "(")),
               next_token("\"a\\\\b\"("));

    assert_eq!(Some((Token::Error(("", TokenError::MissingQuoteEnd)), "\"abc ")),
               next_token("\"abc "));

    assert_eq!(Some((Token::Error(("n ", TokenError::InvalidEscape)), "\"abc\\n ")),
               next_token("\"abc\\n "));

}
<|endoftext|>


<commit_before>//! Tracing and profiling functions. Error and warning log.
use libc::{c_char, c_int, c_void};
use std::ffi::CString;
use std::ptr;

use super::ffi;
use {SqliteError, SqliteResult, SqliteConnection};

pub type LogCallback =
    Option<extern "C" fn (udp: *mut c_void, err: c_int, msg: *const c_char)>;

/// Set up the error logging callback
///
/// cf [The Error And Warning Log](http://sqlite.org/errlog.html).
pub fn config_log(cb: LogCallback) -> SqliteResult<()> {
    let rc = unsafe {
        let p_arg: *mut c_void = ptr::null_mut();
        ffi::sqlite3_config(ffi::SQLITE_CONFIG_LOG, cb, p_arg)
    };
    if rc != ffi::SQLITE_OK {
        return Err(SqliteError{ code: rc, message: "sqlite3_config(SQLITE_CONFIG_LOG, ...)".to_string() });
    }
    Ok(())
}

/// Write a message into the error log established by `config_log`.
pub fn log(err_code: c_int, msg: &str) {
    let msg = CString::new(msg).unwrap();
    unsafe {
        ffi::sqlite3_log(err_code, msg.as_ptr());
    }
}

/// The trace callback function signature.
pub type TraceCallback =
    Option<extern "C" fn (p_arg: *mut c_void, z_sql: *const c_char)>;
/// The profile callback function signature.
pub type ProfileCallback =
    Option<extern "C" fn (p_arg: *mut c_void, z_sql: *const c_char, nanoseconds: u64)>;

impl SqliteConnection {
    /// Register or clear a callback function that can be used for tracing the execution of SQL statements.
    ///
    /// Prepared statement placeholders are replaced/logged with their assigned values.
    /// There can only be a single tracer defined for each database connection.
    /// Setting a new tracer clears the old one.
    pub fn trace(&mut self, x_trace: TraceCallback) {
        let c = self.db.borrow_mut();
        unsafe { ffi::sqlite3_trace(c.db(), x_trace, ptr::null_mut()); }
    }
    /// Register or clear a callback function that can be used for profiling the execution of SQL statements.
    ///
    /// There can only be a single profiler defined for each database connection.
    /// Setting a new profiler clears the old one.
    pub fn profile(&mut self, x_profile: ProfileCallback) {
        let c = self.db.borrow_mut();
        unsafe { ffi::sqlite3_profile(c.db(), x_profile, ptr::null_mut()); }
    }
}

#[cfg(test)]
mod test {
    use libc::{c_char, c_int, c_void};
    use std::ffi::CStr;
    use std::io::Write;
    use std::str;

    use ffi;
    use SqliteConnection;

    extern "C" fn log_callback(_: *mut c_void, err: c_int, msg: *const c_char) {
        unsafe {
            let c_slice = CStr::from_ptr(msg).to_bytes();
            let _ = writeln!(::std::io::stderr(), "{}: {:?}", err, str::from_utf8(c_slice));
        }
    }

    #[test]
    fn test_log() {
        if true { // To avoid freezing tests
            return
        }
        unsafe { ffi::sqlite3_shutdown() };
        super::config_log(Some(log_callback)).unwrap();
        //super::log(ffi::SQLITE_NOTICE, "message from rusqlite");
        super::config_log(None).unwrap();
    }

    extern "C" fn trace_callback(_: *mut ::libc::c_void, sql: *const ::libc::c_char) {
        unsafe {
            let c_slice = ::std::ffi::CStr::from_ptr(sql).to_bytes();
            let _ = writeln!(::std::io::stderr(), "TRACE: {:?}", ::std::str::from_utf8(c_slice));
        }
    }

    #[test]
    fn test_trace() {
        let mut db = SqliteConnection::open_in_memory().unwrap();
        db.trace(Some(trace_callback));
        db.execute_batch("PRAGMA application_id = 1").unwrap();
    }
}<commit_msg>Ignore test_log.<commit_after>//! Tracing and profiling functions. Error and warning log.
use libc::{c_char, c_int, c_void};
use std::ffi::CString;
use std::ptr;

use super::ffi;
use {SqliteError, SqliteResult, SqliteConnection};

pub type LogCallback =
    Option<extern "C" fn (udp: *mut c_void, err: c_int, msg: *const c_char)>;

/// Set up the error logging callback
///
/// cf [The Error And Warning Log](http://sqlite.org/errlog.html).
pub fn config_log(cb: LogCallback) -> SqliteResult<()> {
    let rc = unsafe {
        let p_arg: *mut c_void = ptr::null_mut();
        ffi::sqlite3_config(ffi::SQLITE_CONFIG_LOG, cb, p_arg)
    };
    if rc != ffi::SQLITE_OK {
        return Err(SqliteError{ code: rc, message: "sqlite3_config(SQLITE_CONFIG_LOG, ...)".to_string() });
    }
    Ok(())
}

/// Write a message into the error log established by `config_log`.
pub fn log(err_code: c_int, msg: &str) {
    let msg = CString::new(msg).unwrap();
    unsafe {
        ffi::sqlite3_log(err_code, msg.as_ptr());
    }
}

/// The trace callback function signature.
pub type TraceCallback =
    Option<extern "C" fn (p_arg: *mut c_void, z_sql: *const c_char)>;
/// The profile callback function signature.
pub type ProfileCallback =
    Option<extern "C" fn (p_arg: *mut c_void, z_sql: *const c_char, nanoseconds: u64)>;

impl SqliteConnection {
    /// Register or clear a callback function that can be used for tracing the execution of SQL statements.
    ///
    /// Prepared statement placeholders are replaced/logged with their assigned values.
    /// There can only be a single tracer defined for each database connection.
    /// Setting a new tracer clears the old one.
    pub fn trace(&mut self, x_trace: TraceCallback) {
        let c = self.db.borrow_mut();
        unsafe { ffi::sqlite3_trace(c.db(), x_trace, ptr::null_mut()); }
    }
    /// Register or clear a callback function that can be used for profiling the execution of SQL statements.
    ///
    /// There can only be a single profiler defined for each database connection.
    /// Setting a new profiler clears the old one.
    pub fn profile(&mut self, x_profile: ProfileCallback) {
        let c = self.db.borrow_mut();
        unsafe { ffi::sqlite3_profile(c.db(), x_profile, ptr::null_mut()); }
    }
}

#[cfg(test)]
mod test {
    use libc::{c_char, c_int, c_void};
    use std::ffi::CStr;
    use std::io::Write;
    use std::str;

    use ffi;
    use SqliteConnection;

    extern "C" fn log_callback(_: *mut c_void, err: c_int, msg: *const c_char) {
        unsafe {
            let c_slice = CStr::from_ptr(msg).to_bytes();
            let _ = writeln!(::std::io::stderr(), "{}: {:?}", err, str::from_utf8(c_slice));
        }
    }

    #[test] #[ignore] // To avoid freezing tests
    fn test_log() {
        unsafe { ffi::sqlite3_shutdown() };
        super::config_log(Some(log_callback)).unwrap();
        //super::log(ffi::SQLITE_NOTICE, "message from rusqlite");
        super::config_log(None).unwrap();
    }

    extern "C" fn trace_callback(_: *mut ::libc::c_void, sql: *const ::libc::c_char) {
        unsafe {
            let c_slice = ::std::ffi::CStr::from_ptr(sql).to_bytes();
            let _ = writeln!(::std::io::stderr(), "TRACE: {:?}", ::std::str::from_utf8(c_slice));
        }
    }

    #[test]
    fn test_trace() {
        let mut db = SqliteConnection::open_in_memory().unwrap();
        db.trace(Some(trace_callback));
        db.execute_batch("PRAGMA application_id = 1").unwrap();
    }
}
<|endoftext|>


<commit_before>use traits::{AtEof, Compare, CompareResult, InputLength, InputIter, InputTake, Slice, FindSubstring, FindToken, ParseTo};
use util::Offset;

use std::str::{self, FromStr, Chars, CharIndices};
use std::ops::{Range, RangeTo, RangeFrom, RangeFull};
use std::iter::{Enumerate, Map};
use std::slice::Iter;

#[derive(Clone, Copy, Debug, PartialEq, Hash)]
pub struct CompleteStr<'a>(pub &'a str);

impl<'a> AtEof for CompleteStr<'a> {
  fn at_eof(&self) -> bool {
    true
  }
}

impl<'a> Slice<Range<usize>> for CompleteStr<'a> {
  fn slice(&self, range: Range<usize>) -> Self {
    CompleteStr(self.0.slice(range))
  }
}

impl<'a> Slice<RangeTo<usize>> for CompleteStr<'a> {
  fn slice(&self, range: RangeTo<usize>) -> Self {
    CompleteStr(self.0.slice(range))
  }
}

impl<'a> Slice<RangeFrom<usize>> for CompleteStr<'a> {
  fn slice(&self, range: RangeFrom<usize>) -> Self {
    CompleteStr(self.0.slice(range))
  }
}

impl<'a> Slice<RangeFull> for CompleteStr<'a> {
  fn slice(&self, range: RangeFull) -> Self {
    CompleteStr(self.0.slice(range))
  }
}

impl<'a> InputIter for CompleteStr<'a> {
  type Item = char;
  type RawItem = char;
  type Iter = CharIndices<'a>;
  type IterElem = Chars<'a>;

  fn iter_indices(&self) -> Self::Iter {
    self.0.iter_indices()
  }
  fn iter_elements(&self) -> Self::IterElem {
    self.0.iter_elements()
  }
  fn position<P>(&self, predicate: P) -> Option<usize>
  where
    P: Fn(Self::RawItem) -> bool,
  {
    self.0.position(predicate)
  }
  fn slice_index(&self, count: usize) -> Option<usize> {
    self.0.slice_index(count)
  }
}

impl<'a> InputTake for CompleteStr<'a> {
  fn take(&self, count: usize) -> Option<Self> {
    self.0.take(count).map(|s| CompleteStr(s))
  }

  fn take_split(&self, count: usize) -> Option<(Self, Self)> {
    self.0.take_split(count).map(|(s1, s2)| {
      (CompleteStr(s1), CompleteStr(s2))
    })
  }
}

impl<'a> InputLength for CompleteStr<'a> {
  fn input_len(&self) -> usize {
    self.0.input_len()
  }
}

impl<'a, 'b> Compare<&'b str> for CompleteStr<'a> {
  fn compare(&self, t: &'b str) -> CompareResult {
    self.0.compare(t)
  }
  fn compare_no_case(&self, t: &'b str) -> CompareResult {
    self.0.compare_no_case(t)
  }
}

impl<'a, 'b> FindSubstring<&'b str> for CompleteStr<'a> {
  fn find_substring(&self, substr: &'b str) -> Option<usize> {
    self.0.find_substring(substr)
  }
}

impl<'a> FindToken<char> for CompleteStr<'a> {
  fn find_token(&self, token: char) -> bool {
    self.0.find_token(token)
  }
}

impl<'a> FindToken<u8> for CompleteStr<'a> {
  fn find_token(&self, token: u8) -> bool {
    self.0.find_token(token)
  }
}

impl<'a, 'b> FindToken<&'a u8> for CompleteStr<'b> {
  fn find_token(&self, token: &u8) -> bool {
    self.0.find_token(token)
  }
}

impl<'a, R: FromStr> ParseTo<R> for CompleteStr<'a> {
  fn parse_to(&self) -> Option<R> {
    self.0.parse().ok()
  }
}

impl<'a> Offset for CompleteStr<'a> {
  fn offset(&self, second: &CompleteStr<'a>) -> usize {
    self.0.offset(second.0)
  }
}

#[derive(Clone, Copy, Debug, PartialEq, Hash)]
pub struct CompleteByteSlice<'a>(pub &'a [u8]);

impl<'a> AtEof for CompleteByteSlice<'a> {
  fn at_eof(&self) -> bool {
    true
  }
}

impl<'a> Slice<Range<usize>> for CompleteByteSlice<'a> {
  fn slice(&self, range: Range<usize>) -> Self {
    CompleteByteSlice(self.0.slice(range))
  }
}

impl<'a> Slice<RangeTo<usize>> for CompleteByteSlice<'a> {
  fn slice(&self, range: RangeTo<usize>) -> Self {
    CompleteByteSlice(self.0.slice(range))
  }
}

impl<'a> Slice<RangeFrom<usize>> for CompleteByteSlice<'a> {
  fn slice(&self, range: RangeFrom<usize>) -> Self {
    CompleteByteSlice(self.0.slice(range))
  }
}

impl<'a> Slice<RangeFull> for CompleteByteSlice<'a> {
  fn slice(&self, range: RangeFull) -> Self {
    CompleteByteSlice(self.0.slice(range))
  }
}

impl<'a> InputIter for CompleteByteSlice<'a> {
  type Item = u8;
  type RawItem = u8;
  type Iter = Enumerate<Self::IterElem>;
  type IterElem = Map<Iter<'a, Self::Item>, fn(&u8) -> u8>; //Iter<'a, Self::RawItem>;

  fn iter_indices(&self) -> Self::Iter {
    self.0.iter_indices()
  }
  fn iter_elements(&self) -> Self::IterElem {
    self.0.iter_elements()
  }
  fn position<P>(&self, predicate: P) -> Option<usize>
  where
    P: Fn(Self::RawItem) -> bool,
  {
    self.0.position(predicate)
  }
  fn slice_index(&self, count: usize) -> Option<usize> {
    self.0.slice_index(count)
  }
}

impl<'a> InputTake for CompleteByteSlice<'a> {
  fn take(&self, count: usize) -> Option<Self> {
    self.0.take(count).map(|s| CompleteByteSlice(s))
  }

  fn take_split(&self, count: usize) -> Option<(Self, Self)> {
    self.0.take_split(count).map(|(s1, s2)| {
      (CompleteByteSlice(s1), CompleteByteSlice(s2))
    })
  }
}

impl<'a> InputLength for CompleteByteSlice<'a> {
  fn input_len(&self) -> usize {
    self.0.input_len()
  }
}

impl<'a, 'b> Compare<&'b [u8]> for CompleteByteSlice<'a> {
  fn compare(&self, t: &'b [u8]) -> CompareResult {
    self.0.compare(t)
  }
  fn compare_no_case(&self, t: &'b [u8]) -> CompareResult {
    self.0.compare_no_case(t)
  }
}

impl<'a, 'b> Compare<&'b str> for CompleteByteSlice<'a> {
  fn compare(&self, t: &'b str) -> CompareResult {
    self.0.compare(t)
  }
  fn compare_no_case(&self, t: &'b str) -> CompareResult {
    self.0.compare_no_case(t)
  }
}

impl<'a, 'b> FindSubstring<&'b [u8]> for CompleteByteSlice<'a> {
  fn find_substring(&self, substr: &'b [u8]) -> Option<usize> {
    self.0.find_substring(substr)
  }
}

impl<'a, 'b> FindSubstring<&'b str> for CompleteByteSlice<'a> {
  fn find_substring(&self, substr: &'b str) -> Option<usize> {
    self.0.find_substring(substr)
  }
}

impl<'a> FindToken<char> for CompleteByteSlice<'a> {
  fn find_token(&self, token: char) -> bool {
    self.0.find_token(token)
  }
}

impl<'a> FindToken<u8> for CompleteByteSlice<'a> {
  fn find_token(&self, token: u8) -> bool {
    self.0.find_token(token)
  }
}

impl<'a, 'b> FindToken<&'a u8> for CompleteByteSlice<'b> {
  fn find_token(&self, token: &u8) -> bool {
    self.0.find_token(token)
  }
}

impl<'a, R: FromStr> ParseTo<R> for CompleteByteSlice<'a> {
  fn parse_to(&self) -> Option<R> {
    self.0.parse_to()
  }
}

impl<'a> Offset for CompleteByteSlice<'a> {
  fn offset(&self, second: &CompleteByteSlice<'a>) -> usize {
    self.0.offset(second.0)
  }
}

#[cfg(feature = "std")]
impl<'a> super::util::HexDisplay for CompleteByteSlice<'a> {
  fn to_hex(&self, chunk_size: usize) -> String {
    self.0.to_hex(chunk_size)
  }

  fn to_hex_from(&self, chunk_size: usize, from: usize) -> String {
    self.0.to_hex_from(chunk_size, from)
  }
}
<commit_msg>impl AsBytes for Complete*<commit_after>use traits::{AtEof, Compare, CompareResult, InputLength, InputIter, InputTake, Slice, FindSubstring, FindToken, ParseTo, AsBytes};
use util::Offset;

use std::str::{self, FromStr, Chars, CharIndices};
use std::ops::{Range, RangeTo, RangeFrom, RangeFull};
use std::iter::{Enumerate, Map};
use std::slice::Iter;

#[derive(Clone, Copy, Debug, PartialEq, Hash)]
pub struct CompleteStr<'a>(pub &'a str);

impl<'a> AtEof for CompleteStr<'a> {
  fn at_eof(&self) -> bool {
    true
  }
}

impl<'a> Slice<Range<usize>> for CompleteStr<'a> {
  fn slice(&self, range: Range<usize>) -> Self {
    CompleteStr(self.0.slice(range))
  }
}

impl<'a> Slice<RangeTo<usize>> for CompleteStr<'a> {
  fn slice(&self, range: RangeTo<usize>) -> Self {
    CompleteStr(self.0.slice(range))
  }
}

impl<'a> Slice<RangeFrom<usize>> for CompleteStr<'a> {
  fn slice(&self, range: RangeFrom<usize>) -> Self {
    CompleteStr(self.0.slice(range))
  }
}

impl<'a> Slice<RangeFull> for CompleteStr<'a> {
  fn slice(&self, range: RangeFull) -> Self {
    CompleteStr(self.0.slice(range))
  }
}

impl<'a> InputIter for CompleteStr<'a> {
  type Item = char;
  type RawItem = char;
  type Iter = CharIndices<'a>;
  type IterElem = Chars<'a>;

  fn iter_indices(&self) -> Self::Iter {
    self.0.iter_indices()
  }
  fn iter_elements(&self) -> Self::IterElem {
    self.0.iter_elements()
  }
  fn position<P>(&self, predicate: P) -> Option<usize>
  where
    P: Fn(Self::RawItem) -> bool,
  {
    self.0.position(predicate)
  }
  fn slice_index(&self, count: usize) -> Option<usize> {
    self.0.slice_index(count)
  }
}

impl<'a> InputTake for CompleteStr<'a> {
  fn take(&self, count: usize) -> Option<Self> {
    self.0.take(count).map(|s| CompleteStr(s))
  }

  fn take_split(&self, count: usize) -> Option<(Self, Self)> {
    self.0.take_split(count).map(|(s1, s2)| {
      (CompleteStr(s1), CompleteStr(s2))
    })
  }
}

impl<'a> InputLength for CompleteStr<'a> {
  fn input_len(&self) -> usize {
    self.0.input_len()
  }
}

impl<'a, 'b> Compare<&'b str> for CompleteStr<'a> {
  fn compare(&self, t: &'b str) -> CompareResult {
    self.0.compare(t)
  }
  fn compare_no_case(&self, t: &'b str) -> CompareResult {
    self.0.compare_no_case(t)
  }
}

impl<'a, 'b> FindSubstring<&'b str> for CompleteStr<'a> {
  fn find_substring(&self, substr: &'b str) -> Option<usize> {
    self.0.find_substring(substr)
  }
}

impl<'a> FindToken<char> for CompleteStr<'a> {
  fn find_token(&self, token: char) -> bool {
    self.0.find_token(token)
  }
}

impl<'a> FindToken<u8> for CompleteStr<'a> {
  fn find_token(&self, token: u8) -> bool {
    self.0.find_token(token)
  }
}

impl<'a, 'b> FindToken<&'a u8> for CompleteStr<'b> {
  fn find_token(&self, token: &u8) -> bool {
    self.0.find_token(token)
  }
}

impl<'a, R: FromStr> ParseTo<R> for CompleteStr<'a> {
  fn parse_to(&self) -> Option<R> {
    self.0.parse().ok()
  }
}

impl<'a> Offset for CompleteStr<'a> {
  fn offset(&self, second: &CompleteStr<'a>) -> usize {
    self.0.offset(second.0)
  }
}

impl<'a> AsBytes for CompleteStr<'a> {
  fn as_bytes(&self) -> &[u8] {
    self.0.as_bytes()
  }
}

#[derive(Clone, Copy, Debug, PartialEq, Hash)]
pub struct CompleteByteSlice<'a>(pub &'a [u8]);

impl<'a> AtEof for CompleteByteSlice<'a> {
  fn at_eof(&self) -> bool {
    true
  }
}

impl<'a> Slice<Range<usize>> for CompleteByteSlice<'a> {
  fn slice(&self, range: Range<usize>) -> Self {
    CompleteByteSlice(self.0.slice(range))
  }
}

impl<'a> Slice<RangeTo<usize>> for CompleteByteSlice<'a> {
  fn slice(&self, range: RangeTo<usize>) -> Self {
    CompleteByteSlice(self.0.slice(range))
  }
}

impl<'a> Slice<RangeFrom<usize>> for CompleteByteSlice<'a> {
  fn slice(&self, range: RangeFrom<usize>) -> Self {
    CompleteByteSlice(self.0.slice(range))
  }
}

impl<'a> Slice<RangeFull> for CompleteByteSlice<'a> {
  fn slice(&self, range: RangeFull) -> Self {
    CompleteByteSlice(self.0.slice(range))
  }
}

impl<'a> InputIter for CompleteByteSlice<'a> {
  type Item = u8;
  type RawItem = u8;
  type Iter = Enumerate<Self::IterElem>;
  type IterElem = Map<Iter<'a, Self::Item>, fn(&u8) -> u8>; //Iter<'a, Self::RawItem>;

  fn iter_indices(&self) -> Self::Iter {
    self.0.iter_indices()
  }
  fn iter_elements(&self) -> Self::IterElem {
    self.0.iter_elements()
  }
  fn position<P>(&self, predicate: P) -> Option<usize>
  where
    P: Fn(Self::RawItem) -> bool,
  {
    self.0.position(predicate)
  }
  fn slice_index(&self, count: usize) -> Option<usize> {
    self.0.slice_index(count)
  }
}

impl<'a> InputTake for CompleteByteSlice<'a> {
  fn take(&self, count: usize) -> Option<Self> {
    self.0.take(count).map(|s| CompleteByteSlice(s))
  }

  fn take_split(&self, count: usize) -> Option<(Self, Self)> {
    self.0.take_split(count).map(|(s1, s2)| {
      (CompleteByteSlice(s1), CompleteByteSlice(s2))
    })
  }
}

impl<'a> InputLength for CompleteByteSlice<'a> {
  fn input_len(&self) -> usize {
    self.0.input_len()
  }
}

impl<'a, 'b> Compare<&'b [u8]> for CompleteByteSlice<'a> {
  fn compare(&self, t: &'b [u8]) -> CompareResult {
    self.0.compare(t)
  }
  fn compare_no_case(&self, t: &'b [u8]) -> CompareResult {
    self.0.compare_no_case(t)
  }
}

impl<'a, 'b> Compare<&'b str> for CompleteByteSlice<'a> {
  fn compare(&self, t: &'b str) -> CompareResult {
    self.0.compare(t)
  }
  fn compare_no_case(&self, t: &'b str) -> CompareResult {
    self.0.compare_no_case(t)
  }
}

impl<'a, 'b> FindSubstring<&'b [u8]> for CompleteByteSlice<'a> {
  fn find_substring(&self, substr: &'b [u8]) -> Option<usize> {
    self.0.find_substring(substr)
  }
}

impl<'a, 'b> FindSubstring<&'b str> for CompleteByteSlice<'a> {
  fn find_substring(&self, substr: &'b str) -> Option<usize> {
    self.0.find_substring(substr)
  }
}

impl<'a> FindToken<char> for CompleteByteSlice<'a> {
  fn find_token(&self, token: char) -> bool {
    self.0.find_token(token)
  }
}

impl<'a> FindToken<u8> for CompleteByteSlice<'a> {
  fn find_token(&self, token: u8) -> bool {
    self.0.find_token(token)
  }
}

impl<'a, 'b> FindToken<&'a u8> for CompleteByteSlice<'b> {
  fn find_token(&self, token: &u8) -> bool {
    self.0.find_token(token)
  }
}

impl<'a, R: FromStr> ParseTo<R> for CompleteByteSlice<'a> {
  fn parse_to(&self) -> Option<R> {
    self.0.parse_to()
  }
}

impl<'a> Offset for CompleteByteSlice<'a> {
  fn offset(&self, second: &CompleteByteSlice<'a>) -> usize {
    self.0.offset(second.0)
  }
}

impl<'a> AsBytes for CompleteByteSlice<'a> {
  fn as_bytes(&self) -> &[u8] {
    self.0.as_bytes()
  }
}

#[cfg(feature = "std")]
impl<'a> super::util::HexDisplay for CompleteByteSlice<'a> {
  fn to_hex(&self, chunk_size: usize) -> String {
    self.0.to_hex(chunk_size)
  }

  fn to_hex_from(&self, chunk_size: usize, from: usize) -> String {
    self.0.to_hex_from(chunk_size, from)
  }
}
<|endoftext|>


<commit_before>//! A collection of ascii representation (u8) utility functions

use std::str::{self, FromStr};

pub trait Ascii: Clone {
  fn is_alpha(self) -> bool;
  fn is_digit(self) -> bool;
  fn is_hex_digit(self) -> bool;
  fn is_space(self) -> bool;
}

impl Ascii for u8 {
  /// Returns true if this `u8` is an alphabetic code point, and false if not.
  #[inline]
  fn is_alpha(self) -> bool {
    match self {
      b'a'...b'z' | b'A'...b'Z' => true,
      _ => false
    }
  }

  /// Checks if a `char` is a digit in a radix of ten.
  #[inline]
  fn is_digit(self) -> bool {
    b'0' <= self && self <= b'9'
  }

  /// Checks if a `char` is a digit in a radix of hexadecimal.
  #[inline]
  fn is_hex_digit(self) -> bool {
    match self {
      b'0'...b'9' | b'a'...b'f' | b'A'...b'F' => true,
      _ => false
    }
  }

  #[inline]
  fn is_space(self) -> bool {
    match self {
      b' ' | b'\t' | b'\n' | b'\r' => true,
      _ => false
    }
  }
}

/// Convert a string to a i64 value and
/// return a remaning part which is not valid in decimal
pub fn strtoi<'a>(s: &'a [u8], start_idx: usize) -> (i32, Option<&'a [u8]>) {
  let (val, remain) = strtol(s, start_idx);
  if val != ((val as i32) as i64) {
    panic!(format!("integer overflow in strtoi: {}",
      unsafe { str::from_utf8_unchecked(s) }));
  }
  return (val as i32, remain);
}

/// Convert a string to a i64 value and
/// return a remaning part which is not valid in decimal
pub fn strtol<'a>(s: &'a [u8], start_idx: usize) -> (i64, Option<&'a [u8]>) {
  debug_assert!(s.len() > start_idx, format!(
    "the length of input string must be greater than start_idx, \
    but length = {} and start_idx = {}", s.len(), start_idx));

  let bytes = &s[start_idx..];
  let last_digit_idx = match bytes.iter().position(|&c| !c.is_digit()) {
    Some(idx) => idx,
    None => bytes.len()
  };

  let val = i64::from_str(unsafe {
      str::from_utf8_unchecked(&bytes[..last_digit_idx])
    }).ok().unwrap();

  let remain = if s.len() == last_digit_idx {
    None
  } else {
    Some(&bytes[last_digit_idx..])
  };

  (val, remain)
}

#[cfg(test)]
mod tests {
  use super::{strtoi, strtol};

  #[test]
  fn test_strtol() {
    let (val, remain) = strtol(b"12345", 0);
    assert_eq!(12345i64, val);
    assert!(remain.is_none());

    let (val, remain) = strtol(b"12345l", 0);
    assert_eq!(12345i64, val);
    assert!(remain.is_some());
    assert_eq!(b"l", remain.unwrap());

    let (val, remain) = strtol(b"12345lll", 0);
    assert_eq!(12345i64, val);
    assert!(remain.is_some());
    assert_eq!(b"lll", remain.unwrap());
  }

  #[test]
  #[should_panic]
  fn test_strtoi_overflow() {
    strtoi(b"123456789012345lll", 0);
  }
}<commit_msg>Fix out of index range bug in ascii::strtol<commit_after>//! A collection of ascii representation (u8) utility functions

use std::str::{self, FromStr};

pub trait Ascii: Clone {
  fn is_alpha(self) -> bool;
  fn is_digit(self) -> bool;
  fn is_hex_digit(self) -> bool;
  fn is_space(self) -> bool;
}

impl Ascii for u8 {
  /// Returns true if this `u8` is an alphabetic code point, and false if not.
  #[inline]
  fn is_alpha(self) -> bool {
    match self {
      b'a'...b'z' | b'A'...b'Z' => true,
      _ => false
    }
  }

  /// Checks if a `char` is a digit in a radix of ten.
  #[inline]
  fn is_digit(self) -> bool {
    b'0' <= self && self <= b'9'
  }

  /// Checks if a `char` is a digit in a radix of hexadecimal.
  #[inline]
  fn is_hex_digit(self) -> bool {
    match self {
      b'0'...b'9' | b'a'...b'f' | b'A'...b'F' => true,
      _ => false
    }
  }

  #[inline]
  fn is_space(self) -> bool {
    match self {
      b' ' | b'\t' | b'\n' | b'\r' => true,
      _ => false
    }
  }
}

/// Convert a string to a i64 value and
/// return a remaning part which is not valid in decimal
pub fn strtoi<'a>(s: &'a [u8], start_idx: usize) -> (i32, Option<&'a [u8]>) {
  let (val, remain) = strtol(s, start_idx);
  if val != ((val as i32) as i64) {
    panic!(format!("integer overflow in strtoi: {}",
      unsafe { str::from_utf8_unchecked(s) }));
  }
  return (val as i32, remain);
}

/// Convert a string to a i64 value and
/// return a remaning part which is not valid in decimal
pub fn strtol<'a>(s: &'a [u8], start_idx: usize) -> (i64, Option<&'a [u8]>) {
  debug_assert!(s.len() > start_idx, format!(
    "the length of input string must be greater than start_idx, \
    but length = {} and start_idx = {}", s.len(), start_idx));

  let bytes = &s[start_idx..];
  let last_digit_idx = match bytes.iter().position(|&c| !c.is_digit()) {
    Some(idx) => idx,
    None => bytes.len()
  };

  let val = i64::from_str(unsafe {
      str::from_utf8_unchecked(&bytes[..last_digit_idx])
    }).ok().unwrap();

  let remain = if bytes.len() == last_digit_idx {
    None
  } else {
    Some(&bytes[last_digit_idx..])
  };

  (val, remain)
}

#[cfg(test)]
mod tests {
  use super::{strtoi, strtol};

  #[test]
  fn test_strtol() {
    let (val, remain) = strtol(b"12345", 0);
    assert_eq!(12345i64, val);
    assert!(remain.is_none());

    let (val, remain) = strtol(b"12345l", 0);
    assert_eq!(12345i64, val);
    assert!(remain.is_some());
    assert_eq!(b"l", remain.unwrap());

    let (val, remain) = strtol(b"12345lll", 0);
    assert_eq!(12345i64, val);
    assert!(remain.is_some());
    assert_eq!(b"lll", remain.unwrap());

    let (val, remain) = strtol(b"+1", 1);
    assert_eq!(1, val);
    assert!(remain.is_none());
  }

  #[test]
  #[should_panic]
  fn test_strtoi_overflow() {
    strtoi(b"123456789012345lll", 0);
  }
}<|endoftext|>


<commit_before>//! Types representing for data which will be retrieved from the database.
//! Currently this data is expected to look like a JSON object but this may be
//! changed in the future. Driver authors must cast the data they retrieve from
//! the database to these types.

use std::collections::BTreeMap;
use structure::Collection;

/// The atomic level of a pointer.
pub type Key = String;

/// Represents a JSON pointer to a document property.
pub type Pointer = Vec<Key>;

/// Various value types. Based on types in the [JSON standard][1] (see section
/// 5).
///
/// [1]: http://ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf
pub enum Value {
  /// The abscense of any value.
  Null,
  /// True or false.
  Boolean(bool),
  /// A numeric value, float, integer, whatever.
  Number(f64),
  /// A list of characters.
  String(String),
  /// A list of values.
  Array(Vec<Value>),
  /// A map of key/value pairs.
  Object(BTreeMap<Key, Value>)
}

/// A schema detailing what the data received from the database (or inserted
/// into the database) should be. Inspired after [JSON Schema][1]. A reference
/// on JSON Schema type-specific validations used in this enum may be found
/// [here][2].
///
/// [1]: http://json-schema.org
/// [2]: http://spacetelescope.github.io/understanding-json-schema/reference/type.html
pub enum Schema {
  /// Represents the absence of any value.
  Null,
  /// Represents a binary true/false value.
  Boolean,
  /// Represents a numeric type.
  Number {
    /// Forces the number to be a multiple of another. This helps in specifying
    /// integers if this value is `Some(1)` for example.
    multiple_of: Option<f32>,
    /// The minimum value the number can be.
    minimum: Option<f64>,
    /// The maximum value the number can be.
    maximum: Option<f64>
  },
  String {
    /// The mimimum length of characters in the string.
    min_length: Option<u64>,
    /// The maximum length of characters in the string.
    max_length: Option<u64>,
    /// A regular expression pattern to validate the string against.
    // TODO: Use a regex crate.
    pattern: Option<String>
  },
  /// Represents a set of any type.
  Array {
    /// A schema which all items in the array must match.
    items: Option<Schema>
  },
  /// Represents a set of key/value pairs.
  Object {
    /// Schemas associated to the object properties.
    properties: BTreeMap<String, Schema>,
    /// Properties that are required to be in the object.
    required: Vec<String>,
    /// Whether or not there may be extra properties outside of the ones
    /// defined by the properties map.
    additional_properties: bool
  },
  /// Represents a value which *must* be one of the defined values.
  Enum(Vec<Value>)
}

/// Different database collection property updates.
pub enum Patch {
  /// Set a property to a new value.
  Set(Pointer, Value),
  /// Reset a property to its default value.
  Reset(Pointer),
  /// Remove a property from the database entirely.
  Remove(Pointer)
}

/// A recursive filter condition for a `Value`.
pub enum Filter {
  /// Combine multiple filters with an “and” operator.
  And(Vec<Filter>),
  /// Combine multiple filters with an “or” operator.
  Or(Vec<Filter>),
  /// Inverts the filter.
  Not(Box<Filter>),
  /// The basic condition of a filter.
  Condition(Pointer, FilterCondition)
}

pub enum FilterCondition {
  Equal(Value),
  OneOf(Vec<Value>),
  GreaterThan(Value),
  LessThan(Value)
}

/// A single way in which to order a collection of documents.
pub struct Ordering(Pointer, OrderDirection);

pub enum OrderDirection {
  Ascending,
  Descending
}

// TODO: Find a more Rust idiomatic solution for ranges.
pub struct Range {
  from: Option<u32>,
  to: Option<u32>
}
<commit_msg>fix: recursive type<commit_after>//! Types representing for data which will be retrieved from the database.
//! Currently this data is expected to look like a JSON object but this may be
//! changed in the future. Driver authors must cast the data they retrieve from
//! the database to these types.

use std::collections::BTreeMap;
use structure::Collection;

/// The atomic level of a pointer.
pub type Key = String;

/// Represents a JSON pointer to a document property.
pub type Pointer = Vec<Key>;

/// Various value types. Based on types in the [JSON standard][1] (see section
/// 5).
///
/// [1]: http://ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf
pub enum Value {
  /// The abscense of any value.
  Null,
  /// True or false.
  Boolean(bool),
  /// A numeric value, float, integer, whatever.
  Number(f64),
  /// A list of characters.
  String(String),
  /// A list of values.
  Array(Vec<Value>),
  /// A map of key/value pairs.
  Object(BTreeMap<Key, Value>)
}

/// A schema detailing what the data received from the database (or inserted
/// into the database) should be. Inspired after [JSON Schema][1]. A reference
/// on JSON Schema type-specific validations used in this enum may be found
/// [here][2].
///
/// [1]: http://json-schema.org
/// [2]: http://spacetelescope.github.io/understanding-json-schema/reference/type.html
pub enum Schema {
  /// Represents the absence of any value.
  Null,
  /// Represents a binary true/false value.
  Boolean,
  /// Represents a numeric type.
  Number {
    /// Forces the number to be a multiple of another. This helps in specifying
    /// integers if this value is `Some(1)` for example.
    multiple_of: Option<f32>,
    /// The minimum value the number can be.
    minimum: Option<f64>,
    /// The maximum value the number can be.
    maximum: Option<f64>
  },
  String {
    /// The mimimum length of characters in the string.
    min_length: Option<u64>,
    /// The maximum length of characters in the string.
    max_length: Option<u64>,
    /// A regular expression pattern to validate the string against.
    // TODO: Use a regex crate.
    pattern: Option<String>
  },
  /// Represents a set of any type.
  Array {
    /// A schema which all items in the array must match.
    items: Option<Box<Schema>>
  },
  /// Represents a set of key/value pairs.
  Object {
    /// Schemas associated to the object properties.
    properties: BTreeMap<String, Schema>,
    /// Properties that are required to be in the object.
    required: Vec<String>,
    /// Whether or not there may be extra properties outside of the ones
    /// defined by the properties map.
    additional_properties: bool
  },
  /// Represents a value which *must* be one of the defined values.
  Enum(Vec<Value>)
}

/// Different database collection property updates.
pub enum Patch {
  /// Set a property to a new value.
  Set(Pointer, Value),
  /// Reset a property to its default value.
  Reset(Pointer),
  /// Remove a property from the database entirely.
  Remove(Pointer)
}

/// A recursive filter condition for a `Value`.
pub enum Filter {
  /// Combine multiple filters with an “and” operator.
  And(Vec<Filter>),
  /// Combine multiple filters with an “or” operator.
  Or(Vec<Filter>),
  /// Inverts the filter.
  Not(Box<Filter>),
  /// The basic condition of a filter.
  Condition(Pointer, FilterCondition)
}

pub enum FilterCondition {
  Equal(Value),
  OneOf(Vec<Value>),
  GreaterThan(Value),
  LessThan(Value)
}

/// A single way in which to order a collection of documents.
pub struct Ordering(Pointer, OrderDirection);

pub enum OrderDirection {
  Ascending,
  Descending
}

// TODO: Find a more Rust idiomatic solution for ranges.
pub struct Range {
  from: Option<u32>,
  to: Option<u32>
}
<|endoftext|>