<commit_before>use std::io::{IoError, Stream};
use std::io::net::ip;
use {Header, HTTPVersion, Method, Response, StatusCode};
use util::{AnyReader, AnyWriter};

/// Represents an HTTP request made by a client.
///
/// A `Request` object is what is produced by the server, and is your what
///  your code must analyse and answer.
///
/// This object implements the `Send` trait, therefore you can dispatch your requests to
///  worker threads.
///
/// # Pipelining
///
/// If a client sends multiple requests in a row (without waiting for the response), then you will
///  get multiple `Request` objects simultaneously. This is called *requests pipelining*.
/// Tiny-http automatically reorders the responses so that you don't need to worry about the order
///  in which you call `respond` or `into_writer`.
///
/// This mechanic is disabled if:
///
///  - The body of a request is large enough (handling requires pipelining requires storing the
///     body of the request in a buffer ; if the body is too big, tiny-http will avoid doing that)
///  - A request sends a `Expect: 100-continue` header (which means that the client waits to
///     know whether its body will be processed before sending it)
///  - A request sends a `Connection: close` header or `Connection: upgrade` header (used for
///     websockets), which indicates that this is the last request that will be received on this
///     connection
///
/// # Automatic cleanup
///
/// If a `Request` object is destroyed without `into_writer` or `respond` being called,
///  an empty response with a 500 status code (internal server error) will automatically be
///  sent back to the client.
/// This means that if your code fails during the handling of a request, this "internal server
///  error" response will automatically be sent during the stack unwinding.
#[unstable]
pub struct Request {
    // where to read the body from
    data_reader: Option<AnyReader>,

    // if this writer is empty, then the request has been answered
    response_writer: Option<AnyWriter>,

    remote_addr: ip::SocketAddr,

    method: Method,

    path: ::url::Path,

    http_version: HTTPVersion,

    headers: Vec<Header>,

    body_length: Option<uint>,

    // true if a `100 Continue` response must be sent when `as_reader()` is called
    must_send_continue: bool,
}

/// Error that can happen when building a `Request` object.
pub enum RequestCreationError {
    ExpectationFailed,
    CreationIoError(IoError),
}

// this trait is to make sure that Request implements Send
trait MustBeSendDummy : Send {}
impl MustBeSendDummy for Request {}

/// Builds a new request
pub fn new_request<R: Reader + Send, W: Writer + Send>(method: Method, path: ::url::Path,
                             version: HTTPVersion, headers: Vec<Header>,
                             remote_addr: ip::SocketAddr, mut source_data: R, writer: W)
    -> Result<Request, RequestCreationError>
{
    // finding the transfer-encoding header
    let transfer_encoding = headers.iter()
        .find(|h: &&Header| h.field.equiv(&"Transfer-Encoding"))
        .map(|h| h.value.clone());

    // finding the content-length header
    let content_length = if transfer_encoding.is_some() {
        // if transfer-encoding is specified, the Content-Length
        //  header must be ignored (RFC2616 #4.4)
        None

    } else {
        headers.iter()
            .find(|h: &&Header| h.field.equiv(&"Content-Length"))
            .and_then(|h| from_str::<uint>(h.value.as_slice()))
    };

    // true if the client sent a `Expect: 100-continue` header
    let expects_continue = {
        use std::ascii::StrAsciiExt;

        match headers.iter().find(|h: &&Header| h.field.equiv(&"Expect")) {
            None => false,
            Some(h) if h.value.as_slice().eq_ignore_ascii_case("100-continue")
                => true,
            _ => return Err(ExpectationFailed)
        }
    };

    // building the reader depending on
    //  transfer-encoding and content-length
    let reader =
        if content_length.is_some() {
            let content_length = content_length.as_ref().unwrap().clone();

            if content_length == 0 {
                use std::io::util::NullReader;
                box NullReader as Box<Reader + Send>

            } else if content_length <= 1024 && !expects_continue {
                use std::io::MemReader;
                let data = try!(source_data.read_exact(content_length)
                    .map_err(|e| CreationIoError(e)));
                box MemReader::new(data) as Box<Reader + Send>

            } else {
                use util::EqualReader;
                let (data_reader, _) = EqualReader::new(source_data, content_length);   // TODO:
                box data_reader as Box<Reader + Send>
            }

        } else if transfer_encoding.is_some() {
            // if a transfer-encoding was specified, then "chunked"
            //  is ALWAYS applied over the message (RFC2616 #3.6)
            use util::ChunksDecoder;
            box ChunksDecoder::new(source_data) as Box<Reader + Send>

        } else {
            // if we have neither a Content-Length nor a Transfer-Encoding,
            //  assuming that we have no data
            // TODO: could also be multipart/byteranges
            use std::io::util::NullReader;
            box NullReader as Box<Reader + Send>
        };

    Ok(Request {
        data_reader: Some(AnyReader::new(reader)),
        response_writer: Some(AnyWriter::new(box writer as Box<Writer + Send>)),
        remote_addr: remote_addr,
        method: method,
        path: path,
        http_version: version,
        headers: headers,
        body_length: content_length,
        must_send_continue: expects_continue,
    })
}

impl Request {
    /// Returns the method requested by the client (eg. `GET`, `POST`, etc.).
    #[stable]
    #[inline]
    pub fn get_method<'a>(&'a self) -> &'a Method {
        &self.method
    }

    /// Returns the resource requested by the client.
    #[unstable]
    #[inline]
    pub fn get_url<'a>(&'a self) -> &'a ::url::Path {
        &self.path
    }

    /// Returns a list of all headers sent by the client.
    #[stable]
    #[inline]
    pub fn get_headers<'a>(&'a self) -> &'a [Header] {
        self.headers.as_slice()
    }

    /// Returns the HTTP version of the request.
    #[unstable]
    #[inline]
    pub fn get_http_version<'a>(&'a self) -> &'a HTTPVersion {
        &self.http_version
    }

    /// Returns the length of the body in bytes.
    ///
    /// Returns `None` if the length is unknown.
    #[unstable]
    #[inline]
    pub fn get_body_length(&self) -> Option<uint> {
        self.body_length
    }

    /// Returns the length of the body in bytes.
    #[stable]
    #[inline]
    pub fn get_remote_addr<'a>(&'a self) -> &'a ip::SocketAddr {
        &self.remote_addr
    }

    /// Turns the `Request` into a `Stream`.
    ///
    /// The main purpose of this function is to support websockets.
    /// If you detect that the request wants to use some kind of protocol upgrade, you can
    ///  call this function to obtain full control of the socket stream.
    /// Keep in mind that it is up to you to send the initial response.
    ///
    /// If you call this on a non-websocket request, tiny-http will wait until this `Stream` object
    ///  is destroyed before continuing to read or write on the socket. Therefore you should always
    ///  destroy it as soon as possible.
    #[unstable]
    pub fn into_stream(mut self) -> Box<Stream + Send> {
        use util::CustomStream;

        let stream = CustomStream::new(self.into_reader_impl(), self.into_writer_impl());
        box stream as Box<Stream + Send>
    }

    /// Allows to read the body of the request.
    ///
    /// # Example
    ///
    /// ```
    /// let request = server.recv();
    ///
    /// if get_content_type(&request) == "application/json" {
    ///     let json: Json = from_str(request.as_reader().read_to_string()).unwrap();
    /// }
    /// ```
    ///
    /// If the client sent a `Expect: 100-continue` header with the request, calling this
    ///  function will send back a `100 Continue` response.
    #[unstable]
    #[inline]
    pub fn as_reader<'a>(&'a mut self) -> &'a mut Reader {
        if self.must_send_continue {
            let msg = Response::new_empty(StatusCode(100));
            msg.raw_print(self.response_writer.as_mut().unwrap().by_ref(),
                self.http_version, self.headers.as_slice(), true).ok();
            self.response_writer.as_mut().unwrap().flush().ok();
            self.must_send_continue = false;
        }

        fn passthrough<'a>(r: &'a mut Reader) -> &'a mut Reader { r }
        passthrough(self.data_reader.as_mut().unwrap())
    }

    /// Turns the `Request` into a writer.
    ///
    /// The writer has a raw access to the stream to the user.
    /// This function is useful for things like CGI.
    ///
    /// Note that the destruction of the `Writer` object may trigger
    ///  some events. For exemple if a client has sent multiple requests and the requests
    ///  have been processed in parallel, the destruction of a writer will trigger
    ///  the writing of the next response.
    /// Therefore you should always destroy the `Writer` as soon as possible.
    #[stable]
    #[inline]
    pub fn into_writer(mut self) -> Box<Writer + Send> {
        self.into_writer_impl().unwrap()
    }

    fn into_writer_impl(&mut self) -> AnyWriter {
        use std::mem;

        assert!(self.response_writer.is_some());

        let mut writer = None;
        mem::swap(&mut self.response_writer, &mut writer);
        writer.unwrap()
    }

    fn into_reader_impl(&mut self) -> AnyReader {
        use std::mem;

        assert!(self.data_reader.is_some());

        let mut reader = None;
        mem::swap(&mut self.data_reader, &mut reader);
        reader.unwrap()
    }

    /// Sends a response to this request.
    #[unstable]
    #[inline]
    pub fn respond<R: Reader>(mut self, response: Response<R>) {
        self.respond_impl(response)
    }

    fn respond_impl<R: Reader>(&mut self, response: Response<R>) {
        use std::io;

        let mut writer = self.into_writer_impl();

        let do_not_send_body = self.method.equiv(&"HEAD");

        match response.raw_print(writer.by_ref(),
                                self.http_version, self.headers.as_slice(),
                                do_not_send_body)
        {
            Ok(_) => (),
            Err(ref err) if err.kind == io::Closed => (),
            Err(ref err) if err.kind == io::BrokenPipe => (),
            Err(ref err) if err.kind == io::ConnectionAborted => (),
            Err(ref err) if err.kind == io::ConnectionRefused => (),
            Err(ref err) if err.kind == io::ConnectionReset => (),
            Err(ref err) =>
                println!("error while sending answer: {}", err)     // TODO: handle better?
        };

        writer.flush().ok();
    }
}

impl ::std::fmt::Show for Request {
    fn fmt(&self, formatter: &mut ::std::fmt::Formatter)
        -> Result<(), ::std::fmt::FormatError>
    {
        (format!("Request({} {} from {})",
            self.method, self.path, self.remote_addr.ip)).fmt(formatter)
    }
}

impl Drop for Request {
    fn drop(&mut self) {
        if self.response_writer.is_some() {
            let response = Response::new_empty(StatusCode(500));
            self.respond_impl(response);
        }
    }
}
<commit_msg>Update for lifetime ellision<commit_after>use std::io::{IoError, Stream};
use std::io::net::ip;
use {Header, HTTPVersion, Method, Response, StatusCode};
use util::{AnyReader, AnyWriter};

/// Represents an HTTP request made by a client.
///
/// A `Request` object is what is produced by the server, and is your what
///  your code must analyse and answer.
///
/// This object implements the `Send` trait, therefore you can dispatch your requests to
///  worker threads.
///
/// # Pipelining
///
/// If a client sends multiple requests in a row (without waiting for the response), then you will
///  get multiple `Request` objects simultaneously. This is called *requests pipelining*.
/// Tiny-http automatically reorders the responses so that you don't need to worry about the order
///  in which you call `respond` or `into_writer`.
///
/// This mechanic is disabled if:
///
///  - The body of a request is large enough (handling requires pipelining requires storing the
///     body of the request in a buffer ; if the body is too big, tiny-http will avoid doing that)
///  - A request sends a `Expect: 100-continue` header (which means that the client waits to
///     know whether its body will be processed before sending it)
///  - A request sends a `Connection: close` header or `Connection: upgrade` header (used for
///     websockets), which indicates that this is the last request that will be received on this
///     connection
///
/// # Automatic cleanup
///
/// If a `Request` object is destroyed without `into_writer` or `respond` being called,
///  an empty response with a 500 status code (internal server error) will automatically be
///  sent back to the client.
/// This means that if your code fails during the handling of a request, this "internal server
///  error" response will automatically be sent during the stack unwinding.
#[unstable]
pub struct Request {
    // where to read the body from
    data_reader: Option<AnyReader>,

    // if this writer is empty, then the request has been answered
    response_writer: Option<AnyWriter>,

    remote_addr: ip::SocketAddr,

    method: Method,

    path: ::url::Path,

    http_version: HTTPVersion,

    headers: Vec<Header>,

    body_length: Option<uint>,

    // true if a `100 Continue` response must be sent when `as_reader()` is called
    must_send_continue: bool,
}

/// Error that can happen when building a `Request` object.
pub enum RequestCreationError {
    ExpectationFailed,
    CreationIoError(IoError),
}

// this trait is to make sure that Request implements Send
trait MustBeSendDummy : Send {}
impl MustBeSendDummy for Request {}

/// Builds a new request
pub fn new_request<R: Reader + Send, W: Writer + Send>(method: Method, path: ::url::Path,
                             version: HTTPVersion, headers: Vec<Header>,
                             remote_addr: ip::SocketAddr, mut source_data: R, writer: W)
    -> Result<Request, RequestCreationError>
{
    // finding the transfer-encoding header
    let transfer_encoding = headers.iter()
        .find(|h: &&Header| h.field.equiv(&"Transfer-Encoding"))
        .map(|h| h.value.clone());

    // finding the content-length header
    let content_length = if transfer_encoding.is_some() {
        // if transfer-encoding is specified, the Content-Length
        //  header must be ignored (RFC2616 #4.4)
        None

    } else {
        headers.iter()
            .find(|h: &&Header| h.field.equiv(&"Content-Length"))
            .and_then(|h| from_str::<uint>(h.value.as_slice()))
    };

    // true if the client sent a `Expect: 100-continue` header
    let expects_continue = {
        use std::ascii::StrAsciiExt;

        match headers.iter().find(|h: &&Header| h.field.equiv(&"Expect")) {
            None => false,
            Some(h) if h.value.as_slice().eq_ignore_ascii_case("100-continue")
                => true,
            _ => return Err(ExpectationFailed)
        }
    };

    // building the reader depending on
    //  transfer-encoding and content-length
    let reader =
        if content_length.is_some() {
            let content_length = content_length.as_ref().unwrap().clone();

            if content_length == 0 {
                use std::io::util::NullReader;
                box NullReader as Box<Reader + Send>

            } else if content_length <= 1024 && !expects_continue {
                use std::io::MemReader;
                let data = try!(source_data.read_exact(content_length)
                    .map_err(|e| CreationIoError(e)));
                box MemReader::new(data) as Box<Reader + Send>

            } else {
                use util::EqualReader;
                let (data_reader, _) = EqualReader::new(source_data, content_length);   // TODO:
                box data_reader as Box<Reader + Send>
            }

        } else if transfer_encoding.is_some() {
            // if a transfer-encoding was specified, then "chunked"
            //  is ALWAYS applied over the message (RFC2616 #3.6)
            use util::ChunksDecoder;
            box ChunksDecoder::new(source_data) as Box<Reader + Send>

        } else {
            // if we have neither a Content-Length nor a Transfer-Encoding,
            //  assuming that we have no data
            // TODO: could also be multipart/byteranges
            use std::io::util::NullReader;
            box NullReader as Box<Reader + Send>
        };

    Ok(Request {
        data_reader: Some(AnyReader::new(reader)),
        response_writer: Some(AnyWriter::new(box writer as Box<Writer + Send>)),
        remote_addr: remote_addr,
        method: method,
        path: path,
        http_version: version,
        headers: headers,
        body_length: content_length,
        must_send_continue: expects_continue,
    })
}

impl Request {
    /// Returns the method requested by the client (eg. `GET`, `POST`, etc.).
    #[stable]
    #[inline]
    pub fn get_method(&self) -> &Method {
        &self.method
    }

    /// Returns the resource requested by the client.
    #[unstable]
    #[inline]
    pub fn get_url(&self) -> &::url::Path {
        &self.path
    }

    /// Returns a list of all headers sent by the client.
    #[stable]
    #[inline]
    pub fn get_headers(&self) -> &[Header] {
        self.headers.as_slice()
    }

    /// Returns the HTTP version of the request.
    #[unstable]
    #[inline]
    pub fn get_http_version(&self) -> &HTTPVersion {
        &self.http_version
    }

    /// Returns the length of the body in bytes.
    ///
    /// Returns `None` if the length is unknown.
    #[unstable]
    #[inline]
    pub fn get_body_length(&self) -> Option<uint> {
        self.body_length
    }

    /// Returns the length of the body in bytes.
    #[stable]
    #[inline]
    pub fn get_remote_addr(&self) -> &ip::SocketAddr {
        &self.remote_addr
    }

    /// Turns the `Request` into a `Stream`.
    ///
    /// The main purpose of this function is to support websockets.
    /// If you detect that the request wants to use some kind of protocol upgrade, you can
    ///  call this function to obtain full control of the socket stream.
    /// Keep in mind that it is up to you to send the initial response.
    ///
    /// If you call this on a non-websocket request, tiny-http will wait until this `Stream` object
    ///  is destroyed before continuing to read or write on the socket. Therefore you should always
    ///  destroy it as soon as possible.
    #[unstable]
    pub fn into_stream(mut self) -> Box<Stream + Send> {
        use util::CustomStream;

        let stream = CustomStream::new(self.into_reader_impl(), self.into_writer_impl());
        box stream as Box<Stream + Send>
    }

    /// Allows to read the body of the request.
    ///
    /// # Example
    ///
    /// ```
    /// let request = server.recv();
    ///
    /// if get_content_type(&request) == "application/json" {
    ///     let json: Json = from_str(request.as_reader().read_to_string()).unwrap();
    /// }
    /// ```
    ///
    /// If the client sent a `Expect: 100-continue` header with the request, calling this
    ///  function will send back a `100 Continue` response.
    #[unstable]
    #[inline]
    pub fn as_reader(&mut self) -> &mut Reader {
        if self.must_send_continue {
            let msg = Response::new_empty(StatusCode(100));
            msg.raw_print(self.response_writer.as_mut().unwrap().by_ref(),
                self.http_version, self.headers.as_slice(), true).ok();
            self.response_writer.as_mut().unwrap().flush().ok();
            self.must_send_continue = false;
        }

        fn passthrough<'a>(r: &'a mut Reader) -> &'a mut Reader { r }
        passthrough(self.data_reader.as_mut().unwrap())
    }

    /// Turns the `Request` into a writer.
    ///
    /// The writer has a raw access to the stream to the user.
    /// This function is useful for things like CGI.
    ///
    /// Note that the destruction of the `Writer` object may trigger
    ///  some events. For exemple if a client has sent multiple requests and the requests
    ///  have been processed in parallel, the destruction of a writer will trigger
    ///  the writing of the next response.
    /// Therefore you should always destroy the `Writer` as soon as possible.
    #[stable]
    #[inline]
    pub fn into_writer(mut self) -> Box<Writer + Send> {
        self.into_writer_impl().unwrap()
    }

    fn into_writer_impl(&mut self) -> AnyWriter {
        use std::mem;

        assert!(self.response_writer.is_some());

        let mut writer = None;
        mem::swap(&mut self.response_writer, &mut writer);
        writer.unwrap()
    }

    fn into_reader_impl(&mut self) -> AnyReader {
        use std::mem;

        assert!(self.data_reader.is_some());

        let mut reader = None;
        mem::swap(&mut self.data_reader, &mut reader);
        reader.unwrap()
    }

    /// Sends a response to this request.
    #[unstable]
    #[inline]
    pub fn respond<R: Reader>(mut self, response: Response<R>) {
        self.respond_impl(response)
    }

    fn respond_impl<R: Reader>(&mut self, response: Response<R>) {
        use std::io;

        let mut writer = self.into_writer_impl();

        let do_not_send_body = self.method.equiv(&"HEAD");

        match response.raw_print(writer.by_ref(),
                                self.http_version, self.headers.as_slice(),
                                do_not_send_body)
        {
            Ok(_) => (),
            Err(ref err) if err.kind == io::Closed => (),
            Err(ref err) if err.kind == io::BrokenPipe => (),
            Err(ref err) if err.kind == io::ConnectionAborted => (),
            Err(ref err) if err.kind == io::ConnectionRefused => (),
            Err(ref err) if err.kind == io::ConnectionReset => (),
            Err(ref err) =>
                println!("error while sending answer: {}", err)     // TODO: handle better?
        };

        writer.flush().ok();
    }
}

impl ::std::fmt::Show for Request {
    fn fmt(&self, formatter: &mut ::std::fmt::Formatter)
        -> Result<(), ::std::fmt::FormatError>
    {
        (format!("Request({} {} from {})",
            self.method, self.path, self.remote_addr.ip)).fmt(formatter)
    }
}

impl Drop for Request {
    fn drop(&mut self) {
        if self.response_writer.is_some() {
            let response = Response::new_empty(StatusCode(500));
            self.respond_impl(response);
        }
    }
}
<|endoftext|>


<commit_before>pub mod builtins;

use std::collections::HashMap;
use std::path::PathBuf;
use std::io;
use std::io::{Read, Write};

#[derive(PartialEq, Debug)]
pub struct State {
    cwd: PathBuf,
    builtins: HashMap<String, builtins::Builtin>,
    environment: HashMap<String, String>,
    aliases: HashMap<String, String>,
    argv: Vec<str>,
    argc: i32,
}

impl State {
    pub fn new(cwd: String) -> State {
        State{
            cwd: PathBuf::from(cwd),
            builtins: builtins::load(),
            environment: HashMap::new(),
            aliases: HashMap::new(),
            argv: Vec::new(),
            argc: 0,
        }
    }
}

pub fn run(mut s: State) {
    println!("Welcome to rsh! {:?}", s);

    loop {

        print!("{} -> ", s.cwd.to_str().unwrap());

        // this forces the prompt to print
        io::stdout().flush();

        // read the user input
        let mut input = String::new();
        io::stdin().read_line(&mut input).unwrap();
        s.argv = input.split_whitespace().collect();
        s.argc = s.argv.size();

        print!("\n");
        println!("Input: {}\nState: {:?}", input, s);
    }
}
<commit_msg>got a basic working echo shell<commit_after>pub mod builtins;

use std::collections::HashMap;
use std::path::PathBuf;
use std::io;
use std::io::{Read, Write};

#[derive(PartialEq, Debug)]
pub struct State {
    cwd: PathBuf,
    builtins: HashMap<String, builtins::Builtin>,
    environment: HashMap<String, String>,
    aliases: HashMap<String, String>,
    argv: Vec<String>,
    argc: usize,
}

impl State {
    pub fn new(cwd: String) -> State {
        State{
            cwd: PathBuf::from(cwd),
            builtins: builtins::load(),
            environment: HashMap::new(),
            aliases: HashMap::new(),
            argv: Vec::new(),
            argc: 0,
        }
    }
}

pub fn run(mut s: State) {
    println!("Welcome to rsh! {:?}", s);

    loop {

        print!("{} -> ", s.cwd.to_str().unwrap());

        // this forces the prompt to print
        io::stdout().flush();

        // read the user input
        let mut input = String::new();
        io::stdin().read_line(&mut input).unwrap();

        s.argv = input.split_whitespace().
            map(|s| s.to_string() ).
            collect();
        s.argc = s.argv.len();

        print!("\n");
        println!("Input: {}\nState: {:?}", input, s);
    }
}
<|endoftext|>


<commit_before>use std::from_str::FromStr;

use super::ScanCursor;
use super::{ScanError, OtherScanError};

macro_rules! from_str_scanner {
	($scan_fn:path -> $T:ty as $name:expr) => {
		impl<'a> Scanner<'a> for $T {
			fn scan<Cur: ScanCursor<'a>>(cursor: &Cur) -> Result<($T, Cur), ScanError> {
				let err = |cur:&Cur| Err(OtherScanError(format!(concat!("expected ",$name,", got `{}`"), cur.tail_str()), cursor.consumed()));

				let end = match $scan_fn(cursor.tail_str()) {
					Some(i) => i,
					None => return err(cursor)
				};

				let s = cursor.str_slice_to(end);
				let cursor = cursor.slice_from(end);

				FromStr::from_str(s)
					.map(ref |i| Ok((i, cursor.clone())))
					.unwrap_or_else(|| err(&cursor))
			}
		}
	};
}

pub trait Scanner<'a> {
	fn scan<Cur: ScanCursor<'a>>(cursor: &Cur) -> Result<(Self, Cur), ScanError>;
}

impl<'a> Scanner<'a> for bool {
	fn scan<Cur: ScanCursor<'a>>(cursor: &Cur) -> Result<(bool, Cur), ScanError> {
		cursor.expect_tok("true").map(|c| (true, c))
			.or_else(|_| cursor.expect_tok("false").map(|c| (false, c)))
			.or_else(|_| Err(OtherScanError(format!("expected `true` or `false`, got `{}`", cursor.tail_str()), cursor.consumed())))
	}
}

impl<'a> Scanner<'a> for char {
	fn scan<Cur: ScanCursor<'a>>(cursor: &Cur) -> Result<(char, Cur), ScanError> {
		let s = cursor.tail_str();
		if s.len() == 0 {
			Err(OtherScanError("expected character".into_string(), cursor.consumed()))
		} else {
			let ::std::str::CharRange { ch, next } = s.char_range_at(0);
			Ok((ch, cursor.slice_from(next)))
		}
	}
}

impl<'a> Scanner<'a> for &'a str {
	fn scan<Cur: ScanCursor<'a>>(cursor: &Cur) -> Result<(&'a str, Cur), ScanError> {
		cursor.pop_token().map(|sc| Ok(sc))
			.unwrap_or_else(|| Err(OtherScanError("expected token".into_string(), cursor.consumed())))
	}
}

from_str_scanner! { scan_float -> f32 as "real number" }
from_str_scanner! { scan_float -> f64 as "real number" }
from_str_scanner! { scan_int -> i8 as "8-bit integer" }
from_str_scanner! { scan_int -> i16 as "16-bit integer" }
from_str_scanner! { scan_int -> i32 as "32-bit integer" }
from_str_scanner! { scan_int -> i64 as "64-bit integer" }
from_str_scanner! { scan_int -> int as "integer" }
from_str_scanner! { scan_uint -> u8 as "8-bit unsigned integer" }
from_str_scanner! { scan_uint -> u16 as "16-bit unsigned integer" }
from_str_scanner! { scan_uint -> u32 as "32-bit unsigned integer" }
from_str_scanner! { scan_uint -> u64 as "64-bit unsigned integer" }
from_str_scanner! { scan_uint -> uint as "unsigned integer" }

fn next_char_at(s: &str, at: uint) -> uint {
	let ::std::str::CharRange { ch: _, next } = s.char_range_at(at);
	next
}

fn scan_float(s: &str) -> Option<uint> {
	enum State {
		Start,
		Whole,
		Suffix,
		ExponentStart,
		Exponent,
	}

	let mut state = Start;

	for (i,c) in s.char_indices() {
		match state {
			Start => {
				assert!(i == 0);
				match c {
					'0'..'9' | '-' => state = Whole,
					_ => return None
				}
			},
			Whole => match c {
				'0'..'9' => (),
				'.' => state = Suffix,
				'e' | 'E' => state = ExponentStart,
				_ => return Some(i)
			},
			Suffix => match c {
				'0'..'9' => (),
				'e' | 'E' => state = ExponentStart,
				_ => return Some(i),
			},
			ExponentStart => match c {
				'+' | '-' | '0'..'9' => state = Exponent,
				_ => return Some(i)
			},
			Exponent => match c {
				'0'..'9' => (),
				_ => return Some(i)
			}
		}
	}

	return Some(s.len())
}

fn scan_uint<'a>(s: &'a str) -> Option<uint> {
	s.char_indices()
		.take_while(|&(_,c)| '0' <= c && c <= '9')
		.map(|(i,_)| Some(next_char_at(s, i)))
		.last().unwrap_or(None)
}

fn scan_int<'a>(s: &'a str) -> Option<uint> {
	if s.len() == 0 { return None }

	let (s, off) = if s.char_at(0) == '-' {
		(s.slice_from(1), 1)
	} else {
		(s, 0)
	};

	scan_uint(s).map(|end| end+off)
}

#[cfg(test)]
mod test {
	use Cursor;
	use ScanError;
	use super::Scanner;
	use tokenizer::WordsAndInts;
	use whitespace::Ignore;

	fn cur<'a>(s: &'a str) -> Cursor<'a, WordsAndInts, Ignore> {
		Cursor::new(s, WordsAndInts, Ignore)
	}

	fn scan_a<'a, T: Scanner<'a, T>>(s: &'a str) -> Result<(T, Cursor<'a, WordsAndInts, Ignore>), ScanError> {
		Scanner::scan(&cur(s))
	}

	#[test]
	fn test_bool() {
		assert!(scan_a::<bool>("").err().is_some());
		assert!(scan_a::<bool>("true").unwrap().0 == true);
		assert!(scan_a::<bool>("false").unwrap().0 == false);
		assert!(scan_a::<bool>("yes").err().is_some());
		assert!(scan_a::<bool>("no").err().is_some());
		assert!(scan_a::<bool>("on").err().is_some());
		assert!(scan_a::<bool>("off").err().is_some());
		assert!(scan_a::<bool>("1").err().is_some());
		assert!(scan_a::<bool>("0").err().is_some());
	}

	#[test]
	fn test_char() {
		assert!(scan_a::<char>("").err().is_some());
		assert!(scan_a::<char>("x").unwrap().0 == 'x');
		assert!(scan_a::<char>("xy").unwrap().0 == 'x');
		assert!(scan_a::<char>("日本語").unwrap().0 == '日');
	}

	#[test]
	fn test_floats() {
		use std::from_str::FromStr;
		use std::num::{Float, Zero};

		fn test<'a, F: Float + Scanner<'a, F> + FromStr>() {
			let f = |v:F| v;
			let fs = |s:&str| -> F FromStr::from_str(s).unwrap();

			assert!(scan_a::<F>("").err().is_some());
			assert!(scan_a::<F>("0").ok().unwrap().0 == f(Zero::zero()));
			assert!(scan_a::<F>("0.0").ok().unwrap().0 == f(Zero::zero()));
			assert!(scan_a::<F>("-0").ok().unwrap().0 == -f(Zero::zero()));
			assert!(scan_a::<F>("1.0").ok().unwrap().0 == fs("1.0"));
			assert!(scan_a::<F>("1.00").ok().unwrap().0 == fs("1.0"));
			assert!(scan_a::<F>("1.0e0").ok().unwrap().0 == fs("1.0"));
			assert!(scan_a::<F>("1.0e1").ok().unwrap().0 == fs("10.0"));
		}

		test::<f32>();
		test::<f64>();
	}

	#[test]
	fn test_sized_ints() {
		use std::fmt::Show;
		use std::num::{Bounded, Int, Zero};

		fn test<'a, I: Int + Scanner<'a, I> + Show>(check_past: bool) {
			let zero: I = Zero::zero();
			let min: I = Bounded::min_value();
			let max: I = Bounded::max_value();

			assert!(scan_a::<I>("").err().is_some());
			assert!(scan_a::<I>("0").ok().unwrap().0 == zero);
			assert!(scan_a::<I>(format!("{}", min).as_slice()).ok().unwrap().0 == min);
			assert!(scan_a::<I>(format!("{}", max).as_slice()).ok().unwrap().0 == max);

			if check_past {
				let past_min: i64 = (min.to_i64()).unwrap() - 1;
				let past_max: u64 = (max.to_u64()).unwrap() + 1;
				assert!(scan_a::<I>(format!("{}", past_min).as_slice()).err().is_some());
				assert!(scan_a::<I>(format!("{}", past_max).as_slice()).err().is_some());
			}
		}

		test::<i8>(true);
		test::<i16>(true);
		test::<i32>(true);
		test::<i64>(false);
		test::<u8>(true);
		test::<u16>(true);
		test::<u32>(true);
		test::<u64>(false);
	}

	#[test]
	fn test_int() {
		assert!(scan_a::<int>("").err().is_some());
		assert!(scan_a::<int>("0").ok().unwrap().0 == 0);
		assert!(scan_a::<int>("42").ok().unwrap().0 == 42);
		assert!(scan_a::<int>("1_234").ok().unwrap().0 == 1);
		assert!(scan_a::<int>("x").err().is_some());
		assert!(scan_a::<int>("0x").ok().unwrap().0 == 0);
		assert!(scan_a::<int>("42x").ok().unwrap().0 == 42);
		assert!(scan_a::<int>("-").err().is_some());
		assert!(scan_a::<int>("-0").ok().unwrap().0 == 0);
		assert!(scan_a::<int>("-42").ok().unwrap().0 == -42);
		assert!(scan_a::<int>("-1_234").ok().unwrap().0 == -1);
	}

	#[test]
	fn test_uint() {
		assert!(scan_a::<uint>("").err().is_some());
		assert!(scan_a::<uint>("0").ok().unwrap().0 == 0);
		assert!(scan_a::<uint>("42").ok().unwrap().0 == 42);
		assert!(scan_a::<uint>("1_234").ok().unwrap().0 == 1);
		assert!(scan_a::<uint>("x").err().is_some());
		assert!(scan_a::<uint>("0x").ok().unwrap().0 == 0);
		assert!(scan_a::<uint>("42x").ok().unwrap().0 == 42);
		assert!(scan_a::<uint>("-").err().is_some());
		assert!(scan_a::<uint>("-0").err().is_some());
		assert!(scan_a::<uint>("-42").err().is_some());
		assert!(scan_a::<uint>("-1_234").err().is_some());
	}

	#[test]
	fn test_str() {
		assert!(scan_a::<&str>("").err().is_some());
		assert!(scan_a::<&str>("a").ok().unwrap().0 == "a");
		assert!(scan_a::<&str>("a b").ok().unwrap().0 == "a");
		assert!(scan_a::<&str>("abc").ok().unwrap().0 == "abc");
		assert!(scan_a::<&str>("ab-c").ok().unwrap().0 == "ab");
	}
}
<commit_msg>Updated to latest rustc nightly.<commit_after>use std::from_str::FromStr;

use super::ScanCursor;
use super::{ScanError, OtherScanError};

macro_rules! from_str_scanner {
	($scan_fn:path -> $T:ty as $name:expr) => {
		impl<'a> Scanner<'a> for $T {
			fn scan<Cur: ScanCursor<'a>>(cursor: &Cur) -> Result<($T, Cur), ScanError> {
				let err = |cur:&Cur| Err(OtherScanError(format!(concat!("expected ",$name,", got `{}`"), cur.tail_str()), cursor.consumed()));

				let end = match $scan_fn(cursor.tail_str()) {
					Some(i) => i,
					None => return err(cursor)
				};

				let s = cursor.str_slice_to(end);
				let cursor = cursor.slice_from(end);

				FromStr::from_str(s)
					.map(|i| Ok((i, cursor.clone())))
					.unwrap_or_else(|| err(&cursor))
			}
		}
	};
}

pub trait Scanner<'a> {
	fn scan<Cur: ScanCursor<'a>>(cursor: &Cur) -> Result<(Self, Cur), ScanError>;
}

impl<'a> Scanner<'a> for bool {
	fn scan<Cur: ScanCursor<'a>>(cursor: &Cur) -> Result<(bool, Cur), ScanError> {
		cursor.expect_tok("true").map(|c| (true, c))
			.or_else(|_| cursor.expect_tok("false").map(|c| (false, c)))
			.or_else(|_| Err(OtherScanError(format!("expected `true` or `false`, got `{}`", cursor.tail_str()), cursor.consumed())))
	}
}

impl<'a> Scanner<'a> for char {
	fn scan<Cur: ScanCursor<'a>>(cursor: &Cur) -> Result<(char, Cur), ScanError> {
		let s = cursor.tail_str();
		if s.len() == 0 {
			Err(OtherScanError("expected character".into_string(), cursor.consumed()))
		} else {
			let ::std::str::CharRange { ch, next } = s.char_range_at(0);
			Ok((ch, cursor.slice_from(next)))
		}
	}
}

impl<'a> Scanner<'a> for &'a str {
	fn scan<Cur: ScanCursor<'a>>(cursor: &Cur) -> Result<(&'a str, Cur), ScanError> {
		cursor.pop_token().map(|sc| Ok(sc))
			.unwrap_or_else(|| Err(OtherScanError("expected token".into_string(), cursor.consumed())))
	}
}

from_str_scanner! { scan_float -> f32 as "real number" }
from_str_scanner! { scan_float -> f64 as "real number" }
from_str_scanner! { scan_int -> i8 as "8-bit integer" }
from_str_scanner! { scan_int -> i16 as "16-bit integer" }
from_str_scanner! { scan_int -> i32 as "32-bit integer" }
from_str_scanner! { scan_int -> i64 as "64-bit integer" }
from_str_scanner! { scan_int -> int as "integer" }
from_str_scanner! { scan_uint -> u8 as "8-bit unsigned integer" }
from_str_scanner! { scan_uint -> u16 as "16-bit unsigned integer" }
from_str_scanner! { scan_uint -> u32 as "32-bit unsigned integer" }
from_str_scanner! { scan_uint -> u64 as "64-bit unsigned integer" }
from_str_scanner! { scan_uint -> uint as "unsigned integer" }

fn next_char_at(s: &str, at: uint) -> uint {
	let ::std::str::CharRange { ch: _, next } = s.char_range_at(at);
	next
}

fn scan_float(s: &str) -> Option<uint> {
	enum State {
		Start,
		Whole,
		Suffix,
		ExponentStart,
		Exponent,
	}

	let mut state = Start;

	for (i,c) in s.char_indices() {
		match state {
			Start => {
				assert!(i == 0);
				match c {
					'0'...'9' | '-' => state = Whole,
					_ => return None
				}
			},
			Whole => match c {
				'0'...'9' => (),
				'.' => state = Suffix,
				'e' | 'E' => state = ExponentStart,
				_ => return Some(i)
			},
			Suffix => match c {
				'0'...'9' => (),
				'e' | 'E' => state = ExponentStart,
				_ => return Some(i),
			},
			ExponentStart => match c {
				'+' | '-' | '0'...'9' => state = Exponent,
				_ => return Some(i)
			},
			Exponent => match c {
				'0'...'9' => (),
				_ => return Some(i)
			}
		}
	}

	return Some(s.len())
}

fn scan_uint<'a>(s: &'a str) -> Option<uint> {
	s.char_indices()
		.take_while(|&(_,c)| '0' <= c && c <= '9')
		.map(|(i,_)| Some(next_char_at(s, i)))
		.last().unwrap_or(None)
}

fn scan_int<'a>(s: &'a str) -> Option<uint> {
	if s.len() == 0 { return None }

	let (s, off) = if s.char_at(0) == '-' {
		(s.slice_from(1), 1)
	} else {
		(s, 0)
	};

	scan_uint(s).map(|end| end+off)
}

#[cfg(test)]
mod test {
	use Cursor;
	use ScanError;
	use super::Scanner;
	use tokenizer::WordsAndInts;
	use whitespace::Ignore;

	fn cur<'a>(s: &'a str) -> Cursor<'a, WordsAndInts, Ignore> {
		Cursor::new(s, WordsAndInts, Ignore)
	}

	fn scan_a<'a, T: Scanner<'a, T>>(s: &'a str) -> Result<(T, Cursor<'a, WordsAndInts, Ignore>), ScanError> {
		Scanner::scan(&cur(s))
	}

	#[test]
	fn test_bool() {
		assert!(scan_a::<bool>("").err().is_some());
		assert!(scan_a::<bool>("true").unwrap().0 == true);
		assert!(scan_a::<bool>("false").unwrap().0 == false);
		assert!(scan_a::<bool>("yes").err().is_some());
		assert!(scan_a::<bool>("no").err().is_some());
		assert!(scan_a::<bool>("on").err().is_some());
		assert!(scan_a::<bool>("off").err().is_some());
		assert!(scan_a::<bool>("1").err().is_some());
		assert!(scan_a::<bool>("0").err().is_some());
	}

	#[test]
	fn test_char() {
		assert!(scan_a::<char>("").err().is_some());
		assert!(scan_a::<char>("x").unwrap().0 == 'x');
		assert!(scan_a::<char>("xy").unwrap().0 == 'x');
		assert!(scan_a::<char>("日本語").unwrap().0 == '日');
	}

	#[test]
	fn test_floats() {
		use std::from_str::FromStr;
		use std::num::{Float, Zero};

		fn test<'a, F: Float + Scanner<'a, F> + FromStr>() {
			let f = |v:F| v;
			let fs = |s:&str| -> F FromStr::from_str(s).unwrap();

			assert!(scan_a::<F>("").err().is_some());
			assert!(scan_a::<F>("0").ok().unwrap().0 == f(Zero::zero()));
			assert!(scan_a::<F>("0.0").ok().unwrap().0 == f(Zero::zero()));
			assert!(scan_a::<F>("-0").ok().unwrap().0 == -f(Zero::zero()));
			assert!(scan_a::<F>("1.0").ok().unwrap().0 == fs("1.0"));
			assert!(scan_a::<F>("1.00").ok().unwrap().0 == fs("1.0"));
			assert!(scan_a::<F>("1.0e0").ok().unwrap().0 == fs("1.0"));
			assert!(scan_a::<F>("1.0e1").ok().unwrap().0 == fs("10.0"));
		}

		test::<f32>();
		test::<f64>();
	}

	#[test]
	fn test_sized_ints() {
		use std::fmt::Show;
		use std::num::{Bounded, Int, Zero};

		fn test<'a, I: Int + Scanner<'a, I> + Show>(check_past: bool) {
			let zero: I = Zero::zero();
			let min: I = Bounded::min_value();
			let max: I = Bounded::max_value();

			assert!(scan_a::<I>("").err().is_some());
			assert!(scan_a::<I>("0").ok().unwrap().0 == zero);
			assert!(scan_a::<I>(format!("{}", min).as_slice()).ok().unwrap().0 == min);
			assert!(scan_a::<I>(format!("{}", max).as_slice()).ok().unwrap().0 == max);

			if check_past {
				let past_min: i64 = (min.to_i64()).unwrap() - 1;
				let past_max: u64 = (max.to_u64()).unwrap() + 1;
				assert!(scan_a::<I>(format!("{}", past_min).as_slice()).err().is_some());
				assert!(scan_a::<I>(format!("{}", past_max).as_slice()).err().is_some());
			}
		}

		test::<i8>(true);
		test::<i16>(true);
		test::<i32>(true);
		test::<i64>(false);
		test::<u8>(true);
		test::<u16>(true);
		test::<u32>(true);
		test::<u64>(false);
	}

	#[test]
	fn test_int() {
		assert!(scan_a::<int>("").err().is_some());
		assert!(scan_a::<int>("0").ok().unwrap().0 == 0);
		assert!(scan_a::<int>("42").ok().unwrap().0 == 42);
		assert!(scan_a::<int>("1_234").ok().unwrap().0 == 1);
		assert!(scan_a::<int>("x").err().is_some());
		assert!(scan_a::<int>("0x").ok().unwrap().0 == 0);
		assert!(scan_a::<int>("42x").ok().unwrap().0 == 42);
		assert!(scan_a::<int>("-").err().is_some());
		assert!(scan_a::<int>("-0").ok().unwrap().0 == 0);
		assert!(scan_a::<int>("-42").ok().unwrap().0 == -42);
		assert!(scan_a::<int>("-1_234").ok().unwrap().0 == -1);
	}

	#[test]
	fn test_uint() {
		assert!(scan_a::<uint>("").err().is_some());
		assert!(scan_a::<uint>("0").ok().unwrap().0 == 0);
		assert!(scan_a::<uint>("42").ok().unwrap().0 == 42);
		assert!(scan_a::<uint>("1_234").ok().unwrap().0 == 1);
		assert!(scan_a::<uint>("x").err().is_some());
		assert!(scan_a::<uint>("0x").ok().unwrap().0 == 0);
		assert!(scan_a::<uint>("42x").ok().unwrap().0 == 42);
		assert!(scan_a::<uint>("-").err().is_some());
		assert!(scan_a::<uint>("-0").err().is_some());
		assert!(scan_a::<uint>("-42").err().is_some());
		assert!(scan_a::<uint>("-1_234").err().is_some());
	}

	#[test]
	fn test_str() {
		assert!(scan_a::<&str>("").err().is_some());
		assert!(scan_a::<&str>("a").ok().unwrap().0 == "a");
		assert!(scan_a::<&str>("a b").ok().unwrap().0 == "a");
		assert!(scan_a::<&str>("abc").ok().unwrap().0 == "abc");
		assert!(scan_a::<&str>("ab-c").ok().unwrap().0 == "ab");
	}
}
<|endoftext|>


<commit_before>use std::fmt;
use std::option::Option;
use std::result::Result;
use std::vec::Vec;

pub struct Scanner {
    position: isize,
    length: isize,
    buffer: Vec<char>,
}

impl Scanner {
    pub fn new() -> Scanner {
        Scanner {
            position: -1,
            length: 0,
            buffer: Vec::new(),
        }
    }

    pub fn from_str(string: &str) -> Scanner {
        let mut scanner = Scanner::new();
        scanner.push_str(string);
        scanner
    }

    pub fn len(&self) -> isize {
        self.length
    }

    pub fn push_str(&mut self, string: &str) {
        self.buffer.extend(string.chars());
        self.length += string.len() as isize;
    }

    pub fn get_char(&mut self) -> Option<char> {
        if self.position < self.length - 1 {
            self.position += 1;
            let c = self.buffer[self.position as usize];
            Some(c)
        }
        else {
            None
        }
    }

    pub fn unget_char(&mut self) -> Result<(), &str> {
        if self.position >= 0 {
            self.position -= 1;
            Ok(())
        }
        else {
            Err("No character to unget")
        }
    }
}

impl fmt::Display for Scanner {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Scanner {{position: {}, length: {}}}", self.position, self.length)
    }
}

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

    #[test]
    fn new() {
        let scanner = Scanner::new();
        assert_eq!(scanner.len(), 0);
    }

    #[test]
    fn can_add_text() {
        let mut scanner = Scanner::new();
        let text = "This is some text";

        scanner.push_str(text);
        scanner.push_str(text);

        assert_eq!(scanner.len(), text.len() as isize * 2);
    }

    #[test]
    fn can_get_characters() {
        // A string with uppercase and lowercase letters where all are unique
        let text = "Total";
        let mut scanner = Scanner::from_str(text);

        for c in text.chars() {
            if let Some(next) = scanner.get_char() {
                assert!(next == c, format!("Got character `{}` instead of character `{}`", next, c));
            }
            else {
                panic!("Could not get expected number of characters. Stopped when expecting `{}`", c);
            }
        }
    }

    #[test]
    fn cannot_get_beyond_length() {
        let mut scanner = Scanner::new();
        assert!(scanner.get_char().is_none());

        scanner = Scanner::from_str("T");
        scanner.get_char();
        assert!(scanner.get_char().is_none());
    }

    #[test]
    fn can_unget_characters() {
        let mut scanner = Scanner::from_str("abc");
        // 1. Get up to the expected character
        // position -> 0
        scanner.get_char();
        // position -> 1
        let expected = scanner.get_char();

        // 2. Unget back to before any character
        // position -> 0
        scanner.unget_char().unwrap();
        // position -> -1
        scanner.unget_char().unwrap();

        // 3. Get up to the end
        // position -> 0
        scanner.get_char();
        // position -> 1
        scanner.get_char();
        // position -> 2
        scanner.get_char();

        // 4. Unget back to the expected character
        // position -> 1
        scanner.unget_char().unwrap();
        // position -> 0
        scanner.unget_char().unwrap();
        // position -> 1
        let actual = scanner.get_char();

        assert_eq!(expected, actual);
    }

    #[test]
    fn cannot_unget_before_start() {
        let mut scanner = Scanner::from_str("T");
        assert!(scanner.unget_char().is_err());
        scanner.get_char();
        assert!(!scanner.unget_char().is_err());
        assert!(scanner.unget_char().is_err());
    }
}

<commit_msg>Ensuring that getting to the end or ungetting to the beginning cannot be repeated past that point<commit_after>use std::fmt;
use std::option::Option;
use std::result::Result;
use std::vec::Vec;

pub struct Scanner {
    position: isize,
    length: isize,
    buffer: Vec<char>,
}

impl Scanner {
    pub fn new() -> Scanner {
        Scanner {
            position: -1,
            length: 0,
            buffer: Vec::new(),
        }
    }

    pub fn from_str(string: &str) -> Scanner {
        let mut scanner = Scanner::new();
        scanner.push_str(string);
        scanner
    }

    pub fn len(&self) -> isize {
        self.length
    }

    pub fn push_str(&mut self, string: &str) {
        self.buffer.extend(string.chars());
        self.length += string.len() as isize;
    }

    pub fn get_char(&mut self) -> Option<char> {
        if self.position < self.length - 1 {
            self.position += 1;
            let c = self.buffer[self.position as usize];
            Some(c)
        }
        else {
            None
        }
    }

    pub fn unget_char(&mut self) -> Result<(), &str> {
        if self.position >= 0 {
            self.position -= 1;
            Ok(())
        }
        else {
            Err("No character to unget")
        }
    }
}

impl fmt::Display for Scanner {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Scanner {{position: {}, length: {}}}", self.position, self.length)
    }
}

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

    #[test]
    fn new() {
        let scanner = Scanner::new();
        assert_eq!(scanner.len(), 0);
    }

    #[test]
    fn can_add_text() {
        let mut scanner = Scanner::new();
        let text = "This is some text";

        scanner.push_str(text);
        scanner.push_str(text);

        assert_eq!(scanner.len(), text.len() as isize * 2);
    }

    #[test]
    fn can_get_characters() {
        // A string with uppercase and lowercase letters where all are unique
        let text = "Total";
        let mut scanner = Scanner::from_str(text);

        for c in text.chars() {
            if let Some(next) = scanner.get_char() {
                assert!(next == c, format!("Got character `{}` instead of character `{}`", next, c));
            }
            else {
                panic!("Could not get expected number of characters. Stopped when expecting `{}`", c);
            }
        }
    }

    #[test]
    fn cannot_get_beyond_length() {
        let mut scanner = Scanner::new();
        assert!(scanner.get_char().is_none());

        scanner = Scanner::from_str("T");
        scanner.get_char();
        assert!(scanner.get_char().is_none());
        // try again just in case
        assert!(scanner.get_char().is_none());
    }

    #[test]
    fn can_unget_characters() {
        let mut scanner = Scanner::from_str("abc");
        // 1. Get up to the expected character
        // position -> 0
        scanner.get_char();
        // position -> 1
        let expected = scanner.get_char();

        // 2. Unget back to before any character
        // position -> 0
        scanner.unget_char().unwrap();
        // position -> -1
        scanner.unget_char().unwrap();

        // 3. Get up to the end
        // position -> 0
        scanner.get_char();
        // position -> 1
        scanner.get_char();
        // position -> 2
        scanner.get_char();

        // 4. Unget back to the expected character
        // position -> 1
        scanner.unget_char().unwrap();
        // position -> 0
        scanner.unget_char().unwrap();
        // position -> 1
        let actual = scanner.get_char();

        assert_eq!(expected, actual);
    }

    #[test]
    fn cannot_unget_before_start() {
        let mut scanner = Scanner::from_str("T");
        assert!(scanner.unget_char().is_err());
        scanner.get_char();
        assert!(!scanner.unget_char().is_err());
        assert!(scanner.unget_char().is_err());
        // Try again just in case
        assert!(scanner.unget_char().is_err());
    }
}

<|endoftext|>


<commit_before>//! Tracks metadata for a loading/rendering session.

use std::sync::Arc;

use crate::log;

/// Metadata for a loading/rendering session.
///
/// When the calling program first uses one of the API entry points (e.g. `Loader::new()`
/// in the Rust API, or `rsvg_handle_new()` in the C API), there is no context yet where
/// librsvg's code may start to track things.  This struct provides that context.
#[derive(Clone)]
pub struct Session {
    inner: Arc<SessionInner>,
}

struct SessionInner {
    log_enabled: bool,
}

impl Session {
    pub fn new() -> Self {
        Self {
            inner: Arc::new(SessionInner {
                log_enabled: log::log_enabled(),
            }),
        }
    }

    pub fn log_enabled(&self) -> bool {
        self.inner.log_enabled
    }
}
<commit_msg>Session.new_for_test_suite(): New method<commit_after>//! Tracks metadata for a loading/rendering session.

use std::sync::Arc;

use crate::log;

/// Metadata for a loading/rendering session.
///
/// When the calling program first uses one of the API entry points (e.g. `Loader::new()`
/// in the Rust API, or `rsvg_handle_new()` in the C API), there is no context yet where
/// librsvg's code may start to track things.  This struct provides that context.
#[derive(Clone)]
pub struct Session {
    inner: Arc<SessionInner>,
}

struct SessionInner {
    log_enabled: bool,
}

impl Session {
    pub fn new() -> Self {
        Self {
            inner: Arc::new(SessionInner {
                log_enabled: log::log_enabled(),
            }),
        }
    }

    #[cfg(test)]
    pub fn new_for_test_suite() -> Self {
        Self {
            inner: Arc::new(SessionInner {
                log_enabled: false,
            }),
        }
    }

    pub fn log_enabled(&self) -> bool {
        self.inner.log_enabled
    }
}
<|endoftext|>


<commit_before>//!
//! A session runs a filesystem implementation while it is being mounted
//! to a specific mount point. A session begins by mounting the filesystem
//! and ends by unmounting it. While the filesystem is mounted, the session
//! loop receives, dispatches and replies to kernel requests for filesystem
//! operations under its mount point.
//!

use std::io;
use std::ffi::OsStr;
use std::path::{PathBuf, Path};
use std::thread::{Builder, JoinGuard};
use channel;
use channel::Channel;
use Filesystem;
use request::{request, dispatch};

/// The max size of write requests from the kernel. The absolute minimum is 4k,
/// FUSE recommends at least 128k, max 16M. The FUSE default is 16M on OS X
/// and 128k on other systems.
pub const MAX_WRITE_SIZE: usize = 16*1024*1024;

/// Size of the buffer for reading a request from the kernel. Since the kernel may send
/// up to MAX_WRITE_SIZE bytes in a write request, we use that value plus some extra space.
const BUFFER_SIZE: usize = MAX_WRITE_SIZE + 4096;

/// The session data structure
pub struct Session<FS> {
    /// Filesystem operation implementations
    pub filesystem: FS,
    /// Communication channel to the kernel driver
    ch: Channel,
    /// FUSE protocol major version
    pub proto_major: u32,
    /// FUSE protocol minor version
    pub proto_minor: u32,
    /// True if the filesystem is initialized (init operation done)
    pub initialized: bool,
    /// True if the filesystem was destroyed (destroy operation done)
    pub destroyed: bool,
}

impl<FS: Filesystem> Session<FS> {
    /// Create a new session by mounting the given filesystem to the given mountpoint
    pub fn new (filesystem: FS, mountpoint: &Path, options: &[&OsStr]) -> Session<FS> {
        info!("Mounting {}", mountpoint.display());
        let ch = match Channel::new(mountpoint, options) {
            Ok(ch) => ch,
            Err(err) => panic!("Unable to mount filesystem. Error {}", err),
        };
        Session {
            filesystem: filesystem,
            ch: ch,
            proto_major: 0,
            proto_minor: 0,
            initialized: false,
            destroyed: false,
        }
    }

    /// Return path of the mounted filesystem
    pub fn mountpoint (&self) -> &Path {
        &self.ch.mountpoint()
    }

    /// Run the session loop that receives kernel requests and dispatches them to method
    /// calls into the filesystem. This read-dispatch-loop is non-concurrent to prevent
    /// having multiple buffers (which take up much memory), but the filesystem methods
    /// may run concurrent by spawning threads.
    pub fn run (&mut self) {
        // Buffer for receiving requests from the kernel. Only one is allocated and
        // it is reused immediately after dispatching to conserve memory and allocations.
        let mut buffer: Vec<u8> = Vec::with_capacity(BUFFER_SIZE);
        loop {
            // Read the next request from the given channel to kernel driver
            // The kernel driver makes sure that we get exactly one request per read
            match self.ch.receive(&mut buffer) {
                Ok(()) => match request(self.ch.sender(), &buffer) {
                    // Dispatch request
                    Some(req) => dispatch(&req, self),
                    // Quit loop on illegal request
                    None => break,
                },
                Err(ref err) => match err.kind() {
                    // ENOENT: operation interrupted. Accordingly to FUSE, this is safe to retry
                    io::ErrorKind::FileNotFound => continue,
                    // EINTR: interrupted system call, retry
                    io::ErrorKind::Interrupted => continue,
                    // EAGAIN: explicitly try again
                    io::ErrorKind::ResourceUnavailable => continue,
                    // ENODEV: filesystem was unmounted, quit the loop
                    // FIXME: This does match more error codes than just ENODEV
                    io::ErrorKind::Other => break,
                    // Unhandled error
                    _ => panic!("Lost connection to FUSE device. Error {}", err),
                },
            }
        }
    }
}

impl<FS: Filesystem+Send+'static> Session<FS> {
    /// Run the session loop in a background thread
    pub fn spawn<'a> (self) -> io::Result<BackgroundSession<'a>> {
        BackgroundSession::new(self)
    }
}

#[unsafe_destructor]
impl<FS: Filesystem> Drop for Session<FS> {
    fn drop (&mut self) {
        info!("Unmounted {}", self.mountpoint().display());
    }
}

/// The background session data structure
pub struct BackgroundSession<'a> {
    /// Path of the mounted filesystem
    pub mountpoint: PathBuf,
    /// Thread guard of the background session
    pub guard: JoinGuard<'a ()>,
}

impl<'a> BackgroundSession<'a> {
    /// Create a new background session for the given session by running its
    /// session loop in a background thread. If the returned handle is dropped,
    /// the filesystem is unmounted and the given session ends.
    pub fn new<FS: Filesystem+Send+'static> (se: Session<FS>) -> io::Result<BackgroundSession<'a>> {
        let mountpoint = se.mountpoint().to_path_buf();
        let builder = Builder::new().name(format!("FUSE {}", mountpoint.display()));
        let guard = try!(builder.scoped(move || {
            let mut se = se;
            se.run();
        }));
        Ok(BackgroundSession { mountpoint: mountpoint, guard: guard })
    }
}

#[unsafe_destructor]
impl<'a> Drop for BackgroundSession<'a> {
    fn drop (&mut self) {
        info!("Unmounting {}", self.mountpoint.display());
        // Unmounting the filesystem will eventually end the session loop,
        // drop the session and hence end the background thread.
        match channel::unmount(&self.mountpoint) {
            Ok(()) => (),
            Err(err) => error!("Failed to unmount {}: {}", self.mountpoint.display(), err),
        }
    }
}
<commit_msg>Take advantage of raw_os_error values in session loop<commit_after>//!
//! A session runs a filesystem implementation while it is being mounted
//! to a specific mount point. A session begins by mounting the filesystem
//! and ends by unmounting it. While the filesystem is mounted, the session
//! loop receives, dispatches and replies to kernel requests for filesystem
//! operations under its mount point.
//!

use std::io;
use std::ffi::OsStr;
use std::path::{PathBuf, Path};
use std::thread::{Builder, JoinGuard};
use libc::{EAGAIN, EINTR, ENODEV, ENOENT};
use channel;
use channel::Channel;
use Filesystem;
use request::{request, dispatch};

/// The max size of write requests from the kernel. The absolute minimum is 4k,
/// FUSE recommends at least 128k, max 16M. The FUSE default is 16M on OS X
/// and 128k on other systems.
pub const MAX_WRITE_SIZE: usize = 16*1024*1024;

/// Size of the buffer for reading a request from the kernel. Since the kernel may send
/// up to MAX_WRITE_SIZE bytes in a write request, we use that value plus some extra space.
const BUFFER_SIZE: usize = MAX_WRITE_SIZE + 4096;

/// The session data structure
pub struct Session<FS> {
    /// Filesystem operation implementations
    pub filesystem: FS,
    /// Communication channel to the kernel driver
    ch: Channel,
    /// FUSE protocol major version
    pub proto_major: u32,
    /// FUSE protocol minor version
    pub proto_minor: u32,
    /// True if the filesystem is initialized (init operation done)
    pub initialized: bool,
    /// True if the filesystem was destroyed (destroy operation done)
    pub destroyed: bool,
}

impl<FS: Filesystem> Session<FS> {
    /// Create a new session by mounting the given filesystem to the given mountpoint
    pub fn new (filesystem: FS, mountpoint: &Path, options: &[&OsStr]) -> Session<FS> {
        info!("Mounting {}", mountpoint.display());
        let ch = match Channel::new(mountpoint, options) {
            Ok(ch) => ch,
            Err(err) => panic!("Unable to mount filesystem. Error {}", err),
        };
        Session {
            filesystem: filesystem,
            ch: ch,
            proto_major: 0,
            proto_minor: 0,
            initialized: false,
            destroyed: false,
        }
    }

    /// Return path of the mounted filesystem
    pub fn mountpoint (&self) -> &Path {
        &self.ch.mountpoint()
    }

    /// Run the session loop that receives kernel requests and dispatches them to method
    /// calls into the filesystem. This read-dispatch-loop is non-concurrent to prevent
    /// having multiple buffers (which take up much memory), but the filesystem methods
    /// may run concurrent by spawning threads.
    pub fn run (&mut self) {
        // Buffer for receiving requests from the kernel. Only one is allocated and
        // it is reused immediately after dispatching to conserve memory and allocations.
        let mut buffer: Vec<u8> = Vec::with_capacity(BUFFER_SIZE);
        loop {
            // Read the next request from the given channel to kernel driver
            // The kernel driver makes sure that we get exactly one request per read
            match self.ch.receive(&mut buffer) {
                Ok(()) => match request(self.ch.sender(), &buffer) {
                    // Dispatch request
                    Some(req) => dispatch(&req, self),
                    // Quit loop on illegal request
                    None => break,
                },
                Err(ref err) => match err.raw_os_error() {
                    // Operation interrupted. Accordingly to FUSE, this is safe to retry
                    Some(ENOENT) => continue,
                    // Interrupted system call, retry
                    Some(EINTR) => continue,
                    // Explicitly try again
                    Some(EAGAIN) => continue,
                    // Filesystem was unmounted, quit the loop
                    Some(ENODEV) => break,
                    // Unhandled error
                    _ => panic!("Lost connection to FUSE device. Error: {}", err),
                },
            }
        }
    }
}

impl<FS: Filesystem+Send+'static> Session<FS> {
    /// Run the session loop in a background thread
    pub fn spawn<'a> (self) -> io::Result<BackgroundSession<'a>> {
        BackgroundSession::new(self)
    }
}

#[unsafe_destructor]
impl<FS: Filesystem> Drop for Session<FS> {
    fn drop (&mut self) {
        info!("Unmounted {}", self.mountpoint().display());
    }
}

/// The background session data structure
pub struct BackgroundSession<'a> {
    /// Path of the mounted filesystem
    pub mountpoint: PathBuf,
    /// Thread guard of the background session
    pub guard: JoinGuard<'a ()>,
}

impl<'a> BackgroundSession<'a> {
    /// Create a new background session for the given session by running its
    /// session loop in a background thread. If the returned handle is dropped,
    /// the filesystem is unmounted and the given session ends.
    pub fn new<FS: Filesystem+Send+'static> (se: Session<FS>) -> io::Result<BackgroundSession<'a>> {
        let mountpoint = se.mountpoint().to_path_buf();
        let builder = Builder::new().name(format!("FUSE {}", mountpoint.display()));
        let guard = try!(builder.scoped(move || {
            let mut se = se;
            se.run();
        }));
        Ok(BackgroundSession { mountpoint: mountpoint, guard: guard })
    }
}

#[unsafe_destructor]
impl<'a> Drop for BackgroundSession<'a> {
    fn drop (&mut self) {
        info!("Unmounting {}", self.mountpoint.display());
        // Unmounting the filesystem will eventually end the session loop,
        // drop the session and hence end the background thread.
        match channel::unmount(&self.mountpoint) {
            Ok(()) => (),
            Err(err) => error!("Failed to unmount {}: {}", self.mountpoint.display(), err),
        }
    }
}
<|endoftext|>


<commit_before>//imports
use std::collections::TreeMap;
use std::io::BufferedReader;
use std::io::File;

///rust all this set's challenges
fn main(){
	println!("set one ::");
	//challenge_one();
	//challenge_two();
	//challenge_three();
	challenge_four();
	//challenge_five();
	//challenge_six();
	//challenge_seven();
	//challenge_eight();
}

//challenges
/// solution for challenge one
fn challenge_one(){
	//println!("challenge one ::");
	//params
	let input = "49276d206b696c6c696e6720796f757220627261696e206c696b65206120706f69736f6e6f7573206d757368726f6f6d";
	let desired = "SSdtIGtpbGxpbmcgeW91ciBicmFpbiBsaWtlIGEgcG9pc29ub3VzIG11c2hyb29t";
	//get result
	let result = hexstr_tob64str( input);
	//print info
	//println!("input: {}", input);
	println!("\tresult: {}", result);
	println!("\tresult == desired: {}", result.as_slice() == desired);}

/// solution for challenge two
fn challenge_two(){
	println!("challenge two ::");
	//params
	let input_a = "1c0111001f010100061a024b53535009181c";
	let input_b = "686974207468652062756c6c277320657965";
	let desired = "746865206b696420646f6e277420706c6179";
	//convert to bytes
	let a_bytes = hexstr_tobytes( input_a);
	let b_bytes = hexstr_tobytes( input_b);
	//apply xor
	let result_bytes = xor_bytes(
		a_bytes.as_slice(), b_bytes.as_slice());
	//convert to string
	let result = bytes_tohexstr( result_bytes.as_slice());
	//print info
	println!("\tresult: {}", result);
	println!("\tresult == desired: {}", result.as_slice() == desired);}

/// solution for challenge three
fn challenge_three(){
	println!("challenge three ::");
	//params
	let input =
		"1b37373331363f78151b7f2b783431333d78397828372d363c78373e783a393b3736";
	let extractions = crack_xor_cipher( input);
	println!("extractions:");
	println!("cipher :: message :: rating");
	for ( &cipher, &( ref message, ref rating)) in extractions.iter() {
		println!("{:02x} :: {} :: {:.2f}", cipher, message, *rating);}}

/// solution for challenge four
fn challenge_four(){
	println!("challenge four ::");
	let lines = read_lines( "data/4.txt");
	let mut i : int = 0;
	for ref line in lines.iter() {
		let extractions = crack_xor_cipher( line.as_slice());
		if ! extractions.is_empty() {
			println!("extractions for line {}:", i);
			for ( &cipher, &( ref message, ref rating)) in extractions.iter() {
				println!("{:02x} :: {} :: {:.2f}\n", cipher, message, *rating);}}
		i += 1;}}

/// solution for challenge five
fn challenge_five(){
	println!("challenge five ::");}

/// solution for challenge six
fn challenge_six(){
	println!("challenge six ::");}

/// solution for challenge seven
fn challenge_seven(){
	println!("challenge seven ::");}

/// solution for challenge eight
fn challenge_eight(){
	println!("challenge eight ::");}

//conversion functions
/// convert a hex string into base64
fn hexstr_tob64str( input: &str) -> String {
	//vars
	let bytes = hexstr_tobytes( input);
	let b64_codes : Vec<u8> = bytes_tob64codes( bytes.as_slice());
	return b64codes_tob64str( b64_codes.as_slice());
	}

/// convert a hex string into a byte vector
fn hexstr_tobytes( input: &str) -> Vec<u8> {
	let mut i = 0;
	let mut bytes : Vec<u8> = Vec::new();
	//parse bytes
	while i < input.len() {
		//current byte we're parsing
		let mut byte = 0u8;
		//parse first half
		byte += hex_toi( input.char_at( i));
		byte <<= 4;
		i += 1;
		//break if byte is \nincomplete
		if i == input.len() {
			bytes.push( byte);
			break;}
		//parse second half
		byte += hex_toi( input.char_at( i));
		//add byte
		bytes.push( byte);
		i += 1;}
	return bytes;}

fn bytes_tohexstr( bytes : &[u8]) -> String {
	let mut result = String::new();
	for &byte in bytes.iter() {
		let half1 = ( byte >> 4) & 0b1111;
		let half2 = byte & 0b1111;
		result.push_char( hexcode_tochar( half1));
		result.push_char( hexcode_tochar( half2));}
	return result;}

/// convert a byte string into b64 codes
fn bytes_tob64codes( bytes : &[u8]) -> Vec<u8> {
	let mut codes : Vec<u8> = Vec::new();
	//write bytes into b64 codes
	let mut leftover: u8 = 0;
	for i in range( 0, bytes.len()) {
		let byte = bytes[ i];
		match i % 3 {
			0 => {
				codes.push( ( byte >> 2));
				leftover = byte & 0b11;}
			1 => {
				codes.push( ( (leftover << 4) + (byte >> 4)));
				leftover = byte & 0b1111;}
			_ => {
				codes.push( ( (leftover << 2) + (byte >> 6)));
				codes.push( ( byte & 0b111111));}}}
	return codes;}

fn b64codes_tob64str( codes : &[u8]) -> String {
	let mut result = String::new();
	//convert b64 codes into string
	for i in range( 0, codes.len()) {
		result.push_char( b64code_tochar( codes[ i]));}
	return result;}

/// convert a hex char its corresponding int
fn hex_toi( character : char) -> u8 {
	match character {
		'0'..'9' => (character as u8) - ('0' as u8) + 0,
		'a'..'f' => (character as u8) - ('a' as u8) + 10,
		_ => fail!("error: input contained non-hex chars")}}

/// convert a base64 code into its corresponding char
fn b64code_tochar( code : u8) -> char {
	match code {
		00..25 => ( code - 00 + ('A' as u8)) as char,
		26..51 => ( code - 26 + ('a' as u8)) as char,
		52..61 => ( code - 52 + ('0' as u8)) as char,
		62 => '+',
		63 => '/',
		_ => fail!("error: code out of range")}}

/// convert a hec
fn hexcode_tochar( code : u8) -> char {
	match code {
		00..09 => ( code - 00 + ('0' as u8)) as char,
		10..15 => ( code - 10 + ('a' as u8)) as char,
		_ => fail!("error: code out of range")}}

//utilities
/// xor two byte arrays
fn xor_bytes( a : &[u8], b : &[u8]) -> Vec<u8> {
	let size = std::cmp::min( a.len(), b.len());
	let mut result = Vec::new();
	for i in range( 0, size) {
		result.push( a[i] ^ b[i]);}
	return result;}

fn score_bytes( bytes : &[u8]) -> TreeMap<u8, u8> {
	let mut table : TreeMap<u8, u8> = TreeMap::new();
	for &byte in bytes.iter() {
		let new_count =
			match table.find( &byte) {
				Some( count) => count + 1,
				None => 1};
		table.insert( byte, new_count);}
	return table;}

/// single byte xor cipher
fn byte_cipher( original : &[u8], cipher : u8) -> Vec<u8> {
	let mut result = Vec::new();
	for &byte in original.iter() {
		result.push( byte ^ cipher);}
	return result;}

fn crack_xor_cipher( input : &str) -> TreeMap< u8, (String, f32)> {
	//convert input to bytes
	let input_bytes = hexstr_tobytes( input);
	//create score table
	let input_scores = score_bytes( input_bytes.as_slice());
	//println!("score table: {}", input_scores);
	//find top scores
	let mut top_scores : Vec<u8> = Vec::new();
	let scores_cap = 3;
	for &value in input_scores.values() {
		top_scores.push( value);}
	top_scores.sort();
	top_scores.reverse();
	let top_scores = top_scores.slice_to( scores_cap);
	//println!("top {} scores: {}", scores_cap, top_scores);
	//find bytes that match top scores
	let mut common_bytes : Vec<u8> = Vec::new();
	for ( &byte, score) in input_scores.iter() {
		if top_scores.contains( score) {
			common_bytes.push( byte);}}
	//result of above:
	//  most common bytes in input_bytes: { 120: 6, 55: 5, 54: 3 }
	let common_bytes = common_bytes.as_slice();
	//println!("common bytes: {}", common_bytes);
	//let common_chars = [' ','e','t','a','o','i','n','s','h','r','d','l','u'];
	let common_chars = [' ', 'e', 't', 'a', 'o', 'i'];
	let mut extractions : TreeMap< u8, (String, f32)> = TreeMap::new();
	let rating_cap : f32 = 0.8;
	for &common_byte in common_bytes.iter() {
		for &common_char in common_chars.iter() {
			let common_char_byte = common_char.to_ascii().to_byte();
			let cipher_byte = common_char_byte ^ common_byte;
			if extractions.contains_key( &cipher_byte) {
				continue;}
			//create attempt
			let attempt_bytes = byte_cipher( input_bytes.as_slice(), cipher_byte);
			let attempt_slice = attempt_bytes.as_slice();
			if ! attempt_slice.is_ascii() {
				continue;}
			let attempt_ascii = attempt_slice.to_ascii();
			let attempt = attempt_ascii.as_str_ascii();
			for &trimchar in ['\n',' ','\t'].iter() {
				attempt.trim_chars( trimchar);}
			//try to filter attempt
			let rating  = rate_attempt( attempt);
			if rating > rating_cap {
				extractions.insert(
					cipher_byte, ( String::from_str( attempt), rating));}}}
	return extractions;}

/// rate the likelihood a string is a good decoding attempt
fn rate_attempt( attempt : &str) -> f32 {
	let good_chars = "abcdefghijklmnopqrstuvwxyz ,.?!\'\"";
	let neut_chars = "()[]{}<>-=_+:;/@#$%^&*~`";
	let mut positive : f32 = 0.0;
	let mut negative : f32 = 0.0;
	let count = attempt.len();
	//count good and (probably) bad chars
	for attempt_char in attempt.chars() {
		let char_lower = attempt_char.to_lowercase();
		if good_chars.contains_char( char_lower) {
			positive += 1.0;}
		else if ! neut_chars.contains_char( char_lower) {
			negative += 1.0;}}
	let numerator = positive.powi( 2) - negative.powi( 2);
	let denominator = count.to_f32().unwrap().powi( 2);
	return numerator / denominator;}

fn read_lines( filename : &str) -> Vec<String> {
	// Create a path to the desired file
	let path = Path::new( filename);
	let file = File::open( &path);
	let mut reader = BufferedReader::new( file);

	let mut result : Vec<String> = Vec::new();
	for line_result in reader.lines() {
		if ! line_result.is_ok() {
			fail!( "reading a line failed");}
		let line = line_result.unwrap();
		let line_stripped = line.as_slice().trim_chars('\n');
		result.push( String::from_str( line_stripped));}
	return result;}
<commit_msg>moving on to challenge five<commit_after>//imports
use std::collections::TreeMap;
use std::io::BufferedReader;
use std::io::File;

///rust all this set's challenges
fn main(){
	println!("set one ::");
	//challenge_one();
	//challenge_two();
	//challenge_three();
	//challenge_four();
	challenge_five();
	//challenge_six();
	//challenge_seven();
	//challenge_eight();
}

//challenges
/// solution for challenge one
fn challenge_one(){
	//println!("challenge one ::");
	//params
	let input = "49276d206b696c6c696e6720796f757220627261696e206c696b65206120706f69736f6e6f7573206d757368726f6f6d";
	let desired = "SSdtIGtpbGxpbmcgeW91ciBicmFpbiBsaWtlIGEgcG9pc29ub3VzIG11c2hyb29t";
	//get result
	let result = hexstr_tob64str( input);
	//print info
	//println!("input: {}", input);
	println!("\tresult: {}", result);
	println!("\tresult == desired: {}", result.as_slice() == desired);}

/// solution for challenge two
fn challenge_two(){
	println!("challenge two ::");
	//params
	let input_a = "1c0111001f010100061a024b53535009181c";
	let input_b = "686974207468652062756c6c277320657965";
	let desired = "746865206b696420646f6e277420706c6179";
	//convert to bytes
	let a_bytes = hexstr_tobytes( input_a);
	let b_bytes = hexstr_tobytes( input_b);
	//apply xor
	let result_bytes = xor_bytes(
		a_bytes.as_slice(), b_bytes.as_slice());
	//convert to string
	let result = bytes_tohexstr( result_bytes.as_slice());
	//print info
	println!("\tresult: {}", result);
	println!("\tresult == desired: {}", result.as_slice() == desired);}

/// solution for challenge three
fn challenge_three(){
	println!("challenge three ::");
	//params
	let input =
		"1b37373331363f78151b7f2b783431333d78397828372d363c78373e783a393b3736";
	let extractions = crack_xor_cipher( input);
	println!("extractions:");
	println!("cipher :: message :: rating");
	for ( &cipher, &( ref message, ref rating)) in extractions.iter() {
		println!("{:02x} :: {} :: {:.2f}", cipher, message, *rating);}}

/// solution for challenge four
fn challenge_four(){
	println!("challenge four ::");
	let lines = read_lines( "data/4.txt");
	let mut i : int = 0;
	for ref line in lines.iter() {
		let extractions = crack_xor_cipher( line.as_slice());
		if ! extractions.is_empty() {
			println!("extractions for line {}:", i);
			for ( &cipher, &( ref message, ref rating)) in extractions.iter() {
				println!("{:02x} :: {} :: {:.2f}\n", cipher, message, *rating);}}
		i += 1;}}

/// solution for challenge five
fn challenge_five(){
	println!("challenge five ::");}

/// solution for challenge six
fn challenge_six(){
	println!("challenge six ::");}

/// solution for challenge seven
fn challenge_seven(){
	println!("challenge seven ::");}

/// solution for challenge eight
fn challenge_eight(){
	println!("challenge eight ::");}

//conversion functions
/// convert a hex string into base64
fn hexstr_tob64str( input: &str) -> String {
	//vars
	let bytes = hexstr_tobytes( input);
	let b64_codes : Vec<u8> = bytes_tob64codes( bytes.as_slice());
	return b64codes_tob64str( b64_codes.as_slice());
	}

/// convert a hex string into a byte vector
fn hexstr_tobytes( input: &str) -> Vec<u8> {
	let mut i = 0;
	let mut bytes : Vec<u8> = Vec::new();
	//parse bytes
	while i < input.len() {
		//current byte we're parsing
		let mut byte = 0u8;
		//parse first half
		byte += hex_toi( input.char_at( i));
		byte <<= 4;
		i += 1;
		//break if byte is \nincomplete
		if i == input.len() {
			bytes.push( byte);
			break;}
		//parse second half
		byte += hex_toi( input.char_at( i));
		//add byte
		bytes.push( byte);
		i += 1;}
	return bytes;}

fn bytes_tohexstr( bytes : &[u8]) -> String {
	let mut result = String::new();
	for &byte in bytes.iter() {
		let half1 = ( byte >> 4) & 0b1111;
		let half2 = byte & 0b1111;
		result.push_char( hexcode_tochar( half1));
		result.push_char( hexcode_tochar( half2));}
	return result;}

/// convert a byte string into b64 codes
fn bytes_tob64codes( bytes : &[u8]) -> Vec<u8> {
	let mut codes : Vec<u8> = Vec::new();
	//write bytes into b64 codes
	let mut leftover: u8 = 0;
	for i in range( 0, bytes.len()) {
		let byte = bytes[ i];
		match i % 3 {
			0 => {
				codes.push( ( byte >> 2));
				leftover = byte & 0b11;}
			1 => {
				codes.push( ( (leftover << 4) + (byte >> 4)));
				leftover = byte & 0b1111;}
			_ => {
				codes.push( ( (leftover << 2) + (byte >> 6)));
				codes.push( ( byte & 0b111111));}}}
	return codes;}

fn b64codes_tob64str( codes : &[u8]) -> String {
	let mut result = String::new();
	//convert b64 codes into string
	for i in range( 0, codes.len()) {
		result.push_char( b64code_tochar( codes[ i]));}
	return result;}

/// convert a hex char its corresponding int
fn hex_toi( character : char) -> u8 {
	match character {
		'0'..'9' => (character as u8) - ('0' as u8) + 0,
		'a'..'f' => (character as u8) - ('a' as u8) + 10,
		_ => fail!("error: input contained non-hex chars")}}

/// convert a base64 code into its corresponding char
fn b64code_tochar( code : u8) -> char {
	match code {
		00..25 => ( code - 00 + ('A' as u8)) as char,
		26..51 => ( code - 26 + ('a' as u8)) as char,
		52..61 => ( code - 52 + ('0' as u8)) as char,
		62 => '+',
		63 => '/',
		_ => fail!("error: code out of range")}}

/// convert a hec
fn hexcode_tochar( code : u8) -> char {
	match code {
		00..09 => ( code - 00 + ('0' as u8)) as char,
		10..15 => ( code - 10 + ('a' as u8)) as char,
		_ => fail!("error: code out of range")}}

//utilities
/// xor two byte arrays
fn xor_bytes( a : &[u8], b : &[u8]) -> Vec<u8> {
	let size = std::cmp::min( a.len(), b.len());
	let mut result = Vec::new();
	for i in range( 0, size) {
		result.push( a[i] ^ b[i]);}
	return result;}

fn score_bytes( bytes : &[u8]) -> TreeMap<u8, u8> {
	let mut table : TreeMap<u8, u8> = TreeMap::new();
	for &byte in bytes.iter() {
		let new_count =
			match table.find( &byte) {
				Some( count) => count + 1,
				None => 1};
		table.insert( byte, new_count);}
	return table;}

/// single byte xor cipher
fn byte_cipher( original : &[u8], cipher : u8) -> Vec<u8> {
	let mut result = Vec::new();
	for &byte in original.iter() {
		result.push( byte ^ cipher);}
	return result;}

fn crack_xor_cipher( input : &str) -> TreeMap< u8, (String, f32)> {
	//convert input to bytes
	let input_bytes = hexstr_tobytes( input);
	//create score table
	let input_scores = score_bytes( input_bytes.as_slice());
	//println!("score table: {}", input_scores);
	//find top scores
	let mut top_scores : Vec<u8> = Vec::new();
	let scores_cap = 3;
	for &value in input_scores.values() {
		top_scores.push( value);}
	top_scores.sort();
	top_scores.reverse();
	let top_scores = top_scores.slice_to( scores_cap);
	//println!("top {} scores: {}", scores_cap, top_scores);
	//find bytes that match top scores
	let mut common_bytes : Vec<u8> = Vec::new();
	for ( &byte, score) in input_scores.iter() {
		if top_scores.contains( score) {
			common_bytes.push( byte);}}
	//result of above:
	//  most common bytes in input_bytes: { 120: 6, 55: 5, 54: 3 }
	let common_bytes = common_bytes.as_slice();
	//println!("common bytes: {}", common_bytes);
	//let common_chars = [' ','e','t','a','o','i','n','s','h','r','d','l','u'];
	let common_chars = [' ', 'e', 't', 'a', 'o', 'i'];
	let mut extractions : TreeMap< u8, (String, f32)> = TreeMap::new();
	let rating_cap : f32 = 0.8;
	for &common_byte in common_bytes.iter() {
		for &common_char in common_chars.iter() {
			let common_char_byte = common_char.to_ascii().to_byte();
			let cipher_byte = common_char_byte ^ common_byte;
			if extractions.contains_key( &cipher_byte) {
				continue;}
			//create attempt
			let attempt_bytes = byte_cipher( input_bytes.as_slice(), cipher_byte);
			let attempt_slice = attempt_bytes.as_slice();
			if ! attempt_slice.is_ascii() {
				continue;}
			let attempt_ascii = attempt_slice.to_ascii();
			let attempt = attempt_ascii.as_str_ascii();
			for &trimchar in ['\n',' ','\t'].iter() {
				attempt.trim_chars( trimchar);}
			//try to filter attempt
			let rating  = rate_attempt( attempt);
			if rating > rating_cap {
				extractions.insert(
					cipher_byte, ( String::from_str( attempt), rating));}}}
	return extractions;}

/// rate the likelihood a string is a good decoding attempt
fn rate_attempt( attempt : &str) -> f32 {
	let good_chars = "abcdefghijklmnopqrstuvwxyz ,.?!\'\"";
	let neut_chars = "()[]{}<>-=_+:;/@#$%^&*~`";
	let mut positive : f32 = 0.0;
	let mut negative : f32 = 0.0;
	let count = attempt.len();
	//count good and (probably) bad chars
	for attempt_char in attempt.chars() {
		let char_lower = attempt_char.to_lowercase();
		if good_chars.contains_char( char_lower) {
			positive += 1.0;}
		else if ! neut_chars.contains_char( char_lower) {
			negative += 1.0;}}
	let numerator = positive.powi( 2) - negative.powi( 2);
	let denominator = count.to_f32().unwrap().powi( 2);
	return numerator / denominator;}

fn read_lines( filename : &str) -> Vec<String> {
	// Create a path to the desired file
	let path = Path::new( filename);
	let file = File::open( &path);
	let mut reader = BufferedReader::new( file);

	let mut result : Vec<String> = Vec::new();
	for line_result in reader.lines() {
		if ! line_result.is_ok() {
			fail!( "reading a line failed");}
		let line = line_result.unwrap();
		let line_stripped = line.as_slice().trim_chars('\n');
		result.push( String::from_str( line_stripped));}
	return result;}
<|endoftext|>