<commit_before>extern crate rustc_serialize;
extern crate docopt;

use std::io::{Write, BufRead};
use docopt::Docopt;

static LF: char = '\n';
static NULL: char = '\0';
static VERSION: &'static str = "0.0.1";
static USAGE: &'static str = "
Usage: wc [options] FILE...
       wc [options] --files0-from=F
       wc (-h|--help)
       wc (-v|--version)

Print newline, word, and byte counts for each FILE, and a total line if
more than one FILE is specified. With no FILE, or when FILE is -,
read standard input. A word is a non-zero-length sequence of characters
delimited by white space.
The options below may be used to select which counts are printed, always in
the following order: newline, word, character, byte, maximum line length.

Options:
   -c, --bytes            print the byte counts
   -m, --chars            print the character counts
   -l, --lines            print the newline counts
   --files0-from=F        read input from the files specified by NUL-terminated
                          names in file F; If F is - then read names from
                          standard input
   -L, --max-line-length  print the length of the longest line
   -w, --words            print the word counts
   -h, --help             display this help and quit
   -v, --version          output version information and exit
";

#[allow(non_snake_case)]
#[derive(RustcDecodable, Debug)]
struct Args {
    arg_FILE: Vec<String>,
    flag_bytes: bool,
    flag_chars: bool,
    flag_lines: bool,
    flag_max_line_length: bool,
    flag_words: bool,
    flag_help: bool,
    flag_version: bool,
    flag_files0_from: String,
}

#[derive(Debug)]
enum ProcessingError {
    IO(std::io::Error),
    Utf8(std::str::Utf8Error),
}

impl std::fmt::Display for ProcessingError {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        match *self {
            ProcessingError::IO(ref e) => write!(f, "{}", e),
            ProcessingError::Utf8(ref e) => write!(f, "{}", e),
        }
    }
}

impl From<std::io::Error> for ProcessingError {
    fn from(e: std::io::Error) -> ProcessingError {
        ProcessingError::IO(e)
    }
}
impl From<std::str::Utf8Error> for ProcessingError {
    fn from(e: std::str::Utf8Error) -> ProcessingError {
        ProcessingError::Utf8(e)
    }
}

#[derive(Debug)]
struct FileInfo{
    bytes: usize,
    chars: usize,
    lines: usize,
    words: usize,
    max_line_length: usize,
}

type FileInfoResult = Result<FileInfo, ProcessingError>;

fn process_reader<T: std::io::Read>(reader: T) -> FileInfoResult {
    let mut info = FileInfo{
        bytes: 0,
        chars: 0,
        lines: 0,
        words: 0,
        max_line_length: 0,
    };
    // TODO: Only to as much processing as is absolutely necessary to
    // provide the data we will end up printing.
    let mut rbuf = std::io::BufReader::new(reader);
    let mut lbuf = Vec::new();
    loop {
        let size = try!(rbuf.read_until(LF as u8, &mut lbuf));
        info.bytes += size;
        if size == 0 {
            break;
        }
        // Create a scope because we're going to borrow lbuf and
        // the borrow must end before we can clear it.
        {
            // TODO: Handle files which are not utf8-encoded. Right
            // now we get an error here.
            let line = try!(std::str::from_utf8(&lbuf));
            let size = line.chars().count();
            let last = line.chars().last().unwrap_or(NULL);
            info.max_line_length = if last == LF {
                info.lines += 1;
                std::cmp::max(info.max_line_length, size - 1)
            } else {
                std::cmp::max(info.max_line_length, size)
            };
            info.chars += size;
            let mut words: Vec<&str> = line
                .split(|c: char| c.is_whitespace())
                .collect();
            words.retain(|s: &&str| s.len() > 0);
            info.words += words.len();
        }
        lbuf.clear()
    }
    Ok(info)
}

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

// type NullDelimitedFileResult<'a> = Result<Vec<String>, ProcessingError>;
type NullDelimitedFileResult = Result<Vec<String>, ProcessingError>;

fn split_file_on_nulls<T: std::io::Read>(file: T) -> NullDelimitedFileResult {
    let mut rbuf = std::io::BufReader::new(file);
    let mut result = Vec::new();
    let mut lbuf = Vec::new();
    loop {
        let size = try!(rbuf.read_until(NULL as u8, &mut lbuf));
        if size == 0 {
            break;
        }
        {
            let line = try!(std::str::from_utf8(&lbuf));
            result.push(line.trim_right_matches(NULL).to_string());
        }
        lbuf.clear()
    }
    Ok(result)
}

fn main() {
    let args: Args = Docopt::new(USAGE)
        .and_then(|d| d.decode())
        .unwrap_or_else(|e| e.exit());

    if args.flag_version {
        println!("wc v{}", VERSION);
        std::process::exit(0);
    }

    if args.flag_files0_from.len() != 0  && args.arg_FILE.len() != 0 {
        println_stderr!("wc: file operands cannot be combined with --files0-from");
        println_stderr!("Try 'wc --help' for more information");
        std::process::exit(1);
    }

    let mut files: Vec<String> = Vec::new();
    if args.flag_files0_from.len() != 0 {
        let split_results = match args.flag_files0_from.as_ref() {
            "-" => split_file_on_nulls(std::io::stdin()),
            _ => {
                match std::fs::File::open(&args.flag_files0_from) {
                    Ok(f) => split_file_on_nulls(f),
                    Err(e) => {
                        println_stderr!("wc: cannot open {} for reading: {}",
                                        args.flag_files0_from, e);
                        std::process::exit(1);
                    },
                }
            },
        };
        match split_results {
            Ok(parts) => files.extend(parts),
            Err(e) => {
                println_stderr!("wc: error reading {}: {}",
                                args.flag_files0_from, e);
                std::process::exit(1);
            },
        };
        if &args.flag_files0_from == "-" && files.contains(&"-".to_string()) {
            println_stderr!("wc: when reading file names from stdin, no file name of '-' allowed");
            std::process::exit(1);
        }
    } else {
        files.extend(args.arg_FILE);
    };

    let mut results = Vec::new();
    let mut totals = FileInfo{
        bytes: 0,
        chars: 0,
        lines: 0,
        words: 0,
        max_line_length: 0,
    };
    for filename in files {
        let result = match filename.as_ref() {
            "-" => process_reader(std::io::stdin()),
            _ => {
                let file = std::fs::File::open(filename.to_string());
                match file {
                    Ok(f) => process_reader(f),
                    Err(e) => Err(ProcessingError::IO(e)),
                }
            }
        };
        match result {
            Ok(ref r) => {
                totals.chars += r.chars;
                totals.lines += r.lines;
                totals.bytes += r.bytes;
                totals.words += r.words;
                totals.max_line_length = std::cmp::max(totals.max_line_length,
                                                       r.max_line_length);
            },
            Err(_) => {},
        }
        results.push((filename, result));
    }

    // This is used for formatting. The number in the byte count will
    // be the largest, and so will be the widest string, so it's
    // suitable for a field width.
    let field_size = totals.bytes.to_string().len();

    if results.len() > 1 {
        results.push(("total".to_string(), Ok(totals)));
    }

    let mut errors_encountered = false;

    // Present the results
    for data in results.iter() {
        let (ref filename, ref result) = *data;
        match *result {
            Ok(ref r) => {
                // let mut parts = Vec::new();
                let mut requested_field = false;
                if args.flag_lines {
                    print!("{:1$} ", r.lines, field_size);
                    requested_field = true;
                }
                if args.flag_words {
                    print!("{:1$} ", r.words, field_size);
                    requested_field = true;
                }
                if args.flag_bytes {
                    print!("{:1$} ", r.bytes, field_size);
                    requested_field = true;
                }
                if args.flag_chars {
                    print!("{:1$} ", r.chars, field_size);
                    requested_field = true;
                }
                if args.flag_max_line_length {
                    print!("{:1$} ", r.max_line_length, field_size);
                    requested_field = true;
                }
                if !requested_field {
                    print!("{:1$} ", r.lines, field_size);
                    print!("{:1$} ", r.words, field_size);
                    print!("{:1$} ", r.bytes, field_size);
                }
                println!("{}", filename);
            },
            Err(ref e) => {
                errors_encountered = true;
                println_stderr!("wc: {}: {}", filename, e);
            },
        }
    }

    if errors_encountered {
        std::process::exit(1);
    }
}
<commit_msg>Cleanup ...<commit_after>extern crate rustc_serialize;
extern crate docopt;

use std::cmp::max;
use std::fmt::Result as FmtResult;
use std::fmt::{Display, Formatter};
use std::fs::File;
use std::io::Error as IOError;
use std::io::{BufReader, stderr, stdin};
use std::io::{Write, BufRead, Read};
use std::process::exit;
use std::str::{Utf8Error, from_utf8};
use docopt::Docopt;

static LF: char = '\n';
static NULL: char = '\0';
static VERSION: &'static str = "0.0.1";
static USAGE: &'static str = "
Usage: wc [options] FILE...
       wc [options] --files0-from=F
       wc (-h|--help)
       wc (-v|--version)

Print newline, word, and byte counts for each FILE, and a total line if
more than one FILE is specified. With no FILE, or when FILE is -,
read standard input. A word is a non-zero-length sequence of characters
delimited by white space.
The options below may be used to select which counts are printed, always in
the following order: newline, word, character, byte, maximum line length.

Options:
   -c, --bytes            print the byte counts
   -m, --chars            print the character counts
   -l, --lines            print the newline counts
   --files0-from=F        read input from the files specified by NUL-terminated
                          names in file F; If F is - then read names from
                          standard input
   -L, --max-line-length  print the length of the longest line
   -w, --words            print the word counts
   -h, --help             display this help and quit
   -v, --version          output version information and exit
";

#[allow(non_snake_case)]
#[derive(RustcDecodable, Debug)]
struct Args {
    arg_FILE: Vec<String>,
    flag_bytes: bool,
    flag_chars: bool,
    flag_lines: bool,
    flag_max_line_length: bool,
    flag_words: bool,
    flag_help: bool,
    flag_version: bool,
    flag_files0_from: String,
}

#[derive(Debug)]
enum WCError {
    IO(IOError),
    Utf8(Utf8Error),
}

type WCResult<T> = Result<T, WCError>;

impl Display for WCError {
    fn fmt(&self, f: &mut Formatter) -> FmtResult {
        match *self {
            WCError::IO(ref e) => write!(f, "{}", e),
            WCError::Utf8(ref e) => write!(f, "{}", e),
        }
    }
}

impl From<IOError> for WCError {
    fn from(e: IOError) -> WCError {
        WCError::IO(e)
    }
}
impl From<Utf8Error> for WCError {
    fn from(e: Utf8Error) -> WCError {
        WCError::Utf8(e)
    }
}

#[derive(Debug)]
struct FileInfo{
    bytes: usize,
    chars: usize,
    lines: usize,
    words: usize,
    max_line_length: usize,
}

type FileInfoResult = WCResult<FileInfo>;

fn process_reader<T: Read>(reader: T) -> FileInfoResult {
    let mut info = FileInfo{
        bytes: 0,
        chars: 0,
        lines: 0,
        words: 0,
        max_line_length: 0,
    };
    // TODO: Only to as much processing as is absolutely necessary to
    // provide the data we will end up printing.
    let mut rbuf = BufReader::new(reader);
    let mut lbuf = Vec::new();
    loop {
        let size = try!(rbuf.read_until(LF as u8, &mut lbuf));
        info.bytes += size;
        if size == 0 {
            break;
        }
        // Create a scope because we're going to borrow lbuf and
        // the borrow must end before we can clear it.
        {
            // TODO: Handle files which are not utf8-encoded. Right
            // now we get an error here.
            let line = try!(from_utf8(&lbuf));
            let size = line.chars().count();
            let last = line.chars().last().unwrap_or(NULL);
            info.max_line_length = if last == LF {
                info.lines += 1;
                max(info.max_line_length, size - 1)
            } else {
                max(info.max_line_length, size)
            };
            info.chars += size;
            let mut words: Vec<&str> = line
                .split(|c: char| c.is_whitespace())
                .collect();
            words.retain(|s: &&str| s.len() > 0);
            info.words += words.len();
        }
        lbuf.clear()
    }
    Ok(info)
}

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

type NullDelimitedFileResult = WCResult<Vec<String>>;

fn split_file_on_nulls<T: Read>(file: T) -> NullDelimitedFileResult {
    let mut rbuf = BufReader::new(file);
    let mut result = Vec::new();
    let mut lbuf = Vec::new();
    loop {
        let size = try!(rbuf.read_until(NULL as u8, &mut lbuf));
        if size == 0 {
            break;
        }
        {
            let line = try!(from_utf8(&lbuf));
            result.push(line.trim_right_matches(NULL).to_string());
        }
        lbuf.clear()
    }
    Ok(result)
}

fn main() {
    let args: Args = Docopt::new(USAGE)
        .and_then(|d| d.decode())
        .unwrap_or_else(|e| e.exit());

    if args.flag_version {
        println!("wc v{}", VERSION);
        exit(0);
    }

    if args.flag_files0_from.len() != 0  && args.arg_FILE.len() != 0 {
        println_stderr!("wc: file operands cannot be combined with --files0-from");
        println_stderr!("Try 'wc --help' for more information");
        exit(1);
    }

    let mut files: Vec<String> = Vec::new();
    if args.flag_files0_from.len() != 0 {
        let split_results = match args.flag_files0_from.as_ref() {
            "-" => split_file_on_nulls(stdin()),
            _ => {
                match File::open(&args.flag_files0_from) {
                    Ok(f) => split_file_on_nulls(f),
                    Err(e) => {
                        println_stderr!("wc: cannot open {} for reading: {}",
                                        args.flag_files0_from, e);
                        exit(1);
                    },
                }
            },
        };
        match split_results {
            Ok(parts) => files.extend(parts),
            Err(e) => {
                println_stderr!("wc: error reading {}: {}",
                                args.flag_files0_from, e);
                exit(1);
            },
        };
        if &args.flag_files0_from == "-" && files.contains(&"-".to_string()) {
            println_stderr!("wc: when reading file names from stdin, no file name of '-' allowed");
            exit(1);
        }
    } else {
        files.extend(args.arg_FILE);
    };

    let mut results = Vec::new();
    let mut totals = FileInfo{
        bytes: 0,
        chars: 0,
        lines: 0,
        words: 0,
        max_line_length: 0,
    };
    for filename in files {
        let result = match filename.as_ref() {
            "-" => process_reader(stdin()),
            _ => {
                let file = File::open(filename.to_string());
                match file {
                    Ok(f) => process_reader(f),
                    Err(e) => Err(WCError::IO(e)),
                }
            }
        };
        match result {
            Ok(ref r) => {
                totals.chars += r.chars;
                totals.lines += r.lines;
                totals.bytes += r.bytes;
                totals.words += r.words;
                totals.max_line_length = max(totals.max_line_length,
                                             r.max_line_length);
            },
            Err(_) => {},
        }
        results.push((filename, result));
    }

    // This is used for formatting. The number in the byte count will
    // be the largest, and so will be the widest string, so it's
    // suitable for a field width.
    let field_size = totals.bytes.to_string().len();

    if results.len() > 1 {
        results.push(("total".to_string(), Ok(totals)));
    }

    let mut errors_encountered = false;

    // Present the results
    for data in results.iter() {
        let (ref filename, ref result) = *data;
        match *result {
            Ok(ref r) => {
                let mut requested_field = false;
                if args.flag_lines {
                    print!("{:1$} ", r.lines, field_size);
                    requested_field = true;
                }
                if args.flag_words {
                    print!("{:1$} ", r.words, field_size);
                    requested_field = true;
                }
                if args.flag_bytes {
                    print!("{:1$} ", r.bytes, field_size);
                    requested_field = true;
                }
                if args.flag_chars {
                    print!("{:1$} ", r.chars, field_size);
                    requested_field = true;
                }
                if args.flag_max_line_length {
                    print!("{:1$} ", r.max_line_length, field_size);
                    requested_field = true;
                }
                if !requested_field {
                    print!("{:1$} ", r.lines, field_size);
                    print!("{:1$} ", r.words, field_size);
                    print!("{:1$} ", r.bytes, field_size);
                }
                println!("{}", filename);
            },
            Err(ref e) => {
                errors_encountered = true;
                println_stderr!("wc: {}: {}", filename, e);
            },
        }
    }

    if errors_encountered {
        exit(1);
    }
}
<|endoftext|>


<commit_before>extern crate rustc_serialize;
extern crate docopt;
#[macro_use]
extern crate lust;

use std::io::{self, Write, Read};
use std::path::Path;
use std::fs::{File, metadata};
use docopt::Docopt;
use lust::Parser;
use lust::State;

macro_rules! try_ok {
    ($e:expr) => (match $e {
        Ok(res) => {
            res
        },
        Err(err) => {
            return println!("Whoops, error detected.\n{}.\n\
                             Please, try again...", err)
        }
    })
}

static USAGE: &'static str = "
Usage:
    lust [options] [<expr>]

Options:
    -f <file_path>, --file <file_path>          Evaluate expresions from file
    -i, --interactive                           Run REPL session
";

#[derive(RustcDecodable, Debug)]
struct CliArgs {
    arg_expr: Option<String>,
    flag_file: Option<String>,
    flag_interactive: bool,
}

#[cfg_attr(test, allow(dead_code))]
fn main() {
    let args = try_ok!(Docopt::new(USAGE).and_then(|d| d.decode::<CliArgs>()));
    let ref mut state = State::new("user".to_string());
    let mut last_evaled = None;

    if let Some(ref flag_file) = args.flag_file {
        let path = Path::new(flag_file);
        let md = metadata(path);
        if is_file_exists!(md) {
            if is_file!(md) {
                let mut file = try_ok!(File::open(&path));
                let ref mut buf = String::new();
                try_ok!(file.read_to_string(buf));
                for parsed_expr in Parser::new(buf.chars()) {
                    last_evaled = Some(try_ok!(state.eval(&try_ok!(parsed_expr))));
                }
            } else {
                return println!("Whoops, error detected.\n\
                                 Specified path is not a file.\n\
                                 Please, specify existing file.");
            }
        } else {
            return println!("Whoops, error detected.\n\
                             File doesn't exist.\n\
                             Please, specify existing file.");
        }
    }

    if let Some(ref arg_expr) = args.arg_expr {
        for parsed_expr in Parser::new(arg_expr.chars()) {
            last_evaled = Some(try_ok!(state.eval(&try_ok!(parsed_expr))))
        }
    }

    if args.flag_interactive {
        let stdin = io::stdin();
        let mut stdout = io::stdout();

        loop {
            print!("-> ");
            stdout.flush().ok();
            let ref mut buf = String::new();
            if try_ok!(stdin.read_line(buf)) > 0 {
                for expr in Parser::new(buf.chars()) {
                    match expr {
                        Ok(parsed_expr) => {
                            match state.eval(&parsed_expr) {
                                Ok(res) => {
                                    println!("{}", res);
                                },
                                Err(err) => {
                                    println!("Whoops, error detected.\n{}.\n\
                                              Please, try again...", err);
                                }
                            }
                        },
                        Err(err) => {
                            println!("Whoops, error detected.\n{}.\n\
                                      Please, try again...", err)
                        },
                    }
                }
            } else {
                return println!("\nHope you enjoyed.\nSee you...");
            }
        }
    } else if let Some(ref expr) = last_evaled {
        println!("{}", expr);
    }
}
<commit_msg>Add println_error macro<commit_after>extern crate rustc_serialize;
extern crate docopt;
#[macro_use]
extern crate lust;

use std::io::{self, Write, Read};
use std::path::Path;
use std::fs::{File, metadata};
use docopt::Docopt;
use lust::Parser;
use lust::State;

macro_rules! println_error {
    ($err:expr) => (println!("Whoops, error detected.\n{}.\n\
                              Please, try again...", $err))
}

macro_rules! try_ok {
    ($e:expr) => (match $e {
        Ok(res) => {
            res
        },
        Err(err) => {
            return println_error!(err)
        }
    })
}

static USAGE: &'static str = "
Usage:
    lust [options] [<expr>]

Options:
    -f <file_path>, --file <file_path>          Evaluate expresions from file
    -i, --interactive                           Run REPL session
";

#[derive(RustcDecodable, Debug)]
struct CliArgs {
    arg_expr: Option<String>,
    flag_file: Option<String>,
    flag_interactive: bool,
}

#[cfg_attr(test, allow(dead_code))]
fn main() {
    let args = try_ok!(Docopt::new(USAGE).and_then(|d| d.decode::<CliArgs>()));
    let ref mut state = State::new("user".to_string());
    let mut last_evaled = None;

    if let Some(ref flag_file) = args.flag_file {
        let path = Path::new(flag_file);
        let md = metadata(path);
        if is_file_exists!(md) {
            if is_file!(md) {
                let mut file = try_ok!(File::open(&path));
                let ref mut buf = String::new();
                try_ok!(file.read_to_string(buf));
                for parsed_expr in Parser::new(buf.chars()) {
                    last_evaled = Some(try_ok!(state.eval(&try_ok!(parsed_expr))));
                }
            } else {
                return println_error!("Specified path is not a file.");
            }
        } else {
            return println_error!("File doesn't exist.");
        }
    }

    if let Some(ref arg_expr) = args.arg_expr {
        for parsed_expr in Parser::new(arg_expr.chars()) {
            last_evaled = Some(try_ok!(state.eval(&try_ok!(parsed_expr))))
        }
    }

    if args.flag_interactive {
        let stdin = io::stdin();
        let mut stdout = io::stdout();

        loop {
            print!("-> ");
            stdout.flush().ok();
            let ref mut buf = String::new();
            if try_ok!(stdin.read_line(buf)) > 0 {
                for expr in Parser::new(buf.chars()) {
                    match expr {
                        Ok(parsed_expr) => {
                            match state.eval(&parsed_expr) {
                                Ok(res) => {
                                    println!("{}", res);
                                },
                                Err(err) => {
                                    println_error!(err);
                                }
                            }
                        },
                        Err(err) => {
                            println_error!(err);
                        },
                    }
                }
            } else {
                return println!("\nHope you enjoyed.\nSee you...");
            }
        }
    } else if let Some(ref expr) = last_evaled {
        println!("{}", expr);
    }
}
<|endoftext|>


<commit_before>extern crate colored;
extern crate json;
extern crate structopt;
extern crate syntax;

mod builder;
mod dot_printer;
mod error;
mod manifest;
mod printer;
mod tree;

use std::path;
use std::process;

use syntax::ast::NodeId;
use syntax::parse::{self, ParseSess};
use syntax::source_map;
use syntax::visit::Visitor;

use structopt::StructOpt;

use colored::*;

use builder::Builder;
use builder::Config as BuilderConfig;

use error::Error;

use manifest::{Manifest, Target};

use printer::Config as PrinterConfig;
use printer::Printer;

use dot_printer::Config as DotPrinterConfig;
use dot_printer::DotPrinter;

fn choose_target<'a>(args: &Arguments, manifest: &'a Manifest) -> Result<&'a Target, Error> {
    if args.lib {
        // If `--lib` is enabled use the first library target.
        manifest
            .targets
            .iter()
            .find(|t| t.is_lib())
            .ok_or(Error::NoLibraryTargetFound)
    } else if let Some(ref name) = args.bin {
        // If a binary target is specified use that.
        manifest
            .targets
            .iter()
            .find(|t| t.is_bin() && &t.name() == &name)
            .ok_or(Error::NoMatchingBinaryTargetFound)
    } else if manifest.targets.len() == 1 {
        // If neither `--lib` is enabled nor `--bin` target is specified but
        // there is only one target, use that.
        Ok(manifest.targets.first().unwrap())
    } else {
        // If there are multiple targets use the first library target.
        manifest
            .targets
            .iter()
            .find(|t| t.is_lib())
            .ok_or(Error::NoTargetProvided)
    }
}

fn run(args: &Arguments) -> Result<(), Error> {
    let manifest: Manifest = {
        let output = process::Command::new("cargo").arg("read-manifest").output();
        let stdout = try!(output.map_err(Error::CargoExecutionFailed)).stdout;
        let json_string = String::from_utf8(stdout).expect("Failed reading cargo output");
        Manifest::from_str(&json_string)?
    };

    // TODO: Check to see if build scripts really need to be ignored.
    //       Seems like they are not mistaken as orphans anyway.
    let build_scripts: Vec<path::PathBuf> = manifest
        .custom_builds()
        .iter()
        .map(|t| t.src_path().clone())
        .collect();

    let (target_name, src_path): (&str, &path::Path) = {
        let target: &Target = try!(choose_target(args, &manifest));
        (target.name(), target.src_path().as_path())
    };
    let parse_session = ParseSess::new(source_map::FilePathMapping::empty());

    syntax::with_globals(|| {
        let krate = try!(
            match parse::parse_crate_from_file(src_path.as_ref(), &parse_session) {
                Ok(_) if parse_session.span_diagnostic.has_errors() => Err(None),
                Ok(krate) => Ok(krate),
                Err(e) => Err(Some(e)),
            }
            .map_err(|e| Error::Syntax(format!("{:?}", e)))
        );

        let builder_config = BuilderConfig {
            include_orphans: args.orphans,
            ignored_files: build_scripts,
        };
        let mut builder = Builder::new(
            builder_config,
            target_name.to_string(),
            parse_session.source_map(),
        );
        builder.visit_mod(
            &krate.module,
            krate.span,
            &krate.attrs[..],
            NodeId::from(0_usize),
        );

        match args.command {
            Command::Graph {
                conditional,
                external,
                types,
            } => {
                let printer_config = DotPrinterConfig {
                    colored: !args.plain,
                    show_conditional: conditional,
                    show_external: external,
                    show_types: types,
                };
                println!("digraph something {{");
                let tree = builder.tree();
                let printer = DotPrinter::new(printer_config, tree);
                tree.accept(&mut vec![], &mut vec![], &printer);
                println!("}}");
            }
            Command::Tree => {
                let printer_config = PrinterConfig {
                    colored: !args.plain,
                };
                let printer = Printer::new(printer_config);
                println!();
                let tree = builder.tree();
                tree.accept(&mut vec![], &mut vec![], &printer);
                println!();
            }
        }

        Ok(())
    })
}

#[derive(StructOpt)]
#[structopt(
    name = "cargo-modules",
    about = "Print a crate's module tree or graph.",
    author = "",
    after_help = "If neither `--bin` nor `--example` are given,\n\
                  then if the project only has one bin target it will be run.\n\
                  Otherwise `--bin` specifies the bin target to run.\n\
                  At most one `--bin` can be provided.\n\
                  \n(On 'Windows' systems coloring is disabled. Sorry.)\n"
)]
struct Arguments {
    /// Include orphaned modules (i.e. unused files in /src).
    #[structopt(short = "o", long = "orphans")]
    orphans: bool,

    /// List modules of this package's library (overrides '--bin')
    #[structopt(short = "l", long = "lib")]
    lib: bool,

    /// Plain uncolored output.
    #[structopt(short = "p", long = "plain")]
    plain: bool,

    /// List modules of the specified binary
    #[structopt(short = "b", long = "bin")]
    bin: Option<String>,

    /// [Experimental] Enable support for edition 2018 of Rust (ignored)
    #[structopt(long = "enable-edition-2018")]
    _enable_edition_2018: bool,

    /// Sets an explicit crate path (ignored)
    #[structopt(name = "CRATE_DIR")]
    _dir: Option<String>,

    #[structopt(subcommand)]
    command: Command,
}

#[derive(StructOpt)]
enum Command {
    #[structopt(name = "tree", about = "Print a crate's module tree.", author = "")]
    Tree,
    #[structopt(
        name = "graph",
        about = "Print a crate's module graph.",
        author = "",
        after_help = "If you have xdot installed on your system, you can run this using:\n\
                      `cargo modules graph | xdot -`"
    )]
    Graph {
        /// Show external types.
        #[structopt(short = "e", long = "external")]
        external: bool,
        /// Show conditional modules.
        #[structopt(short = "c", long = "conditional")]
        conditional: bool,
        /// Plain uncolored output.
        #[structopt(short = "t", long = "types")]
        types: bool,
    },
}

fn main() {
    let arguments = Arguments::from_args();

    if let Err(error) = run(&arguments) {
        let error_string = match error {
            Error::CargoExecutionFailed(error) => {
                format!("Error: Failed to run `cargo` command.\n{:?}", error)
            }
            Error::InvalidManifestJson(error) => {
                format!("Error: Failed to parse JSON response.\n{:?}", error)
            }
            Error::NoLibraryTargetFound => "Error: No library target found.".to_string(),
            Error::NoMatchingBinaryTargetFound => {
                "Error: No matching binary target found.".to_string()
            }
            Error::NoTargetProvided => "Error: Please specify a target to process.".to_string(),
            Error::Syntax(error) => format!("Error: Failed to parse: {}", error),
        };
        println!("{}", error_string.red());
    }
}
<commit_msg>Refactor out unnecessary values<commit_after>extern crate colored;
extern crate json;
extern crate structopt;
extern crate syntax;

mod builder;
mod dot_printer;
mod error;
mod manifest;
mod printer;
mod tree;

use std::path;
use std::process;

use syntax::ast::NodeId;
use syntax::parse::{self, ParseSess};
use syntax::source_map;
use syntax::visit::Visitor;

use structopt::StructOpt;

use colored::*;

use builder::Builder;
use builder::Config as BuilderConfig;

use error::Error;

use manifest::{Manifest, Target};

use printer::Config as PrinterConfig;
use printer::Printer;

use dot_printer::Config as DotPrinterConfig;
use dot_printer::DotPrinter;

fn choose_target<'a>(args: &Arguments, manifest: &'a Manifest) -> Result<&'a Target, Error> {
    if args.lib {
        // If `--lib` is enabled use the first library target.
        manifest
            .targets
            .iter()
            .find(|t| t.is_lib())
            .ok_or(Error::NoLibraryTargetFound)
    } else if let Some(ref name) = args.bin {
        // If a binary target is specified use that.
        manifest
            .targets
            .iter()
            .find(|t| t.is_bin() && &t.name() == &name)
            .ok_or(Error::NoMatchingBinaryTargetFound)
    } else if manifest.targets.len() == 1 {
        // If neither `--lib` is enabled nor `--bin` target is specified but
        // there is only one target, use that.
        Ok(manifest.targets.first().unwrap())
    } else {
        // If there are multiple targets use the first library target.
        manifest
            .targets
            .iter()
            .find(|t| t.is_lib())
            .ok_or(Error::NoTargetProvided)
    }
}

fn run(args: &Arguments) -> Result<(), Error> {
    let manifest: Manifest = {
        let output = process::Command::new("cargo").arg("read-manifest").output();
        let stdout = try!(output.map_err(Error::CargoExecutionFailed)).stdout;
        let json_string = String::from_utf8(stdout).expect("Failed reading cargo output");
        Manifest::from_str(&json_string)?
    };

    // TODO: Check to see if build scripts really need to be ignored.
    //       Seems like they are not mistaken as orphans anyway.
    let build_scripts: Vec<path::PathBuf> = manifest
        .custom_builds()
        .iter()
        .map(|t| t.src_path().clone())
        .collect();

    let target: &Target = choose_target(args, &manifest)?;
    let parse_session = ParseSess::new(source_map::FilePathMapping::empty());

    syntax::with_globals(|| {
        let krate = try!(match parse::parse_crate_from_file(
            target.src_path().as_ref(),
            &parse_session
        ) {
            Ok(_) if parse_session.span_diagnostic.has_errors() => Err(None),
            Ok(krate) => Ok(krate),
            Err(e) => Err(Some(e)),
        }
        .map_err(|e| Error::Syntax(format!("{:?}", e))));

        let builder_config = BuilderConfig {
            include_orphans: args.orphans,
            ignored_files: build_scripts,
        };
        let mut builder = Builder::new(
            builder_config,
            target.name().clone(),
            parse_session.source_map(),
        );
        builder.visit_mod(
            &krate.module,
            krate.span,
            &krate.attrs[..],
            NodeId::from(0_usize),
        );

        match args.command {
            Command::Graph {
                conditional,
                external,
                types,
            } => {
                let printer_config = DotPrinterConfig {
                    colored: !args.plain,
                    show_conditional: conditional,
                    show_external: external,
                    show_types: types,
                };
                println!("digraph something {{");
                let tree = builder.tree();
                let printer = DotPrinter::new(printer_config, tree);
                tree.accept(&mut vec![], &mut vec![], &printer);
                println!("}}");
            }
            Command::Tree => {
                let printer_config = PrinterConfig {
                    colored: !args.plain,
                };
                let printer = Printer::new(printer_config);
                println!();
                let tree = builder.tree();
                tree.accept(&mut vec![], &mut vec![], &printer);
                println!();
            }
        }

        Ok(())
    })
}

#[derive(StructOpt)]
#[structopt(
    name = "cargo-modules",
    about = "Print a crate's module tree or graph.",
    author = "",
    after_help = "If neither `--bin` nor `--example` are given,\n\
                  then if the project only has one bin target it will be run.\n\
                  Otherwise `--bin` specifies the bin target to run.\n\
                  At most one `--bin` can be provided.\n\
                  \n(On 'Windows' systems coloring is disabled. Sorry.)\n"
)]
struct Arguments {
    /// Include orphaned modules (i.e. unused files in /src).
    #[structopt(short = "o", long = "orphans")]
    orphans: bool,

    /// List modules of this package's library (overrides '--bin')
    #[structopt(short = "l", long = "lib")]
    lib: bool,

    /// Plain uncolored output.
    #[structopt(short = "p", long = "plain")]
    plain: bool,

    /// List modules of the specified binary
    #[structopt(short = "b", long = "bin")]
    bin: Option<String>,

    /// [Experimental] Enable support for edition 2018 of Rust (ignored)
    #[structopt(long = "enable-edition-2018")]
    _enable_edition_2018: bool,

    /// Sets an explicit crate path (ignored)
    #[structopt(name = "CRATE_DIR")]
    _dir: Option<String>,

    #[structopt(subcommand)]
    command: Command,
}

#[derive(StructOpt)]
enum Command {
    #[structopt(name = "tree", about = "Print a crate's module tree.", author = "")]
    Tree,
    #[structopt(
        name = "graph",
        about = "Print a crate's module graph.",
        author = "",
        after_help = "If you have xdot installed on your system, you can run this using:\n\
                      `cargo modules graph | xdot -`"
    )]
    Graph {
        /// Show external types.
        #[structopt(short = "e", long = "external")]
        external: bool,
        /// Show conditional modules.
        #[structopt(short = "c", long = "conditional")]
        conditional: bool,
        /// Plain uncolored output.
        #[structopt(short = "t", long = "types")]
        types: bool,
    },
}

fn main() {
    let arguments = Arguments::from_args();

    if let Err(error) = run(&arguments) {
        let error_string = match error {
            Error::CargoExecutionFailed(error) => {
                format!("Error: Failed to run `cargo` command.\n{:?}", error)
            }
            Error::InvalidManifestJson(error) => {
                format!("Error: Failed to parse JSON response.\n{:?}", error)
            }
            Error::NoLibraryTargetFound => "Error: No library target found.".to_string(),
            Error::NoMatchingBinaryTargetFound => {
                "Error: No matching binary target found.".to_string()
            }
            Error::NoTargetProvided => "Error: Please specify a target to process.".to_string(),
            Error::Syntax(error) => format!("Error: Failed to parse: {}", error),
        };
        println!("{}", error_string.red());
    }
}
<|endoftext|>


<commit_before>// logging
#[macro_use]
extern crate log;
extern crate pretty_env_logger;

// modbus
extern crate libmodbus_rs;

// iron
extern crate iron;
extern crate router;
extern crate staticfile;

// json
extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;

// misc
extern crate clap;
extern crate ctrlc;
extern crate hex_slice;
extern crate retry;

// datatypes
#[macro_use]
extern crate enum_primitive;
#[macro_use]
extern crate bitflags;

use std::fs;
use std::os::unix::fs::FileTypeExt;
use std::path::Path;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Mutex};

use clap::{App, Arg};

use iron::headers::{AccessControlAllowMethods, AccessControlAllowOrigin};
use iron::method::Method;
use iron::middleware::Handler;
use iron::mime::Mime;
use iron::prelude::*;
use iron::status;
use iron::Iron;
use router::Router;
use staticfile::Static;

mod sunsaver_connection;
use crate::sunsaver_connection::{FileSunSaverConnection, ModbusSunSaverConnection, SunSaverConnection};
mod sunsaver;
use crate::sunsaver::{ArrayFault, ChargeState, LoggedResponseDay};
mod api;
use crate::api::*;

#[derive(Clone)]
struct ApiHandler {
    connection: Arc<Mutex<Box<SunSaverConnection>>>,
}

impl ApiHandler {
    fn new(connection: Box<SunSaverConnection>) -> ApiHandler {
        ApiHandler {
            connection: Arc::new(Mutex::new(connection)),
        }
    }
}

unsafe impl Send for ApiHandler {}
unsafe impl Sync for ApiHandler {}

impl Handler for ApiHandler {
    fn handle(&self, req: &mut Request) -> IronResult<Response> {
        debug!("{:?}", req.url);

        let mut response = Response::new();
        response.headers.set(AccessControlAllowMethods(vec![Method::Get]));
        response.headers.set(AccessControlAllowOrigin::Any);
        let mime: Mime = "application/json".parse().unwrap();
        response = response.set(mime);

        let path = req.url.path();
        let last_path = path.clone().pop().unwrap();
        match last_path {
            "status" => {
                let connection = self.connection.clone();
                let mut unlocked_connection = connection.lock().unwrap();
                let a = ApiStatusResponse::from(unlocked_connection.read_status());
                let b = serde_json::to_string_pretty(&a).unwrap();
                response = response.set((status::Ok, b));
            }
            "logged" => {
                let connection = self.connection.clone();
                let mut unlocked_connection = connection.lock().unwrap();
                let a = ApiLoggedResponse::from(unlocked_connection.read_logged());
                let b = serde_json::to_string_pretty(&a).unwrap();
                response = response.set((status::Ok, b));
            }
            _ => {
                response = response.set((status::NotFound, String::new()));
            }
        };

        Ok(response)
    }
}

fn is_rtu_modbus_device(path: &Path) -> bool {
    let metadata = fs::metadata(path).unwrap();
    let file_type = metadata.file_type();
    debug!("is_socket for {:?} had metadata {:?}", path, metadata);
    debug!(
        "is_socket for {:?} is_block_device: {}, is_char_device: {}, is_fifo: {}, is_socket: {}",
        path,
        file_type.is_block_device(),
        file_type.is_char_device(),
        file_type.is_fifo(),
        file_type.is_socket()
    );
    metadata.file_type().is_char_device()
}

static CLI_ARG_DEVICE: &'static str = "DEVICE";
static CLI_ARG_PORT: &'static str = "PORT";
static CLI_ARG_WEB_ROOT: &'static str = "WEB_ROOT";

fn is_port_number(port_string: String) -> Result<(), String> {
    port_string.parse::<u16>().map(|_| ()).map_err(|_| String::from("Invalid port number"))
}

fn main() {
    assert!(pretty_env_logger::try_init().is_ok());

    let matches = App::new(env!("CARGO_PKG_NAME"))
        .version(env!("CARGO_PKG_VERSION"))
        .about("HTTP RESTful server for SunSaver MPPT ModBus data")
        .author("Guy Taylor <thebiggerguy.co.uk@gmail.com>")
        .arg(
            Arg::with_name(CLI_ARG_DEVICE)
                .help("Serial device e.g. /dev/ttyUSB0")
                .long("device")
                .short("d")
                .takes_value(true)
                .empty_values(false)
                .required(true),
        )
        .arg(
            Arg::with_name(CLI_ARG_PORT)
                .help("HTTP server port")
                .long("port")
                .short("p")
                .takes_value(true)
                .empty_values(false)
                .required(false)
                .default_value("8080")
                .validator(is_port_number),
        )
        .arg(
            Arg::with_name(CLI_ARG_WEB_ROOT)
                .help("HTTP server root folder")
                .long("webroot")
                .takes_value(true)
                .empty_values(false)
                .required(false)
                .default_value("web"),
        )
        .get_matches();

    let serial_interface = Path::new(matches.value_of(CLI_ARG_DEVICE).unwrap());
    let port_number = matches.value_of(CLI_ARG_PORT).unwrap().parse::<u16>().unwrap();
    let web_root = Path::new(matches.value_of(CLI_ARG_WEB_ROOT).unwrap());

    if !serial_interface.exists() {
        panic!("Device does not exists: {:?}", serial_interface);
    }

    let connection: Box<SunSaverConnection> = if is_rtu_modbus_device(serial_interface) {
        info!("Device is a socket. Using Modbus");
        Box::new(ModbusSunSaverConnection::open(serial_interface))
    } else {
        info!("Device is not a socket. Using File");
        Box::new(FileSunSaverConnection::open(serial_interface))
    };

    let api_handler = ApiHandler::new(connection);
    let static_handler = Static::new(web_root);

    let mut router = Router::new();
    router.get("/", static_handler.clone(), "index");
    router.get("/:filepath(*)", static_handler.clone(), "static");
    router.get("/api/v1/status", api_handler.clone(), "api_v1_status");
    router.get("/api/v1/logged", api_handler.clone(), "api_v2_logger");

    let running = Arc::new(AtomicBool::new(true));
    let r = running.clone();
    ctrlc::set_handler(move || {
        r.store(false, Ordering::SeqCst);
        info!("Cought Ctrl-C");
    })
    .expect("Error setting Ctrl-C handler");

    info!("Starting server ...");
    let bind_address = format!("0.0.0.0:{}", port_number);
    let mut listening = Iron::new(router).http(&bind_address).unwrap();
    info!("Started server {}", bind_address);

    info!("Use Ctrl-C to stop");
    while running.load(Ordering::SeqCst) {}
    listening.close().unwrap();
}

#[cfg(test)]
mod test {
    extern crate tempdir;

    use std::fs::OpenOptions;

    use self::tempdir::TempDir;

    use super::*;

    #[test]
    fn is_rtu_modbus_device_test() {
        assert_eq!(is_rtu_modbus_device(Path::new("/dev/zero")), true); // TODO
        assert_eq!(is_rtu_modbus_device(Path::new("/dev/urandom")), true); // TODO
        assert_eq!(is_rtu_modbus_device(Path::new("/dev/tty")), true);
        let tty_usb = Path::new("/dev/ttyUSB0");
        if tty_usb.exists() {
            assert_eq!(is_rtu_modbus_device(tty_usb), true);
        }

        let temp_dir = TempDir::new(concat!(module_path!(), "is_rtu_modbus_device_test")).unwrap();
        let test_file = temp_dir.path().join("test");
        OpenOptions::new().create(true).write(true).open(&test_file).unwrap();
        assert_eq!(is_rtu_modbus_device(test_file.as_path()), false);
    }
}
<commit_msg>Fixup code to be 2018 best practice<commit_after>// logging
#[macro_use]
extern crate log;
use pretty_env_logger;

// json
#[macro_use]
extern crate serde_derive;
use serde_json;

// misc
use ctrlc;

// datatypes
#[macro_use]
extern crate enum_primitive;
#[macro_use]
extern crate bitflags;

use std::fs;
use std::os::unix::fs::FileTypeExt;
use std::path::Path;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Mutex};

use clap::{App, Arg};

use iron::headers::{AccessControlAllowMethods, AccessControlAllowOrigin};
use iron::method::Method;
use iron::middleware::Handler;
use iron::mime::Mime;
use iron::prelude::*;
use iron::status;
use iron::Iron;
use router::Router;
use staticfile::Static;

mod sunsaver_connection;
use crate::sunsaver_connection::{FileSunSaverConnection, ModbusSunSaverConnection, SunSaverConnection};
mod sunsaver;
use crate::sunsaver::{ArrayFault, ChargeState, LoggedResponseDay};
mod api;
use crate::api::*;

#[derive(Clone)]
struct ApiHandler {
    connection: Arc<Mutex<Box<dyn SunSaverConnection>>>,
}

impl ApiHandler {
    fn new(connection: Box<dyn SunSaverConnection>) -> ApiHandler {
        ApiHandler {
            connection: Arc::new(Mutex::new(connection)),
        }
    }
}

unsafe impl Send for ApiHandler {}
unsafe impl Sync for ApiHandler {}

impl Handler for ApiHandler {
    fn handle(&self, req: &mut Request<'_, '_>) -> IronResult<Response> {
        debug!("{:?}", req.url);

        let mut response = Response::new();
        response.headers.set(AccessControlAllowMethods(vec![Method::Get]));
        response.headers.set(AccessControlAllowOrigin::Any);
        let mime: Mime = "application/json".parse().unwrap();
        response = response.set(mime);

        let path = req.url.path();
        let last_path = path.clone().pop().unwrap();
        match last_path {
            "status" => {
                let connection = self.connection.clone();
                let mut unlocked_connection = connection.lock().unwrap();
                let a = ApiStatusResponse::from(unlocked_connection.read_status());
                let b = serde_json::to_string_pretty(&a).unwrap();
                response = response.set((status::Ok, b));
            }
            "logged" => {
                let connection = self.connection.clone();
                let mut unlocked_connection = connection.lock().unwrap();
                let a = ApiLoggedResponse::from(unlocked_connection.read_logged());
                let b = serde_json::to_string_pretty(&a).unwrap();
                response = response.set((status::Ok, b));
            }
            _ => {
                response = response.set((status::NotFound, String::new()));
            }
        };

        Ok(response)
    }
}

fn is_rtu_modbus_device(path: &Path) -> bool {
    let metadata = fs::metadata(path).unwrap();
    let file_type = metadata.file_type();
    debug!("is_socket for {:?} had metadata {:?}", path, metadata);
    debug!(
        "is_socket for {:?} is_block_device: {}, is_char_device: {}, is_fifo: {}, is_socket: {}",
        path,
        file_type.is_block_device(),
        file_type.is_char_device(),
        file_type.is_fifo(),
        file_type.is_socket()
    );
    metadata.file_type().is_char_device()
}

static CLI_ARG_DEVICE: &'static str = "DEVICE";
static CLI_ARG_PORT: &'static str = "PORT";
static CLI_ARG_WEB_ROOT: &'static str = "WEB_ROOT";

fn is_port_number(port_string: String) -> Result<(), String> {
    port_string.parse::<u16>().map(|_| ()).map_err(|_| String::from("Invalid port number"))
}

fn main() {
    assert!(pretty_env_logger::try_init().is_ok());

    let matches = App::new(env!("CARGO_PKG_NAME"))
        .version(env!("CARGO_PKG_VERSION"))
        .about("HTTP RESTful server for SunSaver MPPT ModBus data")
        .author("Guy Taylor <thebiggerguy.co.uk@gmail.com>")
        .arg(
            Arg::with_name(CLI_ARG_DEVICE)
                .help("Serial device e.g. /dev/ttyUSB0")
                .long("device")
                .short("d")
                .takes_value(true)
                .empty_values(false)
                .required(true),
        )
        .arg(
            Arg::with_name(CLI_ARG_PORT)
                .help("HTTP server port")
                .long("port")
                .short("p")
                .takes_value(true)
                .empty_values(false)
                .required(false)
                .default_value("8080")
                .validator(is_port_number),
        )
        .arg(
            Arg::with_name(CLI_ARG_WEB_ROOT)
                .help("HTTP server root folder")
                .long("webroot")
                .takes_value(true)
                .empty_values(false)
                .required(false)
                .default_value("web"),
        )
        .get_matches();

    let serial_interface = Path::new(matches.value_of(CLI_ARG_DEVICE).unwrap());
    let port_number = matches.value_of(CLI_ARG_PORT).unwrap().parse::<u16>().unwrap();
    let web_root = Path::new(matches.value_of(CLI_ARG_WEB_ROOT).unwrap());

    if !serial_interface.exists() {
        panic!("Device does not exists: {:?}", serial_interface);
    }

    let connection: Box<dyn SunSaverConnection> = if is_rtu_modbus_device(serial_interface) {
        info!("Device is a socket. Using Modbus");
        Box::new(ModbusSunSaverConnection::open(serial_interface))
    } else {
        info!("Device is not a socket. Using File");
        Box::new(FileSunSaverConnection::open(serial_interface))
    };

    let api_handler = ApiHandler::new(connection);
    let static_handler = Static::new(web_root);

    let mut router = Router::new();
    router.get("/", static_handler.clone(), "index");
    router.get("/:filepath(*)", static_handler.clone(), "static");
    router.get("/api/v1/status", api_handler.clone(), "api_v1_status");
    router.get("/api/v1/logged", api_handler.clone(), "api_v2_logger");

    let running = Arc::new(AtomicBool::new(true));
    let r = running.clone();
    ctrlc::set_handler(move || {
        r.store(false, Ordering::SeqCst);
        info!("Cought Ctrl-C");
    })
    .expect("Error setting Ctrl-C handler");

    info!("Starting server ...");
    let bind_address = format!("0.0.0.0:{}", port_number);
    let mut listening = Iron::new(router).http(&bind_address).unwrap();
    info!("Started server {}", bind_address);

    info!("Use Ctrl-C to stop");
    while running.load(Ordering::SeqCst) {}
    listening.close().unwrap();
}

#[cfg(test)]
mod test {
    use tempdir;

    use std::fs::OpenOptions;

    use self::tempdir::TempDir;

    use super::*;

    #[test]
    fn is_rtu_modbus_device_test() {
        assert_eq!(is_rtu_modbus_device(Path::new("/dev/zero")), true); // TODO
        assert_eq!(is_rtu_modbus_device(Path::new("/dev/urandom")), true); // TODO
        assert_eq!(is_rtu_modbus_device(Path::new("/dev/tty")), true);
        let tty_usb = Path::new("/dev/ttyUSB0");
        if tty_usb.exists() {
            assert_eq!(is_rtu_modbus_device(tty_usb), true);
        }

        let temp_dir = TempDir::new(concat!(module_path!(), "is_rtu_modbus_device_test")).unwrap();
        let test_file = temp_dir.path().join("test");
        OpenOptions::new().create(true).write(true).open(&test_file).unwrap();
        assert_eq!(is_rtu_modbus_device(test_file.as_path()), false);
    }
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate clap;
extern crate to;

use to::database::Database;
use to::dir;
use to::cli::Action;
use to::{ToResult, ToError, Bookmark};
use clap::{App, Arg, ArgGroup};
use std::path::PathBuf;
use std::fs;

fn main() {
    let version = crate_version!();
    let actions = [
        Arg::with_name("get")
            .help("Show bookmark information")
            .long("info")
            .short("i"),

        // to --save,--put # Save current dir as a bookmark
        // to --save,--put foo # Save current directory as bookmark foo
        Arg::with_name("put")
            .help("Save bookmark")
            .long("save")
            .short("s"),

        // to --delete foo # Delete bookmark foo
        Arg::with_name("delete")
            .help("Delete bookmark")
            .long("delete")
            .short("d"),

        Arg::with_name("list")
            .help("List all bookmarks")
            .long("list")
            .short("l"),

        Arg::with_name("last")
            .help("Change current working directory to last")
            .long("last"),
    ];
    let matches = App::new("to")
                    .about("Bookmark directories")
                    .version(version)
                    .author("Jason Campbell <jason@artifact.sh>")
                    .arg(Arg::with_name("name")
                        .help("Name of the bookamrk")
                        .index(1))
                    .arg(Arg::with_name("directory")
                        .help("Path of the bookamrk")
                        .index(2))
                    .args(&actions)
                    .get_matches();

    let request = match to::cli::parse_matches(matches) {
        Ok(value) => value,
        Err(err) => panic!(err),
    };

    println!("CLI Request: {:?}", request);

    // NEXT: Store get, put, delete, list
    let config_dir = match dir::config() {
        Ok(value) => value,
        Err(err) => panic!(err),
    };

    let mut store = match Database::open(config_dir) {
        Ok(db) => db,
        Err(e) => panic!("failed to open database: {:?}", e),
    };

    println!("store {:?}", store);
    let result = match request.action {
        Action::Put => store.put(request.name, request.directory),
        Action::Get => match store.get(request.name) {
            Ok(bookmark) => {
                println!("bookmark {:?}", bookmark);
                Ok(())
            },
            Err(err) => Err(err),
        },
        _ => panic!("NOT IMPLEMENTED!"),
    };

    match result {
        Ok(value) => println!("success! {:?}", value),
        Err(err) => panic!(err),
    }
}
<commit_msg>Removes unused imports.<commit_after>#[macro_use]
extern crate clap;
extern crate to;

use to::database::Database;
use to::dir;
use to::cli::Action;
use clap::{App, Arg};

fn main() {
    let version = crate_version!();
    let actions = [
        Arg::with_name("get")
            .help("Show bookmark information")
            .long("info")
            .short("i"),

        // to --save,--put # Save current dir as a bookmark
        // to --save,--put foo # Save current directory as bookmark foo
        Arg::with_name("put")
            .help("Save bookmark")
            .long("save")
            .short("s"),

        // to --delete foo # Delete bookmark foo
        Arg::with_name("delete")
            .help("Delete bookmark")
            .long("delete")
            .short("d"),

        Arg::with_name("list")
            .help("List all bookmarks")
            .long("list")
            .short("l"),

        Arg::with_name("last")
            .help("Change current working directory to last")
            .long("last"),
    ];
    let matches = App::new("to")
                    .about("Bookmark directories")
                    .version(version)
                    .author("Jason Campbell <jason@artifact.sh>")
                    .arg(Arg::with_name("name")
                        .help("Name of the bookamrk")
                        .index(1))
                    .arg(Arg::with_name("directory")
                        .help("Path of the bookamrk")
                        .index(2))
                    .args(&actions)
                    .get_matches();

    let request = match to::cli::parse_matches(matches) {
        Ok(value) => value,
        Err(err) => panic!(err),
    };

    println!("CLI Request: {:?}", request);

    // NEXT: Store get, put, delete, list
    let config_dir = match dir::config() {
        Ok(value) => value,
        Err(err) => panic!(err),
    };

    let mut store = match Database::open(config_dir) {
        Ok(db) => db,
        Err(e) => panic!("failed to open database: {:?}", e),
    };

    println!("store {:?}", store);
    let result = match request.action {
        Action::Put => store.put(request.name, request.directory),
        Action::Get => match store.get(request.name) {
            Ok(bookmark) => {
                println!("bookmark {:?}", bookmark);
                Ok(())
            },
            Err(err) => Err(err),
        },
        _ => panic!("NOT IMPLEMENTED!"),
    };

    match result {
        Ok(value) => println!("success! {:?}", value),
        Err(err) => panic!(err),
    }
}
<|endoftext|>


<commit_before>// Spec found at https://tools.ietf.org/html/rfc959

/*
 * FIXME: Filezilla says: Le serveur ne supporte pas les caractères non-ASCII.
 * FIXME: ftp cli says "WARNING! 71 bare linefeeds received in ASCII mode" when retrieving a file.
 */

#![feature(proc_macro, conservative_impl_trait, generators)]

extern crate bytes;
#[macro_use]
extern crate cfg_if;
extern crate futures_await as futures;
extern crate time;
extern crate tokio_core;
extern crate tokio_io;

mod cmd; // FIXME: rename this module.
mod codec;
mod ftp;

use std::env;
use std::fs::{DirEntry, File, Metadata, read_dir};
use std::io::{self, Read, Write};
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use std::path::{Path, PathBuf};

use futures::{Sink, Stream};
use futures::prelude::{async, await};
use futures::stream::{SplitSink, SplitStream};
use tokio_core::reactor::{Core, Handle};
use tokio_core::net::{TcpListener, TcpStream};
use tokio_io::AsyncRead;
use tokio_io::codec::Framed;

use cmd::{Command, TransferType};
use codec::{FtpCodec, StringCodec};
use ftp::{Answer, ResultCode};

const MONTHS: [&'static str; 12] = ["Jan", "Feb", "Mar", "Apr", "May", "Jun",
                                    "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];

type DataReader = SplitStream<Framed<TcpStream, StringCodec>>;
type DataWriter = SplitSink<Framed<TcpStream, StringCodec>>;
type Writer = SplitSink<Framed<TcpStream, FtpCodec>>;

cfg_if! {
    if #[cfg(windows)] {
        fn get_file_info(meta: &Metadata) -> (time::Tm, u64) {
            use std::os::windows::prelude::*;
            (time::at(time::Timespec::new(meta.last_write_time())), meta.file_size())
        }
    } else {
        fn get_file_info(meta: &Metadata) -> (time::Tm, u64) {
            use std::os::unix::prelude::*;
            (time::at(time::Timespec::new(meta.mtime(), 0)), meta.size())
        }
    }
}

fn add_file_info(entry: DirEntry, out: &mut String) {
    let path = entry.path(); // TODO: handle error.
    let extra = if path.is_dir() { "/" } else { "" };
    let is_dir = if path.is_dir() { "d" } else { "-" };

    let meta = ::std::fs::metadata(&path).unwrap(); // TODO: handle error.
    let (time, file_size) = get_file_info(&meta);
    let path = path.to_str().unwrap().split("/").last().unwrap(); // TODO: handle error.
    let file_str = format!("{} {} {} {} {}:{} {}{}\r\n",
                           is_dir,
                           file_size,
                           MONTHS[time.tm_mon as usize],
                           time.tm_mday,
                           time.tm_hour,
                           time.tm_min,
                           path,
                           extra);
    out.push_str(&file_str);
    println!("==> {:?}", &file_str);
}

#[allow(dead_code)]
struct Client {
    address: String, // TODO: remove this?
    cwd: PathBuf,
    data_port: Option<u16>,
    data_reader: Option<DataReader>,
    data_writer: Option<DataWriter>,
    handle: Handle,
    name: Option<String>,
    server_root: PathBuf,
    transfer_type: TransferType,
    writer: Writer,
}

impl Client {
    fn new(address: String, handle: Handle, writer: Writer, server_root: PathBuf) -> Client {
        Client {
            address,
            cwd: PathBuf::from(""),
            data_port: None,
            data_reader: None,
            data_writer: None,
            handle,
            name: None,
            server_root,
            transfer_type: TransferType::Ascii,
            writer,
        }
    }

    #[async]
    fn handle_cmd(mut self, cmd: Command) -> Result<Self, ()> {
        println!("Received command: {:?}", cmd);
        match cmd {
            Command::Auth =>
                self = await!(self.send(Answer::new(ResultCode::CommandNotImplemented, "Not implemented")))?
            ,
            Command::Cwd(directory) => self = await!(self.cwd(directory))?,
            Command::List(path) => self = await!(self.list(path))?,
            Command::Pasv => self = await!(self.pasv())?,
            Command::Port(port) => {
                self.data_port = Some(port);
                self = await!(self.send(Answer::new(ResultCode::Ok, &format!("Data port is now {}", port))))?;
            }
            Command::Pwd => {
                let message = format!("\"/{}\" ", self.cwd.to_str().unwrap());
                self = await!(self.send(Answer::new(ResultCode::PATHNAMECreated, &message)))?;
            } // TODO: handle error.
            Command::Quit => self = await!(self.quit())?,
            Command::Retr(file) => self = await!(self.retr(file))?,
            Command::Stor(file) => self = await!(self.stor(file))?,
            Command::Syst => {
                self = await!(self.send(Answer::new(ResultCode::Ok, "I won't tell!")))?;
            }
            Command::Type(typ) => {
                self.transfer_type = typ;
                self = await!(self.send(Answer::new(ResultCode::Ok, "Transfer type changed successfully")))?;
            }
            Command::Unknown =>
                self = await!(self.send(Answer::new(ResultCode::UnknownCommand, "Not implemented")))?
            ,
            Command::User(content) => {
                if content.is_empty() {
                    self = await!(self.send(Answer::new(ResultCode::InvalidParameterOrArgument, "Invalid username")))?;
                } else {
                    self.name = Some(content.to_owned());
                    self = await!(self.send(Answer::new(ResultCode::UserloggedIn, &format!("Welcome {}!", content))))?;
                }
            }
            Command::CdUp => {
                if let Some(path) = self.cwd.parent().map(Path::to_path_buf) {
                    self.cwd = path;
                }
                self = await!(self.send(Answer::new(ResultCode::Ok, "Done")))?;
            }
        }
        Ok(self)
    }

    fn close_data_connection(&mut self) {
        self.data_reader = None;
        self.data_writer = None;
    }

    fn complete_path(self, path: PathBuf) -> Result<(PathBuf, Self), io::Error> {
        let directory = self.server_root.join(if path.has_root() {
            path.iter().skip(1).collect()
        } else {
            path
        });
        let dir = directory.canonicalize();
        if let Ok(ref dir) = dir {
            if !dir.starts_with(&self.server_root) {
                return Err(io::ErrorKind::PermissionDenied.into());
            }
        }
        dir.map(|dir| (dir, self))
    }

    #[async]
    fn cwd(mut self, directory: String) -> Result<Self, ()> {
        let directory = PathBuf::from(directory);
        let path = self.cwd.join(&directory);
        let (dir, new_self) = self.complete_path(path).unwrap(); // TODO: handle error.
        self = new_self;
        self.cwd = dir.strip_prefix(&self.server_root).unwrap().to_path_buf(); // TODO: handle error.
        self = await!(self.send(Answer::new(ResultCode::Ok,
                                            &format!("Directory changed to \"{}\"", directory.display()))))?;
        Ok(self)
    }

    #[async]
    fn list(mut self, path: Option<PathBuf>) -> Result<Self, ()> {
        if self.data_writer.is_some() {
            let path = self.cwd.join(path.unwrap_or_default());
            let directory = PathBuf::from(&path);
            let (dir, new_self) = self.complete_path(directory).unwrap(); // TODO: handle error.
            self = new_self;
            self = await!(self.send(Answer::new(ResultCode::DataConnectionAlreadyOpen,
                                                "Starting to list directory...")))?;
            let mut out = String::new();
            for entry in read_dir(dir).unwrap() { // TODO: handle error.
                add_file_info(entry.unwrap(), &mut out); // TODO: handle error.
            }
            self = await!(self.send_data(out))?;
            println!("-> and done!");
        } else {
            self = await!(self.send(Answer::new(ResultCode::ConnectionClosed, "No opened data connection")))?;
        }
        if self.data_writer.is_some() {
            self.close_data_connection();
            self = await!(self.send(Answer::new(ResultCode::ClosingDataConnection, "Transfer done")))?;
        }
        Ok(self)
    }

    #[async]
    fn pasv(mut self) -> Result<Self, ()> {
        let port =
            if let Some(port) = self.data_port {
                port
            } else {
                0
            };
        if self.data_writer.is_some() {
            self = await!(self.send(Answer::new(ResultCode::DataConnectionAlreadyOpen, "Already listening...")))?;
            return Ok(self);
        }

        let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), port);
        let listener = TcpListener::bind(&addr, &self.handle).unwrap(); // TODO: handle error
        let port = listener.local_addr().unwrap().port(); // TODO: handle error.

        self = await!(self.send(Answer::new(ResultCode::EnteringPassiveMode,
                              &format!("127,0,0,1,{},{}", port >> 8, port & 0xFF))))?;

        println!("Waiting clients on port {}...", port);
        // TODO: use into_future() instead of for loop?
        #[async]
        for (stream, _rest) in listener.incoming().map_err(|_| ()) {
            let (writer, reader) = stream.framed(StringCodec).split();
            self.data_writer = Some(writer);
            self.data_reader = Some(reader);
            break;
        }
        Ok(self)
    }

    #[async]
    fn quit(mut self) -> Result<Self, ()> {
        if self.data_writer.is_some() {
            unimplemented!();
        } else {
            self = await!(self.send(Answer::new(ResultCode::ServiceClosingControlConnection, "Closing connection...")))?;
            self.writer.close().unwrap(); // TODO: handle error.
        }
        Ok(self)
    }

    #[async]
    fn retr(mut self, path: PathBuf) -> Result<Self, ()> {
        // TODO: check if multiple data connection can be opened at the same time.
        if self.data_writer.is_some() {
            let path = self.cwd.join(path);
            let (path, new_self) = self.complete_path(path).unwrap(); // TODO: handle error.
            self = new_self;
            if path.is_file() {
                self = await!(self.send(Answer::new(ResultCode::DataConnectionAlreadyOpen, "Starting to send file...")))?;
                let mut file = File::open(path).unwrap(); // TODO: handle error.
                let mut out = String::new();
                file.read_to_string(&mut out).unwrap(); // TODO: handle error.
                self = await!(self.send_data(out))?;
                println!("-> file transfer done!");
            } else {
                self = await!(self.send(Answer::new(ResultCode::LocalErrorInProcessing,
                                      &format!("\"{}\" doesn't exist", path.to_str().unwrap()))))?;
            }
        } else {
            self = await!(self.send(Answer::new(ResultCode::ConnectionClosed, "No opened data connection")))?;
        }
        if self.data_writer.is_some() {
            self.close_data_connection();
            self = await!(self.send(Answer::new(ResultCode::ClosingDataConnection, "Transfer done")))?;
        }
        Ok(self)
    }

    #[async]
    fn stor(mut self, path: PathBuf) -> Result<Self, ()> {
        if self.data_reader.is_some() {
            let path = self.cwd.join(path);
            // TODO: check if path contains characters that are not allowed (cannot use
            // canonicalize for a non-existing path).
            self = await!(self.send(Answer::new(ResultCode::DataConnectionAlreadyOpen, "Starting to send file...")))?;
            let (data, new_self) = await!(self.receive_data())?;
            self = new_self;
            let mut file = File::create(path).unwrap(); // TODO: handle error.
            write!(file, "{}", data).unwrap(); // TODO: handle error.
            println!("-> file transfer done!");
            self.close_data_connection();
            self = await!(self.send(Answer::new(ResultCode::ClosingDataConnection, "Transfer done")))?;
        } else {
            self = await!(self.send(Answer::new(ResultCode::ConnectionClosed, "No opened data connection")))?;
        }
        Ok(self)
    }

    #[async]
    fn receive_data(mut self) -> Result<(String, Self), ()> {
        let mut file_data = String::new();
        // NOTE: have to use this weird trick because of futures-await.
        // TODO: fix that when the lifetime stuff is improved for generators.
        if self.data_reader.is_none() {
            return Ok((String::new(), self));
        }
        let reader = self.data_reader.take().unwrap();
        #[async]
        for data in reader.map_err(|_| ()) {
            file_data.push_str(&data);
        }
        Ok((file_data, self))
    }

    #[async]
    fn send(mut self, answer: Answer) -> Result<Self, ()> {
        self.writer = await!(self.writer.send(answer)).map_err(|_| ())?;
        Ok(self)
    }

    #[async]
    fn send_data(mut self, data: String) -> Result<Self, ()> {
        if let Some(writer) = self.data_writer {
            self.data_writer = Some(await!(writer.send(data)).map_err(|_| ())?);
        }
        Ok(self)
    }
}

#[async]
fn handle_client(stream: TcpStream, handle: Handle, address: String, server_root: PathBuf) -> Result<(), ()> {
    let (writer, reader) = stream.framed(FtpCodec).split();
    let writer = await!(writer.send(Answer::new(ResultCode::ServiceReadyForNewUser, "Welcome to this FTP server!")))
        .map_err(|_| ())?;
    let mut client = Client::new(address, handle, writer, server_root);
    #[async]
    for cmd in reader.map_err(|_| ()) {
        client = await!(client.handle_cmd(cmd))?;
    }
    println!("Client closed");
    Ok(())
}

#[async]
fn server(handle: Handle, server_root: PathBuf) -> io::Result<()> {
    let port = 1234;
    let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), port);
    let listener = TcpListener::bind(&addr, &handle).unwrap();

    println!("Waiting clients on port {}...", port);
    #[async]
    for (stream, addr) in listener.incoming() {
        let address = format!("[address : {}]", addr);
        println!("New client: {}", address);
        handle.spawn(handle_client(stream, handle.clone(), address, server_root.clone()));
        println!("Waiting another client...");
    }
    Ok(())
}

fn main() {
    let mut core = Core::new()
        .expect("Cannot create tokio Core");
    let handle = core.handle();

    let server_root = env::current_dir().unwrap(); // TODO: handle error.

    core.run(server(handle, server_root))
        .expect("Run tokio server");
}
<commit_msg>Add TODO comments<commit_after>// Spec found at https://tools.ietf.org/html/rfc959

/*
 * FIXME: Filezilla says: Le serveur ne supporte pas les caractères non-ASCII.
 * FIXME: ftp cli says "WARNING! 71 bare linefeeds received in ASCII mode" when retrieving a file.
 */

#![feature(proc_macro, conservative_impl_trait, generators)]

extern crate bytes;
#[macro_use]
extern crate cfg_if;
extern crate futures_await as futures;
extern crate time;
extern crate tokio_core;
extern crate tokio_io;

mod cmd; // FIXME: rename this module.
mod codec;
mod ftp;

use std::env;
use std::fs::{DirEntry, File, Metadata, read_dir};
use std::io::{self, Read, Write};
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use std::path::{Path, PathBuf};

use futures::{Sink, Stream};
use futures::prelude::{async, await};
use futures::stream::{SplitSink, SplitStream};
use tokio_core::reactor::{Core, Handle};
use tokio_core::net::{TcpListener, TcpStream};
use tokio_io::AsyncRead;
use tokio_io::codec::Framed;

use cmd::{Command, TransferType};
use codec::{FtpCodec, StringCodec};
use ftp::{Answer, ResultCode};

const MONTHS: [&'static str; 12] = ["Jan", "Feb", "Mar", "Apr", "May", "Jun",
                                    "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];

type DataReader = SplitStream<Framed<TcpStream, StringCodec>>;
type DataWriter = SplitSink<Framed<TcpStream, StringCodec>>;
type Writer = SplitSink<Framed<TcpStream, FtpCodec>>;

cfg_if! {
    if #[cfg(windows)] {
        fn get_file_info(meta: &Metadata) -> (time::Tm, u64) {
            use std::os::windows::prelude::*;
            (time::at(time::Timespec::new(meta.last_write_time())), meta.file_size())
        }
    } else {
        fn get_file_info(meta: &Metadata) -> (time::Tm, u64) {
            use std::os::unix::prelude::*;
            (time::at(time::Timespec::new(meta.mtime(), 0)), meta.size())
        }
    }
}

fn add_file_info(entry: DirEntry, out: &mut String) {
    let path = entry.path(); // TODO: handle error.
    let extra = if path.is_dir() { "/" } else { "" };
    let is_dir = if path.is_dir() { "d" } else { "-" };

    let meta = ::std::fs::metadata(&path).unwrap(); // TODO: handle error.
    let (time, file_size) = get_file_info(&meta);
    let path = path.to_str().unwrap().split("/").last().unwrap(); // TODO: handle error.
    let file_str = format!("{} {} {} {} {}:{} {}{}\r\n",
                           is_dir,
                           file_size,
                           MONTHS[time.tm_mon as usize],
                           time.tm_mday,
                           time.tm_hour,
                           time.tm_min,
                           path,
                           extra);
    out.push_str(&file_str);
    println!("==> {:?}", &file_str);
}

#[allow(dead_code)]
struct Client {
    address: String, // TODO: remove this?
    cwd: PathBuf,
    data_port: Option<u16>,
    data_reader: Option<DataReader>,
    data_writer: Option<DataWriter>, // TODO: do we really need to split the data socket?
    handle: Handle,
    name: Option<String>,
    server_root: PathBuf,
    transfer_type: TransferType,
    writer: Writer,
}

impl Client {
    fn new(address: String, handle: Handle, writer: Writer, server_root: PathBuf) -> Client {
        Client {
            address,
            cwd: PathBuf::from(""),
            data_port: None,
            data_reader: None,
            data_writer: None,
            handle,
            name: None,
            server_root,
            transfer_type: TransferType::Ascii,
            writer,
        }
    }

    #[async]
    fn handle_cmd(mut self, cmd: Command) -> Result<Self, ()> {
        println!("Received command: {:?}", cmd);
        match cmd {
            Command::Auth =>
                self = await!(self.send(Answer::new(ResultCode::CommandNotImplemented, "Not implemented")))?
            ,
            Command::Cwd(directory) => self = await!(self.cwd(directory))?,
            Command::List(path) => self = await!(self.list(path))?,
            Command::Pasv => self = await!(self.pasv())?,
            Command::Port(port) => {
                self.data_port = Some(port);
                self = await!(self.send(Answer::new(ResultCode::Ok, &format!("Data port is now {}", port))))?;
            }
            Command::Pwd => {
                let message = format!("\"/{}\" ", self.cwd.to_str().unwrap());
                self = await!(self.send(Answer::new(ResultCode::PATHNAMECreated, &message)))?;
            } // TODO: handle error.
            Command::Quit => self = await!(self.quit())?,
            Command::Retr(file) => self = await!(self.retr(file))?,
            Command::Stor(file) => self = await!(self.stor(file))?,
            Command::Syst => {
                self = await!(self.send(Answer::new(ResultCode::Ok, "I won't tell!")))?;
            }
            Command::Type(typ) => {
                self.transfer_type = typ;
                self = await!(self.send(Answer::new(ResultCode::Ok, "Transfer type changed successfully")))?;
            }
            Command::Unknown =>
                self = await!(self.send(Answer::new(ResultCode::UnknownCommand, "Not implemented")))?
            ,
            Command::User(content) => {
                if content.is_empty() {
                    self = await!(self.send(Answer::new(ResultCode::InvalidParameterOrArgument, "Invalid username")))?;
                } else {
                    self.name = Some(content.to_owned());
                    self = await!(self.send(Answer::new(ResultCode::UserloggedIn, &format!("Welcome {}!", content))))?;
                }
            }
            Command::CdUp => {
                if let Some(path) = self.cwd.parent().map(Path::to_path_buf) {
                    self.cwd = path;
                }
                self = await!(self.send(Answer::new(ResultCode::Ok, "Done")))?;
            }
        }
        Ok(self)
    }

    fn close_data_connection(&mut self) {
        self.data_reader = None;
        self.data_writer = None;
    }

    fn complete_path(self, path: PathBuf) -> Result<(PathBuf, Self), io::Error> {
        let directory = self.server_root.join(if path.has_root() {
            path.iter().skip(1).collect()
        } else {
            path
        });
        let dir = directory.canonicalize();
        if let Ok(ref dir) = dir {
            if !dir.starts_with(&self.server_root) {
                return Err(io::ErrorKind::PermissionDenied.into());
            }
        }
        dir.map(|dir| (dir, self))
    }

    #[async]
    fn cwd(mut self, directory: String) -> Result<Self, ()> {
        let directory = PathBuf::from(directory);
        let path = self.cwd.join(&directory);
        let (dir, new_self) = self.complete_path(path).unwrap(); // TODO: handle error.
        self = new_self;
        self.cwd = dir.strip_prefix(&self.server_root).unwrap().to_path_buf(); // TODO: handle error.
        self = await!(self.send(Answer::new(ResultCode::Ok,
                                            &format!("Directory changed to \"{}\"", directory.display()))))?;
        Ok(self)
    }

    #[async]
    fn list(mut self, path: Option<PathBuf>) -> Result<Self, ()> {
        if self.data_writer.is_some() {
            let path = self.cwd.join(path.unwrap_or_default());
            let directory = PathBuf::from(&path);
            let (dir, new_self) = self.complete_path(directory).unwrap(); // TODO: handle error.
            self = new_self;
            self = await!(self.send(Answer::new(ResultCode::DataConnectionAlreadyOpen,
                                                "Starting to list directory...")))?;
            let mut out = String::new();
            for entry in read_dir(dir).unwrap() { // TODO: handle error.
                add_file_info(entry.unwrap(), &mut out); // TODO: handle error.
            }
            self = await!(self.send_data(out))?;
            println!("-> and done!");
        } else {
            self = await!(self.send(Answer::new(ResultCode::ConnectionClosed, "No opened data connection")))?;
        }
        if self.data_writer.is_some() {
            self.close_data_connection();
            self = await!(self.send(Answer::new(ResultCode::ClosingDataConnection, "Transfer done")))?;
        }
        Ok(self)
    }

    #[async]
    fn pasv(mut self) -> Result<Self, ()> {
        let port =
            if let Some(port) = self.data_port {
                port
            } else {
                0
            };
        if self.data_writer.is_some() {
            self = await!(self.send(Answer::new(ResultCode::DataConnectionAlreadyOpen, "Already listening...")))?;
            return Ok(self);
        }

        let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), port);
        let listener = TcpListener::bind(&addr, &self.handle).unwrap(); // TODO: handle error
        let port = listener.local_addr().unwrap().port(); // TODO: handle error.

        self = await!(self.send(Answer::new(ResultCode::EnteringPassiveMode,
                              &format!("127,0,0,1,{},{}", port >> 8, port & 0xFF))))?;

        println!("Waiting clients on port {}...", port);
        // TODO: use into_future() instead of for loop?
        #[async]
        for (stream, _rest) in listener.incoming().map_err(|_| ()) { // TODO: handle error.
            let (writer, reader) = stream.framed(StringCodec).split();
            self.data_writer = Some(writer);
            self.data_reader = Some(reader);
            break;
        }
        Ok(self)
    }

    #[async]
    fn quit(mut self) -> Result<Self, ()> {
        if self.data_writer.is_some() {
            unimplemented!();
        } else {
            self = await!(self.send(Answer::new(ResultCode::ServiceClosingControlConnection, "Closing connection...")))?;
            self.writer.close().unwrap(); // TODO: handle error.
        }
        Ok(self)
    }

    #[async]
    fn retr(mut self, path: PathBuf) -> Result<Self, ()> {
        // TODO: check if multiple data connection can be opened at the same time.
        if self.data_writer.is_some() {
            let path = self.cwd.join(path);
            let (path, new_self) = self.complete_path(path).unwrap(); // TODO: handle error.
            self = new_self;
            if path.is_file() {
                self = await!(self.send(Answer::new(ResultCode::DataConnectionAlreadyOpen, "Starting to send file...")))?;
                let mut file = File::open(path).unwrap(); // TODO: handle error.
                let mut out = String::new();
                file.read_to_string(&mut out).unwrap(); // TODO: handle error.
                self = await!(self.send_data(out))?;
                println!("-> file transfer done!");
            } else {
                self = await!(self.send(Answer::new(ResultCode::LocalErrorInProcessing,
                                      &format!("\"{}\" doesn't exist", path.to_str().unwrap()))))?;
            }
        } else {
            self = await!(self.send(Answer::new(ResultCode::ConnectionClosed, "No opened data connection")))?;
        }
        if self.data_writer.is_some() {
            self.close_data_connection();
            self = await!(self.send(Answer::new(ResultCode::ClosingDataConnection, "Transfer done")))?;
        }
        Ok(self)
    }

    #[async]
    fn stor(mut self, path: PathBuf) -> Result<Self, ()> {
        if self.data_reader.is_some() {
            let path = self.cwd.join(path);
            // TODO: check if path contains characters that are not allowed (cannot use
            // canonicalize for a non-existing path).
            self = await!(self.send(Answer::new(ResultCode::DataConnectionAlreadyOpen, "Starting to send file...")))?;
            let (data, new_self) = await!(self.receive_data())?;
            self = new_self;
            let mut file = File::create(path).unwrap(); // TODO: handle error.
            write!(file, "{}", data).unwrap(); // TODO: handle error.
            println!("-> file transfer done!");
            self.close_data_connection();
            self = await!(self.send(Answer::new(ResultCode::ClosingDataConnection, "Transfer done")))?;
        } else {
            self = await!(self.send(Answer::new(ResultCode::ConnectionClosed, "No opened data connection")))?;
        }
        Ok(self)
    }

    #[async]
    fn receive_data(mut self) -> Result<(String, Self), ()> {
        let mut file_data = String::new();
        // NOTE: have to use this weird trick because of futures-await.
        // TODO: fix that when the lifetime stuff is improved for generators.
        if self.data_reader.is_none() {
            return Ok((String::new(), self));
        }
        let reader = self.data_reader.take().unwrap();
        #[async]
        for data in reader.map_err(|_| ()) { // TODO: handle error.
            file_data.push_str(&data);
        }
        Ok((file_data, self))
    }

    #[async]
    fn send(mut self, answer: Answer) -> Result<Self, ()> {
        self.writer = await!(self.writer.send(answer)).map_err(|_| ())?; // TODO: handle error.
        Ok(self)
    }

    #[async]
    fn send_data(mut self, data: String) -> Result<Self, ()> {
        if let Some(writer) = self.data_writer {
            self.data_writer = Some(await!(writer.send(data)).map_err(|_| ())?); // TODO: handle error.
        }
        Ok(self)
    }
}

#[async]
fn handle_client(stream: TcpStream, handle: Handle, address: String, server_root: PathBuf) -> Result<(), ()> {
    let (writer, reader) = stream.framed(FtpCodec).split();
    let writer = await!(writer.send(Answer::new(ResultCode::ServiceReadyForNewUser, "Welcome to this FTP server!")))
        .map_err(|_| ())?; // TODO: handle error.
    let mut client = Client::new(address, handle, writer, server_root);
    #[async]
    for cmd in reader.map_err(|_| ()) { // TODO: handle error.
        client = await!(client.handle_cmd(cmd))?;
    }
    println!("Client closed");
    Ok(())
}

#[async]
fn server(handle: Handle, server_root: PathBuf) -> io::Result<()> {
    let port = 1234;
    let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), port);
    let listener = TcpListener::bind(&addr, &handle).unwrap();

    println!("Waiting clients on port {}...", port);
    #[async]
    for (stream, addr) in listener.incoming() {
        let address = format!("[address : {}]", addr);
        println!("New client: {}", address);
        handle.spawn(handle_client(stream, handle.clone(), address, server_root.clone()));
        println!("Waiting another client...");
    }
    Ok(())
}

fn main() {
    let mut core = Core::new()
        .expect("Cannot create tokio Core");
    let handle = core.handle();

    let server_root = env::current_dir().unwrap(); // TODO: handle error.

    core.run(server(handle, server_root))
        .expect("Run tokio server");
}
<|endoftext|>


<commit_before>fn main() {

    for i in 1..100 {
        println!(
            "{}",
            if div_by_fifteen(i) { "FizzBuzz".to_string() }
            else if div_by_three(i) { "Fizz".to_string() }
            else if div_by_five(i) { "Buzz".to_string() }
            else { i.to_string() });
    }

}

fn div_by_three(num: u64) -> bool {
    (num % 3) == 0
}

fn div_by_five(num: u64) -> bool {
    (num % 5) == 0
}

fn div_by_fifteen(num: u64) -> bool {
    div_by_five(num) && div_by_three(num)
}

#[cfg(test)]
mod test {

    use super::div_by_three;
    use super::div_by_five;
    use super::div_by_fifteen;

    #[test]
    fn test_div_by_three_returns_false() {

        assert_eq!(false, div_by_three(1));
        assert_eq!(false, div_by_three(2));

        assert_eq!(false, div_by_three(4));
        assert_eq!(false, div_by_three(5));
    }

    #[test]
    fn test_div_by_three_returns_true() {

        for i in 1..300 {
            assert_eq!(true, div_by_three(3 * i));
        }
    }

    #[test]
    fn test_div_by_five_returns_false() {

        assert_eq!(false, div_by_five(3));
        assert_eq!(false, div_by_five(4));
        assert_eq!(false, div_by_five(1));
        assert_eq!(false, div_by_five(11));
        assert_eq!(false, div_by_five(19));
    }

    #[test]
    fn test_div_by_five_returns_true() {

        for i in 1..100 {
            assert_eq!(true, div_by_five(5 * i));
        }
    }

    #[test]
    fn test_div_by_fifteen_returns_false() {

        assert_eq!(false, div_by_fifteen(5));
        assert_eq!(false, div_by_fifteen(3));

        assert_eq!(false, div_by_fifteen(14));
        assert_eq!(false, div_by_fifteen(31));
    }

    #[test]
    fn test_div_by_fifteen_returns_true() {

        for i in 1..300 {
            assert_eq!(true, div_by_fifteen(15 * i));
        }
    }
}
<commit_msg>Update Threading Stuff<commit_after>#[cfg(not(test))]
fn main() {
    use std::sync::mpsc;
    use std::thread;

    let (tx, rx) = mpsc::channel();

    let _thr = thread::scoped(move || {
        for i in 1u64..100u64 {
            tx.send(i).unwrap();
        };
    });

    for _ in 1..100 {
        let i = rx.recv().unwrap();;
        println!(
            "{}",
            if div_by_fifteen(i) { "FizzBuzz".to_string() }
            else if div_by_three(i) { "Fizz".to_string() }
            else if div_by_five(i) { "Buzz".to_string() }
            else { i.to_string() });
    }

    println!("Done!");
}

fn div_by_three(num: u64) -> bool {
    (num % 3) == 0
}

fn div_by_five(num: u64) -> bool {
    (num % 5) == 0
}

fn div_by_fifteen(num: u64) -> bool {
    div_by_five(num) && div_by_three(num)
}

#[cfg(test)]
mod test {

    use super::div_by_three;
    use super::div_by_five;
    use super::div_by_fifteen;

    #[test]
    fn test_div_by_three_returns_false() {

        assert_eq!(false, div_by_three(1));
        assert_eq!(false, div_by_three(2));

        assert_eq!(false, div_by_three(4));
        assert_eq!(false, div_by_three(5));
    }

    #[test]
    fn test_div_by_three_returns_true() {

        for i in 1..300 {
            assert_eq!(true, div_by_three(3 * i));
        }
    }

    #[test]
    fn test_div_by_five_returns_false() {

        assert_eq!(false, div_by_five(3));
        assert_eq!(false, div_by_five(4));
        assert_eq!(false, div_by_five(1));
        assert_eq!(false, div_by_five(11));
        assert_eq!(false, div_by_five(19));
    }

    #[test]
    fn test_div_by_five_returns_true() {

        for i in 1..100 {
            assert_eq!(true, div_by_five(5 * i));
        }
    }

    #[test]
    fn test_div_by_fifteen_returns_false() {

        assert_eq!(false, div_by_fifteen(5));
        assert_eq!(false, div_by_fifteen(3));

        assert_eq!(false, div_by_fifteen(14));
        assert_eq!(false, div_by_fifteen(31));
    }

    #[test]
    fn test_div_by_fifteen_returns_true() {

        for i in 1..300 {
            assert_eq!(true, div_by_fifteen(15 * i));
        }
    }
}
<|endoftext|>