<commit_before>// C preprocessor

use crate::token::*;
use std::cell::RefCell;
use std::collections::HashMap;

thread_local! {
    static MACROS: RefCell<HashMap<String, Macro>> = RefCell::new(HashMap::new());

    static ENV: RefCell<Env> = RefCell::new(Env::new());
}

fn set_env(env: Env) {
    ENV.with(|c| {
        *c.borrow_mut() = env;
    })
}

fn get_env() -> Option<Env> {
    ENV.with(|c| {
        Some(c.borrow().clone())
    })
}

fn env_pop() {
    ENV.with(|c| {
        let next = c.borrow().next.clone();
        if next.is_some() {
            *c.borrow_mut() = *next.unwrap();
        }
    })
}

fn env_output() -> Vec<Token> {
    ENV.with(|c| {
        return c.borrow().output.clone();
    })
}

fn macros_get(key: &String) -> Option<Macro> {
    MACROS.with(|m| {
        match m.borrow().get(key) {
            Some(v) => Some(v.clone()),
            None => None,
        }
    })
}

fn macros_put(key: String, value: Macro) {
    MACROS.with(|m| {
        m.borrow_mut().insert(key, value);
    })
}

#[derive(Clone, Debug)]
struct Env {
    input: Vec<Token>,
    output: Vec<Token>,
    pos: usize,
    next: Option<Box<Env>>,
}

impl Env {
    fn new() -> Env {
        Env {
            input: Vec::new(),
            output: Vec::new(),
            pos: 0,
            next: None,
        }
    }
}

fn new_env(next: Option<Env>, input: Vec<Token>) -> Env {
    let mut ctx = Env::new();
    ctx.input = input;
    if next.is_none() {
        ctx.next = None;
    } else {
        ctx.next = Some(Box::new(next.unwrap()));
    }
    return ctx;
}

#[derive(Clone, Debug, PartialEq)]
enum MacroType {
    OBJLIKE,
    FUNCLIKE,
}

#[derive(Clone, Debug)]
struct Macro {
    ty: MacroType,
    tokens: Vec<Token>,
    params: Vec<String>,
}

fn new_macro(ty: MacroType, name: String) {
    let m = Macro {
        ty: ty,
        tokens: Vec::new(),
        params: Vec::new(),
    };
    macros_put(name, m);
}

fn append(v: &mut Vec<Token>) {
    ENV.with(|env| {
        env.borrow_mut().output.append(v);
    })
}

fn emit(t: Token) {
    ENV.with(|env| {
        env.borrow_mut().output.push(t);
    })
}

fn next() -> Token {
    ENV.with(|env| {
        let pos = env.borrow().pos;
        assert!(pos < env.borrow().input.len());
        env.borrow_mut().pos += 1;
        return env.borrow().input[pos].clone();
    })
}

fn eof() -> bool {
    ENV.with(|c| {
        let env = c.borrow();
        return env.pos == env.input.len();
    })
}

fn get(ty: TokenType, msg: String) -> Token {
    let t = next();
    if t.ty != ty {
        bad_token(&t, msg);
    }
    return t;
}

fn ident(msg: String) -> String {
    let t = get(TokenType::IDENT, msg);
    return t.name.clone();
}

fn peek() -> Token {
    ENV.with(|c| {
        let env = c.borrow();
        return env.input[env.pos].clone();
    })
}

fn consume(ty: TokenType) -> bool {
    if peek().ty != ty {
        return false;
    }
    ENV.with(|c| {
        c.borrow_mut().pos += 1;
    });
    return true;
}

fn read_until_eol() -> Vec<Token> {
    let mut v = Vec::new();
    while !eof() {
        let t = next();
        if t.ty == TokenType::NEW_LINE {
            break;
        }
        v.push(t.clone());
    }
    return v;
}

fn new_int(tmpl: & Token, val: i32) -> Token {
    let mut t = tmpl.clone();
    t.ty = TokenType::NUM;
    t.val = val;
    return t;
}

fn new_string(tmpl: & Token, str_cnt: String, len: usize) -> Token {
    let mut t = tmpl.clone();
    t.ty = TokenType::STR;
    t.str_cnt = str_cnt;
    t.len = len;
    return t;
}

fn new_param(tmpl: & Token, val: i32) -> Token {
    let mut t = tmpl.clone();
    t.ty = TokenType::PARAM;
    t.val = val;
    return t;
}

fn is_ident(t: &Token, s: &str) -> bool {
    return t.ty == TokenType::IDENT && t.name == s;
}

// Replaces macro parameter tokens with TokenType::PARAM tokens.
fn replace_macro_params(m: &mut Macro) {
    let params = m.params.clone();
    let mut tokens = m.tokens.clone();

    let mut map = HashMap::new();
    for i in 0..params.len() {
        let name = params[i].clone();
        map.insert(name, i as i32);
    }

    for i in 0..tokens.len() {
        let t = &tokens[i].clone();
        if t.ty != TokenType::IDENT {
            continue;
        }
        let n = match map.get(&t.name) {
            Some(i) => *i,
            None => {
                continue;
            }
        };
        tokens.remove(i);
        tokens.insert(i, new_param(t, n));
    }
    m.tokens = tokens;
}

// Replaces '#' followed by a macro parameter.
fn replace_hash_ident(m: &mut Macro) {
    let tokens = m.tokens.clone();
    let mut v = Vec::new();
    let mut i = 0;
    while i < tokens.len()-1 {
        let t1 = tokens[i].clone();
        let mut t2 = tokens[i+1].clone();

        if t1.ty == TokenType::SHARP && t2.ty == TokenType::PARAM {
            t2.stringize = true;
            v.push(t2);
            i += 1;
        } else {
            v.push(t1);
        }
        i += 1;
    }

    if i == tokens.len() - 1 {
        v.push(tokens[i].clone());
    }
    m.tokens = v;
}

fn read_one_arg() -> Vec<Token> {
    let mut v = Vec::new();
    let start = peek();
    let mut level = 0;

    while !eof() {
        let t = peek();
        if level == 0 {
            if t.ty == TokenType::KET || t.ty == TokenType::COMMA {
                return v;
            }
        }

        next();
        if t.ty == TokenType::BRA {
            level += 1;
        } else if t.ty == TokenType::KET {
            level -= 1;
        }
        v.push(t);
    }
    bad_token(&start, "unclosed macro argument".to_string());
    panic!();
}

fn read_args() -> HashMap<usize, Vec<Token>> {
    let mut v = HashMap::new();
    if consume(TokenType::KET) {
        return v;
    }
    let i = v.len();
    v.insert(i, read_one_arg());
    while !consume(TokenType::KET) {
        get(TokenType::COMMA, "comma expected".to_string());
        let i = v.len();
        v.insert(i, read_one_arg());
    }
    return v;
}

fn stringize(tmpl: & Token, tokens: Vec<Token>) -> Token {
    let mut sb = String::new();

    for i in 0..tokens.len() {
        let t = &tokens[i];
        if i > 0 {
            sb.push(' ');
        }
        sb.push_str(tokstr(&t));
    }
    sb.push('\0');

    let len = sb.len();
    return new_string(tmpl, sb, len);
}

fn add_special_macro(t: & Token) -> bool {
    if is_ident(t, "__LINE__") {
        emit(new_int(t, get_line_number(t)));
        return true;
    }
    return false;
}

fn apply_objlike(m: &mut Macro, _start: & Token) {
    for t in m.tokens.iter() {
        if add_special_macro(t) {
            continue;
        }
        emit(t.clone());
    }
}

fn apply_functionlike(m: &mut Macro, start: & Token) {
    get(TokenType::BRA, "comma expected".to_string());
    let args = read_args();
    if m.params.len() != args.len() {
        bad_token(&start, format!("number of parameter does not match ({} != {})", m.params.len(), args.len()).to_string());
        panic!();
    }

    for i in 0..m.tokens.len() {
        let t = &m.tokens[i];
        if add_special_macro(t) {
            continue;
        }

        if t.ty == TokenType::PARAM {
            if t.stringize {
                let j = t.val as usize;
                emit(stringize(t, args.get(&j).unwrap().clone()));
            } else {
                let j = t.val as usize;
                append(&mut args.get(&j).unwrap().clone());
            }
            continue;
        }
        emit(t.clone());
    }
}

fn apply(m: &mut Macro, start: & Token) {
    if m.ty == MacroType::OBJLIKE {
        apply_objlike(m, start);
    } else {
        apply_functionlike(m, start);
    }
}

fn define_funclike(name: String) {
    new_macro(MacroType::FUNCLIKE, name.clone());
    let mut m = macros_get(&name).unwrap();
    m.params.push(ident("parameter name expected".to_string()));
    while !consume(TokenType::KET) {
        get(TokenType::COMMA, "comma expected".to_string());
        m.params.push(ident("parameter name expected".to_string()));
    }
    m.tokens = read_until_eol();

    replace_macro_params(&mut m);
    replace_hash_ident(&mut m);

    macros_put(name, m);
}

fn define_objlike(name: String) {
    new_macro(MacroType::OBJLIKE, name.clone());
    let mut m = macros_get(&name).unwrap();
    m.tokens = read_until_eol();
    macros_put(name, m);
}

fn define() {
    let name = ident("macro name expected".to_string());
    if consume(TokenType::BRA) {
        return define_funclike(name);
    }
    return define_objlike(name);
}

fn include() {
    let t = get(TokenType::STR, "string expected".to_string());
    let path = t.str_cnt;
    get(TokenType::NEW_LINE, "newline expected".to_string());
    return append(&mut tokenize(path, false));
}

pub fn preprocess(tokens: Vec<Token>) -> Vec<Token> {
    set_env(new_env(get_env(), tokens.clone()));

    while !eof() {
        let mut t = next();

        if t.ty == TokenType::IDENT {
            match macros_get(&t.name) {
                Some(macro_token) => {
                    apply(&mut macro_token.clone(), &t);
                }
                None => {
                    emit(t.clone());
                }
            }
            continue;
        }

        if t.ty != TokenType::SHARP {
            emit(t.clone());
            continue;
        }

        t = get(TokenType::IDENT, "identifier expected".to_string());

        if t.name == "define" {
            define();
        } else if t.name == "include" {
            include();
        } else {
            bad_token(&t, "unknown directive".to_string());
        }
    }

    let v = env_output();
    env_pop();
    return v;
}
<commit_msg>Rename.<commit_after>// C preprocessor

use crate::token::*;
use std::cell::RefCell;
use std::collections::HashMap;

thread_local! {
    static MACROS: RefCell<HashMap<String, Macro>> = RefCell::new(HashMap::new());

    static ENV: RefCell<Env> = RefCell::new(Env::new());
}

fn set_env(env: Env) {
    ENV.with(|c| {
        *c.borrow_mut() = env;
    })
}

fn get_env() -> Option<Env> {
    ENV.with(|c| {
        Some(c.borrow().clone())
    })
}

fn env_pop() {
    ENV.with(|c| {
        let next = c.borrow().next.clone();
        if next.is_some() {
            *c.borrow_mut() = *next.unwrap();
        }
    })
}

fn env_output() -> Vec<Token> {
    ENV.with(|c| {
        return c.borrow().output.clone();
    })
}

fn macros_get(key: &String) -> Option<Macro> {
    MACROS.with(|m| {
        match m.borrow().get(key) {
            Some(v) => Some(v.clone()),
            None => None,
        }
    })
}

fn macros_put(key: String, value: Macro) {
    MACROS.with(|m| {
        m.borrow_mut().insert(key, value);
    })
}

#[derive(Clone, Debug)]
struct Env {
    input: Vec<Token>,
    output: Vec<Token>,
    pos: usize,
    next: Option<Box<Env>>,
}

impl Env {
    fn new() -> Env {
        Env {
            input: Vec::new(),
            output: Vec::new(),
            pos: 0,
            next: None,
        }
    }
}

fn new_env(next: Option<Env>, input: Vec<Token>) -> Env {
    let mut ctx = Env::new();
    ctx.input = input;
    if next.is_none() {
        ctx.next = None;
    } else {
        ctx.next = Some(Box::new(next.unwrap()));
    }
    return ctx;
}

#[derive(Clone, Debug, PartialEq)]
enum MacroType {
    OBJLIKE,
    FUNCLIKE,
}

#[derive(Clone, Debug)]
struct Macro {
    ty: MacroType,
    tokens: Vec<Token>,
    params: Vec<String>,
}

fn new_macro(ty: MacroType, name: String) {
    let m = Macro {
        ty: ty,
        tokens: Vec::new(),
        params: Vec::new(),
    };
    macros_put(name, m);
}

fn append(v: &mut Vec<Token>) {
    ENV.with(|env| {
        env.borrow_mut().output.append(v);
    })
}

fn emit(t: Token) {
    ENV.with(|env| {
        env.borrow_mut().output.push(t);
    })
}

fn next() -> Token {
    ENV.with(|env| {
        let pos = env.borrow().pos;
        assert!(pos < env.borrow().input.len());
        env.borrow_mut().pos += 1;
        return env.borrow().input[pos].clone();
    })
}

fn is_eof() -> bool {
    ENV.with(|c| {
        let env = c.borrow();
        return env.pos == env.input.len();
    })
}

fn get(ty: TokenType, msg: String) -> Token {
    let t = next();
    if t.ty != ty {
        bad_token(&t, msg);
    }
    return t;
}

fn ident(msg: String) -> String {
    let t = get(TokenType::IDENT, msg);
    return t.name.clone();
}

fn peek() -> Token {
    ENV.with(|c| {
        let env = c.borrow();
        return env.input[env.pos].clone();
    })
}

fn consume(ty: TokenType) -> bool {
    if peek().ty != ty {
        return false;
    }
    ENV.with(|c| {
        c.borrow_mut().pos += 1;
    });
    return true;
}

fn read_until_eol() -> Vec<Token> {
    let mut v = Vec::new();
    while !is_eof() {
        let t = next();
        if t.ty == TokenType::NEW_LINE {
            break;
        }
        v.push(t.clone());
    }
    return v;
}

fn new_int(tmpl: & Token, val: i32) -> Token {
    let mut t = tmpl.clone();
    t.ty = TokenType::NUM;
    t.val = val;
    return t;
}

fn new_string(tmpl: & Token, str_cnt: String, len: usize) -> Token {
    let mut t = tmpl.clone();
    t.ty = TokenType::STR;
    t.str_cnt = str_cnt;
    t.len = len;
    return t;
}

fn new_param(tmpl: & Token, val: i32) -> Token {
    let mut t = tmpl.clone();
    t.ty = TokenType::PARAM;
    t.val = val;
    return t;
}

fn is_ident(t: &Token, s: &str) -> bool {
    return t.ty == TokenType::IDENT && t.name == s;
}

// Replaces macro parameter tokens with TokenType::PARAM tokens.
fn replace_macro_params(m: &mut Macro) {
    let params = m.params.clone();
    let mut tokens = m.tokens.clone();

    let mut map = HashMap::new();
    for i in 0..params.len() {
        let name = params[i].clone();
        map.insert(name, i as i32);
    }

    for i in 0..tokens.len() {
        let t = &tokens[i].clone();
        if t.ty != TokenType::IDENT {
            continue;
        }
        let n = match map.get(&t.name) {
            Some(i) => *i,
            None => {
                continue;
            }
        };
        tokens.remove(i);
        tokens.insert(i, new_param(t, n));
    }
    m.tokens = tokens;
}

// Replaces '#' followed by a macro parameter.
fn replace_hash_ident(m: &mut Macro) {
    let tokens = m.tokens.clone();
    let mut v = Vec::new();
    let mut i = 0;
    while i < tokens.len()-1 {
        let t1 = tokens[i].clone();
        let mut t2 = tokens[i+1].clone();

        if t1.ty == TokenType::SHARP && t2.ty == TokenType::PARAM {
            t2.stringize = true;
            v.push(t2);
            i += 1;
        } else {
            v.push(t1);
        }
        i += 1;
    }

    if i == tokens.len() - 1 {
        v.push(tokens[i].clone());
    }
    m.tokens = v;
}

fn read_one_arg() -> Vec<Token> {
    let mut v = Vec::new();
    let start = peek();
    let mut level = 0;

    while !is_eof() {
        let t = peek();
        if level == 0 {
            if t.ty == TokenType::KET || t.ty == TokenType::COMMA {
                return v;
            }
        }

        next();
        if t.ty == TokenType::BRA {
            level += 1;
        } else if t.ty == TokenType::KET {
            level -= 1;
        }
        v.push(t);
    }
    bad_token(&start, "unclosed macro argument".to_string());
    panic!();
}

fn read_args() -> HashMap<usize, Vec<Token>> {
    let mut v = HashMap::new();
    if consume(TokenType::KET) {
        return v;
    }
    let i = v.len();
    v.insert(i, read_one_arg());
    while !consume(TokenType::KET) {
        get(TokenType::COMMA, "comma expected".to_string());
        let i = v.len();
        v.insert(i, read_one_arg());
    }
    return v;
}

fn stringize(tmpl: & Token, tokens: Vec<Token>) -> Token {
    let mut sb = String::new();

    for i in 0..tokens.len() {
        let t = &tokens[i];
        if i > 0 {
            sb.push(' ');
        }
        sb.push_str(tokstr(&t));
    }
    sb.push('\0');

    let len = sb.len();
    return new_string(tmpl, sb, len);
}

fn add_special_macro(t: & Token) -> bool {
    if is_ident(t, "__LINE__") {
        emit(new_int(t, get_line_number(t)));
        return true;
    }
    return false;
}

fn apply_objlike(m: &mut Macro, _start: & Token) {
    for t in m.tokens.iter() {
        if add_special_macro(t) {
            continue;
        }
        emit(t.clone());
    }
}

fn apply_functionlike(m: &mut Macro, start: & Token) {
    get(TokenType::BRA, "comma expected".to_string());
    let args = read_args();
    if m.params.len() != args.len() {
        bad_token(&start, format!("number of parameter does not match ({} != {})", m.params.len(), args.len()).to_string());
        panic!();
    }

    for i in 0..m.tokens.len() {
        let t = &m.tokens[i];
        if add_special_macro(t) {
            continue;
        }

        if t.ty == TokenType::PARAM {
            if t.stringize {
                let j = t.val as usize;
                emit(stringize(t, args.get(&j).unwrap().clone()));
            } else {
                let j = t.val as usize;
                append(&mut args.get(&j).unwrap().clone());
            }
            continue;
        }
        emit(t.clone());
    }
}

fn apply(m: &mut Macro, start: & Token) {
    if m.ty == MacroType::OBJLIKE {
        apply_objlike(m, start);
    } else {
        apply_functionlike(m, start);
    }
}

fn define_funclike(name: String) {
    new_macro(MacroType::FUNCLIKE, name.clone());
    let mut m = macros_get(&name).unwrap();
    m.params.push(ident("parameter name expected".to_string()));
    while !consume(TokenType::KET) {
        get(TokenType::COMMA, "comma expected".to_string());
        m.params.push(ident("parameter name expected".to_string()));
    }
    m.tokens = read_until_eol();

    replace_macro_params(&mut m);
    replace_hash_ident(&mut m);

    macros_put(name, m);
}

fn define_objlike(name: String) {
    new_macro(MacroType::OBJLIKE, name.clone());
    let mut m = macros_get(&name).unwrap();
    m.tokens = read_until_eol();
    macros_put(name, m);
}

fn define() {
    let name = ident("macro name expected".to_string());
    if consume(TokenType::BRA) {
        return define_funclike(name);
    }
    return define_objlike(name);
}

fn include() {
    let t = get(TokenType::STR, "string expected".to_string());
    let path = t.str_cnt;
    get(TokenType::NEW_LINE, "newline expected".to_string());
    return append(&mut tokenize(path, false));
}

pub fn preprocess(tokens: Vec<Token>) -> Vec<Token> {
    set_env(new_env(get_env(), tokens.clone()));

    while !is_eof() {
        let mut t = next();

        if t.ty == TokenType::IDENT {
            match macros_get(&t.name) {
                Some(macro_token) => {
                    apply(&mut macro_token.clone(), &t);
                }
                None => {
                    emit(t.clone());
                }
            }
            continue;
        }

        if t.ty != TokenType::SHARP {
            emit(t.clone());
            continue;
        }

        t = get(TokenType::IDENT, "identifier expected".to_string());

        if t.name == "define" {
            define();
        } else if t.name == "include" {
            include();
        } else {
            bad_token(&t, "unknown directive".to_string());
        }
    }

    let v = env_output();
    env_pop();
    return v;
}
<|endoftext|>


<commit_before>use super::mode;
use super::util;
use std::collections::Deque;
use std::collections::RingBuf;

#[deriving(Clone)]
#[deriving(Show)]
enum DOC {
    Nil,
    Append(Box<Doc>, Box<Doc>),
    Group(Box<Doc>),
    Nest(uint, Box<Doc>),
    Newline,
    Text(String),
}

pub type Doc = DOC;
type Cmd<'a> = (uint,mode::Mode,&'a Doc);

fn fitting(mut cmds:RingBuf<Cmd>, mut rem:int) -> bool {
    let mut fits = true;

    loop {
        if rem < 0 {
            fits = false;
            break;
        }
        match cmds.pop_front() {
            None => {
                break;
            },
            Some((ind, mode, doc)) => match doc {
                &Nil => {
                },
                &Append(box ref ldoc, box ref rdoc) => {
                    cmds.push_front((ind, mode, rdoc));
                    cmds.push_front((ind, mode, ldoc));
                },
                &Group(box ref doc) => {
                    cmds.push_front((ind, mode, doc));
                },
                &Nest(off, box ref doc) => {
                    cmds.push_front((ind + off, mode, doc));
                },
                &Newline => {
                    fits = true;
                },
                &Text(ref str) => {
                    rem -= str.len() as int;
                },
            }
        }
    }

    fits
}

impl Doc {

    fn best(&self, width:uint) -> Vec<String> {
        let mut pos = 0u;
        let mut cmds = RingBuf::new();
        let mut result = Vec::new();

        cmds.push((0, mode::Break, self));

        loop {
            match cmds.pop_front() {
                None => {
                    break;
                },
                Some((ind, mode, doc)) => match doc {
                    &Nil => {
                    },
                    &Append(box ref ldoc, box ref rdoc) => {
                        cmds.push_front((ind, mode, rdoc));
                        cmds.push_front((ind, mode, ldoc));
                    },
                    &Group(box ref doc) => match mode {
                        mode::Flat => {
                            cmds.push_front((ind, mode::Flat, doc));
                        },
                        mode::Break => {
                            // FIXME: fitting() modifies cmds so we must either
                            // take a clone and pass it or rewrite fitting()
                            // to modify a separate cmd stack and only observe
                            // this one.
                            let mut cmds_dup = cmds.clone();
                            let next = (ind, mode::Flat, doc);
                            cmds_dup.push_front(next);
                            if fitting(cmds_dup, width as int - pos as int) {
                                cmds.push_front(next);
                            } else {
                                cmds.push_front((ind, mode::Break, doc));
                            }
                        }
                    },
                    &Nest(off, box ref doc) => {
                        cmds.push_front((ind + off, mode, doc));
                    },
                    &Newline => {
                        result.push(util::string::nl_spaces(ind));
                        pos = ind;
                    },
                    &Text(ref str) => {
                        // FIXME: we have to clone here otherwise result would
                        // have to contain String and &String. We cannot make
                        // Newline case return &String because the region is
                        // limited to the scope of its arm; it does not live
                        // long enough (the strings in Text will outlast).
                        result.push(str.clone());
                        pos += str.len();
                    },
                }
            }
        }

        result
    }

    #[inline]
    pub fn nil() -> Doc {
        Nil
    }

    #[inline]
    pub fn append(self, that:Doc) -> Doc {
        match self {
            Nil => {
                that
            },
            ldoc => match that {
                Nil => {
                    ldoc
                },
                rdoc => {
                    Append(box ldoc, box rdoc)
                },
            }
        }
    }

    #[inline]
    pub fn as_string<T:ToString>(t:T) -> Doc {
        Doc::text(t.to_string())
    }

    #[inline]
    pub fn concat(ds:&[Doc]) -> Doc {
        ds.iter().fold(Nil, |a, b| a.append(b.clone()))
    }

    #[inline]
    pub fn group(self) -> Doc {
        Group(box self)
    }

    #[inline]
    pub fn nest(self, off:uint) -> Doc {
        Nest(off, box self)
    }

    #[inline]
    pub fn newline() -> Doc {
        Newline
    }

    // FIXME: could possibly parallelize this part since string append should
    // be associative. Might be a good example for rust-monoid…
    #[inline]
    pub fn render(&self, width:uint) -> String {
        let mut result = String::new();
        for str in self.best(width).iter() {
            result.push_str(str.as_slice());
        }
        result
    }

    #[inline]
    pub fn text<T:Str>(str:T) -> Doc {
        Text(String::from_str(str.as_slice()))
    }

}
<commit_msg>Eliminate Vec; just mutate String<commit_after>use super::mode;
use super::util;
use std::collections::Deque;
use std::collections::RingBuf;

#[deriving(Clone)]
#[deriving(Show)]
enum DOC {
    Nil,
    Append(Box<Doc>, Box<Doc>),
    Group(Box<Doc>),
    Nest(uint, Box<Doc>),
    Newline,
    Text(String),
}

pub type Doc = DOC;
type Cmd<'a> = (uint,mode::Mode,&'a Doc);

fn fitting(mut cmds:RingBuf<Cmd>, mut rem:int) -> bool {
    let mut fits = true;

    loop {
        if rem < 0 {
            fits = false;
            break;
        }
        match cmds.pop_front() {
            None => {
                break;
            },
            Some((ind, mode, doc)) => match doc {
                &Nil => {
                },
                &Append(box ref ldoc, box ref rdoc) => {
                    cmds.push_front((ind, mode, rdoc));
                    cmds.push_front((ind, mode, ldoc));
                },
                &Group(box ref doc) => {
                    cmds.push_front((ind, mode, doc));
                },
                &Nest(off, box ref doc) => {
                    cmds.push_front((ind + off, mode, doc));
                },
                &Newline => {
                    fits = true;
                },
                &Text(ref str) => {
                    rem -= str.len() as int;
                },
            }
        }
    }

    fits
}

impl Doc {

    fn best(&self, width:uint) -> String {
        let mut pos = 0u;
        let mut cmds = RingBuf::new();
        let mut result = String::new();

        cmds.push((0, mode::Break, self));

        loop {
            match cmds.pop_front() {
                None => {
                    break;
                },
                Some((ind, mode, doc)) => match doc {
                    &Nil => {
                    },
                    &Append(box ref ldoc, box ref rdoc) => {
                        cmds.push_front((ind, mode, rdoc));
                        cmds.push_front((ind, mode, ldoc));
                    },
                    &Group(box ref doc) => match mode {
                        mode::Flat => {
                            cmds.push_front((ind, mode::Flat, doc));
                        },
                        mode::Break => {
                            // FIXME: fitting() modifies cmds so we must either
                            // take a clone and pass it or rewrite fitting()
                            // to modify a separate cmd stack and only observe
                            // this one.
                            let mut cmds_dup = cmds.clone();
                            let next = (ind, mode::Flat, doc);
                            cmds_dup.push_front(next);
                            if fitting(cmds_dup, width as int - pos as int) {
                                cmds.push_front(next);
                            } else {
                                cmds.push_front((ind, mode::Break, doc));
                            }
                        }
                    },
                    &Nest(off, box ref doc) => {
                        cmds.push_front((ind + off, mode, doc));
                    },
                    &Newline => {
                        result.push_str(util::string::nl_spaces(ind).as_slice());
                        pos = ind;
                    },
                    &Text(ref str) => {
                        result.push_str(str.as_slice());
                        pos += str.len();
                    },
                }
            }
        }

        result
    }

    #[inline]
    pub fn nil() -> Doc {
        Nil
    }

    #[inline]
    pub fn append(self, that:Doc) -> Doc {
        match self {
            Nil => {
                that
            },
            ldoc => match that {
                Nil => {
                    ldoc
                },
                rdoc => {
                    Append(box ldoc, box rdoc)
                },
            }
        }
    }

    #[inline]
    pub fn as_string<T:ToString>(t:T) -> Doc {
        Doc::text(t.to_string())
    }

    #[inline]
    pub fn concat(ds:&[Doc]) -> Doc {
        ds.iter().fold(Nil, |a, b| a.append(b.clone()))
    }

    #[inline]
    pub fn group(self) -> Doc {
        Group(box self)
    }

    #[inline]
    pub fn nest(self, off:uint) -> Doc {
        Nest(off, box self)
    }

    #[inline]
    pub fn newline() -> Doc {
        Newline
    }

    #[inline]
    pub fn render(&self, width:uint) -> String {
        self.best(width)
    }

    #[inline]
    pub fn text<T:Str>(str:T) -> Doc {
        Text(String::from_str(str.as_slice()))
    }

}
<|endoftext|>


<commit_before>//! List merge requests
//!
//! https://docs.gitlab.com/ce/api/merge_requests.html#list-merge-requests
//!
//! # List merge requests
//!
//! Get all merge requests for this project.
//! The `state` parameter can be used to get only merge requests with a given state (`opened`, `closed`, or `merged`) or all of them (`all`).
//! The pagination parameters `page` and `per_page` can be used to restrict the list of merge requests.
//!
//! ```text
//! GET /projects/ID/merge_requests
//! GET /projects/ID/merge_requests?state=opened
//! GET /projects/ID/merge_requests?state=all
//! GET /projects/ID/merge_requests?iid=42
//! GET /projects/ID/merge_requests?iid[]=42&iid[]=43
//! ```
//!
//! Parameters:
//!
//! - `id` (required) - The ID of a project
//! - `iid` (optional) - Return the request having the given `iid`
//! - `state` (optional) - Return `all` requests or just those that are `merged`, `opened` or `closed`
//! - `order_by` (optional) - Return requests ordered by `created_at` or `updated_at` fields. Default is `created_at`
//! - `sort` (optional) - Return requests sorted in `asc` or `desc` order. Default is `desc`
//!
//!

use BuildQuery;

use serde_json;

use gitlab::GitLab;
use MergeRequests;

// Types from serde_types.in.rs
use MergeRequestState;

pub mod single;

impl GitLab {
    pub fn merge_requests(&self, listing: Listing) -> Result<MergeRequests, serde_json::Error> {
        let query = listing.build_query();
        self.get(&query)
    }
}

#[derive(Debug, Copy, Clone)]
pub enum ListingOrderBy {
    CreatedAt,
    UpdatedAt,
}

#[derive(Debug, Copy, Clone)]
pub enum ListingSort {
    Asc,
    Desc,
}

#[derive(Default, Debug, Clone)]
pub struct Listing {
    /// Project id
    id: i64,
    /// Merge request's IID
    iid: Vec<i64>,
    /// State of the requests
    state: Option<MergeRequestState>,
    /// Return requests ordered by. Default is `ListingOrderBy::CreatedAt`.
    order_by: Option<ListingOrderBy>,
    /// Return requests sorted. Default is `ListingSort::Desc`.
    sort: Option<ListingSort>,
}

#[allow(dead_code)]
impl Listing {
    pub fn new(id: i64) -> Listing {
        Listing {id: id, ..Default::default()}
    }
    pub fn iid(&mut self, iid: Vec<i64>) -> &mut Listing {
        self.iid = iid;
        self
    }
    pub fn state(&mut self, state: MergeRequestState) -> &mut Listing {
        self.state = Some(state);
        self
    }
    pub fn order_by(&mut self, order_by: ListingOrderBy) -> &mut Listing {
        self.order_by = Some(order_by);
        self
    }
    fn sort(&mut self, sort: ListingSort) -> &mut Listing {
        self.sort = Some(sort);
        self
    }
}

impl BuildQuery for Listing {
    fn build_query(&self) -> String {

        let mut query = format!("projects/{}/merge_requests", self.id);

        let amp_char = "&";
        let none_char = "";
        let mut split_char = &none_char;

        // Append a "?" only if at least one of the `Option` is `Some(_)` or if
        // strings contain something.
        query.push_str(match (self.iid.is_empty(),
                              &self.state,
                              &self.order_by,
                              &self.sort) {
            (true, &None, &None, &None) => "",
            _ => "?",
        });

        if !self.iid.is_empty() {
            query.push_str(split_char);
            split_char = &amp_char;

            if self.iid.len() == 1 {
                query.push_str("iid=");
                query.push_str(&self.iid[0].to_string());
            } else {
                let mut array_split_char = &none_char;
                for iid in &self.iid {
                    query.push_str(array_split_char);
                    query.push_str("iid[]=");
                    query.push_str(&iid.to_string());
                    array_split_char = &amp_char;
                }
            }
        }

        self.state.map(|state| {
            query.push_str(split_char);
            split_char = &amp_char;

            query.push_str("state=");
            query.push_str(match state {
                MergeRequestState::Merged => "merged",
                MergeRequestState::Opened => "opened",
                MergeRequestState::Closed => "closed",
                MergeRequestState::All => "all",
            });
        });

        self.order_by.map(|order_by| {
            query.push_str(split_char);
            split_char = &amp_char;

            query.push_str("order_by=");
            query.push_str(match order_by {
                ListingOrderBy::CreatedAt => "created_at",
                ListingOrderBy::UpdatedAt => "updated_at",
            });
        });

        self.sort.map(|sort| {
            query.push_str(split_char);
            split_char = &amp_char;

            query.push_str("sort=");
            query.push_str(match sort {
                ListingSort::Asc => "asc",
                ListingSort::Desc => "desc",
            });
        });

        query
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use BuildQuery;
    use MergeRequestState;

    #[test]
    fn build_query_default() {
        let expected_string = "projects/123/merge_requests";
        let listing = Listing {id: 123, ..Default::default()};
        let query = listing.build_query();
        assert_eq!(query, expected_string);

        let expected_string = "projects/123/merge_requests";
        let listing = Listing::new(123);
        let query = listing.build_query();
        assert_eq!(query, expected_string);
    }

    // /// Return requests ordered by. Default is `ListingOrderBy::CreatedAt`.
    // order_by: Option<ListingOrderBy>,
    // /// Return requests sorted. Default is `ListingSort::Desc`.
    // sort: Option<ListingSort>,

    #[test]
    fn build_query_iid() {
        let expected_string = format!("projects/123/merge_requests?iid=456");
        let query = Listing::new(123).iid(vec![456]).build_query();
        assert_eq!(query, expected_string);

        let expected_string = format!("projects/123/merge_requests?iid[]=456&iid[]=789");
        let query = Listing::new(123).iid(vec![456,789]).build_query();
        assert_eq!(query, expected_string);
    }

    #[test]
    fn build_query_state() {
        let expected_string = "projects/123/merge_requests?state=opened";
        let query = Listing::new(123).state(MergeRequestState::Opened).build_query();
        assert_eq!(query, expected_string);

        let expected_string = "projects/123/merge_requests?state=closed";
        let query = Listing::new(123).state(MergeRequestState::Closed).build_query();
        assert_eq!(query, expected_string);
    }

    #[test]
    fn build_query_order_by() {
        let expected_string = "projects/123/merge_requests?order_by=created_at";
        let query = Listing::new(123).order_by(ListingOrderBy::CreatedAt).build_query();
        assert_eq!(query, expected_string);

        let expected_string = "projects/123/merge_requests?order_by=updated_at";
        let query = Listing::new(123).order_by(ListingOrderBy::UpdatedAt).build_query();
        assert_eq!(query, expected_string);
    }

    #[test]
    fn build_query_sort() {
        let expected_string = "projects/123/merge_requests?sort=asc";
        let query = Listing::new(123).sort(ListingSort::Asc).build_query();
        assert_eq!(query, expected_string);

        let expected_string = "projects/123/merge_requests?sort=desc";
        let query = Listing::new(123).sort(ListingSort::Desc).build_query();
        assert_eq!(query, expected_string);
    }

    #[test]
    fn build_query_multiple() {
        let expected_string = "projects/123/merge_requests?iid[]=456&iid[]=789&order_by=created_at&sort=asc";
        let query = Listing::new(123).iid(vec![456,789]).sort(ListingSort::Asc).order_by(ListingOrderBy::CreatedAt).build_query();
        assert_eq!(query, expected_string);
    }
}
<commit_msg>Move merge request's enums and structs to `merge_requests/serde_types.in.rs`<commit_after>//! List merge requests
//!
//! https://docs.gitlab.com/ce/api/merge_requests.html#list-merge-requests
//!
//! # List merge requests
//!
//! Get all merge requests for this project.
//! The `state` parameter can be used to get only merge requests with a given state (`opened`, `closed`, or `merged`) or all of them (`all`).
//! The pagination parameters `page` and `per_page` can be used to restrict the list of merge requests.
//!
//! ```text
//! GET /projects/ID/merge_requests
//! GET /projects/ID/merge_requests?state=opened
//! GET /projects/ID/merge_requests?state=all
//! GET /projects/ID/merge_requests?iid=42
//! GET /projects/ID/merge_requests?iid[]=42&iid[]=43
//! ```
//!
//! Parameters:
//!
//! - `id` (required) - The ID of a project
//! - `iid` (optional) - Return the request having the given `iid`
//! - `state` (optional) - Return `all` requests or just those that are `merged`, `opened` or `closed`
//! - `order_by` (optional) - Return requests ordered by `created_at` or `updated_at` fields. Default is `created_at`
//! - `sort` (optional) - Return requests sorted in `asc` or `desc` order. Default is `desc`
//!
//!

use BuildQuery;

use serde_json;
use serde_urlencoded;

use gitlab::GitLab;
use MergeRequests;

// Types from serde_types.in.rs
use MergeRequestState;

pub mod single;

// Include serializable types
#[cfg(feature = "serde_derive")]
include!("merge_requests/serde_types.in.rs");
#[cfg(feature = "serde_codegen")]
include!(concat!(env!("OUT_DIR"), "/merge_requests/serde_types.rs"));

impl GitLab {
    pub fn merge_requests(&self, listing: Listing) -> Result<MergeRequests, serde_json::Error> {
        let query = listing.build_query();
        self.get(&query)
    }
}

#[allow(dead_code)]
impl Listing {
    pub fn new(id: i64) -> Listing {
        Listing {id: id, ..Default::default()}
    }
    pub fn iid(&mut self, iid: Vec<i64>) -> &mut Listing {
        self.iid = iid;
        self
    }
    pub fn state(&mut self, state: MergeRequestState) -> &mut Listing {
        self.state = Some(state);
        self
    }
    pub fn order_by(&mut self, order_by: ListingOrderBy) -> &mut Listing {
        self.order_by = Some(order_by);
        self
    }
    fn sort(&mut self, sort: ListingSort) -> &mut Listing {
        self.sort = Some(sort);
        self
    }
}

impl BuildQuery for Listing {
    fn build_query(&self) -> String {

        let mut query = format!("projects/{}/merge_requests", self.id);

        let amp_char = "&";
        let none_char = "";
        let mut split_char = &none_char;

        // Append a "?" only if at least one of the `Option` is `Some(_)` or if
        // strings contain something.
        query.push_str(match (self.iid.is_empty(),
                              &self.state,
                              &self.order_by,
                              &self.sort) {
            (true, &None, &None, &None) => "",
            _ => "?",
        });

        if !self.iid.is_empty() {
            query.push_str(split_char);
            split_char = &amp_char;

            if self.iid.len() == 1 {
                query.push_str("iid=");
                query.push_str(&self.iid[0].to_string());
            } else {
                let mut array_split_char = &none_char;
                for iid in &self.iid {
                    query.push_str(array_split_char);
                    query.push_str("iid[]=");
                    query.push_str(&iid.to_string());
                    array_split_char = &amp_char;
                }
            }
        }

        self.state.map(|state| {
            query.push_str(split_char);
            split_char = &amp_char;

            query.push_str("state=");
            query.push_str(match state {
                MergeRequestState::Merged => "merged",
                MergeRequestState::Opened => "opened",
                MergeRequestState::Closed => "closed",
                MergeRequestState::All => "all",
            });
        });

        self.order_by.map(|order_by| {
            query.push_str(split_char);
            split_char = &amp_char;

            query.push_str("order_by=");
            query.push_str(match order_by {
                ListingOrderBy::CreatedAt => "created_at",
                ListingOrderBy::UpdatedAt => "updated_at",
            });
        });

        self.sort.map(|sort| {
            query.push_str(split_char);
            split_char = &amp_char;

            query.push_str("sort=");
            query.push_str(match sort {
                ListingSort::Asc => "asc",
                ListingSort::Desc => "desc",
            });
        });

        query
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use BuildQuery;
    use MergeRequestState;

    #[test]
    fn build_query_default() {
        let expected_string = "projects/123/merge_requests";
        let listing = Listing {id: 123, ..Default::default()};
        let query = listing.build_query();
        assert_eq!(query, expected_string);

        let expected_string = "projects/123/merge_requests";
        let listing = Listing::new(123);
        let query = listing.build_query();
        assert_eq!(query, expected_string);
    }

    // /// Return requests ordered by. Default is `ListingOrderBy::CreatedAt`.
    // order_by: Option<ListingOrderBy>,
    // /// Return requests sorted. Default is `ListingSort::Desc`.
    // sort: Option<ListingSort>,

    #[test]
    fn build_query_iid() {
        let expected_string = format!("projects/123/merge_requests?iid=456");
        let query = Listing::new(123).iid(vec![456]).build_query();
        assert_eq!(query, expected_string);

        let expected_string = format!("projects/123/merge_requests?iid[]=456&iid[]=789");
        let query = Listing::new(123).iid(vec![456,789]).build_query();
        assert_eq!(query, expected_string);
    }

    #[test]
    fn build_query_state() {
        let expected_string = "projects/123/merge_requests?state=opened";
        let query = Listing::new(123).state(MergeRequestState::Opened).build_query();
        assert_eq!(query, expected_string);

        let expected_string = "projects/123/merge_requests?state=closed";
        let query = Listing::new(123).state(MergeRequestState::Closed).build_query();
        assert_eq!(query, expected_string);
    }

    #[test]
    fn build_query_order_by() {
        let expected_string = "projects/123/merge_requests?order_by=created_at";
        let query = Listing::new(123).order_by(ListingOrderBy::CreatedAt).build_query();
        assert_eq!(query, expected_string);

        let expected_string = "projects/123/merge_requests?order_by=updated_at";
        let query = Listing::new(123).order_by(ListingOrderBy::UpdatedAt).build_query();
        assert_eq!(query, expected_string);
    }

    #[test]
    fn build_query_sort() {
        let expected_string = "projects/123/merge_requests?sort=asc";
        let query = Listing::new(123).sort(ListingSort::Asc).build_query();
        assert_eq!(query, expected_string);

        let expected_string = "projects/123/merge_requests?sort=desc";
        let query = Listing::new(123).sort(ListingSort::Desc).build_query();
        assert_eq!(query, expected_string);
    }

    #[test]
    fn build_query_multiple() {
        let expected_string = "projects/123/merge_requests?iid[]=456&iid[]=789&order_by=created_at&sort=asc";
        let query = Listing::new(123).iid(vec![456,789]).sort(ListingSort::Asc).order_by(ListingOrderBy::CreatedAt).build_query();
        assert_eq!(query, expected_string);
    }
}
<|endoftext|>


<commit_before>extern crate telegram_bot;
extern crate tokio_core;
extern crate futures;

use std::env;

use futures::{Stream};
use tokio_core::reactor::Core;
use telegram_bot::{Api, MessageKind, UpdateKind};
use telegram_bot::prelude::*;

fn main() {
    let token = env::var("TELEGRAM_BOT_TOKEN").unwrap();

    let mut core = Core::new().unwrap();
    let api = Api::from_token(&core.handle(), &token).unwrap();

    let future = api.stream().for_each(|update| {
        if let UpdateKind::Message(message) = update.kind {
            if let MessageKind::Text {ref data, ..} = message.kind {
                let text = format!("Got the message: '{}'", data);
                api.spawn(&message.text_reply(text));
            }
        }
        Ok(())
    });

    core.run(future).unwrap();
}
<commit_msg>refactor: delete reply example<commit_after><|endoftext|>


<commit_before>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

//! Functions for detecting the terminal screen size.
//!
//! # OS Support
//!
//! The following operating systems are supported:
//!
//! * Linux
//! * macOS
//! * FreeBSD
//! * Windows

use std::fmt;

/// Represents the width of a terminal.
#[derive(Debug)]
pub struct Width(pub u16);

impl fmt::Display for Width {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let Width(width) = *self;

        try!(write!(f, "{}", width));
        Ok(())
    }
}

/// Represents the height of a terminal.
#[derive(Debug)]
pub struct Height(pub u16);

impl fmt::Display for Height {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let Height(height) = *self;

        try!(write!(f, "{}", height));
        Ok(())
    }
}

#[cfg(unix)]
mod unix;

#[cfg(unix)]
pub use self::unix::size;
#[cfg(unix)]
pub use self::unix::width;
#[cfg(unix)]
pub use self::unix::height;

#[cfg(windows)]
mod windows;

#[cfg(windows)]
pub use self::windows::size;
#[cfg(windows)]
pub use self::windows::width;
#[cfg(windows)]
pub use self::windows::height;

#[cfg(test)]
mod test {
    use super::*;

    // TODO: Test the `fmt::Display` implementations for `Width` and `Height`.
}
<commit_msg>Remove unneeded import<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

//! Functions for detecting the terminal screen size.
//!
//! # OS Support
//!
//! The following operating systems are supported:
//!
//! * Linux
//! * macOS
//! * FreeBSD
//! * Windows

use std::fmt;

/// Represents the width of a terminal.
#[derive(Debug)]
pub struct Width(pub u16);

impl fmt::Display for Width {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let Width(width) = *self;

        try!(write!(f, "{}", width));
        Ok(())
    }
}

/// Represents the height of a terminal.
#[derive(Debug)]
pub struct Height(pub u16);

impl fmt::Display for Height {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let Height(height) = *self;

        try!(write!(f, "{}", height));
        Ok(())
    }
}

#[cfg(unix)]
mod unix;

#[cfg(unix)]
pub use self::unix::size;
#[cfg(unix)]
pub use self::unix::width;
#[cfg(unix)]
pub use self::unix::height;

#[cfg(windows)]
mod windows;

#[cfg(windows)]
pub use self::windows::size;
#[cfg(windows)]
pub use self::windows::width;
#[cfg(windows)]
pub use self::windows::height;

#[cfg(test)]
mod test {
    // TODO: Test the `fmt::Display` implementations for `Width` and `Height`.
}
<|endoftext|>


<commit_before>pub use self::os::{entry};

#[cfg(target_os = "linux")]
#[path = "linux/mod.rs"]
mod os;

pub type ENTRY = entry;

#[repr(C)]
pub enum ACTION {
    FIND,
    ENTER,
}

#[repr(C)]
pub enum VISIT {
    preorder,
    postorder,
    endorder,
    leaf,
}

pub type compar_fn_t = extern fn (*const ::void_t, *const ::void_t) -> ::int_t;
pub type action_fn_t = extern fn (*const ::void_t, VISIT, ::int_t);

extern "C" {
    pub fn hcreate(nel: ::size_t) -> ::int_t;
    pub fn hdestroy();
    pub fn hsearch(item: ENTRY, action: ACTION) -> *mut ENTRY;
    pub fn insque(elem: *mut ::void_t, prev: *mut ::void_t);
    pub fn lfind(key: *const ::void_t, base: *const ::void_t,
                 nmemb: *mut ::size_t, size: ::size_t,
                 compar: compar_fn_t) -> *mut ::void_t;
    pub fn lsearch(key: *const ::void_t, base: *mut ::void_t,
                   nmemb: *mut ::size_t, size: ::size_t,
                   compar: compar_fn_t) -> *mut ::void_t;
    pub fn remque(elem: *mut ::void_t);
    pub fn tdelete(key: *const ::void_t, rootp: *mut *mut ::void_t,
                   compar: compar_fn_t) -> *mut ::void_t;
    pub fn tfind(key: *const ::void_t, rootp: *const *mut ::void_t,
                 compar: compar_fn_t) -> *mut ::void_t;
    pub fn tsearch(key: *const ::void_t, rootp: *mut *mut ::void_t,
                   compar: compar_fn_t) -> *mut ::void_t;
    pub fn twalk(root: *const ::void_t, action: action_fn_t);
}
<commit_msg>search.h<commit_after>pub use self::os::{entry};

use {void_t, int_t, size_t};

#[cfg(target_os = "linux")]
#[path = "linux/mod.rs"]
mod os;

pub type ENTRY = entry;

#[repr(C)]
pub enum ACTION {
    FIND,
    ENTER,
}

#[repr(C)]
pub enum VISIT {
    preorder,
    postorder,
    endorder,
    leaf,
}

pub type compar_fn_t = extern fn (*const void_t, *const void_t) -> int_t;
pub type action_fn_t = extern fn (*const void_t, VISIT, int_t);

extern {
    pub fn hcreate(nel: size_t) -> int_t;
    pub fn hdestroy();
    pub fn hsearch(item: ENTRY, action: ACTION) -> *mut ENTRY;
    pub fn insque(elem: *mut void_t, prev: *mut void_t);
    pub fn lfind(key: *const void_t, base: *const void_t, nmemb: *mut size_t,
                 size: size_t, compar: compar_fn_t) -> *mut void_t;
    pub fn lsearch(key: *const void_t, base: *mut void_t, nmemb: *mut size_t,
                   size: size_t, compar: compar_fn_t) -> *mut void_t;
    pub fn remque(elem: *mut void_t);
    pub fn tdelete(key: *const void_t, rootp: *mut *mut void_t,
                   compar: compar_fn_t) -> *mut void_t;
    pub fn tfind(key: *const void_t, rootp: *const *mut void_t,
                 compar: compar_fn_t) -> *mut void_t;
    pub fn tsearch(key: *const void_t, rootp: *mut *mut void_t,
                   compar: compar_fn_t) -> *mut void_t;
    pub fn twalk(root: *const void_t, action: action_fn_t);
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */

#[cfg(feature = "energy-profiling")]
pub fn read_energy_uj() -> u64 {
    energymon::read_energy_uj()
}

#[cfg(not(feature = "energy-profiling"))]
pub fn read_energy_uj() -> u64 {
    0
}

#[cfg(feature = "energy-profiling")]
pub fn energy_interval_ms() -> u32 {
    energymon::get_min_interval_ms()
}

#[cfg(not(feature = "energy-profiling"))]
pub fn energy_interval_ms() -> u32 {
    1000
}

#[cfg(feature = "energy-profiling")]
mod energymon {
    extern crate energy_monitor;
    extern crate energymon;

    use self::energy_monitor::EnergyMonitor;
    use self::energymon::EnergyMon;
    use std::mem;
    use std::sync::{Once, ONCE_INIT};

    static mut EM: Option<*mut EnergyMon> = None;

    fn init() {
        // can't use lazy_static macro for EM (no Sync trait for EnergyMon)
        static ONCE: Once = ONCE_INIT;
        ONCE.call_once(|| {
            if let Ok(em) = EnergyMon::new() {
                println!("Started energy monitoring from: {}", em.source());
                unsafe {
                    EM = Some(mem::transmute(Box::new(em)));
                }
            }
        });
    }

    /// Read energy from the energy monitor, otherwise return 0.
    pub fn read_energy_uj() -> u64 {
        init();
        unsafe {
            // EnergyMon implementations of EnergyMonitor always return a value
            EM.map_or(0, |em| (*em).read_uj().unwrap())
        }
    }

    pub fn get_min_interval_ms() -> u32 {
        init();
        unsafe { EM.map_or(0, |em| ((*em).interval_us() as f64 / 1000.0).ceil() as u32) }
    }
}
<commit_msg>Fix undefined behavior in `energymon::init`<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */

#[cfg(feature = "energy-profiling")]
pub fn read_energy_uj() -> u64 {
    energymon::read_energy_uj()
}

#[cfg(not(feature = "energy-profiling"))]
pub fn read_energy_uj() -> u64 {
    0
}

#[cfg(feature = "energy-profiling")]
pub fn energy_interval_ms() -> u32 {
    energymon::get_min_interval_ms()
}

#[cfg(not(feature = "energy-profiling"))]
pub fn energy_interval_ms() -> u32 {
    1000
}

#[cfg(feature = "energy-profiling")]
mod energymon {
    extern crate energy_monitor;
    extern crate energymon;

    use self::energy_monitor::EnergyMonitor;
    use self::energymon::EnergyMon;
    use std::sync::{Once, ONCE_INIT};

    static mut EM: Option<*mut EnergyMon> = None;

    fn init() {
        // can't use lazy_static macro for EM (no Sync trait for EnergyMon)
        static ONCE: Once = ONCE_INIT;
        ONCE.call_once(|| {
            if let Ok(em) = EnergyMon::new() {
                println!("Started energy monitoring from: {}", em.source());
                unsafe {
                    EM = Some(Box::into_raw(Box::new(em)));
                }
            }
        });
    }

    /// Read energy from the energy monitor, otherwise return 0.
    pub fn read_energy_uj() -> u64 {
        init();
        unsafe {
            // EnergyMon implementations of EnergyMonitor always return a value
            EM.map_or(0, |em| (*em).read_uj().unwrap())
        }
    }

    pub fn get_min_interval_ms() -> u32 {
        init();
        unsafe { EM.map_or(0, |em| ((*em).interval_us() as f64 / 1000.0).ceil() as u32) }
    }
}
<|endoftext|>


<commit_before>#![cfg(all(unix, feature = "io_uring"))]

use libc::off_t;
use std::convert::TryFrom;
use std::fs::File;
use std::io;
use std::mem;
use std::os::unix::io::AsRawFd;
use std::sync::Arc;

use crate::LogOffset;
use crate::{Error, Result};

use liburing::*;

/// TODO: support for SQPOLL with idle timeout
pub(crate) struct Uring<T> {
    /// Mutable unsafe FFI struct from liburing::
    ring: io_uring,

    /// File
    file: Arc<File>,

    /// IOVec structures that hold information about buffers
    /// used in SQEs.
    iovecs: Vec<libc::iovec>,

    /// iovec buffs. this is specifically needed to preserve buf
    /// ptr lifetime because all write operations are async.
    buf: Vec<Option<Arc<T>>>,

    /// List of free slots: iovecs, user data, etc.
    free_slots: Vec<usize>,
}

/// Pointers can't be passed safely through threads boundaries.
/// `Uring` must enforce it, yet it is not Sync.
#[allow(unsafe_code)]
unsafe impl<T> Send for Uring<T> {}

impl<T> Uring<T> {
    /// Create and initialize new `io_uring` structure with
    /// `size` queue length and `flags` specified properties.
    ///
    /// Available flags from io_uring.h:
    ///     - `IORING_SETUP_IOPOLL`	(1U << 0)	/* io_context is polled */
    ///     - `IORING_SETUP_SQPOLL`	(1U << 1)	/* SQ poll thread */
    ///     - `IORING_SETUP_SQ_AFF`	(1U << 2)	/* sq_thread_cpu is valid */
    ///     - `IORING_SETUP_CQSIZE`	(1U << 3)	/* app defines CQ size */
    pub fn new(
        file: Arc<File>,
        size: usize,
        flags: libc::c_uint,
    ) -> Result<Self> {
        if size & 1 != 0 || size < 2 {
            return Err(Error::Unsupported("invalid queue size".into()));
        }

        let ring = unsafe {
            let mut s = mem::MaybeUninit::<io_uring>::uninit();
            let ret = io_uring_queue_init(
                u32::try_from(size).unwrap(),
                s.as_mut_ptr(),
                flags,
            );
            if ret < 0 {
                return Err(Error::Io(io::Error::from_raw_os_error(ret)));
            }

            s.assume_init()
        };

        let mut uring = Uring::<T> {
            ring,
            file,
            iovecs: Vec::with_capacity(size),
            buf: Vec::with_capacity(size),
            free_slots: Vec::with_capacity(size),
        };

        unsafe {
            let ret = io_uring_register_files(
                &mut uring.ring,
                [uring.file.as_raw_fd()].as_ptr(),
                1,
            );
            if ret < 0 {
                return Err(Error::Io(io::Error::from_raw_os_error(ret)));
            }
        };

        uring.iovecs.resize(
            size,
            libc::iovec { iov_base: std::ptr::null_mut(), iov_len: 0 },
        );
        uring.buf.resize(size, None);

        // index free elements
        for i in 0..size {
            uring.free_slots.push(i);
        }

        Ok(uring)
    }

    unsafe fn drain_cqe(&mut self) -> Result<()> {
        loop {
            let mut cqe: *mut io_uring_cqe = std::ptr::null_mut();
            let ret = io_uring_peek_cqe(&mut self.ring, &mut cqe);
            if ret == -libc::EAGAIN {
                // peek found nothing, no completions to drain
                return Ok(());
            }
            if ret < 0 {
                return Err(Error::Io(io::Error::from_raw_os_error(ret)));
            }
            let i = usize::try_from((*cqe).user_data).unwrap();
            if i < self.free_slots.capacity() {
                self.free_slots.push(i);
                // release
                self.iovecs[i].iov_base = std::ptr::null_mut();
                self.iovecs[i].iov_len = 0;
                // drop arc buf
                self.buf[i] = None;
            }
            io_uring_cqe_seen(&mut self.ring, cqe);
        }
    }

    pub fn pwrite_all(
        &mut self,
        buf: &mut [u8],
        data: Arc<T>,
        offset: LogOffset,
        fsync: bool,
    ) -> Result<()> {
        unsafe {
            // 1. drain completed operations
            self.drain_cqe()?;

            // it is easier to track queue len if num
            // of iovecs will be the same as sqes
            let mut reserve = 1;
            if fsync {
                reserve += 1;
            }

            // 2. reserve SQE slots
            if self.free_slots.len() < reserve {
                let ret = io_uring_submit_and_wait(
                    &mut self.ring,
                    libc::c_uint::try_from(reserve).unwrap(),
                );
                if ret < 0 {
                    return Err(Error::Io(io::Error::from_raw_os_error(ret)));
                }
                self.drain_cqe()?;
                if self.free_slots.len() < reserve {
                    return Err(Error::Io(io::Error::new(
                        io::ErrorKind::Other,
                        "wait for free sqes",
                    )));
                }
            }

            // 3. build write() SQE
            {
                let sqe = io_uring_get_sqe(&mut self.ring);
                if sqe.is_null() {
                    return Err(Error::Io(io::Error::new(
                        io::ErrorKind::Other,
                        "unexpected lack of sqes",
                    )));
                }

                let i = self.free_slots.pop().unwrap();
                self.buf[i] = Some(data);
                self.iovecs[i].iov_base =
                    buf.as_mut_ptr() as *mut std::ffi::c_void;
                self.iovecs[i].iov_len = buf.len();

                io_uring_prep_writev(
                    sqe,
                    self.file.as_raw_fd(),
                    &self.iovecs[i],
                    1,
                    off_t::try_from(offset).unwrap(),
                );
                (*sqe).user_data = u64::try_from(i).unwrap();
            }

            // 4. build fsync() SQE
            if fsync {
                let sqe = io_uring_get_sqe(&mut self.ring);
                if sqe.is_null() {
                    return Err(Error::Io(io::Error::new(
                        io::ErrorKind::Other,
                        "unexpected lack of sqes",
                    )));
                }

                let i = self.free_slots.pop().unwrap();

                io_uring_prep_fsync(
                    sqe,
                    self.file.as_raw_fd(),
                    IORING_FSYNC_DATASYNC,
                );
                io_uring_sqe_set_flags(sqe, IOSQE_IO_DRAIN);
                (*sqe).user_data = u64::try_from(i).unwrap();
            }

            // 5. submit
            let ret = io_uring_submit(&mut self.ring);
            if ret < 0 {
                return Err(Error::Io(io::Error::from_raw_os_error(ret)));
            }
        };

        Ok(())
    }
}

impl<T> Drop for Uring<T> {
    fn drop(&mut self) {
        unsafe {
            // flush unfinished ops
            if self.free_slots.len() < self.iovecs.len() {
                let ret = io_uring_submit_and_wait(
                    &mut self.ring,
                    u32::try_from(self.iovecs.len() - self.free_slots.len())
                        .unwrap(),
                );
                if ret < 0 {
                    panic!(Error::Io(io::Error::from_raw_os_error(ret)));
                }
            }

            self.drain_cqe().unwrap();

            io_uring_unregister_files(&mut self.ring);
            io_uring_queue_exit(&mut self.ring);
        };
    }
}
<commit_msg>Properly document constants and module names<commit_after>#![cfg(all(unix, feature = "io_uring"))]

use libc::off_t;
use std::convert::TryFrom;
use std::fs::File;
use std::io;
use std::mem;
use std::os::unix::io::AsRawFd;
use std::sync::Arc;

use crate::LogOffset;
use crate::{Error, Result};

use liburing::*;

/// TODO: support for SQPOLL with idle timeout
pub(crate) struct Uring<T> {
    /// Mutable unsafe FFI struct from liburing::
    ring: io_uring,

    /// File
    file: Arc<File>,

    /// IOVec structures that hold information about buffers
    /// used in SQEs.
    iovecs: Vec<libc::iovec>,

    /// iovec buffs. this is specifically needed to preserve buf
    /// ptr lifetime because all write operations are async.
    buf: Vec<Option<Arc<T>>>,

    /// List of free slots: iovecs, user data, etc.
    free_slots: Vec<usize>,
}

/// Pointers can't be passed safely through threads boundaries.
/// `Uring` must enforce it, yet it is not Sync.
#[allow(unsafe_code)]
unsafe impl<T> Send for Uring<T> {}

impl<T> Uring<T> {
    /// Create and initialize new `io_uring` structure with
    /// `size` queue length and `flags` specified properties.
    ///
    /// Available flags from `io_uring.h`:
    ///     - `IORING_SETUP_IOPOLL`	(1U << 0)	/* `io_context` is polled */
    ///     - `IORING_SETUP_SQPOLL`	(1U << 1)	/* SQ poll thread */
    ///     - `IORING_SETUP_SQ_AFF`	(1U << 2)	/* `sq_thread_cpu` is valid */
    ///     - `IORING_SETUP_CQSIZE`	(1U << 3)	/* app defines CQ size */
    pub fn new(
        file: Arc<File>,
        size: usize,
        flags: libc::c_uint,
    ) -> Result<Self> {
        if size & 1 != 0 || size < 2 {
            return Err(Error::Unsupported("invalid queue size".into()));
        }

        let ring = unsafe {
            let mut s = mem::MaybeUninit::<io_uring>::uninit();
            let ret = io_uring_queue_init(
                u32::try_from(size).unwrap(),
                s.as_mut_ptr(),
                flags,
            );
            if ret < 0 {
                return Err(Error::Io(io::Error::from_raw_os_error(ret)));
            }

            s.assume_init()
        };

        let mut uring = Uring::<T> {
            ring,
            file,
            iovecs: Vec::with_capacity(size),
            buf: Vec::with_capacity(size),
            free_slots: Vec::with_capacity(size),
        };

        unsafe {
            let ret = io_uring_register_files(
                &mut uring.ring,
                [uring.file.as_raw_fd()].as_ptr(),
                1,
            );
            if ret < 0 {
                return Err(Error::Io(io::Error::from_raw_os_error(ret)));
            }
        };

        uring.iovecs.resize(
            size,
            libc::iovec { iov_base: std::ptr::null_mut(), iov_len: 0 },
        );
        uring.buf.resize(size, None);

        // index free elements
        for i in 0..size {
            uring.free_slots.push(i);
        }

        Ok(uring)
    }

    unsafe fn drain_cqe(&mut self) -> Result<()> {
        loop {
            let mut cqe: *mut io_uring_cqe = std::ptr::null_mut();
            let ret = io_uring_peek_cqe(&mut self.ring, &mut cqe);
            if ret == -libc::EAGAIN {
                // peek found nothing, no completions to drain
                return Ok(());
            }
            if ret < 0 {
                return Err(Error::Io(io::Error::from_raw_os_error(ret)));
            }
            let i = usize::try_from((*cqe).user_data).unwrap();
            if i < self.free_slots.capacity() {
                self.free_slots.push(i);
                // release
                self.iovecs[i].iov_base = std::ptr::null_mut();
                self.iovecs[i].iov_len = 0;
                // drop arc buf
                self.buf[i] = None;
            }
            io_uring_cqe_seen(&mut self.ring, cqe);
        }
    }

    pub fn pwrite_all(
        &mut self,
        buf: &mut [u8],
        data: Arc<T>,
        offset: LogOffset,
        fsync: bool,
    ) -> Result<()> {
        unsafe {
            // 1. drain completed operations
            self.drain_cqe()?;

            // it is easier to track queue len if num
            // of iovecs will be the same as sqes
            let mut reserve = 1;
            if fsync {
                reserve += 1;
            }

            // 2. reserve SQE slots
            if self.free_slots.len() < reserve {
                let ret = io_uring_submit_and_wait(
                    &mut self.ring,
                    libc::c_uint::try_from(reserve).unwrap(),
                );
                if ret < 0 {
                    return Err(Error::Io(io::Error::from_raw_os_error(ret)));
                }
                self.drain_cqe()?;
                if self.free_slots.len() < reserve {
                    return Err(Error::Io(io::Error::new(
                        io::ErrorKind::Other,
                        "wait for free sqes",
                    )));
                }
            }

            // 3. build write() SQE
            {
                let sqe = io_uring_get_sqe(&mut self.ring);
                if sqe.is_null() {
                    return Err(Error::Io(io::Error::new(
                        io::ErrorKind::Other,
                        "unexpected lack of sqes",
                    )));
                }

                let i = self.free_slots.pop().unwrap();
                self.buf[i] = Some(data);
                self.iovecs[i].iov_base =
                    buf.as_mut_ptr() as *mut std::ffi::c_void;
                self.iovecs[i].iov_len = buf.len();

                io_uring_prep_writev(
                    sqe,
                    self.file.as_raw_fd(),
                    &self.iovecs[i],
                    1,
                    off_t::try_from(offset).unwrap(),
                );
                (*sqe).user_data = u64::try_from(i).unwrap();
            }

            // 4. build fsync() SQE
            if fsync {
                let sqe = io_uring_get_sqe(&mut self.ring);
                if sqe.is_null() {
                    return Err(Error::Io(io::Error::new(
                        io::ErrorKind::Other,
                        "unexpected lack of sqes",
                    )));
                }

                let i = self.free_slots.pop().unwrap();

                io_uring_prep_fsync(
                    sqe,
                    self.file.as_raw_fd(),
                    IORING_FSYNC_DATASYNC,
                );
                io_uring_sqe_set_flags(sqe, IOSQE_IO_DRAIN);
                (*sqe).user_data = u64::try_from(i).unwrap();
            }

            // 5. submit
            let ret = io_uring_submit(&mut self.ring);
            if ret < 0 {
                return Err(Error::Io(io::Error::from_raw_os_error(ret)));
            }
        };

        Ok(())
    }
}

impl<T> Drop for Uring<T> {
    fn drop(&mut self) {
        unsafe {
            // flush unfinished ops
            if self.free_slots.len() < self.iovecs.len() {
                let ret = io_uring_submit_and_wait(
                    &mut self.ring,
                    u32::try_from(self.iovecs.len() - self.free_slots.len())
                        .unwrap(),
                );
                if ret < 0 {
                    panic!(Error::Io(io::Error::from_raw_os_error(ret)));
                }
            }

            self.drain_cqe().unwrap();

            io_uring_unregister_files(&mut self.ring);
            io_uring_queue_exit(&mut self.ring);
        };
    }
}
<|endoftext|>


<commit_before>// Std
use std::ffi::OsString;
use std::mem;
use std::ops::Deref;

// Internal
use crate::builder::{Arg, ArgPredicate, Command};
use crate::parser::AnyValue;
use crate::parser::Identifier;
use crate::parser::PendingArg;
use crate::parser::{ArgMatches, MatchedArg, SubCommand, ValueSource};
use crate::util::FlatMap;
use crate::util::Id;
use crate::INTERNAL_ERROR_MSG;

#[derive(Debug, Default)]
pub(crate) struct ArgMatcher {
    matches: ArgMatches,
    pending: Option<PendingArg>,
}

impl ArgMatcher {
    pub(crate) fn new(_cmd: &Command) -> Self {
        ArgMatcher {
            matches: ArgMatches {
                #[cfg(debug_assertions)]
                valid_args: {
                    let args = _cmd.get_arguments().map(|a| a.get_id().clone());
                    let groups = _cmd.get_groups().map(|g| g.get_id().clone());
                    args.chain(groups).collect()
                },
                #[cfg(debug_assertions)]
                valid_subcommands: _cmd
                    .get_subcommands()
                    .map(|sc| sc.get_name_str().clone())
                    .collect(),
                ..Default::default()
            },
            pending: None,
        }
    }

    pub(crate) fn into_inner(self) -> ArgMatches {
        self.matches
    }

    pub(crate) fn propagate_globals(&mut self, global_arg_vec: &[Id]) {
        debug!(
            "ArgMatcher::get_global_values: global_arg_vec={:?}",
            global_arg_vec
        );
        let mut vals_map = FlatMap::new();
        self.fill_in_global_values(global_arg_vec, &mut vals_map);
    }

    fn fill_in_global_values(
        &mut self,
        global_arg_vec: &[Id],
        vals_map: &mut FlatMap<Id, MatchedArg>,
    ) {
        for global_arg in global_arg_vec {
            if let Some(ma) = self.get(global_arg) {
                // We have to check if the parent's global arg wasn't used but still exists
                // such as from a default value.
                //
                // For example, `myprog subcommand --global-arg=value` where `--global-arg` defines
                // a default value of `other` myprog would have an existing MatchedArg for
                // `--global-arg` where the value is `other`
                let to_update = if let Some(parent_ma) = vals_map.get(global_arg) {
                    if parent_ma.source() > ma.source() {
                        parent_ma
                    } else {
                        ma
                    }
                } else {
                    ma
                }
                .clone();
                vals_map.insert(global_arg.clone(), to_update);
            }
        }
        if let Some(ref mut sc) = self.matches.subcommand {
            let mut am = ArgMatcher {
                matches: mem::take(&mut sc.matches),
                pending: None,
            };
            am.fill_in_global_values(global_arg_vec, vals_map);
            mem::swap(&mut am.matches, &mut sc.matches);
        }

        for (name, matched_arg) in vals_map.iter_mut() {
            self.matches.args.insert(name.clone(), matched_arg.clone());
        }
    }

    pub(crate) fn get(&self, arg: &Id) -> Option<&MatchedArg> {
        self.matches.args.get(arg)
    }

    pub(crate) fn get_mut(&mut self, arg: &Id) -> Option<&mut MatchedArg> {
        self.matches.args.get_mut(arg)
    }

    pub(crate) fn remove(&mut self, arg: &Id) -> bool {
        self.matches.args.remove(arg).is_some()
    }

    pub(crate) fn contains(&self, arg: &Id) -> bool {
        self.matches.args.contains_key(arg)
    }

    pub(crate) fn arg_ids(&self) -> std::slice::Iter<'_, Id> {
        self.matches.args.keys()
    }

    pub(crate) fn entry(&mut self, arg: Id) -> crate::util::Entry<Id, MatchedArg> {
        self.matches.args.entry(arg)
    }

    pub(crate) fn subcommand(&mut self, sc: SubCommand) {
        self.matches.subcommand = Some(Box::new(sc));
    }

    pub(crate) fn subcommand_name(&self) -> Option<&str> {
        self.matches.subcommand_name()
    }

    pub(crate) fn check_explicit(&self, arg: &Id, predicate: &ArgPredicate) -> bool {
        self.get(arg).map_or(false, |a| a.check_explicit(predicate))
    }

    pub(crate) fn start_custom_arg(&mut self, arg: &Arg, source: ValueSource) {
        let id = arg.get_id().clone();
        debug!(
            "ArgMatcher::start_custom_arg: id={:?}, source={:?}",
            id, source
        );
        let ma = self.entry(id).or_insert(MatchedArg::new_arg(arg));
        debug_assert_eq!(ma.type_id(), Some(arg.get_value_parser().type_id()));
        ma.set_source(source);
        ma.new_val_group();
    }

    pub(crate) fn start_custom_group(&mut self, id: Id, source: ValueSource) {
        debug!(
            "ArgMatcher::start_custom_arg: id={:?}, source={:?}",
            id, source
        );
        let ma = self.entry(id).or_insert(MatchedArg::new_group());
        debug_assert_eq!(ma.type_id(), None);
        ma.set_source(source);
        ma.new_val_group();
    }

    pub(crate) fn start_occurrence_of_arg(&mut self, arg: &Arg) {
        let id = arg.get_id().clone();
        debug!("ArgMatcher::start_occurrence_of_arg: id={:?}", id);
        let ma = self.entry(id).or_insert(MatchedArg::new_arg(arg));
        debug_assert_eq!(ma.type_id(), Some(arg.get_value_parser().type_id()));
        ma.set_source(ValueSource::CommandLine);
        ma.new_val_group();
    }

    pub(crate) fn start_occurrence_of_group(&mut self, id: Id) {
        debug!("ArgMatcher::start_occurrence_of_group: id={:?}", id);
        let ma = self.entry(id).or_insert(MatchedArg::new_group());
        debug_assert_eq!(ma.type_id(), None);
        ma.set_source(ValueSource::CommandLine);
        ma.new_val_group();
    }

    pub(crate) fn start_occurrence_of_external(&mut self, cmd: &crate::Command) {
        let id = Id::from_static_ref(Id::EXTERNAL);
        debug!("ArgMatcher::start_occurrence_of_external: id={:?}", id,);
        let ma = self.entry(id).or_insert(MatchedArg::new_external(cmd));
        debug_assert_eq!(
            ma.type_id(),
            Some(
                cmd.get_external_subcommand_value_parser()
                    .expect(INTERNAL_ERROR_MSG)
                    .type_id()
            )
        );
        ma.set_source(ValueSource::CommandLine);
        ma.new_val_group();
    }

    pub(crate) fn add_val_to(&mut self, arg: &Id, val: AnyValue, raw_val: OsString) {
        let ma = self.get_mut(arg).expect(INTERNAL_ERROR_MSG);
        ma.append_val(val, raw_val);
    }

    pub(crate) fn add_index_to(&mut self, arg: &Id, idx: usize) {
        let ma = self.get_mut(arg).expect(INTERNAL_ERROR_MSG);
        ma.push_index(idx);
    }

    pub(crate) fn needs_more_vals(&self, o: &Arg) -> bool {
        let num_pending = self
            .pending
            .as_ref()
            .and_then(|p| (p.id == *o.get_id()).then(|| p.raw_vals.len()))
            .unwrap_or(0);
        debug!(
            "ArgMatcher::needs_more_vals: o={}, pending={}",
            o.get_id(),
            num_pending
        );
        let expected = o.get_num_args().expect(INTERNAL_ERROR_MSG);
        debug!(
            "ArgMatcher::needs_more_vals: expected={}, actual={}",
            expected, num_pending
        );
        expected.accepts_more(num_pending)
    }

    pub(crate) fn pending_arg_id(&self) -> Option<&Id> {
        self.pending.as_ref().map(|p| &p.id)
    }

    pub(crate) fn pending_values_mut(
        &mut self,
        id: &Id,
        ident: Option<Identifier>,
        trailing_values: bool,
    ) -> &mut Vec<OsString> {
        let pending = self.pending.get_or_insert_with(|| PendingArg {
            id: id.clone(),
            ident,
            raw_vals: Default::default(),
            trailing_idx: None,
        });
        debug_assert_eq!(pending.id, *id, "{}", INTERNAL_ERROR_MSG);
        if ident.is_some() {
            debug_assert_eq!(pending.ident, ident, "{}", INTERNAL_ERROR_MSG);
        }
        if trailing_values {
            pending.trailing_idx.get_or_insert(pending.raw_vals.len());
        }
        &mut pending.raw_vals
    }

    pub(crate) fn start_trailing(&mut self) {
        if let Some(pending) = &mut self.pending {
            // Allow asserting its started on subsequent calls
            pending.trailing_idx.get_or_insert(pending.raw_vals.len());
        }
    }

    pub(crate) fn take_pending(&mut self) -> Option<PendingArg> {
        self.pending.take()
    }
}

impl Deref for ArgMatcher {
    type Target = ArgMatches;

    fn deref(&self) -> &Self::Target {
        &self.matches
    }
}
<commit_msg>refactor(parser): Reduce code duplication<commit_after>// Std
use std::ffi::OsString;
use std::mem;
use std::ops::Deref;

// Internal
use crate::builder::{Arg, ArgPredicate, Command};
use crate::parser::AnyValue;
use crate::parser::Identifier;
use crate::parser::PendingArg;
use crate::parser::{ArgMatches, MatchedArg, SubCommand, ValueSource};
use crate::util::FlatMap;
use crate::util::Id;
use crate::INTERNAL_ERROR_MSG;

#[derive(Debug, Default)]
pub(crate) struct ArgMatcher {
    matches: ArgMatches,
    pending: Option<PendingArg>,
}

impl ArgMatcher {
    pub(crate) fn new(_cmd: &Command) -> Self {
        ArgMatcher {
            matches: ArgMatches {
                #[cfg(debug_assertions)]
                valid_args: {
                    let args = _cmd.get_arguments().map(|a| a.get_id().clone());
                    let groups = _cmd.get_groups().map(|g| g.get_id().clone());
                    args.chain(groups).collect()
                },
                #[cfg(debug_assertions)]
                valid_subcommands: _cmd
                    .get_subcommands()
                    .map(|sc| sc.get_name_str().clone())
                    .collect(),
                ..Default::default()
            },
            pending: None,
        }
    }

    pub(crate) fn into_inner(self) -> ArgMatches {
        self.matches
    }

    pub(crate) fn propagate_globals(&mut self, global_arg_vec: &[Id]) {
        debug!(
            "ArgMatcher::get_global_values: global_arg_vec={:?}",
            global_arg_vec
        );
        let mut vals_map = FlatMap::new();
        self.fill_in_global_values(global_arg_vec, &mut vals_map);
    }

    fn fill_in_global_values(
        &mut self,
        global_arg_vec: &[Id],
        vals_map: &mut FlatMap<Id, MatchedArg>,
    ) {
        for global_arg in global_arg_vec {
            if let Some(ma) = self.get(global_arg) {
                // We have to check if the parent's global arg wasn't used but still exists
                // such as from a default value.
                //
                // For example, `myprog subcommand --global-arg=value` where `--global-arg` defines
                // a default value of `other` myprog would have an existing MatchedArg for
                // `--global-arg` where the value is `other`
                let to_update = if let Some(parent_ma) = vals_map.get(global_arg) {
                    if parent_ma.source() > ma.source() {
                        parent_ma
                    } else {
                        ma
                    }
                } else {
                    ma
                }
                .clone();
                vals_map.insert(global_arg.clone(), to_update);
            }
        }
        if let Some(ref mut sc) = self.matches.subcommand {
            let mut am = ArgMatcher {
                matches: mem::take(&mut sc.matches),
                pending: None,
            };
            am.fill_in_global_values(global_arg_vec, vals_map);
            mem::swap(&mut am.matches, &mut sc.matches);
        }

        for (name, matched_arg) in vals_map.iter_mut() {
            self.matches.args.insert(name.clone(), matched_arg.clone());
        }
    }

    pub(crate) fn get(&self, arg: &Id) -> Option<&MatchedArg> {
        self.matches.args.get(arg)
    }

    pub(crate) fn get_mut(&mut self, arg: &Id) -> Option<&mut MatchedArg> {
        self.matches.args.get_mut(arg)
    }

    pub(crate) fn remove(&mut self, arg: &Id) -> bool {
        self.matches.args.remove(arg).is_some()
    }

    pub(crate) fn contains(&self, arg: &Id) -> bool {
        self.matches.args.contains_key(arg)
    }

    pub(crate) fn arg_ids(&self) -> std::slice::Iter<'_, Id> {
        self.matches.args.keys()
    }

    pub(crate) fn entry(&mut self, arg: Id) -> crate::util::Entry<Id, MatchedArg> {
        self.matches.args.entry(arg)
    }

    pub(crate) fn subcommand(&mut self, sc: SubCommand) {
        self.matches.subcommand = Some(Box::new(sc));
    }

    pub(crate) fn subcommand_name(&self) -> Option<&str> {
        self.matches.subcommand_name()
    }

    pub(crate) fn check_explicit(&self, arg: &Id, predicate: &ArgPredicate) -> bool {
        self.get(arg).map_or(false, |a| a.check_explicit(predicate))
    }

    pub(crate) fn start_custom_arg(&mut self, arg: &Arg, source: ValueSource) {
        let id = arg.get_id().clone();
        debug!(
            "ArgMatcher::start_custom_arg: id={:?}, source={:?}",
            id, source
        );
        let ma = self.entry(id).or_insert(MatchedArg::new_arg(arg));
        debug_assert_eq!(ma.type_id(), Some(arg.get_value_parser().type_id()));
        ma.set_source(source);
        ma.new_val_group();
    }

    pub(crate) fn start_custom_group(&mut self, id: Id, source: ValueSource) {
        debug!(
            "ArgMatcher::start_custom_arg: id={:?}, source={:?}",
            id, source
        );
        let ma = self.entry(id).or_insert(MatchedArg::new_group());
        debug_assert_eq!(ma.type_id(), None);
        ma.set_source(source);
        ma.new_val_group();
    }

    pub(crate) fn start_occurrence_of_arg(&mut self, arg: &Arg) {
        self.start_custom_arg(arg, ValueSource::CommandLine);
    }

    pub(crate) fn start_occurrence_of_group(&mut self, id: Id) {
        self.start_custom_group(id, ValueSource::CommandLine);
    }

    pub(crate) fn start_occurrence_of_external(&mut self, cmd: &crate::Command) {
        let id = Id::from_static_ref(Id::EXTERNAL);
        debug!("ArgMatcher::start_occurrence_of_external: id={:?}", id,);
        let ma = self.entry(id).or_insert(MatchedArg::new_external(cmd));
        debug_assert_eq!(
            ma.type_id(),
            Some(
                cmd.get_external_subcommand_value_parser()
                    .expect(INTERNAL_ERROR_MSG)
                    .type_id()
            )
        );
        ma.set_source(ValueSource::CommandLine);
        ma.new_val_group();
    }

    pub(crate) fn add_val_to(&mut self, arg: &Id, val: AnyValue, raw_val: OsString) {
        let ma = self.get_mut(arg).expect(INTERNAL_ERROR_MSG);
        ma.append_val(val, raw_val);
    }

    pub(crate) fn add_index_to(&mut self, arg: &Id, idx: usize) {
        let ma = self.get_mut(arg).expect(INTERNAL_ERROR_MSG);
        ma.push_index(idx);
    }

    pub(crate) fn needs_more_vals(&self, o: &Arg) -> bool {
        let num_pending = self
            .pending
            .as_ref()
            .and_then(|p| (p.id == *o.get_id()).then(|| p.raw_vals.len()))
            .unwrap_or(0);
        debug!(
            "ArgMatcher::needs_more_vals: o={}, pending={}",
            o.get_id(),
            num_pending
        );
        let expected = o.get_num_args().expect(INTERNAL_ERROR_MSG);
        debug!(
            "ArgMatcher::needs_more_vals: expected={}, actual={}",
            expected, num_pending
        );
        expected.accepts_more(num_pending)
    }

    pub(crate) fn pending_arg_id(&self) -> Option<&Id> {
        self.pending.as_ref().map(|p| &p.id)
    }

    pub(crate) fn pending_values_mut(
        &mut self,
        id: &Id,
        ident: Option<Identifier>,
        trailing_values: bool,
    ) -> &mut Vec<OsString> {
        let pending = self.pending.get_or_insert_with(|| PendingArg {
            id: id.clone(),
            ident,
            raw_vals: Default::default(),
            trailing_idx: None,
        });
        debug_assert_eq!(pending.id, *id, "{}", INTERNAL_ERROR_MSG);
        if ident.is_some() {
            debug_assert_eq!(pending.ident, ident, "{}", INTERNAL_ERROR_MSG);
        }
        if trailing_values {
            pending.trailing_idx.get_or_insert(pending.raw_vals.len());
        }
        &mut pending.raw_vals
    }

    pub(crate) fn start_trailing(&mut self) {
        if let Some(pending) = &mut self.pending {
            // Allow asserting its started on subsequent calls
            pending.trailing_idx.get_or_insert(pending.raw_vals.len());
        }
    }

    pub(crate) fn take_pending(&mut self) -> Option<PendingArg> {
        self.pending.take()
    }
}

impl Deref for ArgMatcher {
    type Target = ArgMatches;

    fn deref(&self) -> &Self::Target {
        &self.matches
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2016 Chef Software Inc. and/or applicable contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

pub mod create {
    use std::fs::create_dir_all;
    use std::fs::File;
    use std::io::Write;
    use std::path::Path;
    use std::collections::HashMap;

    use handlebars::Handlebars;

    use common::ui::{UI, Status};
    use error::Result;

    const PLAN_TEMPLATE: &'static str = include_str!("../../static/template_plan.sh");
    const RUN_HOOK_TEMPLATE: &'static str = include_str!("../../static/template_run");
    const INIT_HOOK_TEMPLATE: &'static str = include_str!("../../static/template_init");

    pub fn start(ui: &mut UI, origin: String, name: String) -> Result<()> {
        try!(ui.begin("Constructing a cozy habitat for your app..."));
        try!(ui.br());

        // Build out the variables passed.
        let handlebars = Handlebars::new();
        let mut data = HashMap::new();
        data.insert("pkg_name".to_string(), name);
        data.insert("pkg_origin".to_string(), origin);

        // Unlike hooks we want to render the configured variables to the `plan.sh`
        let rendered_plan = try!(handlebars.template_render(PLAN_TEMPLATE, &data));
        try!(create_with_template(ui, "habitat/plan.sh", &rendered_plan));
        try!(ui.para("The `plan.sh` is the foundation of your new habitat. You can \
            define core metadata, dependencies, and tasks. More documentation here: \
            https://www.habitat.sh/docs/reference/plan-syntax/"));

        try!(create_with_template(ui, "habitat/hooks/init", INIT_HOOK_TEMPLATE));
        try!(create_with_template(ui, "habitat/hooks/run", RUN_HOOK_TEMPLATE));
        try!(ui.para("The `hooks` directory is where you can create a number of automation hooks \
            into your habitat. We'll make an `init` and a `run` hook to get you started, but there \
            are more hooks to create and tweak! See the full list with info here: \
            https://www.habitat.sh/docs/reference/plan-syntax/#hooks"));

        try!(ui.end("A happy abode for your code has been initialized! Now it's time to explore!"));
        Ok(())
    }

    fn create_with_template(ui: &mut UI, location: &str, template:  &str) -> Result<()> {
        let path = Path::new(&location);
        match path.exists() {
            false => {
                try!(ui.status(Status::Creating, format!("file: {}", location)));
                // If the directory doesn't exist we need to make it.
                if let Some(directory) = path.parent() {
                    try!(create_dir_all(directory));
                }
                // Create and then render the template with Handlebars
                try!(File::create(path).and_then(|mut file| file.write(template.as_bytes())));
            },
            true => {
                // If the user has already configured a file overwriting would be impolite.
                try!(ui.status(Status::Using, format!("existing file: {}", location)));
            }
        };
        Ok(())
    }
}
<commit_msg>Use `pkg_name` as folder name instead of habitat.<commit_after>// Copyright (c) 2016 Chef Software Inc. and/or applicable contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

pub mod create {
    use std::fs::create_dir_all;
    use std::fs::File;
    use std::io::Write;
    use std::path::Path;
    use std::collections::HashMap;

    use handlebars::Handlebars;

    use common::ui::{UI, Status};
    use error::Result;

    const PLAN_TEMPLATE: &'static str = include_str!("../../static/template_plan.sh");
    const RUN_HOOK_TEMPLATE: &'static str = include_str!("../../static/template_run");
    const INIT_HOOK_TEMPLATE: &'static str = include_str!("../../static/template_init");

    pub fn start(ui: &mut UI, origin: String, name: String) -> Result<()> {
        try!(ui.begin("Constructing a cozy habitat for your app..."));
        try!(ui.br());

        // Build out the variables passed.
        let handlebars = Handlebars::new();
        let mut data = HashMap::new();
        data.insert("pkg_name".to_string(), name.clone());
        data.insert("pkg_origin".to_string(), origin.clone());

        // Unlike hooks we want to render the configured variables to the `plan.sh`
        let rendered_plan = try!(handlebars.template_render(PLAN_TEMPLATE, &data));
        try!(create_with_template(ui, &format!("{}/plan.sh", name), &rendered_plan));
        try!(ui.para("The `plan.sh` is the foundation of your new habitat. You can \
            define core metadata, dependencies, and tasks. More documentation here: \
            https://www.habitat.sh/docs/reference/plan-syntax/"));

        try!(create_with_template(ui, &format!("{}/hooks/init", name), INIT_HOOK_TEMPLATE));
        try!(create_with_template(ui, &format!("{}/hooks/run", name), RUN_HOOK_TEMPLATE));
        try!(ui.para("The `hooks` directory is where you can create a number of automation hooks \
            into your habitat. We'll make an `init` and a `run` hook to get you started, but there \
            are more hooks to create and tweak! See the full list with info here: \
            https://www.habitat.sh/docs/reference/plan-syntax/#hooks"));

        try!(ui.end("A happy abode for your code has been initialized! Now it's time to explore!"));
        Ok(())
    }

    fn create_with_template(ui: &mut UI, location: &str, template:  &str) -> Result<()> {
        let path = Path::new(&location);
        match path.exists() {
            false => {
                try!(ui.status(Status::Creating, format!("file: {}", location)));
                // If the directory doesn't exist we need to make it.
                if let Some(directory) = path.parent() {
                    try!(create_dir_all(directory));
                }
                // Create and then render the template with Handlebars
                try!(File::create(path).and_then(|mut file| file.write(template.as_bytes())));
            },
            true => {
                // If the user has already configured a file overwriting would be impolite.
                try!(ui.status(Status::Using, format!("existing file: {}", location)));
            }
        };
        Ok(())
    }
}
<|endoftext|>