<commit_before>extern crate regex;
extern crate colored;

mod charpeek;

use charpeek::Charpeek;

use regex::Regex;
use colored::Colorize;

use std::collections::BTreeMap;
use std::fs;
use std::io::prelude::*;
use std::io;
use std::iter::{once,repeat};
use std::env;
use std::process;

fn color_keywords(keys: &Regex, mut line: &str) -> String {
    use std::fmt::Write;
    let mut buf = String::new();
    while let Some((start, end)) = keys.find(line) {
        write!(buf, "{}{}", &line[..start], line[start..end].bold()).unwrap();
        line = &line[end..];
    }
    write!(buf, "{}", line).unwrap();
    buf
}

fn main() {
    let args: Vec<_> = env::args_os().collect();
    if args.len() <= 1 {
        let i = io::stdin();
        do_it(&mut i.lock());
    } else {
        let mut cmd = process::Command::new(&args[1])
            .args(&args[2..])
            .stdout(process::Stdio::piped())
            .spawn()
            .expect("Can't spawn process");
        do_it(&mut io::BufReader::new(cmd.stdout.as_mut().unwrap()));
        let status = cmd.wait().expect("Can't wait for process");
        if !status.success() {
            println!("{}: {}", "Exit status".yellow(), status);
        }
    }
}

fn print_line<'a, F>(
        file: &'a[String], row: usize, span: Option<(Option<usize>, usize)>,
        colorize: F, keywords_r: &Regex) -> &'a str
where F: Fn(&str) -> colored::ColoredString
{
    let rownum = format!("{}| ", row);
    let line = &file.get(row - 1).map_or("<NO SUCH LINE>", |s| s.as_str());

    println!("{}{}", rownum.cyan(), color_keywords(keywords_r, line));

    if let Some((start, end)) = span {
        // Print underline

        let start = start.unwrap_or_else(||
            line.chars().take_while(|c| c.is_whitespace()).count());

        for _ in 0..rownum.len() { print!(" ") }
        // print!("{}", "|".cyan());
        for c in line[..start].chars() {
            if c.is_whitespace() { print!("{}", c) }
            else { print!(" ") }
        }

        let underline_len = std::cmp::min(end, line.len()) - start;
        println!("{}", colorize(&repeat('~').take(underline_len).collect::<String>()));
    }

    line
}

fn do_it(reader: &mut BufRead) {
    let location_r = Regex::new(r#"^File "(.*)", line (\d+), characters (\d+)-(\d+):$"#).unwrap();
    let command_r = Regex::new(r"ocaml.* -I .* -o .*|^ocamldep.*\.mli? >|^ocamlbuild -package").unwrap();
    let val_r = Regex::new(r"^((?:let |val )?)(.+?) : (.+?) =(.*)$").unwrap();
    let type_r = Regex::new(r"^type (.+?) =(.*)$").unwrap();
    let word_r = Regex::new(r"^\w+").unwrap();
    let keywords_r = Regex::new(r"\b(let|in|match|with|for|do|done|if|then|else|begin|end|rec|when|and|or|val)\b").unwrap();
    let escape_r = Regex::new(r"\033\[.+?m").unwrap();

    let make_fence = |len: usize| repeat('-').take(len).collect::<String>();

    let mut lines = Charpeek::new(reader);
    let mut files: BTreeMap<String, Vec<String>> = BTreeMap::new();
    let mut inside = false;

    while let Some(byte) = lines.peek_byte() {
        // Handle prompt (we want to flush it immediately)
        if byte == b'#' || (byte as char).is_whitespace() {
            if byte == b'#' {
                print!("\n{}", "λ".green().bold());
                lines.flush_peek(io::sink());
            } else {
                lines.flush_peek(&mut io::stdout());
            }
            continue;
        }

        let line = if let Some(line) = lines.next_line() { line } else { break };
        let unescaped_line = escape_r.replace_all(&line, "");

        if let Some(captures) = location_r.captures(&unescaped_line) {
            // Handle error/warning
            inside = true;

            let filename = &captures[1];
            let i_row: usize = captures[2].parse().unwrap();
            let col_start: usize = captures[3].parse().unwrap();
            let mut col_end: usize = captures[4].parse().unwrap();

            let file = files.entry(filename.to_owned())
                .or_insert_with(||
                    fs::File::open(&filename)
                        .map(|file|
                             io::BufReader::new(file)
                                .lines()
                                .map(|x| x.unwrap_or_else(|_| "".into()))
                                .chain(once("<END OF FILE>".into()))
                                .collect()
                        )
                        .unwrap_or_else(|_| vec![])
                );

            let filerow = format!("{}:{}", filename, i_row);
            let fence = format!("-- {} {}", filerow, make_fence(80 - 4 - filerow.len()));
            println!("\n{}", fence.cyan());

            // The message
            let message = lines.next_line().unwrap_or("".into());
            let message = escape_r.replace_all(&message, "");
            let message_type = &message[..word_r.find(&message).unwrap_or((0,0)).1];
            let colorize = |s: &str| match message_type {
                "Warning" => s.yellow(),
                "Error" => s.red().bold(),
                _ => s.bold(),
            };
            println!("{}{}", colorize(message_type), &message[message_type.len()..]);
            while lines
                    .peek_byte()
                    .map_or(false, |b| (b as char).is_whitespace()) {
                println!("{}", lines.next_line().unwrap());
            }
            println!("");

            // The snippet.
            if file.get(i_row - 1).map_or(false, |l|
                    l.chars().take_while(|c| c.is_whitespace()).count() == col_start) {
                // If the error is on the beginning of line,
                // print also the first non-empty line before.
                let mut start = None;
                for i in (0..i_row - 1).rev() {
                    start = Some(i);
                    if file[i].trim() != "" { break; }
                }
                if let Some(start) = start {
                    for i in start..i_row - 1 {
                        print_line(file, i + 1, None, &colorize, &keywords_r);
                    }
                }
            }

            for i in i_row.. {
                let len = print_line(
                        file, i,
                        Some((if i == i_row { Some(col_start) } else { None }, col_end)),
                        &colorize, &keywords_r)
                    .len();

                if col_end <= len { break }
                else { col_end -= len + 1 }
            }

        } else if command_r.is_match(&line) {
            // Handle commands printed by ocamlbuild
            if inside {
                println!("");
                inside = false;
            }
            println!("{}", line.dimmed());

        } else if let Some(captures) = val_r.captures(&line) {
            // Handle val/let assignments
            if inside {
                println!("");
                inside = false;
            }
            println!("{}{} : {} ={}", captures[1].bold(), &captures[2], captures[3].cyan(), &captures[4]);

        } else if let Some(captures) = type_r.captures(&line) {
            // Handle type declarations
            if inside {
                println!("");
                inside = false;
            }
            println!("{} {} ={}", "type".bold(), &captures[1].cyan(), &captures[2]);

        } else if line.starts_with("Hint:") {
            println!("\n{}\n{}{}", make_fence(80).cyan(), "Hint".green(), &line[4..]);

        } else {
            println!("{}", line);
        }
    }
}
<commit_msg>fix: properly colorize output of #show_module<commit_after>extern crate regex;
extern crate colored;

mod charpeek;

use charpeek::Charpeek;

use regex::Regex;
use colored::Colorize;

use std::collections::BTreeMap;
use std::fs;
use std::io::prelude::*;
use std::io;
use std::iter::{once,repeat};
use std::env;
use std::process;

fn color_keywords(keys: &Regex, mut line: &str) -> String {
    use std::fmt::Write;
    let mut buf = String::new();
    while let Some((start, end)) = keys.find(line) {
        write!(buf, "{}{}", &line[..start], line[start..end].bold()).unwrap();
        line = &line[end..];
    }
    write!(buf, "{}", line).unwrap();
    buf
}

fn main() {
    let args: Vec<_> = env::args_os().collect();
    if args.len() <= 1 {
        let i = io::stdin();
        do_it(&mut i.lock());
    } else {
        let mut cmd = process::Command::new(&args[1])
            .args(&args[2..])
            .stdout(process::Stdio::piped())
            .spawn()
            .expect("Can't spawn process");
        do_it(&mut io::BufReader::new(cmd.stdout.as_mut().unwrap()));
        let status = cmd.wait().expect("Can't wait for process");
        if !status.success() {
            println!("{}: {}", "Exit status".yellow(), status);
        }
    }
}

fn print_line<'a, F>(
        file: &'a[String], row: usize, span: Option<(Option<usize>, usize)>,
        colorize: F, keywords_r: &Regex) -> &'a str
where F: Fn(&str) -> colored::ColoredString
{
    let rownum = format!("{}| ", row);
    let line = &file.get(row - 1).map_or("<NO SUCH LINE>", |s| s.as_str());

    println!("{}{}", rownum.cyan(), color_keywords(keywords_r, line));

    if let Some((start, end)) = span {
        // Print underline

        let start = start.unwrap_or_else(||
            line.chars().take_while(|c| c.is_whitespace()).count());

        for _ in 0..rownum.len() { print!(" ") }
        // print!("{}", "|".cyan());
        for c in line[..start].chars() {
            if c.is_whitespace() { print!("{}", c) }
            else { print!(" ") }
        }

        let underline_len = std::cmp::min(end, line.len()) - start;
        println!("{}", colorize(&repeat('~').take(underline_len).collect::<String>()));
    }

    line
}

fn do_it(reader: &mut BufRead) {
    let location_r = Regex::new(r#"^File "(.*)", line (\d+), characters (\d+)-(\d+):$"#).unwrap();
    let command_r = Regex::new(r"ocaml.* -I .* -o .*|^ocamldep.*\.mli? >|^ocamlbuild -package").unwrap();
    let val_r = Regex::new(r"^((?:let |val )?)(.+?) : (.+?)( =.*$|$)").unwrap();
    let type_r = Regex::new(r"^type (.+?) =(.*)$").unwrap();
    let word_r = Regex::new(r"^\w+").unwrap();
    let keywords_r = Regex::new(r"\b(let|in|match|with|for|do|done|if|then|else|begin|end|rec|when|and|or|val)\b").unwrap();
    let escape_r = Regex::new(r"\033\[.+?m").unwrap();

    let make_fence = |len: usize| repeat('-').take(len).collect::<String>();

    let mut lines = Charpeek::new(reader);
    let mut files: BTreeMap<String, Vec<String>> = BTreeMap::new();
    let mut inside = false;

    while let Some(byte) = lines.peek_byte() {
        // Handle prompt (we want to flush it immediately)
        if byte == b'#' || (byte as char).is_whitespace() {
            if byte == b'#' {
                print!("\n{}", "λ".green().bold());
                lines.flush_peek(io::sink());
            } else {
                lines.flush_peek(&mut io::stdout());
            }
            continue;
        }

        let line = if let Some(line) = lines.next_line() { line } else { break };
        let unescaped_line = escape_r.replace_all(&line, "");

        if let Some(captures) = location_r.captures(&unescaped_line) {
            // Handle error/warning
            inside = true;

            let filename = &captures[1];
            let i_row: usize = captures[2].parse().unwrap();
            let col_start: usize = captures[3].parse().unwrap();
            let mut col_end: usize = captures[4].parse().unwrap();

            let file = files.entry(filename.to_owned())
                .or_insert_with(||
                    fs::File::open(&filename)
                        .map(|file|
                             io::BufReader::new(file)
                                .lines()
                                .map(|x| x.unwrap_or_else(|_| "".into()))
                                .chain(once("<END OF FILE>".into()))
                                .collect()
                        )
                        .unwrap_or_else(|_| vec![])
                );

            let filerow = format!("{}:{}", filename, i_row);
            let fence = format!("-- {} {}", filerow, make_fence(80 - 4 - filerow.len()));
            println!("\n{}", fence.cyan());

            // The message
            let message = lines.next_line().unwrap_or("".into());
            let message = escape_r.replace_all(&message, "");
            let message_type = &message[..word_r.find(&message).unwrap_or((0,0)).1];
            let colorize = |s: &str| match message_type {
                "Warning" => s.yellow(),
                "Error" => s.red().bold(),
                _ => s.bold(),
            };
            println!("{}{}", colorize(message_type), &message[message_type.len()..]);
            while lines
                    .peek_byte()
                    .map_or(false, |b| (b as char).is_whitespace()) {
                println!("{}", lines.next_line().unwrap());
            }
            println!("");

            // The snippet.
            if file.get(i_row - 1).map_or(false, |l|
                    l.chars().take_while(|c| c.is_whitespace()).count() == col_start) {
                // If the error is on the beginning of line,
                // print also the first non-empty line before.
                let mut start = None;
                for i in (0..i_row - 1).rev() {
                    start = Some(i);
                    if file[i].trim() != "" { break; }
                }
                if let Some(start) = start {
                    for i in start..i_row - 1 {
                        print_line(file, i + 1, None, &colorize, &keywords_r);
                    }
                }
            }

            for i in i_row.. {
                let len = print_line(
                        file, i,
                        Some((if i == i_row { Some(col_start) } else { None }, col_end)),
                        &colorize, &keywords_r)
                    .len();

                if col_end <= len { break }
                else { col_end -= len + 1 }
            }

        } else if command_r.is_match(&line) {
            // Handle commands printed by ocamlbuild
            if inside {
                println!("");
                inside = false;
            }
            println!("{}", line.dimmed());

        } else if let Some(captures) = val_r.captures(&line) {
            // Handle val/let assignments
            if inside {
                println!("");
                inside = false;
            }
            println!("{}{} : {}{}", captures[1].bold(), &captures[2], captures[3].cyan(), &captures[4]);

        } else if let Some(captures) = type_r.captures(&line) {
            // Handle type declarations
            if inside {
                println!("");
                inside = false;
            }
            println!("{} {} ={}", "type".bold(), &captures[1].cyan(), &captures[2]);

        } else if line.starts_with("Hint:") {
            println!("\n{}\n{}{}", make_fence(80).cyan(), "Hint".green(), &line[4..]);

        } else {
            println!("{}", line);
        }
    }
}
<|endoftext|>


<commit_before>#![feature(slicing_syntax, macro_rules)]
#![feature(overloaded_calls)]

extern crate http;
extern crate url;
extern crate serialize;

use std::os;
use std::str;
use std::sync::{Arc, Mutex};
use std::io::net::ip::{SocketAddr, Ipv4Addr};
use std::collections::HashMap;

use http::method::{Get, Post};
use http::server::{Config, Server, Request, ResponseWriter};
use http::server::request::{AbsolutePath};

use serialize::json;

use incoming::{SlackPayload};
use outgoing::{SlackEndpoint, OutgoingWebhook};

mod incoming;
mod outgoing;

type Scores = HashMap<String, i32>;

#[deriving(Send,Clone)]
struct KarmaServer {
    scores: Arc<Mutex<Scores>>,
    endpoint: String,
}

impl KarmaServer {
    fn new(scores: Scores, endpoint: String) -> KarmaServer {
        KarmaServer {
            scores: Arc::new(Mutex::new(scores)),
            endpoint: endpoint
        }
    }
}

fn handle_karma(req: Vec<u8>, points: &mut Scores, cb: |&&str, &i32, &&str|) {
    let payload = match SlackPayload::from_body(req.as_slice()) {
        Ok(payload) => payload,
        Err(err) => {
            println!("Error! {}", err); return
        }
    };

    let op = match payload.command {
        "%2F%2B%2B" => |c: i32| c + 1,
        "%2F++" => |c: i32| c + 1,
        "%2F%2D%2D" => |c: i32| c - 1,
        "%2F--" => |c: i32| c - 1,
        _ => return,
    };

    let mut matches = payload.text.splitn(1, ' ');
    let target = match (matches.next(), matches.next()) {
        (None, None) => return, // No message or user
        (Some(user), None) => user, // user, no message
        (Some(user), Some(_)) => user, // TODO deal with the message
        (None, Some(_)) => unreachable!(),
    };

    let current = points.insert(target.to_string(), 0).unwrap_or(0);
    points.insert(target.to_string(), op(current));

    cb(&target, &op(current), &payload.channel_name);
}

impl Server for KarmaServer {
    fn get_config(&self) -> Config {
        match os::getenv("PORT") {
            None => panic!("Must specify PORT"),
            Some(port) => {
                let port = from_str::<u16>(port.as_slice()).expect("PORT must be an int");
                Config { bind_address: SocketAddr { ip: Ipv4Addr(0, 0, 0, 0), port: port } }
            }
        }
    }

    #[allow(unused_must_use)]
    fn handle_request(&self, r: Request, w: &mut ResponseWriter) {
        let path = match r.request_uri { AbsolutePath(ref path) => path[], _ => return };

        println!("{}: {}", r.method, path);

        match (&r.method, path) {
            (&Get, "/") => {
                w.write(b"Hello world!");
            },
            (&Get, "/karma") => {
                let scores = (*self.scores).lock();
                let json = json::encode(&*scores);
                w.write(json.as_bytes());
            }
            (&Post, "/normalise") => {
                let mut scores = (*self.scores).lock();
                for (_, v) in scores.iter_mut() {
                    let new = *v as f64;
                    *v = new.sqrt() as i32;
                }
            }
            (&Post, "/slack") => {
                let mut scores = (*self.scores).lock();
                let slack = self.get_slack_endpoint();
                handle_karma(r.body, &mut *scores, |u, s, c| {
                    let msg = format!("{} now at {}", u, s);
                    let payload = OutgoingWebhook {
                        text: msg.as_slice(),
                        channel: *c,
                        username: "karmabot",
                        icon_emoji: Some(":ghost:"),
                    };

                    slack.send(&payload);
                });
            },
            (_, _) => {
                w.write(b"Not found :(");
            }
        }
    }
}

impl KarmaServer {
    fn get_slack_endpoint(&self) -> SlackEndpoint {
        return SlackEndpoint {
            url: self.endpoint.clone()
        }
    }
}

fn main() {
    let endpoint = match os::getenv("WEBHOOK_ENDPOINT") {
        Some(e) => e,
        None => panic!("Must set WEBHOOK_ENDPOINT"),
    };
    let server = KarmaServer::new(HashMap::new(), endpoint);
    server.serve_forever();
}
<commit_msg>Slack doesn't send the channel name with a #<commit_after>#![feature(slicing_syntax, macro_rules)]
#![feature(overloaded_calls)]

extern crate http;
extern crate url;
extern crate serialize;

use std::os;
use std::str;
use std::sync::{Arc, Mutex};
use std::io::net::ip::{SocketAddr, Ipv4Addr};
use std::collections::HashMap;

use http::method::{Get, Post};
use http::server::{Config, Server, Request, ResponseWriter};
use http::server::request::{AbsolutePath};

use serialize::json;

use incoming::{SlackPayload};
use outgoing::{SlackEndpoint, OutgoingWebhook};

mod incoming;
mod outgoing;

type Scores = HashMap<String, i32>;

#[deriving(Send,Clone)]
struct KarmaServer {
    scores: Arc<Mutex<Scores>>,
    endpoint: String,
}

impl KarmaServer {
    fn new(scores: Scores, endpoint: String) -> KarmaServer {
        KarmaServer {
            scores: Arc::new(Mutex::new(scores)),
            endpoint: endpoint
        }
    }
}

fn handle_karma(req: Vec<u8>, points: &mut Scores, cb: |&&str, &i32, &&str|) {
    let payload = match SlackPayload::from_body(req.as_slice()) {
        Ok(payload) => payload,
        Err(err) => {
            println!("Error! {}", err); return
        }
    };

    let op = match payload.command {
        "%2F%2B%2B" => |c: i32| c + 1,
        "%2F++" => |c: i32| c + 1,
        "%2F%2D%2D" => |c: i32| c - 1,
        "%2F--" => |c: i32| c - 1,
        _ => return,
    };

    let mut matches = payload.text.splitn(1, ' ');
    let target = match (matches.next(), matches.next()) {
        (None, None) => return, // No message or user
        (Some(user), None) => user, // user, no message
        (Some(user), Some(_)) => user, // TODO deal with the message
        (None, Some(_)) => unreachable!(),
    };

    let current = points.insert(target.to_string(), 0).unwrap_or(0);
    points.insert(target.to_string(), op(current));

    cb(&target, &op(current), &payload.channel_name);
}

impl Server for KarmaServer {
    fn get_config(&self) -> Config {
        match os::getenv("PORT") {
            None => panic!("Must specify PORT"),
            Some(port) => {
                let port = from_str::<u16>(port.as_slice()).expect("PORT must be an int");
                Config { bind_address: SocketAddr { ip: Ipv4Addr(0, 0, 0, 0), port: port } }
            }
        }
    }

    #[allow(unused_must_use)]
    fn handle_request(&self, r: Request, w: &mut ResponseWriter) {
        let path = match r.request_uri { AbsolutePath(ref path) => path[], _ => return };

        println!("{}: {}", r.method, path);

        match (&r.method, path) {
            (&Get, "/") => {
                w.write(b"Hello world!");
            },
            (&Get, "/karma") => {
                let scores = (*self.scores).lock();
                let json = json::encode(&*scores);
                w.write(json.as_bytes());
            }
            (&Post, "/normalise") => {
                let mut scores = (*self.scores).lock();
                for (_, v) in scores.iter_mut() {
                    let new = *v as f64;
                    *v = new.sqrt() as i32;
                }
            }
            (&Post, "/slack") => {
                let mut scores = (*self.scores).lock();
                let slack = self.get_slack_endpoint();
                handle_karma(r.body, &mut *scores, |u, s, c| {
                    let msg = format!("{} now at {}", u, s);
                    let channel = format!("#{}", c);
                    let payload = OutgoingWebhook {
                        text: msg.as_slice(),
                        channel: channel.as_slice(),
                        username: "karmabot",
                        icon_emoji: Some(":ghost:"),
                    };

                    slack.send(&payload);
                });
            },
            (_, _) => {
                w.write(b"Not found :(");
            }
        }
    }
}

impl KarmaServer {
    fn get_slack_endpoint(&self) -> SlackEndpoint {
        return SlackEndpoint {
            url: self.endpoint.clone()
        }
    }
}

fn main() {
    let endpoint = match os::getenv("WEBHOOK_ENDPOINT") {
        Some(e) => e,
        None => panic!("Must set WEBHOOK_ENDPOINT"),
    };
    let server = KarmaServer::new(HashMap::new(), endpoint);
    server.serve_forever();
}
<|endoftext|>


<commit_before>extern crate chrono;

extern crate rustc_serialize;
use rustc_serialize::json;

extern crate rs_es;
use rs_es::Client;
use rs_es::operations::search::{Sort, SortField, Order};
use rs_es::query::{Filter, Query};

extern crate postgres;
extern crate postgres_array;
use postgres::{Connection, SslMode};

extern crate iron;
extern crate router;
extern crate urlencoded;
use iron::prelude::*;
use iron::status;
use iron::mime::Mime;
use router::Router;
use urlencoded::UrlEncodedQuery;

mod user;
use user::User;

mod company;

mod filters;
use filters::{visibility_filters, VectorOfTerms};

use std::env;

#[derive(Debug, RustcDecodable)]
struct TalentsSearchResult {
  id: i32
}

const PG_URL:     &'static str            = "postgres://lando@localhost/lando_development";
const ES_INDEXES: &'static [&'static str] = &["honeypot_dev_talents"];
const VERSION:    &'static str            = env!("CARGO_PKG_VERSION");

fn main() {
  let port = env::args().nth(1).unwrap_or(String::from("3000"));
  let host = format!("127.0.0.1:{}", port);

  println!("Honeysearch v{}", VERSION);
  println!("Listening on http://{}...", host);

  let mut router = Router::new();
  router.get("/talents", talents);

  Iron::new(router).http(&*host).unwrap();
}

fn talents(req: &mut Request) -> IronResult<Response> {
  let mut es = Client::new("localhost", 9200);
  let     pg = Connection::connect(PG_URL, SslMode::None).unwrap();

  // TODO: How to deal with this two private parameters?
  let company_ids: Vec<i32>    = vec![];
  let company_id:  Option<i32> = None;

  let params = req.get_ref::<UrlEncodedQuery>().ok().unwrap();
  let empty_vec: Vec<String> = vec![];

  let query = Query::build_filtered(Filter::build_bool()
                                           .with_must(
                                             vec![
                                               <Filter as VectorOfTerms<String>>::build_terms(
                                                 "work_roles", params.get("work_roles")
                                                                     .unwrap_or(&empty_vec)),

                                               <Filter as VectorOfTerms<String>>::build_terms(
                                                 "work_languages", params.get("work_languages")
                                                                        .unwrap_or(&empty_vec)),

                                               <Filter as VectorOfTerms<String>>::build_terms(
                                                 "work_experience", params.get("work_experience")
                                                                        .unwrap_or(&empty_vec)),

                                               <Filter as VectorOfTerms<String>>::build_terms(
                                                 "work_locations", params.get("work_locations")
                                                                        .unwrap_or(&empty_vec)),

                                               <Filter as VectorOfTerms<String>>::build_terms(
                                                "work_authorization", params.get("work_authorization")
                                                                        .unwrap_or(&empty_vec)),

                                               visibility_filters(&pg, &company_id)
                                             ].into_iter()
                                              .flat_map(|x| x)
                                              .collect::<Vec<Filter>>())
                                           .with_must_not(
                                             vec![
                                               <Filter as VectorOfTerms<i32>>::build_terms(
                                                 "company_ids", &company_ids),

                                               <Filter as VectorOfTerms<i32>>::build_terms(
                                                 "blocked_companies", &company_ids)
                                             ].into_iter()
                                              .flat_map(|x| x)
                                              .collect::<Vec<Filter>>())
                                           .build())
                    .build();

  let result = es.search_query()
                 .with_indexes(ES_INDEXES)
                 .with_query(&query)
                 .with_sort(&Sort::new(
                   vec![
                     SortField::new("updated_at", Some(Order::Desc)).build()
                   ]))
                 .send()
                 .ok()
                 .unwrap();

  let user_ids = result.hits.hits.into_iter()
                                 .filter_map(|hit| {
                                   let talent: TalentsSearchResult = hit.source().unwrap();
                                   User::find(&pg, &talent.id)
                                 })
                                 .collect::<Vec<User>>();

  let content_type = "application/json".parse::<Mime>().unwrap();
  Ok(Response::with((content_type, status::Ok, json::encode(&user_ids).unwrap())))
}
<commit_msg>main: implement company_id<commit_after>extern crate chrono;

extern crate rustc_serialize;
use rustc_serialize::json;

extern crate rs_es;
use rs_es::Client;
use rs_es::operations::search::{Sort, SortField, Order};
use rs_es::query::{Filter, Query};

extern crate postgres;
extern crate postgres_array;
use postgres::{Connection, SslMode};

extern crate iron;
extern crate router;
extern crate urlencoded;
use iron::prelude::*;
use iron::status;
use iron::mime::Mime;
use router::Router;
use urlencoded::UrlEncodedQuery;

mod user;
use user::User;

mod company;

mod filters;
use filters::{visibility_filters, VectorOfTerms};

use std::env;

#[derive(Debug, RustcDecodable)]
struct TalentsSearchResult {
  id: i32
}

const PG_URL:     &'static str            = "postgres://lando@localhost/lando_development";
const ES_INDEXES: &'static [&'static str] = &["honeypot_dev_talents"];
const VERSION:    &'static str            = env!("CARGO_PKG_VERSION");

fn main() {
  let port = env::args().nth(1).unwrap_or(String::from("3000"));
  let host = format!("127.0.0.1:{}", port);

  println!("Honeysearch v{}", VERSION);
  println!("Listening on http://{}...", host);

  let mut router = Router::new();
  router.get("/talents", talents);

  Iron::new(router).http(&*host).unwrap();
}

fn talents(req: &mut Request) -> IronResult<Response> {
  let mut es = Client::new("localhost", 9200);
  let     pg = Connection::connect(PG_URL, SslMode::None).unwrap();

  let params = req.get_ref::<UrlEncodedQuery>().ok().unwrap();
  let empty_vec: Vec<String> = vec![];

  let company_id = match params.clone().get_mut("company_id") {
    Some(company_id) => company_id[0].parse::<i32>()
                                     .map(|x| vec![x])
                                     .unwrap_or(vec![]),
    None => vec![]
  };

  let query = Query::build_filtered(Filter::build_bool()
                                           .with_must(
                                             vec![
                                               <Filter as VectorOfTerms<String>>::build_terms(
                                                 "work_roles", params.get("work_roles")
                                                                     .unwrap_or(&empty_vec)),

                                               <Filter as VectorOfTerms<String>>::build_terms(
                                                 "work_languages", params.get("work_languages")
                                                                         .unwrap_or(&empty_vec)),

                                               <Filter as VectorOfTerms<String>>::build_terms(
                                                 "work_experience", params.get("work_experience")
                                                                          .unwrap_or(&empty_vec)),

                                               <Filter as VectorOfTerms<String>>::build_terms(
                                                 "work_locations", params.get("work_locations")
                                                                         .unwrap_or(&empty_vec)),

                                               <Filter as VectorOfTerms<String>>::build_terms(
                                                "work_authorization", params.get("work_authorization")
                                                                            .unwrap_or(&empty_vec)),

                                               visibility_filters(&pg, match company_id.is_empty() {
                                                 true  => None,
                                                 false => Some(company_id[0]),
                                               })
                                             ].into_iter()
                                              .flat_map(|x| x)
                                              .collect::<Vec<Filter>>())
                                           .with_must_not(
                                             vec![
                                               <Filter as VectorOfTerms<i32>>::build_terms(
                                                 "company_ids", &company_id),

                                               <Filter as VectorOfTerms<i32>>::build_terms(
                                                 "blocked_companies", &company_id)
                                             ].into_iter()
                                              .flat_map(|x| x)
                                              .collect::<Vec<Filter>>())
                                           .build())
                    .build();

  let result = es.search_query()
                 .with_indexes(ES_INDEXES)
                 .with_query(&query)
                 .with_sort(&Sort::new(
                   vec![
                     SortField::new("updated_at", Some(Order::Desc)).build()
                   ]))
                 .send()
                 .ok()
                 .unwrap();

  let user_ids = result.hits.hits.into_iter()
                                 .filter_map(|hit| {
                                   let talent: TalentsSearchResult = hit.source().unwrap();
                                   User::find(&pg, &talent.id)
                                 })
                                 .collect::<Vec<User>>();

  let content_type = "application/json".parse::<Mime>().unwrap();
  Ok(Response::with((content_type, status::Ok, json::encode(&user_ids).unwrap())))
}
<|endoftext|>


<commit_before>use std::io::fs::rename;
use std::io::fs::PathExtensions;
use std::collections::HashMap;
use std::os;
use std::io;
use std::vec::Vec;
use std::str::StrSlice;

mod util;
mod server;

fn main() {

    let args = os::args();

    if args.len() == 2 && args[1] == "--server".to_string() {
        serve();
        return;
    }

    if args.len() == 2 && args[1] == "--start-server".to_string() {
        server::start_server();
        return;
    }

    let use_external_process = args.contains(&String::from_str("--use-external-process"));

    if args.len() < 3 {
        println!("Usage: {} <original-directory> <new-directory>", args[0]);
        return;
    }

    let remote_source = args[1].as_slice().contains(":");
    let host: String;
    let source_path: String;

    if remote_source {
        let components: Vec<&str> = args[1].as_slice().split(':').collect();
        host = components[0].to_string();
        source_path = components[1].to_string();
    }
    else {
        source_path = args[1].clone();
        host = "".to_string();
    }

    let src_directory = Path::new(source_path);
    let dst_directory = Path::new(args[2].clone());

    if remote_source || use_external_process {
        let mut server = server::StdServer::new(host);
        let mut dest_server = server::InMemoryServer::new();
        sync_renames(&mut server, &mut dest_server, &src_directory, &dst_directory);
    }
    else {
        let mut server = server::InMemoryServer::new();
        let mut dest_server = server::InMemoryServer::new();
        sync_renames(&mut server, &mut dest_server, &src_directory, &dst_directory);
    }
}

fn sync_renames(source_server: &mut server::Server, dest_server: &mut server::Server, src_directory: &Path, dst_directory: &Path) {

    let src_map = source_server.get_metadata(src_directory);
    let new_map = dest_server.get_metadata(dst_directory);

    for (&(size, mod_date), paths) in new_map.iter() {

        let mut matching_paths = 0i;

        for (i, value) in paths.iter().enumerate() {

            let new_path = Path::new(value.clone());
            let src_path :Path;

            match find_matching_file(&new_path, size, mod_date, &src_map, source_server, dest_server) {
                Some(p) => src_path = p,
                None => continue
            }

            let mut file_path = dst_directory.clone();
            file_path.push(src_path.path_relative_from(src_directory).unwrap());

            let src_relative_path = src_path.path_relative_from(src_directory).unwrap();
            let dst_relative_path = new_path.path_relative_from(dst_directory).unwrap();

            // No point in comparing a file with itself
            if src_relative_path == dst_relative_path {
                continue;
            }

            matching_paths += 1;

            if matching_paths > 1 && file_path.exists() && source_server.get_checksum(&src_path) == dest_server.get_checksum(&file_path) {
                println!("{} was copied to {}", dst_relative_path.display(), src_relative_path.display());

            } else {
                println!("{} was renamed to {}", dst_relative_path.display(), src_relative_path.display());
                rename(
                    &dst_directory.join(dst_relative_path),
                    &dst_directory.join(src_relative_path)).unwrap();
            }
        }
    }
}

fn find_matching_file(file: &Path, size: u64, modification_date: u64, master_metadata: &HashMap<(u64, u64), Vec<String>>, src_server: &mut server::Server, dest_server: &mut server::Server) -> Option<Path> {

    // Check whether there are any files in master with the same
    // size and modification date of the sought file
    match master_metadata.find(&(size, modification_date)) {

        Some(matches) => {

            // Compare the file contents until we find a match, or return None if
            // we don't find one
            for potential_match in matches.iter() {
                let path = Path::new(potential_match.clone());
                if src_server.get_checksum(&path) == dest_server.get_checksum(file) {
                    return Some(path);
                }
            }

            return None

        }

        None => return None
    }

}

fn serve() {

    for line in io::stdin().lines() {
        let unwrapped_line = line.unwrap();
        let line_slice = unwrapped_line.as_slice().trim_right_chars('\n');
        if line_slice.starts_with("checksum ") {
           let file_name = line_slice.slice_from(9);
           println!("{}", util::get_file_checksum(&Path::new(file_name)));
           println!("\0");
        }

        if line_slice.starts_with("metadata ") {
            let path = line_slice.slice_from(9);
            for metadata in util::MetadataReader::new(&Path::new(path)) {
                println!("{}", metadata.path);
                println!("{}", metadata.size);
                println!("{}", metadata.modified);
            }
            println!("\0");

        }

    }
}
<commit_msg>Add a "dry-run" mode<commit_after>use std::io::fs::rename;
use std::io::fs::PathExtensions;
use std::collections::HashMap;
use std::os;
use std::io;
use std::vec::Vec;
use std::str::StrSlice;

mod util;
mod server;

fn main() {

    let args = os::args();

    if args.len() == 2 && args[1] == "--server".to_string() {
        serve();
        return;
    }

    if args.len() == 2 && args[1] == "--start-server".to_string() {
        server::start_server();
        return;
    }

    let use_external_process = args.contains(&String::from_str("--use-external-process"));

    if args.len() < 3 {
        println!("Usage: {} <original-directory> <new-directory>", args[0]);
        return;
    }

    let remote_source = args[1].as_slice().contains(":");
    let dry_run = args.contains(&"--dry-run".to_string());
    let host: String;
    let source_path: String;

    if remote_source {
        let components: Vec<&str> = args[1].as_slice().split(':').collect();
        host = components[0].to_string();
        source_path = components[1].to_string();
    }
    else {
        source_path = args[1].clone();
        host = "".to_string();
    }

    let src_directory = Path::new(source_path);
    let dst_directory = Path::new(args[2].clone());

    if remote_source || use_external_process {
        let mut server = server::StdServer::new(host);
        let mut dest_server = server::InMemoryServer::new();
        sync_renames(dry_run, &mut server, &mut dest_server, &src_directory, &dst_directory);
    }
    else {
        let mut server = server::InMemoryServer::new();
        let mut dest_server = server::InMemoryServer::new();
        sync_renames(dry_run, &mut server, &mut dest_server, &src_directory, &dst_directory);
    }
}

fn sync_renames(dry_run: bool, source_server: &mut server::Server, dest_server: &mut server::Server, src_directory: &Path, dst_directory: &Path) {

    let src_map = source_server.get_metadata(src_directory);
    let new_map = dest_server.get_metadata(dst_directory);

    for (&(size, mod_date), paths) in new_map.iter() {

        let mut matching_paths = 0i;

        for (i, value) in paths.iter().enumerate() {

            let new_path = Path::new(value.clone());
            let src_path :Path;

            match find_matching_file(&new_path, size, mod_date, &src_map, source_server, dest_server) {
                Some(p) => src_path = p,
                None => continue
            }

            let mut file_path = dst_directory.clone();
            file_path.push(src_path.path_relative_from(src_directory).unwrap());

            let src_relative_path = src_path.path_relative_from(src_directory).unwrap();
            let dst_relative_path = new_path.path_relative_from(dst_directory).unwrap();

            // No point in comparing a file with itself
            if src_relative_path == dst_relative_path {
                continue;
            }

            matching_paths += 1;

            if matching_paths > 1 && file_path.exists() && source_server.get_checksum(&src_path) == dest_server.get_checksum(&file_path) {
                println!("{} was copied to {}", dst_relative_path.display(), src_relative_path.display());

            } else {
                println!("{} was renamed to {}", dst_relative_path.display(), src_relative_path.display());
                if !dry_run {
                    rename(
                        &dst_directory.join(dst_relative_path),
                        &dst_directory.join(src_relative_path)).unwrap();
                }
            }
        }
    }
}

fn find_matching_file(file: &Path, size: u64, modification_date: u64, master_metadata: &HashMap<(u64, u64), Vec<String>>, src_server: &mut server::Server, dest_server: &mut server::Server) -> Option<Path> {

    // Check whether there are any files in master with the same
    // size and modification date of the sought file
    match master_metadata.find(&(size, modification_date)) {

        Some(matches) => {

            // Compare the file contents until we find a match, or return None if
            // we don't find one
            for potential_match in matches.iter() {
                let path = Path::new(potential_match.clone());
                if src_server.get_checksum(&path) == dest_server.get_checksum(file) {
                    return Some(path);
                }
            }

            return None

        }

        None => return None
    }

}

fn serve() {

    for line in io::stdin().lines() {
        let unwrapped_line = line.unwrap();
        let line_slice = unwrapped_line.as_slice().trim_right_chars('\n');
        if line_slice.starts_with("checksum ") {
           let file_name = line_slice.slice_from(9);
           println!("{}", util::get_file_checksum(&Path::new(file_name)));
           println!("\0");
        }

        if line_slice.starts_with("metadata ") {
            let path = line_slice.slice_from(9);
            for metadata in util::MetadataReader::new(&Path::new(path)) {
                println!("{}", metadata.path);
                println!("{}", metadata.size);
                println!("{}", metadata.modified);
            }
            println!("\0");

        }

    }
}
<|endoftext|>


<commit_before>use std::os;
use std::io::process::{Command,InheritFd,ExitStatus,ExitSignal};

use self::State::{Quoted, Escaped, Normal};

enum State {
    Quoted(char),
    Escaped,
    Normal
}

fn trimmed(s: &String) -> String {
    from_str::<String>(s.trim_chars(' ')).unwrap()
}

fn extract_commands(input: String) -> Vec<String> {
    let mut state: State = Normal;
    let mut commands: Vec<String> = vec![];
    let mut it = input.chars();
    let mut s = String::new();
    loop {
        let c = match it.next() {
            Some(ch) => ch.clone(),
            None => {
                commands.push(trimmed(&s));
                return commands;
            }
        };
        match state {
            Normal => {
                match c {
                    '\\' => {
                        state = Escaped;
                    },
                    '\'' | '"' => {
                        state = Quoted(c);
                        s.push(c);
                    },
                    ',' => {
                        commands.push(trimmed(&s));
                        s.clear();
                    },
                    _ => {
                        s.push(c);
                    },
                }
            },
            Quoted(terminator) => {
                if c == terminator {
                    state = Normal;
                }
                s.push(c);
            },
            Escaped => {
                state = Normal;
                s.push(c);
            },
        }
    }
}

fn main() {
    let mut a = os::args();
    // remove the binary name
    let _ = a.remove(0);
    let args = a.connect(" ");
    for command in extract_commands(args).iter() {
        // need to improve this, it assumes cargo is in our path..
        let status = Command::new("cargo")
                        .args(&[command])
                        .stdin(InheritFd(0))
                        .stdout(InheritFd(1))
                        .stderr(InheritFd(2))
                        .status();
        match status {
            Ok(ExitStatus(0)) => (),
            Ok(ExitStatus(i)) => {
                os::set_exit_status(i)
            },
            Ok(ExitSignal(i)) => {
                os::set_exit_status(i)
            },
            Err(_) => {
                os::set_exit_status(127)
            },
        }
    }
}

<commit_msg>strip off the 'cargo' and 'do' arguments too<commit_after>use std::os;
use std::io::process::{Command,InheritFd,ExitStatus,ExitSignal};

use self::State::{Quoted, Escaped, Normal};

enum State {
    Quoted(char),
    Escaped,
    Normal
}

fn trimmed(s: &String) -> String {
    from_str::<String>(s.trim_chars(' ')).unwrap()
}

fn extract_commands(input: String) -> Vec<String> {
    let mut state: State = Normal;
    let mut commands: Vec<String> = vec![];
    let mut it = input.chars();
    let mut s = String::new();
    loop {
        let c = match it.next() {
            Some(ch) => ch.clone(),
            None => {
                commands.push(trimmed(&s));
                return commands;
            }
        };
        match state {
            Normal => {
                match c {
                    '\\' => {
                        state = Escaped;
                    },
                    '\'' | '"' => {
                        state = Quoted(c);
                        s.push(c);
                    },
                    ',' => {
                        commands.push(trimmed(&s));
                        s.clear();
                    },
                    _ => {
                        s.push(c);
                    },
                }
            },
            Quoted(terminator) => {
                if c == terminator {
                    state = Normal;
                }
                s.push(c);
            },
            Escaped => {
                state = Normal;
                s.push(c);
            },
        }
    }
}

fn main() {
    let orig_args = os::args();
    // remove the binary name "cargo-do", and the next 2 arguments,
    // which are just "cargo" and "do"
    let args = orig_args.slice_from(3).connect(" ");
    for command in extract_commands(args).iter() {
        // need to improve this, it assumes cargo is in our path..
        let status = Command::new("cargo")
                        .args(&[command])
                        .stdin(InheritFd(0))
                        .stdout(InheritFd(1))
                        .stderr(InheritFd(2))
                        .status();
        match status {
            Ok(ExitStatus(0)) => (),
            Ok(ExitStatus(i)) => {
                os::set_exit_status(i)
            },
            Ok(ExitSignal(i)) => {
                os::set_exit_status(i)
            },
            Err(_) => {
                os::set_exit_status(127)
            },
        }
    }
}

<|endoftext|>


<commit_before>//! Main module of way-cooler

#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate bitflags;
#[macro_use]
extern crate dbus_macros;
#[cfg(not(test))]
extern crate rustwlc;
extern crate getopts;
#[cfg(test)]
extern crate dummy_rustwlc as rustwlc;
#[macro_use]
extern crate log;
extern crate env_logger;
#[macro_use]
extern crate hlua;
extern crate rustc_serialize;
#[macro_use]
extern crate json_macro;
extern crate nix;
extern crate petgraph;
extern crate uuid;
extern crate dbus;

#[macro_use]
mod macros;
mod convert;

mod callbacks;
mod keys;

mod lua;
mod registry;
mod commands;
mod ipc;

mod layout;

use std::env;
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::path::Path;

use getopts::Options;
use log::LogLevel;
use nix::sys::signal::{self, SigHandler, SigSet, SigAction, SaFlags};

use rustwlc::types::LogType;

const VERSION: &'static str = env!("CARGO_PKG_VERSION");
const DRIVER_MOD_PATH: &'static str = "/proc/modules";

/// Callback to route wlc logs into env_logger
fn log_handler(level: LogType, message: &str) {
    match level {
        LogType::Info => info!("wlc: {}", message),
        LogType::Warn => warn!("wlc: {}", message),
        LogType::Error => error!("wlc: {}", message),
        LogType::Wayland => info!("wayland: {}", message)
    }
}

/// Formats the log strings properly
fn log_format(record: &log::LogRecord) -> String {
    let color = match record.level() {
        LogLevel::Info => "",
        LogLevel::Trace => "\x1B[37m",
        LogLevel::Debug => "\x1B[37m",
        LogLevel::Warn =>  "\x1B[33m",
        LogLevel::Error => "\x1B[31m",
    };
    let location = record.location();
    let file = location.file();
    let line = location.line();
    let mut module_path = location.module_path();
    if let Some(index) = module_path.find("way_cooler::") {
        let index = index + "way_cooler::".len();
        module_path = &module_path[index..];
    }
    format!("{} {} [{}] \x1B[37m{}:{}\x1B[0m{0} {} \x1B[0m",
            color, record.level(), module_path, file, line, record.args())
}

/// Checks the loaded modules, and reports any problematic proprietary ones
fn detect_proprietary() {
    // If DISPLAY is present, we are running embedded
    if env::var("DISPLAY").is_ok() {
        return
    }
    match File::open(Path::new(DRIVER_MOD_PATH)) {
        Ok(file) => {
            let reader = BufReader::new(&file);
            for line in reader.lines() {
                if let Ok(line) = line {
                    if line.contains("nvidia") {
                        error!("Warning: Proprietary nvidia graphics drivers are installed, \
                               but they are not compatible with Wayland. \
                               Consider using nouveau drivers for Wayland.");
                        break;
                    } else if line.contains("fglrx") {
                        error!("Warning: Proprietary AMD graphics drivers are installed, \
                                but they are not compatible with Wayland. \
                               Consider using radeon drivers for Waylad.");
                        break;
                    }
                }
            }
        },
        Err(err) => {
            warn!("Could not read proprietary modules at \"{}\", because: {:#?}",
                  DRIVER_MOD_PATH, err);
            warn!("If you are running proprietary AMD or Nvidia graphics drivers, \
                   Way Cooler may not work for you");
        }
    }
}

/// Initializes the logging system.
pub fn init_logs() {
    let mut builder = env_logger::LogBuilder::new();
    builder.format(log_format);
    builder.filter(None, log::LogLevelFilter::Trace);
    if env::var("WAY_COOLER_LOG").is_ok() {
        builder.parse(&env::var("WAY_COOLER_LOG")
                      .expect("WAY_COOLER_LOG not defined"));
    }
    builder.init().expect("Unable to initialize logging!");
    info!("Logger initialized, setting wlc handlers.");
}

fn log_environment() {
    for (key, value) in env::vars() {
        debug!("{}: {}", key, value);
    }
}

/// Handler for signals
extern "C" fn sig_handle(_: nix::libc::c_int) {
    rustwlc::terminate();
}

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

    let mut opts = Options::new();
    opts.optflag("", "version", "show version information");
    let matches = match opts.parse(&args[1..]) {
        Ok(m) => m,
        Err(f) => {
            println!("{}", f.to_string());
            std::process::exit(1);
        }
    };
    if matches.opt_present("version") {
        println!("Way Cooler {}", VERSION);
        println!("Way Cooler IPC version {}", ipc::VERSION);
        return
    }
    println!("Launching way-cooler...");

    let sig_action = SigAction::new(SigHandler::Handler(sig_handle), SaFlags::empty(),
                                    SigSet::empty());
    unsafe {signal::sigaction(signal::SIGINT, &sig_action).unwrap() };

    init_logs();
    log_environment();
    detect_proprietary();
    // This prepares wlc, doesn't run main loop until run_wlc is called
    let run_wlc = rustwlc::init2().expect("Unable to initialize wlc!");
    rustwlc::log_set_rust_handler(log_handler);
    callbacks::init();
    commands::init();
    registry::init();
    ipc::init();

    info!("Running wlc...");
    run_wlc();
}
<commit_msg>Added git version to --version output<commit_after>//! Main module of way-cooler

#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate bitflags;
#[macro_use]
extern crate dbus_macros;
#[cfg(not(test))]
extern crate rustwlc;
extern crate getopts;
#[cfg(test)]
extern crate dummy_rustwlc as rustwlc;
#[macro_use]
extern crate log;
extern crate env_logger;
#[macro_use]
extern crate hlua;
extern crate rustc_serialize;
#[macro_use]
extern crate json_macro;
extern crate nix;
extern crate petgraph;
extern crate uuid;
extern crate dbus;

#[macro_use]
mod macros;
mod convert;

mod callbacks;
mod keys;

mod lua;
mod registry;
mod commands;
mod ipc;

mod layout;

use std::env;
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::path::Path;

use getopts::Options;
use log::LogLevel;
use nix::sys::signal::{self, SigHandler, SigSet, SigAction, SaFlags};

use rustwlc::types::LogType;

const VERSION: &'static str = env!("CARGO_PKG_VERSION");
const GIT_VERSION: &'static str = include_str!(concat!(env!("OUT_DIR"), "/git-version.txt"));
const DRIVER_MOD_PATH: &'static str = "/proc/modules";

/// Callback to route wlc logs into env_logger
fn log_handler(level: LogType, message: &str) {
    match level {
        LogType::Info => info!("wlc: {}", message),
        LogType::Warn => warn!("wlc: {}", message),
        LogType::Error => error!("wlc: {}", message),
        LogType::Wayland => info!("wayland: {}", message)
    }
}

/// Formats the log strings properly
fn log_format(record: &log::LogRecord) -> String {
    let color = match record.level() {
        LogLevel::Info => "",
        LogLevel::Trace => "\x1B[37m",
        LogLevel::Debug => "\x1B[37m",
        LogLevel::Warn =>  "\x1B[33m",
        LogLevel::Error => "\x1B[31m",
    };
    let location = record.location();
    let file = location.file();
    let line = location.line();
    let mut module_path = location.module_path();
    if let Some(index) = module_path.find("way_cooler::") {
        let index = index + "way_cooler::".len();
        module_path = &module_path[index..];
    }
    format!("{} {} [{}] \x1B[37m{}:{}\x1B[0m{0} {} \x1B[0m",
            color, record.level(), module_path, file, line, record.args())
}

/// Checks the loaded modules, and reports any problematic proprietary ones
fn detect_proprietary() {
    // If DISPLAY is present, we are running embedded
    if env::var("DISPLAY").is_ok() {
        return
    }
    match File::open(Path::new(DRIVER_MOD_PATH)) {
        Ok(file) => {
            let reader = BufReader::new(&file);
            for line in reader.lines() {
                if let Ok(line) = line {
                    if line.contains("nvidia") {
                        error!("Warning: Proprietary nvidia graphics drivers are installed, \
                               but they are not compatible with Wayland. \
                               Consider using nouveau drivers for Wayland.");
                        break;
                    } else if line.contains("fglrx") {
                        error!("Warning: Proprietary AMD graphics drivers are installed, \
                                but they are not compatible with Wayland. \
                               Consider using radeon drivers for Waylad.");
                        break;
                    }
                }
            }
        },
        Err(err) => {
            warn!("Could not read proprietary modules at \"{}\", because: {:#?}",
                  DRIVER_MOD_PATH, err);
            warn!("If you are running proprietary AMD or Nvidia graphics drivers, \
                   Way Cooler may not work for you");
        }
    }
}

/// Initializes the logging system.
pub fn init_logs() {
    let mut builder = env_logger::LogBuilder::new();
    builder.format(log_format);
    builder.filter(None, log::LogLevelFilter::Trace);
    if env::var("WAY_COOLER_LOG").is_ok() {
        builder.parse(&env::var("WAY_COOLER_LOG")
                      .expect("WAY_COOLER_LOG not defined"));
    }
    builder.init().expect("Unable to initialize logging!");
    info!("Logger initialized, setting wlc handlers.");
}

fn log_environment() {
    for (key, value) in env::vars() {
        debug!("{}: {}", key, value);
    }
}

/// Handler for signals
extern "C" fn sig_handle(_: nix::libc::c_int) {
    rustwlc::terminate();
}

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

    let mut opts = Options::new();
    opts.optflag("", "version", "show version information");
    let matches = match opts.parse(&args[1..]) {
        Ok(m) => m,
        Err(f) => {
            println!("{}", f.to_string());
            std::process::exit(1);
        }
    };
    if matches.opt_present("version") {
        if !GIT_VERSION.is_empty() {
            println!("Way Cooler {} @ {}", VERSION, GIT_VERSION);
        } else {
            println!("Way Cooler {}", VERSION);
        }
        println!("Way Cooler IPC version {}", ipc::VERSION);
        return
    }
    println!("Launching way-cooler...");

    let sig_action = SigAction::new(SigHandler::Handler(sig_handle), SaFlags::empty(),
                                    SigSet::empty());
    unsafe {signal::sigaction(signal::SIGINT, &sig_action).unwrap() };

    init_logs();
    log_environment();
    detect_proprietary();
    // This prepares wlc, doesn't run main loop until run_wlc is called
    let run_wlc = rustwlc::init2().expect("Unable to initialize wlc!");
    rustwlc::log_set_rust_handler(log_handler);
    callbacks::init();
    commands::init();
    registry::init();
    ipc::init();

    info!("Running wlc...");
    run_wlc();
}
<|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: The list command should specify which files are directory.
 */

#![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::ffi::OsStr;
use std::fs::{read_dir, DirEntry, Metadata};
use std::io;
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use std::path::{Component, PathBuf};

use futures::{Sink, Stream};
use futures::prelude::{async, await};
use futures::stream::SplitSink;
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 DEFAULT_PORT: u16 = 4321;
const MONTHS: [&'static str; 12] = ["Jan", "Feb", "Mar", "Apr", "May", "Jun",
                                     "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];

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 = if path.starts_with("./") {
        path.strip_prefix("./")
            .unwrap() // TODO: handle error.
            .to_str()
            .unwrap() // TODO: handle error.
    } else {
        path.to_str().unwrap()
    };
    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_writer: Option<DataWriter>,
    handle: Handle,
    name: Option<String>,
    transfer_type: TransferType,
    writer: Writer,
}

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

    #[async]
    fn handle_cmd(mut self, cmd: Command) -> Result<Self, ()> {
        println!("Received command: {:?}", cmd);
        match cmd {
            Command::Auth =>
                // TODO: create a macro await_self to avoid this awkward syntax?
                self = await!(self.send(Answer::new(ResultCode::CommandNotImplemented, "Not implemented")))?
            ,
            Command::Cwd(directory) => {
                // TODO: Actually implement the command. Since chroot works only on UNIX
                // platforms, we can't use it for that. :'(
                self = await!(self.send(Answer::new(ResultCode::Ok, &format!("Directory changed to \"{}\"", 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::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))))?;
                }
            }
        }
        Ok(self)
    }

    #[async]
    fn list(mut self, path: Option<PathBuf>) -> Result<Self, ()> {
        if self.data_writer.is_some() {
            let mut tmp = PathBuf::from(".");
            {
                let path = path.as_ref().unwrap_or(&self.cwd);
                // TODO: would it be better to check if the directory is a child on the root?
                for item in path.components().skip(1) {
                    match item {
                        Component::Normal(ref s) if s != &OsStr::new("..") => tmp.push(s),
                        _ => {}
                    }
                }
            }
            if tmp.is_dir() {
                self = await!(self.send(Answer::new(ResultCode::DataConnectionAlreadyOpen, "Starting to list directory...")))?;
                let mut out = String::new();
                for entry in read_dir(tmp).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::LocalErrorInProcessing,
                                      &format!("\"{}\" doesn't exist", tmp.to_str().unwrap()))))?;
            }
        } else {
            self = await!(self.send(Answer::new(ResultCode::ConnectionClosed, "No opened data connection")))?;
        }
        if self.data_writer.is_some() {
            self.data_writer = None;
            self = await!(self.send(Answer::new(ResultCode::ClosingDataConnection, "Transfer done")))?;
        }
        Ok(self)
    }

    #[async]
    fn pasv(mut self) -> Result<Self, ()> {
        // TODO: I believe this command should be blocking.
        let port =
            if let Some(port) = self.data_port {
                port
            } else {
                DEFAULT_PORT
            };
        if self.data_writer.is_some() {
            self = await!(self.send(Answer::new(ResultCode::DataConnectionAlreadyOpen, "Already listening...")))?;
            return Ok(self);
        }
        self = await!(self.send(Answer::new(ResultCode::EnteringPassiveMode,
                              &format!("127,0,0,1,{},{}", port >> 8, port & 0xFF))))?;

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

        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);
            break;
        }
        Ok(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(mut 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) -> 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);
    #[async]
    for cmd in reader.map_err(|_| ()) {
        client = await!(client.handle_cmd(cmd))?;
    }
    Ok(())
}

#[async]
fn server(handle: Handle) -> 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));
        println!("Waiting another client...");
    }
    Ok(())
}

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

    core.run(server(handle))
        .expect("Run tokio server");
}
<commit_msg>Fix list and cwd<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: The list command should specify which files are directory.
 */

#![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::ffi::OsStr;
use std::fs::{read_dir, DirEntry, Metadata};
use std::io;
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use std::path::{Component, PathBuf};

use futures::{Sink, Stream};
use futures::prelude::{async, await};
use futures::stream::SplitSink;
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 DEFAULT_PORT: u16 = 4321;
const MONTHS: [&'static str; 12] = ["Jan", "Feb", "Mar", "Apr", "May", "Jun",
                                    "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];

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_writer: Option<DataWriter>,
    handle: Handle,
    name: Option<String>,
    transfer_type: TransferType,
    writer: Writer,
}

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

    #[async]
    fn handle_cmd(mut self, cmd: Command) -> Result<Self, ()> {
        println!("Received command: {:?}", cmd);
        match cmd {
            Command::Auth =>
                // TODO: create a macro await_self to avoid this awkward syntax?
                self = await!(self.send(Answer::new(ResultCode::CommandNotImplemented, "Not implemented")))?
            ,
            Command::Cwd(directory) => {
                let mut directory = PathBuf::from(directory);
                if !directory.has_root() {
                    directory = self.cwd.join(directory);
                }
                let current = env::current_dir().unwrap(); // TODO: handle error.

                let directory = current.join(if directory.has_root() {
                    directory.iter().skip(1).collect()
                } else {
                    directory
                });
                let mut changed = false;
                if let Ok(dir) = directory.canonicalize() {
                    if dir.starts_with(&current) { // TODO: handle error.
                        self.cwd = dir.strip_prefix(&current).unwrap().to_path_buf(); // TODO: handle error.
                        changed = true;
                        println!("switched to {:?}", self.cwd);
                    }
                }
                if changed {
                    self = await!(self.send(Answer::new(ResultCode::Ok,
                                  &format!("Directory changed to \"{}\"", directory.display()))))?;
                } else {
                    self = await!(self.send(Answer::new(ResultCode::FileNotFound,
                                  "Requested folder doesn't exist")))?;
                }
                // TODO: Actually implement the command. Since chroot works only on UNIX
                // platforms, we can't use it for that. :'(
            },
            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::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))))?;
                }
            }
        }
        Ok(self)
    }

    #[async]
    fn list(mut self, path: Option<PathBuf>) -> Result<Self, ()> {
        if self.data_writer.is_some() {
            let ori = path.as_ref().unwrap_or(&self.cwd).to_path_buf();
            let directory = PathBuf::from(&ori);
            let current = env::current_dir().unwrap(); // TODO: handle error.
            let directory = if self.cwd.has_root() {
                directory.iter().skip(1).collect()
            } else {
                directory.clone() // no very beautiful...
            };
            let directory = current.join(if directory.has_root() {
                directory.iter().skip(1).collect()
            } else {
                directory
            });
            let mut done = false;
            let dir = directory.canonicalize();
            println!("===> {:?} {:?} {:?} {:?}", dir, directory, ori, path);
            if let Ok(dir) = dir {
                if dir.starts_with(&current) {
                    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!");
                    done = true;
                }
            }
            if !done {
                self = await!(self.send(Answer::new(ResultCode::LocalErrorInProcessing,
                                      &format!("\"{}\" doesn't exist", ori.to_str().unwrap()))))?;
            }
        } else {
            self = await!(self.send(Answer::new(ResultCode::ConnectionClosed, "No opened data connection")))?;
        }
        if self.data_writer.is_some() {
            self.data_writer = None;
            self = await!(self.send(Answer::new(ResultCode::ClosingDataConnection, "Transfer done")))?;
        }
        Ok(self)
    }

    #[async]
    fn pasv(mut self) -> Result<Self, ()> {
        // TODO: I believe this command should be blocking.
        let port =
            if let Some(port) = self.data_port {
                port
            } else {
                DEFAULT_PORT
            };
        if self.data_writer.is_some() {
            self = await!(self.send(Answer::new(ResultCode::DataConnectionAlreadyOpen, "Already listening...")))?;
            return Ok(self);
        }
        self = await!(self.send(Answer::new(ResultCode::EnteringPassiveMode,
                              &format!("127,0,0,1,{},{}", port >> 8, port & 0xFF))))?;

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

        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);
            break;
        }
        Ok(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(mut 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) -> 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);
    #[async]
    for cmd in reader.map_err(|_| ()) {
        client = await!(client.handle_cmd(cmd))?;
    }
    Ok(())
}

#[async]
fn server(handle: Handle) -> 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));
        println!("Waiting another client...");
    }
    Ok(())
}

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

    core.run(server(handle))
        .expect("Run tokio server");
}
<|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;

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

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

fn get_manifest() -> Result<json::JsonValue, Error> {
    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");
    json::parse(&json_string).map_err(Error::InvalidManifestJson)
}

fn get_target_config<'a>(
    target_cfgs: &'a [json::JsonValue],
    args: &Arguments,
) -> Result<&'a json::JsonValue, Error> {
    fn is_lib(cfg: &json::JsonValue) -> bool {
        let is_lib = cfg["kind"].contains("lib");
        let is_rlib = cfg["kind"].contains("rlib");
        let is_staticlib = cfg["kind"].contains("staticlib");
        is_lib || is_rlib || is_staticlib
    }
    if args.lib {
        target_cfgs
            .iter()
            .find(|cfg| is_lib(cfg))
            .ok_or(Error::NoLibraryTargetFound)
    } else if let Some(ref name) = args.bin {
        target_cfgs
            .iter()
            .find(|cfg| cfg["kind"].contains("bin") && cfg["name"] == name.as_ref())
            .ok_or(Error::NoMatchingBinaryTargetFound)
    } else if target_cfgs.len() == 1 {
        Ok(&target_cfgs[0])
    } else {
        target_cfgs
            .iter()
            .find(|cfg| is_lib(cfg))
            .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)?
    };

    let json = try!(get_manifest());
    let target_cfgs: Vec<_> = json["targets"].members().cloned().collect();
    let build_scripts: Vec<path::PathBuf> = manifest
        .custom_builds()
        .iter()
        .map(|t| path::Path::new(t.src_path()).to_path_buf())
        .collect();
    let target_config = try!(get_target_config(&target_cfgs, args));
    let target_name = target_config["name"]
        .as_str()
        .expect("Expected `name` property.");
    let src_path = target_config["src_path"]
        .as_str()
        .expect("Expected `src_path` property.");
    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>Add TODO<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;

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

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

fn get_manifest() -> Result<json::JsonValue, Error> {
    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");
    json::parse(&json_string).map_err(Error::InvalidManifestJson)
}

fn get_target_config<'a>(
    target_cfgs: &'a [json::JsonValue],
    args: &Arguments,
) -> Result<&'a json::JsonValue, Error> {
    fn is_lib(cfg: &json::JsonValue) -> bool {
        let is_lib = cfg["kind"].contains("lib");
        let is_rlib = cfg["kind"].contains("rlib");
        let is_staticlib = cfg["kind"].contains("staticlib");
        is_lib || is_rlib || is_staticlib
    }
    if args.lib {
        target_cfgs
            .iter()
            .find(|cfg| is_lib(cfg))
            .ok_or(Error::NoLibraryTargetFound)
    } else if let Some(ref name) = args.bin {
        target_cfgs
            .iter()
            .find(|cfg| cfg["kind"].contains("bin") && cfg["name"] == name.as_ref())
            .ok_or(Error::NoMatchingBinaryTargetFound)
    } else if target_cfgs.len() == 1 {
        Ok(&target_cfgs[0])
    } else {
        target_cfgs
            .iter()
            .find(|cfg| is_lib(cfg))
            .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)?
    };

    let json = try!(get_manifest());
    let target_cfgs: Vec<_> = json["targets"].members().cloned().collect();
    // 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| path::Path::new(t.src_path()).to_path_buf())
        .collect();
    let target_config = try!(get_target_config(&target_cfgs, args));
    let target_name = target_config["name"]
        .as_str()
        .expect("Expected `name` property.");
    let src_path = target_config["src_path"]
        .as_str()
        .expect("Expected `src_path` property.");
    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());
    }
}
<|endoftext|>