<commit_before>use crate::parser::errors::CustomError;
use crate::parser::trivia::{is_non_ascii, is_wschar, newline, ws, ws_newlines};
use crate::repr::InternalString;
use combine::error::{Commit, Info};
use combine::parser::char::char;
use combine::parser::range::{range, take, take_while};
use combine::stream::RangeStream;
use combine::*;
use std::char;

// ;; String

// string = ml-basic-string / basic-string / ml-literal-string / literal-string
parse!(string() -> InternalString, {
    choice((
        ml_basic_string(),
        basic_string(),
        ml_literal_string(),
        literal_string().map(|s: &'a str| s.into()),
    ))
});

// ;; Basic String

// basic-string = quotation-mark *basic-char quotation-mark
parse!(basic_string() -> InternalString, {
    between(char(QUOTATION_MARK), char(QUOTATION_MARK),
            many(basic_char()))
        .message("While parsing a Basic String")
});

// quotation-mark = %x22            ; "
const QUOTATION_MARK: char = '"';

// basic-char = basic-unescaped / escaped
parse!(basic_char() -> char, {
    satisfy(|c| is_basic_unescaped(c) || c == ESCAPE)
        .then(|c| parser(move |input| {
            match c {
                ESCAPE => escape().parse_stream(input).into_result(),
                _      => Ok((c, Commit::Peek(()))),
            }
        }))
});

// basic-unescaped = wschar / %x21 / %x23-5B / %x5D-7E / non-ascii
#[inline]
fn is_basic_unescaped(c: char) -> bool {
    is_wschar(c)
        | matches!(c, '\u{21}' | '\u{23}'..='\u{5B}' | '\u{5D}'..='\u{7E}')
        | is_non_ascii(c)
}

// escape = %x5C                    ; \
const ESCAPE: char = '\\';

parse!(escape() -> char, {
    satisfy(is_escape_seq_char)
        .message("While parsing escape sequence")
        .then(|c| {
            parser(move |input| {
                match c {
                    'b'  => Ok(('\u{8}', Commit::Peek(()))),
                    'f'  => Ok(('\u{c}', Commit::Peek(()))),
                    'n'  => Ok(('\n',    Commit::Peek(()))),
                    'r'  => Ok(('\r',    Commit::Peek(()))),
                    't'  => Ok(('\t',    Commit::Peek(()))),
                    'u'  => hexescape(4).parse_stream(input).into_result(),
                    'U'  => hexescape(8).parse_stream(input).into_result(),
                    // ['\\', '"',]
                    _    => Ok((c,       Commit::Peek(()))),
                }
            })
        })
});

// escape-seq-char =  %x22         ; "    quotation mark  U+0022
// escape-seq-char =/ %x5C         ; \    reverse solidus U+005C
// escape-seq-char =/ %x62         ; b    backspace       U+0008
// escape-seq-char =/ %x66         ; f    form feed       U+000C
// escape-seq-char =/ %x6E         ; n    line feed       U+000A
// escape-seq-char =/ %x72         ; r    carriage return U+000D
// escape-seq-char =/ %x74         ; t    tab             U+0009
// escape-seq-char =/ %x75 4HEXDIG ; uXXXX                U+XXXX
// escape-seq-char =/ %x55 8HEXDIG ; UXXXXXXXX            U+XXXXXXXX
#[inline]
fn is_escape_seq_char(c: char) -> bool {
    matches!(c, '"' | '\\' | 'b' | 'f' | 'n' | 'r' | 't' | 'u' | 'U')
}

parse!(hexescape(n: usize) -> char, {
    take(*n)
        .and_then(|s| u32::from_str_radix(s, 16))
        .and_then(|h| char::from_u32(h).ok_or(CustomError::InvalidHexEscape(h)))
});

// ;; Multiline Basic String

// ml-basic-string = ml-basic-string-delim ml-basic-body ml-basic-string-delim
parse!(ml_basic_string() -> InternalString, {
    between(range(ML_BASIC_STRING_DELIM),
            range(ML_BASIC_STRING_DELIM),
            ml_basic_body())
        .message("While parsing a Multiline Basic String")
});

// ml-basic-string-delim = 3quotation-mark
const ML_BASIC_STRING_DELIM: &str = "\"\"\"";

// ml-basic-body = *( ( escape ws-newline ) / ml-basic-char / newline )
parse!(ml_basic_body() -> InternalString, {
    //  A newline immediately following the opening delimiter will be trimmed.
    optional(newline())
        .skip(try_eat_escaped_newline())
        .with(
            many(
                not_followed_by(range(ML_BASIC_STRING_DELIM).map(Info::Range))
                    .with(
                        choice((
                            // `TOML parsers should feel free to normalize newline
                            //  to whatever makes sense for their platform.`
                            newline(),
                            mlb_char(),
                        ))
                    )
                    .skip(try_eat_escaped_newline())
            )
        )
});

// mlb-char = mlb-unescaped / escaped
parse!(mlb_char() -> char, {
    satisfy(|c| is_mlb_unescaped(c) || c == ESCAPE)
        .then(|c| parser(move |input| {
            match c {
                ESCAPE => escape().parse_stream(input).into_result(),
                _      => Ok((c, Commit::Peek(()))),
            }
        }))
});

// mlb-unescaped = wschar / %x21 / %x23-5B / %x5D-7E / non-ascii
#[inline]
fn is_mlb_unescaped(c: char) -> bool {
    is_wschar(c)
        | matches!(c, '\u{21}' | '\u{23}'..='\u{5B}' | '\u{5D}'..='\u{7E}')
        | is_non_ascii(c)
        // Unlike the official grammar, we can handle quotes just fine
        | (c == '\u{22}')
}

// When the last non-whitespace character on a line is a \,
// it will be trimmed along with all whitespace
// (including newlines) up to the next non-whitespace
// character or closing delimiter.
parse!(try_eat_escaped_newline() -> (), {
    skip_many(attempt((
        char(ESCAPE),
        ws(),
        ws_newlines(),
    )))
});

// ;; Literal String

// literal-string = apostrophe *literal-char apostrophe
parse!(literal_string() -> &'a str, {
    between(char(APOSTROPHE), char(APOSTROPHE),
            take_while(is_literal_char))
        .message("While parsing a Literal String")
});

// apostrophe = %x27 ; ' apostrophe
const APOSTROPHE: char = '\'';

// literal-char = %x09 / %x20-26 / %x28-7E / non-ascii
#[inline]
fn is_literal_char(c: char) -> bool {
    matches!(c, '\u{09}' | '\u{20}'..='\u{26}' | '\u{28}'..='\u{7E}') | is_non_ascii(c)
}

// ;; Multiline Literal String

// ml-literal-string = ml-literal-string-delim ml-literal-body ml-literal-string-delim
parse!(ml_literal_string() -> InternalString, {
    between(range(ML_LITERAL_STRING_DELIM),
            range(ML_LITERAL_STRING_DELIM),
            ml_literal_body())
        .message("While parsing a Multiline Literal String")
});

// ml-literal-string-delim = 3apostrophe
const ML_LITERAL_STRING_DELIM: &str = "'''";

// ml-literal-body = *( ml-literal-char / newline )
parse!(ml_literal_body() -> InternalString, {
    //  A newline immediately following the opening delimiter will be trimmed.
    optional(newline())
        .with(
            many(
                not_followed_by(range(ML_LITERAL_STRING_DELIM).map(Info::Range))
                    .with(
                        choice((
                            // `TOML parsers should feel free to normalize newline
                            //  to whatever makes sense for their platform.`
                            newline(),
                            satisfy(is_mll_char),
                        ))
                    )
            )
        )
});

// mll-char = %x09 / %x20-26 / %x28-7E / non-ascii
#[inline]
fn is_mll_char(c: char) -> bool {
    matches!(c, '\u{09}' | '\u{20}'..='\u{26}' | '\u{28}'..='\u{7E}') | is_non_ascii(c)
        // Unlike the official grammar, we can handle quotes just fine
        | (c == '\u{27}')
}
<commit_msg>perf(parser): Batch create strings (#209)<commit_after>use crate::parser::errors::CustomError;
use crate::parser::trivia::{is_non_ascii, is_wschar, newline, ws, ws_newlines};
use crate::repr::InternalString;
use combine::error::{Commit, Info};
use combine::parser::char::char;
use combine::parser::range::{range, take, take_while, take_while1};
use combine::stream::RangeStream;
use combine::*;
use std::borrow::Cow;
use std::char;

// ;; String

// string = ml-basic-string / basic-string / ml-literal-string / literal-string
parse!(string() -> InternalString, {
    choice((
        ml_basic_string(),
        basic_string(),
        ml_literal_string(),
        literal_string().map(|s: &'a str| s.into()),
    ))
});

// ;; Basic String

// basic-string = quotation-mark *basic-char quotation-mark
parse!(basic_string() -> InternalString, {
    between(
        char(QUOTATION_MARK), char(QUOTATION_MARK),
        many(basic_chars())
    )
    .message("While parsing a Basic String")
});

// quotation-mark = %x22            ; "
const QUOTATION_MARK: char = '"';

// basic-char = basic-unescaped / escaped
parse!(basic_chars() -> Cow<'a, str>, {
    choice((
        // Deviate from the official grammar by batching the unescaped chars so we build a string a
        // chunk at a time, rather than a `char` at a time.
        take_while1(is_basic_unescaped).map(Cow::Borrowed),
        satisfy(|c| c == ESCAPE)
            .then(|_| parser(move |input| {
                escape().parse_stream(input).into_result().map(|(c, e)| (Cow::Owned(String::from(c)), e))
            }))
    ))
});

// basic-unescaped = wschar / %x21 / %x23-5B / %x5D-7E / non-ascii
#[inline]
fn is_basic_unescaped(c: char) -> bool {
    is_wschar(c)
        | matches!(c, '\u{21}' | '\u{23}'..='\u{5B}' | '\u{5D}'..='\u{7E}')
        | is_non_ascii(c)
}

// escape = %x5C                    ; \
const ESCAPE: char = '\\';

parse!(escape() -> char, {
    satisfy(is_escape_seq_char)
        .message("While parsing escape sequence")
        .then(|c| {
            parser(move |input| {
                match c {
                    'b'  => Ok(('\u{8}', Commit::Peek(()))),
                    'f'  => Ok(('\u{c}', Commit::Peek(()))),
                    'n'  => Ok(('\n',    Commit::Peek(()))),
                    'r'  => Ok(('\r',    Commit::Peek(()))),
                    't'  => Ok(('\t',    Commit::Peek(()))),
                    'u'  => hexescape(4).parse_stream(input).into_result(),
                    'U'  => hexescape(8).parse_stream(input).into_result(),
                    // ['\\', '"',]
                    _    => Ok((c,       Commit::Peek(()))),
                }
            })
        })
});

// escape-seq-char =  %x22         ; "    quotation mark  U+0022
// escape-seq-char =/ %x5C         ; \    reverse solidus U+005C
// escape-seq-char =/ %x62         ; b    backspace       U+0008
// escape-seq-char =/ %x66         ; f    form feed       U+000C
// escape-seq-char =/ %x6E         ; n    line feed       U+000A
// escape-seq-char =/ %x72         ; r    carriage return U+000D
// escape-seq-char =/ %x74         ; t    tab             U+0009
// escape-seq-char =/ %x75 4HEXDIG ; uXXXX                U+XXXX
// escape-seq-char =/ %x55 8HEXDIG ; UXXXXXXXX            U+XXXXXXXX
#[inline]
fn is_escape_seq_char(c: char) -> bool {
    matches!(c, '"' | '\\' | 'b' | 'f' | 'n' | 'r' | 't' | 'u' | 'U')
}

parse!(hexescape(n: usize) -> char, {
    take(*n)
        .and_then(|s| u32::from_str_radix(s, 16))
        .and_then(|h| char::from_u32(h).ok_or(CustomError::InvalidHexEscape(h)))
});

// ;; Multiline Basic String

// ml-basic-string = ml-basic-string-delim ml-basic-body ml-basic-string-delim
parse!(ml_basic_string() -> InternalString, {
    between(range(ML_BASIC_STRING_DELIM),
            range(ML_BASIC_STRING_DELIM),
            ml_basic_body())
        .message("While parsing a Multiline Basic String")
});

// ml-basic-string-delim = 3quotation-mark
const ML_BASIC_STRING_DELIM: &str = "\"\"\"";

// ml-basic-body = *( ( escape ws-newline ) / ml-basic-char / newline )
parse!(ml_basic_body() -> InternalString, {
    //  A newline immediately following the opening delimiter will be trimmed.
    optional(newline())
        .skip(try_eat_escaped_newline())
        .with(
            many(
                not_followed_by(range(ML_BASIC_STRING_DELIM).map(Info::Range))
                    .with(
                        choice((
                            // `TOML parsers should feel free to normalize newline
                            //  to whatever makes sense for their platform.`
                            newline(),
                            mlb_char(),
                        ))
                    )
                    .skip(try_eat_escaped_newline())
            )
        )
});

// mlb-char = mlb-unescaped / escaped
parse!(mlb_char() -> char, {
    satisfy(|c| is_mlb_unescaped(c) || c == ESCAPE)
        .then(|c| parser(move |input| {
            match c {
                ESCAPE => escape().parse_stream(input).into_result(),
                _      => Ok((c, Commit::Peek(()))),
            }
        }))
});

// mlb-unescaped = wschar / %x21 / %x23-5B / %x5D-7E / non-ascii
#[inline]
fn is_mlb_unescaped(c: char) -> bool {
    is_wschar(c)
        | matches!(c, '\u{21}' | '\u{23}'..='\u{5B}' | '\u{5D}'..='\u{7E}')
        | is_non_ascii(c)
        // Unlike the official grammar, we can handle quotes just fine
        | (c == '\u{22}')
}

// When the last non-whitespace character on a line is a \,
// it will be trimmed along with all whitespace
// (including newlines) up to the next non-whitespace
// character or closing delimiter.
parse!(try_eat_escaped_newline() -> (), {
    skip_many(attempt((
        char(ESCAPE),
        ws(),
        ws_newlines(),
    )))
});

// ;; Literal String

// literal-string = apostrophe *literal-char apostrophe
parse!(literal_string() -> &'a str, {
    between(char(APOSTROPHE), char(APOSTROPHE),
            take_while(is_literal_char))
        .message("While parsing a Literal String")
});

// apostrophe = %x27 ; ' apostrophe
const APOSTROPHE: char = '\'';

// literal-char = %x09 / %x20-26 / %x28-7E / non-ascii
#[inline]
fn is_literal_char(c: char) -> bool {
    matches!(c, '\u{09}' | '\u{20}'..='\u{26}' | '\u{28}'..='\u{7E}') | is_non_ascii(c)
}

// ;; Multiline Literal String

// ml-literal-string = ml-literal-string-delim ml-literal-body ml-literal-string-delim
parse!(ml_literal_string() -> InternalString, {
    between(range(ML_LITERAL_STRING_DELIM),
            range(ML_LITERAL_STRING_DELIM),
            ml_literal_body())
        .message("While parsing a Multiline Literal String")
});

// ml-literal-string-delim = 3apostrophe
const ML_LITERAL_STRING_DELIM: &str = "'''";

// ml-literal-body = *( ml-literal-char / newline )
parse!(ml_literal_body() -> InternalString, {
    //  A newline immediately following the opening delimiter will be trimmed.
    optional(newline())
        .with(
            many(
                not_followed_by(range(ML_LITERAL_STRING_DELIM).map(Info::Range))
                    .with(
                        choice((
                            // `TOML parsers should feel free to normalize newline
                            //  to whatever makes sense for their platform.`
                            newline(),
                            satisfy(is_mll_char),
                        ))
                    )
            )
        )
});

// mll-char = %x09 / %x20-26 / %x28-7E / non-ascii
#[inline]
fn is_mll_char(c: char) -> bool {
    matches!(c, '\u{09}' | '\u{20}'..='\u{26}' | '\u{28}'..='\u{7E}') | is_non_ascii(c)
        // Unlike the official grammar, we can handle quotes just fine
        | (c == '\u{27}')
}
<|endoftext|>


<commit_before>#![feature(plugin_registrar, quote, box_syntax, core, collections, rustc_private, io, unicode, path)]

extern crate rustc;
extern crate syntax;

use syntax::ast;
use syntax::codemap;
use syntax::ext::base::{ExtCtxt, MacResult, MacItems, DummyResult};
use syntax::parse;
use syntax::parse::token;
use syntax::fold::Folder;
use rustc::plugin::Registry;
use std::old_io::fs::File;
use std::str;

use rustast::{AstBuilder, DUMMY_SP};

mod translate;
mod grammar;
mod rustast;

#[plugin_registrar]
pub fn plugin_registrar(reg: &mut Registry) {
    reg.register_syntax_extension(
            token::intern("peg"),
            syntax::ext::base::IdentTT(box expand_peg_str, None));

    reg.register_syntax_extension(
            token::intern("peg_file"),
            syntax::ext::base::IdentTT(box expand_peg_file, None));
}

fn expand_peg_str<'s>(cx: &'s mut ExtCtxt, sp: codemap::Span, ident: ast::Ident, tts: Vec<ast::TokenTree>) -> Box<MacResult + 's> {
    let source = match parse_arg(cx, tts.as_slice()) {
        Some(source) => source,
        None => return DummyResult::any(sp),
    };

    expand_peg(cx, sp, ident, source.as_slice())
}

fn expand_peg_file<'s>(cx: &'s mut ExtCtxt, sp: codemap::Span, ident: ast::Ident, tts: Vec<ast::TokenTree>) -> Box<MacResult + 's> {
    let fname = match parse_arg(cx, tts.as_slice()) {
        Some(fname) => fname,
        None => return DummyResult::any(sp),
    };

    let path = Path::new(cx.codemap().span_to_filename(sp)).dir_path().join(fname);

    let source_utf8 = match File::open(&path).read_to_end() {
      Ok(source_utf8) => source_utf8,
      Err(e) => {
        cx.span_err(sp, e.to_string().as_slice());
        return DummyResult::any(sp)
      }
    };

    let source = str::from_utf8(source_utf8.as_slice()).unwrap();

    cx.codemap().new_filemap(path.as_str().unwrap().to_string(), "".to_string());

    expand_peg(cx, sp, ident, source.as_slice())
}

fn expand_peg(cx: &mut ExtCtxt, sp: codemap::Span, ident: ast::Ident, source: &str) -> Box<MacResult + 'static> {
    let grammar_def = grammar::grammar(source);

    let grammar_def = match grammar_def {
      Ok(grammar_def) => grammar_def,
      Err(msg) => {
        cx.span_err(sp, msg.as_slice());
        return DummyResult::any(sp)
      }
    };

    let ast = translate::compile_grammar(cx, &grammar_def);

    // #![allow(non_snake_case, unused)]
    let allow = cx.attribute(DUMMY_SP, cx.meta_list(DUMMY_SP, token::InternedString::new("allow"), vec![
        cx.meta_word(DUMMY_SP, token::InternedString::new("non_snake_case")),
        cx.meta_word(DUMMY_SP, token::InternedString::new("unused")),
    ]));

    MacItems::new(Some(cx.item_mod(sp, sp, ident, vec![allow], ast.items.clone())).into_iter())
}

fn parse_arg(cx: &mut ExtCtxt, tts: &[ast::TokenTree]) -> Option<String> {
    use syntax::print::pprust;

    let mut parser = parse::new_parser_from_tts(cx.parse_sess(), cx.cfg(),
                                                tts.to_vec());
    // The `expand_expr` method is called so that any macro calls in the
    // parsed expression are expanded.
    let arg = cx.expander().fold_expr(parser.parse_expr());
    match arg.node {
        ast::ExprLit(ref spanned) => {
            match spanned.node {
                ast::LitStr(ref n, _) => {
                    if !parser.eat(&token::Eof) {
                        cx.span_err(parser.span,
                                    "expected only one string literal");
                        return None
                    }
                    return Some(n.get().to_string())
                }
                _ => {}
            }
        }
        _ => {}
    }

    let err = format!("expected string literal but got `{}`",
                      pprust::expr_to_string(&*arg));
    cx.span_err(parser.span, err.as_slice());
    None
}
<commit_msg>Use deref instead of get on syntax::parse::token::InternedString<commit_after>#![feature(plugin_registrar, quote, box_syntax, core, collections, rustc_private, io, unicode, path)]

extern crate rustc;
extern crate syntax;

use syntax::ast;
use syntax::codemap;
use syntax::ext::base::{ExtCtxt, MacResult, MacItems, DummyResult};
use syntax::parse;
use syntax::parse::token;
use syntax::fold::Folder;
use rustc::plugin::Registry;
use std::old_io::fs::File;
use std::str;

use rustast::{AstBuilder, DUMMY_SP};

mod translate;
mod grammar;
mod rustast;

#[plugin_registrar]
pub fn plugin_registrar(reg: &mut Registry) {
    reg.register_syntax_extension(
            token::intern("peg"),
            syntax::ext::base::IdentTT(box expand_peg_str, None));

    reg.register_syntax_extension(
            token::intern("peg_file"),
            syntax::ext::base::IdentTT(box expand_peg_file, None));
}

fn expand_peg_str<'s>(cx: &'s mut ExtCtxt, sp: codemap::Span, ident: ast::Ident, tts: Vec<ast::TokenTree>) -> Box<MacResult + 's> {
    let source = match parse_arg(cx, tts.as_slice()) {
        Some(source) => source,
        None => return DummyResult::any(sp),
    };

    expand_peg(cx, sp, ident, source.as_slice())
}

fn expand_peg_file<'s>(cx: &'s mut ExtCtxt, sp: codemap::Span, ident: ast::Ident, tts: Vec<ast::TokenTree>) -> Box<MacResult + 's> {
    let fname = match parse_arg(cx, tts.as_slice()) {
        Some(fname) => fname,
        None => return DummyResult::any(sp),
    };

    let path = Path::new(cx.codemap().span_to_filename(sp)).dir_path().join(fname);

    let source_utf8 = match File::open(&path).read_to_end() {
      Ok(source_utf8) => source_utf8,
      Err(e) => {
        cx.span_err(sp, e.to_string().as_slice());
        return DummyResult::any(sp)
      }
    };

    let source = str::from_utf8(source_utf8.as_slice()).unwrap();

    cx.codemap().new_filemap(path.as_str().unwrap().to_string(), "".to_string());

    expand_peg(cx, sp, ident, source.as_slice())
}

fn expand_peg(cx: &mut ExtCtxt, sp: codemap::Span, ident: ast::Ident, source: &str) -> Box<MacResult + 'static> {
    let grammar_def = grammar::grammar(source);

    let grammar_def = match grammar_def {
      Ok(grammar_def) => grammar_def,
      Err(msg) => {
        cx.span_err(sp, msg.as_slice());
        return DummyResult::any(sp)
      }
    };

    let ast = translate::compile_grammar(cx, &grammar_def);

    // #![allow(non_snake_case, unused)]
    let allow = cx.attribute(DUMMY_SP, cx.meta_list(DUMMY_SP, token::InternedString::new("allow"), vec![
        cx.meta_word(DUMMY_SP, token::InternedString::new("non_snake_case")),
        cx.meta_word(DUMMY_SP, token::InternedString::new("unused")),
    ]));

    MacItems::new(Some(cx.item_mod(sp, sp, ident, vec![allow], ast.items.clone())).into_iter())
}

fn parse_arg(cx: &mut ExtCtxt, tts: &[ast::TokenTree]) -> Option<String> {
    use syntax::print::pprust;

    let mut parser = parse::new_parser_from_tts(cx.parse_sess(), cx.cfg(),
                                                tts.to_vec());
    // The `expand_expr` method is called so that any macro calls in the
    // parsed expression are expanded.
    let arg = cx.expander().fold_expr(parser.parse_expr());
    match arg.node {
        ast::ExprLit(ref spanned) => {
            match spanned.node {
                ast::LitStr(ref n, _) => {
                    if !parser.eat(&token::Eof) {
                        cx.span_err(parser.span,
                                    "expected only one string literal");
                        return None
                    }
                    return Some(n.to_string())
                }
                _ => {}
            }
        }
        _ => {}
    }

    let err = format!("expected string literal but got `{}`",
                      pprust::expr_to_string(&*arg));
    cx.span_err(parser.span, err.as_slice());
    None
}
<|endoftext|>


<commit_before>use std::collections::{HashMap, HashSet};
use std::fs::{self, File};
use std::io::prelude::*;
use std::io;
use std::path::{Path, PathBuf};

use core::{Package, Manifest, SourceId, PackageId};
use util::{self, CargoResult, human, Config, ChainError};
use util::important_paths::find_project_manifest_exact;
use util::toml::{Layout, project_layout};

pub fn read_manifest(contents: &[u8], layout: Layout, source_id: &SourceId,
                     config: &Config)
                     -> CargoResult<(Manifest, Vec<PathBuf>)> {
    let root = layout.root.clone();
    util::toml::to_manifest(contents, source_id, layout, config).chain_error(|| {
        human(format!("failed to parse manifest at `{}`",
                      root.join("Cargo.toml").display()))
    })
}

pub fn read_package(path: &Path, source_id: &SourceId, config: &Config)
                    -> CargoResult<(Package, Vec<PathBuf>)> {
    trace!("read_package; path={}; source-id={}", path.display(), source_id);
    let mut file = try!(File::open(path));
    let mut data = Vec::new();
    try!(file.read_to_end(&mut data));

    let layout = project_layout(path.parent().unwrap());
    let (manifest, nested) =
        try!(read_manifest(&data, layout, source_id, config));

    Ok((Package::new(manifest, path), nested))
}

pub fn read_packages(path: &Path, source_id: &SourceId, config: &Config)
                     -> CargoResult<Vec<Package>> {
    let mut all_packages = HashMap::new();
    let mut visited = HashSet::<PathBuf>::new();

    trace!("looking for root package: {}, source_id={}", path.display(), source_id);

    try!(walk(path, &mut |dir| {
        trace!("looking for child package: {}", dir.display());

        // Don't recurse into git databases
        if dir.file_name().and_then(|s| s.to_str()) == Some(".git") {
            return Ok(false)
        }

        // Don't automatically discover packages across git submodules
        if dir != path && fs::metadata(&dir.join(".git")).is_ok() {
            return Ok(false)
        }

        // Don't ever look at target directories
        if dir.file_name().and_then(|s| s.to_str()) == Some("target") &&
           has_manifest(dir.parent().unwrap()) {
            return Ok(false)
        }

        if has_manifest(dir) {
            try!(read_nested_packages(dir, &mut all_packages, source_id, config,
                                      &mut visited));
        }
        Ok(true)
    }));

    if all_packages.is_empty() {
        Err(human(format!("Could not find Cargo.toml in `{}`", path.display())))
    } else {
        Ok(all_packages.into_iter().map(|(_, v)| v).collect())
    }
}

fn walk(path: &Path, callback: &mut FnMut(&Path) -> CargoResult<bool>)
        -> CargoResult<()> {
    if !try!(callback(path)) {
        trace!("not processing {}", path.display());
        return Ok(())
    }

    // Ignore any permission denied errors because temporary directories
    // can often have some weird permissions on them.
    let dirs = match fs::read_dir(path) {
        Ok(dirs) => dirs,
        Err(ref e) if e.kind() == io::ErrorKind::PermissionDenied => {
            return Ok(())
        }
        Err(e) => return Err(From::from(e)),
    };
    for dir in dirs {
        let dir = try!(dir);
        if try!(dir.file_type()).is_dir() {
            try!(walk(&dir.path(), callback));
        }
    }
    Ok(())
}

fn has_manifest(path: &Path) -> bool {
    find_project_manifest_exact(path, "Cargo.toml").is_ok()
}

fn read_nested_packages(path: &Path,
                        all_packages: &mut HashMap<PackageId, Package>,
                        source_id: &SourceId,
                        config: &Config,
                        visited: &mut HashSet<PathBuf>) -> CargoResult<()> {
    if !visited.insert(path.to_path_buf()) { return Ok(()) }

    let manifest = try!(find_project_manifest_exact(path, "Cargo.toml"));

    let (pkg, nested) = try!(read_package(&manifest, source_id, config));
    all_packages.insert(pkg.package_id().clone(), pkg);

    // Registry sources are not allowed to have `path=` dependencies because
    // they're all translated to actual registry dependencies.
    //
    // We normalize the path here ensure that we don't infinitely walk around
    // looking for crates. By normalizing we ensure that we visit this crate at
    // most once.
    //
    // TODO: filesystem/symlink implications?
    if !source_id.is_registry() {
        for p in nested.iter() {
            let path = util::normalize_path(&path.join(p));
            try!(read_nested_packages(&path, all_packages, source_id,
                                      config, visited));
        }
    }

    Ok(())
}
<commit_msg>Disallow nesting of packages with same packge_id<commit_after>use std::collections::{HashMap, HashSet};
use std::fs::{self, File};
use std::io::prelude::*;
use std::io;
use std::path::{Path, PathBuf};

use core::{Package, Manifest, SourceId, PackageId};
use util::{self, CargoResult, human, Config, ChainError};
use util::important_paths::find_project_manifest_exact;
use util::toml::{Layout, project_layout};

pub fn read_manifest(contents: &[u8], layout: Layout, source_id: &SourceId,
                     config: &Config)
                     -> CargoResult<(Manifest, Vec<PathBuf>)> {
    let root = layout.root.clone();
    util::toml::to_manifest(contents, source_id, layout, config).chain_error(|| {
        human(format!("failed to parse manifest at `{}`",
                      root.join("Cargo.toml").display()))
    })
}

pub fn read_package(path: &Path, source_id: &SourceId, config: &Config)
                    -> CargoResult<(Package, Vec<PathBuf>)> {
    trace!("read_package; path={}; source-id={}", path.display(), source_id);
    let mut file = try!(File::open(path));
    let mut data = Vec::new();
    try!(file.read_to_end(&mut data));

    let layout = project_layout(path.parent().unwrap());
    let (manifest, nested) =
        try!(read_manifest(&data, layout, source_id, config));

    Ok((Package::new(manifest, path), nested))
}

pub fn read_packages(path: &Path, source_id: &SourceId, config: &Config)
                     -> CargoResult<Vec<Package>> {
    let mut all_packages = HashMap::new();
    let mut visited = HashSet::<PathBuf>::new();

    trace!("looking for root package: {}, source_id={}", path.display(), source_id);

    try!(walk(path, &mut |dir| {
        trace!("looking for child package: {}", dir.display());

        // Don't recurse into git databases
        if dir.file_name().and_then(|s| s.to_str()) == Some(".git") {
            return Ok(false)
        }

        // Don't automatically discover packages across git submodules
        if dir != path && fs::metadata(&dir.join(".git")).is_ok() {
            return Ok(false)
        }

        // Don't ever look at target directories
        if dir.file_name().and_then(|s| s.to_str()) == Some("target") &&
           has_manifest(dir.parent().unwrap()) {
            return Ok(false)
        }

        if has_manifest(dir) {
            try!(read_nested_packages(dir, &mut all_packages, source_id, config,
                                      &mut visited));
        }
        Ok(true)
    }));

    if all_packages.is_empty() {
        Err(human(format!("Could not find Cargo.toml in `{}`", path.display())))
    } else {
        Ok(all_packages.into_iter().map(|(_, v)| v).collect())
    }
}

fn walk(path: &Path, callback: &mut FnMut(&Path) -> CargoResult<bool>)
        -> CargoResult<()> {
    if !try!(callback(path)) {
        trace!("not processing {}", path.display());
        return Ok(())
    }

    // Ignore any permission denied errors because temporary directories
    // can often have some weird permissions on them.
    let dirs = match fs::read_dir(path) {
        Ok(dirs) => dirs,
        Err(ref e) if e.kind() == io::ErrorKind::PermissionDenied => {
            return Ok(())
        }
        Err(e) => return Err(From::from(e)),
    };
    for dir in dirs {
        let dir = try!(dir);
        if try!(dir.file_type()).is_dir() {
            try!(walk(&dir.path(), callback));
        }
    }
    Ok(())
}

fn has_manifest(path: &Path) -> bool {
    find_project_manifest_exact(path, "Cargo.toml").is_ok()
}

fn read_nested_packages(path: &Path,
                        all_packages: &mut HashMap<PackageId, Package>,
                        source_id: &SourceId,
                        config: &Config,
                        visited: &mut HashSet<PathBuf>) -> CargoResult<()> {
    if !visited.insert(path.to_path_buf()) { return Ok(()) }

    let manifest = try!(find_project_manifest_exact(path, "Cargo.toml"));

    let (pkg, nested) = try!(read_package(&manifest, source_id, config));
    let pkg_id = pkg.package_id().clone();
    if !all_packages.contains_key(&pkg_id) {
        all_packages.insert(pkg_id, pkg);
    } else {
        warn!("skipping nested package `{}` found at `{}`",
              &pkg.name(), &path.to_string_lossy());
    }

    // Registry sources are not allowed to have `path=` dependencies because
    // they're all translated to actual registry dependencies.
    //
    // We normalize the path here ensure that we don't infinitely walk around
    // looking for crates. By normalizing we ensure that we visit this crate at
    // most once.
    //
    // TODO: filesystem/symlink implications?
    if !source_id.is_registry() {
        for p in nested.iter() {
            let path = util::normalize_path(&path.join(p));
            try!(read_nested_packages(&path, all_packages, source_id,
                                      config, visited));
        }
    }

    Ok(())
}
<|endoftext|>


<commit_before>use std::{io, path, mem, fs};
use std::io::Read;
use std::collections::BTreeMap;
use std::time::Duration;
use libc::{c_ulong, c_ushort, c_uint, c_long, c_schar};
use data::*;
use super::common::*;
use super::unix;

fn read_file(path: &str) -> io::Result<String> {
    let mut s = String::new();
    fs::File::open(path)
        .and_then(|mut f| f.read_to_string(&mut s))
        .map(|_| s)
}

fn value_from_file(path: &str) -> io::Result<i32> {
    try!(read_file(path))
        .trim_right_matches("\n")
        .parse()
        .and_then(|n| Ok(n))
        .or_else(|_| {
            Err(io::Error::new(io::ErrorKind::Other,
                               format!("File: \"{}\" doesn't contain an int value", &path)))
        })
}

fn capacity(charge_full: i32, charge_now: i32) -> f32 {
    charge_now as f32 / charge_full as f32
}

fn time(charge_full: i32, charge_now: i32, current_now: i32) -> Duration {
    if current_now != 0 {
        Duration::from_secs((charge_full - charge_now).abs() as u64 * 3600u64 / current_now as u64)
    } else {
        Duration::new(0, 0)
    }
}

pub struct PlatformImpl;

/// An implementation of `Platform` for Linux.
/// See `Platform` for documentation.
impl Platform for PlatformImpl {
    #[inline(always)]
    fn new() -> Self {
        PlatformImpl
    }

    fn cpu_load(&self) -> io::Result<DelayedMeasurement<Vec<CPULoad>>> {
        Err(io::Error::new(io::ErrorKind::Other, "Not supported"))
    }

    fn load_average(&self) -> io::Result<LoadAverage> {
        unix::load_average()
    }

    fn memory(&self) -> io::Result<Memory> {
        let mut info: sysinfo = unsafe { mem::zeroed() };
        unsafe { sysinfo(&mut info) };
        let unit = info.mem_unit as usize;
        let pmem = PlatformMemory {
            total: ByteSize::b(info.totalram as usize * unit),
            free: ByteSize::b(info.freeram as usize * unit),
            shared: ByteSize::b(info.sharedram as usize * unit),
            buffer: ByteSize::b(info.bufferram as usize * unit),
        };
        Ok(Memory {
            total: pmem.total,
            free: pmem.free,
            platform_memory: pmem,
        })
    }

    fn battery_life(&self) -> io::Result<BatteryLife> {
        let dir = "/sys/class/power_supply";
        let entries = try!(fs::read_dir(&dir));
        let mut full = 0;
        let mut now = 0;
        let mut current = 0;
        for e in entries {
            let p = e.unwrap().path();
            let s = p.to_str().unwrap();
            let f = p.file_name().unwrap().to_str().unwrap();
            if f.len() > 3 {
                if f.split_at(3).0 == "BAT" {
                    full += try!(value_from_file(&(s.to_string() + "/energy_full"))
                                 .or_else(|_| value_from_file(&(s.to_string() + "/charge_full"))));
                    now += try!(value_from_file(&(s.to_string() + "/energy_now"))
                                .or_else(|_| value_from_file(&(s.to_string() + "/charge_now"))));
                    current += try!(value_from_file(&(s.to_string() + "/energy_now"))
                                    .or_else(|_| value_from_file(&(s.to_string() + "/current_now"))));
                }
            }
        }
        if full != 0 {
            Ok(BatteryLife {
                remaining_capacity: capacity(full, now),
                remaining_time: time(full, now, current),
            })
        } else {
            Err(io::Error::new(io::ErrorKind::Other, "Missing battery information"))
        }
    }

    fn on_ac_power(&self) -> io::Result<bool> {
        value_from_file("/sys/class/power_supply/AC/online").map(|v| v == 1)
    }

    fn mounts(&self) -> io::Result<Vec<Filesystem>> {
        Err(io::Error::new(io::ErrorKind::Other, "Not supported"))
    }

    fn mount_at<P: AsRef<path::Path>>(&self, path: P) -> io::Result<Filesystem> {
        Err(io::Error::new(io::ErrorKind::Other, "Not supported"))
    }

    fn networks(&self) -> io::Result<BTreeMap<String, Network>> {
        unix::networks()
    }
}

#[repr(C)]
#[derive(Debug)]
struct sysinfo {
    uptime: c_long,
    loads: [c_ulong; 3],
    totalram: c_ulong,
    freeram: c_ulong,
    sharedram: c_ulong,
    bufferram: c_ulong,
    totalswap: c_ulong,
    freeswap: c_ulong,
    procs: c_ushort,
    totalhigh: c_ulong,
    freehigh: c_ulong,
    mem_unit: c_uint,
    padding: [c_schar; 8],
}

#[link(name = "c")]
extern "C" {
    fn sysinfo(info: *mut sysinfo);
}
<commit_msg>Fix (dis)charge time on linux<commit_after>use std::{io, path, mem, fs};
use std::io::Read;
use std::collections::BTreeMap;
use std::time::Duration;
use libc::{c_ulong, c_ushort, c_uint, c_long, c_schar};
use data::*;
use super::common::*;
use super::unix;

fn read_file(path: &str) -> io::Result<String> {
    let mut s = String::new();
    fs::File::open(path)
        .and_then(|mut f| f.read_to_string(&mut s))
        .map(|_| s)
}

fn value_from_file(path: &str) -> io::Result<i32> {
    try!(read_file(path))
        .trim_right_matches("\n")
        .parse()
        .and_then(|n| Ok(n))
        .or_else(|_| {
            Err(io::Error::new(io::ErrorKind::Other,
                               format!("File: \"{}\" doesn't contain an int value", &path)))
        })
}

fn capacity(charge_full: i32, charge_now: i32) -> f32 {
    charge_now as f32 / charge_full as f32
}

fn time(on_ac: bool, charge_full: i32, charge_now: i32, current_now: i32) -> Duration {
    if current_now != 0 {
        if on_ac {
            // Charge time
            Duration::from_secs((charge_full - charge_now).abs() as u64 * 3600u64 / current_now as u64)
        }
        else {
            // Discharge time
            Duration::from_secs(charge_now as u64 * 3600u64 / current_now as u64)
        }
    } else {
        Duration::new(0, 0)
    }
}

pub struct PlatformImpl;

/// An implementation of `Platform` for Linux.
/// See `Platform` for documentation.
impl Platform for PlatformImpl {
    #[inline(always)]
    fn new() -> Self {
        PlatformImpl
    }

    fn cpu_load(&self) -> io::Result<DelayedMeasurement<Vec<CPULoad>>> {
        Err(io::Error::new(io::ErrorKind::Other, "Not supported"))
    }

    fn load_average(&self) -> io::Result<LoadAverage> {
        unix::load_average()
    }

    fn memory(&self) -> io::Result<Memory> {
        let mut info: sysinfo = unsafe { mem::zeroed() };
        unsafe { sysinfo(&mut info) };
        let unit = info.mem_unit as usize;
        let pmem = PlatformMemory {
            total: ByteSize::b(info.totalram as usize * unit),
            free: ByteSize::b(info.freeram as usize * unit),
            shared: ByteSize::b(info.sharedram as usize * unit),
            buffer: ByteSize::b(info.bufferram as usize * unit),
        };
        Ok(Memory {
            total: pmem.total,
            free: pmem.free,
            platform_memory: pmem,
        })
    }

    fn battery_life(&self) -> io::Result<BatteryLife> {
        let dir = "/sys/class/power_supply";
        let entries = try!(fs::read_dir(&dir));
        let mut full = 0;
        let mut now = 0;
        let mut current = 0;
        for e in entries {
            let p = e.unwrap().path();
            let s = p.to_str().unwrap();
            let f = p.file_name().unwrap().to_str().unwrap();
            if f.len() > 3 {
                if f.split_at(3).0 == "BAT" {
                    full += try!(value_from_file(&(s.to_string() + "/energy_full"))
                                 .or_else(|_| value_from_file(&(s.to_string() + "/charge_full"))));
                    now += try!(value_from_file(&(s.to_string() + "/energy_now"))
                                .or_else(|_| value_from_file(&(s.to_string() + "/charge_now"))));
                    current += try!(value_from_file(&(s.to_string() + "/power_now"))
                                    .or_else(|_| value_from_file(&(s.to_string() + "/current_now"))));
                }
            }
        }
        if full != 0 {
            let on_ac =
                match self.on_ac_power() {
                    Ok(true) => true,
                    _ => false,
                };
            Ok(BatteryLife {
                remaining_capacity: capacity(full, now),
                remaining_time: time(on_ac, full, now, current),
            })
        } else {
            Err(io::Error::new(io::ErrorKind::Other, "Missing battery information"))
        }
    }

    fn on_ac_power(&self) -> io::Result<bool> {
        value_from_file("/sys/class/power_supply/AC/online").map(|v| v == 1)
    }

    fn mounts(&self) -> io::Result<Vec<Filesystem>> {
        Err(io::Error::new(io::ErrorKind::Other, "Not supported"))
    }

    fn mount_at<P: AsRef<path::Path>>(&self, path: P) -> io::Result<Filesystem> {
        Err(io::Error::new(io::ErrorKind::Other, "Not supported"))
    }

    fn networks(&self) -> io::Result<BTreeMap<String, Network>> {
        unix::networks()
    }
}

#[repr(C)]
#[derive(Debug)]
struct sysinfo {
    uptime: c_long,
    loads: [c_ulong; 3],
    totalram: c_ulong,
    freeram: c_ulong,
    sharedram: c_ulong,
    bufferram: c_ulong,
    totalswap: c_ulong,
    freeswap: c_ulong,
    procs: c_ushort,
    totalhigh: c_ulong,
    freehigh: c_ulong,
    mem_unit: c_uint,
    padding: [c_schar; 8],
}

#[link(name = "c")]
extern "C" {
    fn sysinfo(info: *mut sysinfo);
}
<|endoftext|>


<commit_before>use std::io::{self, Write};
use std::fs::File;
use std::sync::mpsc::{sync_channel, SyncSender, Receiver};

use time::{get_time, now_utc};
use irc::parse::IrcMsg;

use command_mapper::{Replier, RustBotPlugin};

fn logger_loop(rx: Receiver<IrcMsg>) -> Result<(), io::Error> {
    let logfile = format!("logs/{}.irclog", now_utc().rfc3339());
    let mut log = try!(File::create(&logfile));
    for msg in rx.iter() {
        let timespec = get_time();
        let timefmt = format!("{}:{} ", timespec.sec, timespec.nsec);
        try!(log.write_all(timefmt.as_bytes()));
        try!(log.write_all(msg.as_bytes()));
        try!(log.write_all(b"\r\n"));
    }
    Ok(())
}

pub struct LoggerPlugin {
    sender: Option<SyncSender<IrcMsg>>
}

impl LoggerPlugin {
    pub fn new() -> LoggerPlugin {
        LoggerPlugin { sender: None }
    }

    pub fn get_plugin_name() -> &'static str {
        "logger"
    }
}

impl RustBotPlugin for LoggerPlugin {
    fn start(&mut self) {
        let (tx, rx) = sync_channel(10);
        let _ = ::std::thread::Builder::new().name("plugin-logger".to_string()).spawn(move || {
            if let Err(err) = logger_loop(rx) {
                info!("Error in logger loop: {:?}", err);
            }
        });
        self.sender = Some(tx);
    }

    fn on_message(&mut self, _: &mut Replier, msg: &IrcMsg) {
        let mut disable_self = false;
        if let Some(ref sender) = self.sender {
            if let Err(err) = sender.send(msg.clone()) {
                info!("Logger service gone: {:?}", err);
                disable_self = true;
            }
        }
        if disable_self {
            self.sender = None;
        }
    }
}
<commit_msg>Change logging timestamp format<commit_after>use std::io::{self, Write};
use std::fs::File;
use std::sync::mpsc::{sync_channel, SyncSender, Receiver};

use time::{get_time, now_utc};
use irc::parse::IrcMsg;

use command_mapper::{Replier, RustBotPlugin};

fn logger_loop(rx: Receiver<IrcMsg>) -> Result<(), io::Error> {
    let logfile = format!("logs/{}.irclog", now_utc().rfc3339());
    let mut log = try!(File::create(&logfile));
    for msg in rx.iter() {
        let timespec = get_time();
        let mut ms_time = 0;
        ms_time += 1000 * timespec.sec as u64;
        ms_time += timespec.nsec as u64 / 1_000_000;
        let timefmt = format!("{} ", ms_time);
        try!(log.write_all(timefmt.as_bytes()));
        try!(log.write_all(msg.as_bytes()));
        try!(log.write_all(b"\r\n"));
    }
    Ok(())
}

pub struct LoggerPlugin {
    sender: Option<SyncSender<IrcMsg>>
}

impl LoggerPlugin {
    pub fn new() -> LoggerPlugin {
        LoggerPlugin { sender: None }
    }

    pub fn get_plugin_name() -> &'static str {
        "logger"
    }
}

impl RustBotPlugin for LoggerPlugin {
    fn start(&mut self) {
        let (tx, rx) = sync_channel(10);
        let _ = ::std::thread::Builder::new().name("plugin-logger".to_string()).spawn(move || {
            if let Err(err) = logger_loop(rx) {
                info!("Error in logger loop: {:?}", err);
            }
        });
        self.sender = Some(tx);
    }

    fn on_message(&mut self, _: &mut Replier, msg: &IrcMsg) {
        let mut disable_self = false;
        if let Some(ref sender) = self.sender {
            if let Err(err) = sender.send(msg.clone()) {
                info!("Logger service gone: {:?}", err);
                disable_self = true;
            }
        }
        if disable_self {
            self.sender = None;
        }
    }
}
<|endoftext|>


<commit_before>use crate::errors::ServerReceiveError;
use crate::AuthObj;
use oauth2::{AuthorizationCode, CsrfToken};
use std::io::{BufRead, BufReader, Write};
use std::net::TcpListener;
use url::Url;

pub fn naive_server(auth_obj: &AuthObj, port: u32) -> Result<AuthorizationCode, ServerReceiveError> {
    // A very naive implementation of the redirect server.
    // A ripoff of https://github.com/ramosbugs/oauth2-rs/blob/master/examples/msgraph.rs, stripped
    // down for simplicity.
    let listener = TcpListener::bind(format!("127.0.0.1:{}", port)).unwrap();
    for stream in listener.incoming() {
        if let Ok(mut stream) = stream {
            {
                let mut reader = BufReader::new(&stream);

                let mut request_line = String::new();
                reader.read_line(&mut request_line).unwrap();

                let redirect_url = match request_line.split_whitespace().nth(1) {
                    Some(redirect_url) => redirect_url,
                    None => return Err(ServerReceiveError::UnexpectedRedirectUrl { url: request_line }),
                };
                let url = Url::parse(&("http://localhost".to_string() + redirect_url)).unwrap();

                println!("url == {}", url);

                let code = match url.query_pairs().find(|pair| {
                    let &(ref key, _) = pair;
                    key == "code"
                }) {
                    Some(qp) => AuthorizationCode::new(qp.1.into_owned()),
                    None => {
                        return Err(ServerReceiveError::QueryPairNotFound {
                            query_pair: "code".to_owned(),
                        })
                    }
                };

                let state = match url.query_pairs().find(|pair| {
                    let &(ref key, _) = pair;
                    key == "state"
                }) {
                    Some(qp) => CsrfToken::new(qp.1.into_owned()),
                    None => {
                        return Err(ServerReceiveError::QueryPairNotFound {
                            query_pair: "state".to_owned(),
                        })
                    }
                };

                if state.secret() != auth_obj.csrf_state.secret() {
                    return Err(ServerReceiveError::StateSecretMismatch {
                        expected_state_secret: auth_obj.csrf_state.secret().to_owned(),
                        received_state_secret: state.secret().to_owned(),
                    });
                }

                let message = "Authentication complete. You can close this window now.";
                let response = format!("HTTP/1.1 200 OK\r\ncontent-length: {}\r\n\r\n{}", message.len(), message);
                stream.write_all(response.as_bytes()).unwrap();

                // The server will terminate itself after collecting the first code.
                return Ok(code);
            }
        }
    }

    unreachable!()
}
<commit_msg>Don't print from library code by default<commit_after>use crate::errors::ServerReceiveError;
use crate::AuthObj;
use log::debug;
use oauth2::{AuthorizationCode, CsrfToken};
use std::io::{BufRead, BufReader, Write};
use std::net::TcpListener;
use url::Url;

pub fn naive_server(auth_obj: &AuthObj, port: u32) -> Result<AuthorizationCode, ServerReceiveError> {
    // A very naive implementation of the redirect server.
    // A ripoff of https://github.com/ramosbugs/oauth2-rs/blob/master/examples/msgraph.rs, stripped
    // down for simplicity.
    let listener = TcpListener::bind(format!("127.0.0.1:{}", port)).unwrap();
    for stream in listener.incoming() {
        if let Ok(mut stream) = stream {
            {
                let mut reader = BufReader::new(&stream);

                let mut request_line = String::new();
                reader.read_line(&mut request_line).unwrap();

                let redirect_url = match request_line.split_whitespace().nth(1) {
                    Some(redirect_url) => redirect_url,
                    None => return Err(ServerReceiveError::UnexpectedRedirectUrl { url: request_line }),
                };
                let url = Url::parse(&("http://localhost".to_string() + redirect_url)).unwrap();

                debug!("url == {}", url);

                let code = match url.query_pairs().find(|pair| {
                    let &(ref key, _) = pair;
                    key == "code"
                }) {
                    Some(qp) => AuthorizationCode::new(qp.1.into_owned()),
                    None => {
                        return Err(ServerReceiveError::QueryPairNotFound {
                            query_pair: "code".to_owned(),
                        })
                    }
                };

                let state = match url.query_pairs().find(|pair| {
                    let &(ref key, _) = pair;
                    key == "state"
                }) {
                    Some(qp) => CsrfToken::new(qp.1.into_owned()),
                    None => {
                        return Err(ServerReceiveError::QueryPairNotFound {
                            query_pair: "state".to_owned(),
                        })
                    }
                };

                if state.secret() != auth_obj.csrf_state.secret() {
                    return Err(ServerReceiveError::StateSecretMismatch {
                        expected_state_secret: auth_obj.csrf_state.secret().to_owned(),
                        received_state_secret: state.secret().to_owned(),
                    });
                }

                let message = "Authentication complete. You can close this window now.";
                let response = format!("HTTP/1.1 200 OK\r\ncontent-length: {}\r\n\r\n{}", message.len(), message);
                stream.write_all(response.as_bytes()).unwrap();

                // The server will terminate itself after collecting the first code.
                return Ok(code);
            }
        }
    }

    unreachable!()
}
<|endoftext|>


<commit_before>#![crate_type = "bin"]

extern crate cairo;
extern crate gdk;
extern crate gdk_pixbuf;
extern crate gio;
extern crate glib;
extern crate gtk;
extern crate libc;
extern crate pango;
extern crate sysinfo;

use sysinfo::*;

use gdk_pixbuf::Pixbuf;
use gio::{ActionExt, ActionMapExt, ApplicationExt, MenuExt, SimpleActionExt};
use glib::{IsA, ToVariant};
use gtk::{AboutDialog, Button, Dialog, EditableSignals, Entry, Inhibit, MessageDialog};
use gtk::{
    AboutDialogExt, BoxExt, ButtonExt, ContainerExt, DialogExt, EntryExt, GtkApplicationExt,
    ListStoreExt, ListStoreExtManual, ToggleButtonExt, TreeModelExt, TreeSortableExtManual,
    TreeViewExt, WidgetExt, WindowExt,
};

use std::cell::RefCell;
use std::collections::{HashMap, HashSet};
use std::process::{Command, Stdio};
use std::os::unix::process::CommandExt;
use std::rc::Rc;

use display_sysinfo::DisplaySysInfo;
use notebook::NoteBook;
use procs::{create_and_fill_model, Procs};

mod color;
mod display_sysinfo;
mod graph;
mod notebook;
mod process_dialog;
mod procs;
mod utils;

fn update_window(list: &gtk::ListStore, system: &Rc<RefCell<sysinfo::System>>,
                 info: &mut DisplaySysInfo, display_fahrenheit: bool) {
    let mut system = system.borrow_mut();
    system.refresh_all();
    info.update_ram_display(&system, display_fahrenheit);
    info.update_process_display(&system);
    let entries: &HashMap<i32, Process> = system.get_process_list();
    let mut seen: HashSet<i32> = HashSet::new();

    if let Some(mut iter) = list.get_iter_first() {
        let mut valid = true;
        while valid {
            if let Some(pid) = list.get_value(&iter, 0).get::<i32>() {
                if let Some(p) = entries.get(&(pid)) {
                    list.set(&iter,
                             &[2, 3, 5],
                             &[&format!("{:.1}", p.cpu_usage), &p.memory, &p.cpu_usage]);
                    valid = list.iter_next(&mut iter);
                    seen.insert(pid);
                } else {
                    valid = list.remove(&mut iter);
                }
            }
        }
    }

    for (pid, pro) in entries.iter() {
        if !seen.contains(pid) {
            create_and_fill_model(list, pro.pid, &format!("{:?}", &pro.cmd), &pro.name,
                                  pro.cpu_usage, pro.memory);
        }
    }
}

fn parse_quote(line: &str, quote: char) -> Vec<String> {
    let args = line.split(quote).collect::<Vec<&str>>();
    let mut out_args = vec!();

    for (num, arg) in args.iter().enumerate() {
        if num != 1 {
            out_args.extend_from_slice(&parse_entry(arg));
        } else {
            out_args.push((*arg).to_owned());
        }
    }
    out_args
}

// super simple parsing
fn parse_entry(line: &str) -> Vec<String> {
    match (line.find('\''), line.find('"')) {
        (Some(x), Some(y)) => {
            if x < y {
                parse_quote(line, '\'')
            } else {
                parse_quote(line, '"')
            }
        }
        (Some(_), None) => parse_quote(line, '\''),
        (None, Some(_)) => parse_quote(line, '"'),
        (None, None) => line.split(' ').into_iter()
                                       .map(|s| s.to_owned())
                                       .collect::<Vec<String>>(),
    }
}

fn start_detached_process(line: &str) -> Option<String> {
    let args = parse_entry(line);
    let command = args[0].clone();

    if let Err(_) = Command::new(&command)
                            .args(&args)
                            .before_exec(|| {
                                unsafe { libc::setsid() };
                                Ok(())
                            })
                            .stdin(Stdio::null())
                            .stderr(Stdio::null())
                            .stdout(Stdio::null())
                            .spawn() {
        Some(format!("Failed to start '{}'", &command))
    } else {
        None
    }
}

fn run_command<T: IsA<gtk::Window>>(input: &Entry, window: &T, d: &Dialog) {
    if let Some(text) = input.get_text() {
        let x = if let Some(x) = start_detached_process(&text) {
            x
        } else {
            "The command started successfully".to_owned()
        };
        d.destroy();
        let m = MessageDialog::new(Some(window),
                                   gtk::DIALOG_DESTROY_WITH_PARENT,
                                   gtk::MessageType::Info,
                                   gtk::ButtonsType::Ok,
                                   &x);
        m.run();
        m.destroy();
    }
}

fn build_ui(application: &gtk::Application) {
    let menu = gio::Menu::new();
    let menu_bar = gio::Menu::new();
    let more_menu = gio::Menu::new();
    let settings_menu = gio::Menu::new();

    menu.append("Launch new executable", "app.new-task");
    menu.append("Quit", "app.quit");

    settings_menu.append("Display temperature in °F", "app.temperature");
    settings_menu.append("Display graphs", "app.graphs");
    menu_bar.append_submenu("_Setting", &settings_menu);

    more_menu.append("About", "app.about");
    menu_bar.append_submenu("?", &more_menu);

    application.set_app_menu(&menu);
    application.set_menubar(&menu_bar);

    let window = gtk::ApplicationWindow::new(application);
    let sys = sysinfo::System::new();
    let start_time = unsafe { if let Some(p) = sys.get_process(libc::getpid()) {
        p.start_time
    } else {
        0
    }};
    let running_since = Rc::new(RefCell::new(0));
    let sys = Rc::new(RefCell::new(sys));
    let mut note = NoteBook::new();
    let procs = Procs::new(sys.borrow().get_process_list(), &mut note);
    let current_pid = procs.current_pid.clone();
    let current_pid2 = procs.current_pid.clone();
    let sys1 = sys.clone();
    let sys2 = sys.clone();
    let sys3 = sys.clone();
    let info_button = procs.info_button.clone();

    window.set_title("Process viewer");
    window.set_position(gtk::WindowPosition::Center);
    // To silence the annying warning:
    // "(.:2257): Gtk-WARNING **: Allocating size to GtkWindow 0x7f8a31038290 without
    // calling gtk_widget_get_preferred_width/height(). How does the code know the size to
    // allocate?"
    window.get_preferred_width();
    window.set_default_size(500, 700);

    window.connect_delete_event(|_, _| {
        gtk::main_quit();
        Inhibit(false)
    });

    sys.borrow_mut().refresh_all();
    procs.kill_button.connect_clicked(move |_| {
        let sys = sys1.borrow();
        if let Some(process) = current_pid.get().and_then(|pid| sys.get_process(pid)) {
            process.kill(Signal::Kill);
        }
    });

    let mut display_tab = DisplaySysInfo::new(sys.clone(), &mut note, &window);

    let v_box = gtk::Box::new(gtk::Orientation::Vertical, 0);

    let ram_check_box = display_tab.ram_check_box.clone();
    let swap_check_box = display_tab.swap_check_box.clone();
    let temperature_check_box = display_tab.temperature_check_box.clone();

    let graphs = gio::SimpleAction::new_stateful("graphs", None, &false.to_variant());
    graphs.connect_activate(move |g, _| {
        let mut is_active = false;
        if let Some(g) = g.get_state() {
            is_active = g.get().expect("couldn't get bool");
            ram_check_box.set_active(!is_active);
            swap_check_box.set_active(!is_active);
            if let Some(ref temperature_check_box) = temperature_check_box {
                temperature_check_box.set_active(!is_active);
            }
        }
        // We need to change the toggle state ourselves. `gio` dark magic.
        g.change_state(&(!is_active).to_variant());
    });
    let temperature = gio::SimpleAction::new_stateful("temperature", None, &false.to_variant());
    temperature.connect_activate(move |g, _| {
        let mut is_active = false;
        if let Some(g) = g.get_state() {
            is_active = g.get().expect("couldn't get graph state");
        }
        // We need to change the toggle state ourselves. `gio` dark magic.
        g.change_state(&(!is_active).to_variant());
    });
    application.add_action(&temperature);

    // I think it's now useless to have this one...
    v_box.pack_start(&note.notebook, true, true, 0);

    window.add(&v_box);
    window.show_all();
    window.activate();

    let list_store = procs.list_store.clone();
    let run1 = running_since.clone();
    let run2 = running_since.clone();
    gtk::timeout_add(1000, move || {
        *run1.borrow_mut() += 1;
        // first part, deactivate sorting
        let sorted = TreeSortableExtManual::get_sort_column_id(&list_store);
        list_store.set_unsorted();

        // we update the tree view
        if let Some(temperature) = temperature.get_state() {
            update_window(&list_store, &sys, &mut display_tab,
                          temperature.get::<bool>().expect("couldn't get temperature state"));
        }

        // we re-enable the sorting
        if let Some((col, order)) = sorted {
            list_store.set_sort_column_id(col, order);
        }
        glib::Continue(true)
    });
    let window1 = window.clone();
    info_button.connect_clicked(move |_| {
        let sys = sys2.borrow();
        if let Some(process) = current_pid2.get().and_then(|pid| sys.get_process(pid)) {
            process_dialog::create_process_dialog(&process, &window1, start_time,
                                                  run2.borrow().clone());
        }
    });
    let window2 = window.clone();
    procs.left_tree.connect_row_activated(move |tree_view, path, _| {
        let model = tree_view.get_model().unwrap();
        let iter = model.get_iter(path).unwrap();
        let sys = sys3.borrow();
        if let Some(process) = sys.get_process(model.get_value(&iter, 0).get().unwrap()) {
            process_dialog::create_process_dialog(&process, &window2, start_time,
                                                  running_since.borrow().clone());
        }
    });

    let window2 = window.clone();
    let quit = gio::SimpleAction::new("quit", None);
    quit.connect_activate(move |_, _| {
        window2.destroy();
    });
    let window3 = window.clone();
    window3.present();
    let about = gio::SimpleAction::new("about", None);
    about.connect_activate(move |_, _| {
        let p = AboutDialog::new();
        p.set_authors(&["Guillaume Gomez"]);
        p.set_website_label(Some("my website"));
        p.set_website(Some("https://guillaume-gomez.fr/"));
        p.set_comments(Some("A process viewer GUI wrote with gtk-rs"));
        p.set_copyright(Some("This is under MIT license"));
        p.set_transient_for(Some(&window3));
        p.set_program_name("process-viewer");
        let logo = Pixbuf::new_from_file("assets/eye.png");
        if let Ok(logo) = logo {
            p.set_logo(Some(&logo));
        }
        p.run();
        p.destroy();
    });
    let new_task = gio::SimpleAction::new("new-task", None);
    new_task.connect_activate(move |_, _| {
        let d = Dialog::new();
        d.set_title("Launch new executable");
        let content_area = d.get_content_area();
        let input = Entry::new();
        let run = Button::new_with_label("Run");
        let cancel = Button::new_with_label("Cancel");
        let v_box = gtk::Box::new(gtk::Orientation::Vertical, 0);
        let h_box = gtk::Box::new(gtk::Orientation::Horizontal, 0);
        h_box.pack_start(&run, true, true, 0);
        h_box.pack_start(&cancel, true, true, 0);
        v_box.pack_start(&input, true, true, 0);
        v_box.pack_start(&h_box, true, true, 0);
        content_area.add(&v_box);
        let window2 = window.clone();
        d.set_transient_for(Some(&window2));
        // To silence the annying warning:
        // "(.:2257): Gtk-WARNING **: Allocating size to GtkWindow 0x7f8a31038290 without
        // calling gtk_widget_get_preferred_width/height(). How does the code know the size to
        // allocate?"
        d.get_preferred_width();
        d.set_size_request(400, 70);
        d.show_all();

        run.set_sensitive(false);
        let run2 = run.clone();
        let input2 = input.clone();
        input.connect_changed(move |_| {
            match input2.get_text() {
                Some(ref x) if x.len() > 0 => run2.set_sensitive(true),
                _ => run2.set_sensitive(false),
            }
        });
        let d2 = d.clone();
        cancel.connect_clicked(move |_| {
            d2.destroy();
        });
        let window3 = window.clone();
        let input3 = input.clone();
        let d3 = d.clone();
        input.connect_activate(move |_| {
            run_command(&input3, &window3, &d3);
        });
        let window4 = window.clone();
        run.connect_clicked(move |_| {
            run_command(&input, &window4, &d);
        });
    });

    application.add_action(&about);
    application.add_action(&graphs);
    application.add_action(&new_task);
    application.add_action(&quit);
    application.connect_activate(move |_| {});
}

fn main() {
    let application = gtk::Application::new("com.github.GuillaumeGomez.process-viewer",
                                            gio::ApplicationFlags::empty())
                                       .expect("Initialization failed...");

    application.connect_startup(move |app| {
        build_ui(app);
    });

    let original = ::std::env::args().collect::<Vec<_>>();
    let mut tmp = Vec::with_capacity(original.len());
    for i in 0..original.len() {
        tmp.push(original[i].as_str());
    }
    application.run(&tmp);
}
<commit_msg>Remove loop quit<commit_after>#![crate_type = "bin"]

extern crate cairo;
extern crate gdk;
extern crate gdk_pixbuf;
extern crate gio;
extern crate glib;
extern crate gtk;
extern crate libc;
extern crate pango;
extern crate sysinfo;

use sysinfo::*;

use gdk_pixbuf::Pixbuf;
use gio::{ActionExt, ActionMapExt, ApplicationExt, MenuExt, SimpleActionExt};
use glib::{IsA, ToVariant};
use gtk::{AboutDialog, Button, Dialog, EditableSignals, Entry, Inhibit, MessageDialog};
use gtk::{
    AboutDialogExt, BoxExt, ButtonExt, ContainerExt, DialogExt, EntryExt, GtkApplicationExt,
    ListStoreExt, ListStoreExtManual, ToggleButtonExt, TreeModelExt, TreeSortableExtManual,
    TreeViewExt, WidgetExt, WindowExt,
};

use std::cell::RefCell;
use std::collections::{HashMap, HashSet};
use std::process::{Command, Stdio};
use std::os::unix::process::CommandExt;
use std::rc::Rc;

use display_sysinfo::DisplaySysInfo;
use notebook::NoteBook;
use procs::{create_and_fill_model, Procs};

mod color;
mod display_sysinfo;
mod graph;
mod notebook;
mod process_dialog;
mod procs;
mod utils;

fn update_window(list: &gtk::ListStore, system: &Rc<RefCell<sysinfo::System>>,
                 info: &mut DisplaySysInfo, display_fahrenheit: bool) {
    let mut system = system.borrow_mut();
    system.refresh_all();
    info.update_ram_display(&system, display_fahrenheit);
    info.update_process_display(&system);
    let entries: &HashMap<i32, Process> = system.get_process_list();
    let mut seen: HashSet<i32> = HashSet::new();

    if let Some(mut iter) = list.get_iter_first() {
        let mut valid = true;
        while valid {
            if let Some(pid) = list.get_value(&iter, 0).get::<i32>() {
                if let Some(p) = entries.get(&(pid)) {
                    list.set(&iter,
                             &[2, 3, 5],
                             &[&format!("{:.1}", p.cpu_usage), &p.memory, &p.cpu_usage]);
                    valid = list.iter_next(&mut iter);
                    seen.insert(pid);
                } else {
                    valid = list.remove(&mut iter);
                }
            }
        }
    }

    for (pid, pro) in entries.iter() {
        if !seen.contains(pid) {
            create_and_fill_model(list, pro.pid, &format!("{:?}", &pro.cmd), &pro.name,
                                  pro.cpu_usage, pro.memory);
        }
    }
}

fn parse_quote(line: &str, quote: char) -> Vec<String> {
    let args = line.split(quote).collect::<Vec<&str>>();
    let mut out_args = vec!();

    for (num, arg) in args.iter().enumerate() {
        if num != 1 {
            out_args.extend_from_slice(&parse_entry(arg));
        } else {
            out_args.push((*arg).to_owned());
        }
    }
    out_args
}

// super simple parsing
fn parse_entry(line: &str) -> Vec<String> {
    match (line.find('\''), line.find('"')) {
        (Some(x), Some(y)) => {
            if x < y {
                parse_quote(line, '\'')
            } else {
                parse_quote(line, '"')
            }
        }
        (Some(_), None) => parse_quote(line, '\''),
        (None, Some(_)) => parse_quote(line, '"'),
        (None, None) => line.split(' ').into_iter()
                                       .map(|s| s.to_owned())
                                       .collect::<Vec<String>>(),
    }
}

fn start_detached_process(line: &str) -> Option<String> {
    let args = parse_entry(line);
    let command = args[0].clone();

    if let Err(_) = Command::new(&command)
                            .args(&args)
                            .before_exec(|| {
                                unsafe { libc::setsid() };
                                Ok(())
                            })
                            .stdin(Stdio::null())
                            .stderr(Stdio::null())
                            .stdout(Stdio::null())
                            .spawn() {
        Some(format!("Failed to start '{}'", &command))
    } else {
        None
    }
}

fn run_command<T: IsA<gtk::Window>>(input: &Entry, window: &T, d: &Dialog) {
    if let Some(text) = input.get_text() {
        let x = if let Some(x) = start_detached_process(&text) {
            x
        } else {
            "The command started successfully".to_owned()
        };
        d.destroy();
        let m = MessageDialog::new(Some(window),
                                   gtk::DIALOG_DESTROY_WITH_PARENT,
                                   gtk::MessageType::Info,
                                   gtk::ButtonsType::Ok,
                                   &x);
        m.run();
        m.destroy();
    }
}

fn build_ui(application: &gtk::Application) {
    let menu = gio::Menu::new();
    let menu_bar = gio::Menu::new();
    let more_menu = gio::Menu::new();
    let settings_menu = gio::Menu::new();

    menu.append("Launch new executable", "app.new-task");
    menu.append("Quit", "app.quit");

    settings_menu.append("Display temperature in °F", "app.temperature");
    settings_menu.append("Display graphs", "app.graphs");
    menu_bar.append_submenu("_Setting", &settings_menu);

    more_menu.append("About", "app.about");
    menu_bar.append_submenu("?", &more_menu);

    application.set_app_menu(&menu);
    application.set_menubar(&menu_bar);

    let window = gtk::ApplicationWindow::new(application);
    let sys = sysinfo::System::new();
    let start_time = unsafe { if let Some(p) = sys.get_process(libc::getpid()) {
        p.start_time
    } else {
        0
    }};
    let running_since = Rc::new(RefCell::new(0));
    let sys = Rc::new(RefCell::new(sys));
    let mut note = NoteBook::new();
    let procs = Procs::new(sys.borrow().get_process_list(), &mut note);
    let current_pid = procs.current_pid.clone();
    let current_pid2 = procs.current_pid.clone();
    let sys1 = sys.clone();
    let sys2 = sys.clone();
    let sys3 = sys.clone();
    let info_button = procs.info_button.clone();

    window.set_title("Process viewer");
    window.set_position(gtk::WindowPosition::Center);
    // To silence the annying warning:
    // "(.:2257): Gtk-WARNING **: Allocating size to GtkWindow 0x7f8a31038290 without
    // calling gtk_widget_get_preferred_width/height(). How does the code know the size to
    // allocate?"
    window.get_preferred_width();
    window.set_default_size(500, 700);

    window.connect_delete_event(|w, _| {
        w.destroy();
        Inhibit(false)
    });

    sys.borrow_mut().refresh_all();
    procs.kill_button.connect_clicked(move |_| {
        let sys = sys1.borrow();
        if let Some(process) = current_pid.get().and_then(|pid| sys.get_process(pid)) {
            process.kill(Signal::Kill);
        }
    });

    let mut display_tab = DisplaySysInfo::new(sys.clone(), &mut note, &window);

    let v_box = gtk::Box::new(gtk::Orientation::Vertical, 0);

    let ram_check_box = display_tab.ram_check_box.clone();
    let swap_check_box = display_tab.swap_check_box.clone();
    let temperature_check_box = display_tab.temperature_check_box.clone();

    let graphs = gio::SimpleAction::new_stateful("graphs", None, &false.to_variant());
    graphs.connect_activate(move |g, _| {
        let mut is_active = false;
        if let Some(g) = g.get_state() {
            is_active = g.get().expect("couldn't get bool");
            ram_check_box.set_active(!is_active);
            swap_check_box.set_active(!is_active);
            if let Some(ref temperature_check_box) = temperature_check_box {
                temperature_check_box.set_active(!is_active);
            }
        }
        // We need to change the toggle state ourselves. `gio` dark magic.
        g.change_state(&(!is_active).to_variant());
    });
    let temperature = gio::SimpleAction::new_stateful("temperature", None, &false.to_variant());
    temperature.connect_activate(move |g, _| {
        let mut is_active = false;
        if let Some(g) = g.get_state() {
            is_active = g.get().expect("couldn't get graph state");
        }
        // We need to change the toggle state ourselves. `gio` dark magic.
        g.change_state(&(!is_active).to_variant());
    });
    application.add_action(&temperature);

    // I think it's now useless to have this one...
    v_box.pack_start(&note.notebook, true, true, 0);

    window.add(&v_box);
    window.show_all();
    window.activate();

    let list_store = procs.list_store.clone();
    let run1 = running_since.clone();
    let run2 = running_since.clone();
    gtk::timeout_add(1000, move || {
        *run1.borrow_mut() += 1;
        // first part, deactivate sorting
        let sorted = TreeSortableExtManual::get_sort_column_id(&list_store);
        list_store.set_unsorted();

        // we update the tree view
        if let Some(temperature) = temperature.get_state() {
            update_window(&list_store, &sys, &mut display_tab,
                          temperature.get::<bool>().expect("couldn't get temperature state"));
        }

        // we re-enable the sorting
        if let Some((col, order)) = sorted {
            list_store.set_sort_column_id(col, order);
        }
        glib::Continue(true)
    });
    let window1 = window.clone();
    info_button.connect_clicked(move |_| {
        let sys = sys2.borrow();
        if let Some(process) = current_pid2.get().and_then(|pid| sys.get_process(pid)) {
            process_dialog::create_process_dialog(&process, &window1, start_time,
                                                  run2.borrow().clone());
        }
    });
    let window2 = window.clone();
    procs.left_tree.connect_row_activated(move |tree_view, path, _| {
        let model = tree_view.get_model().unwrap();
        let iter = model.get_iter(path).unwrap();
        let sys = sys3.borrow();
        if let Some(process) = sys.get_process(model.get_value(&iter, 0).get().unwrap()) {
            process_dialog::create_process_dialog(&process, &window2, start_time,
                                                  running_since.borrow().clone());
        }
    });

    let window2 = window.clone();
    let quit = gio::SimpleAction::new("quit", None);
    quit.connect_activate(move |_, _| {
        window2.destroy();
    });
    let window3 = window.clone();
    window3.present();
    let about = gio::SimpleAction::new("about", None);
    about.connect_activate(move |_, _| {
        let p = AboutDialog::new();
        p.set_authors(&["Guillaume Gomez"]);
        p.set_website_label(Some("my website"));
        p.set_website(Some("https://guillaume-gomez.fr/"));
        p.set_comments(Some("A process viewer GUI wrote with gtk-rs"));
        p.set_copyright(Some("This is under MIT license"));
        p.set_transient_for(Some(&window3));
        p.set_program_name("process-viewer");
        let logo = Pixbuf::new_from_file("assets/eye.png");
        if let Ok(logo) = logo {
            p.set_logo(Some(&logo));
        }
        p.run();
        p.destroy();
    });
    let new_task = gio::SimpleAction::new("new-task", None);
    new_task.connect_activate(move |_, _| {
        let d = Dialog::new();
        d.set_title("Launch new executable");
        let content_area = d.get_content_area();
        let input = Entry::new();
        let run = Button::new_with_label("Run");
        let cancel = Button::new_with_label("Cancel");
        let v_box = gtk::Box::new(gtk::Orientation::Vertical, 0);
        let h_box = gtk::Box::new(gtk::Orientation::Horizontal, 0);
        h_box.pack_start(&run, true, true, 0);
        h_box.pack_start(&cancel, true, true, 0);
        v_box.pack_start(&input, true, true, 0);
        v_box.pack_start(&h_box, true, true, 0);
        content_area.add(&v_box);
        let window2 = window.clone();
        d.set_transient_for(Some(&window2));
        // To silence the annying warning:
        // "(.:2257): Gtk-WARNING **: Allocating size to GtkWindow 0x7f8a31038290 without
        // calling gtk_widget_get_preferred_width/height(). How does the code know the size to
        // allocate?"
        d.get_preferred_width();
        d.set_size_request(400, 70);
        d.show_all();

        run.set_sensitive(false);
        let run2 = run.clone();
        let input2 = input.clone();
        input.connect_changed(move |_| {
            match input2.get_text() {
                Some(ref x) if x.len() > 0 => run2.set_sensitive(true),
                _ => run2.set_sensitive(false),
            }
        });
        let d2 = d.clone();
        cancel.connect_clicked(move |_| {
            d2.destroy();
        });
        let window3 = window.clone();
        let input3 = input.clone();
        let d3 = d.clone();
        input.connect_activate(move |_| {
            run_command(&input3, &window3, &d3);
        });
        let window4 = window.clone();
        run.connect_clicked(move |_| {
            run_command(&input, &window4, &d);
        });
    });

    application.add_action(&about);
    application.add_action(&graphs);
    application.add_action(&new_task);
    application.add_action(&quit);
    application.connect_activate(move |_| {});
}

fn main() {
    let application = gtk::Application::new("com.github.GuillaumeGomez.process-viewer",
                                            gio::ApplicationFlags::empty())
                                       .expect("Initialization failed...");

    application.connect_startup(move |app| {
        build_ui(app);
    });

    let original = ::std::env::args().collect::<Vec<_>>();
    let mut tmp = Vec::with_capacity(original.len());
    for i in 0..original.len() {
        tmp.push(original[i].as_str());
    }
    application.run(&tmp);
}
<|endoftext|>