<commit_before>/**
 * s3-file-upload
 */

extern crate rusoto;
extern crate crypto;

use std::env;
use std::fs;
use std::path::Path;
use std::path::PathBuf;
use std::fs::{File, remove_file};
use std::io::{stdin, Read, Write};
use std::clone::Clone;
use std::collections::HashMap;

use rusoto::{ProfileProvider, Region};
use rusoto::s3::{Object, S3Client, ListObjectsV2Request, GetObjectRequest, PutObjectRequest};
use rusoto::default_tls_client;

use crypto::md5::Md5;
use crypto::digest::Digest;

/**
 * Enum for file status
 */
#[derive(PartialEq)]
enum FileStatus
{
    NotModified,
    Modified,
    New
}

/**
 * Struct for files
 */
#[derive(Clone)]
struct LocalFile
{
    path: PathBuf,
    md5: String
}

/**
 * Get the mime type for a local file
 */
fn local_file_get_mime(file: &LocalFile) -> String
{
    let extension = file.path.extension().unwrap().to_str();

    let mime_type = match extension.as_ref()
    {
        Some(&"html") => "text/html",
        Some(&"css")  => "text/css",
        Some(&"png")  => "image/png",
        Some(&"gif")  => "image/gif",
        Some(&"jpg")  => "image/jpeg",
        Some(&"xml")  => "application/xml",
        Some(&"txt")  => "text/plain",
        _             => "application/octet-stream",
    };

    return mime_type.to_owned();
}

/**
 * Get the MD5 checksum of a local file
 */
fn local_file_get_md5(file: &LocalFile) -> String
{
    let mut file_handle = File::open(&file.path).unwrap();
    let mut contents: Vec<u8> = Vec::new();

    match file_handle.read_to_end(&mut contents)
    {
        Ok(_) =>
        {
            let mut md5_checksum = Md5::new();
            md5_checksum.input(&contents);
            return md5_checksum.result_str();
        }
        Err(error) =>
        {
            println!("Error: {}", error);
            return String::from("");
        }
    }
}

/**
 * Get a relative path for a local file
 */
fn local_file_get_relative_path(file: &LocalFile, local_path: &String) -> String
{
    let full_path: String = file.path.as_path().to_str().unwrap().to_owned();
    let last_path_section = full_path.split(local_path)
        .last()
        .unwrap()
        .replace("\\", "/");

    return last_path_section.to_owned();
}

/**
 * Upload a local file to an s3 bucket location
 */
fn local_file_upload_to_bucket(file: &LocalFile, local_path: &String, bucket_name: &String, public_file: bool)
{
    let provider = ProfileProvider::with_configuration(Path::new("credentials"), "user");
    let client = S3Client::new(default_tls_client().unwrap(), provider, Region::EuWest1);

    let file = file.clone();

    print!("Uploading \"{}\" to \"{}/{}\"...", file.path.to_str().unwrap(), bucket_name, local_file_get_relative_path(&file, &local_path));
    Some(std::io::stdout().flush());

    let mut file_handle = File::open(&file.path).expect("Could not open file");
    let mut contents: Vec<u8> = Vec::new();

    match file_handle.read_to_end(&mut contents)
    {
        Ok(_) =>
        {
            let mut acl = String::from("private");
            if public_file
            {
                acl = String::from("public-read");
            }

            let request = PutObjectRequest
            {
                bucket: bucket_name.to_owned(),
                key: local_file_get_relative_path(&file, &local_path),
                body: Some(contents),
                acl: Some(acl),
                content_type: Some(local_file_get_mime(&file)),
                ..PutObjectRequest::default()
            };

            match client.put_object(&request)
            {
                Ok(_) =>
                {
                    println!(" DONE");
                }
                Err(error) =>
                {
                   println!(" Error: {}", error);
                }
            }
        }
        Err(error) =>
        {
            println!("Error: {}", error);
        }
    }
}

/**
 * Create the local file checksums
 */
fn local_file_create_checksums(files: &Vec<LocalFile>, local_path: &String) -> LocalFile
{
    let mut path = PathBuf::from(local_path);
    path.push("checksums.txt");

    let mut checksums_file = File::create(path.to_str().unwrap()).expect("Could not create checksums");

    for file in files.clone()
    {
        let checksums_line = format!("{} {}\n", local_file_get_relative_path(&file, local_path), file.md5);
        checksums_file.write(checksums_line.as_bytes()).expect("Could not write checksums");
    }

    return LocalFile { path: path, md5: String::from("") };
}

/**
 * Delete the local file checksums
 */
fn local_file_delete_checksums(local_path: &String)
{
    let mut path = PathBuf::from(local_path);
    path.push("checksums.txt");

    remove_file(path).expect("Could not remove checksums");
}

/**
 * Get objects in an s3 bucket
 */
fn get_bucket_objects(bucket_name: &String, objects: &mut Vec<Object>)
{
    let provider = ProfileProvider::with_configuration(Path::new("credentials"), "user");
    let client = S3Client::new(default_tls_client().unwrap(), provider, Region::EuWest1);

    let request = ListObjectsV2Request
    {
        bucket: bucket_name.to_owned(),
        ..ListObjectsV2Request::default()
    };

    if let Ok(response) = client.list_objects_v2(&request)
    {
        for object in &response.contents.unwrap()
        {
            objects.push(object.clone());
        }
    }
}

/**
 * Struct for checksums
 */
#[derive(Clone)]
struct Checksums
{
    files: HashMap<String, String>
}

/**
 * Get the s3 bucket checksums
 */
fn get_bucket_object_checksums(bucket_name: &String, bucket_objects: &Vec<Object>) -> Option<Checksums>
{
    match bucket_objects.iter().position(|obj| obj.clone().key.unwrap() == String::from("checksums.txt"))
    {
        Some(index) =>
        {
            let provider = ProfileProvider::with_configuration(Path::new("credentials"), "user");
            let client = S3Client::new(default_tls_client().unwrap(), provider, Region::EuWest1);

            let obj = bucket_objects.get(index).clone().unwrap();

            let request = GetObjectRequest
            {
                bucket: bucket_name.to_owned(),
                key: obj.clone().key.unwrap(),
                ..GetObjectRequest::default()
            };

            if let Ok(response) = client.get_object(&request)
            {
                let contents_bytes = response.body.unwrap();
                let contents = String::from_utf8_lossy(&contents_bytes);

                let mut hash_map = HashMap::new();

                for line in contents.split("\n")
                {
                    let mut line_split = line.split_whitespace();
                    let path = line_split.next();
                    let md5 = line_split.next();

                    if path != None && md5 != None
                    {
                        let path = String::from(path.unwrap());
                        let md5 = String::from(md5.unwrap());
                        hash_map.insert(path, md5);
                    }
                }

                let checksums = Checksums {files: hash_map};
                Some(checksums)
            }
            else
            {
                None
            }
        },
        None =>
        {
            None
        }
    }
}

/**
 * Check if the file matches the checksums
 */
fn local_file_matches_checksums(local_path: &String, file: &LocalFile, checksums: &Checksums) -> FileStatus
{
    for (path, md5) in &checksums.files
    {
        if &local_file_get_relative_path(file, local_path) == path
        {
            if file.md5 == md5.to_owned()
            {
                return FileStatus::NotModified;
            }
            else
            {
                return FileStatus::Modified;
            }
        }
    }
    return FileStatus::New;
}

/**
 * Get files from a local path
 */
fn get_local_files(local_path: &String, files: &mut Vec<LocalFile>, ignored_directories: &mut Vec<String>)
{
    if let Ok(entries) = fs::read_dir(Path::new(&local_path))
    {
        for entry in entries
        {
            let file = entry.unwrap();

            if let Ok(metadata) = file.metadata()
            {
                if metadata.is_dir()
                {
                    if !ignored_directories.contains(&file.file_name().to_str().unwrap().to_owned())
                    {
                        get_local_files(&file.path().to_str().unwrap().to_owned(), files, ignored_directories);
                    }
                }
                else
                {
                    let temp_file = LocalFile { path: file.path(), md5: String::from("") };
                    let file = LocalFile { path: file.path(), md5: local_file_get_md5(&temp_file) };
                    files.push(file);
                }
            }
        }
    }
}

/**
 * Main function
 */
fn main()
{
    if env::args().len() < 3
    {
        println!("\nUsage:\n\ns3-file-upload LOCAL_PATH BUCKET_NAME [OPTIONS]");
        println!("\nOptions:\n\n--ignored_directories    List of directory names using a comma separator, e.g. --ignored_directories=ignored_dir_one,ignored_dir_two");
        return;
    }

    let local_path  = env::args().nth(1).unwrap();
    let bucket_name = env::args().nth(2).unwrap();

    if !Path::new("credentials").exists()
    {
        println!("credentials file could not be found");
        return;
    }

    let mut ignored_directories: Vec<String> = Vec::new();

    // Set the ignored directories if the ignored_directories parameter is set
    for parameter in env::args()
    {
        if parameter.contains("--ignored_directories=")
        {
            let directories = parameter.split("=").nth(1).unwrap();

            for directory in directories.split(",")
            {
                ignored_directories.push(String::from(directory));
            }
        }
    }

    let mut files: Vec<LocalFile> = Vec::new();
    get_local_files(&local_path, &mut files, &mut ignored_directories);

    let mut bucket_objects: Vec<Object> = Vec::new();
    get_bucket_objects(&bucket_name, &mut bucket_objects);

    let checksums = match get_bucket_object_checksums(&bucket_name, &bucket_objects)
    {
        Some(checksums) =>
        {
            Some(checksums)
        },
        None =>
        {
            let hash_map = HashMap::new();
            let checksums = Checksums {files: hash_map};
            Some(checksums)
        }
    }.unwrap();

    println!("\nFiles found to be uploaded:\n");

    for file in &files
    {
        let file_status: FileStatus = local_file_matches_checksums(&local_path, &file, &checksums);
        if file_status == FileStatus::Modified
        {
            println!("Modified: {}", local_file_get_relative_path(file, &local_path));
        }
        else if file_status == FileStatus::New
        {
            println!("New: {}", local_file_get_relative_path(file, &local_path));
        }
    }

    let mut input_string = String::new();

    while input_string != "y" && input_string != "n"
    {
        println!("\nConfirm upload? <y/N>");

        input_string.clear();
        stdin().read_line(&mut input_string).expect("Did not input string");
        input_string = String::from(input_string.trim().to_lowercase());

        if input_string == "" || input_string == "no"
        {
            input_string = String::from("n");
        }
        else if input_string == "yes"
        {
            input_string = String::from("y");
        }
    }

    let confirm_upload = input_string == "y";

    if confirm_upload
    {
        for file in &files
        {
            let file_status: FileStatus = local_file_matches_checksums(&local_path, &file, &checksums);
            if file_status == FileStatus::Modified || file_status == FileStatus::New
            {
                local_file_upload_to_bucket(&file, &local_path, &bucket_name, true);
            }
        }

        let new_checksums = local_file_create_checksums(&files, &local_path);
        local_file_upload_to_bucket(&new_checksums, &local_path, &bucket_name, false);
        local_file_delete_checksums(&local_path);
    }
    else
    {
        println!("Upload cancelled")
    }
}
<commit_msg>Quit early if there are no pending new/modified files<commit_after>/**
 * s3-file-upload
 */

extern crate rusoto;
extern crate crypto;

use std::env;
use std::fs;
use std::path::Path;
use std::path::PathBuf;
use std::fs::{File, remove_file};
use std::io::{stdin, Read, Write};
use std::clone::Clone;
use std::collections::HashMap;

use rusoto::{ProfileProvider, Region};
use rusoto::s3::{Object, S3Client, ListObjectsV2Request, GetObjectRequest, PutObjectRequest};
use rusoto::default_tls_client;

use crypto::md5::Md5;
use crypto::digest::Digest;

/**
 * Enum for file status
 */
#[derive(PartialEq)]
enum FileStatus
{
    NotModified,
    Modified,
    New
}

/**
 * Struct for files
 */
#[derive(Clone)]
struct LocalFile
{
    path: PathBuf,
    md5: String
}

/**
 * Get the mime type for a local file
 */
fn local_file_get_mime(file: &LocalFile) -> String
{
    let extension = file.path.extension().unwrap().to_str();

    let mime_type = match extension.as_ref()
    {
        Some(&"html") => "text/html",
        Some(&"css")  => "text/css",
        Some(&"png")  => "image/png",
        Some(&"gif")  => "image/gif",
        Some(&"jpg")  => "image/jpeg",
        Some(&"xml")  => "application/xml",
        Some(&"txt")  => "text/plain",
        _             => "application/octet-stream",
    };

    return mime_type.to_owned();
}

/**
 * Get the MD5 checksum of a local file
 */
fn local_file_get_md5(file: &LocalFile) -> String
{
    let mut file_handle = File::open(&file.path).unwrap();
    let mut contents: Vec<u8> = Vec::new();

    match file_handle.read_to_end(&mut contents)
    {
        Ok(_) =>
        {
            let mut md5_checksum = Md5::new();
            md5_checksum.input(&contents);
            return md5_checksum.result_str();
        }
        Err(error) =>
        {
            println!("Error: {}", error);
            return String::from("");
        }
    }
}

/**
 * Get a relative path for a local file
 */
fn local_file_get_relative_path(file: &LocalFile, local_path: &String) -> String
{
    let full_path: String = file.path.as_path().to_str().unwrap().to_owned();
    let last_path_section = full_path.split(local_path)
        .last()
        .unwrap()
        .replace("\\", "/");

    return last_path_section.to_owned();
}

/**
 * Upload a local file to an s3 bucket location
 */
fn local_file_upload_to_bucket(file: &LocalFile, local_path: &String, bucket_name: &String, public_file: bool)
{
    let provider = ProfileProvider::with_configuration(Path::new("credentials"), "user");
    let client = S3Client::new(default_tls_client().unwrap(), provider, Region::EuWest1);

    let file = file.clone();

    print!("Uploading \"{}\" to \"{}/{}\"...", file.path.to_str().unwrap(), bucket_name, local_file_get_relative_path(&file, &local_path));
    Some(std::io::stdout().flush());

    let mut file_handle = File::open(&file.path).expect("Could not open file");
    let mut contents: Vec<u8> = Vec::new();

    match file_handle.read_to_end(&mut contents)
    {
        Ok(_) =>
        {
            let mut acl = String::from("private");
            if public_file
            {
                acl = String::from("public-read");
            }

            let request = PutObjectRequest
            {
                bucket: bucket_name.to_owned(),
                key: local_file_get_relative_path(&file, &local_path),
                body: Some(contents),
                acl: Some(acl),
                content_type: Some(local_file_get_mime(&file)),
                ..PutObjectRequest::default()
            };

            match client.put_object(&request)
            {
                Ok(_) =>
                {
                    println!(" DONE");
                }
                Err(error) =>
                {
                   println!(" Error: {}", error);
                }
            }
        }
        Err(error) =>
        {
            println!("Error: {}", error);
        }
    }
}

/**
 * Create the local file checksums
 */
fn local_file_create_checksums(files: &Vec<LocalFile>, local_path: &String) -> LocalFile
{
    let mut path = PathBuf::from(local_path);
    path.push("checksums.txt");

    let mut checksums_file = File::create(path.to_str().unwrap()).expect("Could not create checksums");

    for file in files.clone()
    {
        let checksums_line = format!("{} {}\n", local_file_get_relative_path(&file, local_path), file.md5);
        checksums_file.write(checksums_line.as_bytes()).expect("Could not write checksums");
    }

    return LocalFile { path: path, md5: String::from("") };
}

/**
 * Delete the local file checksums
 */
fn local_file_delete_checksums(local_path: &String)
{
    let mut path = PathBuf::from(local_path);
    path.push("checksums.txt");

    remove_file(path).expect("Could not remove checksums");
}

/**
 * Get objects in an s3 bucket
 */
fn get_bucket_objects(bucket_name: &String, objects: &mut Vec<Object>)
{
    let provider = ProfileProvider::with_configuration(Path::new("credentials"), "user");
    let client = S3Client::new(default_tls_client().unwrap(), provider, Region::EuWest1);

    let request = ListObjectsV2Request
    {
        bucket: bucket_name.to_owned(),
        ..ListObjectsV2Request::default()
    };

    if let Ok(response) = client.list_objects_v2(&request)
    {
        for object in &response.contents.unwrap()
        {
            objects.push(object.clone());
        }
    }
}

/**
 * Struct for checksums
 */
#[derive(Clone)]
struct Checksums
{
    files: HashMap<String, String>
}

/**
 * Get the s3 bucket checksums
 */
fn get_bucket_object_checksums(bucket_name: &String, bucket_objects: &Vec<Object>) -> Option<Checksums>
{
    match bucket_objects.iter().position(|obj| obj.clone().key.unwrap() == String::from("checksums.txt"))
    {
        Some(index) =>
        {
            let provider = ProfileProvider::with_configuration(Path::new("credentials"), "user");
            let client = S3Client::new(default_tls_client().unwrap(), provider, Region::EuWest1);

            let obj = bucket_objects.get(index).clone().unwrap();

            let request = GetObjectRequest
            {
                bucket: bucket_name.to_owned(),
                key: obj.clone().key.unwrap(),
                ..GetObjectRequest::default()
            };

            if let Ok(response) = client.get_object(&request)
            {
                let contents_bytes = response.body.unwrap();
                let contents = String::from_utf8_lossy(&contents_bytes);

                let mut hash_map = HashMap::new();

                for line in contents.split("\n")
                {
                    let mut line_split = line.split_whitespace();
                    let path = line_split.next();
                    let md5 = line_split.next();

                    if path != None && md5 != None
                    {
                        let path = String::from(path.unwrap());
                        let md5 = String::from(md5.unwrap());
                        hash_map.insert(path, md5);
                    }
                }

                let checksums = Checksums {files: hash_map};
                Some(checksums)
            }
            else
            {
                None
            }
        },
        None =>
        {
            None
        }
    }
}

/**
 * Check if the file matches the checksums
 */
fn local_file_matches_checksums(local_path: &String, file: &LocalFile, checksums: &Checksums) -> FileStatus
{
    for (path, md5) in &checksums.files
    {
        if &local_file_get_relative_path(file, local_path) == path
        {
            if file.md5 == md5.to_owned()
            {
                return FileStatus::NotModified;
            }
            else
            {
                return FileStatus::Modified;
            }
        }
    }
    return FileStatus::New;
}

/**
 * Get files from a local path
 */
fn get_local_files(local_path: &String, files: &mut Vec<LocalFile>, ignored_directories: &mut Vec<String>)
{
    if let Ok(entries) = fs::read_dir(Path::new(&local_path))
    {
        for entry in entries
        {
            let file = entry.unwrap();

            if let Ok(metadata) = file.metadata()
            {
                if metadata.is_dir()
                {
                    if !ignored_directories.contains(&file.file_name().to_str().unwrap().to_owned())
                    {
                        get_local_files(&file.path().to_str().unwrap().to_owned(), files, ignored_directories);
                    }
                }
                else
                {
                    let temp_file = LocalFile { path: file.path(), md5: String::from("") };
                    let file = LocalFile { path: file.path(), md5: local_file_get_md5(&temp_file) };
                    files.push(file);
                }
            }
        }
    }
}

/**
 * Main function
 */
fn main()
{
    if env::args().len() < 3
    {
        println!("\nUsage:\n\ns3-file-upload LOCAL_PATH BUCKET_NAME [OPTIONS]");
        println!("\nOptions:\n\n--ignored_directories    List of directory names using a comma separator, e.g. --ignored_directories=ignored_dir_one,ignored_dir_two");
        return;
    }

    let local_path  = env::args().nth(1).unwrap();
    let bucket_name = env::args().nth(2).unwrap();

    if !Path::new("credentials").exists()
    {
        println!("credentials file could not be found");
        return;
    }

    let mut ignored_directories: Vec<String> = Vec::new();

    // Set the ignored directories if the ignored_directories parameter is set
    for parameter in env::args()
    {
        if parameter.contains("--ignored_directories=")
        {
            let directories = parameter.split("=").nth(1).unwrap();

            for directory in directories.split(",")
            {
                ignored_directories.push(String::from(directory));
            }
        }
    }

    let mut files: Vec<LocalFile> = Vec::new();
    get_local_files(&local_path, &mut files, &mut ignored_directories);

    let mut bucket_objects: Vec<Object> = Vec::new();
    get_bucket_objects(&bucket_name, &mut bucket_objects);

    let checksums = match get_bucket_object_checksums(&bucket_name, &bucket_objects)
    {
        Some(checksums) =>
        {
            Some(checksums)
        },
        None =>
        {
            let hash_map = HashMap::new();
            let checksums = Checksums {files: hash_map};
            Some(checksums)
        }
    }.unwrap();

    let mut new_files: Vec<LocalFile> = Vec::new();
    let mut modified_files: Vec<LocalFile> = Vec::new();

    for file in &files
    {
        let file_status: FileStatus = local_file_matches_checksums(&local_path, &file, &checksums);
        if file_status == FileStatus::New
        {
            new_files.push(file.to_owned());
        }
        else if file_status == FileStatus::Modified
        {
            modified_files.push(file.to_owned());
        }
    }

    if modified_files.len() > 0 || new_files.len() > 0
    {
        println!("\nFiles found to be uploaded:\n");

        for file in &new_files
        {
            println!("New: {}", local_file_get_relative_path(file, &local_path));
        }

        for file in &modified_files
        {
            println!("Modified: {}", local_file_get_relative_path(file, &local_path));
        }

        let mut input_string = String::new();

        while input_string != "y" && input_string != "n"
        {
            println!("\nConfirm upload? <y/N>");

            input_string.clear();
            stdin().read_line(&mut input_string).expect("Did not input string");
            input_string = String::from(input_string.trim().to_lowercase());

            if input_string == "" || input_string == "no"
            {
                input_string = String::from("n");
            }
            else if input_string == "yes"
            {
                input_string = String::from("y");
            }
        }

        let confirm_upload = input_string == "y";

        if confirm_upload
        {
            for file in &modified_files
            {
                local_file_upload_to_bucket(&file, &local_path, &bucket_name, true);
            }

            for file in &new_files
            {
                local_file_upload_to_bucket(&file, &local_path, &bucket_name, true);
            }

            let new_checksums = local_file_create_checksums(&files, &local_path);
            local_file_upload_to_bucket(&new_checksums, &local_path, &bucket_name, false);
            local_file_delete_checksums(&local_path);
        }
        else
        {
            println!("\nUpload cancelled")
        }
    }
    else
    {
        println!("\nNo pending modified/new files")
    }

}
<|endoftext|>


<commit_before>extern crate discord;

use std::env;
use discord::{Discord, State};
use discord::model::Event;

pub fn main() {
	// Log in to Discord using a bot token from the environment
	let discord = Discord::from_bot_token(
		&env::var("DISCORD_TOKEN").expect("Expected token"),
	).expect("login failed");

	// establish websocket and voice connection
	let (mut connection, ready) = discord.connect().expect("connect failed");
	println!("[Ready] {} is serving {} servers", ready.user.username, ready.servers.len());
	let mut state = State::new(ready);
	connection.sync_calls(&state.all_private_channels());

	// receive events forever
	loop {
		let event = match connection.recv_event() {
			Ok(event) => event,
			Err(err) => {
				println!("[Warning] Receive error: {:?}", err);
				if let discord::Error::WebSocket(..) = err {
					// Handle the websocket connection being dropped
					let (new_connection, ready) = discord.connect().expect("connect failed");
					connection = new_connection;
					state = State::new(ready);
					println!("[Ready] Reconnected successfully.");
				}
				if let discord::Error::Closed(..) = err {
					break
				}
				continue
			},
		};
		state.update(&event);

		match event {
			Event::MessageCreate(message) => {
				use std::ascii::AsciiExt;
				// safeguard: stop if the message is from us
				if message.author.id == state.user().id {
					continue
				}

				// reply to a command if there was one
				let mut split = message.content.split(' ');
				let first_word = split.next().unwrap_or("");
				let argument = split.next().unwrap_or("");

                if first_word.eq_ignore_ascii_case("!help") {
                    if argument.eq_ignore_ascii_case("commands") {
                        warn(discord.send_message(message.channel_id, "help, about, uber, and sourcecode are the avaliable commands. (!dj is buggy)", "", false))
                    }
                    else {
                    warn(discord.send_message(message.channel_id, "Umm, you gotta use it like this. !help [args]", "", false))
                    }
                }

                if first_word.eq_ignore_ascii_case("!about") {
                    warn(discord.send_message(message.channel_id, "I have no clue. All I know is that I was written by Arroz in a language called Rust, and my code is still being developed.", "", false))
                }

                if first_word.eq_ignore_ascii_case("!uber"){
                    if argument.eq_ignore_ascii_case("ddog75") {
                        warn(discord.send_message(message.channel_id, "Alright, 10 minutes.", "", false))
                    } else {
                        warn(discord.send_message(message.channel_id, "Fuck off!", "", false))
                    }
                }

                if first_word.eq_ignore_ascii_case("!sourcecode") {
                    warn(discord.send_message(message.channel_id, "Okay, here you go. https://github.com/aarroz/rice_bot2", "", false))
                }

                // Command for using dj.
				if first_word.eq_ignore_ascii_case("!dj") {
					let vchan = state.find_voice_user(message.author.id);
					if argument.eq_ignore_ascii_case("stop") {
						vchan.map(|(sid, _)| connection.voice(sid).stop());
					} else if argument.eq_ignore_ascii_case("quit") {
						vchan.map(|(sid, _)| connection.drop_voice(sid));
					} else {
						let output = if let Some((server_id, channel_id)) = vchan {
							match discord::voice::open_ytdl_stream(argument) {
								Ok(stream) => {
									let voice = connection.voice(server_id);
									voice.set_deaf(true);
									voice.connect(channel_id);
									voice.play(stream);
									String::new()
								},
								Err(error) => format!("Error: {}", error),
							}
						} else {
							"You must be in a voice channel to DJ".to_owned()
						};
						if !output.is_empty() {
							warn(discord.send_message(message.channel_id, &output, "", false));
						}
					}
				}
			}
			Event::VoiceStateUpdate(server_id, _) => {
				// If someone moves/hangs up, and we are in a voice channel,
				if let Some(cur_channel) = connection.voice(server_id).current_channel() {
					// and our current voice channel is empty, disconnect from voice
					match server_id {
						Some(server_id) => if let Some(srv) = state.servers().iter().find(|srv| srv.id == server_id) {
							if srv.voice_states.iter().filter(|vs| vs.channel_id == Some(cur_channel)).count() <= 1 {
								connection.voice(Some(server_id)).disconnect();
							}
						},
						None => if let Some(call) = state.calls().get(&cur_channel) {
							if call.voice_states.len() <= 1 {
								connection.voice(server_id).disconnect();
							}
						}
					}
				}
			}
			_ => {}, // discard other events
		}
	}
}

fn warn<T, E: ::std::fmt::Debug>(result: Result<T, E>) {
	match result {
		Ok(_) => {},
		Err(err) => println!("[Warning] {:?}", err)
	}
}
<commit_msg>Added distrowiki command.<commit_after>extern crate discord;

use std::env;
use discord::{Discord, State};
use discord::model::Event;

pub fn main() {
	// Log in to Discord using a bot token from the environment
	let discord = Discord::from_bot_token(
		&env::var("DISCORD_TOKEN").expect("Expected token"),
	).expect("login failed");

	// establish websocket and voice connection
	let (mut connection, ready) = discord.connect().expect("connect failed");
	println!("[Ready] {} is serving {} servers", ready.user.username, ready.servers.len());
	let mut state = State::new(ready);
	connection.sync_calls(&state.all_private_channels());

	// receive events forever
	loop {
		let event = match connection.recv_event() {
			Ok(event) => event,
			Err(err) => {
				println!("[Warning] Receive error: {:?}", err);
				if let discord::Error::WebSocket(..) = err {
					// Handle the websocket connection being dropped
					let (new_connection, ready) = discord.connect().expect("connect failed");
					connection = new_connection;
					state = State::new(ready);
					println!("[Ready] Reconnected successfully.");
				}
				if let discord::Error::Closed(..) = err {
					break
				}
				continue
			},
		};
		state.update(&event);

		match event {
			Event::MessageCreate(message) => {
				use std::ascii::AsciiExt;
				// safeguard: stop if the message is from us
				if message.author.id == state.user().id {
					continue
				}

				// reply to a command if there was one
				let mut split = message.content.split(' ');
				let first_word = split.next().unwrap_or("");
				let argument = split.next().unwrap_or("");

                if first_word.eq_ignore_ascii_case("!help") {
                    if argument.eq_ignore_ascii_case("commands") {
                        warn(discord.send_message(message.channel_id, "help, about, uber, and sourcecode are the avaliable commands. (!dj is buggy)", "", false))
                    }
                    else {
                    warn(discord.send_message(message.channel_id, "Umm, you gotta use it like this. !help [args]", "", false))
                    }
                }

                if first_word.eq_ignore_ascii_case("!about") {
                    warn(discord.send_message(message.channel_id, "I have no clue. All I know is that I was written by Arroz in a language called Rust, and my code is still being developed.", "", false))
                }

                if first_word.eq_ignore_ascii_case("!uber"){
                    if argument.eq_ignore_ascii_case("ddog75") {
                        warn(discord.send_message(message.channel_id, "Alright, 10 minutes.", "", false))
                    } else {
                        warn(discord.send_message(message.channel_id, "Fuck off!", "", false))
                    }
                }

                if first_word.eq_ignore_ascii_case("!sourcecode") {
                    warn(discord.send_message(message.channel_id, "Okay, here you go. https://github.com/aarroz/rice_bot2", "", false))
                }
				//Section that prints wikis for linux distributions.
				if first_word.eq_ignore_ascii_case("!distrowiki") {
					if argument.eq_ignore_ascii_case("arch") {
						warn(discord.send_message(message.channel_id, "Here's the Arch Wiki! https://wiki.archlinux.org/", "", false))
					}
					if argument.eq_ignore_ascii_case("ubuntu") {
						warn(discord.send_message(message.channel_id, "Here's the Ubuntu Wiki! https://wiki.ubuntu.com/", "", false))
					}
					if argument.eq_ignore_ascii_case("debian") {
						warn(discord.send_message(message.channel_id, "Here's the Debian Wiki! https://wiki.debian.org/", "", false))
					}
					if argument.eq_ignore_ascii_case("antergos") {
						warn(discord.send_message(message.channel_id, "Here's the Antergos Wiki! https://antergos.com/wiki/", "", false))
					}
					if argument.eq_ignore_ascii_case("centos") {
						warn(discord.send_message(message.channel_id, "Here's the CentOS Wiki! https://wiki.centos.org/", "", false))
					}
					else {
						warn(discord.send_message(message.channel_id, "Please provide a valid distro. (Or that distro doesn't have a wiki)", "", false))
					}
				}

                // Command for using dj.
				if first_word.eq_ignore_ascii_case("!dj") {
					let vchan = state.find_voice_user(message.author.id);
					if argument.eq_ignore_ascii_case("stop") {
						vchan.map(|(sid, _)| connection.voice(sid).stop());
					} else if argument.eq_ignore_ascii_case("quit") {
						vchan.map(|(sid, _)| connection.drop_voice(sid));
					} else {
						let output = if let Some((server_id, channel_id)) = vchan {
							match discord::voice::open_ytdl_stream(argument) {
								Ok(stream) => {
									let voice = connection.voice(server_id);
									voice.set_deaf(true);
									voice.connect(channel_id);
									voice.play(stream);
									String::new()
								},
								Err(error) => format!("Error: {}", error),
							}
						} else {
							"You must be in a voice channel to DJ".to_owned()
						};
						if !output.is_empty() {
							warn(discord.send_message(message.channel_id, &output, "", false));
						}
					}
				}
			}
			Event::VoiceStateUpdate(server_id, _) => {
				// If someone moves/hangs up, and we are in a voice channel,
				if let Some(cur_channel) = connection.voice(server_id).current_channel() {
					// and our current voice channel is empty, disconnect from voice
					match server_id {
						Some(server_id) => if let Some(srv) = state.servers().iter().find(|srv| srv.id == server_id) {
							if srv.voice_states.iter().filter(|vs| vs.channel_id == Some(cur_channel)).count() <= 1 {
								connection.voice(Some(server_id)).disconnect();
							}
						},
						None => if let Some(call) = state.calls().get(&cur_channel) {
							if call.voice_states.len() <= 1 {
								connection.voice(server_id).disconnect();
							}
						}
					}
				}
			}
			_ => {}, // discard other events
		}
	}
}

fn warn<T, E: ::std::fmt::Debug>(result: Result<T, E>) {
	match result {
		Ok(_) => {},
		Err(err) => println!("[Warning] {:?}", err)
	}
}
<|endoftext|>


<commit_before>extern crate reqwest;
extern crate select;
extern crate serenity;

use select::document::Document;
use select::predicate::Class;

use serenity::client::Client;
use serenity::prelude::*;
use serenity::model::*;

// For file reading
use std::io::Read;
use std::fs::File;
use std::io::Write;
use std::fs::OpenOptions;

// For multithreading
use std::thread;
use std::sync::{Arc, Mutex};
use std::ops::DerefMut;

// Allow openssl crosscompiling to work
extern crate openssl_probe;

extern crate time;

#[derive(Debug)]
struct UMassEvent {
    title: String,
    description: String,
    date: String,
    location: Option<String>,
}

impl UMassEvent {
    fn format(&self) -> String {
        match self.location {
            // "Event_Name at Event_location: Long Description"
            Some(ref location) => format!("{} at {}:\n{}", self.title, location, self.description),
            // "Event_Name: Long Description"
            None => format!("{}:\n{}", self.title, self.description),
        }
    }
}

fn get_document(url: &str) -> Result<select::document::Document, reqwest::Error> {
    reqwest::get(url).map(|mut response| {
        // Extract the data from the http request
        let mut content = String::new();
        let _ = response.read_to_string(&mut content);
        Document::from(&*content)
    })
}

fn get_events() -> Vec<UMassEvent> {
    let document = get_document("http://www.umass.edu/events/").expect("Couldn't get the events page");

    // Parse the data into a list of events
    let events = document.find(Class("views-row"))
        .map(|node| {

            // This is really janky and relies on UMass not changing the event page html...

            let title = node.find(Class("views-field-title"))
                .next()
                .unwrap()
                .children()
                .nth(1)
                .unwrap()
                .first_child()
                .unwrap()
                .first_child()
                .unwrap()
                .text();
            let description = node.find(Class("views-field-field-short-desc"))
                .next()
                .unwrap()
                .children()
                .nth(1)
                .unwrap()
                .first_child()
                .unwrap()
                .text();
            let date = node.find(Class("event-date")).next().unwrap().text();
            let location = node.find(Class("event-location"))
                .next()
                .unwrap()
                .children()
                .nth(1)
                .map(|node| node.first_child().unwrap().text());

            // Return the event, which will then be collected into the events vector
            UMassEvent {
                title: title,
                description: description,
                date: date,
                location: location,
            }
        })
        .collect();

    events
}

#[derive(Copy)]
#[derive(Clone)]
#[derive(Debug)]
enum Meal {
    Breakfast,
    Lunch,
    Dinner,
    LateNight,
}

#[derive(Copy)]
#[derive(Clone)]
#[derive(Debug)]
enum DiningCommon {
    Berk,
    Hamp,
    Frank,
    Worcester,
}

fn get_meal_code(meal: Meal, dining_common: DiningCommon) -> String {
    match dining_common {
            DiningCommon::Worcester => {
                match meal {
                    Meal::Breakfast => "0700001",
                    Meal::Lunch => "1130001",
                    Meal::Dinner => "1630001",
                    Meal::LateNight => "2100001",
                }
            }
            DiningCommon::Frank => {
                match meal {
                    Meal::Breakfast => "0700002",
                    Meal::Lunch => "1130002",
                    Meal::Dinner => "1630002",
                    Meal::LateNight => panic!("Frank doesn't have late night"),
                }
            }
            DiningCommon::Hamp => {
                match meal {
                    Meal::Breakfast => "0700003",
                    Meal::Lunch => "1130003",
                    Meal::Dinner => "1630003",
                    Meal::LateNight => panic!("Hamp doesn't have late night"),
                }
            }
            DiningCommon::Berk => {
                match meal {
                    Meal::Breakfast => panic!("Berk doesn't have breakfast"),
                    Meal::Lunch => "1100004",
                    Meal::Dinner => "1630004",
                    Meal::LateNight => "2100004",
                }
            }
        }
        .to_string()

}

fn get_dining_common_code(dining_common: DiningCommon) -> String {
    match dining_common {
            DiningCommon::Worcester => "0",
            DiningCommon::Frank => "1",
            DiningCommon::Hamp => "2",
            DiningCommon::Berk => "3",
        }
        .to_string()
}

fn get_menu_document(dining_common: DiningCommon,
                     meal: Meal)
                     -> Result<select::document::Document, reqwest::Error> {
    let dining_common_id = get_dining_common_code(dining_common.clone());

    let time = time::now();

    let year = time.tm_year + 1900;
    let month = format!("{:02}", time.tm_mon + 1);
    let day = format!("{:02}", time.tm_mday);
    let meal = get_meal_code(meal, dining_common);

    let url: &str = &format!("https://go.umass.\
                              edu/dining/event?feed=dining-halls&id=id_{id}&calendar=dining-halls_id_{id}_event_calendar&startdate={day}-{month}-{year}&event={year}{month}{day}T{meal}%7C{year}{month}{day}T000000&calendarMode=day",
                             id = dining_common_id,
                             year = year,
                             month = month,
                             day = day,
                             meal = meal);

    println!("{}", url);

    get_document(url)

}

fn is_on_menu(dining_common: DiningCommon, meal: Meal, item: &str) -> bool {
    let nodes: Vec<String> = get_menu_document(dining_common, meal)
        .expect("Couldn't get the menu page")
        .find(Class("kgo_web_content"))
        .map(|node| node.text())
        .collect();

    let text: String = nodes.join(" ");

    return text.to_lowercase().as_str().contains(item.to_lowercase().as_str());
}

// Get the token file from memory
fn load_token() -> String {
    let mut token = String::new();
    let _ = File::open("token").expect("No token file").read_to_string(&mut token);
    token
}

// Login to Discord and connect
fn login(listeners: Arc<Mutex<Vec<(ChannelId, String)>>>) -> Client<Handler> {
    Client::new(load_token().trim(), Handler{listeners: listeners})
}

fn check_for(food: String) -> String {
    let mut places: Vec<String> = vec![];

    for dining_common in vec![DiningCommon::Berk,
                              DiningCommon::Hamp,
                              DiningCommon::Frank,
                              DiningCommon::Worcester] {
        let meals = match dining_common.clone() {
            DiningCommon::Berk => vec![Meal::Lunch, Meal::Dinner, Meal::LateNight],
            DiningCommon::Hamp => vec![Meal::Breakfast, Meal::Lunch, Meal::Dinner],
            DiningCommon::Frank => vec![Meal::Breakfast, Meal::Lunch, Meal::Dinner],
            DiningCommon::Worcester => {
                vec![Meal::Breakfast, Meal::Lunch, Meal::Dinner, Meal::LateNight]
            }
        };
        for meal in meals {
            if is_on_menu(dining_common, meal, &food) {
                places.push(format!("{:?} {:?}", dining_common.clone(), meal.clone()).to_string());
            }
        }
    }

    match places.len() {
        0 => format!("{} not found", food).to_string(),
        _ => format!("{}: {}", food, places.join(", ")).to_string(),
    }
}

fn get_guilds() -> Vec<String> {
    let cache = serenity::CACHE.read().unwrap();
    let guilds = cache.all_guilds();
    guilds.into_iter().map(|guild| {guild.get().unwrap().name}).collect()
}

struct Handler {
    listeners: Arc<Mutex<Vec<(serenity::model::ChannelId, String)>>>
}

impl EventHandler for Handler {
    fn on_message(&self, _ctx: Context, message: Message) {
        let listeners = self.listeners.clone();

        println!("{}: {} says: {}", message.author.name, message.author.id, message.content);

        //We don't want it to respond to other bots or itself!
        if message.author.bot {
            return;
        }

        let is_owner: bool = message.author.id == 90927967651262464;

        if message.content == "!events" {

            let events = get_events();

            // Intro
            let _ = message.channel_id.say("Today's events are:".to_string());

            let _ = events.iter()
                .map(|event| message.channel_id.say(event.format().to_string()));

        } else if message.content.starts_with("!menu ") {
            let item: String = message.content[6..].to_string();

            let _ = message.channel_id.say(format!("Checking for {}", item).to_string());
            let _ = message.channel_id.say(check_for(item));
        } else if message.content.starts_with("!register ") {
            let item: String = message.content[10..].to_string();
            listeners.lock().unwrap().deref_mut().push((message.channel_id, item.clone()));
            save_listeners(listeners.lock().unwrap().deref_mut());
            let _ = message.channel_id.say(format!("Will check for {}", item).to_string());
        } else if message.content == "!help" {
            let _ = message.channel_id.say("UMass Bot help:");
            let _ = message.channel_id.say("```!menu [food name]     | tells you where that food is being served today```");
            let _ = message.channel_id.say("```!register [food name] | schedules it to tell you each day where that food is being served that day```");
        } else if message.content.starts_with("!guilds") && is_owner {
            let _ = message.channel_id.say(format!("Guilds: {}", get_guilds().join(", ")));
        }
    }
}

fn read_listeners() -> Vec<(serenity::model::ChannelId, String)> {
    let mut listeners_string: String = String::new();
    let _ = OpenOptions::new()
        .read(true)
        .write(true)
        .create(true)
        .open("listeners.txt")
        .expect("No listeners file")
        .read_to_string(&mut listeners_string);

    let mut listeners: Vec<(serenity::model::ChannelId, String)> = vec![];

    for line in listeners_string.split('\n') {
        if line == "" {
            continue;
        }
        let sections: Vec<&str> = line.split(' ').collect();
        let id = serenity::model::ChannelId(sections[0]
            .parse::<u64>()
            .expect("Couldn't parse channel id"));
        let food: String = sections[1..].join(" ").to_string();
        listeners.push((id, food));
    }

    listeners
}

fn save_listeners(vec: &Vec<(serenity::model::ChannelId, String)>) {
    let mut listeners_string: String = String::new();
    vec.into_iter().for_each(|x| {
        let (ref id, ref food) = *x;
        listeners_string = format!("{}\n{} {}", listeners_string, id, food);
    });

    let _ = OpenOptions::new()
        .read(true)
        .write(true)
        .create(true)
        .truncate(true)
        .open("listeners.txt")
        .unwrap()
        .write_all(listeners_string.as_bytes());
}

// Technically gets 5 minutes after midnight... :)
fn get_time_till_midnight() -> std::time::Duration {
    let mut next_midnight: time::Tm = (time::now() + time::Duration::days(1)).to_local();
    next_midnight.tm_sec = 0;
    next_midnight.tm_min = 5;
    next_midnight.tm_hour = 0;

    (next_midnight - time::now()).to_std().unwrap()
}

fn main() {
    // Allow openssl crosscompiling to work
    openssl_probe::init_ssl_cert_env_vars();

    let listeners: Arc<Mutex<Vec<(ChannelId, String)>>> =
        Arc::new(Mutex::new(read_listeners()));
    let mut client = login(listeners.clone());
    println!("Connected to Discord");
    println!("Connected to servers: {}", get_guilds().join(", "));

    // Listeners loop
    let listeners_clone = listeners.clone();
    thread::spawn(move || {
        let listeners = listeners_clone;
        loop {
            thread::sleep(get_time_till_midnight());
            listeners.lock().unwrap().to_vec().into_iter().for_each(|(channel, food)| {
                let _ = channel.say(check_for(food));
            });
        }
    });

    let _ = client.start();

}
<commit_msg>Make it only log commands<commit_after>extern crate reqwest;
extern crate select;
extern crate serenity;

use select::document::Document;
use select::predicate::Class;

use serenity::client::Client;
use serenity::prelude::*;
use serenity::model::*;

// For file reading
use std::io::Read;
use std::fs::File;
use std::io::Write;
use std::fs::OpenOptions;

// For multithreading
use std::thread;
use std::sync::{Arc, Mutex};
use std::ops::DerefMut;

// Allow openssl crosscompiling to work
extern crate openssl_probe;

extern crate time;

#[derive(Debug)]
struct UMassEvent {
    title: String,
    description: String,
    date: String,
    location: Option<String>,
}

impl UMassEvent {
    fn format(&self) -> String {
        match self.location {
            // "Event_Name at Event_location: Long Description"
            Some(ref location) => format!("{} at {}:\n{}", self.title, location, self.description),
            // "Event_Name: Long Description"
            None => format!("{}:\n{}", self.title, self.description),
        }
    }
}

fn get_document(url: &str) -> Result<select::document::Document, reqwest::Error> {
    reqwest::get(url).map(|mut response| {
        // Extract the data from the http request
        let mut content = String::new();
        let _ = response.read_to_string(&mut content);
        Document::from(&*content)
    })
}

fn get_events() -> Vec<UMassEvent> {
    let document = get_document("http://www.umass.edu/events/").expect("Couldn't get the events page");

    // Parse the data into a list of events
    let events = document.find(Class("views-row"))
        .map(|node| {

            // This is really janky and relies on UMass not changing the event page html...

            let title = node.find(Class("views-field-title"))
                .next()
                .unwrap()
                .children()
                .nth(1)
                .unwrap()
                .first_child()
                .unwrap()
                .first_child()
                .unwrap()
                .text();
            let description = node.find(Class("views-field-field-short-desc"))
                .next()
                .unwrap()
                .children()
                .nth(1)
                .unwrap()
                .first_child()
                .unwrap()
                .text();
            let date = node.find(Class("event-date")).next().unwrap().text();
            let location = node.find(Class("event-location"))
                .next()
                .unwrap()
                .children()
                .nth(1)
                .map(|node| node.first_child().unwrap().text());

            // Return the event, which will then be collected into the events vector
            UMassEvent {
                title: title,
                description: description,
                date: date,
                location: location,
            }
        })
        .collect();

    events
}

#[derive(Copy)]
#[derive(Clone)]
#[derive(Debug)]
enum Meal {
    Breakfast,
    Lunch,
    Dinner,
    LateNight,
}

#[derive(Copy)]
#[derive(Clone)]
#[derive(Debug)]
enum DiningCommon {
    Berk,
    Hamp,
    Frank,
    Worcester,
}

fn get_meal_code(meal: Meal, dining_common: DiningCommon) -> String {
    match dining_common {
            DiningCommon::Worcester => {
                match meal {
                    Meal::Breakfast => "0700001",
                    Meal::Lunch => "1130001",
                    Meal::Dinner => "1630001",
                    Meal::LateNight => "2100001",
                }
            }
            DiningCommon::Frank => {
                match meal {
                    Meal::Breakfast => "0700002",
                    Meal::Lunch => "1130002",
                    Meal::Dinner => "1630002",
                    Meal::LateNight => panic!("Frank doesn't have late night"),
                }
            }
            DiningCommon::Hamp => {
                match meal {
                    Meal::Breakfast => "0700003",
                    Meal::Lunch => "1130003",
                    Meal::Dinner => "1630003",
                    Meal::LateNight => panic!("Hamp doesn't have late night"),
                }
            }
            DiningCommon::Berk => {
                match meal {
                    Meal::Breakfast => panic!("Berk doesn't have breakfast"),
                    Meal::Lunch => "1100004",
                    Meal::Dinner => "1630004",
                    Meal::LateNight => "2100004",
                }
            }
        }
        .to_string()

}

fn get_dining_common_code(dining_common: DiningCommon) -> String {
    match dining_common {
            DiningCommon::Worcester => "0",
            DiningCommon::Frank => "1",
            DiningCommon::Hamp => "2",
            DiningCommon::Berk => "3",
        }
        .to_string()
}

fn get_menu_document(dining_common: DiningCommon,
                     meal: Meal)
                     -> Result<select::document::Document, reqwest::Error> {
    let dining_common_id = get_dining_common_code(dining_common.clone());

    let time = time::now();

    let year = time.tm_year + 1900;
    let month = format!("{:02}", time.tm_mon + 1);
    let day = format!("{:02}", time.tm_mday);
    let meal = get_meal_code(meal, dining_common);

    let url: &str = &format!("https://go.umass.\
                              edu/dining/event?feed=dining-halls&id=id_{id}&calendar=dining-halls_id_{id}_event_calendar&startdate={day}-{month}-{year}&event={year}{month}{day}T{meal}%7C{year}{month}{day}T000000&calendarMode=day",
                             id = dining_common_id,
                             year = year,
                             month = month,
                             day = day,
                             meal = meal);

    println!("{}", url);

    get_document(url)

}

fn is_on_menu(dining_common: DiningCommon, meal: Meal, item: &str) -> bool {
    let nodes: Vec<String> = get_menu_document(dining_common, meal)
        .expect("Couldn't get the menu page")
        .find(Class("kgo_web_content"))
        .map(|node| node.text())
        .collect();

    let text: String = nodes.join(" ");

    return text.to_lowercase().as_str().contains(item.to_lowercase().as_str());
}

// Get the token file from memory
fn load_token() -> String {
    let mut token = String::new();
    let _ = File::open("token").expect("No token file").read_to_string(&mut token);
    token
}

// Login to Discord and connect
fn login(listeners: Arc<Mutex<Vec<(ChannelId, String)>>>) -> Client<Handler> {
    Client::new(load_token().trim(), Handler{listeners: listeners})
}

fn check_for(food: String) -> String {
    let mut places: Vec<String> = vec![];

    for dining_common in vec![DiningCommon::Berk,
                              DiningCommon::Hamp,
                              DiningCommon::Frank,
                              DiningCommon::Worcester] {
        let meals = match dining_common.clone() {
            DiningCommon::Berk => vec![Meal::Lunch, Meal::Dinner, Meal::LateNight],
            DiningCommon::Hamp => vec![Meal::Breakfast, Meal::Lunch, Meal::Dinner],
            DiningCommon::Frank => vec![Meal::Breakfast, Meal::Lunch, Meal::Dinner],
            DiningCommon::Worcester => {
                vec![Meal::Breakfast, Meal::Lunch, Meal::Dinner, Meal::LateNight]
            }
        };
        for meal in meals {
            if is_on_menu(dining_common, meal, &food) {
                places.push(format!("{:?} {:?}", dining_common.clone(), meal.clone()).to_string());
            }
        }
    }

    match places.len() {
        0 => format!("{} not found", food).to_string(),
        _ => format!("{}: {}", food, places.join(", ")).to_string(),
    }
}

fn get_guilds() -> Vec<String> {
    let cache = serenity::CACHE.read().unwrap();
    let guilds = cache.all_guilds();
    guilds.into_iter().map(|guild| guild.get().unwrap().name).collect()
}

struct Handler {
    listeners: Arc<Mutex<Vec<(serenity::model::ChannelId, String)>>>,
}

impl EventHandler for Handler {
    fn on_message(&self, _ctx: Context, message: Message) {
        let listeners = self.listeners.clone();

        if !message.content.starts_with("!") {
            //It's not a command, so we don't care about it
            return;
        }

        if message.author.bot {
            // We don't want it to respond to other bots or itself!
            return;
        }

        println!("{}: {} says: {}",
                 message.author.name,
                 message.author.id,
                 message.content);

        let is_owner: bool = message.author.id == 90927967651262464;

        if message.content == "!events" {

            let events = get_events();

            // Intro
            let _ = message.channel_id.say("Today's events are:".to_string());

            let _ = events.iter()
                .map(|event| message.channel_id.say(event.format().to_string()));

        } else if message.content.starts_with("!menu ") {
            let item: String = message.content[6..].to_string();

            let _ = message.channel_id.say(format!("Checking for {}", item).to_string());
            let _ = message.channel_id.say(check_for(item));
        } else if message.content.starts_with("!register ") {
            let item: String = message.content[10..].to_string();
            listeners.lock().unwrap().deref_mut().push((message.channel_id, item.clone()));
            save_listeners(listeners.lock().unwrap().deref_mut());
            let _ = message.channel_id.say(format!("Will check for {}", item).to_string());
        } else if message.content == "!help" {
            let _ = message.channel_id.say("UMass Bot help:");
            let _ = message.channel_id.say("```!menu [food name]     | tells you where that food is being served today```");
            let _ = message.channel_id.say("```!register [food name] | schedules it to tell you each day where that food is being served that day```");
        } else if message.content.starts_with("!guilds") && is_owner {
            let _ = message.channel_id.say(format!("Guilds: {}", get_guilds().join(", ")));
        }
    }
}

fn read_listeners() -> Vec<(serenity::model::ChannelId, String)> {
    let mut listeners_string: String = String::new();
    let _ = OpenOptions::new()
        .read(true)
        .write(true)
        .create(true)
        .open("listeners.txt")
        .expect("No listeners file")
        .read_to_string(&mut listeners_string);

    let mut listeners: Vec<(serenity::model::ChannelId, String)> = vec![];

    for line in listeners_string.split('\n') {
        if line == "" {
            continue;
        }
        let sections: Vec<&str> = line.split(' ').collect();
        let id = serenity::model::ChannelId(sections[0]
            .parse::<u64>()
            .expect("Couldn't parse channel id"));
        let food: String = sections[1..].join(" ").to_string();
        listeners.push((id, food));
    }

    listeners
}

fn save_listeners(vec: &Vec<(serenity::model::ChannelId, String)>) {
    let mut listeners_string: String = String::new();
    vec.into_iter().for_each(|x| {
        let (ref id, ref food) = *x;
        listeners_string = format!("{}\n{} {}", listeners_string, id, food);
    });

    let _ = OpenOptions::new()
        .read(true)
        .write(true)
        .create(true)
        .truncate(true)
        .open("listeners.txt")
        .unwrap()
        .write_all(listeners_string.as_bytes());
}

// Technically gets 5 minutes after midnight... :)
fn get_time_till_midnight() -> std::time::Duration {
    let mut next_midnight: time::Tm = (time::now() + time::Duration::days(1)).to_local();
    next_midnight.tm_sec = 0;
    next_midnight.tm_min = 5;
    next_midnight.tm_hour = 0;

    (next_midnight - time::now()).to_std().unwrap()
}

fn main() {
    // Allow openssl crosscompiling to work
    openssl_probe::init_ssl_cert_env_vars();

    let listeners: Arc<Mutex<Vec<(ChannelId, String)>>> =
        Arc::new(Mutex::new(read_listeners()));
    let mut client = login(listeners.clone());
    println!("Connected to Discord");
    println!("Connected to servers: {}", get_guilds().join(", "));

    // Listeners loop
    let listeners_clone = listeners.clone();
    thread::spawn(move || {
        let listeners = listeners_clone;
        loop {
            thread::sleep(get_time_till_midnight());
            listeners.lock().unwrap().to_vec().into_iter().for_each(|(channel, food)| {
                let _ = channel.say(check_for(food));
            });
        }
    });

    let _ = client.start();

}
<|endoftext|>


<commit_before>// Copyright (c) Björn Edström <be@bjrn.se> 2015.
// See LICENSE for details.

extern crate chrono;
extern crate crypto;
extern crate getopts;
extern crate num;
extern crate rustc_serialize;

mod timmy;

use chrono::datetime::DateTime;
use chrono::offset::utc::UTC;
use crypto::digest::Digest;
use crypto::sha2::Sha256;
use getopts::Options;
use num::bigint::BigUint;
use rustc_serialize::base64::{STANDARD, ToBase64, FromBase64};
use rustc_serialize::json::{self};
use std::env;
use std::fs::File;
use std::io::Read;
use std::io::Write;
use std::io;
use std::net::TcpStream;

use timmy::rsa::*;
use timmy::util::*;
use timmy::tls::*;
use timmy::x509::*;

macro_rules! println_stderr(
    ($($arg:tt)*) => (
        match writeln!(&mut ::std::io::stderr(), $($arg)* ) {
            Ok(_) => {},
            Err(x) => panic!("Unable to write to stderr: {}", x),
        }
    )
);

#[derive(RustcDecodable, RustcEncodable, Debug)]
struct JsonOutput {
    blob: String,
    certificates: Vec<String>,
    signature: String
}

fn verify(blob: &Vec<u8>, cert0: &Vec<u8>, signature: &Vec<u8>) -> isize {
    // Parse certificates
    let cert = X509Certificate::new(cert0.clone());
    let parsed_cert = cert.parse().unwrap(); // XXX unwrap

    // Check if certificate expired.
    let utc_now: DateTime<UTC> = UTC::now();
    let cert_still_valid = utc_now >= parsed_cert.validity.0 && utc_now <= parsed_cert.validity.1;
    if !cert_still_valid {
        println_stderr!("ERROR! Signature verification FAILURE: Certificate has expired.");
        return 1;
    }

    match parsed_cert.key {
        PublicKey::RSA(rsa_n, rsa_e) => {

            // Perform RSA signature operation
            let signature_int = BigUint::from_bytes_be(&signature);
            let sig_op = rsa_encrypt(&signature_int, &rsa_e,  &rsa_n);

            // Fix up PKCS1.5 blob
            let mut raw_pkcs1_ = sig_op.to_bytes_be();
            raw_pkcs1_.insert(0, 0x00);
            let raw_pkcs1 = raw_pkcs1_;

            // Construct our own PKCS 1.5 blob to compare against
            let tls_hash = weird_tls_hash(&blob);
            let constructed_pkcs1 = make_pkcs1_sig_padding(&rsa_n, &tls_hash);

            let valid_signature = crypto_compare(&constructed_pkcs1, &raw_pkcs1);

            if valid_signature {
                // Signature is valid, parse out fields from blob.
                let mut tls = TLSHandshake::new();
                tls.client_random = blob[0..32].iter().cloned().collect();
                tls.server_random = blob[32..64].iter().cloned().collect();

                // Validate signed timestamp against certificate validity period.
                let unix_timestamp = tls.get_unix_timestamp();
                let ts = timestamp_to_datetime(unix_timestamp);
                let valid_dates = ts >= parsed_cert.validity.0 && ts <= parsed_cert.validity.1;

                if valid_dates {
                    println!("Signature verification SUCCESS.");
                    println!("Warning! Signature only verified against first X509 certificate.");
                    println!("Please verify yourself that the certificate chain is valid.");
                    println!("");
                    println!("{} Signed SHA-256 {} at {:?} (Unix Timestamp: {})",
                             parsed_cert.subject,
                             to_hex_string(&tls.client_random), ts, unix_timestamp);
                } else {
                    println!("ERROR! Signature verification FAILURE: Invalid timestamp.");
                    return 1;
                }

            } else {
                println!("ERROR! Signature verification FAILURE.");
                return 1;
            }
        }
    }

    0
}

fn sign(server: &String, port: &u16, hash_buf: &[u8; 32]) -> isize {
    // Construct our special ClientHello message and send it to the server.
    let conn_str = format!("{}:{}", server, port);
    let mut tcpconn = TcpStream::connect(&conn_str[..]).unwrap();

    let mut ch = SimpleBinaryWriter::new();
    create_special_client_hello(&mut ch, &hash_buf);

    match tcpconn.write(&ch.buf) {
        Err(_) => {
            println_stderr!("ERROR! Failed to write to server.");
            return 1;
        }
        Ok(_) => {}
    }

    // Parse the response.
    let mut pars = BinaryParser::new(&mut tcpconn);
    let mut tls = TLSHandshake::new();

    tls.client_random.extend(hash_buf[0..32].iter());

    tls.parse_server_hello(&mut pars);
    tls.parse_certificates(&mut pars);
    tls.parse_server_key_exchange(&mut pars);

    // Extract timestamp and perform checks on it.
    let unix_timestamp = tls.get_unix_timestamp();
    let ts = timestamp_to_datetime(unix_timestamp);

    let utc_now: DateTime<UTC> = UTC::now();
    let utc_now_ts = utc_now.timestamp() as u32;

    if unix_timestamp < utc_now_ts - 3600 || unix_timestamp > utc_now_ts + 3600 {
        println_stderr!("ERROR! Server responded with invalid time! Aborting.");
        return 1;
    }

    // Output banner and result JSON blob.
    println_stderr!("{} signed SHA-256 {} at {:?} (Unix Timestamp: {})",
                    server, to_hex_string(&hash_buf[0..32].iter().cloned().collect()), ts, unix_timestamp);

    let json_output = JsonOutput {
        blob: tls.signed_blob.to_base64(STANDARD),
        signature: tls.signature.to_base64(STANDARD),
        certificates: tls.certs.iter().map(
            |c| c.buf.to_base64(STANDARD)
            ).collect(),
    };

    println!("{}", json::encode(&json_output).unwrap());

    0
}

fn hash_content<R: Read, D: Digest>(file_handle: &mut R, hasher: &mut D) {
    let mut buf: [u8; 4096] = [0; 4096];

    loop {
        match file_handle.read(&mut buf) {
            Ok(0) => { break }
            Ok(size) => {
                hasher.input(&mut buf[0..size]);
            }
            // Makes sense?
            Err(_) => { break }
        };
    }
}

fn print_usage(program: &str, opts: Options) {
    let brief = format!("Usage: {} [options]", program);
    print!("{}", opts.usage(&brief));
}

fn main() {
    let args: Vec<String> = env::args().collect();
    let program = args[0].clone();
    let mut opts = Options::new();

    opts.optopt("v", "verify", "verify signature object", "PATH");
    opts.optopt("f", "file", "input file to sign [-]", "PATH");
    opts.optopt("s", "server", "set server for signing [www.google.com]", "HOSTNAME");
    opts.optopt("p", "port", "set port for --server [443]", "PORT");
    //opts.optopt("o", "output", "ouput file [data]", "PATH");

    opts.optflag("h", "help", "print this help menu");

    let matches = match opts.parse(&args[1..]) {
        Ok(m) => { m }
        Err(f) => { panic!(f.to_string()) }
    };

    if matches.opt_present("h") {
        print_usage(&program, opts);
        return;
    }

    // parse "output"
    /*let output = if matches.opt_present("o") {
        matches.opt_str("o").unwrap()
    } else {
        "data".to_string()
    };*/

    // parse "server"
    let server = if matches.opt_present("s") {
        matches.opt_str("s").unwrap()
    } else {
        "www.google.com".to_string()
    };

    // parse "port"
    let port: u16 = if matches.opt_present("p") {
        let port_str = matches.opt_str("p").unwrap();
        match port_str.parse() {
            Ok(p) => p,
            Err(_) => {
                print_usage(&program, opts);
                return;
            }
        }
    } else {
        443
    };

    if !matches.opt_present("v") {
        // sign

        // parse "file" and hash it
        let mut hasher = Sha256::new();

        if matches.opt_present("f") {
            let path = matches.opt_str("f").unwrap();
            let mut file_handle = match File::open(path) {
                Ok(f) => f,
                Err(s) => {
                    println!("Reading file failed: {}", s);
                    //print_usage(&program, opts);
                    return;
                }
            };

            hash_content(&mut file_handle, &mut hasher);
        } else {
            hash_content(&mut io::stdin(), &mut hasher);
        };

        //println!("Hello, world! {} {} {:?}", server, port, hasher.result_str());

        let mut hash_buf: [u8; 32] = [0; 32];
        hasher.result(&mut hash_buf);

        sign(&server, &port, &hash_buf);
    } else {
        // verify

        let path = matches.opt_str("v").unwrap();
        let mut contents: Vec<u8> = Vec::new();
        let file_handle = File::open(path) ;
        file_handle.unwrap().read_to_end(&mut contents).unwrap();

        let filestr = String::from_utf8(contents).unwrap();

        let json_blob: JsonOutput = json::decode(&filestr).unwrap();

        //println!("{:?}", json_blob);

        let blob = json_blob.blob.from_base64().unwrap();
        let cert0 = json_blob.certificates[0].from_base64().unwrap();
        let signature = json_blob.signature.from_base64().unwrap();

        verify(&blob, &cert0, &signature);
    }
}
<commit_msg>Avoid unnecessary panic.<commit_after>// Copyright (c) Björn Edström <be@bjrn.se> 2015.
// See LICENSE for details.

extern crate chrono;
extern crate crypto;
extern crate getopts;
extern crate num;
extern crate rustc_serialize;

mod timmy;

use chrono::datetime::DateTime;
use chrono::offset::utc::UTC;
use crypto::digest::Digest;
use crypto::sha2::Sha256;
use getopts::Options;
use num::bigint::BigUint;
use rustc_serialize::base64::{STANDARD, ToBase64, FromBase64};
use rustc_serialize::json::{self};
use std::env;
use std::fs::File;
use std::io::Read;
use std::io::Write;
use std::io;
use std::net::TcpStream;

use timmy::rsa::*;
use timmy::util::*;
use timmy::tls::*;
use timmy::x509::*;

macro_rules! println_stderr(
    ($($arg:tt)*) => (
        match writeln!(&mut ::std::io::stderr(), $($arg)* ) {
            Ok(_) => {},
            Err(x) => panic!("Unable to write to stderr: {}", x),
        }
    )
);

#[derive(RustcDecodable, RustcEncodable, Debug)]
struct JsonOutput {
    blob: String,
    certificates: Vec<String>,
    signature: String
}

fn verify(blob: &Vec<u8>, cert0: &Vec<u8>, signature: &Vec<u8>) -> isize {
    // Parse certificates
    let cert = X509Certificate::new(cert0.clone());
    let parsed_cert = cert.parse().unwrap(); // XXX unwrap

    // Check if certificate expired.
    let utc_now: DateTime<UTC> = UTC::now();
    let cert_still_valid = utc_now >= parsed_cert.validity.0 && utc_now <= parsed_cert.validity.1;
    if !cert_still_valid {
        println_stderr!("ERROR! Signature verification FAILURE: Certificate has expired.");
        return 1;
    }

    match parsed_cert.key {
        PublicKey::RSA(rsa_n, rsa_e) => {

            // Perform RSA signature operation
            let signature_int = BigUint::from_bytes_be(&signature);
            let sig_op = rsa_encrypt(&signature_int, &rsa_e,  &rsa_n);

            // Fix up PKCS1.5 blob
            let mut raw_pkcs1_ = sig_op.to_bytes_be();
            raw_pkcs1_.insert(0, 0x00);
            let raw_pkcs1 = raw_pkcs1_;

            // Construct our own PKCS 1.5 blob to compare against
            let tls_hash = weird_tls_hash(&blob);
            let constructed_pkcs1 = make_pkcs1_sig_padding(&rsa_n, &tls_hash);

            let valid_signature = crypto_compare(&constructed_pkcs1, &raw_pkcs1);

            if valid_signature {
                // Signature is valid, parse out fields from blob.
                let mut tls = TLSHandshake::new();
                tls.client_random = blob[0..32].iter().cloned().collect();
                tls.server_random = blob[32..64].iter().cloned().collect();

                // Validate signed timestamp against certificate validity period.
                let unix_timestamp = tls.get_unix_timestamp();
                let ts = timestamp_to_datetime(unix_timestamp);
                let valid_dates = ts >= parsed_cert.validity.0 && ts <= parsed_cert.validity.1;

                if valid_dates {
                    println!("Signature verification SUCCESS.");
                    println!("Warning! Signature only verified against first X509 certificate.");
                    println!("Please verify yourself that the certificate chain is valid.");
                    println!("");
                    println!("{} Signed SHA-256 {} at {:?} (Unix Timestamp: {})",
                             parsed_cert.subject,
                             to_hex_string(&tls.client_random), ts, unix_timestamp);
                } else {
                    println!("ERROR! Signature verification FAILURE: Invalid timestamp.");
                    return 1;
                }

            } else {
                println!("ERROR! Signature verification FAILURE.");
                return 1;
            }
        }
    }

    0
}

fn sign(server: &String, port: &u16, hash_buf: &[u8; 32]) -> isize {
    // Construct our special ClientHello message and send it to the server.
    let conn_str = format!("{}:{}", server, port);
    let mut tcpconn = TcpStream::connect(&conn_str[..]).unwrap();

    let mut ch = SimpleBinaryWriter::new();
    create_special_client_hello(&mut ch, &hash_buf);

    match tcpconn.write(&ch.buf) {
        Err(_) => {
            println_stderr!("ERROR! Failed to write to server.");
            return 1;
        }
        Ok(_) => {}
    }

    // Parse the response.
    let mut pars = BinaryParser::new(&mut tcpconn);
    let mut tls = TLSHandshake::new();

    tls.client_random.extend(hash_buf[0..32].iter());

    tls.parse_server_hello(&mut pars);
    tls.parse_certificates(&mut pars);
    tls.parse_server_key_exchange(&mut pars);

    // Extract timestamp and perform checks on it.
    let unix_timestamp = tls.get_unix_timestamp();
    let ts = timestamp_to_datetime(unix_timestamp);

    let utc_now: DateTime<UTC> = UTC::now();
    let utc_now_ts = utc_now.timestamp() as u32;

    if unix_timestamp < utc_now_ts - 3600 || unix_timestamp > utc_now_ts + 3600 {
        println_stderr!("ERROR! Server responded with invalid time! Aborting.");
        return 1;
    }

    // Output banner and result JSON blob.
    println_stderr!("{} signed SHA-256 {} at {:?} (Unix Timestamp: {})",
                    server, to_hex_string(&hash_buf[0..32].iter().cloned().collect()), ts, unix_timestamp);

    let json_output = JsonOutput {
        blob: tls.signed_blob.to_base64(STANDARD),
        signature: tls.signature.to_base64(STANDARD),
        certificates: tls.certs.iter().map(
            |c| c.buf.to_base64(STANDARD)
            ).collect(),
    };

    println!("{}", json::encode(&json_output).unwrap());

    0
}

fn hash_content<R: Read, D: Digest>(file_handle: &mut R, hasher: &mut D) {
    let mut buf: [u8; 4096] = [0; 4096];

    loop {
        match file_handle.read(&mut buf) {
            Ok(0) => { break }
            Ok(size) => {
                hasher.input(&mut buf[0..size]);
            }
            // Makes sense?
            Err(_) => { break }
        };
    }
}

fn print_usage(program: &str, opts: Options) {
    let brief = format!("Usage: {} [options]", program);
    print!("{}", opts.usage(&brief));
}

fn main() {
    let args: Vec<String> = env::args().collect();
    let program = args[0].clone();
    let mut opts = Options::new();

    opts.optopt("v", "verify", "verify signature object", "PATH");
    opts.optopt("f", "file", "input file to sign [-]", "PATH");
    opts.optopt("s", "server", "set server for signing [www.google.com]", "HOSTNAME");
    opts.optopt("p", "port", "set port for --server [443]", "PORT");

    opts.optflag("h", "help", "print this help menu");

    let parse_result = opts.parse(&args[1..]);

    if let Err(f) = parse_result {
        println_stderr!("ERROR: {}", f.to_string());
        return;
    }

    let matches = parse_result.unwrap();

    if matches.opt_present("h") {
        print_usage(&program, opts);
        return;
    }

    // parse "server"
    let server = if matches.opt_present("s") {
        matches.opt_str("s").unwrap()
    } else {
        "www.google.com".to_string()
    };

    // parse "port"
    let port: u16 = if matches.opt_present("p") {
        let port_str = matches.opt_str("p").unwrap();
        match port_str.parse() {
            Ok(p) => p,
            Err(_) => {
                print_usage(&program, opts);
                return;
            }
        }
    } else {
        443
    };

    if !matches.opt_present("v") {
        // sign

        // parse "file" and hash it
        let mut hasher = Sha256::new();

        if matches.opt_present("f") {
            let path = matches.opt_str("f").unwrap();
            let mut file_handle = match File::open(path) {
                Ok(f) => f,
                Err(s) => {
                    println!("Reading file failed: {}", s);
                    //print_usage(&program, opts);
                    return;
                }
            };

            hash_content(&mut file_handle, &mut hasher);
        } else {
            hash_content(&mut io::stdin(), &mut hasher);
        };

        let mut hash_buf: [u8; 32] = [0; 32];
        hasher.result(&mut hash_buf);

        sign(&server, &port, &hash_buf);
    } else {
        // verify

        let path = matches.opt_str("v").unwrap();
        let mut contents: Vec<u8> = Vec::new();
        let file_handle = File::open(path) ;
        file_handle.unwrap().read_to_end(&mut contents).unwrap();

        let filestr = String::from_utf8(contents).unwrap();

        let json_blob: JsonOutput = json::decode(&filestr).unwrap();

        let blob = json_blob.blob.from_base64().unwrap();
        let cert0 = json_blob.certificates[0].from_base64().unwrap();
        let signature = json_blob.signature.from_base64().unwrap();

        verify(&blob, &cert0, &signature);
    }
}
<|endoftext|>


<commit_before>use std::io::fs::File;

pub const NAME: i8 = 0;
pub const STATUS: i8 = 1;
pub const CURRENT: i8 = 3;
pub const MAXIMUM: i8 = 4;
pub const SCORE: i8 = 6;

struct Database {
    name: String,
    status: String,
    current: i32,
    maximum: i32,
    score: i8
}

fn tokenize( data: & str, delimeters: & str ) -> Vec< String >  {
    let mut token: Vec< String > = Vec::new();
    let mut prev = 0us;
    let mut next = 0us;
    let mut comma = false;

    for i in data.chars() {
        comma = match i {
            '"' => !comma,
            _ => comma
        };
        if !comma {
            for j in delimeters.chars() {
                if i == j {
                    if next - prev >= 1us {
                        token.push( data.slice_chars( prev, next ).to_string() );
                        prev = next + 1us;
                        break;
                    }
                    prev = next + 1us;
                }
            }
        }
        next += 1us;
    }
    // add last token
    if next - prev >= 1us {
        token.push( data.slice_chars( prev, next ).to_string() );
    }
    return token;
}

fn print_item( item: & Database ) {
    println!( "<add>: '{}', status: {}, progress: {} / {}, score: {}",
              item.name, item.status, item.current, item.maximum, item.score );
}

fn main() {
    let path = Path::new( "anime-list" );
    let display = path.display();
    let mut file = match File::open( &path ) {
        Ok( file ) => file,
        Err( io_error ) => {
            panic!( io_error.desc );
        }
    };
    let mut anime: Vec< Database > = Vec::new();
    match file.read_to_string() {
        Ok( string ) => {
            let token = tokenize( string.as_slice(), " /\n" );
            let mut item = Database {
                name: "".to_string(),
                status: "plan".to_string(),
                current: 0,
                maximum: 0,
                score: 0
            };
            let mut counter = NAME;
            for element in token.iter() {
                match element.slice_chars( 0, 1 ) {
                    "\"" => {
                        counter = NAME;
                        if item.name.len() > 0 {
                            print_item( &item );
                            anime.push( Database {
                                name: item.name.to_string(),
                                status: item.status.to_string(),
                                current: item.current,
                                maximum: item.maximum,
                                score: item.score
                            });
                        }
                    },
                    _ => {
                        counter += 1;
                    }
                }
                match counter {
                    NAME => item.name  = element.slice_chars( 1, element.len() - 1 ).to_string(),
                    STATUS => item.status = element.to_string(),
                    CURRENT => item.current = element.parse::<i32>().unwrap(),
                    MAXIMUM => item.maximum = element.parse::<i32>().unwrap(),
                    SCORE => item.score = element.parse::<i8>().unwrap(),
                    _ => {}
                };
            }
        },
        Err( why ) => panic!( "couldn't read '{}': {}", display, why.desc )
    };
}<commit_msg>[add] enum 4 status; [upd] database print; [fix] push to database<commit_after>use std::io::fs::File;
use std::clone;
use std::fmt;

pub const NAME: i8 = 0;
pub const STATUS: i8 = 1;
pub const CURRENT: i8 = 3;
pub const MAXIMUM: i8 = 4;
pub const SCORE: i8 = 6;

#[derive(Clone)]
enum ListStatus {
    Error,
    Complete,
    Drop,
    Plan,
    Watch,
    Hold,
}

#[derive(Clone)]
struct Database {
    name: String,
    status: ListStatus,
    current: i32,
    maximum: i32,
    score: u8
}

fn get_status( data: & str ) -> ListStatus {
    match data {
        "complete" => ListStatus::Complete,
        "drop" => ListStatus::Drop,
        "plan" => ListStatus::Plan,
        "watch" => ListStatus::Watch,
        "hold" => ListStatus::Hold,
        _ => ListStatus::Error
    }
}

impl fmt::String for ListStatus {
    fn fmt( &self, f: &mut fmt::Formatter ) -> fmt::Result {
        match *self {
            ListStatus::Complete => f.write_str( "complete" ),
            ListStatus::Drop => f.write_str( "drop" ),
            ListStatus::Plan => f.write_str( "plan" ),
            ListStatus::Watch => f.write_str( "watch" ),
            ListStatus::Hold => f.write_str( "hold" ),
            ListStatus::Error => f.write_str( "<error>" )
        }
    }
}

impl fmt::String for Database {
    fn fmt( &self, f: &mut fmt::Formatter ) -> fmt::Result {
        write!( f, "'{:>16}', status: {:>8}, progress: {:>2} / {:>2}, score: {}",
                self.name, self.status.to_string(), self.current, self.maximum, self.score )
    }
}

fn tokenize( data: & str, delimeters: & str ) -> Vec< String >  {
    let mut token: Vec< String > = Vec::new();
    let mut prev = 0us;
    let mut next = 0us;
    let mut comma = false;

    for i in data.chars() {
        comma = match i {
            '"' => !comma,
            _ => comma
        };
        if !comma {
            for j in delimeters.chars() {
                if i == j {
                    if next - prev >= 1us {
                        token.push( data.slice_chars( prev, next ).to_string() );
                        prev = next + 1us;
                        break;
                    }
                    prev = next + 1us;
                }
            }
        }
        next += 1us;
    }
    // add last token
    if next - prev >= 1us {
        token.push( data.slice_chars( prev, next ).to_string() );
    }
    return token;
}

fn main() {
    let path = Path::new( "anime-list" );
    let display = path.display();
    let mut file = match File::open( &path ) {
        Ok( file ) => file,
        Err( io_error ) => {
            panic!( io_error.desc );
        }
    };
    let mut anime: Vec< Database > = Vec::new();
    match file.read_to_string() {
        Ok( string ) => {
            let token = tokenize( string.as_slice(), " /\n" );
            let mut item = Database {
                name: "".to_string(),
                status: ListStatus::Plan,
                current: 0,
                maximum: 0,
                score: 0
            };
            let mut counter = NAME;
            for element in token.iter() {
                match element.slice_chars( 0, 1 ) {
                    "\"" => {
                        counter = NAME;
                        if item.name.len() > 0 {
                            println!( "add: {}", item );
                            anime.push( item.clone() );
                        }
                    },
                    _ => {
                        counter += 1;
                    }
                }
                match counter {
                    NAME => item.name = element.slice_chars( 1, element.len() - 1 ).to_string(),
                    STATUS => item.status = get_status( element.as_slice() ),
                    CURRENT => item.current = element.parse::<i32>().unwrap_or( 0 ),
                    MAXIMUM => item.maximum = element.parse::<i32>().unwrap_or( 0 ),
                    SCORE => item.score = element.parse::<u8>().unwrap_or( 0 ),
                    _ => {}
                };
            }
            // add last element
            println!( "add: {}", item );
            anime.push( item.clone() );
        },
        Err( why ) => panic!( "couldn't read '{}': {}", display, why.desc )
    };
}<|endoftext|>