<commit_before>//! Contains the routing logic for the website.

use std::error::Error;
use std::fs;
use std::path::Path;

use chrono::NaiveDate;
use mount::Mount;
use hbs::{self, DirectorySource, HandlebarsEngine, Template};
use iron::prelude::*;
use iron::{status, AfterMiddleware, Handler};
use params::{Params, Value};
use persistent::{self, Read};
use router::{Router, NoRoute};
use serde_json;
use staticfile::Static;

use blog;
use config;
use helpers;
use projects::Project;
use persistence::{DatabaseConnectionPool, ConnectionPool, Config, Projects};

/// The number of blog post summaries that should be displayed.
const NUM_SUMMARIES: usize = 3;

fn resume(req: &mut Request) -> IronResult<Response> {
    let data = json!({
        "resume_link": req.get::<Read<Config>>().unwrap().resume_link.to_string(),
    });
    Ok(Response::with((status::Ok, Template::new("resume", data))))
}

fn projects(req: &mut Request) -> IronResult<Response> {
    let projects = req.get::<Read<Projects>>().unwrap();
    let data = json!({
        "projects": projects,
    });
    Ok(Response::with((status::Ok, Template::new("projects", data))))
}

fn blog_post(req: &mut Request) -> IronResult<Response> {
    let connection = req.extensions
        .get::<Read<DatabaseConnectionPool>>()
        .unwrap()
        .get()
        .unwrap();
    let params = req.extensions.get::<Router>().unwrap();

    let year = params.find("year").and_then(|y| y.parse().ok());
    let month = params.find("month").and_then(|m| m.parse().ok());
    let day = params.find("day").and_then(|d| d.parse().ok());
    let title = try!(req.extensions
        .get::<Router>()
        .unwrap()
        .find("title")
        .ok_or(IronError::new(NoRoute, status::NotFound)));

    let date = match (year, month, day) {
        (Some(year), Some(month), Some(day)) => NaiveDate::from_ymd_opt(year, month, day),
        _ => None,
    };

    let date = try!(date.ok_or(IronError::new(NoRoute, status::NotFound)));

    match blog::get_post(&connection, &date, title) {
        Ok(ref post) => Ok(Response::with((status::Ok, Template::new("blog_post", post)))),
        Err(err) => {
            error!("Error retrieving blog post: {}", err);
            Err(IronError::new(NoRoute, status::NotFound))
        }
    }
}

fn blog(req: &mut Request) -> IronResult<Response> {
    let connection = req.get::<Read<DatabaseConnectionPool>>()
        .unwrap()
        .get()
        .unwrap();

    let query = match req.get_ref::<Params>().unwrap().find(&["q"]) {
        Some(&Value::String(ref query)) if !query.is_empty() => Some(query.to_owned()),
        _ => None,
    };

    let summaries = if let Some(ref query) = query {
        blog::find_summaries(&connection, query)
    } else {
        blog::get_summaries(&connection)
    };

    let mut data = json!({
        "posts": itry!(summaries)
    });

    if let Some(query) = query.and_then(|query| serde_json::to_value(query).ok()) {
        *data.get_mut("query").unwrap() = query;
    }

    Ok(Response::with((status::Ok, Template::new("blog", data))))
}

fn about(_: &mut Request) -> IronResult<Response> {
    let images = Path::new("static/images/slideshow");
    let image_urls = fs::read_dir(images)
        .unwrap()
        .into_iter()
        .map(|path| {
            path.unwrap()
                .path()
                .to_str()
                .unwrap()
                .to_owned()
        })
        .collect::<Vec<_>>();

    let data = json!({
        "image_urls": image_urls
    });
    Ok(Response::with((status::Ok, Template::new("about", data))))
}

fn index(req: &mut Request) -> IronResult<Response> {
    let connection = req.extensions
        .get::<Read<DatabaseConnectionPool>>()
        .unwrap()
        .get()
        .unwrap();
    let posts = blog::get_summaries(&connection);

    let data = json!({
        "posts": posts.unwrap().into_iter().take(NUM_SUMMARIES).collect::<Vec<_>>()
    });
    Ok(Response::with((status::Ok, Template::new("index", data))))
}

/// Returns the router for the server.
fn get_router() -> Router {
    router!(
        index:      get "/" => index,
        about:      get "/about" => about,
        blog:       get "/blog" => blog,
        blog_post:  get "/blog/:year/:month/:day/:title" => blog_post,
        projects:   get "/projects" => projects,
        resume:     get "/resume" => resume,

        favicon:    get "/favicon.ico" => Static::new(Path::new("static/images")),
        robots_txt: get "/robots.txt" => Static::new(Path::new("static")),
    )
}

fn initialize_templates(folder: &str,
                        extension: &str)
                        -> Result<HandlebarsEngine, hbs::SourceError> {
    let mut hbse = HandlebarsEngine::new();
    hbse.add(Box::new(DirectorySource::new(folder, extension)));
    try!(hbse.reload());

    {
        let mut reg = hbse.registry.write().unwrap();
        reg.register_helper("join", Box::new(helpers::join));
    }

    Ok(hbse)
}

fn mount(chain: Chain) -> Mount {
    let mut mount = Mount::new();
    mount.mount("/", chain);
    mount.mount("/static",
                Static::new(Path::new(env!("OUT_DIR")).join("static")));
    mount
}

/// Returns the main route handler for the website.
///
/// This handler contains a number of middleware:
///   - Routing for all page requests
///   - Routing for static files
///   - Persistence for the website configuration
///   - Rendering handlebars templates
///   - Error reporting
///   - Error handling
pub fn handler(config: config::Config,
               projects: Vec<Project>,
               connection_pool: ConnectionPool)
               -> Box<Handler> {
    let mut chain = Chain::new(get_router());

    chain.link_before(persistent::Read::<Config>::one(config));
    chain.link_before(persistent::Read::<Projects>::one(projects));
    chain.link_before(persistent::Read::<DatabaseConnectionPool>::one(connection_pool));

    chain.link_after(ErrorHandler);
    chain.link_after(initialize_templates("./templates/", ".hbs").unwrap());
    chain.link_after(ErrorReporter);

    let mount = mount(chain);
    Box::new(mount)
}

struct ErrorReporter;

impl AfterMiddleware for ErrorReporter {
    fn catch(&self, _: &mut Request, err: IronError) -> IronResult<Response> {
        error!("{}", err.description());
        Err(err)
    }
}

struct ErrorHandler;

impl AfterMiddleware for ErrorHandler {
    fn catch(&self, _: &mut Request, err: IronError) -> IronResult<Response> {
        if let Some(_) = err.error.downcast::<NoRoute>() {
            Ok(Response::with((status::NotFound, Template::new("not_found", ()))))
        } else {
            Err(err)
        }
    }
}

#[cfg(test)]
mod tests {
    extern crate iron;
    extern crate iron_test;
    extern crate tempfile;
    extern crate url;

    use std::fs::File;
    use std::io::prelude::*;

    use self::iron::{Handler, Headers};
    use self::iron_test::{request, response};
    use self::tempfile::NamedTempFile;
    use self::url::Url;

    use config::Config;
    use persistence;

    struct Server {
        pub handler: Box<Handler>,
        pub database: NamedTempFile,
    }

    /// Creates a handler for the website.
    ///
    /// Returns a pair containing the handler, and a tempfile containing a test sqlite database.
    fn create_server() -> Server {
        let tempfile = NamedTempFile::new().unwrap();

        // Populate the database.
        //
        // FIXME: This should be handled by a server object, to avoid duplication between code and
        // tests.
        let uri = format!("file:{}", tempfile.path().to_str().unwrap());
        let pool = persistence::get_connection_pool(&uri);
        let connection = pool.get().unwrap();
        let schema = {
            let mut schema_file = File::open("schema.sql").unwrap();
            let mut schema = String::new();
            schema_file.read_to_string(&mut schema).unwrap();
            schema
        };

        connection.execute_batch(&schema).unwrap();

        let handler =
            super::handler(Config { resume_link: Url::parse("http://google.com").unwrap() },
                           vec![],
                           pool);
        Server {
            handler,
            database: tempfile,
        }
    }

    #[test]
    fn index() {
        let server = create_server();
        let response = request::get("http://localhost:3000/", Headers::new(), &server.handler)
            .unwrap();
        assert!(response.status.unwrap().is_success());
    }

    #[test]
    fn blog() {
        let server = create_server();
        let response = request::get("http://localhost:3000/blog",
                                    Headers::new(),
                                    &server.handler)
                .unwrap();
        assert!(response.status.unwrap().is_success());
    }

    #[test]
    fn about() {
        let server = create_server();
        let response = request::get("http://localhost:3000/about",
                                    Headers::new(),
                                    &server.handler)
                .unwrap();
        assert!(response.status.unwrap().is_success());
    }

    #[test]
    fn projects() {
        let server = create_server();
        let response = request::get("http://localhost:3000/projects",
                                    Headers::new(),
                                    &server.handler)
                .unwrap();
        assert!(response.status.unwrap().is_success());
    }

    #[test]
    fn resume() {
        let server = create_server();
        let response = request::get("http://localhost:3000/resume",
                                    Headers::new(),
                                    &server.handler)
                .unwrap();
        assert!(response.status.unwrap().is_success());
    }

    #[test]
    fn post_dates() {
        let server = create_server();
        let response = request::get("http://localhost:3000/blog/2016/13/31/invalid-date",
                                    Headers::new(),
                                    &server.handler)
                .unwrap();
        assert!(response.status.unwrap().is_client_error());
    }

    #[test]
    fn static_files() {
        let server = create_server();
        let response = request::get("http://localhost:3000/favicon.ico",
                                    Headers::new(),
                                    &server.handler)
                .unwrap();
        assert!(response.status.unwrap().is_success());

        let response = request::get("http://localhost:3000/robots.txt",
                                    Headers::new(),
                                    &server.handler)
                .unwrap();
        assert!(response.status.unwrap().is_success());
    }

    #[test]
    fn not_found() {
        let server = create_server();
        let response = request::get("http://localhost:3000/this/path/does/not/exist",
                                    Headers::new(),
                                    &server.handler)
                .unwrap();
        let body = response::extract_body_to_string(response);
        assert!(body.contains("Page Not Found"));
    }

    #[test]
    fn css() {
        let server = create_server();
        let response = request::get("http://localhost:3000/static/css/styles.css",
                                    Headers::new(),
                                    &server.handler)
                .unwrap();
        assert!(response.status.unwrap().is_success());
    }
}
<commit_msg>fix(watch): watch templates when feature enabled<commit_after>//! Contains the routing logic for the website.

use std::error::Error;
use std::fs;
use std::path::Path;
use std::sync::Arc;

use chrono::NaiveDate;
use mount::Mount;
use hbs::{self, DirectorySource, HandlebarsEngine, Template};
use iron::prelude::*;
use iron::{status, AfterMiddleware, Handler};
use params::{Params, Value};
use persistent::{self, Read};
use router::{Router, NoRoute};
use serde_json;
use staticfile::Static;

#[cfg(feature = "watch")]
use hbs::Watchable;

use blog;
use config;
use helpers;
use projects::Project;
use persistence::{DatabaseConnectionPool, ConnectionPool, Config, Projects};

/// The number of blog post summaries that should be displayed.
const NUM_SUMMARIES: usize = 3;

fn resume(req: &mut Request) -> IronResult<Response> {
    let data = json!({
        "resume_link": req.get::<Read<Config>>().unwrap().resume_link.to_string(),
    });
    Ok(Response::with((status::Ok, Template::new("resume", data))))
}

fn projects(req: &mut Request) -> IronResult<Response> {
    let projects = req.get::<Read<Projects>>().unwrap();
    let data = json!({
        "projects": projects,
    });
    Ok(Response::with((status::Ok, Template::new("projects", data))))
}

fn blog_post(req: &mut Request) -> IronResult<Response> {
    let connection = req.extensions
        .get::<Read<DatabaseConnectionPool>>()
        .unwrap()
        .get()
        .unwrap();
    let params = req.extensions.get::<Router>().unwrap();

    let year = params.find("year").and_then(|y| y.parse().ok());
    let month = params.find("month").and_then(|m| m.parse().ok());
    let day = params.find("day").and_then(|d| d.parse().ok());
    let title = try!(req.extensions
        .get::<Router>()
        .unwrap()
        .find("title")
        .ok_or(IronError::new(NoRoute, status::NotFound)));

    let date = match (year, month, day) {
        (Some(year), Some(month), Some(day)) => NaiveDate::from_ymd_opt(year, month, day),
        _ => None,
    };

    let date = try!(date.ok_or(IronError::new(NoRoute, status::NotFound)));

    match blog::get_post(&connection, &date, title) {
        Ok(ref post) => Ok(Response::with((status::Ok, Template::new("blog_post", post)))),
        Err(err) => {
            error!("Error retrieving blog post: {}", err);
            Err(IronError::new(NoRoute, status::NotFound))
        }
    }
}

fn blog(req: &mut Request) -> IronResult<Response> {
    let connection = req.get::<Read<DatabaseConnectionPool>>()
        .unwrap()
        .get()
        .unwrap();

    let query = match req.get_ref::<Params>().unwrap().find(&["q"]) {
        Some(&Value::String(ref query)) if !query.is_empty() => Some(query.to_owned()),
        _ => None,
    };

    let summaries = if let Some(ref query) = query {
        blog::find_summaries(&connection, query)
    } else {
        blog::get_summaries(&connection)
    };

    let mut data = json!({
        "posts": itry!(summaries)
    });

    if let Some(query) = query.and_then(|query| serde_json::to_value(query).ok()) {
        *data.get_mut("query").unwrap() = query;
    }

    Ok(Response::with((status::Ok, Template::new("blog", data))))
}

fn about(_: &mut Request) -> IronResult<Response> {
    let images = Path::new("static/images/slideshow");
    let image_urls = fs::read_dir(images)
        .unwrap()
        .into_iter()
        .map(|path| {
            path.unwrap()
                .path()
                .to_str()
                .unwrap()
                .to_owned()
        })
        .collect::<Vec<_>>();

    let data = json!({
        "image_urls": image_urls
    });
    Ok(Response::with((status::Ok, Template::new("about", data))))
}

fn index(req: &mut Request) -> IronResult<Response> {
    let connection = req.extensions
        .get::<Read<DatabaseConnectionPool>>()
        .unwrap()
        .get()
        .unwrap();
    let posts = blog::get_summaries(&connection);

    let data = json!({
        "posts": posts.unwrap().into_iter().take(NUM_SUMMARIES).collect::<Vec<_>>()
    });
    Ok(Response::with((status::Ok, Template::new("index", data))))
}

/// Returns the router for the server.
fn get_router() -> Router {
    router!(
        index:      get "/" => index,
        about:      get "/about" => about,
        blog:       get "/blog" => blog,
        blog_post:  get "/blog/:year/:month/:day/:title" => blog_post,
        projects:   get "/projects" => projects,
        resume:     get "/resume" => resume,

        favicon:    get "/favicon.ico" => Static::new(Path::new("static/images")),
        robots_txt: get "/robots.txt" => Static::new(Path::new("static")),
    )
}

#[cfg(feature = "watch")]
fn watch_templates(hbse: Arc<HandlebarsEngine>, path: &str) {
    hbse.watch(path);
}

#[cfg(not(feature = "watch"))]
fn watch_templates(_hbse: Arc<HandlebarsEngine>, _path: &str) {}

fn initialize_templates(folder: &str,
                        extension: &str)
                        -> Result<Arc<HandlebarsEngine>, hbs::SourceError> {
    let mut hbse = HandlebarsEngine::new();
    hbse.add(Box::new(DirectorySource::new(folder, extension)));
    try!(hbse.reload());

    {
        let mut reg = hbse.registry.write().unwrap();
        reg.register_helper("join", Box::new(helpers::join));
    }

    let hbse = Arc::new(hbse);

    watch_templates(hbse.clone(), "./templates");

    Ok(hbse)
}

fn mount(chain: Chain) -> Mount {
    let mut mount = Mount::new();
    mount.mount("/", chain);
    mount.mount("/static",
                Static::new(Path::new(env!("OUT_DIR")).join("static")));
    mount
}

/// Returns the main route handler for the website.
///
/// This handler contains a number of middleware:
///   - Routing for all page requests
///   - Routing for static files
///   - Persistence for the website configuration
///   - Rendering handlebars templates
///   - Error reporting
///   - Error handling
pub fn handler(config: config::Config,
               projects: Vec<Project>,
               connection_pool: ConnectionPool)
               -> Box<Handler> {
    let mut chain = Chain::new(get_router());

    chain.link_before(persistent::Read::<Config>::one(config));
    chain.link_before(persistent::Read::<Projects>::one(projects));
    chain.link_before(persistent::Read::<DatabaseConnectionPool>::one(connection_pool));

    chain.link_after(ErrorHandler);
    chain.link_after(initialize_templates("./templates/", ".hbs").unwrap());
    chain.link_after(ErrorReporter);

    let mount = mount(chain);
    Box::new(mount)
}

struct ErrorReporter;

impl AfterMiddleware for ErrorReporter {
    fn catch(&self, _: &mut Request, err: IronError) -> IronResult<Response> {
        error!("{}", err.description());
        Err(err)
    }
}

struct ErrorHandler;

impl AfterMiddleware for ErrorHandler {
    fn catch(&self, _: &mut Request, err: IronError) -> IronResult<Response> {
        if let Some(_) = err.error.downcast::<NoRoute>() {
            Ok(Response::with((status::NotFound, Template::new("not_found", ()))))
        } else {
            Err(err)
        }
    }
}

#[cfg(test)]
mod tests {
    extern crate iron;
    extern crate iron_test;
    extern crate tempfile;
    extern crate url;

    use std::fs::File;
    use std::io::prelude::*;

    use self::iron::{Handler, Headers};
    use self::iron_test::{request, response};
    use self::tempfile::NamedTempFile;
    use self::url::Url;

    use config::Config;
    use persistence;

    struct Server {
        pub handler: Box<Handler>,
        pub database: NamedTempFile,
    }

    /// Creates a handler for the website.
    ///
    /// Returns a pair containing the handler, and a tempfile containing a test sqlite database.
    fn create_server() -> Server {
        let tempfile = NamedTempFile::new().unwrap();

        // Populate the database.
        //
        // FIXME: This should be handled by a server object, to avoid duplication between code and
        // tests.
        let uri = format!("file:{}", tempfile.path().to_str().unwrap());
        let pool = persistence::get_connection_pool(&uri);
        let connection = pool.get().unwrap();
        let schema = {
            let mut schema_file = File::open("schema.sql").unwrap();
            let mut schema = String::new();
            schema_file.read_to_string(&mut schema).unwrap();
            schema
        };

        connection.execute_batch(&schema).unwrap();

        let handler =
            super::handler(Config { resume_link: Url::parse("http://google.com").unwrap() },
                           vec![],
                           pool);
        Server {
            handler,
            database: tempfile,
        }
    }

    #[test]
    fn index() {
        let server = create_server();
        let response = request::get("http://localhost:3000/", Headers::new(), &server.handler)
            .unwrap();
        assert!(response.status.unwrap().is_success());
    }

    #[test]
    fn blog() {
        let server = create_server();
        let response = request::get("http://localhost:3000/blog",
                                    Headers::new(),
                                    &server.handler)
                .unwrap();
        assert!(response.status.unwrap().is_success());
    }

    #[test]
    fn about() {
        let server = create_server();
        let response = request::get("http://localhost:3000/about",
                                    Headers::new(),
                                    &server.handler)
                .unwrap();
        assert!(response.status.unwrap().is_success());
    }

    #[test]
    fn projects() {
        let server = create_server();
        let response = request::get("http://localhost:3000/projects",
                                    Headers::new(),
                                    &server.handler)
                .unwrap();
        assert!(response.status.unwrap().is_success());
    }

    #[test]
    fn resume() {
        let server = create_server();
        let response = request::get("http://localhost:3000/resume",
                                    Headers::new(),
                                    &server.handler)
                .unwrap();
        assert!(response.status.unwrap().is_success());
    }

    #[test]
    fn post_dates() {
        let server = create_server();
        let response = request::get("http://localhost:3000/blog/2016/13/31/invalid-date",
                                    Headers::new(),
                                    &server.handler)
                .unwrap();
        assert!(response.status.unwrap().is_client_error());
    }

    #[test]
    fn static_files() {
        let server = create_server();
        let response = request::get("http://localhost:3000/favicon.ico",
                                    Headers::new(),
                                    &server.handler)
                .unwrap();
        assert!(response.status.unwrap().is_success());

        let response = request::get("http://localhost:3000/robots.txt",
                                    Headers::new(),
                                    &server.handler)
                .unwrap();
        assert!(response.status.unwrap().is_success());
    }

    #[test]
    fn not_found() {
        let server = create_server();
        let response = request::get("http://localhost:3000/this/path/does/not/exist",
                                    Headers::new(),
                                    &server.handler)
                .unwrap();
        let body = response::extract_body_to_string(response);
        assert!(body.contains("Page Not Found"));
    }

    #[test]
    fn css() {
        let server = create_server();
        let response = request::get("http://localhost:3000/static/css/styles.css",
                                    Headers::new(),
                                    &server.handler)
                .unwrap();
        assert!(response.status.unwrap().is_success());
    }
}
<|endoftext|>


<commit_before>#![feature(phase)]
#[phase(syntax, link)] extern crate log;
extern crate collections;
extern crate sync;
extern crate rand;
extern crate serialize;
use std::io::MemWriter;
use serialize::ebml::writer;
use serialize::ebml::reader;
use serialize::{Encodable, Decodable};
mod buffer;

enum SqlType {
	Char(uint),
	Varchar(uint),
	Integer,
}

enum SqlAttribute {
	Null,
	NotNull,
}

struct Column {
	name: ~str,
	datatype: SqlType,
	attributes: Vec<SqlAttribute>,
}

struct Relation {
	name: ~str,
	columns: Vec<Column>,
}

impl Relation {
	pub fn new(name: ~str) -> Relation {
		Relation {name: name, columns: Vec::new()}
	}

	pub fn add_column(&mut self, column: Column) {
		self.columns.push(column);
	}
}

struct Schema {
	relations: Vec<Relation>,
}

impl Schema {
	pub fn new() -> Schema {
		Schema {relations: Vec::new()}
	}

	pub fn new_from_disk(bufmanager: buffer::BufferManager) -> Schema {
		// TODO
		Schema {relations: Vec::new()}
	}

	pub fn add_relation(&mut self, relation: Relation) {
		self.relations.push(relation);
	}

	pub fn save_to_disk(&self) {
		let mut wr = MemWriter::new();
		let v: u64 = 42;
		{
			let mut ebml_w = writer::Encoder(&mut wr);
			let _ = v.encode(&mut ebml_w);
		}
		let ebml_doc = reader::Doc(wr.get_ref());
		let mut deser = reader::Decoder(ebml_doc);
		let v1: u64 = Decodable::decode(&mut deser).unwrap();
		println!("v1 == {:?}", v1);

		for rel in self.relations.iter() {
			//rel.save_to_disk()
		}
	}
}

struct Record {
	// TODO: do we even need this?
	len: uint,
	data: Vec<u8>,
}

impl Record {
	/* move semantics, no copy */
	pub fn new(len: uint, data: Vec<u8>) -> Record {
		Record {len: len, data: data}
	}

	pub fn get_data<'a>(&'a self) -> &'a [u8] {
		self.data.as_slice()
	}
}

struct SPSegment;

/* our newtype struct: create a TID type as an alias to u64 */
struct TID(u64);

impl SPSegment {
	pub fn insert(r: Record) -> TID {
		TID(0_u64)
	}

	pub fn remove(tid: TID) -> bool {
		false
	}

	pub fn lookup(tid: TID) -> Record {
		// TODO
		Record {len: 1, data: vec!(1)}
	}

	pub fn update(tid: TID, r: Record) -> bool {
		false
	}
}

#[test]
fn main() {
}

#[test]
fn create_schema() {
	let name = Column {name: ~"name", datatype: Varchar(128), attributes: vec!(NotNull)};
	let age = Column {name: ~"age", datatype: Integer, attributes: vec!(NotNull)};
	let mut relation = Relation::new(~"Person");
	relation.add_column(name);
	relation.add_column(age);
	let mut schema = Schema::new();
	schema.add_relation(relation);
	schema.save_to_disk();
	assert!(false);
}
<commit_msg>Implement traits for encoding/decoding<commit_after>#![feature(phase)]
#[phase(syntax, link)] extern crate log;
extern crate collections;
extern crate sync;
extern crate rand;
extern crate serialize;
use std::io::MemWriter;
use serialize::ebml::writer;
use serialize::ebml::reader;
use serialize::{Encodable, Decodable};
mod buffer;

#[deriving(Encodable, Decodable)]
enum SqlType {
	Char(uint),
	Varchar(uint),
	Integer,
}

#[deriving(Encodable, Decodable)]
enum SqlAttribute {
	Null,
	NotNull,
}

#[deriving(Encodable, Decodable)]
struct Column {
	name: ~str,
	datatype: SqlType,
	attributes: Vec<SqlAttribute>,
}

#[deriving(Encodable, Decodable)]
struct Relation {
	name: ~str,
	columns: Vec<Column>,
}

impl Relation {
	pub fn new(name: ~str) -> Relation {
		Relation {name: name, columns: Vec::new()}
	}

	pub fn add_column(&mut self, column: Column) {
		self.columns.push(column);
	}
}

#[deriving(Encodable, Decodable)]
struct Schema {
	relations: Vec<Relation>,
}

impl Schema {
	pub fn new() -> Schema {
		Schema {relations: Vec::new()}
	}

	pub fn new_from_disk(bufmanager: buffer::BufferManager) -> Schema {
		// TODO
		Schema {relations: Vec::new()}
	}

	pub fn add_relation(&mut self, relation: Relation) {
		self.relations.push(relation);
	}

	pub fn save_to_disk(&self) {
		let mut wr = MemWriter::new();
		let v: u64 = 42;
		{
			let mut ebml_w = writer::Encoder(&mut wr);
			let _ = self.encode(&mut ebml_w);
		}
		let ebml_doc = reader::Doc(wr.get_ref());
		let mut deser = reader::Decoder(ebml_doc);
		let v1: Schema = Decodable::decode(&mut deser).unwrap();
		println!("v1 == {:?}", v1);

		for rel in self.relations.iter() {
			//rel.save_to_disk()
		}
	}
}

struct Record {
	// TODO: do we even need this?
	len: uint,
	data: Vec<u8>,
}

impl Record {
	/* move semantics, no copy */
	pub fn new(len: uint, data: Vec<u8>) -> Record {
		Record {len: len, data: data}
	}

	pub fn get_data<'a>(&'a self) -> &'a [u8] {
		self.data.as_slice()
	}
}

struct SPSegment;

/* our newtype struct: create a TID type as an alias to u64 */
struct TID(u64);

impl SPSegment {
	pub fn insert(r: Record) -> TID {
		TID(0_u64)
	}

	pub fn remove(tid: TID) -> bool {
		false
	}

	pub fn lookup(tid: TID) -> Record {
		// TODO
		Record {len: 1, data: vec!(1)}
	}

	pub fn update(tid: TID, r: Record) -> bool {
		false
	}
}

#[test]
fn main() {
}

#[test]
fn create_schema() {
	let name = Column {name: ~"name", datatype: Varchar(128), attributes: vec!(NotNull)};
	let age = Column {name: ~"age", datatype: Integer, attributes: vec!(NotNull)};
	let mut relation = Relation::new(~"Person");
	relation.add_column(name);
	relation.add_column(age);
	let mut schema = Schema::new();
	schema.add_relation(relation);
	schema.save_to_disk();
	assert!(false);
}
<|endoftext|>


<commit_before>#![allow(dead_code)]
#![allow(unused_assignments)]

#[derive(PartialEq, Eq, Debug, Clone, Copy)]
pub enum Script {
    Latin,
    Cyrillic,
    Arabic,
    Devanagari,
    Ethiopic,
    Hebrew,
    Cmn,
    Kat
}

macro_rules! check_scripts {
    ( $text:ident, $( $script:expr => $is_script:ident ),* ) => {
        {
            let mut max_score = 0;
            let mut result = None;
            let mut current_score = 0;
            let half = $text.len() / 2;

            $(
                current_score = 0;
                for ch in $text.chars() {
                    if $is_script(ch) { current_score += 1; }
                }
                if current_score > half {
                    return Some($script);
                }
                if current_score > max_score {
                    max_score = current_score;
                    result = Some($script);
                }
            )*
            result
        }
    };
}

pub fn detect_script(text : String) -> Option<Script> {
    check_scripts![
        text,
        Script::Cyrillic => is_cyrillic,
        Script::Latin => is_latin,
        Script::Arabic => is_arabic,
        Script::Devanagari => is_devanagari,
        Script::Hebrew => is_hebrew,
        Script::Ethiopic => is_ethiopic,
        Script::Kat => is_kat,
        Script::Cmn => is_cmn
    ]
}

#[inline(always)]
fn is_cyrillic(ch: char) -> bool {
   match ch {
       '\u{0400}'...'\u{0484}' |
       '\u{0487}'...'\u{052F}' |
       '\u{2DE0}'...'\u{2DFF}' |
       '\u{A640}'...'\u{A69D}' |
       '\u{1D2B}' |
       '\u{1D78}' |
       '\u{A69F}' => true,
       _ => false
   }
}

// https://en.wikipedia.org/wiki/Latin_script_in_Unicode
#[inline(always)]
fn is_latin(ch : char) -> bool {
    match ch {
        'a'...'z' |
        'A'...'Z' |
        '\u{0080}'...'\u{00FF}' |
        '\u{0100}'...'\u{017F}' |
        '\u{0180}'...'\u{024F}' |
        '\u{0250}'...'\u{02AF}' |
        '\u{1D00}'...'\u{1D7F}' |
        '\u{1D80}'...'\u{1DBF}' |
        '\u{1E00}'...'\u{1EFF}' |
        '\u{2100}'...'\u{214F}' |
        '\u{2C60}'...'\u{2C7F}' |
        '\u{A720}'...'\u{A7FF}' |
        '\u{AB30}'...'\u{AB6F}' => true,
        _ => false
    }
}

// Based on https://en.wikipedia.org/wiki/Arabic_script_in_Unicode
#[inline(always)]
fn is_arabic(ch : char) -> bool {
    match ch {
        '\u{0600}'...'\u{06FF}' |
        '\u{0750}'...'\u{07FF}' |
        '\u{08A0}'...'\u{08FF}' |
        '\u{FB50}'...'\u{FDFF}' |
        '\u{FE70}'...'\u{FEFF}' |
        '\u{10E60}'...'\u{10E7F}' |
        '\u{1EE00}'...'\u{1EEFF}' => true,
        _ => false
    }
}

// Based on https://en.wikipedia.org/wiki/Devanagari#Unicode
#[inline(always)]
fn is_devanagari(ch : char) -> bool {
    match ch {
        '\u{0900}'...'\u{097F}' |
        '\u{A8E0}'...'\u{A8FF}' |
        '\u{1CD0}'...'\u{1CFF}' => true,
        _ => false
    }
}

// Based on https://www.key-shortcut.com/en/writing-systems/ethiopian-script/
#[inline(always)]
fn is_ethiopic(ch : char) -> bool {
    match ch {
        '\u{1200}'...'\u{139F}' |
        '\u{2D80}'...'\u{2DDF}' |
        '\u{AB00}'...'\u{AB2F}' => true,
        _ => false
    }
}

// Based on https://en.wikipedia.org/wiki/Hebrew_(Unicode_block)
#[inline(always)]
fn is_hebrew(ch : char) -> bool {
    match ch {
        '\u{0590}'...'\u{05FF}' => true,
        _ => false
    }
}

// Is Georgian char?
#[inline(always)]
fn is_kat(ch : char) -> bool {
   match ch {
       '\u{10A0}'...'\u{10FF}' => true,
       _ => false
   }
}

// TODO: likely not the full set of possible chars..
#[inline(always)]
fn is_cmn(ch : char) -> bool {
    match ch {
        '\u{2E80}'...'\u{2E99}' |
        '\u{2E9B}'...'\u{2EF3}' |
        '\u{2F00}'...'\u{2FD5}' |
        '\u{3005}' |
        '\u{3007}' |
        '\u{3021}'...'\u{3029}' |
        '\u{3038}'...'\u{303B}' |
        '\u{3400}'...'\u{4DB5}' |
        '\u{4E00}'...'\u{9FCC}' |
        '\u{F900}'...'\u{FA6D}' |
        '\u{FA70}'...'\u{FAD9}' => true,
        _ => false
    }
}

#[cfg(test)]
mod tests {
    use super::Script;
    use super::is_cyrillic;
    use super::is_latin;
    use super::is_kat;
    use super::is_ethiopic;
    use super::detect_script;

    #[test]
    fn test_detect_script() {
        assert_eq!(detect_script("1234567890-,;!".to_string()), None);

        // One script
        assert_eq!(detect_script("Hello!".to_string()), Some(Script::Latin));
        assert_eq!(detect_script("Привет всем!".to_string()), Some(Script::Cyrillic));
        assert_eq!(detect_script("ქართული ენა მსოფლიო ".to_string()), Some(Script::Kat));
        assert_eq!(detect_script("県見夜上温国阪題富販".to_string()), Some(Script::Cmn));
        assert_eq!(detect_script(" ككل حوالي 1.6، ومعظم الناس ".to_string()), Some(Script::Arabic));
        assert_eq!(detect_script("हिमालयी वन चिड़िया (जूथेरा सालिमअली) चिड़िया की एक प्रजाति है".to_string()), Some(Script::Devanagari));
        assert_eq!(detect_script("היסטוריה והתפתחות של האלפבית העברי".to_string()), Some(Script::Hebrew));
        assert_eq!(detect_script("የኢትዮጵያ ፌዴራላዊ ዴሞክራሲያዊሪፐብሊክ".to_string()), Some(Script::Ethiopic));

        // Mixed scripts
        assert_eq!(detect_script("Привет! Текст на русском with some English.".to_string()), Some(Script::Cyrillic));
        assert_eq!(detect_script("Russian word любовь means love.".to_string()), Some(Script::Latin));
    }

    #[test]
    fn test_is_latin() {
        assert_eq!(is_latin('z'), true);
        assert_eq!(is_latin('A'), true);
        assert_eq!(is_latin('č'), true);
        assert_eq!(is_latin('š'), true);
        assert_eq!(is_latin('Ĵ'), true);

        assert_eq!(is_latin('ж'), false);
    }

    #[test]
    fn test_is_cyrillic() {
        assert_eq!(is_cyrillic('а'), true);
        assert_eq!(is_cyrillic('Я'), true);
        assert_eq!(is_cyrillic('Ґ'), true);
        assert_eq!(is_cyrillic('ї'), true);
        assert_eq!(is_cyrillic('Ꙕ'), true);

        assert_eq!(is_cyrillic('L'), false);
    }

    #[test]
    fn test_is_ethiopic() {
        assert_eq!(is_ethiopic('ፚ'), true);
        assert_eq!(is_ethiopic('ᎀ'), true);

        assert_eq!(is_ethiopic('а'), false);
        assert_eq!(is_cyrillic('L'), false);
    }

    #[test]
    fn test_is_kat() {
        assert_eq!(is_kat('რ'), true);
        assert_eq!(is_kat('ж'), false);
    }
}
<commit_msg>Adjust detect_script<commit_after>#![allow(dead_code)]
#![allow(unused_assignments)]

#[derive(PartialEq, Eq, Debug, Clone, Copy)]
pub enum Script {
    Latin,
    Cyrillic,
    Arabic,
    Devanagari,
    Ethiopic,
    Hebrew,
    Cmn,
    Kat
}

macro_rules! check_scripts {
    ( $text:ident, $( $script:expr => $is_script:ident ),* ) => {
        {
            let mut max_score = 0;
            let mut result = None;
            let mut current_score = 0;
            let half = $text.len() / 2;

            $(
                current_score = 0;
                for ch in $text.chars() {
                    if $is_script(ch) { current_score += 1; }
                }
                if current_score > half {
                    return Some($script);
                }
                if current_score > max_score {
                    max_score = current_score;
                    result = Some($script);
                }
            )*
            result
        }
    };
}

pub fn detect_script(text : String) -> Option<Script> {
    check_scripts!(
        text,
        Script::Cyrillic   => is_cyrillic,
        Script::Latin      => is_latin,
        Script::Arabic     => is_arabic,
        Script::Devanagari => is_devanagari,
        Script::Hebrew     => is_hebrew,
        Script::Ethiopic   => is_ethiopic,
        Script::Kat        => is_kat,
        Script::Cmn        => is_cmn
    )
}

#[inline(always)]
fn is_cyrillic(ch: char) -> bool {
   match ch {
       '\u{0400}'...'\u{0484}' |
       '\u{0487}'...'\u{052F}' |
       '\u{2DE0}'...'\u{2DFF}' |
       '\u{A640}'...'\u{A69D}' |
       '\u{1D2B}' |
       '\u{1D78}' |
       '\u{A69F}' => true,
       _ => false
   }
}

// https://en.wikipedia.org/wiki/Latin_script_in_Unicode
#[inline(always)]
fn is_latin(ch : char) -> bool {
    match ch {
        'a'...'z' |
        'A'...'Z' |
        '\u{0080}'...'\u{00FF}' |
        '\u{0100}'...'\u{017F}' |
        '\u{0180}'...'\u{024F}' |
        '\u{0250}'...'\u{02AF}' |
        '\u{1D00}'...'\u{1D7F}' |
        '\u{1D80}'...'\u{1DBF}' |
        '\u{1E00}'...'\u{1EFF}' |
        '\u{2100}'...'\u{214F}' |
        '\u{2C60}'...'\u{2C7F}' |
        '\u{A720}'...'\u{A7FF}' |
        '\u{AB30}'...'\u{AB6F}' => true,
        _ => false
    }
}

// Based on https://en.wikipedia.org/wiki/Arabic_script_in_Unicode
#[inline(always)]
fn is_arabic(ch : char) -> bool {
    match ch {
        '\u{0600}'...'\u{06FF}' |
        '\u{0750}'...'\u{07FF}' |
        '\u{08A0}'...'\u{08FF}' |
        '\u{FB50}'...'\u{FDFF}' |
        '\u{FE70}'...'\u{FEFF}' |
        '\u{10E60}'...'\u{10E7F}' |
        '\u{1EE00}'...'\u{1EEFF}' => true,
        _ => false
    }
}

// Based on https://en.wikipedia.org/wiki/Devanagari#Unicode
#[inline(always)]
fn is_devanagari(ch : char) -> bool {
    match ch {
        '\u{0900}'...'\u{097F}' |
        '\u{A8E0}'...'\u{A8FF}' |
        '\u{1CD0}'...'\u{1CFF}' => true,
        _ => false
    }
}

// Based on https://www.key-shortcut.com/en/writing-systems/ethiopian-script/
#[inline(always)]
fn is_ethiopic(ch : char) -> bool {
    match ch {
        '\u{1200}'...'\u{139F}' |
        '\u{2D80}'...'\u{2DDF}' |
        '\u{AB00}'...'\u{AB2F}' => true,
        _ => false
    }
}

// Based on https://en.wikipedia.org/wiki/Hebrew_(Unicode_block)
#[inline(always)]
fn is_hebrew(ch : char) -> bool {
    match ch {
        '\u{0590}'...'\u{05FF}' => true,
        _ => false
    }
}

// Is Georgian char?
#[inline(always)]
fn is_kat(ch : char) -> bool {
   match ch {
       '\u{10A0}'...'\u{10FF}' => true,
       _ => false
   }
}

// TODO: likely not the full set of possible chars..
#[inline(always)]
fn is_cmn(ch : char) -> bool {
    match ch {
        '\u{2E80}'...'\u{2E99}' |
        '\u{2E9B}'...'\u{2EF3}' |
        '\u{2F00}'...'\u{2FD5}' |
        '\u{3005}' |
        '\u{3007}' |
        '\u{3021}'...'\u{3029}' |
        '\u{3038}'...'\u{303B}' |
        '\u{3400}'...'\u{4DB5}' |
        '\u{4E00}'...'\u{9FCC}' |
        '\u{F900}'...'\u{FA6D}' |
        '\u{FA70}'...'\u{FAD9}' => true,
        _ => false
    }
}

#[cfg(test)]
mod tests {
    use super::Script;
    use super::is_cyrillic;
    use super::is_latin;
    use super::is_kat;
    use super::is_ethiopic;
    use super::detect_script;

    #[test]
    fn test_detect_script() {
        assert_eq!(detect_script("1234567890-,;!".to_string()), None);

        // One script
        assert_eq!(detect_script("Hello!".to_string()), Some(Script::Latin));
        assert_eq!(detect_script("Привет всем!".to_string()), Some(Script::Cyrillic));
        assert_eq!(detect_script("ქართული ენა მსოფლიო ".to_string()), Some(Script::Kat));
        assert_eq!(detect_script("県見夜上温国阪題富販".to_string()), Some(Script::Cmn));
        assert_eq!(detect_script(" ككل حوالي 1.6، ومعظم الناس ".to_string()), Some(Script::Arabic));
        assert_eq!(detect_script("हिमालयी वन चिड़िया (जूथेरा सालिमअली) चिड़िया की एक प्रजाति है".to_string()), Some(Script::Devanagari));
        assert_eq!(detect_script("היסטוריה והתפתחות של האלפבית העברי".to_string()), Some(Script::Hebrew));
        assert_eq!(detect_script("የኢትዮጵያ ፌዴራላዊ ዴሞክራሲያዊሪፐብሊክ".to_string()), Some(Script::Ethiopic));

        // Mixed scripts
        assert_eq!(detect_script("Привет! Текст на русском with some English.".to_string()), Some(Script::Cyrillic));
        assert_eq!(detect_script("Russian word любовь means love.".to_string()), Some(Script::Latin));
    }

    #[test]
    fn test_is_latin() {
        assert_eq!(is_latin('z'), true);
        assert_eq!(is_latin('A'), true);
        assert_eq!(is_latin('č'), true);
        assert_eq!(is_latin('š'), true);
        assert_eq!(is_latin('Ĵ'), true);

        assert_eq!(is_latin('ж'), false);
    }

    #[test]
    fn test_is_cyrillic() {
        assert_eq!(is_cyrillic('а'), true);
        assert_eq!(is_cyrillic('Я'), true);
        assert_eq!(is_cyrillic('Ґ'), true);
        assert_eq!(is_cyrillic('ї'), true);
        assert_eq!(is_cyrillic('Ꙕ'), true);

        assert_eq!(is_cyrillic('L'), false);
    }

    #[test]
    fn test_is_ethiopic() {
        assert_eq!(is_ethiopic('ፚ'), true);
        assert_eq!(is_ethiopic('ᎀ'), true);

        assert_eq!(is_ethiopic('а'), false);
        assert_eq!(is_cyrillic('L'), false);
    }

    #[test]
    fn test_is_kat() {
        assert_eq!(is_kat('რ'), true);
        assert_eq!(is_kat('ж'), false);
    }
}
<|endoftext|>


<commit_before>use configuration::Configuration;
use std::slice::SliceExt;
use std::cmp::Ordering;
use score::Score;

struct Search {
    config: Configuration,
    current: uint,
    max_index: uint,
    query: String,
    selection: String,
    result: Vec<String>,
}

impl Search {
    fn blank(config: Configuration) -> Search {
        let max_index = config.visible_limit;
        let query = config.initial_search.clone();

        Search::new(config, query, max_index, 0)
    }

    fn new(config: Configuration, query: String, max_index: uint, index: uint) -> Search {
        let mut result = config.choices.clone();
        result = Search::filter(query.as_slice(), result);

        Search { selection: result[index].to_string(),
                 config: config,
                 query: query,
                 max_index: max_index,
                 result: result,
                 current: index }
    }

    fn new_for_index(self, index: uint) -> Search {
        Search::new(self.config, self.query, self.max_index, index)
    }

    fn filter(query: &str, choices: Vec<String>) -> Vec<String> {
        let mut filtered = choices.iter().filter_map( |choice| {
            let quality = Score::score(choice.as_slice(), query);
            if quality > 0.0 {
                Some((quality, choice.to_string()))
            } else {
                None
            }
        }).collect::<Vec<(f32, String)>>();

        filtered.sort_by( |&(quality_a, _), &(quality_b, _)| {
            quality_a.partial_cmp(&quality_b).unwrap_or(Ordering::Equal).reverse()
        });

        filtered.iter().map( |&(_, ref choice)| choice.to_string() ).collect::<Vec<String>>()
    }

    fn down(self) -> Search {
        let next_index = self.next_index();
        self.new_for_index(next_index)
    }

    fn up(self) -> Search {
        let next_index = self.prev_index();
        self.new_for_index(next_index)
    }

    fn append_to_search(self, input: String) -> Search {
        let mut new_query = self.query;
        new_query.push_str(input.as_slice());
        Search::new(self.config, new_query, self.max_index, self.current)
    }

    fn next_index(&self) -> uint {
        let next_index = self.current + 1;

        if next_index >= self.max_index { 0 } else { next_index }
    }

    fn prev_index(&self) -> uint {
        if self.current == 0 { self.max_index - 1 } else  { self.current - 1 }
    }
}

#[cfg(test)]

#[test]
fn it_selects_the_first_choice_by_default() {
    let input =  one_two_three();

    let config = Configuration::from_inputs(input, None, None);
    let search = Search::blank(config);

    assert_eq!(search.selection, "one");
}

fn one_two_three() -> Vec<String> {
    vec!["one".to_string(),
         "two".to_string(),
         "three".to_string()]
}

#[test]
fn it_selets_the_second_when_down_is_called() {
    let input =  one_two_three();

    let config = Configuration::from_inputs(input, None, None);
    let search = Search::blank(config);

    assert_eq!(search.down().selection, "two");
}

#[test]
fn it_loop_around_when_reaching_end_of_list() {
    let input =  one_two_three();

    let config = Configuration::from_inputs(input, None, None);
    let search = Search::blank(config);

    assert_eq!(search.down().down().down().down().selection, "two");
}

#[test]
fn it_loop_around_when_reaching_top_of_list() {
    let input =  one_two_three();

    let config = Configuration::from_inputs(input, None, None);
    let search = Search::blank(config);

    assert_eq!(search.up().up().selection, "two");
}

#[test]
fn it_loop_around_when_reaching_visible_limit() {
    let input =  one_two_three();

    let config = Configuration::from_inputs(input, None, Some(2));
    let search = Search::blank(config);

    assert_eq!(search.down().down().down().selection, "two");
}

#[test]
fn it_moves_down_the_filtered_search_results() {
    let input =  one_two_three();

    let config = Configuration::from_inputs(input, None, None);
    let search = Search::blank(config);
    assert_eq!(search.append_to_search("t".to_string()).down().selection, "three");
}
<commit_msg>minor refactoring.<commit_after>use configuration::Configuration;
use std::slice::SliceExt;
use std::cmp::Ordering;
use score::Score;

struct Search {
    config: Configuration,
    current: uint,
    max_index: uint,
    query: String,
    selection: String,
    result: Vec<String>,
}

impl Search {
    fn blank(config: Configuration) -> Search {
        let max_index = config.visible_limit;
        let query = config.initial_search.clone();

        Search::new(config, query, max_index, 0)
    }

    fn new(config: Configuration, query: String, max_index: uint, index: uint) -> Search {
        let result = Search::filter(query.as_slice(), config.choices.clone());

        Search { config: config,
                 current: index,
                 max_index: max_index,
                 query: query,
                 selection: result[index].to_string(),
                 result: result }
    }

    fn new_for_index(self, index: uint) -> Search {
        Search::new(self.config, self.query, self.max_index, index)
    }

    fn filter(query: &str, choices: Vec<String>) -> Vec<String> {
        let mut filtered = choices.iter().filter_map( |choice| {
            let quality = Score::score(choice.as_slice(), query);
            if quality > 0.0 {
                Some((quality, choice.to_string()))
            } else {
                None
            }
        }).collect::<Vec<(f32, String)>>();

        filtered.sort_by( |&(quality_a, _), &(quality_b, _)| {
            quality_a.partial_cmp(&quality_b).unwrap_or(Ordering::Equal).reverse()
        });

        filtered.iter().map( |&(_, ref choice)| choice.to_string() ).collect::<Vec<String>>()
    }

    fn down(self) -> Search {
        let next_index = self.next_index();
        self.new_for_index(next_index)
    }

    fn up(self) -> Search {
        let next_index = self.prev_index();
        self.new_for_index(next_index)
    }

    fn append_to_search(self, input: String) -> Search {
        let mut new_query = self.query;
        new_query.push_str(input.as_slice());
        Search::new(self.config, new_query, self.max_index, self.current)
    }

    fn next_index(&self) -> uint {
        let next_index = self.current + 1;

        if next_index >= self.max_index { 0 } else { next_index }
    }

    fn prev_index(&self) -> uint {
        if self.current == 0 { self.max_index - 1 } else  { self.current - 1 }
    }
}

#[cfg(test)]

#[test]
fn it_selects_the_first_choice_by_default() {
    let input =  one_two_three();

    let config = Configuration::from_inputs(input, None, None);
    let search = Search::blank(config);

    assert_eq!(search.selection, "one");
}

fn one_two_three() -> Vec<String> {
    vec!["one".to_string(),
         "two".to_string(),
         "three".to_string()]
}

#[test]
fn it_selets_the_second_when_down_is_called() {
    let input =  one_two_three();

    let config = Configuration::from_inputs(input, None, None);
    let search = Search::blank(config);

    assert_eq!(search.down().selection, "two");
}

#[test]
fn it_loop_around_when_reaching_end_of_list() {
    let input =  one_two_three();

    let config = Configuration::from_inputs(input, None, None);
    let search = Search::blank(config);

    assert_eq!(search.down().down().down().down().selection, "two");
}

#[test]
fn it_loop_around_when_reaching_top_of_list() {
    let input =  one_two_three();

    let config = Configuration::from_inputs(input, None, None);
    let search = Search::blank(config);

    assert_eq!(search.up().up().selection, "two");
}

#[test]
fn it_loop_around_when_reaching_visible_limit() {
    let input =  one_two_three();

    let config = Configuration::from_inputs(input, None, Some(2));
    let search = Search::blank(config);

    assert_eq!(search.down().down().down().selection, "two");
}

#[test]
fn it_moves_down_the_filtered_search_results() {
    let input =  one_two_three();

    let config = Configuration::from_inputs(input, None, None);
    let search = Search::blank(config);
    assert_eq!(search.append_to_search("t".to_string()).down().selection, "three");
}

#[test]
fn it_moves_down_the_filtered_search_results_twice() {
    let input =  one_two_three();

    let config = Configuration::from_inputs(input, None, None);
    let search = Search::blank(config);
    assert_eq!(search.append_to_search("t".to_string())
               .append_to_search("w".to_string()).selection, "two");
}
<|endoftext|>


<commit_before>use std::io::net::tcp::{TcpListener, TcpAcceptor, TcpStream};
use std::io::{Listener, Acceptor, Reader, Writer};
use super::stream::{SmtpStream};
use super::mailbox::{Mailbox};
use super::{utils};
use std::sync::{Arc};

type HandlerDescription<S> = (
    // The prefix in the command sent by the client.
    String,
    // The list of allowed states for this command.
    Vec<SmtpTransactionState>,
    // The handler function to call for this command.
    HandlerFunction<S>
);

type HandlerFunction<S> = fn(&mut SmtpStream<S>,
                             &mut SmtpTransaction,
                             &str) -> Result<(), ()>;

/// Represents an SMTP server which handles client transactions with any kind of stream.
///
/// This is useful for testing purposes as we can test the server from a plain text file. It
/// should not be used for other purposes directly. Use `SmtpServer` instead.
pub struct SmtpServer<S: 'static+Writer+Reader, A: Acceptor<S>> {
    // Underlying acceptor that allows accepting client connections to handle them.
    acceptor: A
}

/// Represents an error during creation of an SMTP server.
#[deriving(Show)]
pub enum SmtpServerError {
    /// The system call `bind` failed.
    BindFailed,
    /// The system call `listen` failed.
    ListenFailed
}

#[test]
fn test_smtp_server_error() {
    fail!();
}

/// Represents the current state of an SMTP transaction.
///
/// This is useful for checking if an incoming SMTP command is allowed at any given moment
/// during an SMTP transaction.
#[deriving(PartialEq, Eq, Clone)]
pub enum SmtpTransactionState {
    Init,
    Helo,
    Mail,
    Rcpt,
    Data
}

#[test]
fn test_smtp_transaction_state() {
    fail!();
}

/// Represents an SMTP transaction.
pub struct SmtpTransaction {
    /// Domain name passed via `HELO`/`EHLO`.
    pub domain: Option<String>,
    /// A vector of recipients' email addresses.
    pub to: Vec<Mailbox>,
    /// The email address of the sender.
    pub from: Option<Mailbox>,
    /// The body of the email.
    pub data: Option<String>,
    /// The current state of the transaction.
    pub state: SmtpTransactionState
}

impl SmtpTransaction {
    /// Creates a new transaction.
    pub fn new() -> SmtpTransaction {
        SmtpTransaction {
            domain: None,
            to: Vec::new(),
            from: None,
            data: None,
            state: Init
        }
    }

    /// Resets the `to`, `from` and `data` fields, as well as the `state` of the transaction.
    ///
    /// This is used when a transaction ends and when `RSET` is sent by the client.
    pub fn reset(&mut self) {
        self.to.clear();
        self.from = None;
        self.data = None;
        if self.state != Init {
            self.state = Helo;
        }
    }
}

#[test]
fn test_smtp_transaction_new() {
    fail!();
}

#[test]
fn test_smtp_transaction_reset() {
    fail!();
}

impl SmtpServer<TcpStream, TcpAcceptor> {
    /// Creates a new SMTP server that listens on `0.0.0.0:2525`.
    pub fn new() -> Result<SmtpServer<TcpStream, TcpAcceptor>, SmtpServerError> {
        let listener = TcpListener::bind("0.0.0.0", 2525).unwrap();
        let acceptor = listener.listen().unwrap();
        SmtpServer::new_from_acceptor(acceptor)
    }
}

impl<S: Writer+Reader+Send, A: Acceptor<S>> SmtpServer<S, A> {
    /// Creates a new SMTP server from an `Acceptor` implementor. Useful for testing.
    pub fn new_from_acceptor(acceptor: A) -> Result<SmtpServer<S, A>, SmtpServerError> {
        Ok(SmtpServer {
            acceptor: acceptor
        })
    }

    fn handlers<S: Writer+Reader>(&self) -> Vec<HandlerDescription<S>> {
        let all = &[Init, Helo, Mail, Rcpt, Data];
        let mut handlers: Vec<HandlerDescription<S>> = Vec::new();
        handlers.push(("HELO ".into_string(),[Init].into_vec(), handle_command_helo));
        handlers.push(("EHLO".into_string(), [Init].into_vec(), handle_command_helo));
        handlers.push(("MAIL FROM:".into_string(), [Helo].into_vec(), handle_command_mail));
        handlers.push(("RCPT TO:".into_string(), [Mail, Rcpt].into_vec(), handle_command_rcpt));
        handlers.push(("DATA".into_string(), [Rcpt].into_vec(), handle_command_data));
        handlers.push(("RSET".into_string(), all.into_vec(), handle_command_rset));
        handlers.push(("VRFY ".into_string(), all.into_vec(), handle_command_vrfy));
        handlers.push(("EXPN ".into_string(), all.into_vec(), handle_command_expn));
        handlers.push(("HELP".into_string(), all.into_vec(), handle_command_help));
        handlers.push(("NOOP".into_string(), all.into_vec(), handle_command_noop));
        handlers.push(("QUIT".into_string(), all.into_vec(), handle_command_quit));
        handlers
    }

    /// Run the SMTP server.
    pub fn run(&mut self) {
        // Since cea
        let handlers = Arc::new(self.handlers());
        for mut stream_res in self.acceptor.incoming() {
            let local_handlers = handlers.clone();
            spawn(proc() {
                // TODO: is there a better way to handle an error here?
                let mut stream = SmtpStream::new(stream_res.unwrap());
                let mut transaction = SmtpTransaction::new();

                // Send the opening welcome message.
                stream.write_line("220 rustastic.org").unwrap();

                // Forever, looooop over command lines and handle them.
                'main_loop: loop {
                    // Find the right handler.
                    // TODO: check the return value and return appropriate error message,
                    // ie "500 Command line too long".
                    let line = stream.read_line().unwrap();
                    for h in local_handlers.iter() {
                        // Check that the begining of the command matches an existing SMTP
                        // command. This could be something like "HELO " or "RCPT TO:".
                        if line.as_slice().starts_with(h.ref0().as_slice()) {
                            if h.ref1().contains(&transaction.state) {
                                // We're good to go!
                                (*h.ref2())(
                                    &mut stream,
                                    &mut transaction,
                                    line.as_slice().slice_from((*h.ref0()).len())
                                ).unwrap(); // TODO: avoid unwrap here.
                                continue 'main_loop;
                            } else {
                                // Bad sequence of commands.
                                stream.write_line("503 Bad sequence of commands").unwrap();
                                continue 'main_loop;
                            }
                        }
                    }
                    // No valid command was given.
                    stream.write_line("500 Command unrecognized").unwrap();
                }
            });
        }
    }
}

#[test]
fn test_smtp_server_new() {
    fail!();
}

#[test]
fn test_smtp_server_new_from_acceptor() {
    fail!();
}

#[test]
fn test_smtp_server_handlers() {
    fail!();
}

#[test]
fn test_smtp_server_run() {
    fail!();
}

fn handle_command_helo<S: Writer+Reader>(stream: &mut SmtpStream<S>,
                       transaction: &mut SmtpTransaction,
                       line: &str) -> Result<(), ()> {
    if line.len() == 0 || utils::get_domain_len(line) != line.len() {
        stream.write_line("501 Domain name is invalid").unwrap();
        Ok(())
    } else {
        transaction.domain = Some(line.into_string());
        transaction.state = Helo;
        stream.write_line("250 OK").unwrap();
        Ok(())
    }
}

#[test]
fn test_command_helo() {
    fail!();
}

fn handle_command_mail<S: Writer+Reader>(stream: &mut SmtpStream<S>,
                       transaction: &mut SmtpTransaction,
                       line: &str) -> Result<(), ()> {
    if line.char_at(0) != '<' || line.char_at(line.len() - 1) != '>' {
        stream.write_line("501 Email address invalid, must start with < and end with >").unwrap();
        Ok(())
    } else {
        let mailbox_res = Mailbox::parse(line.slice(1, line.len() - 1));
        match mailbox_res {
            Err(err) => {
                stream.write_line(format!("501 Email address invalid: {}", err).as_slice())
                    .unwrap();
            },
            Ok(mailbox) => {
                transaction.from = Some(mailbox);
                transaction.state = Mail;
                stream.write_line("250 OK").unwrap();
            }
        }
        Ok(())
    }
}

#[test]
fn test_command_mail() {
    fail!();
}

fn handle_command_rcpt<S: Writer+Reader>(stream: &mut SmtpStream<S>,
                       transaction: &mut SmtpTransaction,
                       line: &str) -> Result<(), ()> {
    if transaction.to.len() >= 100 {
        stream.write_line("452 Too many recipients").unwrap();
        Ok(())
    } else if line.char_at(0) != '<' || line.char_at(line.len() - 1) != '>' {
        stream.write_line("501 Email address invalid, must start with < and end with >").unwrap();
        Ok(())
    } else {
        let mailbox_res = Mailbox::parse(line.slice(1, line.len() - 1));
        match mailbox_res {
            Err(err) => {
                stream.write_line(format!("501 Email address invalid: {}", err).as_slice())
                    .unwrap();
            },
            Ok(mailbox) => {
                transaction.to.push(mailbox);
                transaction.state = Rcpt;
                stream.write_line("250 OK").unwrap();
            }
        }
        Ok(())
    }
}

#[test]
fn test_command_rcpt() {
    fail!();
}

fn handle_command_data<S: Writer+Reader>(stream: &mut SmtpStream<S>,
                       transaction: &mut SmtpTransaction,
                       line: &str) -> Result<(), ()> {
    if line.len() != 0 {
        stream.write_line("501 No arguments allowed").unwrap();
    } else {
        stream.write_line("354 Start mail input; end with <CRLF>.<CRLF>").unwrap();
        transaction.data = Some(stream.read_data().unwrap());
        transaction.state = Data;
        // ... (here is where we'd handle the finished transaction)
        transaction.reset();
        stream.write_line("250 OK").unwrap();
    }
    Ok(())
}

#[test]
fn test_command_data() {
    fail!();
}

fn handle_command_rset<S: Writer+Reader>(stream: &mut SmtpStream<S>,
                       transaction: &mut SmtpTransaction,
                       line: &str) -> Result<(), ()> {
    if line.len() != 0 {
        stream.write_line("501 No arguments allowed").unwrap();
    } else {
        transaction.reset();
        stream.write_line("250 OK").unwrap();
    }
    Ok(())
}

#[test]
fn test_command_rset() {
    fail!();
}

#[allow(unused_variable)]
fn handle_command_vrfy<S: Writer+Reader>(stream: &mut SmtpStream<S>,
                       transaction: &mut SmtpTransaction,
                       line: &str) -> Result<(), ()> {
    stream.write_line("252 Cannot VRFY user").unwrap();
    Ok(())
}

#[test]
fn test_command_vrfy() {
    fail!();
}

#[allow(unused_variable)]
fn handle_command_expn<S: Writer+Reader>(stream: &mut SmtpStream<S>,
                       transaction: &mut SmtpTransaction,
                       line: &str) -> Result<(), ()> {
    stream.write_line("252 Cannot EXPN mailing list").unwrap();
    Ok(())
}

#[test]
fn test_command_expn() {
    fail!();
}

#[allow(unused_variable)]
fn handle_command_help<S: Writer+Reader>(stream: &mut SmtpStream<S>,
                       transaction: &mut SmtpTransaction,
                       line: &str) -> Result<(), ()> {
    if line.len() == 0 || line.char_at(0) == ' ' {
        stream.write_line("502 Command not implemented").unwrap();
    } else {
        stream.write_line("500 Command unrecognized").unwrap();
    }
    Ok(())
}

#[test]
fn test_command_help() {
    fail!();
}

#[allow(unused_variable)]
fn handle_command_noop<S: Writer+Reader>(stream: &mut SmtpStream<S>,
                       transaction: &mut SmtpTransaction,
                       line: &str) -> Result<(), ()> {
    if line.len() == 0 || line.char_at(0) == ' ' {
        stream.write_line("250 OK").unwrap();
    } else {
        stream.write_line("500 Command unrecognized").unwrap();
    }
    Ok(())
}

#[test]
fn test_command_noop() {
    fail!();
}

#[allow(unused_variable)]
fn handle_command_quit<S: Writer+Reader>(stream: &mut SmtpStream<S>,
                       transaction: &mut SmtpTransaction,
                       line: &str) -> Result<(), ()> {
    stream.write_line("221 rustastic.org").unwrap();
    Err(())
}

#[test]
fn test_command_quit() {
    fail!();
}
<commit_msg>remove missing server tests temporarily while working on configuration API<commit_after>use std::io::net::tcp::{TcpListener, TcpAcceptor, TcpStream};
use std::io::{Listener, Acceptor, Reader, Writer};
use super::stream::{SmtpStream};
use super::mailbox::{Mailbox};
use super::{utils};
use std::sync::{Arc};

type HandlerDescription<S> = (
    // The prefix in the command sent by the client.
    String,
    // The list of allowed states for this command.
    Vec<SmtpTransactionState>,
    // The handler function to call for this command.
    HandlerFunction<S>
);

type HandlerFunction<S> = fn(&mut SmtpStream<S>,
                             &mut SmtpTransaction,
                             &str) -> Result<(), ()>;

/// Represents an SMTP server which handles client transactions with any kind of stream.
///
/// This is useful for testing purposes as we can test the server from a plain text file. It
/// should not be used for other purposes directly. Use `SmtpServer` instead.
pub struct SmtpServer<S: 'static+Writer+Reader, A: Acceptor<S>> {
    // Underlying acceptor that allows accepting client connections to handle them.
    acceptor: A
}

/// Represents an error during creation of an SMTP server.
#[deriving(Show)]
pub enum SmtpServerError {
    /// The system call `bind` failed.
    BindFailed,
    /// The system call `listen` failed.
    ListenFailed
}

#[test]
fn test_smtp_server_error() {
    // fail!();
}

/// Represents the current state of an SMTP transaction.
///
/// This is useful for checking if an incoming SMTP command is allowed at any given moment
/// during an SMTP transaction.
#[deriving(PartialEq, Eq, Clone)]
pub enum SmtpTransactionState {
    Init,
    Helo,
    Mail,
    Rcpt,
    Data
}

#[test]
fn test_smtp_transaction_state() {
    // fail!();
}

/// Represents an SMTP transaction.
pub struct SmtpTransaction {
    /// Domain name passed via `HELO`/`EHLO`.
    pub domain: Option<String>,
    /// A vector of recipients' email addresses.
    pub to: Vec<Mailbox>,
    /// The email address of the sender.
    pub from: Option<Mailbox>,
    /// The body of the email.
    pub data: Option<String>,
    /// The current state of the transaction.
    pub state: SmtpTransactionState
}

impl SmtpTransaction {
    /// Creates a new transaction.
    pub fn new() -> SmtpTransaction {
        SmtpTransaction {
            domain: None,
            to: Vec::new(),
            from: None,
            data: None,
            state: Init
        }
    }

    /// Resets the `to`, `from` and `data` fields, as well as the `state` of the transaction.
    ///
    /// This is used when a transaction ends and when `RSET` is sent by the client.
    pub fn reset(&mut self) {
        self.to.clear();
        self.from = None;
        self.data = None;
        if self.state != Init {
            self.state = Helo;
        }
    }
}

#[test]
fn test_smtp_transaction_new() {
    // fail!();
}

#[test]
fn test_smtp_transaction_reset() {
    // fail!();
}

impl SmtpServer<TcpStream, TcpAcceptor> {
    /// Creates a new SMTP server that listens on `0.0.0.0:2525`.
    pub fn new() -> Result<SmtpServer<TcpStream, TcpAcceptor>, SmtpServerError> {
        let listener = TcpListener::bind("0.0.0.0", 2525).unwrap();
        let acceptor = listener.listen().unwrap();
        SmtpServer::new_from_acceptor(acceptor)
    }
}

impl<S: Writer+Reader+Send, A: Acceptor<S>> SmtpServer<S, A> {
    /// Creates a new SMTP server from an `Acceptor` implementor. Useful for testing.
    pub fn new_from_acceptor(acceptor: A) -> Result<SmtpServer<S, A>, SmtpServerError> {
        Ok(SmtpServer {
            acceptor: acceptor
        })
    }

    fn handlers<S: Writer+Reader>(&self) -> Vec<HandlerDescription<S>> {
        let all = &[Init, Helo, Mail, Rcpt, Data];
        let mut handlers: Vec<HandlerDescription<S>> = Vec::new();
        handlers.push(("HELO ".into_string(),[Init].into_vec(), handle_command_helo));
        handlers.push(("EHLO".into_string(), [Init].into_vec(), handle_command_helo));
        handlers.push(("MAIL FROM:".into_string(), [Helo].into_vec(), handle_command_mail));
        handlers.push(("RCPT TO:".into_string(), [Mail, Rcpt].into_vec(), handle_command_rcpt));
        handlers.push(("DATA".into_string(), [Rcpt].into_vec(), handle_command_data));
        handlers.push(("RSET".into_string(), all.into_vec(), handle_command_rset));
        handlers.push(("VRFY ".into_string(), all.into_vec(), handle_command_vrfy));
        handlers.push(("EXPN ".into_string(), all.into_vec(), handle_command_expn));
        handlers.push(("HELP".into_string(), all.into_vec(), handle_command_help));
        handlers.push(("NOOP".into_string(), all.into_vec(), handle_command_noop));
        handlers.push(("QUIT".into_string(), all.into_vec(), handle_command_quit));
        handlers
    }

    /// Run the SMTP server.
    pub fn run(&mut self) {
        // Since cea
        let handlers = Arc::new(self.handlers());
        for mut stream_res in self.acceptor.incoming() {
            let local_handlers = handlers.clone();
            spawn(proc() {
                // TODO: is there a better way to handle an error here?
                let mut stream = SmtpStream::new(stream_res.unwrap());
                let mut transaction = SmtpTransaction::new();

                // Send the opening welcome message.
                stream.write_line("220 rustastic.org").unwrap();

                // Forever, looooop over command lines and handle them.
                'main_loop: loop {
                    // Find the right handler.
                    // TODO: check the return value and return appropriate error message,
                    // ie "500 Command line too long".
                    let line = stream.read_line().unwrap();
                    for h in local_handlers.iter() {
                        // Check that the begining of the command matches an existing SMTP
                        // command. This could be something like "HELO " or "RCPT TO:".
                        if line.as_slice().starts_with(h.ref0().as_slice()) {
                            if h.ref1().contains(&transaction.state) {
                                // We're good to go!
                                (*h.ref2())(
                                    &mut stream,
                                    &mut transaction,
                                    line.as_slice().slice_from((*h.ref0()).len())
                                ).unwrap(); // TODO: avoid unwrap here.
                                continue 'main_loop;
                            } else {
                                // Bad sequence of commands.
                                stream.write_line("503 Bad sequence of commands").unwrap();
                                continue 'main_loop;
                            }
                        }
                    }
                    // No valid command was given.
                    stream.write_line("500 Command unrecognized").unwrap();
                }
            });
        }
    }
}

#[test]
fn test_smtp_server_new() {
    // fail!();
}

#[test]
fn test_smtp_server_new_from_acceptor() {
    // fail!();
}

#[test]
fn test_smtp_server_handlers() {
    // fail!();
}

#[test]
fn test_smtp_server_run() {
    // fail!();
}

fn handle_command_helo<S: Writer+Reader>(stream: &mut SmtpStream<S>,
                       transaction: &mut SmtpTransaction,
                       line: &str) -> Result<(), ()> {
    if line.len() == 0 || utils::get_domain_len(line) != line.len() {
        stream.write_line("501 Domain name is invalid").unwrap();
        Ok(())
    } else {
        transaction.domain = Some(line.into_string());
        transaction.state = Helo;
        stream.write_line("250 OK").unwrap();
        Ok(())
    }
}

#[test]
fn test_command_helo() {
    // fail!();
}

fn handle_command_mail<S: Writer+Reader>(stream: &mut SmtpStream<S>,
                       transaction: &mut SmtpTransaction,
                       line: &str) -> Result<(), ()> {
    if line.char_at(0) != '<' || line.char_at(line.len() - 1) != '>' {
        stream.write_line("501 Email address invalid, must start with < and end with >").unwrap();
        Ok(())
    } else {
        let mailbox_res = Mailbox::parse(line.slice(1, line.len() - 1));
        match mailbox_res {
            Err(err) => {
                stream.write_line(format!("501 Email address invalid: {}", err).as_slice())
                    .unwrap();
            },
            Ok(mailbox) => {
                transaction.from = Some(mailbox);
                transaction.state = Mail;
                stream.write_line("250 OK").unwrap();
            }
        }
        Ok(())
    }
}

#[test]
fn test_command_mail() {
    // fail!();
}

fn handle_command_rcpt<S: Writer+Reader>(stream: &mut SmtpStream<S>,
                       transaction: &mut SmtpTransaction,
                       line: &str) -> Result<(), ()> {
    if transaction.to.len() >= 100 {
        stream.write_line("452 Too many recipients").unwrap();
        Ok(())
    } else if line.char_at(0) != '<' || line.char_at(line.len() - 1) != '>' {
        stream.write_line("501 Email address invalid, must start with < and end with >").unwrap();
        Ok(())
    } else {
        let mailbox_res = Mailbox::parse(line.slice(1, line.len() - 1));
        match mailbox_res {
            Err(err) => {
                stream.write_line(format!("501 Email address invalid: {}", err).as_slice())
                    .unwrap();
            },
            Ok(mailbox) => {
                transaction.to.push(mailbox);
                transaction.state = Rcpt;
                stream.write_line("250 OK").unwrap();
            }
        }
        Ok(())
    }
}

#[test]
fn test_command_rcpt() {
    // fail!();
}

fn handle_command_data<S: Writer+Reader>(stream: &mut SmtpStream<S>,
                       transaction: &mut SmtpTransaction,
                       line: &str) -> Result<(), ()> {
    if line.len() != 0 {
        stream.write_line("501 No arguments allowed").unwrap();
    } else {
        stream.write_line("354 Start mail input; end with <CRLF>.<CRLF>").unwrap();
        transaction.data = Some(stream.read_data().unwrap());
        transaction.state = Data;
        // ... (here is where we'd handle the finished transaction)
        transaction.reset();
        stream.write_line("250 OK").unwrap();
    }
    Ok(())
}

#[test]
fn test_command_data() {
    // fail!();
}

fn handle_command_rset<S: Writer+Reader>(stream: &mut SmtpStream<S>,
                       transaction: &mut SmtpTransaction,
                       line: &str) -> Result<(), ()> {
    if line.len() != 0 {
        stream.write_line("501 No arguments allowed").unwrap();
    } else {
        transaction.reset();
        stream.write_line("250 OK").unwrap();
    }
    Ok(())
}

#[test]
fn test_command_rset() {
    // fail!();
}

#[allow(unused_variable)]
fn handle_command_vrfy<S: Writer+Reader>(stream: &mut SmtpStream<S>,
                       transaction: &mut SmtpTransaction,
                       line: &str) -> Result<(), ()> {
    stream.write_line("252 Cannot VRFY user").unwrap();
    Ok(())
}

#[test]
fn test_command_vrfy() {
    // fail!();
}

#[allow(unused_variable)]
fn handle_command_expn<S: Writer+Reader>(stream: &mut SmtpStream<S>,
                       transaction: &mut SmtpTransaction,
                       line: &str) -> Result<(), ()> {
    stream.write_line("252 Cannot EXPN mailing list").unwrap();
    Ok(())
}

#[test]
fn test_command_expn() {
    // fail!();
}

#[allow(unused_variable)]
fn handle_command_help<S: Writer+Reader>(stream: &mut SmtpStream<S>,
                       transaction: &mut SmtpTransaction,
                       line: &str) -> Result<(), ()> {
    if line.len() == 0 || line.char_at(0) == ' ' {
        stream.write_line("502 Command not implemented").unwrap();
    } else {
        stream.write_line("500 Command unrecognized").unwrap();
    }
    Ok(())
}

#[test]
fn test_command_help() {
    // fail!();
}

#[allow(unused_variable)]
fn handle_command_noop<S: Writer+Reader>(stream: &mut SmtpStream<S>,
                       transaction: &mut SmtpTransaction,
                       line: &str) -> Result<(), ()> {
    if line.len() == 0 || line.char_at(0) == ' ' {
        stream.write_line("250 OK").unwrap();
    } else {
        stream.write_line("500 Command unrecognized").unwrap();
    }
    Ok(())
}

#[test]
fn test_command_noop() {
    // fail!();
}

#[allow(unused_variable)]
fn handle_command_quit<S: Writer+Reader>(stream: &mut SmtpStream<S>,
                       transaction: &mut SmtpTransaction,
                       line: &str) -> Result<(), ()> {
    stream.write_line("221 rustastic.org").unwrap();
    Err(())
}

#[test]
fn test_command_quit() {
    // fail!();
}
<|endoftext|>


<commit_before>use std::net::{TcpListener, TcpStream};
use std::collections::HashMap;

use std::thread;
use std::sync::{Arc, Mutex};
use std::sync::mpsc::{Sender};

use std::io::prelude::*;
//use std::io::BufReader;

extern crate hmacsha1;
extern crate uuid;
extern crate byteorder;

use self::uuid::Uuid;

use client::{Client};
use chat::{read_text,text_as_bytes};
use distributor::Distributor;
use distributor::Kind as DistKind;
use store::Store;

#[allow(dead_code)]
pub struct Player {
    client_idx: usize, //this is dynamic in the sense that it may be different on intial run
}

pub struct Server {
    clients: Vec<Client>, // this always grows
    players: HashMap<Uuid, Player>,
    pub dist_tx: Sender<DistKind<TcpStream>>,
    store: Option<Store>,
}

impl Server {
    pub fn new(ip: &str) {
        let listener = TcpListener::bind(ip).unwrap();

        let (dist_tx,mut dist) = Distributor::new();
        thread::spawn(move || dist.run());

        let mut server = Server {
            clients: vec!(),
            players: HashMap::new(),
            dist_tx: dist_tx,
            store: Store::new(),
        };

        if let Some(ref store) = server.store {
            if let Ok(r) = store.conn.query("select * from clients",&[]) {
                for n in r.iter() {
                    server.clients.push(
                        Client { key:n.get(1),
                                 id:n.get(0),
                                 stream: None, });
                }
            }
        }

        let server = Arc::new(Mutex::new(server));

        for s in listener.incoming() {
            match s {
                Ok(s) => {
                    let server = server.clone();
                    thread::spawn(|| Server::handler(server,s));
                },
                _ => {},
            }
        }
    }

    #[allow(unused_must_use)]
    fn handler (mut server: Arc<Mutex<Server>>, mut s: TcpStream) {
        let mut cmd = [0;1];

        //new conn needs auth code
        let m = uuid::Uuid::new_v4();
        s.write_all(m.as_bytes());

        let mut client_idx = None;

        loop {
            if let Ok(_) = s.read_exact(&mut cmd) {
                match cmd[0] {
                    0 => { //login
                        client_idx = Server::login(&mut server, &mut s, m);
                    },
                    1 => { //register
                        if let Some(c) = Client::load(&mut s) {
                            let mut server = server.lock().unwrap();

                            for n in server.clients.iter() {
                                if n.id == c.id { continue }
                            }

                            let mut key = vec!();
                            key.extend_from_slice(&c.key); //FIXME: postgres execute doesn't like &[u8] for ToSql traits
                            if let Some(ref store) = server.store {
                                store.conn.execute("INSERT INTO clients (uuid, key) VALUES ($1, $2)",
                                                   &[&c.id, &key]);
                            }

                            println!("registered:{:?}",c.id);
                            server.clients.push(c);
                        }
                    },
                    _ => {
                        if client_idx.is_some() {
                            match cmd[0] {
                                2 => { //chat
                                    Server::chat(&mut server, &mut s);
                                },
                                _ => panic!("unknown cmd:{:?}",cmd)
                            }
                        }
                    }
                }
            }
            else { break } //drop dead client
        }

        println!("client dropped");
    }

    #[allow(unused_must_use)]
    fn chat (server: &mut Arc<Mutex<Server>>,
             mut s: &mut TcpStream,) {

        if let Some(text) = read_text(s) {
            println!("chat-client:{:?}",text.trim());

            //broadcast
            let (mut data, bytes) = text_as_bytes(&text);
            data.extend_from_slice(bytes);

            let server = server.lock().unwrap();
            server.dist_tx.send(DistKind::Broadcast(data));
        }
    }

    #[allow(unused_must_use)]
    fn login (server: &mut Arc<Mutex<Server>>,
              mut s: &mut TcpStream,
              m: Uuid) -> Option<usize> {
        let mut client_idx = None;

        if let Some(c) = Client::load(&mut s) {
            let mut server = server.lock().unwrap();

            let mut reg_key = None;
            for (i,n) in server.clients.iter_mut().enumerate() {
                if n.id == c.id {
                    reg_key = Some(n.key.clone());
                    client_idx = Some(i);

                    break
                }
            }

            if let Some(key) = reg_key {
                let hm = hmacsha1::hmac_sha1(&key, m.as_bytes());
                if c.key == hm {
                    server.players.insert(c.id,
                                          Player {client_idx:client_idx.unwrap()});

                    if let Ok(stmp) = s.try_clone() {
                        //n.stream = Some(Arc::new(Mutex::new(stmp)));
                        server.dist_tx.send(DistKind::Add(c.id,stmp));
                    }

                    println!("login:{:?}",c.id);
                    println!("total clients:{:?}",server.clients.len());
                }
                else {
                    panic!("client invalid login {:?}", c)
                }

            }
            else { panic!("client unregistered {:?}", c) }
        }

       client_idx
    }
}
<commit_msg>fixme fixed<commit_after>use std::net::{TcpListener, TcpStream};
use std::collections::HashMap;

use std::thread;
use std::sync::{Arc, Mutex};
use std::sync::mpsc::{Sender};

use std::io::prelude::*;
//use std::io::BufReader;

extern crate hmacsha1;
extern crate uuid;
extern crate byteorder;

use self::uuid::Uuid;

use client::{Client};
use chat::{read_text,text_as_bytes};
use distributor::Distributor;
use distributor::Kind as DistKind;
use store::Store;

#[allow(dead_code)]
pub struct Player {
    client_idx: usize, //this is dynamic in the sense that it may be different on intial run
}

pub struct Server {
    clients: Vec<Client>, // this always grows
    players: HashMap<Uuid, Player>,
    pub dist_tx: Sender<DistKind<TcpStream>>,
    store: Option<Store>,
}

impl Server {
    pub fn new(ip: &str) {
        let listener = TcpListener::bind(ip).unwrap();

        let (dist_tx,mut dist) = Distributor::new();
        thread::spawn(move || dist.run());

        let mut server = Server {
            clients: vec!(),
            players: HashMap::new(),
            dist_tx: dist_tx,
            store: Store::new(),
        };

        if let Some(ref store) = server.store {
            if let Ok(r) = store.conn.query("select * from clients",&[]) {
                for n in r.iter() {
                    server.clients.push(
                        Client { key:n.get(1),
                                 id:n.get(0),
                                 stream: None, });
                }
            }
        }

        let server = Arc::new(Mutex::new(server));

        for s in listener.incoming() {
            match s {
                Ok(s) => {
                    let server = server.clone();
                    thread::spawn(|| Server::handler(server,s));
                },
                _ => {},
            }
        }
    }

    #[allow(unused_must_use)]
    fn handler (mut server: Arc<Mutex<Server>>, mut s: TcpStream) {
        let mut cmd = [0;1];

        //new conn needs auth code
        let m = uuid::Uuid::new_v4();
        s.write_all(m.as_bytes());

        let mut client_idx = None;

        loop {
            if let Ok(_) = s.read_exact(&mut cmd) {
                match cmd[0] {
                    0 => { //login
                        client_idx = Server::login(&mut server, &mut s, m);
                    },
                    1 => { //register
                        if let Some(c) = Client::load(&mut s) {
                            let mut server = server.lock().unwrap();

                            for n in server.clients.iter() {
                                if n.id == c.id { continue }
                            }

                            if let Some(ref store) = server.store {
                                store.conn.execute("INSERT INTO clients (uuid, key) VALUES ($1, $2)",
                                                   &[&c.id, &c.key]);
                            }

                            println!("registered:{:?}",c.id);
                            server.clients.push(c);
                        }
                    },
                    _ => {
                        if client_idx.is_some() {
                            match cmd[0] {
                                2 => { //chat
                                    Server::chat(&mut server, &mut s);
                                },
                                _ => panic!("unknown cmd:{:?}",cmd)
                            }
                        }
                    }
                }
            }
            else { break } //drop dead client
        }

        println!("client dropped");
    }

    #[allow(unused_must_use)]
    fn chat (server: &mut Arc<Mutex<Server>>,
             mut s: &mut TcpStream,) {

        if let Some(text) = read_text(s) {
            println!("chat-client:{:?}",text.trim());

            //broadcast
            let (mut data, bytes) = text_as_bytes(&text);
            data.extend_from_slice(bytes);

            let server = server.lock().unwrap();
            server.dist_tx.send(DistKind::Broadcast(data));
        }
    }

    #[allow(unused_must_use)]
    fn login (server: &mut Arc<Mutex<Server>>,
              mut s: &mut TcpStream,
              m: Uuid) -> Option<usize> {
        let mut client_idx = None;

        if let Some(c) = Client::load(&mut s) {
            let mut server = server.lock().unwrap();

            let mut reg_key = None;
            for (i,n) in server.clients.iter_mut().enumerate() {
                if n.id == c.id {
                    reg_key = Some(n.key.clone());
                    client_idx = Some(i);

                    break
                }
            }

            if let Some(key) = reg_key {
                let hm = hmacsha1::hmac_sha1(&key, m.as_bytes());
                if c.key == hm {
                    server.players.insert(c.id,
                                          Player {client_idx:client_idx.unwrap()});

                    if let Ok(stmp) = s.try_clone() {
                        //n.stream = Some(Arc::new(Mutex::new(stmp)));
                        server.dist_tx.send(DistKind::Add(c.id,stmp));
                    }

                    println!("login:{:?}",c.id);
                    println!("total clients:{:?}",server.clients.len());
                }
                else {
                    panic!("client invalid login {:?}", c)
                }

            }
            else { panic!("client unregistered {:?}", c) }
        }

       client_idx
    }
}
<|endoftext|>