<commit_before>extern crate ftp;
extern crate regex;
extern crate openssl;

use std::env;
use std::fs;
use std::fs::File;
use std::io::prelude::*;
use std::fs::OpenOptions;
use std::io::BufWriter;
use ftp::FtpStream;
// use openssl::ssl::*;

struct DownloadableFile {
    client_dir: String,
    server_dir: String,
    filename: String,
    clientfile_namescheme: String,
}

struct DownRequest {
    host: String,
    user: String,
    pass: String,
    remote_files: Vec<DownloadableFile>,
}

/// Makes a directory specified
fn mk_dir(d: &str) -> std::io::Result<()> {
    try!(fs::create_dir_all(d));
    Ok(())
}

/// Breaks down the config given a String of the config file's contents
fn break_conf(buff: &mut String) -> Option<DownRequest> {

    /// Find line and get the data from it, if there isn't data to get, None is returned
    fn break_line(input: &mut String, line: &str) -> Option<String> {
        if input.contains(line) {
            let loc = input.find(line).unwrap().checked_add(line.len()).unwrap();
            let temp = &input[loc..];

            let loc = temp.find(";").unwrap();
            Some(temp[..loc].trim().to_string())
        } else {
            None
        }
    }
    // Make sure we won't get an unexpected None
    if buff.contains("host:") && buff.contains("user:") && buff.contains("password:") &&
       buff.contains("remoteFiles:") {

        /// Breaks down the remoteFile: line specificly since it's kinda special in layout
        fn break_remotefile(input: &mut String) -> Option<Vec<DownloadableFile>> {

            // Trim string down to what we need
            let loc =
                input.find("remoteFiles:").unwrap().checked_add("remoteFiles:".len()).unwrap();
            let remfile = &input[loc..];
            let remfile = &remfile[..remfile.rfind(";").unwrap().checked_sub(";".len()).unwrap()];
            let remfile = &remfile[remfile.find("[").unwrap().checked_add("[".len()).unwrap()..];

            // Break it up
            let files: Vec<&str> = remfile.split(',').collect();
            let mut dlable_f: Vec<DownloadableFile> = Vec::new();

            // [LOG FUNCTION]
            println!("There are {} entries from this config to download",
                     files.len());

            for file in files {

                if file.contains("remoteDir:") && file.contains("localDir:") &&
                   file.contains("name:") {
                    let a: &mut String = &mut file.to_string();
                    dlable_f.push(DownloadableFile {
                        client_dir: break_line(a, "localDir:").unwrap(),
                        server_dir: break_line(a, "remoteDir:").unwrap(),
                        filename: break_line(a, "name:").unwrap(),
                        clientfile_namescheme: break_line(a, "nameToSaveAs:")
                            .unwrap_or(break_line(a, "name:").unwrap()),
                    });
                } else {}

            }

            if dlable_f.len() > 0 {
                Some(dlable_f)
            } else {
                // [LOG FUNCTION]
                println!("No files found to download or check on in this config. Check your \
                          config.");
                None
            }

        }

        let temp = DownRequest {
            host: break_line(buff, "host:").unwrap(),
            user: break_line(buff, "user:").unwrap(),
            pass: break_line(buff, "password:").unwrap(),
            remote_files: break_remotefile(buff).unwrap(),
        };

        Some(temp)
    } else {
        // If we would there's no point
        None
    }

}

/// Loads all configs into the folder
fn load_configs() -> std::io::Result<Vec<DownRequest>> {

    if mk_dir("./configs").is_ok() {
        println!("Made the dir needed");
    } // make directory

    let config_dir = fs::read_dir("./configs/").unwrap(); //read all files in dir, and expand them
    let mut config_files: Vec<std::fs::DirEntry> = Vec::new();

    for file in config_dir {
        config_files.push(file.unwrap());
    }

    let mut configs_fmted = Vec::new();

    for file in &config_files {
        // for all files in the directory open them to the f var., and save the contained string

        let mut f = try!(File::open(file.path()));
        let mut buff = String::new();

        try!(f.read_to_string(&mut buff));
        let temp_config = break_conf(&mut buff); // send buff off to be broken down

        if temp_config.is_some() {
            // Check to make sure we didn't get nothing.
            configs_fmted.push(temp_config.unwrap());
            println!("Config \"{}\" loaded successfully!",
                     &file.file_name().to_str().unwrap());
        } else {
            println!("Config \"{}\" couldn't be loaded.",
                     &file.file_name().to_str().unwrap());
        }

    }
    println!("There were {} configs to load!", config_files.len());
    Ok(configs_fmted)
}

/// Downloads files from a DownRequest
fn download_from_site(c: DownRequest, dr: bool) -> std::io::Result<()> {
    // TODO ftps support
    let host: String;
    let port: u16;
    if c.host.contains(":") {
        let sp: Vec<&str> = c.host.split(':').collect();
        host = sp[0].trim().to_string();
        port = sp[1].trim().parse().unwrap();

    } else {
        host = c.host;
        port = 21
    }

    println!("Downloading files from \"{}:{}\"", host, port);

    let mut fstream = FtpStream::connect((host.as_str(), port)).unwrap();

    if fstream.login(&c.user.as_str(), &c.pass.as_str()).is_ok() {

        println!("Connected to server, and logged in successfully");

        for f in c.remote_files {
            /// prints the print_progress from a usize from 0 - 100
            fn print_progress(per_done: usize) {
                let outa = per_done.checked_mul(4).unwrap().checked_div(10).unwrap();
                let mut t = 0;
                let mut pbar: String = "[".to_string();
                while t < 40 {
                    if outa < t {
                        pbar.push_str(" ")
                    } else {
                        pbar.push_str("=")
                    }

                    t = t + 1;
                }
                pbar.push_str("]");

                print!("\r{} : {}%", pbar, per_done);
            }

            let mut to_download: Vec<String> = Vec::new();
            let mut cl_names: Vec<String>;
            let _cd = fstream.cwd(&f.server_dir.as_str());

            if f.filename.contains("(*)") {
                let all_in_dir: Vec<String> = fstream.nlst(None).unwrap();
                let spieces_to_check: Vec<&str> = f.filename.split("(*)").collect();
                let cpieces_to_check: Vec<&str> = f.clientfile_namescheme.split("(*)").collect();

                for sf in all_in_dir {

                    let mut check_match = true;

                    while check_match {
                        for p in &spieces_to_check {
                            if !sf.contains(p) {
                                check_match = false;
                            }
                        }
                        break;
                    }
                    if check_match {
                        to_download.push(sf);
                    }
                }

                if f.filename.contains(f.clientfile_namescheme.as_str()) &&
                   f.filename.len() == f.clientfile_namescheme.len() {
                    cl_names = to_download.clone();
                } else {
                    cl_names = Vec::new();

                    if spieces_to_check.len() == cpieces_to_check.len() {
                        for n in &to_download {

                            let mut inv_pieces: Vec<&str> = Vec::new();
                            let mut cfn: String = "".to_string();
                            inv_pieces.push(n.as_str());
                            for piece in &spieces_to_check {
                                let mut temp: Vec<&str> = Vec::new();
                                for a in inv_pieces {
                                    if a.contains(piece) {
                                        let t: Vec<&str> = a.split(piece).collect();
                                        for tt in t {
                                            temp.push(tt);
                                        }
                                    } else {
                                        temp.push(a);
                                    }
                                }
                                inv_pieces = temp;
                            }

                            for x in 0..inv_pieces.len() {
                                if x > 0 {
                                    cfn.push_str(cpieces_to_check[x - 1])
                                }
                                cfn.push_str(inv_pieces[x]);
                            }

                            cl_names.push(cfn);
                        }
                    } else {
                        println!("Can't download files. The client and server name must have the \
                                  same number of (*)!");
                    }
                }

                println!("{} files matching \"{}\"", &to_download.len(), &f.filename);

            } else {
                cl_names = Vec::new();
                cl_names.push(f.clientfile_namescheme);
                to_download.push(f.filename);
            }

            for fz in 0..cl_names.len() {
                let dir = &f.client_dir.as_str().clone();
                let _md = mk_dir(dir); // Make Client dir

                /// Checks to see if the file is downloaded
                /// Takes a local file with it's path and the remote filee's byte size in usize
                fn is_downloaded(fileandpath: &String, rsz: &usize) -> Option<bool> {
                    let mdata = fs::metadata(fileandpath);
                    if mdata.is_ok() {

                        let lsz = mdata.unwrap().len() as usize;
                        if &lsz == rsz { Some(true) } else { Some(false) }
                    } else {
                        None
                    }
                }
                if dr == false {
                    let rsize = fstream.size(&to_download[fz].as_str()).unwrap();

                    let mut fnm = "".to_string();
                    fnm.push_str(dir);
                    fnm.push_str("/");
                    fnm.push_str(&cl_names[fz].as_str());

                    println!("Downloading \"{}\"", to_download[fz]);

                    if !is_downloaded(&fnm, &rsize.unwrap()).unwrap_or(false) {

                        // Make local file
                        let _tt = fstream.transfer_type(::ftp::types::FileType::Binary);

                        let _result = fstream.retr(&to_download[fz].as_str(), |stream| {
                            let mut buf = [0; 4096];
                            let mut flnm = "".to_string();
                            flnm.push_str(dir);
                            flnm.push_str("/");
                            flnm.push_str(&cl_names[fz].as_str());

                            let mut file = File::create(flnm).unwrap();
                            let mut lsize: usize = 0;

                            loop {
                                match stream.read(&mut buf) {
                                    Ok(0) => break,
                                    Ok(n) => {
                                        file.write_all(&buf[0..n]).unwrap();
                                        lsize += n;
                                    }
                                    Err(_err) => break,
                                };
                                print_progress(((lsize.checked_mul(100).unwrap())
                                    .checked_div(rsize.unwrap())
                                    .unwrap()));
                            }
                            print!("\r\n");
                            Ok(())
                        });
                    } else {
                        println!("File is already downloaded");
                    }
                } else {
                    println!("Would be downloading {} and saving as {}",
                             &to_download[fz],
                             &cl_names[fz]);
                }
            }
        }

        let _f = fstream.quit();

        Ok(())
    } else {
        let _f = fstream.quit();
        println!("User login credentals were incorrect");
        Ok(())
    }
}

// Adds a file to downlaod to a config
fn add_to_file(mut file: Vec<String>) -> std::io::Result<()> {
    // TODO Catch errors and check inputs
    if file.len() == 4 {
        let g = file[3].clone();
        file.push(g);
    }
    let mut name = env::current_exe().unwrap();
    println!("{:?}", name);
    let t = file[0].clone();
    name.push(t.as_str());
    let mut b = "".to_string();
    let mut _a = File::open(&name).unwrap();
    let __ = _a.read_to_string(&mut b);
    let mut _f = OpenOptions::new().write(true).truncate(true).open(name).unwrap();
    let mut _w = BufWriter::new(_f);
    let _s1: &str;
    let _s2: &str;

    let (_s1, _s2) = b.split_at(b.rfind("}").unwrap().checked_add("}".len()).unwrap());

    println!("Writing to config");

    let _ =
        _w.write_all(format!("{},\r\n{{\r\n  remoteDir: {};\r\n  localDir: {};\r\n  name: \
                              {};\r\n  nameToSaveAs: {};\r\n}}{}",
                             _s1,
                             file[1],
                             file[2],
                             file[3],
                             file[4],
                             _s2)
            .as_bytes());
    Ok(())
}

fn main() {

    let mut _should_ls = false;
    let mut dry_run = false;
    let mut should_add = false;
    let mut add_to_config: Vec<String> = Vec::new();
    let ag: Vec<String> = env::args().collect();

    for x in 0..ag.len() {
        if ag[x].contains("-dr") {
            dry_run = true;
        }
        if ag[x].contains("-af") {
            if (x + 4) < ag.len() {
                should_add = true;
                add_to_config.push(ag[x + 1].clone());
                add_to_config.push(ag[x + 2].clone());
                add_to_config.push(ag[x + 3].clone());
                add_to_config.push(ag[x + 4].clone());
                if x + 5 < ag.len() {
                    add_to_config.push(ag[x + 5].clone());
                }
            } else {
                println!("Config name, client and server directory, and server filename must be \
                          supplied");
            }
        }
    }

    if should_add {
        let _ = add_to_file(add_to_config);
    }

    // TODO ls command
    println!("\rLoading configs...");

    let configs = load_configs().unwrap();

    for con in configs {
        let _d = download_from_site(con, dry_run);
    }
    std::process::exit(0);

}
<commit_msg>Real config file fix<commit_after>extern crate ftp;
extern crate regex;
extern crate openssl;

use std::env;
use std::fs;
use std::fs::File;
use std::io::prelude::*;
use std::fs::OpenOptions;
use std::io::BufWriter;
use ftp::FtpStream;
// use openssl::ssl::*;

struct DownloadableFile {
    client_dir: String,
    server_dir: String,
    filename: String,
    clientfile_namescheme: String,
}

struct DownRequest {
    host: String,
    user: String,
    pass: String,
    remote_files: Vec<DownloadableFile>,
}

/// Makes a directory specified
fn mk_dir(d: &str) -> std::io::Result<()> {
    try!(fs::create_dir_all(d));
    Ok(())
}

/// Breaks down the config given a String of the config file's contents
fn break_conf(buff: &mut String) -> Option<DownRequest> {

    /// Find line and get the data from it, if there isn't data to get, None is returned
    fn break_line(input: &mut String, line: &str) -> Option<String> {
        if input.contains(line) {
            let loc = input.find(line).unwrap().checked_add(line.len()).unwrap();
            let temp = &input[loc..];

            let loc = temp.find(";").unwrap();
            Some(temp[..loc].trim().to_string())
        } else {
            None
        }
    }
    // Make sure we won't get an unexpected None
    if buff.contains("host:") && buff.contains("user:") && buff.contains("password:") &&
       buff.contains("remoteFiles:") {

        /// Breaks down the remoteFile: line specificly since it's kinda special in layout
        fn break_remotefile(input: &mut String) -> Option<Vec<DownloadableFile>> {

            // Trim string down to what we need
            let loc =
                input.find("remoteFiles:").unwrap().checked_add("remoteFiles:".len()).unwrap();
            let remfile = &input[loc..];
            let remfile = &remfile[..remfile.rfind(";").unwrap().checked_sub(";".len()).unwrap()];
            let remfile = &remfile[remfile.find("[").unwrap().checked_add("[".len()).unwrap()..];

            // Break it up
            let files: Vec<&str> = remfile.split(',').collect();
            let mut dlable_f: Vec<DownloadableFile> = Vec::new();

            // [LOG FUNCTION]
            println!("There are {} entries from this config to download",
                     files.len());

            for file in files {

                if file.contains("remoteDir:") && file.contains("localDir:") &&
                   file.contains("name:") {
                    let a: &mut String = &mut file.to_string();
                    dlable_f.push(DownloadableFile {
                        client_dir: break_line(a, "localDir:").unwrap(),
                        server_dir: break_line(a, "remoteDir:").unwrap(),
                        filename: break_line(a, "name:").unwrap(),
                        clientfile_namescheme: break_line(a, "nameToSaveAs:")
                            .unwrap_or(break_line(a, "name:").unwrap()),
                    });
                } else {}

            }

            if dlable_f.len() > 0 {
                Some(dlable_f)
            } else {
                // [LOG FUNCTION]
                println!("No files found to download or check on in this config. Check your \
                          config.");
                None
            }

        }

        let temp = DownRequest {
            host: break_line(buff, "host:").unwrap(),
            user: break_line(buff, "user:").unwrap(),
            pass: break_line(buff, "password:").unwrap(),
            remote_files: break_remotefile(buff).unwrap(),
        };

        Some(temp)
    } else {
        // If we would there's no point
        None
    }

}

/// Loads all configs into the folder
fn load_configs() -> std::io::Result<Vec<DownRequest>> {
    let mut conf_d = env::current_exe().unwrap();
    conf_d.push("/config/");

    if mk_dir(&conf_d.to_str().unwrap()).is_ok() {
        println!("Made the dir needed");
    } // make directory

    let config_dir = fs::read_dir(conf_d).unwrap(); //read all files in dir, and expand them
    let mut config_files: Vec<std::fs::DirEntry> = Vec::new();

    for file in config_dir {
        config_files.push(file.unwrap());
    }

    let mut configs_fmted = Vec::new();

    for file in &config_files {
        // for all files in the directory open them to the f var., and save the contained string

        let mut f = try!(File::open(file.path()));
        let mut buff = String::new();

        try!(f.read_to_string(&mut buff));
        let temp_config = break_conf(&mut buff); // send buff off to be broken down

        if temp_config.is_some() {
            // Check to make sure we didn't get nothing.
            configs_fmted.push(temp_config.unwrap());
            println!("Config \"{}\" loaded successfully!",
                     &file.file_name().to_str().unwrap());
        } else {
            println!("Config \"{}\" couldn't be loaded.",
                     &file.file_name().to_str().unwrap());
        }

    }
    println!("There were {} configs to load!", config_files.len());
    Ok(configs_fmted)
}

/// Downloads files from a DownRequest
fn download_from_site(c: DownRequest, dr: bool) -> std::io::Result<()> {
    // TODO ftps support
    let host: String;
    let port: u16;
    if c.host.contains(":") {
        let sp: Vec<&str> = c.host.split(':').collect();
        host = sp[0].trim().to_string();
        port = sp[1].trim().parse().unwrap();

    } else {
        host = c.host;
        port = 21
    }

    println!("Downloading files from \"{}:{}\"", host, port);

    let mut fstream = FtpStream::connect((host.as_str(), port)).unwrap();

    if fstream.login(&c.user.as_str(), &c.pass.as_str()).is_ok() {

        println!("Connected to server, and logged in successfully");

        for f in c.remote_files {
            /// prints the print_progress from a usize from 0 - 100
            fn print_progress(per_done: usize) {
                let outa = per_done.checked_mul(4).unwrap().checked_div(10).unwrap();
                let mut t = 0;
                let mut pbar: String = "[".to_string();
                while t < 40 {
                    if outa < t {
                        pbar.push_str(" ")
                    } else {
                        pbar.push_str("=")
                    }

                    t = t + 1;
                }
                pbar.push_str("]");

                print!("\r{} : {}%", pbar, per_done);
            }

            let mut to_download: Vec<String> = Vec::new();
            let mut cl_names: Vec<String>;
            let _cd = fstream.cwd(&f.server_dir.as_str());

            if f.filename.contains("(*)") {
                let all_in_dir: Vec<String> = fstream.nlst(None).unwrap();
                let spieces_to_check: Vec<&str> = f.filename.split("(*)").collect();
                let cpieces_to_check: Vec<&str> = f.clientfile_namescheme.split("(*)").collect();

                for sf in all_in_dir {

                    let mut check_match = true;

                    while check_match {
                        for p in &spieces_to_check {
                            if !sf.contains(p) {
                                check_match = false;
                            }
                        }
                        break;
                    }
                    if check_match {
                        to_download.push(sf);
                    }
                }

                if f.filename.contains(f.clientfile_namescheme.as_str()) &&
                   f.filename.len() == f.clientfile_namescheme.len() {
                    cl_names = to_download.clone();
                } else {
                    cl_names = Vec::new();

                    if spieces_to_check.len() == cpieces_to_check.len() {
                        for n in &to_download {

                            let mut inv_pieces: Vec<&str> = Vec::new();
                            let mut cfn: String = "".to_string();
                            inv_pieces.push(n.as_str());
                            for piece in &spieces_to_check {
                                let mut temp: Vec<&str> = Vec::new();
                                for a in inv_pieces {
                                    if a.contains(piece) {
                                        let t: Vec<&str> = a.split(piece).collect();
                                        for tt in t {
                                            temp.push(tt);
                                        }
                                    } else {
                                        temp.push(a);
                                    }
                                }
                                inv_pieces = temp;
                            }

                            for x in 0..inv_pieces.len() {
                                if x > 0 {
                                    cfn.push_str(cpieces_to_check[x - 1])
                                }
                                cfn.push_str(inv_pieces[x]);
                            }

                            cl_names.push(cfn);
                        }
                    } else {
                        println!("Can't download files. The client and server name must have the \
                                  same number of (*)!");
                    }
                }

                println!("{} files matching \"{}\"", &to_download.len(), &f.filename);

            } else {
                cl_names = Vec::new();
                cl_names.push(f.clientfile_namescheme);
                to_download.push(f.filename);
            }

            for fz in 0..cl_names.len() {
                let dir = &f.client_dir.as_str().clone();
                let _md = mk_dir(dir); // Make Client dir

                /// Checks to see if the file is downloaded
                /// Takes a local file with it's path and the remote filee's byte size in usize
                fn is_downloaded(fileandpath: &String, rsz: &usize) -> Option<bool> {
                    let mdata = fs::metadata(fileandpath);
                    if mdata.is_ok() {

                        let lsz = mdata.unwrap().len() as usize;
                        if &lsz == rsz { Some(true) } else { Some(false) }
                    } else {
                        None
                    }
                }
                if dr == false {
                    let rsize = fstream.size(&to_download[fz].as_str()).unwrap();

                    let mut fnm = "".to_string();
                    fnm.push_str(dir);
                    fnm.push_str("/");
                    fnm.push_str(&cl_names[fz].as_str());

                    println!("Downloading \"{}\"", to_download[fz]);

                    if !is_downloaded(&fnm, &rsize.unwrap()).unwrap_or(false) {

                        // Make local file
                        let _tt = fstream.transfer_type(::ftp::types::FileType::Binary);

                        let _result = fstream.retr(&to_download[fz].as_str(), |stream| {
                            let mut buf = [0; 4096];
                            let mut flnm = "".to_string();
                            flnm.push_str(dir);
                            flnm.push_str("/");
                            flnm.push_str(&cl_names[fz].as_str());

                            let mut file = File::create(flnm).unwrap();
                            let mut lsize: usize = 0;

                            loop {
                                match stream.read(&mut buf) {
                                    Ok(0) => break,
                                    Ok(n) => {
                                        file.write_all(&buf[0..n]).unwrap();
                                        lsize += n;
                                    }
                                    Err(_err) => break,
                                };
                                print_progress(((lsize.checked_mul(100).unwrap())
                                    .checked_div(rsize.unwrap())
                                    .unwrap()));
                            }
                            print!("\r\n");
                            Ok(())
                        });
                    } else {
                        println!("File is already downloaded");
                    }
                } else {
                    println!("Would be downloading {} and saving as {}",
                             &to_download[fz],
                             &cl_names[fz]);
                }
            }
        }

        let _f = fstream.quit();

        Ok(())
    } else {
        let _f = fstream.quit();
        println!("User login credentals were incorrect");
        Ok(())
    }
}

// Adds a file to downlaod to a config
fn add_to_file(mut file: Vec<String>) -> std::io::Result<()> {
    // TODO Catch errors and check inputs
    if file.len() == 4 {
        let g = file[3].clone();
        file.push(g);
    }
    let mut name = env::current_exe().unwrap();
    println!("{:?}", name);
    let t = file[0].clone();
    name.push(t.as_str());
    let mut b = "".to_string();
    let mut _a = File::open(&name).unwrap();
    let __ = _a.read_to_string(&mut b);
    let mut _f = OpenOptions::new().write(true).truncate(true).open(name).unwrap();
    let mut _w = BufWriter::new(_f);
    let _s1: &str;
    let _s2: &str;

    let (_s1, _s2) = b.split_at(b.rfind("}").unwrap().checked_add("}".len()).unwrap());

    println!("Writing to config");

    let _ =
        _w.write_all(format!("{},\r\n{{\r\n  remoteDir: {};\r\n  localDir: {};\r\n  name: \
                              {};\r\n  nameToSaveAs: {};\r\n}}{}",
                             _s1,
                             file[1],
                             file[2],
                             file[3],
                             file[4],
                             _s2)
            .as_bytes());
    Ok(())
}

fn main() {

    let mut _should_ls = false;
    let mut dry_run = false;
    let mut should_add = false;
    let mut add_to_config: Vec<String> = Vec::new();
    let ag: Vec<String> = env::args().collect();

    for x in 0..ag.len() {
        if ag[x].contains("-dr") {
            dry_run = true;
        }
        if ag[x].contains("-af") {
            if (x + 4) < ag.len() {
                should_add = true;
                add_to_config.push(ag[x + 1].clone());
                add_to_config.push(ag[x + 2].clone());
                add_to_config.push(ag[x + 3].clone());
                add_to_config.push(ag[x + 4].clone());
                if x + 5 < ag.len() {
                    add_to_config.push(ag[x + 5].clone());
                }
            } else {
                println!("Config name, client and server directory, and server filename must be \
                          supplied");
            }
        }
    }

    if should_add {
        let _ = add_to_file(add_to_config);
    }

    // TODO ls command
    println!("\rLoading configs...");

    let configs = load_configs().unwrap();

    for con in configs {
        let _d = download_from_site(con, dry_run);
    }
    std::process::exit(0);

}
<|endoftext|>


<commit_before>#![feature(box_syntax)]

extern crate iron;
extern crate rand;
extern crate "rustc-serialize" as rustc_serialize;

use iron::middleware::Handler;
use iron::prelude::*;
use iron::status;
use rand::{ OsRng, Rng };
use rand::distributions::{ IndependentSample, Range };
use rand::distributions::range::SampleRange;
use rustc_serialize::json;
use std::cell::RefCell;
use std::collections::hash_map::{ HashMap, Entry };

type SrandResult = Result<QueryResult, &'static str>;

#[derive(RustcEncodable, RustcDecodable)]
struct QueryResult {
    high: u64,
    low: u64,
    avg: f64,
    rolls: Box<Vec<u64>>,
}

struct PrngHandler<R: Rng + Send + Sync + 'static> {
    map: HashMap<u64, Range<u64>>,
    rng: R,
}

impl<R: Rng + Send + Sync + 'static> PrngHandler<R> {
    fn build_query_result<'a>(&mut self, inputs: &[u64]) -> SrandResult {
        let values: Vec<_> = inputs.iter().map(|i| {
            if !self.map.contains_key(i) {
                self.map.insert(*i, Range::new(0, *i));
            }

            self.get_random(*i)
        }).collect();

        if values.len() > 0 {
            let average = {
                let (count, total) = values.iter().fold((0,0), |(count,total),b| (count + 1, total + *b));
                total as f64 / count as f64
            };

            Ok(QueryResult {
                high: *values.iter().max().unwrap_or(&0),
                low: *values.iter().min().unwrap_or(&0),
                avg: average,
                rolls: box values,
            })
        } else {
            Err("Invalid request.")
        }
    }

    fn get_random(&mut self, i: u64) -> u64 {
        self.map[i].ind_sample(&mut self.rng)
    }
}

/// It's impossible to write a stateful handler this way because (I'm assuming) the underlying
/// Iron framework is based on the assumption that handlers are stateless, or should behave
/// statelessly, which means that some manner of synchronization will have to be applied to
/// anything that modifies the PrngHandler (and therefore borrows it as mutable).
///
/// I know that there is some kind of way to do this in Rust, but I have no idea what that is,
/// so I'm kind of stymied for the evening. I believe it has something to do with Arcs or some
/// crap along those lines--basically, you have an immutable borrowed reference to something
/// that can (magically) give you (and only you) a mutable reference to itself.
///
/// Damned if I know. I really never thought I'd have to use anything of the kind.
impl<R: Rng + Send + Sync + 'static> Handler for RefCell<PrngHandler<R>> {
    fn handle(&self, req: &mut Request) -> IronResult<Response> {
        let ref_mut = match self.try_borrow_mut() {
            Some(ref_mut) => ref_mut,
            _ => return Ok(Response::with((status::Ok, "Unable to process request.")))
        };

        let inputs: Vec<u64> = req.url.path.iter().filter_map(|i| i.parse().ok()).collect();

        match ref_mut.deref_mut().build_query_result(&inputs[..]) {
            Ok(result) => Ok(Response::with((status::Ok, json::encode(&result).unwrap()))),
            Err(e) => Ok(Response::with((status::Ok, e))),
        }
    }
}

fn main() {
    let mut prng = match OsRng::new() {
        Ok(rng) => PrngHandler {
            map: HashMap::new(),
            rng: rng,
        },
        Err(e) => {
            println!("{}", e.description());
            return;
        }
    };

    match Iron::new(prng).http("localhost:2020") {
        Ok(_) => (),
        Err(_) => println!("srand failed to start"),
    }
}
<commit_msg>Use Arc/Mutex to deal with requirement for immutable handler.<commit_after>#![feature(box_syntax, io)]

extern crate iron;
extern crate rand;
extern crate "rustc-serialize" as rustc_serialize;

use iron::middleware::Handler;
use iron::prelude::*;
use iron::status;
use rand::{ OsRng, Rng };
use rand::distributions::{ IndependentSample, Range };
use rustc_serialize::json;
use std::collections::hash_map::HashMap;
use std::sync::{ Arc, Mutex };

type SrandResult = Result<QueryResult, &'static str>;

#[derive(RustcEncodable, RustcDecodable)]
struct QueryResult {
    high: u64,
    low: u64,
    avg: f64,
    rolls: Box<Vec<u64>>,
}

struct PrngHandler<R: Rng + Send + Sync + 'static> {
    map: HashMap<u64, Range<u64>>,
    rng: R,
}

impl<R: Rng + Send + Sync + 'static> PrngHandler<R> {
    fn build_query_result<'a>(&mut self, inputs: &[u64]) -> SrandResult {
        let values: Vec<_> = inputs.iter().map(|i| {
            if !self.map.contains_key(i) {
                self.map.insert(*i, Range::new(0, *i));
            }

            self.get_random(*i)
        }).collect();

        if values.len() > 0 {
            let average = {
                let (count, total) = values.iter().fold((0,0), |(count,total),b| (count + 1, total + *b));
                total as f64 / count as f64
            };

            Ok(QueryResult {
                high: *values.iter().max().unwrap_or(&0),
                low: *values.iter().min().unwrap_or(&0),
                avg: average,
                rolls: box values,
            })
        } else {
            Err("Invalid request.")
        }
    }

    fn get_random(&mut self, i: u64) -> u64 {
        self.map[i].ind_sample(&mut self.rng)
    }
}

impl<R: Rng + Send + Sync + 'static> Handler for Arc<Mutex<PrngHandler<R>>> {
    fn handle(&self, req: &mut Request) -> IronResult<Response> {
        let mut handler = match self.lock() {
            Ok(handler) => handler,
            Err(_) => return Ok(Response::with((status::Ok, "Unable to process request."))),
        };

        let inputs: Vec<u64> = req.url.path.iter().filter_map(|i| i.parse().ok()).collect();
        match handler.build_query_result(&inputs[..]) {
            Ok(result) => Ok(Response::with((status::Ok, json::encode(&result).unwrap()))),
            Err(e) => Ok(Response::with((status::Ok, e))),
        }
    }
}

fn main() {
    let prng = match OsRng::new() {
        Ok(rng) => Arc::new(Mutex::new(PrngHandler {
            map: HashMap::new(),
            rng: rng,
        })),
        Err(e) => {
            println!("{}", e.description());
            return;
        }
    };

    match Iron::new(prng).http("localhost:2020") {
        Ok(_) => (),
        Err(_) => println!("srand failed to start"),
    }
}
<|endoftext|>


<commit_before>use std::env;
use std::f32;
use std::thread::sleep;
use std::time::Duration;

extern crate getopts;
use getopts::Options;
extern crate rand;
use rand::Rng;
use rand::distributions::{IndependentSample,Range};
extern crate sacn;
use sacn::DmxSource;

const DECAY_FACTOR: f32 = 2_f32;
const LIGHT_THRESHOLD: f32 = 0.10;
const MAX_BRIGHTNESS: f32 = 75_f32;

const UNIVERSE_SIZE: usize = 510;

#[derive(Debug)]
struct Params {
    decay:          f32,
    threshold:      f32,
    max_brightness: f32,
    sleep:          Duration
}

#[derive(Debug)]
#[derive(Clone)]
struct Pixel { level: i16, age: i16 }
// impl Pixel {
//     fn clone(&self) -> Pixel {
//         let mut pixel = Pixel { level: self.level, age: self.age };
//     }
// }

struct Zone  { head: u8, body: u8, tail: u8, name: String }

fn build_params () -> Params {
    // seed default params
    let mut params = Params { decay: 2_f32, threshold: 0.1, max_brightness: 75_f32, sleep: Duration::new(1, 500_000_000) };

    // parse command line args and adjust params accordingly
    let args: Vec<String> = env::args().collect();
    let program = args[0].clone();
    let mut opts = Options::new();
    opts.optopt("d", "decay", "slow decay by this factor, defaults to 2", "DECAY");
    opts.optopt("t", "threshold", "probablity that a pixel lights up, default 0.10", "THRESHOLD");
    opts.optopt("m", "maxbrightness", "maximum brightness, 1..255, default 75", "MAX");
    opts.optopt("s", "sleep", "sleep interval in seconds, default 1.5", "SECONDS");
    let matches = match opts.parse(&args[1..]) {
        Ok(m) => { m }
        Err(f) => { panic!(f.to_string()) }
    };
    if matches.opt_present("d") {
        params.decay = matches.opt_str("d").unwrap().parse::<f32>().unwrap();
    }
    if matches.opt_present("t") {
        params.threshold = matches.opt_str("t").unwrap().parse::<f32>().unwrap();
    }
    if matches.opt_present("m") {
        params.max_brightness = matches.opt_str("m").unwrap().parse::<f32>().unwrap();
    }
    if matches.opt_present("s") {
        // take float seconds
        // convert to int seconds and nanoseconds to make Duration happy
        let seconds: f32 = matches.opt_str("s").unwrap().parse::<f32>().unwrap();
        let whole_seconds: u64 = seconds as u64;
        let nano_seconds: u32 = ((seconds - whole_seconds as f32) * 1_000_000_000_f32) as u32;
        params.sleep = Duration::new(whole_seconds, nano_seconds);
    }
    return params;
}

fn main() {
    let params = build_params();

    let dmx = DmxSource::new("Controller").unwrap();

    let zones: [Zone; 6] = [
        Zone { head: 3, body: 47, tail: 0, name: "10".to_string() },
        Zone { head: 2, body: 92, tail: 2, name: "11a".to_string() },
        Zone { head: 2, body: 92, tail: 2, name: "11b".to_string() },
        Zone { head: 2, body: 90, tail: 3, name: "12a".to_string() },
        Zone { head: 2, body: 91, tail: 3, name: "12b".to_string() },
        Zone { head: 2, body: 43, tail: 0, name: "13".to_string() }
    ];

    let mut lights: Vec<Pixel> = vec![];
    // TODO: probably a more idiomatic way to built the default state
    for zone in zones.iter() {
        for i in 1..zone.body {
            let pixel = Pixel { level: 0, age: 0 };
            lights.push(pixel);
        }
    }

    let mut rng = rand::thread_rng();
    let bright_range = Range::new(0_f32, params.max_brightness);
    let zero_to_one = Range::new(0_f32, 1_f32);

    loop {
        for light in lights.iter_mut() {
            if light.level == 0 {
                // light is currently dark
                // test to see if we want to light it
                if zero_to_one.ind_sample(&mut rng) < params.threshold {
                    // we do, so pick a random
                    light.level = bright_range.ind_sample(&mut rng) as i16;
                    light.age += 1;
                }
            } else {
                // light is lit
                // test to see if we rise or fall
                // probability of falling should go up as light ages
                if zero_to_one.ind_sample(&mut rng) > 1.0/(light.age as f32) {
                    // falling
                    light.level -= (bright_range.ind_sample(&mut rng)/params.decay) as i16;
                    light.age += 1;
                    // test to see if we bottomed out
                    if light.level <= 0 {
                        light.level = 0;
                        light.age = 0;
                    }
                } else {
                    // rising
                    light.level += (zero_to_one.ind_sample(&mut rng) * (params.max_brightness - light.level as f32)) as i16;
                    light.age += 1;
                }
            }
        }
        render(&lights, &zones, &dmx);
        sleep(params.sleep);
    }
    dmx.terminate_stream(1);
}

// debug, rendor output to console
// fn render( lights: &[Pixel], zones: &[Zone], dmx: &DmxSource ) {
//    let mut out: Vec<i16> = vec![];
//    for light in lights {
//        out.push(light.level);
//   }
//    println!("{:?}", out)
//     let mut lit: u16 = 0;
//     let mut level: i16 = 0;
//     for light in lights {
//         if light.level > 0 {
//             lit += 1;
//             level += light.level;
//         }
//     }
//     println!("{} pixels, {} lit, {} avg level", lights.len(), lit, (level as f32)/(lit as f32));
// }

// output to lighting controller via sACN
fn render( lights: &[Pixel], zones: &[Zone], dmx: &DmxSource ) {
    let mut out: Vec<u8> = vec![];
    let mut copy: Vec<Pixel> = vec![];
    copy.extend_from_slice(lights);
    let mut idx: usize = 0;
    for zone in zones {
        // null pixels at head
        for i in 1..zone.head {
            out.push(0); // Red
            out.push(0); // Green
            out.push(0); // Blue
        }
        // set via light.level in the body
        for i in 0..(zone.body - 1) {
            out.push(copy[idx].level as u8); // Red
            out.push(copy[idx].level as u8); // Green
            out.push(copy[idx].level as u8); // Blue
            idx += 1;
        }
        // null pixels at tail
        for i in 1..zone.tail {
            out.push(0); // Red
            out.push(0); // Green
            out.push(0); // Blue
        }
    }
    let mut universes = Vec::new();
    while out.len() > UNIVERSE_SIZE {
        let u = out.split_off(UNIVERSE_SIZE);
        universes.push(u);
    }
    universes.push(out);
    // println!("{:?}", universes);
    let mut universe: u16 = 1;
    while let Some(u) = universes.pop() {
        dmx.send(universe, &u);
        universe += 1;
    }
}

<commit_msg>tweaking the output<commit_after>use std::env;
use std::f32;
use std::thread::sleep;
use std::time::Duration;

extern crate getopts;
use getopts::Options;
extern crate rand;
use rand::Rng;
use rand::distributions::{IndependentSample,Range};
extern crate sacn;
use sacn::DmxSource;

const DECAY_FACTOR: f32 = 2_f32;
const LIGHT_THRESHOLD: f32 = 0.10;
const MAX_BRIGHTNESS: f32 = 75_f32;

const UNIVERSE_SIZE: usize = 510;

#[derive(Debug)]
struct Params {
    decay:          f32,
    threshold:      f32,
    max_brightness: f32,
    sleep:          Duration
}

#[derive(Debug)]
#[derive(Clone)]
struct Pixel { level: i16, age: i16 }
// impl Pixel {
//     fn clone(&self) -> Pixel {
//         let mut pixel = Pixel { level: self.level, age: self.age };
//     }
// }

struct Zone  { head: u8, body: u8, tail: u8, name: String }

fn build_params () -> Params {
    // seed default params
    let mut params = Params { decay: 2_f32, threshold: 0.1, max_brightness: 75_f32, sleep: Duration::new(1, 500_000_000) };

    // parse command line args and adjust params accordingly
    let args: Vec<String> = env::args().collect();
    let program = args[0].clone();
    let mut opts = Options::new();
    opts.optopt("d", "decay", "slow decay by this factor, defaults to 2", "DECAY");
    opts.optopt("t", "threshold", "probablity that a pixel lights up, default 0.10", "THRESHOLD");
    opts.optopt("m", "maxbrightness", "maximum brightness, 1..255, default 75", "MAX");
    opts.optopt("s", "sleep", "sleep interval in seconds, default 1.5", "SECONDS");
    let matches = match opts.parse(&args[1..]) {
        Ok(m) => { m }
        Err(f) => { panic!(f.to_string()) }
    };
    if matches.opt_present("d") {
        params.decay = matches.opt_str("d").unwrap().parse::<f32>().unwrap();
    }
    if matches.opt_present("t") {
        params.threshold = matches.opt_str("t").unwrap().parse::<f32>().unwrap();
    }
    if matches.opt_present("m") {
        params.max_brightness = matches.opt_str("m").unwrap().parse::<f32>().unwrap();
    }
    if matches.opt_present("s") {
        // take float seconds
        // convert to int seconds and nanoseconds to make Duration happy
        let seconds: f32 = matches.opt_str("s").unwrap().parse::<f32>().unwrap();
        let whole_seconds: u64 = seconds as u64;
        let nano_seconds: u32 = ((seconds - whole_seconds as f32) * 1_000_000_000_f32) as u32;
        params.sleep = Duration::new(whole_seconds, nano_seconds);
    }
    return params;
}

fn main() {
    let params = build_params();

    let dmx = DmxSource::new("Controller").unwrap();

    let zones: [Zone; 6] = [
        Zone { head: 3, body: 47, tail: 0, name: "10".to_string() },
        Zone { head: 2, body: 92, tail: 2, name: "11a".to_string() },
        Zone { head: 2, body: 92, tail: 2, name: "11b".to_string() },
        Zone { head: 2, body: 90, tail: 3, name: "12a".to_string() },
        Zone { head: 2, body: 91, tail: 3, name: "12b".to_string() },
        Zone { head: 2, body: 43, tail: 0, name: "13".to_string() }
    ];

    let mut lights: Vec<Pixel> = vec![];
    // TODO: probably a more idiomatic way to built the default state
    for zone in zones.iter() {
        for i in 1..zone.body {
            let pixel = Pixel { level: 0, age: 0 };
            lights.push(pixel);
        }
    }

    let mut rng = rand::thread_rng();
    let bright_range = Range::new(0_f32, params.max_brightness);
    let zero_to_one = Range::new(0_f32, 1_f32);

    loop {
        for light in lights.iter_mut() {
            if light.level == 0 {
                // light is currently dark
                // test to see if we want to light it
                if zero_to_one.ind_sample(&mut rng) < params.threshold {
                    // we do, so pick a random
                    light.level = bright_range.ind_sample(&mut rng) as i16;
                    light.age += 1;
                }
            } else {
                // light is lit
                // test to see if we rise or fall
                // probability of falling should go up as light ages
                if zero_to_one.ind_sample(&mut rng) > 1.0/(light.age as f32) {
                    // falling
                    light.level -= (bright_range.ind_sample(&mut rng)/params.decay) as i16;
                    light.age += 1;
                    // test to see if we bottomed out
                    if light.level <= 0 {
                        light.level = 0;
                        light.age = 0;
                    }
                } else {
                    // rising
                    light.level += (zero_to_one.ind_sample(&mut rng) * (params.max_brightness - light.level as f32)) as i16;
                    light.age += 1;
                }
            }
        }
        render(&lights, &zones, &dmx);
        sleep(params.sleep);
    }
    dmx.terminate_stream(1);
}

// debug, rendor output to console
// fn render( lights: &[Pixel], zones: &[Zone], dmx: &DmxSource ) {
//    let mut out: Vec<i16> = vec![];
//    for light in lights {
//        out.push(light.level);
//   }
//    println!("{:?}", out)
//     let mut lit: u16 = 0;
//     let mut level: i16 = 0;
//     for light in lights {
//         if light.level > 0 {
//             lit += 1;
//             level += light.level;
//         }
//     }
//     println!("{} pixels, {} lit, {} avg level", lights.len(), lit, (level as f32)/(lit as f32));
// }

// output to lighting controller via sACN
fn render( lights: &[Pixel], zones: &[Zone], dmx: &DmxSource ) {
    let mut out: Vec<u8> = vec![];
    let mut copy: Vec<Pixel> = vec![];
    copy.extend_from_slice(lights);
    let mut idx: usize = 0;
    for zone in zones {
        // null pixels at head
        for i in 1..zone.head {
            out.push(0); // Red
            out.push(0); // Green
            out.push(0); // Blue
        }
        // set via light.level in the body
        for i in 0..(zone.body - 1) {
            out.push(copy[idx].level as u8); // Red
            out.push(copy[idx].level as u8); // Green
            out.push(copy[idx].level as u8); // Blue
            idx += 1;
        }
        // null pixels at tail
        for i in 1..zone.tail {
            out.push(0); // Red
            out.push(0); // Green
            out.push(0); // Blue
        }
    }
    let mut universes = Vec::new();
    while out.len() > UNIVERSE_SIZE {
        let u = out.split_off(UNIVERSE_SIZE);
        universes.push(out);
        out = u;
    }
    universes.push(out);
    let mut universe: u16 = 1;
    for u in universes {
        dmx.send(universe, &u);
        universe += 1;
    }
}

<|endoftext|>


<commit_before>use std::fs;
use std::env;
use std::fs::File;
use std::io::Read;
use std::hash::Hasher;
use std::path::PathBuf;
use std::collections::HashMap;
//use std::os::unix::fs::MetadataExt;
use std::collections::hash_map::DefaultHasher;

#[macro_use]
extern crate log;
extern crate env_logger;

fn collect_files(dir: &String, h: &mut HashMap<u64, Vec<String>>) {

    let entries = match fs::read_dir(dir) {
        Ok(x) => x,
        _ => {
            warn!("{}: invalid directory", dir);
            return
        },
    };

    for path in  entries.filter( |x| x.is_ok() ).map( |x| x.unwrap().path() )
                .collect::<Vec<PathBuf>>() {
        if let (Some(path_str), Ok(metadata)) =
                (path.to_str(), path.symlink_metadata()) {
            let ft = metadata.file_type();
            if ft.is_symlink() {
                continue;
            } else if ft.is_file() {
                match metadata.len() {
                    0 => continue, //Ignore empty files.
                    i => h.entry(i).or_insert_with(Vec::new)
                          .push(String::from(path_str)),
                };
            } else if ft.is_dir() {
                collect_files(&(String::from(path_str)), h);
            }
        } else {
            warn!("Failed to retrieve metadata for {}", path.to_str().unwrap());
        }
    }
}

fn find_duplicates(duplicates: &mut HashMap<u64, Vec<String>>,
                   vec: &Vec<String>, thorough: bool ) {
    for file_entry in vec {
        if let Ok(mut file) = File::open(file_entry) {
            let mut hash: u64 = 0;
            let mut hasher = DefaultHasher::new();
            if thorough {
                let mut contents: Vec<u8> = Vec::new();
                if let Ok(_) = file.read_to_end(&mut contents) {
                    hasher.write(&contents[..]);
                    hash = hasher.finish();
                } else {
                    warn!("Failed to read {}", file_entry);
                }
            } else {
                let mut buf: [u8; 1024] = [0; 1024];
                if let Ok(nbytes) = file.read(&mut buf) {
                    hasher.write(&buf[..nbytes as usize]);
                    hash = hasher.finish();
                } else {
                    warn!("Failed to read {} chunk", file_entry);
                }
            }
            if hash != 0 {
                duplicates.entry(hash).or_insert_with(Vec::new)
                    .push(file_entry.to_string());
            }
        } else {
            warn!("Failed to open file {}", file_entry);
        }
    }
}

fn print_duplicates(vec: &Vec<String>, cluster: i32, key: u64, hash: u64) {
    println!("{} files in cluster {} (size: {}, digest: {})",
             vec.len(), cluster, key, hash);
    // for_each becomes stable v1.22.0 onwards. Should uncomment then.
    //vec.iter().for_each(|f| println!("{}", f.path));
    for file_entry in vec {
        println!("{}", file_entry);
    }
}

fn exhaustive_search(emap: &mut HashMap<Vec<u8>, Vec<String>>,
                     vec: &Vec<String>) {
    for file_entry in vec {
        if let Ok(mut file) = File::open(file_entry) {
            let mut contents: Vec<u8> = Vec::new();
            if let Ok(_) = file.read_to_end(&mut contents) {
                emap.entry(contents).or_insert_with(Vec::new)
                    .push(file_entry.to_string());
            } else {
                warn!("Failed to read {}", file_entry);
            }
        } else {
            warn!("Failed to open {}", file_entry);
        }
    }
}

fn print_usage_and_exit(binary_name: &String) {
        println!("Usage: {} [-t] [-e] <dir1> [dir2 [dir3 ...]]", binary_name);
        ::std::process::exit(0);
}

fn main() {
    env_logger::init().unwrap();

    let args: Vec<String> = env::args().collect();

    let binary_name = &args[0];

    // Print out usage if no directories are given.
    if args.len() == 1 {
        print_usage_and_exit(binary_name);
    }

    let mut hmap: HashMap<u64, Vec<String> > = HashMap::new();

    let mut thorough = false;
    let mut exhaustive = false;
    let mut print_usage = true;

    for dir in &args[1..] {
        if dir == "-t" {
            thorough = true;
            continue;
        } else if dir == "-e" {
            thorough = false;
            exhaustive = false;
            continue;
        }
        print_usage = false;
        collect_files(dir, &mut hmap);
    }

    if print_usage {
        print_usage_and_exit(binary_name);
    }

    // Get rid of all the single entries.
    hmap.retain(|_, v| v.len() >= 2);

    let mut cluster = 1;

    for (key, val) in hmap {
        let mut duplicates: HashMap<u64, Vec<String> > = HashMap::new();
        find_duplicates(&mut duplicates, &val, thorough);

        for (hash, vec) in duplicates {
            if vec.len() >= 2 {
                if exhaustive {
                    let mut emap: HashMap<Vec<u8>, Vec<String>> = HashMap::new();
                    exhaustive_search(&mut emap, &vec);
                    for (_, v) in emap {
                        if v.len() >= 2 {
                            print_duplicates(&v, cluster, key, hash);
                            cluster += 1;
                        }
                    }
                } else {
                    print_duplicates(&vec, cluster, key, hash);
                    cluster += 1;
                }
            }
        }
    }
}
<commit_msg>Pop the binary name from the args vector<commit_after>use std::fs;
use std::env;
use std::fs::File;
use std::io::Read;
use std::hash::Hasher;
use std::path::PathBuf;
use std::collections::HashMap;
//use std::os::unix::fs::MetadataExt;
use std::collections::hash_map::DefaultHasher;

#[macro_use]
extern crate log;
extern crate env_logger;

fn collect_files(dir: &String, h: &mut HashMap<u64, Vec<String>>) {

    let entries = match fs::read_dir(dir) {
        Ok(x) => x,
        _ => {
            warn!("{}: invalid directory", dir);
            return
        },
    };

    for path in  entries.filter( |x| x.is_ok() ).map( |x| x.unwrap().path() )
                .collect::<Vec<PathBuf>>() {
        if let (Some(path_str), Ok(metadata)) =
                (path.to_str(), path.symlink_metadata()) {
            let ft = metadata.file_type();
            if ft.is_symlink() {
                continue;
            } else if ft.is_file() {
                match metadata.len() {
                    0 => continue, //Ignore empty files.
                    i => h.entry(i).or_insert_with(Vec::new)
                          .push(String::from(path_str)),
                };
            } else if ft.is_dir() {
                collect_files(&(String::from(path_str)), h);
            }
        } else {
            warn!("Failed to retrieve metadata for {}", path.to_str().unwrap());
        }
    }
}

fn find_duplicates(duplicates: &mut HashMap<u64, Vec<String>>,
                   vec: &Vec<String>, thorough: bool ) {
    for file_entry in vec {
        if let Ok(mut file) = File::open(file_entry) {
            let mut hash: u64 = 0;
            let mut hasher = DefaultHasher::new();
            if thorough {
                let mut contents: Vec<u8> = Vec::new();
                if let Ok(_) = file.read_to_end(&mut contents) {
                    hasher.write(&contents[..]);
                    hash = hasher.finish();
                } else {
                    warn!("Failed to read {}", file_entry);
                }
            } else {
                let mut buf: [u8; 1024] = [0; 1024];
                if let Ok(nbytes) = file.read(&mut buf) {
                    hasher.write(&buf[..nbytes as usize]);
                    hash = hasher.finish();
                } else {
                    warn!("Failed to read {} chunk", file_entry);
                }
            }
            if hash != 0 {
                duplicates.entry(hash).or_insert_with(Vec::new)
                    .push(file_entry.to_string());
            }
        } else {
            warn!("Failed to open file {}", file_entry);
        }
    }
}

fn print_duplicates(vec: &Vec<String>, cluster: i32, key: u64, hash: u64) {
    println!("{} files in cluster {} (size: {}, digest: {})",
             vec.len(), cluster, key, hash);
    // for_each becomes stable v1.22.0 onwards. Should uncomment then.
    //vec.iter().for_each(|f| println!("{}", f.path));
    for file_entry in vec {
        println!("{}", file_entry);
    }
}

fn exhaustive_search(emap: &mut HashMap<Vec<u8>, Vec<String>>,
                     vec: &Vec<String>) {
    for file_entry in vec {
        if let Ok(mut file) = File::open(file_entry) {
            let mut contents: Vec<u8> = Vec::new();
            if let Ok(_) = file.read_to_end(&mut contents) {
                emap.entry(contents).or_insert_with(Vec::new)
                    .push(file_entry.to_string());
            } else {
                warn!("Failed to read {}", file_entry);
            }
        } else {
            warn!("Failed to open {}", file_entry);
        }
    }
}

fn print_usage_and_exit(binary_name: &String) {
        println!("Usage: {} [-t] [-e] <dir1> [dir2 [dir3 ...]]", binary_name);
        ::std::process::exit(0);
}

fn main() {
    env_logger::init().unwrap();

    let mut args: Vec<String> = env::args().collect();

    let binary_name = args.remove(0);

    // Print out usage if no directories are given.
    if args.len() == 0 {
        print_usage_and_exit(&binary_name);
    }

    let mut hmap: HashMap<u64, Vec<String> > = HashMap::new();

    let mut thorough = false;
    let mut exhaustive = false;
    let mut print_usage = true;

    for dir in &args[..] {
        if dir == "-t" {
            thorough = true;
            continue;
        } else if dir == "-e" {
            thorough = false;
            exhaustive = false;
            continue;
        }
        print_usage = false;
        collect_files(dir, &mut hmap);
    }

    if print_usage {
        print_usage_and_exit(&binary_name);
    }

    // Get rid of all the single entries.
    hmap.retain(|_, v| v.len() >= 2);

    let mut cluster = 1;

    for (key, val) in hmap {
        let mut duplicates: HashMap<u64, Vec<String> > = HashMap::new();
        find_duplicates(&mut duplicates, &val, thorough);

        for (hash, vec) in duplicates {
            if vec.len() >= 2 {
                if exhaustive {
                    let mut emap: HashMap<Vec<u8>, Vec<String>> = HashMap::new();
                    exhaustive_search(&mut emap, &vec);
                    for (_, v) in emap {
                        if v.len() >= 2 {
                            print_duplicates(&v, cluster, key, hash);
                            cluster += 1;
                        }
                    }
                } else {
                    print_duplicates(&vec, cluster, key, hash);
                    cluster += 1;
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>use std::io;
use std::io::prelude::*;

fn print_prompt() {
    print!("db > ");
    io::stdout().flush().unwrap();
}

enum MetaCommand {
    Exit
}

fn do_meta_command(command: &str) -> Result<MetaCommand, ParseError> {
    if command.starts_with(".exit") {
        Ok(MetaCommand::Exit)
    } else {
        Err(ParseError::Unrecognized)
    }
}

enum ParseError {
    Unrecognized
}

enum Statement {
    Insert, Select
}

fn prepare_statement(input: &str) -> Result<Statement, ParseError> {
    if input.starts_with("insert") {
        Ok(Statement::Insert)
    }  else if input.starts_with("select") {
        Ok(Statement::Select)
    } else {
        Err(ParseError::Unrecognized)
    }
}

fn execute_statement(statement: Statement) {
  match statement {
    Statement::Insert => println!("This is where we would do an insert."),
    Statement::Select => println!("This is where we would do a select.")
  }
}

fn main() {
    println!("Hello, world!");
    let stdin = io::stdin();
    let lines = stdin.lock().lines();

    print_prompt();
    for line in lines {
        if let Ok(input) = line {
            if let Some('.') = input.chars().next() {
                match do_meta_command(&input) {
                    Ok(MetaCommand::Exit) => break,
                    Err(ParseError::Unrecognized) => println!("Unrecognized command '{}'", input)
                }
            }

            match prepare_statement(&input) {
                Ok(statement) => {
                    execute_statement(statement);
                    println!("Executed.")
                }
                Err(ParseError::Unrecognized) => println!("Unrecognized keyword at start of {}", input),
            }
        }
        print_prompt();
    }
}
<commit_msg>Part 3<commit_after>#![feature(const_fn)]
use std::io;
use std::io::prelude::*;
use std::cmp;
use std::fmt;
use std::str;

const MAX_UNAME_LENGTH: usize = 32;
const MAX_EMAIL_LENGTH: usize = 256;

#[derive(Copy)]
struct Row {
    id: u32,
    username_len: u32,
    username: [u8; MAX_UNAME_LENGTH],
    email_len: u32,
    email: [u8; MAX_EMAIL_LENGTH],
}

impl Row {
    fn new(id: u32, username: &[u8], email: &[u8]) -> Self {
        Row {
            id: id,
            username_len: username.len() as u32,
            username: {
                let mut tmp = [0; MAX_UNAME_LENGTH];
                tmp[..cmp::min(MAX_UNAME_LENGTH, username.len())].clone_from_slice(username);
                tmp
            },
            email_len: email.len() as u32,
            email: {
                let mut tmp = [0; MAX_EMAIL_LENGTH];
                tmp[..cmp::min(MAX_EMAIL_LENGTH, email.len())].clone_from_slice(email);
                tmp
            },
        }
    }
}

impl Default for Row {
    fn default() -> Self {
        Row {
            id: 0,
            username_len: 0,
            username: [0; MAX_UNAME_LENGTH],
            email_len: 0,
            email: [0; MAX_EMAIL_LENGTH],
        }
    }
}

// Hack because not all fixed-size array struct members are `Clone`
impl Clone for Row {
    fn clone(&self) -> Self {
        *self
    }
}

impl fmt::Display for Row {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match (
            str::from_utf8(&self.username[..self.username_len as usize]),
            str::from_utf8(&self.email[..self.email_len as usize]),
        ) {
            (Ok(username), Ok(email)) => write!(f, "({}, {}, {})", self.id, username, email),
            _ => Err(fmt::Error),
        }
    }
}

const ROWS_PER_PAGE: usize = 4096 / std::mem::size_of::<Row>();
const TABLE_MAX_PAGES: usize = 100;
const TABLE_MAX_ROWS: u32 = (ROWS_PER_PAGE * TABLE_MAX_PAGES) as u32;

#[derive(Copy, Clone)]
struct Page {
    rows: [Row; ROWS_PER_PAGE],
}

impl Page {
    fn new() -> Self {
        Page {
            rows: [Default::default(); ROWS_PER_PAGE],
        }
    }

    fn get_row<'a>(&'a self, row_num: usize) -> &'a Row {
        &self.rows[row_num]
    }

    fn set_row<'a>(&'a mut self, row_num: usize, row: &Row) {
        self.rows[row_num] = *row;
    }
}

struct Table {
    pages: [Option<Box<Page>>; 100],
    num_rows: u32,
}

impl Table {
    fn new() -> Self {
        Table {
            pages: {
                let mut array: [Option<Box<Page>>; 100] = unsafe { std::mem::uninitialized() };
                for i in array.iter_mut() {
                    unsafe {
                        ::std::ptr::write(i, None);
                    }
                }
                array
            },
            num_rows: 0,
        }
    }

    fn get_page<'a>(&'a mut self, page_num: usize) -> &'a Page {
        match self.pages[page_num] {
            Some(ref page) => page,
            None => {
                let new_page = Box::new(Page::new());
                self.pages[page_num] = Some(new_page);
                self.pages[page_num].as_ref().unwrap()
            }
        }
    }

    fn get_page_mut<'a>(&'a mut self, page_num: usize) -> &'a mut Page {
        match self.pages[page_num] {
            Some(ref mut page) => page,
            None => {
                let new_page = Box::new(Page::new());
                self.pages[page_num] = Some(new_page);
                self.pages[page_num].as_mut().unwrap()
                // match self.pages[page_num as usize] {
                //     Some(ref mut page) => page.borrow_mut(),
                //     _ => unreachable!(),
                // }
            }
        }
    }

    fn row_slot<'a>(&'a mut self, row_num: usize) -> &'a Row {
        let (page_num, row_offset) = (row_num / ROWS_PER_PAGE, row_num % ROWS_PER_PAGE);
        let page = self.get_page(page_num);
        page.get_row(row_offset)
    }

    fn insert_row(&mut self, row: &Row, row_num: usize) -> Result<(), ExecuteError> {
        if self.num_rows >= TABLE_MAX_ROWS {
            Err(ExecuteError::TableFull)
        } else {
            let (page_num, row_offset) = (row_num / ROWS_PER_PAGE, row_num % ROWS_PER_PAGE);
            let page = self.get_page_mut(page_num);
            page.set_row(row_offset, row);
            Ok(())
        }
    }
}

enum MetaCommand {
    Exit,
}

fn do_meta_command(command: &str) -> Result<MetaCommand, ParseError> {
    if command.starts_with(".exit") {
        Ok(MetaCommand::Exit)
    } else {
        Err(ParseError::Unrecognized)
    }
}

enum ParseError {
    Unrecognized,
    InvalidSyntax,
}

enum Statement {
    Insert(Row),
    Select,
}

fn prepare_statement(input: &str) -> Result<Statement, ParseError> {
    if input.starts_with("insert") {
        let mut tokens = input.split_whitespace();
        let _ = tokens.next(); // skip "insert"
        match (tokens.next(), tokens.next(), tokens.next()) {
            (Some(id_str), Some(username), Some(email)) => {
                let id = id_str.parse::<u32>().map_err(|_| ParseError::InvalidSyntax)?;
                Ok(Statement::Insert(
                    Row::new(id, username.as_bytes(), email.as_bytes()),
                ))
            }
            _ => Err(ParseError::InvalidSyntax),
        }
    } else if input.starts_with("select") {
        Ok(Statement::Select)
    } else {
        Err(ParseError::Unrecognized)
    }
}

enum ExecuteError {
    TableFull,
}

fn execute_insert(row: &Row, table: &mut Table) -> Result<(), ExecuteError> {
    let num_rows = table.num_rows as usize;
    let res = table.insert_row(row, num_rows);
    table.num_rows += 1;
    res
}

fn execute_select(table: &mut Table) -> Result<(), ExecuteError> {
    for i in 0..table.num_rows {
        println!("{}", table.row_slot(i as usize));
    }
    Ok(())
}

fn execute_statement(statement: Statement, table: &mut Table) -> Result<(), ExecuteError> {
    match statement {
        Statement::Insert(row) => execute_insert(&row, table),
        Statement::Select => execute_select(table),
    }
}

fn print_prompt() {
    print!("db > ");
    io::stdout().flush().unwrap();
}

fn main() {
    let mut table = Table::new();

    let stdin = io::stdin();
    let lines = stdin.lock().lines();

    print_prompt();
    for line in lines {
        if let Ok(input) = line {
            if let Some('.') = input.chars().next() {
                match do_meta_command(&input) {
                    Ok(MetaCommand::Exit) => break,
                    Err(ParseError::Unrecognized) => println!("Unrecognized command '{}'", input),
                    _ => {}
                }
            }

            match prepare_statement(&input) {
                Ok(statement) => match execute_statement(statement, &mut table) {
                    Ok(()) => println!("Executed."),
                    Err(ExecuteError::TableFull) => println!("Error: table full"),
                },
                Err(ParseError::Unrecognized) => {
                    println!("Unrecognized keyword at start of {}", input)
                }
                Err(ParseError::InvalidSyntax) => {
                    println!("Syntax error: could not parse statement")
                }
            }
        }
        print_prompt();
    }
}
<|endoftext|>


<commit_before>// Import the Ordering Enum from the Rust Standard Library
// http://doc.rust-lang.org/std/cmp/enum.Ordering.html
// Note: Enums are a powerful tool for data representation and 'generics' may be used
// to make Enums generic across Types to become even more powerful.
// Ordering Enum form used for Comparisons
// enum Ordering {
//   Less,
//   Equal,
//   Greater,
// }
// :: Symbol indicates a Namespace
// 'std' Module contains Sub-Module 'cmp' containing Sub-Sub-Module 'Ordering'
// The Enum Variants (Equal, Less, Greater) have been imported instead of just the Ordering
// Namespace itself to achieve compactness and convenience. This avoids full scoping and
// avoids long notation (i.e. use just Equal instead of Ordering::Equal), however name conflicts
// may arise (so it is best practice to import and apply the Namespace combined with a
// Variants together)
use std::cmp::Ordering;

// Configure the Ordering Enum
// 'cmp' is a Function that compares two given parameters and returns an Ordering
// where depending on the difference between these parameters, the returned Ordering
// may be Ordering:Less, Ordering::Greater, or Ordering::Equal
// Variants of the Enum (Ordering) are Namespaced under the Enum itself.
fn cmp(a: i32, b: i32) -> Ordering {
  if a < b      { Ordering::Less }
  else if a > b { Ordering::Greater }
  else          { Ordering::Equal }
}

/*
  Rust is an Expression-Based Systems language.
  Declare main function
*/
fn main() {

    // Metaprogramming calling a macro and passing staticaly allocated string arg
    println!("Hello, world!");

    /*
      Variable binding expression.
      <let expression> is Full Pattern for Pattern Matching.
      Rust's Type Inference automatically determines an expression's Type.
      Alternatively be explicit and supply an expression's Type after a colon.
      Or simply annotate the Type in a comment to understand the Type Rust infers.
      Rust Bindings are immutable by Default.
      Mutable Bindings require 'mut' prefix for Type Safety.
    */
    let a: i32 = 5; // assign immutable binding 'a' with Type i32 and Value 5

    // If statement based on Branch concept
    if a == 5 {
      println!("x is five");
    } else {
      println!("x is not five");
    }

    let (x, y, z) = (1, 2, 3);  // assign immutable bindings x, y, z to 1, 2, 3

    let mut b: i32 = 10; // assign mutable binding 'b'
    b = 20; // re-assign to new Value

    /*
      Rust's 'If' is an Expression. It returns a value to initialise
      the 'Let' binding ("Declaration Statement") with. 'Let' is assigned an Expression.
      Assignment to an already bound variable is an Expression.
      Assignment Value in Rust is the special Unit Type () in Rust's Type System.
      In other languages 'If' is a Statements (does not return value).

      Rust's "Expression Statement" converts an Expression into a Statement.
      Semi-colons separate each Expression.
      Semi-colons convert Expression into Statement, throws Value away, returns Unit Type ()
      (causing Type Error when expect a different Type such as i32 to be valid)
    */
    let c: i32 = if b == 20 { 25 } else { 15 };  // Explicitely specify 'c' as of Integer Type
    println!("b is: {}", b); // Interpret interpolation with moustaches
    println!("c is: {}", c);

    incr(b, c); // Call Fn passing Args
    print_sum(b, c); // Call Fn passing Args

    // Compound Data Structure Type Tuple is simple fixed size ordered list where &str is String Slice
    let tuple1: (i32, &str) = (1, "hello"); // Mutable. Type annotated. Without: let x = (1, "hello");
    let mut tuple2: (i32, i32) = (1, 2); // Type annotated. Without: let x = (1, "hello");
    let tuple3: (i32, i32) = (3, 4);

    // Note that for a Tuple with a Single value the trailing commas are mandatory
    let (x,): (i32,) = (1,);

    if tuple2 != tuple3 {
        tuple2 = tuple3; // Assign Tuple to another Mutable Tuple (with same Arity and Types)
        println!("tuple2 and tuple3 not equal so assigning");
    } else {
        println!("tuple2 and tuple3 do not have same Arity, Types, Order, and Value, not assigning");
    }

    /*
      Destructuring Let (access parameters of Tuple).
      Let statement performs Multiple Bindings assignments when LHS and RHS Pattern Match
    */
    let (t1, t2) = tuple2;

    println!("t1 and t2 is {}", t1 + t2); // Note: Expects t1 and t2 to be same Type

    // Tuples to return Multiple Values from a Function
    let (tnt1, tnt2) = next_two(5);
    println!("tnt1, tnt2 = {}, {}", tnt1, tnt2);

    try_structs();

    try_tuple_structs();

    try_newtype();

    try_enums();

    try_pattern_matching();

    let n: i32 = 4;
    let mut o: i32 = 0;

    o = try_loops(n);

    println!("o is {}", o);

    // String Slices (aka String Literals) of Type &str are Statically Allocated (i.e. saved inside compiled program
    // and existing for entire run duration). The have fixed size and cannot be mutated (i.e. let mut).
    // stringLiteral is a binding reference to another string that is the Statically Allocated String Slice.
    let mut stringLiteral: &str = "String Slice / String Literal"; // Should not be able to declare as 'let mut'

    // In-Memory Strings (formerly StrBuf) allocate memorary and control their data. They cannot be converted into
    // the equivalent of a &'static str (Static String) like a &str, as In-Memory Strings are not pre-configured
    // to live for the entire lifetime of the compiled program like &str. They can infact be converted from String
    // into a slice &'a str by using the as_slice() method (retaining its original String lifetime)
    let mut stringInMemory: String = "In-Memory String".to_string();

    stringLiteral = try_strings(stringLiteral, stringInMemory);

    println!("stringLiteral is {}", stringLiteral);

}

/*
  Rust Structs are of Record Type form similar to Tuples.
  Elements in Structs each have a Name (aka Field/Member) and a Type
*/

// Declare a Struct with a name (camel case). Values Immutable by Default
struct PointInSpace {
    x: i32,
    y: i32
}

fn try_structs() {
  // Create Instance (Mutable) of Struct using Let and set each Field (any order)
  let mut origin = PointInSpace { x: 0, y: 0 }; // origin: PointInSpace

  origin.x = 20;

  println!("Origin is at ({}, {})", origin.x, origin.y); // Dot Notation to access Fields
}

/*
  Rust Tuple Structs are a hybrid data type similar to Tuple and Struct.
  Elements in Tuple Structs DO NOT have a Name, they only have a Type.
  Structs are preferred over Tuple Structs since they have actual Names for Elements.
  Note: Tuple Structs must use round brackets and colon
*/

struct Color (
  i32,
  i32,
  i32
);

struct PointInWater (
  i32,
  i32,
  i32
);

fn try_tuple_structs() {
  let black = Color(0, 0, 0);
  let origin = PointInWater(0, 0, 0);
}

// Declare a Newtype.
struct Inches (i32);

// Newtypes are Tuple Structs with only One Element. Beneficial for creating
// New Types similar to others (Cloning)
fn try_newtype() {
  let length = Inches(10);
  // Extract inner Integer Type by Desctructing the Let
  let Inches(integer_length) = length; // Assigns length to integer_length
  println!("Newtype length is {} inches", integer_length);
}

// Enums variants are a "Sum Type" that ties a set of alternatives to a specific name
fn try_enums() {
  // Define a Character to be either a Digit or Other. The alternative names may be
  // used via their fully scoped names i.e. Character::Other
  // Sub-datastructure types allowable in an Enum include Structs, Tuple Structs
  // Enums do not have access to operators: ==, !=, *, +, <, >=

  enum Character {
    Digit(i32), // Character::Digit is a name tied to an i32
    Missing, // Missing is just a name (not tied to a specific Type)
  }

  // Assignment
  let a  = Character::Digit(10);
  let b = Character::Missing;

  // Retrieve values contained in Enums, Error Handling where function output does not
  // match the expected Type
  match a {
    Character::Digit(n) => print!("\na is {} ", n),
    Character::Missing => print!("\na is missing"),
  }

  match b {
    Character::Digit(n) => print!("\nb is {}", n),
    Character::Missing => print!("\nb is missing"),
  }

  let x = 5;
  let y = 10;

  // // 'ordering' variable is of Ordering type (ordering: Ordering)
  // // so it contains one of the three values defined in the 'cmp' Function
  // // and we discover which one by using Conditional Operators to check
  // let ordering = cmp(x, y);

  // if ordering == Ordering::Less { println!("Less"); }
  // else if ordering == Ordering::Greater { println!("Greater"); }
  // else if ordering == Ordering::Equal { println!("Equal"); }

  // Replace the above 'if/else' with 'match' Pattern Matching expression instead
  // with less noise and which supports Exhaustiveness Checking across all
  // possible Variants of the Ordering Enum
  println!("{}", match cmp(x, y) {
    Ordering::Less => "\nless",
    Ordering::Greater => "\ngreater",
    Ordering::Equal => "\nequal",
  });

  // Equivalent to the above
  match cmp(x, y) {
    Ordering::Less => println!("\nless"),
    Ordering::Greater => println!("\ngreater"),
    Ordering::Equal => println!("\nequal"),
  };
}

// Pattern Matching is implemented by 'match' and allows elegantly deconstructing Enums
// (known as the Sum Type in Type Theory) whilst using the 'match' keyword
// instead of avoiding 'if/else' (for multiple or a complex set of option cases)
fn try_pattern_matching() {
  let x = 5;

  // 'match' takes Expression and branches into Arms depending on its Value.
  // Evaluation is performed only on the matching Arm.
  match x {
    // Arms are of the form 'val => expression'
    1 => println!("one"),
    2 => println!("two"),
    3 => println!("three"),
    4 => println!("four"),
    5 => println!("five"),
    // Exhaustiveness Checking ensures all possible patterns of Values are covered
    // with the '_' Catch-all- Arm so if any Expression used that were not included
    // (not all bases covered) it prevents a compile error (as '_' will match)
    _ => println!("something else"),
  }
}

fn try_loops(n: i32) -> (i32) {
  let mut o = 0;
  // For Loop with variable m over Iterator Expression 0..n (with start and end positions)
  // that produces a series of elements where each element is a loop itertion fetched from
  // the Iterator and bound to the variable m during each cycle through the loop body.
  // The Iterator is upper bound exclusive and so prints 0 through to n-1

  for m in 0..n {
    if m % 2 == 0 { continue; } // Approval condition only processes even iterations.
    println!("for loop with: {}", m);
    o += m;
  }

  let mut done: bool = false;

  println!("{}", o);

  while !done {
    o += o - 2;
    println!("while loop with: {}", o);
    if o % 3 == 0 { done = true; }
  }

  // Infinite Loop intentionally
  loop { // Use this instead of while true {}
    o -= 1;
    if o % 7 == 0 { break; } // End iteration early
  }

  return o;
}

// Strings are a re-sizable Data Structure.
// String is a sequence of Unicode scalar values validly encoded as a stream of UTF-8 bytes.
// Strings may contain null bytes (as they are not null-terminated)

fn try_strings(s_l: &str, s_m: String) -> &str {

  let mut _s_m: String = s_m; // Declare s_m as a mutable local variable _s_m

  let mut _s: String = s_l.to_string();
  _s.push_str(", is a growable string and is guaranteed to be UTF-8");
  println!("{}", _s);

  // Function to Convert Type &str into Type String (cheap solution that allocates it to memory)
  fn convert_to_string_in_memory_taking_string_in_memory(memory_string: String) {
    let memory_string_slice: &str = memory_string.as_slice();
    println!("Converted Type &str (String Slice / Literal) into Type String (In-Memory String): {}", memory_string_slice);
  }

  // Coerce Type &str into Type String
  convert_to_string_in_memory_taking_string_in_memory(_s_m);

  // Function to Convert Type String into Type &str
  fn convert_to_string_literal_taking_string_slice(slice: &str) {
    println!("Converted Type String (In-Memory String) into Type &str (String Slice / Literal): {}", slice);
  }

  // Coerce Type String into Type &str by prefixing with an & symbol
  convert_to_string_literal_taking_string_slice(&_s);

  // The following attempt to perform the same conversion with the parameter passed into the function gives error:
  //   - error: use of moved value: `s_m`
  //   - note: `s_m` moved here because it has type `collections::string::String`, which is moved by default
  //   - use `ref` to override

  // let mut _s_m2: String = s_m.to_string(); // Declare s_m as a mutable local variable _s_m

  // convert_to_string_literal_taking_string_slice(&_s_m2);

  return "Test String to override the value of the String Slice / Literal that we are supposedly not meant to be able to mutate";
}

// Returned Tuple is a Single Value (containing Multiple Values)
fn next_two(x: i32) -> (i32, i32) { (x + 1, x + 2) }

// All control paths must Return a Value
fn incr(m: i32, n: i32) -> i32 {
    if n < m {
      return n; // Early Return
    } else {
      println!("incrementing n from: {}", n);
      return n + 10;
    }
}

/*
  Rust forces Declare expected Type of incoming Fn Args. Type Inference in Fn body.
  Rust allows Declaration expected Type of outgoing Return Value after '->'
*/
fn print_sum(m: i32, n: i32) -> () { // Return the Unit Type () (aka nil)
    if m < n {
      println!("sum with m < n is: {}", m + n);
    } else {
      println!("sum with m >= n is: {}", m + n);
    }
}<commit_msg>Added Arrays (Statically Allocated) and Vectors (Dynamic In-Memory Arrays) and Slices<commit_after>// Import the Ordering Enum from the Rust Standard Library
// http://doc.rust-lang.org/std/cmp/enum.Ordering.html
// Note: Enums are a powerful tool for data representation and 'generics' may be used
// to make Enums generic across Types to become even more powerful.
// Ordering Enum form used for Comparisons
// enum Ordering {
//   Less,
//   Equal,
//   Greater,
// }
// :: Symbol indicates a Namespace
// 'std' Module contains Sub-Module 'cmp' containing Sub-Sub-Module 'Ordering'
// The Enum Variants (Equal, Less, Greater) have been imported instead of just the Ordering
// Namespace itself to achieve compactness and convenience. This avoids full scoping and
// avoids long notation (i.e. use just Equal instead of Ordering::Equal), however name conflicts
// may arise (so it is best practice to import and apply the Namespace combined with a
// Variants together)
use std::cmp::Ordering;

// Configure the Ordering Enum
// 'cmp' is a Function that compares two given parameters and returns an Ordering
// where depending on the difference between these parameters, the returned Ordering
// may be Ordering:Less, Ordering::Greater, or Ordering::Equal
// Variants of the Enum (Ordering) are Namespaced under the Enum itself.
fn cmp(a: i32, b: i32) -> Ordering {
  if a < b      { Ordering::Less }
  else if a > b { Ordering::Greater }
  else          { Ordering::Equal }
}

/*
  Rust is an Expression-Based Systems language.
  Declare main function
*/
fn main() {

    // Metaprogramming calling a macro and passing staticaly allocated string arg
    println!("Hello, world!");

    /*
      Variable binding expression.
      <let expression> is Full Pattern for Pattern Matching.
      Rust's Type Inference automatically determines an expression's Type.
      Alternatively be explicit and supply an expression's Type after a colon.
      Or simply annotate the Type in a comment to understand the Type Rust infers.
      Rust Bindings are immutable by Default.
      Mutable Bindings require 'mut' prefix for Type Safety.
    */
    let a: i32 = 5; // assign immutable binding 'a' with Type i32 and Value 5

    // If statement based on Branch concept
    if a == 5 {
      println!("x is five");
    } else {
      println!("x is not five");
    }

    let (x, y, z) = (1, 2, 3);  // assign immutable bindings x, y, z to 1, 2, 3

    let mut b: i32 = 10; // assign mutable binding 'b'
    b = 20; // re-assign to new Value

    /*
      Rust's 'If' is an Expression. It returns a value to initialise
      the 'Let' binding ("Declaration Statement") with. 'Let' is assigned an Expression.
      Assignment to an already bound variable is an Expression.
      Assignment Value in Rust is the special Unit Type () in Rust's Type System.
      In other languages 'If' is a Statements (does not return value).

      Rust's "Expression Statement" converts an Expression into a Statement.
      Semi-colons separate each Expression.
      Semi-colons convert Expression into Statement, throws Value away, returns Unit Type ()
      (causing Type Error when expect a different Type such as i32 to be valid)
    */
    let c: i32 = if b == 20 { 25 } else { 15 };  // Explicitely specify 'c' as of Integer Type
    println!("b is: {}", b); // Interpret interpolation with moustaches
    println!("c is: {}", c);

    incr(b, c); // Call Fn passing Args
    print_sum(b, c); // Call Fn passing Args

    // Compound Data Structure Type Tuple is simple fixed size ordered list where &str is String Slice
    let tuple1: (i32, &str) = (1, "hello"); // Mutable. Type annotated. Without: let x = (1, "hello");
    let mut tuple2: (i32, i32) = (1, 2); // Type annotated. Without: let x = (1, "hello");
    let tuple3: (i32, i32) = (3, 4);

    // Note that for a Tuple with a Single value the trailing commas are mandatory
    let (x,): (i32,) = (1,);

    if tuple2 != tuple3 {
        tuple2 = tuple3; // Assign Tuple to another Mutable Tuple (with same Arity and Types)
        println!("tuple2 and tuple3 not equal so assigning");
    } else {
        println!("tuple2 and tuple3 do not have same Arity, Types, Order, and Value, not assigning");
    }

    /*
      Destructuring Let (access parameters of Tuple).
      Let statement performs Multiple Bindings assignments when LHS and RHS Pattern Match
    */
    let (t1, t2) = tuple2;

    println!("t1 and t2 is {}", t1 + t2); // Note: Expects t1 and t2 to be same Type

    // Tuples to return Multiple Values from a Function
    let (tnt1, tnt2) = next_two(5);
    println!("tnt1, tnt2 = {}, {}", tnt1, tnt2);

    try_structs();

    try_tuple_structs();

    try_newtype();

    try_enums();

    try_pattern_matching();

    let n: i32 = 4;
    let mut o: i32 = 0;

    o = try_loops(n);

    println!("o is {}", o);

    // String Slices (aka String Literals) of Type &str are Statically Allocated (i.e. saved inside compiled program
    // and existing for entire run duration). The have fixed size and cannot be mutated (i.e. let mut).
    // stringLiteral is a binding reference to another string that is the Statically Allocated String Slice.
    let mut stringLiteral: &str = "String Slice / String Literal"; // Should not be able to declare as 'let mut'

    // In-Memory Strings (formerly StrBuf) allocate memorary and control their data. They cannot be converted into
    // the equivalent of a &'static str (Static String) like a &str, as In-Memory Strings are not pre-configured
    // to live for the entire lifetime of the compiled program like &str. They can infact be converted from String
    // into a slice &'a str by using the as_slice() method (retaining its original String lifetime)
    let mut stringInMemory: String = "In-Memory String".to_string();

    stringLiteral = try_strings(stringLiteral, stringInMemory);

    println!("stringLiteral is {}", stringLiteral);

    try_arrays();

    try_vectors();
}

/*
  Rust Structs are of Record Type form similar to Tuples.
  Elements in Structs each have a Name (aka Field/Member) and a Type
*/

// Declare a Struct with a name (camel case). Values Immutable by Default
struct PointInSpace {
    x: i32,
    y: i32
}

fn try_structs() {
  // Create Instance (Mutable) of Struct using Let and set each Field (any order)
  let mut origin = PointInSpace { x: 0, y: 0 }; // origin: PointInSpace

  origin.x = 20;

  println!("Origin is at ({}, {})", origin.x, origin.y); // Dot Notation to access Fields
}

/*
  Rust Tuple Structs are a hybrid data type similar to Tuple and Struct.
  Elements in Tuple Structs DO NOT have a Name, they only have a Type.
  Structs are preferred over Tuple Structs since they have actual Names for Elements.
  Note: Tuple Structs must use round brackets and colon
*/

struct Color (
  i32,
  i32,
  i32
);

struct PointInWater (
  i32,
  i32,
  i32
);

fn try_tuple_structs() {
  let black = Color(0, 0, 0);
  let origin = PointInWater(0, 0, 0);
}

// Declare a Newtype.
struct Inches (i32);

// Newtypes are Tuple Structs with only One Element. Beneficial for creating
// New Types similar to others (Cloning)
fn try_newtype() {
  let length = Inches(10);
  // Extract inner Integer Type by Desctructing the Let
  let Inches(integer_length) = length; // Assigns length to integer_length
  println!("Newtype length is {} inches", integer_length);
}

// Enums variants are a "Sum Type" that ties a set of alternatives to a specific name
fn try_enums() {
  // Define a Character to be either a Digit or Other. The alternative names may be
  // used via their fully scoped names i.e. Character::Other
  // Sub-datastructure types allowable in an Enum include Structs, Tuple Structs
  // Enums do not have access to operators: ==, !=, *, +, <, >=

  enum Character {
    Digit(i32), // Character::Digit is a name tied to an i32
    Missing, // Missing is just a name (not tied to a specific Type)
  }

  // Assignment
  let a  = Character::Digit(10);
  let b = Character::Missing;

  // Retrieve values contained in Enums, Error Handling where function output does not
  // match the expected Type
  match a {
    Character::Digit(n) => print!("\na is {} ", n),
    Character::Missing => print!("\na is missing"),
  }

  match b {
    Character::Digit(n) => print!("\nb is {}", n),
    Character::Missing => print!("\nb is missing"),
  }

  let x = 5;
  let y = 10;

  // // 'ordering' variable is of Ordering type (ordering: Ordering)
  // // so it contains one of the three values defined in the 'cmp' Function
  // // and we discover which one by using Conditional Operators to check
  // let ordering = cmp(x, y);

  // if ordering == Ordering::Less { println!("Less"); }
  // else if ordering == Ordering::Greater { println!("Greater"); }
  // else if ordering == Ordering::Equal { println!("Equal"); }

  // Replace the above 'if/else' with 'match' Pattern Matching expression instead
  // with less noise and which supports Exhaustiveness Checking across all
  // possible Variants of the Ordering Enum
  println!("{}", match cmp(x, y) {
    Ordering::Less => "\nless",
    Ordering::Greater => "\ngreater",
    Ordering::Equal => "\nequal",
  });

  // Equivalent to the above
  match cmp(x, y) {
    Ordering::Less => println!("\nless"),
    Ordering::Greater => println!("\ngreater"),
    Ordering::Equal => println!("\nequal"),
  };
}

// Pattern Matching is implemented by 'match' and allows elegantly deconstructing Enums
// (known as the Sum Type in Type Theory) whilst using the 'match' keyword
// instead of avoiding 'if/else' (for multiple or a complex set of option cases)
fn try_pattern_matching() {
  let x = 5;

  // 'match' takes Expression and branches into Arms depending on its Value.
  // Evaluation is performed only on the matching Arm.
  match x {
    // Arms are of the form 'val => expression'
    1 => println!("one"),
    2 => println!("two"),
    3 => println!("three"),
    4 => println!("four"),
    5 => println!("five"),
    // Exhaustiveness Checking ensures all possible patterns of Values are covered
    // with the '_' Catch-all- Arm so if any Expression used that were not included
    // (not all bases covered) it prevents a compile error (as '_' will match)
    _ => println!("something else"),
  }
}

fn try_loops(n: i32) -> (i32) {
  let mut o = 0;
  // For Loop with variable m over Iterator Expression 0..n (with start and end positions)
  // that produces a series of elements where each element is a loop itertion fetched from
  // the Iterator and bound to the variable m during each cycle through the loop body.
  // The Iterator is upper bound exclusive and so prints 0 through to n-1

  for m in 0..n {
    if m % 2 == 0 { continue; } // Approval condition only processes even iterations.
    println!("for loop with: {}", m);
    o += m;
  }

  let mut done: bool = false;

  println!("{}", o);

  while !done {
    o += o - 2;
    println!("while loop with: {}", o);
    if o % 3 == 0 { done = true; }
  }

  // Infinite Loop intentionally
  loop { // Use this instead of while true {}
    o -= 1;
    if o % 7 == 0 { break; } // End iteration early
  }

  return o;
}

// Strings are a re-sizable Data Structure.
// String is a sequence of Unicode scalar values validly encoded as a stream of UTF-8 bytes.
// Strings may contain null bytes (as they are not null-terminated)

fn try_strings(s_l: &str, s_m: String) -> &str {

  let mut _s_m: String = s_m; // Declare s_m as a mutable local variable _s_m

  let mut _s: String = s_l.to_string();
  _s.push_str(", is a growable string and is guaranteed to be UTF-8");
  println!("{}", _s);

  // Function to Convert Type &str into Type String (cheap solution that allocates it to memory)
  fn convert_to_string_in_memory_taking_string_in_memory(memory_string: String) {
    let memory_string_slice: &str = memory_string.as_slice();
    println!("Converted Type &str (String Slice / Literal) into Type String (In-Memory String): {}", memory_string_slice);
  }

  // Coerce Type &str into Type String
  convert_to_string_in_memory_taking_string_in_memory(_s_m);

  // Function to Convert Type String into Type &str
  fn convert_to_string_literal_taking_string_slice(slice: &str) {
    println!("Converted Type String (In-Memory String) into Type &str (String Slice / Literal): {}", slice);
  }

  // Coerce Type String into Type &str by prefixing with an & symbol
  convert_to_string_literal_taking_string_slice(&_s);

  // The following attempt to perform the same conversion with the parameter passed into the function gives error:
  //   - error: use of moved value: `s_m`
  //   - note: `s_m` moved here because it has type `collections::string::String`, which is moved by default
  //   - use `ref` to override

  // let mut _s_m2: String = s_m.to_string(); // Declare s_m as a mutable local variable _s_m

  // convert_to_string_literal_taking_string_slice(&_s_m2);

  return "Test String to override the value of the String Slice / Literal that we are supposedly not meant to be able to mutate";
}

// Arrays (sequence of elements of same "List" Type and of fixed length)
// Arrays are immutable by default
// Arrays "List" Type has T notation [T; N]
fn try_arrays() {
  let arr_imm = [1, 2, 3, 4];

  println!("arr_imm element 2 is: {}", arr_imm[2]);

  // Declare and initialise each element with value of 0
  let mut arr_mut = [0; 3];

  // Slices of Type &[T] are created from an existing variable
  let arr_mut_slice = &arr_mut[1..3]; // Slice with only elements at index 1 and 2

  println!("arr_mut has {} elements", arr_mut.len());
  println!("arr_mut_slice has {} elements", arr_mut_slice.len());

  for e in arr_mut.iter() {
    println!("arr_mut element e: {}", e);
  }

}

// Vectors are Arrays of "dynamic" length implemented as standard library Type Vec<T>
// Vectors allocate their data on the heap
// Vectors are to Slices what String is to &str
// Vectors are created using the vec! macro
fn try_vectors() {
  let mut v_mut = vec![1, 2, 3];

  let v_mut_length_before = v_mut.len();

  v_mut.push(4);

  println!("v_mut length change from {} to {}", v_mut_length_before, v_mut.len() );

}

// Returned Tuple is a Single Value (containing Multiple Values)
fn next_two(x: i32) -> (i32, i32) { (x + 1, x + 2) }

// All control paths must Return a Value
fn incr(m: i32, n: i32) -> i32 {
    if n < m {
      return n; // Early Return
    } else {
      println!("incrementing n from: {}", n);
      return n + 10;
    }
}

/*
  Rust forces Declare expected Type of incoming Fn Args. Type Inference in Fn body.
  Rust allows Declaration expected Type of outgoing Return Value after '->'
*/
fn print_sum(m: i32, n: i32) -> () { // Return the Unit Type () (aka nil)
    if m < n {
      println!("sum with m < n is: {}", m + n);
    } else {
      println!("sum with m >= n is: {}", m + n);
    }
}<|endoftext|>