<commit_before>use std::process;

use lexer::Token;
use lexer::LexerState;
use lexer::get_token;

use log;

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum CC {
    // condition codes
    E, L, LE, G, GE,
}

#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub enum SExpr {
    Symbol(String),
    Number(i64),
    Bool(bool),
    List(Vec<SExpr>),

    Define(String, Vec<String>, Box<SExpr>),
    Let(Vec<(String, SExpr)>, Box<SExpr>),
    If(Box<SExpr>, Box<SExpr>, Box<SExpr>),
    Tuple(Vec<SExpr>),
    Cmp(CC, Box<SExpr>, Box<SExpr>),
    App(Box<SExpr>, Vec<SExpr>),
    Prog(Vec<SExpr>, Box<SExpr>),
    EOF,
}

fn unread(ls: &mut LexerState, tok: Token) {
    if let Some(_) = ls.tok_buf {
        println!("error: unread buffer full");
    }
    else {
        ls.tok_buf = Some(tok)
    }
}

fn get_list(ls: &mut LexerState) -> Vec<SExpr> {
    match get_expr(ls) {
        exp => match get_token(ls) {
            Token::RParen => return vec![exp],
            tok => {
                unread(ls, tok);
                let mut seq = get_list(ls);
                seq.insert(0, exp);
                return seq;
            },
        },
    }
}

pub fn get_expr(ls: &mut LexerState) -> SExpr {
    match get_token(ls) {
        Token::Symbol(s) => return SExpr::Symbol(s),
        Token::Number(n) => return SExpr::Number(n),
        Token::LParen => {
            return SExpr::List(get_list(ls));
        },
        Token::RParen => panic!("line {}:{} unmatched ')'",
                                ls.line_num, ls.col),
        Token::EOF => return SExpr::EOF,
    }
}

pub fn get_arg_names(args: &Vec<SExpr>) -> Vec<String> {
    let mut arg_names = vec![];
    for arg in args {
        match arg {
            &SExpr::Symbol(ref name) => arg_names.push(name.clone()),
            _ => {
                error!("arg should be a Symbol");
                process::exit(0);
            }
        }
    }

    return arg_names;
}

pub fn get_ast(expr: &SExpr) -> SExpr {
    match expr {
        &SExpr::Symbol(ref sym) => {
            match &sym[..] {
                "#f" => SExpr::Bool(false),
                "#t" => SExpr::Bool(true),
                _ => SExpr::Symbol(sym.clone()),
            }
        },
        &SExpr::List(ref elts) =>
            match &elts[..] {
                &[SExpr::Symbol(ref k), SExpr::List(ref defelts), ref body]
                    if k == "define" => {
                    let ref name = defelts[0];
                    let args = defelts[1..].to_vec();

                    match name {
                        &SExpr::Symbol(ref name) => {
                            return SExpr::Define(name.clone(), get_arg_names(&args.to_vec()),
                                                 Box::new(get_ast(body)));
                        },
                        _ => panic!("invalid function prototype"),
                    }
                },
                &[SExpr::Symbol(ref k), ref cnd, ref thn, ref els]
                    if k == "if" => {
                    return SExpr::If(Box::new(get_ast(cnd)),
                                     Box::new(get_ast(thn)),
                                     Box::new(get_ast(els)));
                    },
                &[SExpr::Symbol(ref k), SExpr::List(ref bindings), ref body]
                    if k == "let" => {
                        let mut astified_bindings = vec![];
                        for bind_pair in bindings {
                            let (key, val) = match bind_pair {
                                // TODO: check length
                                &SExpr::List(ref kv) => (kv[0].clone(), kv[1].clone()),
                                _ => panic!("non-list in let-binding"),
                            };

                            let keyname = match key {
                                SExpr::Symbol(k) => k,
                                _ => panic!("let binding key is not symbol"),
                            };
                            astified_bindings.push((keyname, get_ast(&val)));
                        }
                        return SExpr::Let(astified_bindings, Box::new(get_ast(&body)));
                    },
                &[SExpr::Symbol(ref k), _..]
                    if k == "tuple" => {
                        let mut tuple_elts = elts[1..].to_vec();
                        tuple_elts = tuple_elts.iter().map(|e| get_ast(e)).collect();
                        return SExpr::Tuple(tuple_elts);
                    },
                &[SExpr::Symbol(ref cmp), ref left, ref right]
                    if (cmp == ">" || cmp == "<" ||
                        cmp == "<=" || cmp == ">=" ||
                        cmp == "=") => {
                        let cc = match &cmp[..] {
                            ">" => CC::G,
                            "<" => CC::L,
                            ">=" => CC::GE,
                            "<=" => CC::LE,
                            "=" => CC::E,
                            &_ => panic!("NYI"),
                        };

                        return SExpr::Cmp(cc, box left.clone(), box right.clone());
                    },
                &[ref f, _..] => {
                    let mut astified_args = vec![];
                    for arg in elts[1..].to_vec() {
                        astified_args.push(get_ast(&arg));
                    }

                    return SExpr::App(Box::new(f.clone()), astified_args);
                },
                &_ => panic!("NYI: {:?}", elts),
            }
        ,
        &_ => expr.clone(),
    }
}

pub fn read(ls: &mut LexerState) -> SExpr {
    return get_ast(&get_expr(ls));
}

#[test]
fn test_parser() {
    let mut input = String::from("(if #f (+ 42 (foo 12)) 17)
                                  (define (foo x y z) (+ x 10))
                                  (+ 1 2)");
    let mut lexer = LexerState {
        s: input,
        pos: 0,
        col: 1,
        line_num: 1,
        tok_buf: None,
    };
    assert_eq!(SExpr::If(Box::new(SExpr::Bool(false)),
                         Box::new(SExpr::App(Box::new(SExpr::Symbol("+".to_string())),
                                             vec![SExpr::Number(42),
                                                  SExpr::App(Box::new(SExpr::Symbol("foo".to_string())),
                                                             vec![SExpr::Number(12)])])),
                         Box::new(SExpr::Number(17))),
               read(&mut lexer));

    // Second top-level s-expression
    assert_eq!(SExpr::Define("foo".to_string(), vec!["x".to_string(), "y".to_string(), "z".to_string()],
                             Box::new(SExpr::App(Box::new(SExpr::Symbol("+".to_string())),
                                        vec![SExpr::Symbol("x".to_string()), SExpr::Number(10)]))),
               read(&mut lexer));

    // Third top-level s-expression
    assert_eq!(SExpr::App(Box::new(SExpr::Symbol("+".to_string())),
                          vec![SExpr::Number(1),
                               SExpr::Number(2)]),
               read(&mut lexer));
    // nothing left in string
    assert_eq!(SExpr::EOF, read(&mut lexer));
}
<commit_msg>FIX: Recursively call get_ast on `if` condition<commit_after>use std::process;

use lexer::Token;
use lexer::LexerState;
use lexer::get_token;

use log;

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum CC {
    // condition codes
    E, L, LE, G, GE,
}

#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub enum SExpr {
    Symbol(String),
    Number(i64),
    Bool(bool),
    List(Vec<SExpr>),

    Define(String, Vec<String>, Box<SExpr>),
    Let(Vec<(String, SExpr)>, Box<SExpr>),
    If(Box<SExpr>, Box<SExpr>, Box<SExpr>),
    Tuple(Vec<SExpr>),
    Cmp(CC, Box<SExpr>, Box<SExpr>),
    App(Box<SExpr>, Vec<SExpr>),
    Prog(Vec<SExpr>, Box<SExpr>),
    EOF,
}

fn unread(ls: &mut LexerState, tok: Token) {
    if let Some(_) = ls.tok_buf {
        println!("error: unread buffer full");
    }
    else {
        ls.tok_buf = Some(tok)
    }
}

fn get_list(ls: &mut LexerState) -> Vec<SExpr> {
    match get_expr(ls) {
        exp => match get_token(ls) {
            Token::RParen => return vec![exp],
            tok => {
                unread(ls, tok);
                let mut seq = get_list(ls);
                seq.insert(0, exp);
                return seq;
            },
        },
    }
}

pub fn get_expr(ls: &mut LexerState) -> SExpr {
    match get_token(ls) {
        Token::Symbol(s) => return SExpr::Symbol(s),
        Token::Number(n) => return SExpr::Number(n),
        Token::LParen => {
            return SExpr::List(get_list(ls));
        },
        Token::RParen => panic!("line {}:{} unmatched ')'",
                                ls.line_num, ls.col),
        Token::EOF => return SExpr::EOF,
    }
}

pub fn get_arg_names(args: &Vec<SExpr>) -> Vec<String> {
    let mut arg_names = vec![];
    for arg in args {
        match arg {
            &SExpr::Symbol(ref name) => arg_names.push(name.clone()),
            _ => {
                error!("arg should be a Symbol");
                process::exit(0);
            }
        }
    }

    return arg_names;
}

pub fn get_ast(expr: &SExpr) -> SExpr {
    match expr {
        &SExpr::Symbol(ref sym) => {
            match &sym[..] {
                "#f" => SExpr::Bool(false),
                "#t" => SExpr::Bool(true),
                _ => SExpr::Symbol(sym.clone()),
            }
        },
        &SExpr::List(ref elts) =>
            match &elts[..] {
                &[SExpr::Symbol(ref k), SExpr::List(ref defelts), ref body]
                    if k == "define" => {
                    let ref name = defelts[0];
                    let args = defelts[1..].to_vec();

                    match name {
                        &SExpr::Symbol(ref name) => {
                            return SExpr::Define(name.clone(), get_arg_names(&args.to_vec()),
                                                 Box::new(get_ast(body)));
                        },
                        _ => panic!("invalid function prototype"),
                    }
                },
                &[SExpr::Symbol(ref k), ref cnd, ref thn, ref els]
                    if k == "if" => {
                    return SExpr::If(Box::new(get_ast(cnd)),
                                     Box::new(get_ast(thn)),
                                     Box::new(get_ast(els)));
                    },
                &[SExpr::Symbol(ref k), SExpr::List(ref bindings), ref body]
                    if k == "let" => {
                        let mut astified_bindings = vec![];
                        for bind_pair in bindings {
                            let (key, val) = match bind_pair {
                                // TODO: check length
                                &SExpr::List(ref kv) => (kv[0].clone(), kv[1].clone()),
                                _ => panic!("non-list in let-binding"),
                            };

                            let keyname = match key {
                                SExpr::Symbol(k) => k,
                                _ => panic!("let binding key is not symbol"),
                            };
                            astified_bindings.push((keyname, get_ast(&val)));
                        }
                        return SExpr::Let(astified_bindings, Box::new(get_ast(&body)));
                    },
                &[SExpr::Symbol(ref k), _..]
                    if k == "tuple" => {
                        let mut tuple_elts = elts[1..].to_vec();
                        tuple_elts = tuple_elts.iter().map(|e| get_ast(e)).collect();
                        return SExpr::Tuple(tuple_elts);
                    },
                &[SExpr::Symbol(ref cmp), ref left, ref right]
                    if (cmp == ">" || cmp == "<" ||
                        cmp == "<=" || cmp == ">=" ||
                        cmp == "=") => {
                        let cc = match &cmp[..] {
                            ">" => CC::G,
                            "<" => CC::L,
                            ">=" => CC::GE,
                            "<=" => CC::LE,
                            "=" => CC::E,
                            &_ => panic!("NYI"),
                        };

                        return SExpr::Cmp(cc,
                                          box get_ast(left),
                                          box get_ast(right));
                    },
                &[ref f, _..] => {
                    let mut astified_args = vec![];
                    for arg in elts[1..].to_vec() {
                        astified_args.push(get_ast(&arg));
                    }

                    return SExpr::App(Box::new(f.clone()), astified_args);
                },
                &_ => panic!("NYI: {:?}", elts),
            }
        ,
        &_ => expr.clone(),
    }
}

pub fn read(ls: &mut LexerState) -> SExpr {
    return get_ast(&get_expr(ls));
}

#[test]
fn test_parser() {
    let mut input = String::from("(if #f (+ 42 (foo 12)) 17)
                                  (define (foo x y z) (+ x 10))
                                  (+ 1 2)");
    let mut lexer = LexerState {
        s: input,
        pos: 0,
        col: 1,
        line_num: 1,
        tok_buf: None,
    };
    assert_eq!(SExpr::If(Box::new(SExpr::Bool(false)),
                         Box::new(SExpr::App(Box::new(SExpr::Symbol("+".to_string())),
                                             vec![SExpr::Number(42),
                                                  SExpr::App(Box::new(SExpr::Symbol("foo".to_string())),
                                                             vec![SExpr::Number(12)])])),
                         Box::new(SExpr::Number(17))),
               read(&mut lexer));

    // Second top-level s-expression
    assert_eq!(SExpr::Define("foo".to_string(), vec!["x".to_string(), "y".to_string(), "z".to_string()],
                             Box::new(SExpr::App(Box::new(SExpr::Symbol("+".to_string())),
                                        vec![SExpr::Symbol("x".to_string()), SExpr::Number(10)]))),
               read(&mut lexer));

    // Third top-level s-expression
    assert_eq!(SExpr::App(Box::new(SExpr::Symbol("+".to_string())),
                          vec![SExpr::Number(1),
                               SExpr::Number(2)]),
               read(&mut lexer));
    // nothing left in string
    assert_eq!(SExpr::EOF, read(&mut lexer));
}
<|endoftext|>


<commit_before>mod varlink_grammar {
    include!(concat!(env!("OUT_DIR"), "/varlink_grammar.rs"));
}

use itertools::Itertools;
use std::collections::BTreeMap;
use std::borrow::Cow;
use std::collections::HashSet;
use self::varlink_grammar::VInterface;

pub enum VType<'a> {
    Bool(Option<bool>),
    Int(Option<i64>),
    Float(Option<f64>),
    VString(Option<&'a str>),
    VData(Option<&'a str>),
    VTypename(&'a str),
    VStruct(Box<VStruct<'a>>),
    VEnum(Box<VEnum<'a>>),
}

pub struct VTypeExt<'a> {
    pub vtype: VType<'a>,
    pub isarray: bool,
}

pub struct Argument<'a> {
    pub name: &'a str,
    pub vtype: VTypeExt<'a>,
}

pub struct VStruct<'a> {
    pub elts: Vec<Argument<'a>>,
}

pub struct VEnum<'a> {
    pub elts: Vec<&'a str>,
}

pub struct VError<'a> {
    pub name: &'a str,
    pub parm: VStruct<'a>,
}

pub enum VStructOrEnum<'a> {
    VStruct(Box<VStruct<'a>>),
    VEnum(Box<VEnum<'a>>),
}

pub struct Typedef<'a> {
    pub name: &'a str,
    pub elt: VStructOrEnum<'a>,
}

pub struct Method<'a> {
    pub name: &'a str,
    pub input: VStruct<'a>,
    pub output: VStruct<'a>,
}

enum MethodOrTypedefOrError<'a> {
    Error(VError<'a>),
    Typedef(Typedef<'a>),
    Method(Method<'a>),
}

pub struct Interface<'a> {
    pub name: &'a str,
    pub methods: BTreeMap<&'a str, Method<'a>>,
    pub typedefs: BTreeMap<&'a str, Typedef<'a>>,
    pub errors: BTreeMap<&'a str, VError<'a>>,
    pub error: HashSet<Cow<'static, str>>,
}

use std::fmt;
macro_rules! printVTypeExt {
	($s:ident, $f:ident, $t:expr) => {{
                write!($f, "{}", $t)?;
                if $s.isarray {
					write!($f, "[]")?;
                };
	}};
	($s:ident, $f:ident, $v:ident, $t:expr) => {{
                write!($f, "{}", $t)?;
                if $s.isarray {
					write!($f, "[]")?;
                };
                if let Some(val) = *$v {
                    write!($f, " = {}", val)?;
                }
	}};
	($s:ident, $f:ident, $v:ident, $t:expr, $k:expr) => {{
                write!($f, "{}", $t)?;
                if $s.isarray {
					write!($f, "[]")?;
                };
                if let Some(val) = *$v {
                    write!($f, " = {s}{}{s}", val, s=$k)?;
                }
	}};
}

impl<'a> fmt::Display for VTypeExt<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self.vtype {
            VType::Bool(ref v) => printVTypeExt!(self, f, v, "bool"),
            VType::Int(ref v) => printVTypeExt!(self, f, v, "int"),
            VType::Float(ref v) => printVTypeExt!(self, f, v, "float"),
            VType::VString(ref v) => printVTypeExt!(self, f, v, "string", "\""),
            VType::VData(ref v) => printVTypeExt!(self, f, v, "data", "\""),
            VType::VTypename(ref v) => printVTypeExt!(self, f, v),
            VType::VStruct(ref v) => printVTypeExt!(self, f, v),
            VType::VEnum(ref v) => printVTypeExt!(self, f, v),
        }

        Ok(())
    }
}

impl<'a> fmt::Display for VStructOrEnum<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            VStructOrEnum::VStruct(ref v) => write!(f, "{}", v)?,
            VStructOrEnum::VEnum(ref v) => write!(f, "{}", v)?,
        }

        Ok(())
    }
}

impl<'a> fmt::Display for Argument<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}: {}", self.name, self.vtype)?;
        Ok(())
    }
}
impl<'a> fmt::Display for VStruct<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "(")?;
        let mut iter = self.elts.iter();
        if let Some(fst) = iter.next() {
            write!(f, "{}", fst)?;
            for elt in iter {
                write!(f, ", {}", elt)?;
            }
        }
        write!(f, ")")
    }
}

impl<'a> fmt::Display for VEnum<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "(")?;
        let mut iter = self.elts.iter();
        if let Some(fst) = iter.next() {
            write!(f, "{}", fst)?;
            for elt in iter {
                write!(f, ", {}", elt)?;
            }
        }
        write!(f, ")")
    }
}

impl<'a> fmt::Display for Interface<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "interface {}\n", self.name)?;

        for t in self.typedefs.values() {
            write!(f, "type {} {}\n", t.name, t.elt)?;
        }

        for m in self.methods.values() {
            write!(f, "method {}{} -> {}\n", m.name, m.input, m.output)?;
        }

        for e in self.errors.values() {
            write!(f, "error {} {}\n", e.name, e.parm)?;
        }
        Ok(())
    }
}

impl<'a> Interface<'a> {
    fn from_token(n: &'a str, mt: Vec<MethodOrTypedefOrError<'a>>) -> Interface<'a> {
        let mut i = Interface {
            name: n,
            methods: BTreeMap::new(),
            typedefs: BTreeMap::new(),
            errors: BTreeMap::new(),
            error: HashSet::new(),
        };

        for o in mt {
            match o {
                MethodOrTypedefOrError::Method(m) => {
                    if let Some(d) = i.methods.insert(m.name, m) {
                        i.error
                            .insert(format!("Interface `{}`: multiple definitions of type `{}`!",
                                            i.name,
                                            d.name)
                                        .into());
                    };
                }
                MethodOrTypedefOrError::Typedef(t) => {
                    if let Some(d) = i.typedefs.insert(t.name, t) {
                        i.error
                            .insert(format!("Interface `{}`: multiple definitions of type `{}`!",
                                            i.name,
                                            d.name)
                                        .into());
                    };
                }
                MethodOrTypedefOrError::Error(e) => {
                    if let Some(d) = i.errors.insert(e.name, e) {
                        i.error
                            .insert(format!("Interface `{}`: multiple definitions of error `{}`!",
                                            i.name,
                                            d.name)
                                        .into());
                    };
                }
            };
        }
        if i.methods.len() == 0 {
            i.error
                .insert(format!("Interface `{}`: no method defined!", i.name).into());
        }

        i
    }
}

pub struct Varlink<'a> {
    pub string: &'a str,
    pub interface: Interface<'a>,
}

impl<'a> Varlink<'a> {
    pub fn from_string(s: &'a str) -> Result<Varlink, String> {

        let iface = match VInterface(s) {
            Ok(v) => v,
            Err(e) => {
                return Err(e.to_string());
            }
        };

        if iface.error.len() != 0 {
            Err(iface.error.into_iter().sorted().join("\n"))
        } else {
            Ok(Varlink {
                   string: s,
                   interface: iface,
               })
        }
    }
}

#[test]
fn test_standard() {
    let v = Varlink::from_string(
        "
# The Varlink Service Interface is provided by every varlink service. It
# describes the service and the interfaces it implements.
interface org.varlink.service

# Get a list of all the interfaces a service provides and information
# about the implementation.
method GetInfo() -> (
  vendor: string,
  product: string,
  version: string,
  url: string,
  interfaces: string[]
)

# Get the description of an interface that is implemented by this service.
method GetInterfaceDescription(interface: string) -> (description: string)

# The requested interface was not found.
error InterfaceNotFound (interface: string)

# The requested method was not found
error MethodNotFound (method: string)

# The interface defines the requested method, but the service does not
# implement it.
error MethodNotImplemented (method: string)

# One of the passed parameters is invalid.
error InvalidParameter (parameter: string)
",
    ).unwrap();
    assert_eq!(v.interface.name, "org.varlink.service");
    println!("{}", v.interface.to_string());
    assert_eq!(
        v.interface.to_string(),
        r#"interface org.varlink.service
method GetInfo() -> (vendor: string, product: string, version: string, url: string, interfaces: string[])
method GetInterfaceDescription(interface: string) -> (description: string)
error InterfaceNotFound (interface: string)
error InvalidParameter (parameter: string)
error MethodNotFound (method: string)
error MethodNotImplemented (method: string)
"#
    );
}

#[test]
fn test_one_method() {
    let v = Varlink::from_string("interface foo.bar\nmethod Foo()->()");
    assert!(v.is_ok());
}

#[test]
fn test_one_method_no_type() {
    assert!(VInterface("interface foo.bar\nmethod Foo()->(b:)").is_err());
}

#[test]
fn test_domainnames() {
    assert!(Varlink::from_string("interface org.varlink.service\nmethod F()->()").is_ok());
    assert!(Varlink::from_string("interface com.example.0example\nmethod F()->()").is_ok());
    assert!(Varlink::from_string("interface com.example.example-dash\nmethod F()->()").is_ok());
    assert!(Varlink::from_string("interface xn--lgbbat1ad8j.example.algeria\nmethod F()->()")
                .is_ok());
    assert!(Varlink::from_string("interface com.-example.leadinghyphen\nmethod F()->()").is_err());
    assert!(Varlink::from_string("interface com.example-.danglinghyphen-\nmethod F()->()")
                .is_err());
    assert!(Varlink::from_string("interface Com.example.uppercase-toplevel\nmethod F()->()")
                .is_err());
    assert!(Varlink::from_string("interface Co9.example.number-toplevel\nmethod F()->()").is_err());
    assert!(Varlink::from_string("interface 1om.example.number-toplevel\nmethod F()->()").is_err());
    assert!(Varlink::from_string("interface com.Example\nmethod F()->()").is_err());
}

#[test]
fn test_no_method() {
    assert!(
        Varlink::from_string(
            "
interface org.varlink.service
  type Interface (name: string, types: Type[], methods: Method[])
  type Property (key: string, value: string)
",
        ).is_err()
    );
}

#[test]
fn test_type_no_args() {
    assert!(Varlink::from_string("interface foo.bar\n type I ()\nmethod F()->()").is_ok());
}

#[test]
fn test_type_one_arg() {
    assert!(Varlink::from_string("interface foo.bar\n type I (b:bool)\nmethod F()->()").is_ok());
}

#[test]
fn test_type_one_array() {
    assert!(Varlink::from_string("interface foo.bar\n type I (b:bool[])\nmethod  F()->()").is_ok());
    assert!(Varlink::from_string("interface foo.bar\n type I (b:bool[ ])\nmethod  F()->()")
                .is_err());
    assert!(Varlink::from_string("interface foo.bar\n type I (b:bool[1])\nmethod  F()->()")
                .is_err());
    assert!(Varlink::from_string("interface foo.bar\n type I (b:bool[ 1 ])\nmethod  F()->()")
                .is_err());
    assert!(Varlink::from_string("interface foo.bar\n type I (b:bool[ 1 1 ])\nmethod  F()->()")
                .is_err());
}

#[test]
fn test_format() {
    let v = Varlink::from_string("interface foo.bar\ntype I(b:bool[])\nmethod  F()->()").unwrap();
    assert_eq!(
        v.interface.to_string(),
        "\
interface foo.bar
type I (b: bool[])
method F() -> ()
"
    );
}

#[test]
fn test_union() {
    let v = Varlink::from_string(
        "
    interface foo.bar\nmethod F()->(s: (a: bool, b: int), u: bool,int,(foo: bool, bar: bool))",
    ).unwrap();
    assert_eq!(
        v.interface.to_string(),
        "\
interface foo.bar
method F() -> (s: (a: bool, b: int), u: bool , int , (foo: bool, bar: bool))
"
    );
}

#[test]
fn test_duplicate() {
    let e = Varlink::from_string(
        "
interface foo.example
	type Device()
	type Device()
	type T()
	type T()
	method F() -> ()
	method F() -> ()
",
    ).err()
        .unwrap();
    assert_eq!(
        e,
        "\
Interface `foo.example`: multiple definitions of type `Device`!
Interface `foo.example`: multiple definitions of type `F`!
Interface `foo.example`: multiple definitions of type `T`!"
    );
}
<commit_msg>removed test_union(): union is obsolete<commit_after>mod varlink_grammar {
    include!(concat!(env!("OUT_DIR"), "/varlink_grammar.rs"));
}

use itertools::Itertools;
use std::collections::BTreeMap;
use std::borrow::Cow;
use std::collections::HashSet;
use self::varlink_grammar::VInterface;

pub enum VType<'a> {
    Bool(Option<bool>),
    Int(Option<i64>),
    Float(Option<f64>),
    VString(Option<&'a str>),
    VData(Option<&'a str>),
    VTypename(&'a str),
    VStruct(Box<VStruct<'a>>),
    VEnum(Box<VEnum<'a>>),
}

pub struct VTypeExt<'a> {
    pub vtype: VType<'a>,
    pub isarray: bool,
}

pub struct Argument<'a> {
    pub name: &'a str,
    pub vtype: VTypeExt<'a>,
}

pub struct VStruct<'a> {
    pub elts: Vec<Argument<'a>>,
}

pub struct VEnum<'a> {
    pub elts: Vec<&'a str>,
}

pub struct VError<'a> {
    pub name: &'a str,
    pub parm: VStruct<'a>,
}

pub enum VStructOrEnum<'a> {
    VStruct(Box<VStruct<'a>>),
    VEnum(Box<VEnum<'a>>),
}

pub struct Typedef<'a> {
    pub name: &'a str,
    pub elt: VStructOrEnum<'a>,
}

pub struct Method<'a> {
    pub name: &'a str,
    pub input: VStruct<'a>,
    pub output: VStruct<'a>,
}

enum MethodOrTypedefOrError<'a> {
    Error(VError<'a>),
    Typedef(Typedef<'a>),
    Method(Method<'a>),
}

pub struct Interface<'a> {
    pub name: &'a str,
    pub methods: BTreeMap<&'a str, Method<'a>>,
    pub typedefs: BTreeMap<&'a str, Typedef<'a>>,
    pub errors: BTreeMap<&'a str, VError<'a>>,
    pub error: HashSet<Cow<'static, str>>,
}

use std::fmt;
macro_rules! printVTypeExt {
	($s:ident, $f:ident, $t:expr) => {{
                write!($f, "{}", $t)?;
                if $s.isarray {
					write!($f, "[]")?;
                };
	}};
	($s:ident, $f:ident, $v:ident, $t:expr) => {{
                write!($f, "{}", $t)?;
                if $s.isarray {
					write!($f, "[]")?;
                };
                if let Some(val) = *$v {
                    write!($f, " = {}", val)?;
                }
	}};
	($s:ident, $f:ident, $v:ident, $t:expr, $k:expr) => {{
                write!($f, "{}", $t)?;
                if $s.isarray {
					write!($f, "[]")?;
                };
                if let Some(val) = *$v {
                    write!($f, " = {s}{}{s}", val, s=$k)?;
                }
	}};
}

impl<'a> fmt::Display for VTypeExt<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self.vtype {
            VType::Bool(ref v) => printVTypeExt!(self, f, v, "bool"),
            VType::Int(ref v) => printVTypeExt!(self, f, v, "int"),
            VType::Float(ref v) => printVTypeExt!(self, f, v, "float"),
            VType::VString(ref v) => printVTypeExt!(self, f, v, "string", "\""),
            VType::VData(ref v) => printVTypeExt!(self, f, v, "data", "\""),
            VType::VTypename(ref v) => printVTypeExt!(self, f, v),
            VType::VStruct(ref v) => printVTypeExt!(self, f, v),
            VType::VEnum(ref v) => printVTypeExt!(self, f, v),
        }

        Ok(())
    }
}

impl<'a> fmt::Display for VStructOrEnum<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            VStructOrEnum::VStruct(ref v) => write!(f, "{}", v)?,
            VStructOrEnum::VEnum(ref v) => write!(f, "{}", v)?,
        }

        Ok(())
    }
}

impl<'a> fmt::Display for Argument<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}: {}", self.name, self.vtype)?;
        Ok(())
    }
}
impl<'a> fmt::Display for VStruct<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "(")?;
        let mut iter = self.elts.iter();
        if let Some(fst) = iter.next() {
            write!(f, "{}", fst)?;
            for elt in iter {
                write!(f, ", {}", elt)?;
            }
        }
        write!(f, ")")
    }
}

impl<'a> fmt::Display for VEnum<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "(")?;
        let mut iter = self.elts.iter();
        if let Some(fst) = iter.next() {
            write!(f, "{}", fst)?;
            for elt in iter {
                write!(f, ", {}", elt)?;
            }
        }
        write!(f, ")")
    }
}

impl<'a> fmt::Display for Interface<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "interface {}\n", self.name)?;

        for t in self.typedefs.values() {
            write!(f, "type {} {}\n", t.name, t.elt)?;
        }

        for m in self.methods.values() {
            write!(f, "method {}{} -> {}\n", m.name, m.input, m.output)?;
        }

        for e in self.errors.values() {
            write!(f, "error {} {}\n", e.name, e.parm)?;
        }
        Ok(())
    }
}

impl<'a> Interface<'a> {
    fn from_token(n: &'a str, mt: Vec<MethodOrTypedefOrError<'a>>) -> Interface<'a> {
        let mut i = Interface {
            name: n,
            methods: BTreeMap::new(),
            typedefs: BTreeMap::new(),
            errors: BTreeMap::new(),
            error: HashSet::new(),
        };

        for o in mt {
            match o {
                MethodOrTypedefOrError::Method(m) => {
                    if let Some(d) = i.methods.insert(m.name, m) {
                        i.error
                            .insert(format!("Interface `{}`: multiple definitions of type `{}`!",
                                            i.name,
                                            d.name)
                                        .into());
                    };
                }
                MethodOrTypedefOrError::Typedef(t) => {
                    if let Some(d) = i.typedefs.insert(t.name, t) {
                        i.error
                            .insert(format!("Interface `{}`: multiple definitions of type `{}`!",
                                            i.name,
                                            d.name)
                                        .into());
                    };
                }
                MethodOrTypedefOrError::Error(e) => {
                    if let Some(d) = i.errors.insert(e.name, e) {
                        i.error
                            .insert(format!("Interface `{}`: multiple definitions of error `{}`!",
                                            i.name,
                                            d.name)
                                        .into());
                    };
                }
            };
        }
        if i.methods.len() == 0 {
            i.error
                .insert(format!("Interface `{}`: no method defined!", i.name).into());
        }

        i
    }
}

pub struct Varlink<'a> {
    pub string: &'a str,
    pub interface: Interface<'a>,
}

impl<'a> Varlink<'a> {
    pub fn from_string(s: &'a str) -> Result<Varlink, String> {

        let iface = match VInterface(s) {
            Ok(v) => v,
            Err(e) => {
                return Err(e.to_string());
            }
        };

        if iface.error.len() != 0 {
            Err(iface.error.into_iter().sorted().join("\n"))
        } else {
            Ok(Varlink {
                   string: s,
                   interface: iface,
               })
        }
    }
}

#[test]
fn test_standard() {
    let v = Varlink::from_string(
        "
# The Varlink Service Interface is provided by every varlink service. It
# describes the service and the interfaces it implements.
interface org.varlink.service

# Get a list of all the interfaces a service provides and information
# about the implementation.
method GetInfo() -> (
  vendor: string,
  product: string,
  version: string,
  url: string,
  interfaces: string[]
)

# Get the description of an interface that is implemented by this service.
method GetInterfaceDescription(interface: string) -> (description: string)

# The requested interface was not found.
error InterfaceNotFound (interface: string)

# The requested method was not found
error MethodNotFound (method: string)

# The interface defines the requested method, but the service does not
# implement it.
error MethodNotImplemented (method: string)

# One of the passed parameters is invalid.
error InvalidParameter (parameter: string)
",
    ).unwrap();
    assert_eq!(v.interface.name, "org.varlink.service");
    println!("{}", v.interface.to_string());
    assert_eq!(
        v.interface.to_string(),
        r#"interface org.varlink.service
method GetInfo() -> (vendor: string, product: string, version: string, url: string, interfaces: string[])
method GetInterfaceDescription(interface: string) -> (description: string)
error InterfaceNotFound (interface: string)
error InvalidParameter (parameter: string)
error MethodNotFound (method: string)
error MethodNotImplemented (method: string)
"#
    );
}

#[test]
fn test_one_method() {
    let v = Varlink::from_string("interface foo.bar\nmethod Foo()->()");
    assert!(v.is_ok());
}

#[test]
fn test_one_method_no_type() {
    assert!(VInterface("interface foo.bar\nmethod Foo()->(b:)").is_err());
}

#[test]
fn test_domainnames() {
    assert!(Varlink::from_string("interface org.varlink.service\nmethod F()->()").is_ok());
    assert!(Varlink::from_string("interface com.example.0example\nmethod F()->()").is_ok());
    assert!(Varlink::from_string("interface com.example.example-dash\nmethod F()->()").is_ok());
    assert!(Varlink::from_string("interface xn--lgbbat1ad8j.example.algeria\nmethod F()->()")
                .is_ok());
    assert!(Varlink::from_string("interface com.-example.leadinghyphen\nmethod F()->()").is_err());
    assert!(Varlink::from_string("interface com.example-.danglinghyphen-\nmethod F()->()")
                .is_err());
    assert!(Varlink::from_string("interface Com.example.uppercase-toplevel\nmethod F()->()")
                .is_err());
    assert!(Varlink::from_string("interface Co9.example.number-toplevel\nmethod F()->()").is_err());
    assert!(Varlink::from_string("interface 1om.example.number-toplevel\nmethod F()->()").is_err());
    assert!(Varlink::from_string("interface com.Example\nmethod F()->()").is_err());
}

#[test]
fn test_no_method() {
    assert!(
        Varlink::from_string(
            "
interface org.varlink.service
  type Interface (name: string, types: Type[], methods: Method[])
  type Property (key: string, value: string)
",
        ).is_err()
    );
}

#[test]
fn test_type_no_args() {
    assert!(Varlink::from_string("interface foo.bar\n type I ()\nmethod F()->()").is_ok());
}

#[test]
fn test_type_one_arg() {
    assert!(Varlink::from_string("interface foo.bar\n type I (b:bool)\nmethod F()->()").is_ok());
}

#[test]
fn test_type_one_array() {
    assert!(Varlink::from_string("interface foo.bar\n type I (b:bool[])\nmethod  F()->()").is_ok());
    assert!(Varlink::from_string("interface foo.bar\n type I (b:bool[ ])\nmethod  F()->()")
                .is_err());
    assert!(Varlink::from_string("interface foo.bar\n type I (b:bool[1])\nmethod  F()->()")
                .is_err());
    assert!(Varlink::from_string("interface foo.bar\n type I (b:bool[ 1 ])\nmethod  F()->()")
                .is_err());
    assert!(Varlink::from_string("interface foo.bar\n type I (b:bool[ 1 1 ])\nmethod  F()->()")
                .is_err());
}

#[test]
fn test_format() {
    let v = Varlink::from_string("interface foo.bar\ntype I(b:bool[])\nmethod  F()->()").unwrap();
    assert_eq!(
        v.interface.to_string(),
        "\
interface foo.bar
type I (b: bool[])
method F() -> ()
"
    );
}

#[test]
fn test_duplicate() {
    let e = Varlink::from_string(
        "
interface foo.example
	type Device()
	type Device()
	type T()
	type T()
	method F() -> ()
	method F() -> ()
",
    ).err()
        .unwrap();
    assert_eq!(
        e,
        "\
Interface `foo.example`: multiple definitions of type `Device`!
Interface `foo.example`: multiple definitions of type `F`!
Interface `foo.example`: multiple definitions of type `T`!"
    );
}
<|endoftext|>


<commit_before>#![feature(log_syntax, trace_macros)]

#[macro_use(debug_unreachable)]
extern crate debug_unreachable;

pub mod zero;
#[macro_use]
pub mod lazy;
pub mod measure;
pub mod node;
#[macro_use]
pub mod digit;
pub mod finger_tree;
pub mod seq;
// mod monoid;
// #[macro_use]
// mod tree23;
// mod measure;
// #[macro_use]
// mod digit;
// mod finger_tree;
// pub mod seq;
<commit_msg>Removed unused feature not available on stable<commit_after>
#[macro_use(debug_unreachable)]
extern crate debug_unreachable;

pub mod zero;
#[macro_use]
pub mod lazy;
pub mod measure;
pub mod node;
#[macro_use]
pub mod digit;
pub mod finger_tree;
pub mod seq;
<|endoftext|>


<commit_before>
pub fn puzzle(input: &str) -> u32 {
    0
}

pub fn is_a_triangle(a: u32, b: u32, c: u32) -> bool {
    a + b > c && b + c > a && c + a > b
}

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

    #[test]
    fn equilateral_triangle() {
        assert!(is_a_triangle(3, 3, 3));
    }

    #[test]
    fn isoceles_triangle() {
        assert!(is_a_triangle(13, 13, 3));
    }

    #[test]
    fn acute_triangle() { // so cute
        assert!(is_a_triangle(208, 203, 145));
    }

    #[test]
    fn obtuse_triangle() {
        assert!(is_a_triangle(244, 360, 159));
    }

    #[test]
    fn not_a_triangle() {
        assert!( ! is_a_triangle(5, 10, 25) );
        assert!( ! is_a_triangle(5, 25, 10) );
        assert!( ! is_a_triangle(10, 5, 25) );
        assert!( ! is_a_triangle(10, 25, 5) );
        assert!( ! is_a_triangle(25, 5, 10) );
        assert!( ! is_a_triangle(25, 10, 5) );
    }
}
<commit_msg>Handle the input in the file<commit_after>
pub fn puzzle(input: &str) -> u32 {
    let mut num_triangles = 0;

    for line in input.lines() {
        let dims = line.split_whitespace();

        let dims = dims.map(|dim| {
            dim.parse::<u32>().expect("Could not parse!")
        }).collect::<Vec<_>>();

        println!("dimensions are: {}, {}, {}", dims[0], dims[1], dims[2]);

        if is_a_triangle(dims[0], dims[1], dims[2]) {
            num_triangles += 1;
        }
    }

    num_triangles
}

pub fn is_a_triangle(a: u32, b: u32, c: u32) -> bool {
    a + b > c && b + c > a && c + a > b
}

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

    #[test]
    fn equilateral_triangle() {
        assert!(is_a_triangle(3, 3, 3));
    }

    #[test]
    fn isoceles_triangle() {
        assert!(is_a_triangle(13, 13, 3));
    }

    #[test]
    fn acute_triangle() { // so cute
        assert!(is_a_triangle(208, 203, 145));
    }

    #[test]
    fn obtuse_triangle() {
        assert!(is_a_triangle(244, 360, 159));
    }

    #[test]
    fn not_a_triangle() {
        assert!( ! is_a_triangle(5, 10, 25) );
        assert!( ! is_a_triangle(5, 25, 10) );
        assert!( ! is_a_triangle(10, 5, 25) );
        assert!( ! is_a_triangle(10, 25, 5) );
        assert!( ! is_a_triangle(25, 5, 10) );
        assert!( ! is_a_triangle(25, 10, 5) );
    }

    #[test]
    fn whole_puzzle() {
        let input = "\
  785  516  744
  272  511  358
  801  791  693
  572  150   74";
        assert_eq!(puzzle(input), 3);
    }
}
<|endoftext|>


<commit_before>#[cfg(test)]
mod tests {
    #[test]
    fn it_works() {
    }
}
<commit_msg>Fixed formatting<commit_after>#[cfg(test)]
mod tests {
    #[test]
    fn it_works() {}
}
<|endoftext|>


<commit_before>// Copyright 2021 Google LLC
//
// 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
//
//     https://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.

use bit_field::BitField;
use std::fmt::{self, Debug, Display, Formatter};
use thiserror::Error;

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct FieldInfo {
    pub name: &'static str,
    pub start: usize,
    pub width: usize,
    pub value: u64,
    pub decoded: Option<Decoded>,
}

impl FieldInfo {
    fn get(register: u64, name: &'static str, start: usize, end: usize) -> Self {
        let value = register.get_bits(start..end);
        Self {
            name,
            start,
            width: end - start,
            value,
            decoded: None,
        }
    }

    fn get_bit(register: u64, name: &'static str, bit: usize) -> Self {
        Self::get(register, name, bit, bit + 1)
    }
}

impl Display for FieldInfo {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        if self.width == 1 {
            write!(
                f,
                "{}: {}",
                self.name,
                if self.value == 1 { "true" } else { "false" }
            )
        } else {
            write!(
                f,
                "{}: {:#02$x} {1:#03$b}",
                self.name,
                self.value,
                (self.width + 3) / 4 + 2,
                self.width + 2
            )
        }
    }
}

#[derive(Debug, Error)]
pub enum DecodeError {
    #[error("Invalid ESR, res0 is {res0}")]
    InvalidRes0 { res0: u64 },
    #[error("Invalid EC {ec}")]
    InvalidEc { ec: u64 },
}

#[derive(Copy, Clone, Debug, Eq, PartialEq)]
enum SyndromeAccessSize {
    Byte = 0b00,
    Halfword = 0b01,
    Word = 0b10,
    Doubleword = 0b11,
}

impl Display for SyndromeAccessSize {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        let s = match self {
            Self::Byte => "byte",
            Self::Halfword => "halfword",
            Self::Word => "word",
            Self::Doubleword => "doubleword",
        };
        write!(f, "{}", s)
    }
}

fn decode_iss_data_abort(iss: u64) -> Decoded {
    let isv = FieldInfo::get_bit(iss, "ISV", 24);
    let sas = FieldInfo::get(iss, "SAS", 22, 24);
    let sas_decoded = match sas.value {
        0b00 => SyndromeAccessSize::Byte,
        0b01 => SyndromeAccessSize::Halfword,
        0b10 => SyndromeAccessSize::Word,
        0b11 => SyndromeAccessSize::Doubleword,
        _ => unreachable!(),
    };
    let sse = FieldInfo::get_bit(iss, "SSE", 21);
    let srt = FieldInfo::get(iss, "SRT", 16, 21);
    let sf = FieldInfo::get_bit(iss, "SF", 15);
    let ar = FieldInfo::get_bit(iss, "AR", 14);
    let vncr = FieldInfo::get_bit(iss, "VNCR", 13);
    let fnv = FieldInfo::get_bit(iss, "FnV", 10);
    let ea = FieldInfo::get_bit(iss, "EA", 9);
    let cm = FieldInfo::get_bit(iss, "CM", 8);
    let s1ptw = FieldInfo::get_bit(iss, "S1PTW", 7);
    let wnr = FieldInfo::get_bit(iss, "WnR", 6);
    let dfsc = FieldInfo::get(iss, "DFSC", 0, 6);
    let description = format!("{}, SAS:{}", isv, sas_decoded);
    Decoded {
        fields: vec![
            isv, sas, sse, srt, sf, ar, vncr, fnv, ea, cm, s1ptw, wnr, dfsc,
        ],
        description,
    }
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Decoded {
    pub fields: Vec<FieldInfo>,
    pub description: String,
}

pub fn decode(esr: u64) -> Result<Decoded, DecodeError> {
    let res0 = FieldInfo::get(esr, "RES0", 37, 64);
    let iss2 = FieldInfo::get(esr, "ISS2", 32, 37);
    let ec = FieldInfo::get(esr, "EC", 26, 32);
    let il = FieldInfo::get_bit(esr, "IL", 25);
    let iss = FieldInfo::get(esr, "ISS", 0, 25);
    if res0.value != 0 {
        return Err(DecodeError::InvalidRes0 { res0: res0.value });
    }
    let (class, iss_decoded) = match ec.value {
        0b000000 => ("Unknown reason", None),
        0b000001 => ("Wrapped WF* instruction execution", None),
        0b000011 => ("Trapped MCR or MRC access with coproc=0b1111", None),
        0b000100 => ("Trapped MCRR or MRRC access with coproc=0b1111", None),
        0b000101 => ("Trapped MCR or MRC access with coproc=0b1110", None),
        0b000110 => ("Trapped LDC or STC access", None),
        0b000111 => ("Trapped access to SVE, Advanced SIMD or floating point", None),
        0b001010 => ("Trapped execution of an LD64B, ST64B, ST64BV, or ST64BV0 instruction", None),
        0b001100 => ("Trapped MRRC access with (coproc==0b1110)", None),
        0b001101 => ("Branch Target Exception", None),
        0b001110 => ("Illegal Execution state", None),
        0b010001 => ("SVC instruction execution in AArch32 state", None),
        0b010101 => ("SVC instruction execution in AArch64 state", None),
        0b011000 => ("Trapped MSR, MRS or System instruction execution in AArch64 state", None),
        0b011001 => ("Access to SVE functionality trapped as a result of CPACR_EL1.ZEN, CPTR_EL2.ZEN, CPTR_EL2.TZ, or CPTR_EL3.EZ", None),
        0b011100 => ("Exception from a Pointer Authentication instruction authentication failure", None),
        0b100000 => ("Instruction Abort from a lower Exception level", None),
        0b100001 => ("Instruction Abort taken without a change in Exception level", None),
        0b100010 => ("PC alignment fault exception", None),
        0b100100 => ("Data Abort from a lower Exception level", Some(decode_iss_data_abort(iss.value))),
        0b100101 => ("Data Abort taken without a change in Exception level", Some(decode_iss_data_abort(iss.value))),
        0b100110 => ("SP alignment fault exception", None),
        0b101000 => ("Trapped floating-point exception taken from AArch32 state", None),
        0b101100 => ("Trapped floating-point exception taken from AArch64 state", None),
        0b101111 => ("SError interrupt", None),
        0b110000 => ("Breakpoint exception from a lower Exception level", None),
        0b110001 => ("Breakpoint exception taken without a change in Exception level", None),
        0b110010 => ("Software Step exception from a lower Exception level", None),
        0b110011 => ("Software Step exception taken without a change in Exception level", None),
        0b110100 => ("Watchpoint exception from a lower Exception level", None),
        0b110101 => ("Watchpoint exception taken without a change in Exception level", None),
        0b111000 => ("BKPT instruction execution in AArch32 state", None),
        0b111100 => ("BRK instruction execution in AArch64 state", None),
        _ => return Err(DecodeError::InvalidEc { ec: ec.value }),
    };
    let description = if let Some(iss_decoded) = &iss_decoded {
        format!(
            "EC:{:#08b} '{}', {}, {} ({}), {}",
            ec.value, class, il, iss, iss_decoded.description, iss2
        )
    } else {
        format!(
            "EC:{:#08b} '{}', {}, {}, {}",
            ec.value, class, il, iss, iss2
        )
    };
    let iss = FieldInfo {
        decoded: iss_decoded,
        ..iss
    };
    Ok(Decoded {
        fields: vec![res0, iss2, ec, il, iss],
        description,
    })
}
<commit_msg>No need for fields to be in description too.<commit_after>// Copyright 2021 Google LLC
//
// 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
//
//     https://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.

use bit_field::BitField;
use std::fmt::{self, Debug, Display, Formatter};
use thiserror::Error;

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct FieldInfo {
    pub name: &'static str,
    pub start: usize,
    pub width: usize,
    pub value: u64,
    pub decoded: Option<Decoded>,
}

impl FieldInfo {
    fn get(register: u64, name: &'static str, start: usize, end: usize) -> Self {
        let value = register.get_bits(start..end);
        Self {
            name,
            start,
            width: end - start,
            value,
            decoded: None,
        }
    }

    fn get_bit(register: u64, name: &'static str, bit: usize) -> Self {
        Self::get(register, name, bit, bit + 1)
    }
}

impl Display for FieldInfo {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        if self.width == 1 {
            write!(
                f,
                "{}: {}",
                self.name,
                if self.value == 1 { "true" } else { "false" }
            )
        } else {
            write!(
                f,
                "{}: {:#02$x} {1:#03$b}",
                self.name,
                self.value,
                (self.width + 3) / 4 + 2,
                self.width + 2
            )
        }
    }
}

#[derive(Debug, Error)]
pub enum DecodeError {
    #[error("Invalid ESR, res0 is {res0}")]
    InvalidRes0 { res0: u64 },
    #[error("Invalid EC {ec}")]
    InvalidEc { ec: u64 },
}

#[derive(Copy, Clone, Debug, Eq, PartialEq)]
enum SyndromeAccessSize {
    Byte = 0b00,
    Halfword = 0b01,
    Word = 0b10,
    Doubleword = 0b11,
}

impl Display for SyndromeAccessSize {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        let s = match self {
            Self::Byte => "byte",
            Self::Halfword => "halfword",
            Self::Word => "word",
            Self::Doubleword => "doubleword",
        };
        write!(f, "{}", s)
    }
}

fn decode_iss_data_abort(iss: u64) -> Decoded {
    let isv = FieldInfo::get_bit(iss, "ISV", 24);
    let sas = FieldInfo::get(iss, "SAS", 22, 24);
    let sas_decoded = match sas.value {
        0b00 => SyndromeAccessSize::Byte,
        0b01 => SyndromeAccessSize::Halfword,
        0b10 => SyndromeAccessSize::Word,
        0b11 => SyndromeAccessSize::Doubleword,
        _ => unreachable!(),
    };
    let sse = FieldInfo::get_bit(iss, "SSE", 21);
    let srt = FieldInfo::get(iss, "SRT", 16, 21);
    let sf = FieldInfo::get_bit(iss, "SF", 15);
    let ar = FieldInfo::get_bit(iss, "AR", 14);
    let vncr = FieldInfo::get_bit(iss, "VNCR", 13);
    let fnv = FieldInfo::get_bit(iss, "FnV", 10);
    let ea = FieldInfo::get_bit(iss, "EA", 9);
    let cm = FieldInfo::get_bit(iss, "CM", 8);
    let s1ptw = FieldInfo::get_bit(iss, "S1PTW", 7);
    let wnr = FieldInfo::get_bit(iss, "WnR", 6);
    let dfsc = FieldInfo::get(iss, "DFSC", 0, 6);
    let description = format!("{}, SAS:{}", isv, sas_decoded);
    Decoded {
        fields: vec![
            isv, sas, sse, srt, sf, ar, vncr, fnv, ea, cm, s1ptw, wnr, dfsc,
        ],
        description,
    }
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Decoded {
    pub fields: Vec<FieldInfo>,
    pub description: String,
}

pub fn decode(esr: u64) -> Result<Decoded, DecodeError> {
    let res0 = FieldInfo::get(esr, "RES0", 37, 64);
    let iss2 = FieldInfo::get(esr, "ISS2", 32, 37);
    let ec = FieldInfo::get(esr, "EC", 26, 32);
    let il = FieldInfo::get_bit(esr, "IL", 25);
    let iss = FieldInfo::get(esr, "ISS", 0, 25);
    if res0.value != 0 {
        return Err(DecodeError::InvalidRes0 { res0: res0.value });
    }
    let (class, iss_decoded) = match ec.value {
        0b000000 => ("Unknown reason", None),
        0b000001 => ("Wrapped WF* instruction execution", None),
        0b000011 => ("Trapped MCR or MRC access with coproc=0b1111", None),
        0b000100 => ("Trapped MCRR or MRRC access with coproc=0b1111", None),
        0b000101 => ("Trapped MCR or MRC access with coproc=0b1110", None),
        0b000110 => ("Trapped LDC or STC access", None),
        0b000111 => ("Trapped access to SVE, Advanced SIMD or floating point", None),
        0b001010 => ("Trapped execution of an LD64B, ST64B, ST64BV, or ST64BV0 instruction", None),
        0b001100 => ("Trapped MRRC access with (coproc==0b1110)", None),
        0b001101 => ("Branch Target Exception", None),
        0b001110 => ("Illegal Execution state", None),
        0b010001 => ("SVC instruction execution in AArch32 state", None),
        0b010101 => ("SVC instruction execution in AArch64 state", None),
        0b011000 => ("Trapped MSR, MRS or System instruction execution in AArch64 state", None),
        0b011001 => ("Access to SVE functionality trapped as a result of CPACR_EL1.ZEN, CPTR_EL2.ZEN, CPTR_EL2.TZ, or CPTR_EL3.EZ", None),
        0b011100 => ("Exception from a Pointer Authentication instruction authentication failure", None),
        0b100000 => ("Instruction Abort from a lower Exception level", None),
        0b100001 => ("Instruction Abort taken without a change in Exception level", None),
        0b100010 => ("PC alignment fault exception", None),
        0b100100 => ("Data Abort from a lower Exception level", Some(decode_iss_data_abort(iss.value))),
        0b100101 => ("Data Abort taken without a change in Exception level", Some(decode_iss_data_abort(iss.value))),
        0b100110 => ("SP alignment fault exception", None),
        0b101000 => ("Trapped floating-point exception taken from AArch32 state", None),
        0b101100 => ("Trapped floating-point exception taken from AArch64 state", None),
        0b101111 => ("SError interrupt", None),
        0b110000 => ("Breakpoint exception from a lower Exception level", None),
        0b110001 => ("Breakpoint exception taken without a change in Exception level", None),
        0b110010 => ("Software Step exception from a lower Exception level", None),
        0b110011 => ("Software Step exception taken without a change in Exception level", None),
        0b110100 => ("Watchpoint exception from a lower Exception level", None),
        0b110101 => ("Watchpoint exception taken without a change in Exception level", None),
        0b111000 => ("BKPT instruction execution in AArch32 state", None),
        0b111100 => ("BRK instruction execution in AArch64 state", None),
        _ => return Err(DecodeError::InvalidEc { ec: ec.value }),
    };
    let iss = FieldInfo {
        decoded: iss_decoded,
        ..iss
    };
    let ec = FieldInfo {
        decoded: Some(Decoded {
            description: class.to_string(),
            fields: vec![],
        }),
        ..ec
    };
    Ok(Decoded {
        description: class.to_string(),
        fields: vec![res0, iss2, ec, il, iss],
    })
}
<|endoftext|>


<commit_before>use std::fmt;
use std::cell::UnsafeCell;
use std::mem;
use std::mem::ManuallyDrop;
use std::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT};
use std::cmp;
use std::error;

fn next_id() -> usize {
    static mut COUNTER: AtomicUsize = ATOMIC_USIZE_INIT;
    unsafe {
        COUNTER.fetch_add(1, Ordering::SeqCst)
    }
}

fn get_thread_id() -> usize {
    thread_local!(static THREAD_ID: usize = next_id());
    THREAD_ID.with(|&x| x)
}

/// Returned when borrowing fails.
#[derive(Debug)]
pub struct InvalidThreadAccess;

impl fmt::Display for InvalidThreadAccess {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Display::fmt(error::Error::description(self), f)
    }
}

impl error::Error for InvalidThreadAccess {
    fn description(&self) -> &str {
        "fragile value accessed from foreign thread"
    }
}

/// A `Fragile<T>` wraps a non sendable `T` to be safely send to other threads.
///
/// Once the value has been wrapped it can be sent to other threads but access
/// to the value on those threads will fail.
pub struct Fragile<T> {
    value: ManuallyDrop<UnsafeCell<T>>,
    thread_id: usize,
}

impl<T> Fragile<T> {
    /// Creates a new `Fragile` wrapping a `value`.
    ///
    /// The value that is moved into the `Fragile` can be non `Send` and
    /// will be anchored to the thread that created the object.  If the
    /// fragile wrapper type ends up being send from thread to thread
    /// only the original thread can interact with the value.
    pub fn new(value: T) -> Self {
        Fragile {
            value: ManuallyDrop::new(UnsafeCell::new(value)),
            thread_id: get_thread_id(),
        }
    }

    #[inline(always)]
    fn assert_thread(&self) {
        if get_thread_id() != self.thread_id {
            panic!("trying to access wrapped value in fragile container from incorrect thread.");
        }
    }

    /// Consumes the `Fragile`, returning the wrapped value.
    ///
    /// # Panics
    ///
    /// Panics if called from a different thread than the one where the
    /// original value was created.
    pub fn into_inner(mut self) -> T {
        self.assert_thread();
        unsafe {
            let value = mem::replace(&mut self.value, mem::uninitialized());
            mem::forget(self);
            ManuallyDrop::into_inner(value).into_inner()
        }
    }

    /// Consumes the `Fragile`, returning the wrapped value if successful.
    ///
    /// The wrapped value is returned if this is called from the same thread
    /// as the one where the original value was created, otherwise the
    /// `Fragile` is returned as `Err(self)`.
    pub fn try_into_inner(self) -> Result<T, Self> {
        if get_thread_id() == self.thread_id {
            Ok(self.into_inner())
        } else {
            Err(self)
        }
    }

    /// Immutably borrows the wrapped value.
    ///
    /// # Panics
    ///
    /// Panics if the calling thread is not the one that wrapped the value.
    /// For a non-panicking variant, use [`try_get`](#method.try_get`).
    pub fn get(&self) -> &T {
        self.assert_thread();
        unsafe {
            &*self.value.get()
        }
    }

    /// Mutably borrows the wrapped value.
    ///
    /// # Panics
    ///
    /// Panics if the calling thread is not the one that wrapped the value.
    /// For a non-panicking variant, use [`try_get_mut`](#method.try_get_mut`).
    pub fn get_mut(&mut self) -> &mut T {
        self.assert_thread();
        unsafe {
            &mut *self.value.get()
        }
    }

    /// Tries to immutably borrow the wrapped value.
    ///
    /// Returns `None` if the calling thread is not the one that wrapped the value.
    pub fn try_get(&self) -> Result<&T, InvalidThreadAccess> {
        if get_thread_id() == self.thread_id {
            unsafe {
                Ok(&*self.value.get())
            }
        } else {
            Err(InvalidThreadAccess)
        }
    }

    /// Tries to mutably borrow the wrapped value.
    ///
    /// Returns `None` if the calling thread is not the one that wrapped the value.
    pub fn try_get_mut(&mut self) -> Result<&mut T, InvalidThreadAccess> {
        if get_thread_id() == self.thread_id {
            unsafe {
                Ok(&mut *self.value.get())
            }
        } else {
            Err(InvalidThreadAccess)
        }
    }
}

impl<T> Drop for Fragile<T> {
    fn drop(&mut self) {
        if mem::needs_drop::<T>() {
            if get_thread_id() != self.thread_id {
                unsafe {
                    ManuallyDrop::drop(&mut self.value)
                }
            } else {
                panic!("destructor of fragile object ran on wrong thread");
            }
        }
    }
}

impl<T> From<T> for Fragile<T> {
    #[inline]
    fn from(t: T) -> Fragile<T> {
        Fragile::new(t)
    }
}

impl<T: Clone> Clone for Fragile<T> {
    #[inline]
    fn clone(&self) -> Fragile<T> {
        Fragile::new(self.get().clone())
    }
}

impl<T: Default> Default for Fragile<T> {
    #[inline]
    fn default() -> Fragile<T> {
        Fragile::new(T::default())
    }
}

impl<T: PartialEq> PartialEq for Fragile<T> {
    #[inline]
    fn eq(&self, other: &Fragile<T>) -> bool {
        *self.get() == *other.get()
    }
}

impl<T: Eq> Eq for Fragile<T> {}

impl<T: PartialOrd> PartialOrd for Fragile<T> {
    #[inline]
    fn partial_cmp(&self, other: &Fragile<T>) -> Option<cmp::Ordering> {
        self.get().partial_cmp(&*other.get())
    }

    #[inline]
    fn lt(&self, other: &Fragile<T>) -> bool {
        *self.get() < *other.get()
    }

    #[inline]
    fn le(&self, other: &Fragile<T>) -> bool {
        *self.get() <= *other.get()
    }

    #[inline]
    fn gt(&self, other: &Fragile<T>) -> bool {
        *self.get() > *other.get()
    }

    #[inline]
    fn ge(&self, other: &Fragile<T>) -> bool {
        *self.get() >= *other.get()
    }
}

impl<T: Ord> Ord for Fragile<T> {
    #[inline]
    fn cmp(&self, other: &Fragile<T>) -> cmp::Ordering {
        self.get().cmp(&*other.get())
    }
}

impl<T: fmt::Display> fmt::Display for Fragile<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        fmt::Display::fmt(self.get(), f)
    }
}

impl<T: fmt::Debug> fmt::Debug for Fragile<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        match self.try_get() {
            Ok(value) => {
                f.debug_struct("Fragile")
                    .field("value", value)
                    .finish()
            }
            Err(..) => {
                struct InvalidPlaceholder;
                impl fmt::Debug for InvalidPlaceholder {
                    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
                        f.write_str("<invalid thread>")
                    }
                }

                f.debug_struct("Fragile")
                    .field("value", &InvalidPlaceholder)
                    .finish()
            }
        }
    }
}

unsafe impl<T> Send for Fragile<T> {}

#[test]
fn test_basic() {
    use std::thread;
    let val = Fragile::new(true);
    assert_eq!(val.to_string(), "true");
    assert_eq!(val.get(), &true);
    assert!(val.try_get().is_ok());
    thread::spawn(move || {
        assert!(val.try_get().is_err());
    }).join().unwrap();
}
<commit_msg>Added tests<commit_after>use std::fmt;
use std::cell::UnsafeCell;
use std::mem;
use std::mem::ManuallyDrop;
use std::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT};
use std::cmp;
use std::error;

fn next_id() -> usize {
    static mut COUNTER: AtomicUsize = ATOMIC_USIZE_INIT;
    unsafe {
        COUNTER.fetch_add(1, Ordering::SeqCst)
    }
}

fn get_thread_id() -> usize {
    thread_local!(static THREAD_ID: usize = next_id());
    THREAD_ID.with(|&x| x)
}

/// Returned when borrowing fails.
#[derive(Debug)]
pub struct InvalidThreadAccess;

impl fmt::Display for InvalidThreadAccess {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Display::fmt(error::Error::description(self), f)
    }
}

impl error::Error for InvalidThreadAccess {
    fn description(&self) -> &str {
        "fragile value accessed from foreign thread"
    }
}

/// A `Fragile<T>` wraps a non sendable `T` to be safely send to other threads.
///
/// Once the value has been wrapped it can be sent to other threads but access
/// to the value on those threads will fail.
pub struct Fragile<T> {
    value: ManuallyDrop<UnsafeCell<T>>,
    thread_id: usize,
}

impl<T> Fragile<T> {
    /// Creates a new `Fragile` wrapping a `value`.
    ///
    /// The value that is moved into the `Fragile` can be non `Send` and
    /// will be anchored to the thread that created the object.  If the
    /// fragile wrapper type ends up being send from thread to thread
    /// only the original thread can interact with the value.
    pub fn new(value: T) -> Self {
        Fragile {
            value: ManuallyDrop::new(UnsafeCell::new(value)),
            thread_id: get_thread_id(),
        }
    }

    #[inline(always)]
    fn assert_thread(&self) {
        if get_thread_id() != self.thread_id {
            panic!("trying to access wrapped value in fragile container from incorrect thread.");
        }
    }

    /// Consumes the `Fragile`, returning the wrapped value.
    ///
    /// # Panics
    ///
    /// Panics if called from a different thread than the one where the
    /// original value was created.
    pub fn into_inner(mut self) -> T {
        self.assert_thread();
        unsafe {
            let value = mem::replace(&mut self.value, mem::uninitialized());
            mem::forget(self);
            ManuallyDrop::into_inner(value).into_inner()
        }
    }

    /// Consumes the `Fragile`, returning the wrapped value if successful.
    ///
    /// The wrapped value is returned if this is called from the same thread
    /// as the one where the original value was created, otherwise the
    /// `Fragile` is returned as `Err(self)`.
    pub fn try_into_inner(self) -> Result<T, Self> {
        if get_thread_id() == self.thread_id {
            Ok(self.into_inner())
        } else {
            Err(self)
        }
    }

    /// Immutably borrows the wrapped value.
    ///
    /// # Panics
    ///
    /// Panics if the calling thread is not the one that wrapped the value.
    /// For a non-panicking variant, use [`try_get`](#method.try_get`).
    pub fn get(&self) -> &T {
        self.assert_thread();
        unsafe {
            &*self.value.get()
        }
    }

    /// Mutably borrows the wrapped value.
    ///
    /// # Panics
    ///
    /// Panics if the calling thread is not the one that wrapped the value.
    /// For a non-panicking variant, use [`try_get_mut`](#method.try_get_mut`).
    pub fn get_mut(&mut self) -> &mut T {
        self.assert_thread();
        unsafe {
            &mut *self.value.get()
        }
    }

    /// Tries to immutably borrow the wrapped value.
    ///
    /// Returns `None` if the calling thread is not the one that wrapped the value.
    pub fn try_get(&self) -> Result<&T, InvalidThreadAccess> {
        if get_thread_id() == self.thread_id {
            unsafe {
                Ok(&*self.value.get())
            }
        } else {
            Err(InvalidThreadAccess)
        }
    }

    /// Tries to mutably borrow the wrapped value.
    ///
    /// Returns `None` if the calling thread is not the one that wrapped the value.
    pub fn try_get_mut(&mut self) -> Result<&mut T, InvalidThreadAccess> {
        if get_thread_id() == self.thread_id {
            unsafe {
                Ok(&mut *self.value.get())
            }
        } else {
            Err(InvalidThreadAccess)
        }
    }
}

impl<T> Drop for Fragile<T> {
    fn drop(&mut self) {
        if mem::needs_drop::<T>() {
            if get_thread_id() == self.thread_id {
                unsafe {
                    ManuallyDrop::drop(&mut self.value)
                }
            } else {
                panic!("destructor of fragile object ran on wrong thread");
            }
        }
    }
}

impl<T> From<T> for Fragile<T> {
    #[inline]
    fn from(t: T) -> Fragile<T> {
        Fragile::new(t)
    }
}

impl<T: Clone> Clone for Fragile<T> {
    #[inline]
    fn clone(&self) -> Fragile<T> {
        Fragile::new(self.get().clone())
    }
}

impl<T: Default> Default for Fragile<T> {
    #[inline]
    fn default() -> Fragile<T> {
        Fragile::new(T::default())
    }
}

impl<T: PartialEq> PartialEq for Fragile<T> {
    #[inline]
    fn eq(&self, other: &Fragile<T>) -> bool {
        *self.get() == *other.get()
    }
}

impl<T: Eq> Eq for Fragile<T> {}

impl<T: PartialOrd> PartialOrd for Fragile<T> {
    #[inline]
    fn partial_cmp(&self, other: &Fragile<T>) -> Option<cmp::Ordering> {
        self.get().partial_cmp(&*other.get())
    }

    #[inline]
    fn lt(&self, other: &Fragile<T>) -> bool {
        *self.get() < *other.get()
    }

    #[inline]
    fn le(&self, other: &Fragile<T>) -> bool {
        *self.get() <= *other.get()
    }

    #[inline]
    fn gt(&self, other: &Fragile<T>) -> bool {
        *self.get() > *other.get()
    }

    #[inline]
    fn ge(&self, other: &Fragile<T>) -> bool {
        *self.get() >= *other.get()
    }
}

impl<T: Ord> Ord for Fragile<T> {
    #[inline]
    fn cmp(&self, other: &Fragile<T>) -> cmp::Ordering {
        self.get().cmp(&*other.get())
    }
}

impl<T: fmt::Display> fmt::Display for Fragile<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        fmt::Display::fmt(self.get(), f)
    }
}

impl<T: fmt::Debug> fmt::Debug for Fragile<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        match self.try_get() {
            Ok(value) => {
                f.debug_struct("Fragile")
                    .field("value", value)
                    .finish()
            }
            Err(..) => {
                struct InvalidPlaceholder;
                impl fmt::Debug for InvalidPlaceholder {
                    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
                        f.write_str("<invalid thread>")
                    }
                }

                f.debug_struct("Fragile")
                    .field("value", &InvalidPlaceholder)
                    .finish()
            }
        }
    }
}

unsafe impl<T> Send for Fragile<T> {}

#[test]
fn test_basic() {
    use std::thread;
    let val = Fragile::new(true);
    assert_eq!(val.to_string(), "true");
    assert_eq!(val.get(), &true);
    assert!(val.try_get().is_ok());
    thread::spawn(move || {
        assert!(val.try_get().is_err());
    }).join().unwrap();
}

#[test]
fn test_mut() {
    let mut val = Fragile::new(true);
    *val.get_mut() = false;
    assert_eq!(val.to_string(), "false");
    assert_eq!(val.get(), &false);
}

#[test]
#[should_panic]
fn test_access_other_thread() {
    use std::thread;
    let val = Fragile::new(true);
    thread::spawn(move || {
        val.get();
    }).join().unwrap();
}

#[test]
fn test_noop_drop_elsewhere() {
    use std::thread;
    let val = Fragile::new(true);
    thread::spawn(move || {
        // force the move
        val.try_get().ok();
    }).join().unwrap();
}

#[test]
fn test_panic_on_drop_elsewhere() {
    use std::thread;
    use std::sync::Arc;
    use std::sync::atomic::{AtomicBool, Ordering};
    let was_called = Arc::new(AtomicBool::new(false));
    struct X(Arc<AtomicBool>);
    impl Drop for X {
        fn drop(&mut self) {
            self.0.store(true, Ordering::SeqCst);
        }
    }
    let val = Fragile::new(X(was_called.clone()));
    assert!(thread::spawn(move || {
        val.try_get().ok();
    }).join().is_err());
    assert_eq!(was_called.load(Ordering::SeqCst), false);
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate string_cache;
extern crate html5ever;
extern crate unicode_width;

use std::io;
use std::io::Write;
use std::cmp::max;
use html5ever::{Parser,parse_document};
use html5ever::driver::ParseOpts;
use html5ever::tree_builder::TreeBuilderOpts;
use html5ever::rcdom::{RcDom,Handle,Text,Element,Document,Comment};
use html5ever::tendril::TendrilSink;
use unicode_width::{UnicodeWidthStr,UnicodeWidthChar};

/// A dummy writer which does nothing
struct Discard {}
impl Write for Discard {
    fn write(&mut self, _: &[u8]) -> std::result::Result<usize, io::Error> { Ok(0) }
    fn flush(&mut self) -> std::result::Result<(), io::Error> { Ok(()) }
}

fn wrap_text(text: &str, width: usize) -> String {
    let mut result = String::new();
    let mut xpos = 0usize;
    for word in text.split_whitespace() {
        if width <= (xpos + 1) {
            result.push('\n');
            xpos = 0;
        }
        let space_left = width - xpos - 1;
        let word_width = UnicodeWidthStr::width(word);
        if word_width <= space_left {
            /* It fits; no problem. */
            result.push(' ');
            xpos += 1;
            result.push_str(word);
            xpos += word_width;
            continue;
        }

        /* It doesn't fit.  If we're not at the start of the line,
         * then go to a new line. */
        if xpos > 0 {
            result.push('\n');
            xpos = 0;
        }

        /* We're now at the start of a line. */
        if word_width > width {
            /* It doesn't fit at all on the line, so break it. */
            for c in word.chars() {
                let c_width = UnicodeWidthChar::width(c).unwrap();
                if c_width + xpos > width {
                    /* Break here */
                    result.push('\n');
                    xpos = 0;
                }
                /* This might happen with really narrow spaces... */
                assert!(c_width <= width);

                result.push(c);
                xpos += c_width;
            }
        } else {
            result.push_str(word);
            xpos += word_width;
        }
    }
    result
}

fn get_text(handle: Handle) -> String {
    let node = handle.borrow();
    let mut result = String::new();
    if let Text(ref tstr) = node.node {
        result.push_str(&tstr);
    } else {
        for child in node.children.iter() {
            result.push_str(&get_text(child.clone()));
        }
    }
    result
}

fn get_wrapped_text(handle: Handle, width: usize) -> String {
    let text = get_text(handle);
    wrap_text(&text, width)
}

fn dom_to_string<T:Write>(handle: Handle, err_out: &mut T, width: usize) -> String {
    let node = handle.borrow();
    let mut result = String::new();
    match node.node {
        Document => {},
        Element(ref name, _, ref attrs) => {
            match *name {
                qualname!(html, "html") |
                qualname!(html, "div") |
                qualname!(html, "span") |
                qualname!(html, "body") => {
                    /* process children, but don't add anything */
                },
                qualname!(html, "link") |
                qualname!(html, "meta") |
                qualname!(html, "hr") |
                qualname!(html, "head") => {
                    /* Ignore the head and its children */
                    return result;
                },
                qualname!(html, "h1") |
                qualname!(html, "h2") |
                qualname!(html, "h3") |
                qualname!(html, "h4") |
                qualname!(html, "p") => {
                    return get_wrapped_text(handle.clone(), width) + "\n\n";
                },
                qualname!(html, "br") => {
                    result.push('\n');
                }
                qualname!(html, "table") => return table_to_string(handle.clone(), err_out, width),
                _ => {
                    write!(err_out, "Unhandled element: {:?}\n", name.local);
                },
            }
          },
        Text(ref tstr) => {
            return wrap_text(tstr, width);
        }
        Comment(_) => {},
        _ => { write!(err_out, "Unhandled: {:?}\n", node).unwrap(); },
    }
    for child in node.children.iter() {
        result.push_str(&dom_to_string(child.clone(), err_out, width));
    }
    result
}

#[derive(Debug)]
struct TableCell {
    colspan: usize,
    content: Handle,
}

#[derive(Debug)]
struct TableRow {
    cells: Vec<TableCell>,
}

#[derive(Debug)]
struct Table {
    rows: Vec<TableRow>,
}

impl Table {
    pub fn new() -> Table {
        Table{ rows: Vec::new() }
    }
    pub fn push(&mut self, row: TableRow) {
        self.rows.push(row);
    }
    pub fn rows(&self) -> std::slice::Iter<TableRow> {
        self.rows.iter()
    }
}

impl TableRow {
    pub fn new() -> TableRow {
        TableRow{ cells: Vec::new() }
    }
    pub fn push(&mut self, cell: TableCell) {
        self.cells.push(cell);
    }
    pub fn cells(&self) -> std::slice::Iter<TableCell> {
        self.cells.iter()
    }
    pub fn num_cells(&self) -> usize {
        self.cells.len()
    }
}

impl TableCell {
    pub fn new(s: Handle) -> TableCell {
        if let Element(_, _, ref attrs) = s.borrow().node {
            let mut colspan = 1;
            for attr in attrs {
                if &attr.name.local == "colspan" {
                    let v:&str = &*attr.value;
                    colspan = v.parse().unwrap_or(1);
                } else {
                    //println!("Attr: {:?}", attr);
                }
            }
            TableCell{ content: s.clone(), colspan: colspan }
        } else {
            panic!("TableCell::new received a non-Element");
        }
    }
    pub fn render<T:Write>(&self, width: usize, err_out: &mut T) -> String
    {
        dom_to_string(self.content.clone(), err_out, width)
    }
}

fn handle_td(handle: Handle) -> TableCell {
    TableCell::new(handle)
}

fn handle_tr<T:Write>(handle: Handle, _: &mut T) -> TableRow {
    let node = handle.borrow();

    let mut row = TableRow::new();

    for child in node.children.iter() {
        match child.borrow().node {
            Element(ref name, _, _) => {
                match *name {
                    qualname!(html, "td") => {
                        row.push(handle_td(child.clone()));
                    },
                    _ => println!("  [[tr child: {:?}]]", name),
                }
            },
            Comment(_) => {},
            _ => { /*result.push_str(&format!("Unhandled in table: {:?}\n", node));*/ },
        }
    }

    row
}

fn handle_tbody<T:Write>(handle: Handle, err_out: &mut T, width: usize) -> String {
    let node = handle.borrow();

    let mut table = Table::new();

    for child in node.children.iter() {
        match child.borrow().node {
            Element(ref name, _, _) => {
                match *name {
                    qualname!(html, "tr") => {
                        table.push(handle_tr(child.clone(), err_out));
                    },
                    _ => println!("  [[tbody child: {:?}]]", name),
                }
            },
            Comment(_) => {},
            _ => { /*result.push_str(&format!("Unhandled in table: {:?}\n", node));*/ },
        }
    }

    /* Now lay out the table.  Use the simple option of giving each column
     * same width.  TODO: be cleverer, and handle multi-width cells, etc. */
    let num_columns = table.rows().map(|r| r.num_cells()).max().unwrap();

    // Allow for | between columns.
    let col_width = (width - (num_columns-1))/num_columns;

    let mut result = String::new();

    /* Heuristic: scale the column widths according to how much content there is. */
    let test_col_width = 1000;  // Render width for measurement; shouldn't make much difference.
    let min_width = 5;
    let mut col_sizes = vec![0usize; num_columns];

    for row in table.rows() {
        for (colno, cell) in row.cells().enumerate() {
            let celldata = cell.render(test_col_width, &mut Discard{});
            col_sizes[colno] += celldata.len();
        }
    }
    let tot_size: usize = col_sizes.iter().sum();
    let mut col_widths:Vec<usize> = col_sizes.iter()
                                         .map(|sz| {
                                             if *sz == 0 {
                                                 0
                                             } else {
                                                 max(sz * width / tot_size, min_width)
                                             }
                                          }).collect();
    /* The minimums may have put the total width too high */
    while col_widths.iter().cloned().sum::<usize>() > width {
        let (i, v) = col_widths.iter().cloned().enumerate().max_by_key(|k| k.1).unwrap();
        col_widths[i] -= 1;
    }

    let cell_bottom: String = (0..col_width).map(|_| '-').collect::<String>() + "+";
    let mut rowline = String::new();
    for width in col_widths.iter().cloned().filter(|w:&usize| *w > 0) {
        rowline.push_str(&(0..(width-1)).map(|_| '-').collect::<String>());
        rowline.push('+');
    }
    if rowline.len() > 0 {
        rowline.pop().unwrap();  // Remove the last '+'.
    }
    result.push_str(&rowline);
    result.push('\n');

    for row in table.rows() {
        let (used_widths, formatted_cells): (Vec<usize>, Vec<String>) = row.cells()
                                              .enumerate()
                                              .flat_map(|(colno, cell)| {
                                                   let col_width = col_widths[colno];
                                                   if col_width > 0 {
                                                       Some((col_width, cell.render(col_width-1, err_out)))
                                                   } else {
                                                       None
                                                   }
                                               })
                                              .unzip();
        let line_sets: Vec<Vec<&str>> = formatted_cells.iter()
                                                       .map(|s| s.lines()
                                                                 .map(|line| line.trim_right())
                                                                 .collect())
                                                       .collect();
        let cell_height = line_sets.iter()
                                   .map(|v| v.len())
                                   .max().unwrap_or(0);
        for i in 0..cell_height {
            for (cellno, ls) in line_sets.iter().enumerate() {
                result.push_str(&format!("{: <width$}", ls.get(i).cloned().unwrap_or(""), width = used_widths[cellno]));
                if cellno == line_sets.len()-1 {
                    result.push('\n')
                } else {
                    result.push('|')
                }
            }
        }
        if cell_height > 0 {
            result.push_str(&rowline);
            result.push('\n');
        }
    }

    result
}

fn table_to_string<T:Write>(handle: Handle, err_out: &mut T, width: usize) -> String {
    let node = handle.borrow();
    let result = String::new();

    for child in node.children.iter() {
        match child.borrow().node {
            Element(ref name, _, _) => {
                match *name {
                    qualname!(html, "tbody") => return handle_tbody(child.clone(), err_out, width),
                    _ => { writeln!(err_out, "  [[table child: {:?}]]", name);},
                }
            },
            Comment(_) => {},
            _ => { /*result.push_str(&format!("Unhandled in table: {:?}\n", node));*/ },
        }
    }

    result
}

pub fn from_read<R>(mut input: R, width: usize) -> String where R: io::Read {
    let opts = ParseOpts {
        tree_builder: TreeBuilderOpts {
            drop_doctype: true,
            ..Default::default()
        },
        ..Default::default()
    };
    let dom = parse_document(RcDom::default(), opts)
                   .from_utf8()
                   .read_from(&mut input)
                   .unwrap();

    dom_to_string(dom.document, &mut io::stderr(), width)
}

#[cfg(test)]
mod tests {
    use super::{from_read,wrap_text};
    #[test]
    fn test_table() {
        assert_eq!(from_read(&br##"

           1
           2
           3

"##[..], 12), r#"---+---+---
|1  |2 |3 |
"#);
     }

     #[test]
     fn test_para() {
        assert_eq!(from_read(&b"
Hello
"[..], 10),
                   "Hello\n");
     }

     #[test]
     fn test_wrap() {
        assert_eq!(wrap_text("Hello", 10), "Hello\n");
     }
}
<commit_msg>Fix some of the tests.<commit_after>#[macro_use]
extern crate string_cache;
extern crate html5ever;
extern crate unicode_width;

use std::io;
use std::io::Write;
use std::cmp::max;
use html5ever::{Parser,parse_document};
use html5ever::driver::ParseOpts;
use html5ever::tree_builder::TreeBuilderOpts;
use html5ever::rcdom::{RcDom,Handle,Text,Element,Document,Comment};
use html5ever::tendril::TendrilSink;
use unicode_width::{UnicodeWidthStr,UnicodeWidthChar};

/// A dummy writer which does nothing
struct Discard {}
impl Write for Discard {
    fn write(&mut self, _: &[u8]) -> std::result::Result<usize, io::Error> { Ok(0) }
    fn flush(&mut self) -> std::result::Result<(), io::Error> { Ok(()) }
}

fn wrap_text(text: &str, width: usize) -> String {
    let mut result = String::new();
    let mut xpos = 0usize;
    for word in text.split_whitespace() {
        println!("First word: <<{:?}>>", word);
        if width <= (xpos + 1) {
            result.push('\n');
            xpos = 0;
        }
        let space_left = width - xpos - 1;
        let word_width = UnicodeWidthStr::width(word);
        if word_width <= space_left {
            /* It fits; no problem.  Add a space if not at the
             * start of line.*/
            if xpos > 0 {
                result.push(' ');
                xpos += 1;
            }
            result.push_str(word);
            xpos += word_width;
            continue;
        }

        /* It doesn't fit.  If we're not at the start of the line,
         * then go to a new line. */
        if xpos > 0 {
            result.push('\n');
            xpos = 0;
        }

        /* We're now at the start of a line. */
        if word_width > width {
            /* It doesn't fit at all on the line, so break it. */
            for c in word.chars() {
                let c_width = UnicodeWidthChar::width(c).unwrap();
                if c_width + xpos > width {
                    /* Break here */
                    result.push('\n');
                    xpos = 0;
                }
                /* This might happen with really narrow spaces... */
                assert!(c_width <= width);

                result.push(c);
                xpos += c_width;
            }
        } else {
            result.push_str(word);
            xpos += word_width;
        }
    }
    /* Terminate with a newline if needed. */
    if xpos > 0 {
        result.push('\n');
    }
    result
}

fn get_text(handle: Handle) -> String {
    let node = handle.borrow();
    let mut result = String::new();
    if let Text(ref tstr) = node.node {
        result.push_str(&tstr);
    } else {
        for child in node.children.iter() {
            result.push_str(&get_text(child.clone()));
        }
    }
    result
}

fn get_wrapped_text(handle: Handle, width: usize) -> String {
    let text = get_text(handle);
    wrap_text(&text, width)
}

fn dom_to_string<T:Write>(handle: Handle, err_out: &mut T, width: usize) -> String {
    let node = handle.borrow();
    let mut result = String::new();
    match node.node {
        Document => {},
        Element(ref name, _, ref attrs) => {
            match *name {
                qualname!(html, "html") |
                qualname!(html, "div") |
                qualname!(html, "span") |
                qualname!(html, "body") => {
                    /* process children, but don't add anything */
                },
                qualname!(html, "link") |
                qualname!(html, "meta") |
                qualname!(html, "hr") |
                qualname!(html, "head") => {
                    /* Ignore the head and its children */
                    return result;
                },
                qualname!(html, "h1") |
                qualname!(html, "h2") |
                qualname!(html, "h3") |
                qualname!(html, "h4") |
                qualname!(html, "p") => {
                    return get_wrapped_text(handle.clone(), width) + "\n\n";
                },
                qualname!(html, "br") => {
                    result.push('\n');
                }
                qualname!(html, "table") => return table_to_string(handle.clone(), err_out, width),
                _ => {
                    write!(err_out, "Unhandled element: {:?}\n", name.local);
                },
            }
          },
        Text(ref tstr) => {
            return wrap_text(tstr, width);
        }
        Comment(_) => {},
        _ => { write!(err_out, "Unhandled: {:?}\n", node).unwrap(); },
    }
    for child in node.children.iter() {
        result.push_str(&dom_to_string(child.clone(), err_out, width));
    }
    result
}

#[derive(Debug)]
struct TableCell {
    colspan: usize,
    content: Handle,
}

#[derive(Debug)]
struct TableRow {
    cells: Vec<TableCell>,
}

#[derive(Debug)]
struct Table {
    rows: Vec<TableRow>,
}

impl Table {
    pub fn new() -> Table {
        Table{ rows: Vec::new() }
    }
    pub fn push(&mut self, row: TableRow) {
        self.rows.push(row);
    }
    pub fn rows(&self) -> std::slice::Iter<TableRow> {
        self.rows.iter()
    }
}

impl TableRow {
    pub fn new() -> TableRow {
        TableRow{ cells: Vec::new() }
    }
    pub fn push(&mut self, cell: TableCell) {
        self.cells.push(cell);
    }
    pub fn cells(&self) -> std::slice::Iter<TableCell> {
        self.cells.iter()
    }
    pub fn num_cells(&self) -> usize {
        self.cells.len()
    }
}

impl TableCell {
    pub fn new(s: Handle) -> TableCell {
        if let Element(_, _, ref attrs) = s.borrow().node {
            let mut colspan = 1;
            for attr in attrs {
                if &attr.name.local == "colspan" {
                    let v:&str = &*attr.value;
                    colspan = v.parse().unwrap_or(1);
                } else {
                    //println!("Attr: {:?}", attr);
                }
            }
            TableCell{ content: s.clone(), colspan: colspan }
        } else {
            panic!("TableCell::new received a non-Element");
        }
    }
    pub fn render<T:Write>(&self, width: usize, err_out: &mut T) -> String
    {
        dom_to_string(self.content.clone(), err_out, width)
    }
}

fn handle_td(handle: Handle) -> TableCell {
    TableCell::new(handle)
}

fn handle_tr<T:Write>(handle: Handle, _: &mut T) -> TableRow {
    let node = handle.borrow();

    let mut row = TableRow::new();

    for child in node.children.iter() {
        match child.borrow().node {
            Element(ref name, _, _) => {
                match *name {
                    qualname!(html, "td") => {
                        row.push(handle_td(child.clone()));
                    },
                    _ => println!("  [[tr child: {:?}]]", name),
                }
            },
            Comment(_) => {},
            _ => { /*result.push_str(&format!("Unhandled in table: {:?}\n", node));*/ },
        }
    }

    row
}

fn handle_tbody<T:Write>(handle: Handle, err_out: &mut T, width: usize) -> String {
    let node = handle.borrow();

    let mut table = Table::new();

    for child in node.children.iter() {
        match child.borrow().node {
            Element(ref name, _, _) => {
                match *name {
                    qualname!(html, "tr") => {
                        table.push(handle_tr(child.clone(), err_out));
                    },
                    _ => println!("  [[tbody child: {:?}]]", name),
                }
            },
            Comment(_) => {},
            _ => { /*result.push_str(&format!("Unhandled in table: {:?}\n", node));*/ },
        }
    }

    /* Now lay out the table.  Use the simple option of giving each column
     * same width.  TODO: be cleverer, and handle multi-width cells, etc. */
    let num_columns = table.rows().map(|r| r.num_cells()).max().unwrap();

    // Allow for | between columns.
    let col_width = (width - (num_columns-1))/num_columns;

    let mut result = String::new();

    /* Heuristic: scale the column widths according to how much content there is. */
    let test_col_width = 1000;  // Render width for measurement; shouldn't make much difference.
    let min_width = 5;
    let mut col_sizes = vec![0usize; num_columns];

    for row in table.rows() {
        for (colno, cell) in row.cells().enumerate() {
            let celldata = cell.render(test_col_width, &mut Discard{});
            col_sizes[colno] += celldata.len();
        }
    }
    let tot_size: usize = col_sizes.iter().sum();
    let mut col_widths:Vec<usize> = col_sizes.iter()
                                         .map(|sz| {
                                             if *sz == 0 {
                                                 0
                                             } else {
                                                 max(sz * width / tot_size, min_width)
                                             }
                                          }).collect();
    /* The minimums may have put the total width too high */
    while col_widths.iter().cloned().sum::<usize>() > width {
        let (i, v) = col_widths.iter().cloned().enumerate().max_by_key(|k| k.1).unwrap();
        col_widths[i] -= 1;
    }

    let cell_bottom: String = (0..col_width).map(|_| '-').collect::<String>() + "+";
    let mut rowline = String::new();
    for width in col_widths.iter().cloned().filter(|w:&usize| *w > 0) {
        rowline.push_str(&(0..(width-1)).map(|_| '-').collect::<String>());
        rowline.push('+');
    }
    if rowline.len() > 0 {
        rowline.pop().unwrap();  // Remove the last '+'.
    }
    result.push_str(&rowline);
    result.push('\n');

    for row in table.rows() {
        let (used_widths, formatted_cells): (Vec<usize>, Vec<String>) = row.cells()
                                              .enumerate()
                                              .flat_map(|(colno, cell)| {
                                                   let col_width = col_widths[colno];
                                                   if col_width > 0 {
                                                       Some((col_width, cell.render(col_width-1, err_out)))
                                                   } else {
                                                       None
                                                   }
                                               })
                                              .unzip();
        let line_sets: Vec<Vec<&str>> = formatted_cells.iter()
                                                       .map(|s| s.lines()
                                                                 .map(|line| line.trim_right())
                                                                 .collect())
                                                       .collect();
        let cell_height = line_sets.iter()
                                   .map(|v| v.len())
                                   .max().unwrap_or(0);
        for i in 0..cell_height {
            for (cellno, ls) in line_sets.iter().enumerate() {
                result.push_str(&format!("{: <width$}", ls.get(i).cloned().unwrap_or(""), width = used_widths[cellno]-1));
                if cellno == line_sets.len()-1 {
                    result.push('\n')
                } else {
                    result.push('|')
                }
            }
        }
        if cell_height > 0 {
            result.push_str(&rowline);
            result.push('\n');
        }
    }

    result
}

fn table_to_string<T:Write>(handle: Handle, err_out: &mut T, width: usize) -> String {
    let node = handle.borrow();
    let result = String::new();

    for child in node.children.iter() {
        match child.borrow().node {
            Element(ref name, _, _) => {
                match *name {
                    qualname!(html, "tbody") => return handle_tbody(child.clone(), err_out, width),
                    _ => { writeln!(err_out, "  [[table child: {:?}]]", name);},
                }
            },
            Comment(_) => {},
            _ => { /*result.push_str(&format!("Unhandled in table: {:?}\n", node));*/ },
        }
    }

    result
}

pub fn from_read<R>(mut input: R, width: usize) -> String where R: io::Read {
    let opts = ParseOpts {
        tree_builder: TreeBuilderOpts {
            drop_doctype: true,
            ..Default::default()
        },
        ..Default::default()
    };
    let dom = parse_document(RcDom::default(), opts)
                   .from_utf8()
                   .read_from(&mut input)
                   .unwrap();

    dom_to_string(dom.document, &mut io::stderr(), width)
}

#[cfg(test)]
mod tests {
    use super::{from_read,wrap_text};
    #[test]
    fn test_table() {
        assert_eq!(from_read(&br##"

           1
           2
           3

"##[..], 12), r#"---+---+---
1  |2  |3
---+---+---
"#);
     }

     #[test]
     fn test_para() {
        assert_eq!(from_read(&b"
Hello
"[..], 10),
                   "Hello\n");
     }

     #[test]
     fn test_wrap() {
        assert_eq!(wrap_text("Hello", 10), "Hello\n");
     }
}
<|endoftext|>