<commit_before>extern crate varlink_parser;

use std::env;
use std::path::{Path, PathBuf};
use std::process::exit;
use std::io::Error as IOError;
use std::error::Error;
use std::io::{Read, Write};
use std::result::Result;
use std::fmt;
use std::collections::HashMap;
use std::iter::FromIterator;
use std::io;
use std::fs::File;

use varlink_parser::{Interface, VStructOrEnum, VType, VTypeExt, Varlink};

type EnumHash<'a> = HashMap<String, Vec<String>>;

trait ToRust {
    fn to_rust(&self, parent: &str, enumhash: &mut EnumHash) -> Result<String, ToRustError>;
}

#[derive(Debug)]
pub enum ToRustError {
    IoError(IOError),
}

impl Error for ToRustError {
    fn description(&self) -> &str {
        match *self {
            ToRustError::IoError(_) => "an I/O error occurred",
        }
    }

    fn cause(&self) -> Option<&Error> {
        match self {
            &ToRustError::IoError(ref err) => Some(&*err as &Error),
        }
    }
}

impl From<IOError> for ToRustError {
    fn from(err: IOError) -> ToRustError {
        ToRustError::IoError(err)
    }
}

impl fmt::Display for ToRustError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.description())?;
        Ok(())
    }
}

impl<'a> ToRust for VType<'a> {
    fn to_rust(&self, parent: &str, enumhash: &mut EnumHash) -> Result<String, ToRustError> {
        match *self {
            VType::Bool(_) => Ok("bool".into()),
            VType::Int(_) => Ok("i64".into()),
            VType::Float(_) => Ok("f64".into()),
            VType::VString(_) => Ok("String".into()),
            VType::VData(_) => Ok("String".into()),
            VType::VTypename(v) => Ok(v.into()),
            VType::VEnum(ref v) => {
                enumhash.insert(
                    parent.into(),
                    Vec::from_iter(v.elts.iter().map(|s| String::from(*s))),
                );
                Ok(format!("{}", parent).into())
            }
            VType::VStruct(_) => Ok(format!("{}", parent).into()),
        }
    }
}

impl<'a> ToRust for VTypeExt<'a> {
    fn to_rust(&self, parent: &str, enumhash: &mut EnumHash) -> Result<String, ToRustError> {
        let v = self.vtype.to_rust(parent, enumhash)?;

        if self.isarray {
            Ok(format!("Vec<{}>", v).into())
        } else {
            Ok(v.into())
        }
    }
}
fn to_snake_case(mut str: &str) -> String {
    let mut words = vec![];
    // Preserve leading underscores
    str = str.trim_left_matches(|c: char| {
        if c == '_' {
            words.push(String::new());
            true
        } else {
            false
        }
    });
    for s in str.split('_') {
        let mut last_upper = false;
        let mut buf = String::new();
        if s.is_empty() {
            continue;
        }
        for ch in s.chars() {
            if !buf.is_empty() && buf != "'" && ch.is_uppercase() && !last_upper {
                words.push(buf);
                buf = String::new();
            }
            last_upper = ch.is_uppercase();
            buf.extend(ch.to_lowercase());
        }
        words.push(buf);
    }
    words.join("_")
}

trait InterfaceToRust {
    fn to_rust(&self, description: &String) -> Result<String, ToRustError>;
}

impl<'a> InterfaceToRust for Interface<'a> {
    fn to_rust(&self, description: &String) -> Result<String, ToRustError> {
        let mut out: String = "".to_owned();
        let mut enumhash = EnumHash::new();

        for t in self.typedefs.values() {
            out += "#[derive(Serialize, Deserialize, Debug)]\n";
            match t.elt {
                VStructOrEnum::VStruct(ref v) => {
                    out += format!("pub struct {} {{\n", t.name).as_ref();
                    for e in &v.elts {
                        out += format!(
                            "    pub {}: Option<{}>,\n",
                            e.name,
                            e.vtype
                                .to_rust(format!("{}_{}", t.name, e.name).as_ref(), &mut enumhash)?
                        ).as_ref();
                    }
                }
                VStructOrEnum::VEnum(ref v) => {
                    out += format!("pub enum {} {{\n", t.name).as_ref();
                    let mut iter = v.elts.iter();
                    if let Some(fst) = iter.next() {
                        out += format!("    {}", fst).as_ref();
                        for elt in iter {
                            out += format!(",\n    {}", elt).as_ref();
                        }
                    }
                    out += "\n";
                }
            }
            out += "}\n\n";
        }

        for t in self.methods.values() {
            if t.output.elts.len() > 0 {
                out += "#[allow(non_camel_case_types)]\n#[derive(Serialize, Deserialize, Debug)]\n";
                out += format!("struct _{}Reply {{\n", t.name).as_ref();
                for e in &t.output.elts {
                    out += format!(
                        "    {}: Option<{}>,\n",
                        e.name,
                        e.vtype.to_rust(self.name, &mut enumhash)?
                    ).as_ref();
                }
                out += "}\n";
                out += format!("impl varlink::VarlinkReply for _{}Reply {{}}\n\n", t.name).as_ref();
            }

            if t.input.elts.len() > 0 {
                out += "#[allow(non_camel_case_types)]\n#[derive(Serialize, Deserialize, Debug)]\n";
                out += format!("struct _{}Args {{\n", t.name).as_ref();
                for e in &t.input.elts {
                    out += format!(
                        "    {}: Option<{}>,\n",
                        e.name,
                        e.vtype.to_rust(self.name, &mut enumhash)?
                    ).as_ref();
                }
                out += "}\n\n";
            }
        }

        for t in self.errors.values() {
            if t.parm.elts.len() > 0 {
                out += "#[allow(non_camel_case_types)]\n#[derive(Serialize, Deserialize, Debug)]\n";
                out += format!("struct _{}Args {{\n", t.name).as_ref();
                for e in &t.parm.elts {
                    out += format!(
                        "    {}: Option<{}>,\n",
                        e.name,
                        e.vtype.to_rust(self.name, &mut enumhash)?
                    ).as_ref();
                }
                out += "}\n\n";
            }
        }

        out += "pub trait _CallErr: varlink::CallTrait {\n";
        if self.errors.len() > 0 {
            for t in self.errors.values() {
                let mut inparms: String = "".to_owned();
                let mut innames: String = "".to_owned();
                if t.parm.elts.len() > 0 {
                    for e in &t.parm.elts {
                        inparms += format!(
                            ", {}: Option<{}>",
                            e.name,
                            e.vtype.to_rust(self.name, &mut enumhash)?
                        ).as_ref();
                        innames += format!("{}, ", e.name).as_ref();
                    }
                    innames.pop();
                    innames.pop();
                }

                out += format!(
                    r#"    fn reply_{}(&mut self{}) -> io::Result<()> {{
        self.reply_struct(varlink::Reply::error(
            "{}.{}".into(),
"#,
                    to_snake_case(t.name),
                    inparms,
                    self.name,
                    t.name,
                ).as_ref();

                out += format!(
                    "            Some(serde_json::to_value(_{}Args {{ {} }}).unwrap()),",
                    t.name, innames
                ).as_ref();

                out += r#"
        ))
    }
"#;
            }
        }
        out += "}\nimpl<'a> _CallErr for varlink::Call<'a> {}\n\n";

        for (name, v) in &enumhash {
            out += format!("pub enum {} {{\n", name).as_ref();
            let mut iter = v.iter();
            if let Some(fst) = iter.next() {
                out += format!("    {}", fst).as_ref();
                for elt in iter {
                    out += format!(",\n    {}", elt).as_ref();
                }
            }
            out += "\n}\n\n";
        }

        for t in self.methods.values() {
            let mut inparms: String = "".to_owned();
            let mut innames: String = "".to_owned();
            if t.output.elts.len() > 0 {
                for e in &t.output.elts {
                    inparms += format!(
                        ", {}: Option<{}>",
                        e.name,
                        e.vtype.to_rust(self.name, &mut enumhash)?
                    ).as_ref();
                    innames += format!("{}, ", e.name).as_ref();
                }
                innames.pop();
                innames.pop();
            }
            out += format!("pub trait _Call{}: _CallErr {{\n", t.name).as_ref();
            out += format!("    fn reply(&mut self{}) -> io::Result<()> {{\n", inparms).as_ref();
            out += format!(
                "        self.reply_struct(_{}Reply {{ {} }}.into())\n",
                t.name, innames
            ).as_ref();
            out += format!(
                "    }}\n}}\nimpl<'a> _Call{} for varlink::Call<'a> {{}}\n\n",
                t.name
            ).as_ref();
        }

        out += "pub trait VarlinkInterface {\n";
        for t in self.methods.values() {
            let mut inparms: String = "".to_owned();
            if t.input.elts.len() > 0 {
                for e in &t.input.elts {
                    inparms += format!(
                        ", {}: Option<{}>",
                        e.name,
                        e.vtype.to_rust(self.name, &mut enumhash)?
                    ).as_ref();
                }
            }

            out += format!(
                "    fn {}(&self, call: &mut _Call{}{}) -> io::Result<()>;\n",
                to_snake_case(t.name),
                t.name,
                inparms
            ).as_ref();
        }
        out += r#"    fn call_upgraded(&self, _call: &mut varlink::Call) -> io::Result<()> {
        Ok(())
    }
}
"#;

        out += format!(
            r####"
pub struct _InterfaceProxy {{
    inner: Box<VarlinkInterface + Send + Sync>,
}}

pub fn new(inner: Box<VarlinkInterface + Send + Sync>) -> _InterfaceProxy {{
    _InterfaceProxy {{ inner }}
}}

impl varlink::Interface for _InterfaceProxy {{
    fn get_description(&self) -> &'static str {{
        r#"
{}
"#
    }}

    fn get_name(&self) -> &'static str {{
        "{}"
    }}

"####,
            description, self.name
        ).as_ref();

        out += r#"    fn call_upgraded(&self, call: &mut varlink::Call) -> io::Result<()> {
        self.inner.call_upgraded(call)
    }

    fn call(&self, call: &mut varlink::Call) -> io::Result<()> {
        let req = call.request.unwrap();
        let method = req.method.clone();
        match method.as_ref() {
"#;

        for t in self.methods.values() {
            let mut inparms: String = "".to_owned();
            if t.input.elts.len() > 0 {
                let ref e = t.input.elts[0];
                inparms += format!("args.{}", e.name).as_ref();
                for e in &t.input.elts[1..] {
                    inparms += format!(", args.{}, ", e.name).as_ref();
                }
            }

            out += format!("            \"{}.{}\" => {{", self.name, t.name).as_ref();
            if t.input.elts.len() > 0 {
                out +=
                    format!(
                        concat!("\n                if let Some(args) = req.parameters.clone() {{\n",
"                    let args: _{}Args = serde_json::from_value(args)?;\n",
"                    return self.inner.{}(call as &mut _Call{}, {});\n",
"                }} else {{\n",
"                    return call.reply_invalid_parameter(None);\n",
"                }}\n",
"            }}\n"),
                        t.name,
                        to_snake_case(t.name), t.name,
                        inparms
                    ).as_ref();
            } else {
                out += format!(
                    "\n                return self.inner.{}(call as &mut _Call{});\n            }}\n",
                    to_snake_case(t.name), t.name
                ).as_ref();
            }
        }
        out += concat!(
            "\n",
            "            m => {\n",
            "                let method: String = m.clone().into();\n",
            "                return call.reply_method_not_found(Some(method));\n",
            "            }\n",
            "        }\n",
            "    }\n",
            "}"
        );

        Ok(out)
    }
}

pub fn generate(mut reader: Box<Read>, mut writer: Box<Write>) -> io::Result<()> {
    let mut buffer = String::new();

    reader.read_to_string(&mut buffer)?;

    let vr = Varlink::from_string(&buffer);

    if let Err(e) = vr {
        eprintln!("{}", e);
        exit(1);
    }

    match vr.unwrap().interface.to_rust(&buffer) {
        Ok(out) => {
            writeln!(
                writer,
                r#"// This file is automatically generated by the varlink rust generator
use std::io;

use varlink;
use serde_json;
use varlink::CallTrait;

{}"#,
                out
            )?;
        }
        Err(e) => {
            eprintln!("{}", e);
            exit(1);
        }
    }

    Ok(())
}

/// Errors are emitted to stderr and terminate the process.
pub fn cargo_build<T: AsRef<Path> + ?Sized>(input_path: &T) {
    let mut stderr = io::stderr();
    let input_path = input_path.as_ref();

    let reader: Box<Read>;

    let out_dir: PathBuf = env::var_os("OUT_DIR").unwrap().into();
    let rust_path = out_dir
        .join(input_path.file_name().unwrap())
        .with_extension("rs");

    let writer: Box<Write> = Box::new(File::create(&rust_path).unwrap());

    match File::open(input_path) {
        Ok(r) => reader = Box::new(r),
        Err(e) => {
            writeln!(
                stderr,
                "Could not read varlink input file `{}`: {}",
                input_path.display(),
                e
            ).unwrap();
            exit(1);
        }
    }
    if let Err(e) = generate(reader, writer) {
        writeln!(
            stderr,
            "Could not generate rust code from varlink file `{}`: {}",
            input_path.display(),
            e
        ).unwrap();
        exit(1);
    }

    println!("cargo:rerun-if-changed={}", input_path.display());
}
<commit_msg>generator: add Default derive to varlink types<commit_after>extern crate varlink_parser;

use std::env;
use std::path::{Path, PathBuf};
use std::process::exit;
use std::io::Error as IOError;
use std::error::Error;
use std::io::{Read, Write};
use std::result::Result;
use std::fmt;
use std::collections::HashMap;
use std::iter::FromIterator;
use std::io;
use std::fs::File;

use varlink_parser::{Interface, VStructOrEnum, VType, VTypeExt, Varlink};

type EnumHash<'a> = HashMap<String, Vec<String>>;

trait ToRust {
    fn to_rust(&self, parent: &str, enumhash: &mut EnumHash) -> Result<String, ToRustError>;
}

#[derive(Debug)]
pub enum ToRustError {
    IoError(IOError),
}

impl Error for ToRustError {
    fn description(&self) -> &str {
        match *self {
            ToRustError::IoError(_) => "an I/O error occurred",
        }
    }

    fn cause(&self) -> Option<&Error> {
        match self {
            &ToRustError::IoError(ref err) => Some(&*err as &Error),
        }
    }
}

impl From<IOError> for ToRustError {
    fn from(err: IOError) -> ToRustError {
        ToRustError::IoError(err)
    }
}

impl fmt::Display for ToRustError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.description())?;
        Ok(())
    }
}

impl<'a> ToRust for VType<'a> {
    fn to_rust(&self, parent: &str, enumhash: &mut EnumHash) -> Result<String, ToRustError> {
        match *self {
            VType::Bool(_) => Ok("bool".into()),
            VType::Int(_) => Ok("i64".into()),
            VType::Float(_) => Ok("f64".into()),
            VType::VString(_) => Ok("String".into()),
            VType::VData(_) => Ok("String".into()),
            VType::VTypename(v) => Ok(v.into()),
            VType::VEnum(ref v) => {
                enumhash.insert(
                    parent.into(),
                    Vec::from_iter(v.elts.iter().map(|s| String::from(*s))),
                );
                Ok(format!("{}", parent).into())
            }
            VType::VStruct(_) => Ok(format!("{}", parent).into()),
        }
    }
}

impl<'a> ToRust for VTypeExt<'a> {
    fn to_rust(&self, parent: &str, enumhash: &mut EnumHash) -> Result<String, ToRustError> {
        let v = self.vtype.to_rust(parent, enumhash)?;

        if self.isarray {
            Ok(format!("Vec<{}>", v).into())
        } else {
            Ok(v.into())
        }
    }
}
fn to_snake_case(mut str: &str) -> String {
    let mut words = vec![];
    // Preserve leading underscores
    str = str.trim_left_matches(|c: char| {
        if c == '_' {
            words.push(String::new());
            true
        } else {
            false
        }
    });
    for s in str.split('_') {
        let mut last_upper = false;
        let mut buf = String::new();
        if s.is_empty() {
            continue;
        }
        for ch in s.chars() {
            if !buf.is_empty() && buf != "'" && ch.is_uppercase() && !last_upper {
                words.push(buf);
                buf = String::new();
            }
            last_upper = ch.is_uppercase();
            buf.extend(ch.to_lowercase());
        }
        words.push(buf);
    }
    words.join("_")
}

trait InterfaceToRust {
    fn to_rust(&self, description: &String) -> Result<String, ToRustError>;
}

impl<'a> InterfaceToRust for Interface<'a> {
    fn to_rust(&self, description: &String) -> Result<String, ToRustError> {
        let mut out: String = "".to_owned();
        let mut enumhash = EnumHash::new();

        for t in self.typedefs.values() {
            out += "#[derive(Serialize, Deserialize, Debug, Default)]\n";
            match t.elt {
                VStructOrEnum::VStruct(ref v) => {
                    out += format!("pub struct {} {{\n", t.name).as_ref();
                    for e in &v.elts {
                        out += format!(
                            "    pub {}: Option<{}>,\n",
                            e.name,
                            e.vtype
                                .to_rust(format!("{}_{}", t.name, e.name).as_ref(), &mut enumhash)?
                        ).as_ref();
                    }
                }
                VStructOrEnum::VEnum(ref v) => {
                    out += format!("pub enum {} {{\n", t.name).as_ref();
                    let mut iter = v.elts.iter();
                    if let Some(fst) = iter.next() {
                        out += format!("    {}", fst).as_ref();
                        for elt in iter {
                            out += format!(",\n    {}", elt).as_ref();
                        }
                    }
                    out += "\n";
                }
            }
            out += "}\n\n";
        }

        for t in self.methods.values() {
            if t.output.elts.len() > 0 {
                out += "#[allow(non_camel_case_types)]\n#[derive(Serialize, Deserialize, Debug)]\n";
                out += format!("struct _{}Reply {{\n", t.name).as_ref();
                for e in &t.output.elts {
                    out += format!(
                        "    {}: Option<{}>,\n",
                        e.name,
                        e.vtype.to_rust(self.name, &mut enumhash)?
                    ).as_ref();
                }
                out += "}\n";
                out += format!("impl varlink::VarlinkReply for _{}Reply {{}}\n\n", t.name).as_ref();
            }

            if t.input.elts.len() > 0 {
                out += "#[allow(non_camel_case_types)]\n#[derive(Serialize, Deserialize, Debug)]\n";
                out += format!("struct _{}Args {{\n", t.name).as_ref();
                for e in &t.input.elts {
                    out += format!(
                        "    {}: Option<{}>,\n",
                        e.name,
                        e.vtype.to_rust(self.name, &mut enumhash)?
                    ).as_ref();
                }
                out += "}\n\n";
            }
        }

        for t in self.errors.values() {
            if t.parm.elts.len() > 0 {
                out += "#[allow(non_camel_case_types)]\n#[derive(Serialize, Deserialize, Debug)]\n";
                out += format!("struct _{}Args {{\n", t.name).as_ref();
                for e in &t.parm.elts {
                    out += format!(
                        "    {}: Option<{}>,\n",
                        e.name,
                        e.vtype.to_rust(self.name, &mut enumhash)?
                    ).as_ref();
                }
                out += "}\n\n";
            }
        }

        out += "pub trait _CallErr: varlink::CallTrait {\n";
        if self.errors.len() > 0 {
            for t in self.errors.values() {
                let mut inparms: String = "".to_owned();
                let mut innames: String = "".to_owned();
                if t.parm.elts.len() > 0 {
                    for e in &t.parm.elts {
                        inparms += format!(
                            ", {}: Option<{}>",
                            e.name,
                            e.vtype.to_rust(self.name, &mut enumhash)?
                        ).as_ref();
                        innames += format!("{}, ", e.name).as_ref();
                    }
                    innames.pop();
                    innames.pop();
                }
                out += format!(
                    r#"    fn reply_{}(&mut self{}) -> io::Result<()> {{
        self.reply_struct(varlink::Reply::error(
            "{}.{}".into(),
"#,
                    to_snake_case(t.name),
                    inparms,
                    self.name,
                    t.name,
                ).as_ref();

                out += format!(
                    "            Some(serde_json::to_value(_{}Args {{ {} }}).unwrap()),",
                    t.name, innames
                ).as_ref();

                out += r#"
        ))
    }
"#;
            }
        }
        out += "}\nimpl<'a> _CallErr for varlink::Call<'a> {}\n\n";

        for (name, v) in &enumhash {
            out += format!("pub enum {} {{\n", name).as_ref();
            let mut iter = v.iter();
            if let Some(fst) = iter.next() {
                out += format!("    {}", fst).as_ref();
                for elt in iter {
                    out += format!(",\n    {}", elt).as_ref();
                }
            }
            out += "\n}\n\n";
        }

        for t in self.methods.values() {
            let mut inparms: String = "".to_owned();
            let mut innames: String = "".to_owned();
            if t.output.elts.len() > 0 {
                for e in &t.output.elts {
                    inparms += format!(
                        ", {}: Option<{}>",
                        e.name,
                        e.vtype.to_rust(self.name, &mut enumhash)?
                    ).as_ref();
                    innames += format!("{}, ", e.name).as_ref();
                }
                innames.pop();
                innames.pop();
            }
            out += format!("pub trait _Call{}: _CallErr {{\n", t.name).as_ref();
            out += format!("    fn reply(&mut self{}) -> io::Result<()> {{\n", inparms).as_ref();
            if t.output.elts.len() > 0 {
                out += format!(
                    "        self.reply_struct(_{}Reply {{ {} }}.into())\n",
                    t.name, innames
                ).as_ref();
            } else {
                out += "        self.reply_struct(varlink::Reply::parameters(None))\n";
            }
            out += format!(
                "    }}\n}}\nimpl<'a> _Call{} for varlink::Call<'a> {{}}\n\n",
                t.name
            ).as_ref();
        }

        out += "pub trait VarlinkInterface {\n";
        for t in self.methods.values() {
            let mut inparms: String = "".to_owned();
            if t.input.elts.len() > 0 {
                for e in &t.input.elts {
                    inparms += format!(
                        ", {}: Option<{}>",
                        e.name,
                        e.vtype.to_rust(self.name, &mut enumhash)?
                    ).as_ref();
                }
            }

            out += format!(
                "    fn {}(&self, call: &mut _Call{}{}) -> io::Result<()>;\n",
                to_snake_case(t.name),
                t.name,
                inparms
            ).as_ref();
        }
        out += r#"    fn call_upgraded(&self, _call: &mut varlink::Call) -> io::Result<()> {
        Ok(())
    }
}
"#;

        out += format!(
            r####"
pub struct _InterfaceProxy {{
    inner: Box<VarlinkInterface + Send + Sync>,
}}

pub fn new(inner: Box<VarlinkInterface + Send + Sync>) -> _InterfaceProxy {{
    _InterfaceProxy {{ inner }}
}}

impl varlink::Interface for _InterfaceProxy {{
    fn get_description(&self) -> &'static str {{
        r#"
{}
"#
    }}

    fn get_name(&self) -> &'static str {{
        "{}"
    }}

"####,
            description, self.name
        ).as_ref();

        out += r#"    fn call_upgraded(&self, call: &mut varlink::Call) -> io::Result<()> {
        self.inner.call_upgraded(call)
    }

    fn call(&self, call: &mut varlink::Call) -> io::Result<()> {
        let req = call.request.unwrap();
        let method = req.method.clone();
        match method.as_ref() {
"#;

        for t in self.methods.values() {
            let mut inparms: String = "".to_owned();
            if t.input.elts.len() > 0 {
                let ref e = t.input.elts[0];
                inparms += format!("args.{}", e.name).as_ref();
                for e in &t.input.elts[1..] {
                    inparms += format!(", args.{}, ", e.name).as_ref();
                }
            }

            out += format!("            \"{}.{}\" => {{", self.name, t.name).as_ref();
            if t.input.elts.len() > 0 {
                out +=
                    format!(
                        concat!("\n                if let Some(args) = req.parameters.clone() {{\n",
"                    let args: _{}Args = serde_json::from_value(args)?;\n",
"                    return self.inner.{}(call as &mut _Call{}, {});\n",
"                }} else {{\n",
"                    return call.reply_invalid_parameter(None);\n",
"                }}\n",
"            }}\n"),
                        t.name,
                        to_snake_case(t.name), t.name,
                        inparms
                    ).as_ref();
            } else {
                out += format!(
                    "\n                return self.inner.{}(call as &mut _Call{});\n            }}\n",
                    to_snake_case(t.name), t.name
                ).as_ref();
            }
        }
        out += concat!(
            "\n",
            "            m => {\n",
            "                let method: String = m.clone().into();\n",
            "                return call.reply_method_not_found(Some(method));\n",
            "            }\n",
            "        }\n",
            "    }\n",
            "}"
        );

        Ok(out)
    }
}

pub fn generate(mut reader: Box<Read>, mut writer: Box<Write>) -> io::Result<()> {
    let mut buffer = String::new();

    reader.read_to_string(&mut buffer)?;

    let vr = Varlink::from_string(&buffer);

    if let Err(e) = vr {
        eprintln!("{}", e);
        exit(1);
    }

    match vr.unwrap().interface.to_rust(&buffer) {
        Ok(out) => {
            writeln!(
                writer,
                r#"// This file is automatically generated by the varlink rust generator
use std::io;

use varlink;
use serde_json;
use varlink::CallTrait;

{}"#,
                out
            )?;
        }
        Err(e) => {
            eprintln!("{}", e);
            exit(1);
        }
    }

    Ok(())
}

/// Errors are emitted to stderr and terminate the process.
pub fn cargo_build<T: AsRef<Path> + ?Sized>(input_path: &T) {
    let mut stderr = io::stderr();
    let input_path = input_path.as_ref();

    let reader: Box<Read>;

    let out_dir: PathBuf = env::var_os("OUT_DIR").unwrap().into();
    let rust_path = out_dir
        .join(input_path.file_name().unwrap())
        .with_extension("rs");

    let writer: Box<Write> = Box::new(File::create(&rust_path).unwrap());

    match File::open(input_path) {
        Ok(r) => reader = Box::new(r),
        Err(e) => {
            writeln!(
                stderr,
                "Could not read varlink input file `{}`: {}",
                input_path.display(),
                e
            ).unwrap();
            exit(1);
        }
    }
    if let Err(e) = generate(reader, writer) {
        writeln!(
            stderr,
            "Could not generate rust code from varlink file `{}`: {}",
            input_path.display(),
            e
        ).unwrap();
        exit(1);
    }

    println!("cargo:rerun-if-changed={}", input_path.display());
}
<|endoftext|>


<commit_before>pub use self::platform::*;

#[cfg(target_os = "windows")]
#[path="windows/mod.rs"]
mod platform;
#[cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd"))]
#[path="linux/mod.rs"]
mod platform;
#[cfg(target_os = "macos")]
#[path="macos/mod.rs"]
mod platform;
#[cfg(target_os = "android")]
#[path="android/mod.rs"]
mod platform;
#[cfg(target_os = "ios")]
#[path="ios/mod.rs"]
mod platform;
#[cfg(target_os = "emscripten")]
#[path="emscripten/mod.rs"]
mod platform;

#[cfg(all(not(target_os = "ios"), not(target_os = "windows"), not(target_os = "linux"),
  not(target_os = "macos"), not(target_os = "android"), not(target_os = "dragonfly"),
  not(target_os = "freebsd"), not(target_os = "openbsd"), not(target_os = "emscripten")))]
use this_platform_is_not_supported;
<commit_msg>Remove the `use platform_not_supported` hack (#285)<commit_after>pub use self::platform::*;

#[cfg(target_os = "windows")]
#[path="windows/mod.rs"]
mod platform;
#[cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd"))]
#[path="linux/mod.rs"]
mod platform;
#[cfg(target_os = "macos")]
#[path="macos/mod.rs"]
mod platform;
#[cfg(target_os = "android")]
#[path="android/mod.rs"]
mod platform;
#[cfg(target_os = "ios")]
#[path="ios/mod.rs"]
mod platform;
#[cfg(target_os = "emscripten")]
#[path="emscripten/mod.rs"]
mod platform;

#[cfg(all(not(target_os = "ios"), not(target_os = "windows"), not(target_os = "linux"),
  not(target_os = "macos"), not(target_os = "android"), not(target_os = "dragonfly"),
  not(target_os = "freebsd"), not(target_os = "openbsd"), not(target_os = "emscripten")))]
compile_error!("The platform you're compiling for is not supported by winit");
<|endoftext|>


<commit_before>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![deny(warnings)]

extern crate build_helper;
extern crate gcc;

use std::env;
use std::path::PathBuf;
use std::process::Command;
use build_helper::run;

fn main() {
    println!("cargo:rustc-cfg=cargobuild");
    println!("cargo:rerun-if-changed=build.rs");

    let target = env::var("TARGET").expect("TARGET was not set");
    let host = env::var("HOST").expect("HOST was not set");
    let build_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap());
    let src_dir = env::current_dir().unwrap();

    // FIXME: This is a hack to support building targets that don't
    // support jemalloc alongside hosts that do. The jemalloc build is
    // controlled by a feature of the std crate, and if that feature
    // changes between targets, it invalidates the fingerprint of
    // std's build script (this is a cargo bug); so we must ensure
    // that the feature set used by std is the same across all
    // targets, which means we have to build the alloc_jemalloc crate
    // for targets like emscripten, even if we don't use it.
    if target.contains("rumprun") || target.contains("bitrig") || target.contains("openbsd") ||
       target.contains("msvc") || target.contains("emscripten") || target.contains("fuchsia") {
        println!("cargo:rustc-cfg=dummy_jemalloc");
        return;
    }

    if let Some(jemalloc) = env::var_os("JEMALLOC_OVERRIDE") {
        let jemalloc = PathBuf::from(jemalloc);
        println!("cargo:rustc-link-search=native={}",
                 jemalloc.parent().unwrap().display());
        let stem = jemalloc.file_stem().unwrap().to_str().unwrap();
        let name = jemalloc.file_name().unwrap().to_str().unwrap();
        let kind = if name.ends_with(".a") {
            "static"
        } else {
            "dylib"
        };
        println!("cargo:rustc-link-lib={}={}", kind, &stem[3..]);
        return;
    }

    let compiler = gcc::Config::new().get_compiler();
    // only msvc returns None for ar so unwrap is okay
    let ar = build_helper::cc2ar(compiler.path(), &target).unwrap();
    let cflags = compiler.args()
        .iter()
        .map(|s| s.to_str().unwrap())
        .collect::<Vec<_>>()
        .join(" ");

    let mut stack = src_dir.join("../jemalloc")
        .read_dir()
        .unwrap()
        .map(|e| e.unwrap())
        .collect::<Vec<_>>();
    while let Some(entry) = stack.pop() {
        let path = entry.path();
        if entry.file_type().unwrap().is_dir() {
            stack.extend(path.read_dir().unwrap().map(|e| e.unwrap()));
        } else {
            println!("cargo:rerun-if-changed={}", path.display());
        }
    }

    let mut cmd = Command::new("sh");
    cmd.arg(src_dir.join("../jemalloc/configure")
                   .to_str()
                   .unwrap()
                   .replace("C:\\", "/c/")
                   .replace("\\", "/"))
       .current_dir(&build_dir)
       .env("CC", compiler.path())
       .env("EXTRA_CFLAGS", cflags.clone())
       // jemalloc generates Makefile deps using GCC's "-MM" flag. This means
       // that GCC will run the preprocessor, and only the preprocessor, over
       // jemalloc's source files. If we don't specify CPPFLAGS, then at least
       // on ARM that step fails with a "Missing implementation for 32-bit
       // atomic operations" error. This is because no "-march" flag will be
       // passed to GCC, and then GCC won't define the
       // "__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4" macro that jemalloc needs to
       // select an atomic operation implementation.
       .env("CPPFLAGS", cflags.clone())
       .env("AR", &ar)
       .env("RANLIB", format!("{} s", ar.display()));

    if target.contains("windows") {
        // A bit of history here, this used to be --enable-lazy-lock added in
        // #14006 which was filed with jemalloc in jemalloc/jemalloc#83 which
        // was also reported to MinGW:
        //
        //  http://sourceforge.net/p/mingw-w64/bugs/395/
        //
        // When updating jemalloc to 4.0, however, it was found that binaries
        // would exit with the status code STATUS_RESOURCE_NOT_OWNED indicating
        // that a thread was unlocking a mutex it never locked. Disabling this
        // "lazy lock" option seems to fix the issue, but it was enabled by
        // default for MinGW targets in 13473c7 for jemalloc.
        //
        // As a result of all that, force disabling lazy lock on Windows, and
        // after reading some code it at least *appears* that the initialization
        // of mutexes is otherwise ok in jemalloc, so shouldn't cause problems
        // hopefully...
        //
        // tl;dr: make windows behave like other platforms by disabling lazy
        //        locking, but requires passing an option due to a historical
        //        default with jemalloc.
        cmd.arg("--disable-lazy-lock");
    } else if target.contains("ios") {
        cmd.arg("--disable-tls");
    } else if target.contains("android") {
        // We force android to have prefixed symbols because apparently
        // replacement of the libc allocator doesn't quite work. When this was
        // tested (unprefixed symbols), it was found that the `realpath`
        // function in libc would allocate with libc malloc (not jemalloc
        // malloc), and then the standard library would free with jemalloc free,
        // causing a segfault.
        //
        // If the test suite passes, however, without symbol prefixes then we
        // should be good to go!
        cmd.arg("--with-jemalloc-prefix=je_");
        cmd.arg("--disable-tls");
    } else if target.contains("dragonfly") {
        cmd.arg("--with-jemalloc-prefix=je_");
    }

    if cfg!(feature = "debug-jemalloc") {
        cmd.arg("--enable-debug");
    }

    // Turn off broken quarantine (see jemalloc/jemalloc#161)
    cmd.arg("--disable-fill");
    cmd.arg(format!("--host={}", build_helper::gnu_target(&target)));
    cmd.arg(format!("--build={}", build_helper::gnu_target(&host)));

    run(&mut cmd);
    run(Command::new("make")
        .current_dir(&build_dir)
        .arg("build_lib_static")
        .arg("-j")
        .arg(env::var("NUM_JOBS").expect("NUM_JOBS was not set")));

    if target.contains("windows") {
        println!("cargo:rustc-link-lib=static=jemalloc");
    } else {
        println!("cargo:rustc-link-lib=static=jemalloc_pic");
    }
    println!("cargo:rustc-link-search=native={}/lib", build_dir.display());
    if target.contains("android") {
        println!("cargo:rustc-link-lib=gcc");
    } else if !target.contains("windows") && !target.contains("musl") {
        println!("cargo:rustc-link-lib=pthread");
    }
}
<commit_msg>don't rebuild alloc_jemalloc if jemalloc's .git directory has changed<commit_after>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![deny(warnings)]

extern crate build_helper;
extern crate gcc;

use std::env;
use std::path::PathBuf;
use std::process::Command;
use build_helper::run;

fn main() {
    println!("cargo:rustc-cfg=cargobuild");
    println!("cargo:rerun-if-changed=build.rs");

    let target = env::var("TARGET").expect("TARGET was not set");
    let host = env::var("HOST").expect("HOST was not set");
    let build_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap());
    let src_dir = env::current_dir().unwrap();

    // FIXME: This is a hack to support building targets that don't
    // support jemalloc alongside hosts that do. The jemalloc build is
    // controlled by a feature of the std crate, and if that feature
    // changes between targets, it invalidates the fingerprint of
    // std's build script (this is a cargo bug); so we must ensure
    // that the feature set used by std is the same across all
    // targets, which means we have to build the alloc_jemalloc crate
    // for targets like emscripten, even if we don't use it.
    if target.contains("rumprun") || target.contains("bitrig") || target.contains("openbsd") ||
       target.contains("msvc") || target.contains("emscripten") || target.contains("fuchsia") {
        println!("cargo:rustc-cfg=dummy_jemalloc");
        return;
    }

    if let Some(jemalloc) = env::var_os("JEMALLOC_OVERRIDE") {
        let jemalloc = PathBuf::from(jemalloc);
        println!("cargo:rustc-link-search=native={}",
                 jemalloc.parent().unwrap().display());
        let stem = jemalloc.file_stem().unwrap().to_str().unwrap();
        let name = jemalloc.file_name().unwrap().to_str().unwrap();
        let kind = if name.ends_with(".a") {
            "static"
        } else {
            "dylib"
        };
        println!("cargo:rustc-link-lib={}={}", kind, &stem[3..]);
        return;
    }

    let compiler = gcc::Config::new().get_compiler();
    // only msvc returns None for ar so unwrap is okay
    let ar = build_helper::cc2ar(compiler.path(), &target).unwrap();
    let cflags = compiler.args()
        .iter()
        .map(|s| s.to_str().unwrap())
        .collect::<Vec<_>>()
        .join(" ");

    let mut stack = src_dir.join("../jemalloc")
        .read_dir()
        .unwrap()
        .map(|e| e.unwrap())
        .filter(|e| &*e.file_name() != ".git")
        .collect::<Vec<_>>();
    while let Some(entry) = stack.pop() {
        let path = entry.path();
        if entry.file_type().unwrap().is_dir() {
            stack.extend(path.read_dir().unwrap().map(|e| e.unwrap()));
        } else {
            println!("cargo:rerun-if-changed={}", path.display());
        }
    }

    let mut cmd = Command::new("sh");
    cmd.arg(src_dir.join("../jemalloc/configure")
                   .to_str()
                   .unwrap()
                   .replace("C:\\", "/c/")
                   .replace("\\", "/"))
       .current_dir(&build_dir)
       .env("CC", compiler.path())
       .env("EXTRA_CFLAGS", cflags.clone())
       // jemalloc generates Makefile deps using GCC's "-MM" flag. This means
       // that GCC will run the preprocessor, and only the preprocessor, over
       // jemalloc's source files. If we don't specify CPPFLAGS, then at least
       // on ARM that step fails with a "Missing implementation for 32-bit
       // atomic operations" error. This is because no "-march" flag will be
       // passed to GCC, and then GCC won't define the
       // "__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4" macro that jemalloc needs to
       // select an atomic operation implementation.
       .env("CPPFLAGS", cflags.clone())
       .env("AR", &ar)
       .env("RANLIB", format!("{} s", ar.display()));

    if target.contains("windows") {
        // A bit of history here, this used to be --enable-lazy-lock added in
        // #14006 which was filed with jemalloc in jemalloc/jemalloc#83 which
        // was also reported to MinGW:
        //
        //  http://sourceforge.net/p/mingw-w64/bugs/395/
        //
        // When updating jemalloc to 4.0, however, it was found that binaries
        // would exit with the status code STATUS_RESOURCE_NOT_OWNED indicating
        // that a thread was unlocking a mutex it never locked. Disabling this
        // "lazy lock" option seems to fix the issue, but it was enabled by
        // default for MinGW targets in 13473c7 for jemalloc.
        //
        // As a result of all that, force disabling lazy lock on Windows, and
        // after reading some code it at least *appears* that the initialization
        // of mutexes is otherwise ok in jemalloc, so shouldn't cause problems
        // hopefully...
        //
        // tl;dr: make windows behave like other platforms by disabling lazy
        //        locking, but requires passing an option due to a historical
        //        default with jemalloc.
        cmd.arg("--disable-lazy-lock");
    } else if target.contains("ios") {
        cmd.arg("--disable-tls");
    } else if target.contains("android") {
        // We force android to have prefixed symbols because apparently
        // replacement of the libc allocator doesn't quite work. When this was
        // tested (unprefixed symbols), it was found that the `realpath`
        // function in libc would allocate with libc malloc (not jemalloc
        // malloc), and then the standard library would free with jemalloc free,
        // causing a segfault.
        //
        // If the test suite passes, however, without symbol prefixes then we
        // should be good to go!
        cmd.arg("--with-jemalloc-prefix=je_");
        cmd.arg("--disable-tls");
    } else if target.contains("dragonfly") {
        cmd.arg("--with-jemalloc-prefix=je_");
    }

    if cfg!(feature = "debug-jemalloc") {
        cmd.arg("--enable-debug");
    }

    // Turn off broken quarantine (see jemalloc/jemalloc#161)
    cmd.arg("--disable-fill");
    cmd.arg(format!("--host={}", build_helper::gnu_target(&target)));
    cmd.arg(format!("--build={}", build_helper::gnu_target(&host)));

    run(&mut cmd);
    run(Command::new("make")
        .current_dir(&build_dir)
        .arg("build_lib_static")
        .arg("-j")
        .arg(env::var("NUM_JOBS").expect("NUM_JOBS was not set")));

    if target.contains("windows") {
        println!("cargo:rustc-link-lib=static=jemalloc");
    } else {
        println!("cargo:rustc-link-lib=static=jemalloc_pic");
    }
    println!("cargo:rustc-link-search=native={}/lib", build_dir.display());
    if target.contains("android") {
        println!("cargo:rustc-link-lib=gcc");
    } else if !target.contains("windows") && !target.contains("musl") {
        println!("cargo:rustc-link-lib=pthread");
    }
}
<|endoftext|>


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

use dom::bindings::codegen::Bindings::MouseEventBinding;
use dom::bindings::codegen::InheritTypes::{UIEventCast, MouseEventDerived};
use dom::bindings::js::{JS, JSRef, RootedReference, Temporary, OptionalSettable};
use dom::bindings::error::Fallible;
use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object};
use dom::event::{Event, MouseEventTypeId};
use dom::eventtarget::EventTarget;
use dom::uievent::{UIEvent, UIEventMethods};
use dom::window::Window;
use servo_util::str::DOMString;
use std::cell::Cell;

#[deriving(Encodable)]
pub struct MouseEvent {
    pub mouseevent: UIEvent,
    pub screen_x: i32,
    pub screen_y: i32,
    pub client_x: i32,
    pub client_y: i32,
    pub ctrl_key: bool,
    pub shift_key: bool,
    pub alt_key: bool,
    pub meta_key: bool,
    pub button: i16,
    pub related_target: Cell<Option<JS<EventTarget>>>
}

impl MouseEventDerived for Event {
    fn is_mouseevent(&self) -> bool {
        self.type_id == MouseEventTypeId
    }
}

impl MouseEvent {
    pub fn new_inherited() -> MouseEvent {
        MouseEvent {
            mouseevent: UIEvent::new_inherited(MouseEventTypeId),
            screen_x: 0,
            screen_y: 0,
            client_x: 0,
            client_y: 0,
            ctrl_key: false,
            shift_key: false,
            alt_key: false,
            meta_key: false,
            button: 0,
            related_target: Cell::new(None)
        }
    }

    pub fn new_uninitialized(window: &JSRef<Window>) -> Temporary<MouseEvent> {
        reflect_dom_object(box MouseEvent::new_inherited(),
                           window,
                           MouseEventBinding::Wrap)
    }

    pub fn new(window: &JSRef<Window>,
               type_: DOMString,
               canBubble: bool,
               cancelable: bool,
               view: Option<JSRef<Window>>,
               detail: i32,
               screenX: i32,
               screenY: i32,
               clientX: i32,
               clientY: i32,
               ctrlKey: bool,
               altKey: bool,
               shiftKey: bool,
               metaKey: bool,
               button: i16,
               relatedTarget: Option<JSRef<EventTarget>>) -> Temporary<MouseEvent> {
        let mut ev = MouseEvent::new_uninitialized(window).root();
        ev.InitMouseEvent(type_, canBubble, cancelable, view, detail,
                          screenX, screenY, clientX, clientY,
                          ctrlKey, altKey, shiftKey, metaKey,
                          button, relatedTarget);
        Temporary::from_rooted(&*ev)
    }

    pub fn Constructor(owner: &JSRef<Window>,
                       type_: DOMString,
                       init: &MouseEventBinding::MouseEventInit) -> Fallible<Temporary<MouseEvent>> {
        let event = MouseEvent::new(owner, type_, init.parent.parent.bubbles,
                                    init.parent.parent.cancelable,
                                    init.parent.view.root_ref(),
                                    init.parent.detail,
                                    init.screenX, init.screenY,
                                    init.clientX, init.clientY, init.ctrlKey,
                                    init.altKey, init.shiftKey, init.metaKey,
                                    init.button, init.relatedTarget.root_ref());
        Ok(event)
    }
}

pub trait MouseEventMethods {
    fn ScreenX(&self) -> i32;
    fn ScreenY(&self) -> i32;
    fn ClientX(&self) -> i32;
    fn ClientY(&self) -> i32;
    fn CtrlKey(&self) -> bool;
    fn ShiftKey(&self) -> bool;
    fn AltKey(&self) -> bool;
    fn MetaKey(&self) -> bool;
    fn Button(&self) -> i16;
    fn GetRelatedTarget(&self) -> Option<Temporary<EventTarget>>;
    fn InitMouseEvent(&mut self,
                      typeArg: DOMString,
                      canBubbleArg: bool,
                      cancelableArg: bool,
                      viewArg: Option<JSRef<Window>>,
                      detailArg: i32,
                      screenXArg: i32,
                      screenYArg: i32,
                      clientXArg: i32,
                      clientYArg: i32,
                      ctrlKeyArg: bool,
                      altKeyArg: bool,
                      shiftKeyArg: bool,
                      metaKeyArg: bool,
                      buttonArg: i16,
                      relatedTargetArg: Option<JSRef<EventTarget>>);
}

impl<'a> MouseEventMethods for JSRef<'a, MouseEvent> {
    fn ScreenX(&self) -> i32 {
        self.screen_x
    }

    fn ScreenY(&self) -> i32 {
        self.screen_y
    }

    fn ClientX(&self) -> i32 {
        self.client_x
    }

    fn ClientY(&self) -> i32 {
        self.client_y
    }

    fn CtrlKey(&self) -> bool {
        self.ctrl_key
    }

    fn ShiftKey(&self) -> bool {
        self.shift_key
    }

    fn AltKey(&self) -> bool {
        self.alt_key
    }

    fn MetaKey(&self) -> bool {
        self.meta_key
    }

    fn Button(&self) -> i16 {
        self.button
    }

    fn GetRelatedTarget(&self) -> Option<Temporary<EventTarget>> {
        self.related_target.get().clone().map(|target| Temporary::new(target))
    }

    fn InitMouseEvent(&mut self,
                      typeArg: DOMString,
                      canBubbleArg: bool,
                      cancelableArg: bool,
                      viewArg: Option<JSRef<Window>>,
                      detailArg: i32,
                      screenXArg: i32,
                      screenYArg: i32,
                      clientXArg: i32,
                      clientYArg: i32,
                      ctrlKeyArg: bool,
                      altKeyArg: bool,
                      shiftKeyArg: bool,
                      metaKeyArg: bool,
                      buttonArg: i16,
                      relatedTargetArg: Option<JSRef<EventTarget>>) {
        {
            let uievent: &mut JSRef<UIEvent> = UIEventCast::from_mut_ref(self);
            uievent.InitUIEvent(typeArg, canBubbleArg, cancelableArg, viewArg, detailArg);
        }
        self.screen_x = screenXArg;
        self.screen_y = screenYArg;
        self.client_x = clientXArg;
        self.client_y = clientYArg;
        self.ctrl_key = ctrlKeyArg;
        self.alt_key = altKeyArg;
        self.shift_key = shiftKeyArg;
        self.meta_key = metaKeyArg;
        self.button = buttonArg;
        self.related_target.assign(relatedTargetArg);
    }
}

impl Reflectable for MouseEvent {
    fn reflector<'a>(&'a self) -> &'a Reflector {
        self.mouseevent.reflector()
    }

    fn mut_reflector<'a>(&'a mut self) -> &'a mut Reflector {
        self.mouseevent.mut_reflector()
    }
}
<commit_msg>Use interior mutability for MouseEvent.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use dom::bindings::codegen::Bindings::MouseEventBinding;
use dom::bindings::codegen::InheritTypes::{UIEventCast, MouseEventDerived};
use dom::bindings::error::Fallible;
use dom::bindings::js::{JS, JSRef, RootedReference, Temporary, OptionalSettable};
use dom::bindings::trace::Traceable;
use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object};
use dom::event::{Event, MouseEventTypeId};
use dom::eventtarget::EventTarget;
use dom::uievent::{UIEvent, UIEventMethods};
use dom::window::Window;
use servo_util::str::DOMString;
use std::cell::Cell;

#[deriving(Encodable)]
pub struct MouseEvent {
    pub mouseevent: UIEvent,
    pub screen_x: Traceable<Cell<i32>>,
    pub screen_y: Traceable<Cell<i32>>,
    pub client_x: Traceable<Cell<i32>>,
    pub client_y: Traceable<Cell<i32>>,
    pub ctrl_key: Traceable<Cell<bool>>,
    pub shift_key: Traceable<Cell<bool>>,
    pub alt_key: Traceable<Cell<bool>>,
    pub meta_key: Traceable<Cell<bool>>,
    pub button: Traceable<Cell<i16>>,
    pub related_target: Cell<Option<JS<EventTarget>>>
}

impl MouseEventDerived for Event {
    fn is_mouseevent(&self) -> bool {
        self.type_id == MouseEventTypeId
    }
}

impl MouseEvent {
    pub fn new_inherited() -> MouseEvent {
        MouseEvent {
            mouseevent: UIEvent::new_inherited(MouseEventTypeId),
            screen_x: Traceable::new(Cell::new(0)),
            screen_y: Traceable::new(Cell::new(0)),
            client_x: Traceable::new(Cell::new(0)),
            client_y: Traceable::new(Cell::new(0)),
            ctrl_key: Traceable::new(Cell::new(false)),
            shift_key: Traceable::new(Cell::new(false)),
            alt_key: Traceable::new(Cell::new(false)),
            meta_key: Traceable::new(Cell::new(false)),
            button: Traceable::new(Cell::new(0)),
            related_target: Cell::new(None)
        }
    }

    pub fn new_uninitialized(window: &JSRef<Window>) -> Temporary<MouseEvent> {
        reflect_dom_object(box MouseEvent::new_inherited(),
                           window,
                           MouseEventBinding::Wrap)
    }

    pub fn new(window: &JSRef<Window>,
               type_: DOMString,
               canBubble: bool,
               cancelable: bool,
               view: Option<JSRef<Window>>,
               detail: i32,
               screenX: i32,
               screenY: i32,
               clientX: i32,
               clientY: i32,
               ctrlKey: bool,
               altKey: bool,
               shiftKey: bool,
               metaKey: bool,
               button: i16,
               relatedTarget: Option<JSRef<EventTarget>>) -> Temporary<MouseEvent> {
        let ev = MouseEvent::new_uninitialized(window).root();
        ev.deref().InitMouseEvent(type_, canBubble, cancelable, view, detail,
                                  screenX, screenY, clientX, clientY,
                                  ctrlKey, altKey, shiftKey, metaKey,
                                  button, relatedTarget);
        Temporary::from_rooted(&*ev)
    }

    pub fn Constructor(owner: &JSRef<Window>,
                       type_: DOMString,
                       init: &MouseEventBinding::MouseEventInit) -> Fallible<Temporary<MouseEvent>> {
        let event = MouseEvent::new(owner, type_, init.parent.parent.bubbles,
                                    init.parent.parent.cancelable,
                                    init.parent.view.root_ref(),
                                    init.parent.detail,
                                    init.screenX, init.screenY,
                                    init.clientX, init.clientY, init.ctrlKey,
                                    init.altKey, init.shiftKey, init.metaKey,
                                    init.button, init.relatedTarget.root_ref());
        Ok(event)
    }
}

pub trait MouseEventMethods {
    fn ScreenX(&self) -> i32;
    fn ScreenY(&self) -> i32;
    fn ClientX(&self) -> i32;
    fn ClientY(&self) -> i32;
    fn CtrlKey(&self) -> bool;
    fn ShiftKey(&self) -> bool;
    fn AltKey(&self) -> bool;
    fn MetaKey(&self) -> bool;
    fn Button(&self) -> i16;
    fn GetRelatedTarget(&self) -> Option<Temporary<EventTarget>>;
    fn InitMouseEvent(&self,
                      typeArg: DOMString,
                      canBubbleArg: bool,
                      cancelableArg: bool,
                      viewArg: Option<JSRef<Window>>,
                      detailArg: i32,
                      screenXArg: i32,
                      screenYArg: i32,
                      clientXArg: i32,
                      clientYArg: i32,
                      ctrlKeyArg: bool,
                      altKeyArg: bool,
                      shiftKeyArg: bool,
                      metaKeyArg: bool,
                      buttonArg: i16,
                      relatedTargetArg: Option<JSRef<EventTarget>>);
}

impl<'a> MouseEventMethods for JSRef<'a, MouseEvent> {
    fn ScreenX(&self) -> i32 {
        self.screen_x.deref().get()
    }

    fn ScreenY(&self) -> i32 {
        self.screen_y.deref().get()
    }

    fn ClientX(&self) -> i32 {
        self.client_x.deref().get()
    }

    fn ClientY(&self) -> i32 {
        self.client_y.deref().get()
    }

    fn CtrlKey(&self) -> bool {
        self.ctrl_key.deref().get()
    }

    fn ShiftKey(&self) -> bool {
        self.shift_key.deref().get()
    }

    fn AltKey(&self) -> bool {
        self.alt_key.deref().get()
    }

    fn MetaKey(&self) -> bool {
        self.meta_key.deref().get()
    }

    fn Button(&self) -> i16 {
        self.button.deref().get()
    }

    fn GetRelatedTarget(&self) -> Option<Temporary<EventTarget>> {
        self.related_target.get().clone().map(|target| Temporary::new(target))
    }

    fn InitMouseEvent(&self,
                      typeArg: DOMString,
                      canBubbleArg: bool,
                      cancelableArg: bool,
                      viewArg: Option<JSRef<Window>>,
                      detailArg: i32,
                      screenXArg: i32,
                      screenYArg: i32,
                      clientXArg: i32,
                      clientYArg: i32,
                      ctrlKeyArg: bool,
                      altKeyArg: bool,
                      shiftKeyArg: bool,
                      metaKeyArg: bool,
                      buttonArg: i16,
                      relatedTargetArg: Option<JSRef<EventTarget>>) {
        let uievent: &JSRef<UIEvent> = UIEventCast::from_ref(self);
        uievent.InitUIEvent(typeArg, canBubbleArg, cancelableArg, viewArg, detailArg);
        self.screen_x.deref().set(screenXArg);
        self.screen_y.deref().set(screenYArg);
        self.client_x.deref().set(clientXArg);
        self.client_y.deref().set(clientYArg);
        self.ctrl_key.deref().set(ctrlKeyArg);
        self.alt_key.deref().set(altKeyArg);
        self.shift_key.deref().set(shiftKeyArg);
        self.meta_key.deref().set(metaKeyArg);
        self.button.deref().set(buttonArg);
        self.related_target.assign(relatedTargetArg);
    }
}

impl Reflectable for MouseEvent {
    fn reflector<'a>(&'a self) -> &'a Reflector {
        self.mouseevent.reflector()
    }

    fn mut_reflector<'a>(&'a mut self) -> &'a mut Reflector {
        self.mouseevent.mut_reflector()
    }
}
<|endoftext|>


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

use std::fs::create_dir_all;
use std::fs::{File, read_to_string};
use std::io::{Write};
use std::path::Path;
use serde_json::{self, json, Value as Json};
use toml::Value;

use crate::common::templating::TemplateRenderer;
use crate::common::ui::{Status, UIWriter, UI};
use crate::error::Result;

pub fn start(
    ui: &mut UI,
    template_path: &Path,
    default_toml_path: &Path,
    user_toml_path: Option<&Path>,
    mock_data_path: Option<&Path>,
    print: bool,
    no_render_dir: bool,
    render_dir: &Path,
    quiet: bool,
) -> Result<()> {
    // Strip the file name out of our passed template
    let file_name = match Path::new(&template_path).file_name() {
        Some(name) => name.to_str().clone().unwrap(),
        None => panic!(format!("Something went wrong getting filename of {:?}", &template_path)),
    };

    if !(quiet) {
      ui.begin(format!("Rendering: {:?} into: {:?} as: {:?}", template_path, render_dir, file_name))?;
      ui.br()?;
    }

    // read our template from file
    let template = read_to_string(&template_path)?;

    // create a "data" json struct
    let mut data = json!({});

    if !(quiet) {
        // import default.toml values, convert to JSON
        ui.begin(format!("Importing default.toml: {:?}", &default_toml_path))?;
    }

    // we should always have a default.toml, would be nice to "autodiscover" based on package name,
    // for now assume we're working in the plan dir if --default-toml not passed
    let default_toml = read_to_string(&default_toml_path)?;

    // merge default into data struct
    merge(&mut data, toml_to_json(&default_toml));

    // import default.toml values, convert to JSON
    let user_toml = match user_toml_path {
        Some(path) => {
            if !(quiet) {
              // print helper message, maybe only print if '--verbose'? how?
              ui.begin(format!("Importing user.toml: {:?}", path))?;
            }
            read_to_string(path)?
        },
        None => String::new(),
    };
    // merge default into data struct
    merge(&mut data, toml_to_json(&user_toml));

    // read mock data if provided
    let mock_data = match mock_data_path {
        Some(path) => {
            if !(quiet) {
                // print helper message, maybe only print if '--verbose'? how?
                ui.begin(format!("Importing override file: {:?}", path))?;
            }
            read_to_string(path)?
        },
        // return an empty json block if '--mock-data' isn't defined.
        // this allows us to merge an empty JSON block
        None => "{}".to_string(),
    };
    // merge mock data into data
    merge(&mut data, serde_json::from_str(&mock_data).unwrap());

    // create a template renderer
    let mut renderer = TemplateRenderer::new();
    // register our template
    renderer
        .register_template_string(&template, &template)
        .expect("Could not register template content");
    // render our JSON override in our template.
    let rendered_template = renderer.render(&template, &data).ok().unwrap();

    if print {
        if !(quiet) {
          ui.br()?;
          ui.warn(format!("###======== Rendered template: {:?}", &template_path))?;
        }

        println!("{}", rendered_template);

        if !(quiet) {
          ui.warn(format!("========### End rendered template: {:?}", &template_path))?;
        }
    }

    // if not no render dir (aka "unless no_render_dir == true")
    if !(no_render_dir) {
      // Render our template file
      create_with_template(ui, &Path::new(render_dir).join(file_name), &rendered_template, quiet)?;
    }

    if !(quiet) {
      ui.br()?;
    }
    Ok(())
}

fn toml_to_json(cfg: &str) -> Json {
    let toml_value = cfg.parse::<Value>().expect("Error parsing TOML");
    let toml_string = serde_json::to_string(&toml_value).expect("Error encoding JSON");
    serde_json::from_str(&format!(r#"{{ "cfg": {} }}"#, &toml_string)).unwrap()
}

// merge two Json structs
fn merge(a: &mut Json, b: Json) {
    match (a, b) {
        // not sure I understand this...
        (a @ &mut Json::Object(_), Json::Object(b)) => {
            // not sure I understand why we unwrap this
            let a = a.as_object_mut().unwrap();
            // Iterate through key/values in Json object b,
            // merge with Json object b
            for (k, v) in b {
                merge(a.entry(k).or_insert(Json::Null), v);
            }
        }
        // or this...
        (a, b) => *a = b,
    }
}

fn create_with_template(ui: &mut UI, location: &std::path::PathBuf, template: &str, quiet: bool) -> Result<()> {
    let path = Path::new(&location);
    if !(quiet) {
        ui.status(Status::Creating, format!("file: {:?}", location))?;
    }
    // If the directory doesn't exist we need to make it.
    if let Some(directory) = path.parent() {
        create_dir_all(directory)?;
    }
    // Write file to disk
    File::create(path).and_then(|mut file| file.write(template.as_bytes()))?;
    Ok(())
}
<commit_msg>update create_with_template to not check if "render_dir" exists before attempting to create.  This also resolves the need to "name.to_str().clone().unwrap()" the filename.<commit_after>// Copyright (c) 2019 Chef Software Inc. and/or applicable contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::fs::create_dir_all;
use std::fs::{File, read_to_string};
use std::io::{Write};
use std::path::Path;
use serde_json::{self, json, Value as Json};
use toml::Value;

use crate::common::templating::TemplateRenderer;
use crate::common::ui::{Status, UIWriter, UI};
use crate::error::Result;

pub fn start(
    ui: &mut UI,
    template_path: &Path,
    default_toml_path: &Path,
    user_toml_path: Option<&Path>,
    mock_data_path: Option<&Path>,
    print: bool,
    no_render_dir: bool,
    render_dir: &Path,
    quiet: bool,
) -> Result<()> {
    // Strip the file name out of our passed template
    let file_name = match Path::new(&template_path).file_name() {
        Some(name) => Path::new(name),
        None => panic!(format!("Something went wrong getting filename of {:?}", &template_path)),
    };

    if !(quiet) {
      ui.begin(format!("Rendering: {:?} into: {:?} as: {:?}", template_path, render_dir, file_name))?;
      ui.br()?;
    }

    // read our template from file
    let template = read_to_string(&template_path)?;

    // create a "data" json struct
    let mut data = json!({});

    if !(quiet) {
        // import default.toml values, convert to JSON
        ui.begin(format!("Importing default.toml: {:?}", &default_toml_path))?;
    }

    // we should always have a default.toml, would be nice to "autodiscover" based on package name,
    // for now assume we're working in the plan dir if --default-toml not passed
    let default_toml = read_to_string(&default_toml_path)?;

    // merge default into data struct
    merge(&mut data, toml_to_json(&default_toml));

    // import default.toml values, convert to JSON
    let user_toml = match user_toml_path {
        Some(path) => {
            if !(quiet) {
              // print helper message, maybe only print if '--verbose'? how?
              ui.begin(format!("Importing user.toml: {:?}", path))?;
            }
            read_to_string(path)?
        },
        None => String::new(),
    };
    // merge default into data struct
    merge(&mut data, toml_to_json(&user_toml));

    // read mock data if provided
    let mock_data = match mock_data_path {
        Some(path) => {
            if !(quiet) {
                // print helper message, maybe only print if '--verbose'? how?
                ui.begin(format!("Importing override file: {:?}", path))?;
            }
            read_to_string(path)?
        },
        // return an empty json block if '--mock-data' isn't defined.
        // this allows us to merge an empty JSON block
        None => "{}".to_string(),
    };
    // merge mock data into data
    merge(&mut data, serde_json::from_str(&mock_data).unwrap());

    // create a template renderer
    let mut renderer = TemplateRenderer::new();
    // register our template
    renderer
        .register_template_string(&template, &template)
        .expect("Could not register template content");
    // render our JSON override in our template.
    let rendered_template = renderer.render(&template, &data).ok().unwrap();

    if print {
        if !(quiet) {
          ui.br()?;
          ui.warn(format!("###======== Rendered template: {:?}", &template_path))?;
        }

        println!("{}", rendered_template);

        if !(quiet) {
          ui.warn(format!("========### End rendered template: {:?}", &template_path))?;
        }
    }

    // if not no render dir (aka "unless no_render_dir == true")
    if !(no_render_dir) {
      // Render our template file
      create_with_template(ui, &render_dir, &file_name, &rendered_template, quiet)?;
    }

    if !(quiet) {
      ui.br()?;
    }
    Ok(())
}

fn toml_to_json(cfg: &str) -> Json {
    let toml_value = cfg.parse::<Value>().expect("Error parsing TOML");
    let toml_string = serde_json::to_string(&toml_value).expect("Error encoding JSON");
    serde_json::from_str(&format!(r#"{{ "cfg": {} }}"#, &toml_string)).unwrap()
}

// merge two Json structs
fn merge(a: &mut Json, b: Json) {
    match (a, b) {
        // not sure I understand this...
        (a @ &mut Json::Object(_), Json::Object(b)) => {
            // not sure I understand why we unwrap this
            let a = a.as_object_mut().unwrap();
            // Iterate through key/values in Json object b,
            // merge with Json object b
            for (k, v) in b {
                merge(a.entry(k).or_insert(Json::Null), v);
            }
        }
        // or this...
        (a, b) => *a = b,
    }
}

fn create_with_template(ui: &mut UI, render_dir: &std::path::Path, file_name: &std::path::Path, template: &str, quiet: bool) -> Result<()> {
    let path = Path::new(&render_dir).join(&file_name);
    if !(quiet) {
        ui.status(Status::Creating, format!("file: {:?}", path))?;
    }

    create_dir_all(render_dir)?;

    // Write file to disk
    File::create(path).and_then(|mut file| file.write(template.as_bytes()))?;
    Ok(())
}
<|endoftext|>


<commit_before>// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0

//! Cerberus protocol messages.
//!
//! A Cerberus protocol message (also called a "command") consists of three
//! parts:
//! - A *command type*, representing the type of command this is.
//! - A *request bit*, indicating whether this message is a request or a
//!   response.
//! - A *body*, which is a buffer that is interpreted according to the
//!   command type.
//!
//! There is also a special "error" response, with no corresponding request,
//! which is used both to indicate an error, and as an ACK-type response for
//! requests that do not otherwise have data to respond with.
//!
//! This module provides a way to convert a parsed `manticore` header
//! (consisting, at minimum, of a command type and a request bit) and a buffer
//! into a request or response, and, also, to reverse that process.
//!
//! If a message type contains a large buffer, the message type will have an
//! appropriate lifetime attached to it.
//!
//! In addition to providing (de)serialization to and from the Cerberus wire
//! format (via the [`wire` module]), the `serde` feature will provide relevant
//! implementations of [`serde`] traits, for (de)serialization to and from
//! human-readable formats, like JSON.
//!
//! ---
//!
//! This module provides a subset of required and optional commands specified
//! by Cerberus. In particular, `manticore` does not yet implement
//! authentication or secure channel negotiation.
//!
//! `manticore` also provides some additional protocol messages not specified
//! by Cerberus, encoded using command type bytes not allocated by Cerberus.
//!
//! Also, unlike Cerberus, `manticore` does not require that a session be
//! spoken over MCTP, and, as such, does not use the same header as Cerberus.
//!
//! [`wire` module]: wire/index.html
//! [`serde`]: https://serde.rs

// This is required due to the make_fuzz_safe! macro.
#![allow(unused_parens)]

use core::convert::TryInto;

use crate::io::Read;
use crate::io::Write;
use crate::protocol::wire::FromWire;
use crate::protocol::wire::FromWireError;
use crate::protocol::wire::ToWire;
use crate::protocol::wire::ToWireError;

#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};

#[macro_use]
mod macros;
#[cfg(feature = "arbitrary-derive")]
pub use macros::FuzzSafe;

#[macro_use]
pub mod wire;

pub mod device_id;
pub use device_id::DeviceId;

pub mod device_info;
pub use device_info::DeviceInfo;

pub mod device_uptime;
pub use device_uptime::DeviceUptime;

pub mod capabilities;
pub use capabilities::DeviceCapabilities;

pub mod firmware_version;
pub use firmware_version::FirmwareVersion;

pub mod reset_counter;
pub use reset_counter::ResetCounter;

pub mod request_counter;
pub use request_counter::RequestCounter;

#[cfg(test)]
pub(crate) mod test_util;

/// A Cerberus command.
///
/// A Cerberus command is identified by two types, each of which has a
/// corresponding [`CommandType`]:
/// - A unique request type.
/// - A response type, which may be [`Error`].
///
/// This trait is not implemented by any of the request or response types, but
/// is intead implemented by uninhabited types that represent pairs of requests
/// and responses, for use in generic programming.
///
/// [`CommandType`]: enum.CommandType.html
/// [`Error`]: struct.Error.html
pub trait Command<'req> {
    /// The unique request type for this `Command`.
    type Req: Request<'req>;
    /// The response type for this `Command`, which will either be unique or
    /// [`Error`].
    ///
    /// [`Error`]: struct.Error.html
    type Resp: Response<'req>;
}

/// A Cerberus request.
///
/// See [`Command`](trait.Command.html).
pub trait Request<'req>: FromWire<'req> + ToWire {
    /// The unique [`CommandType`] for this `Request`.
    ///
    /// [`CommandType`]: enum.CommandType.html
    const TYPE: CommandType;
}

/// A Cerberus response.
///
/// See [`Command`](trait.Command.html).
pub trait Response<'req>: FromWire<'req> + ToWire {
    /// The unique [`CommandType`] for this `Response`.
    ///
    /// [`CommandType`]: enum.CommandType.html
    const TYPE: CommandType;
}

wire_enum! {
    /// A Cerberus command type.
    ///
    /// This enum represents all command types implemented by `manticore`,
    /// including any `manticore`-specific messages not defined by Cerberus.
    #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
    pub enum CommandType: u8 {
        /// An error message (or a trivial command ACK).
        ///
        /// See [`Error`](struct.Error.html).
        Error = 0x7f,
        /// A request for the RoT's firmware version.
        ///
        /// See [`FirmwareVersion`].
        ///
        /// [`FirmwareVersion`]:
        ///     firmware_version/struct.FirmwareVersion.html
        FirmwareVersion = 0x01,
        /// A request to negotiate device capabilities.
        ///
        /// See [`DeviceCapabilities`].
        ///
        /// [`DeviceCapabilities`]:
        ///     capabilites/struct.DeviceCapabilities.html
        DeviceCapabilities = 0x02,
        /// A request for this device's identity.
        ///
        /// See [`DeviceId`].
        ///
        /// [`DeviceId`]:
        ///     device_id/struct.DeviceId.html
        DeviceId = 0x03,
        /// A request for information about this device.
        ///
        /// See [`DeviceInfo`].
        ///
        /// [`DeviceInfo`]:
        ///     device_info/struct.DeviceInfo.html
        DeviceInfo = 0x04,
        /// A request for the number of times the device has been reset since
        /// POR.
        ///
        /// See [`ResetCounter`].
        ///
        /// [`ResetCounter`]:
        ///     reset_counter/struct.ResetCounter.html
        ResetCounter = 0x87,
        /// A request for the uptime of the device since last reset.
        ///
        /// Note that this command is a `manticore` extension.
        ///
        /// See [`DeviceUptime`].
        ///
        /// [`DeviceUptime`]:
        ///     device_uptime/struct.DeviceUptime.html
        DeviceUptime = 0xa0,
        /// A request for an approximate number of requests the device has
        /// handled since last reset.
        ///
        /// Note that this command is a `manticore` extension.
        ///
        /// See [`RequestCounter`].
        ///
        /// [`RequestCounter`]:
        ///     device_uptime/struct.RequestCounter.html
        RequestCounter = 0xa1,
    }
}

impl CommandType {
    /// Returns `true` when `self` represents a `manticore` extension to the
    /// protocol.
    pub fn is_manticore_extension(self) -> bool {
        match self {
            Self::DeviceUptime => true,
            _ => false,
        }
    }
}

/// A parsed `manticore` header.
///
/// This struct represents all of the meaningful fields from a `manticore`
/// header; the actual format of the header is left up to an integration of
/// this library.
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Header {
    /// The [`CommandType`] for a request.
    ///
    /// [`CommandType`]: enum.CommandType.html
    pub command: CommandType,
    /// The "request bit", for interpreting whether the body is the
    /// request or response variant of a command.
    pub is_request: bool,
}

/// The length of a `manticore` header on the wire, in bytes.
pub const HEADER_LEN: usize = 5;

/// A magic number required at the start of each `manticore` header.
const HEADER_MAGIC: &[u8] = &[0b01111110, 0x14, 0x14];

impl<'a> FromWire<'a> for Header {
    fn from_wire<R: Read<'a>>(mut r: R) -> Result<Self, FromWireError> {
        if r.read_bytes(3)? != HEADER_MAGIC {
            return Err(FromWireError::OutOfRange);
        }

        let request_byte = r.read_le::<u8>()?;
        let is_request = match request_byte {
            0b0000_0000 => false,
            0b1000_0000 => true,
            _ => return Err(FromWireError::OutOfRange),
        };

        let command = CommandType::from_wire(r)?;
        Ok(Self {
            is_request,
            command,
        })
    }
}

impl ToWire for Header {
    fn to_wire<W: Write>(&self, mut w: W) -> Result<(), ToWireError> {
        w.write_bytes(HEADER_MAGIC)?;
        w.write_le((self.is_request as u8) << 7)?;
        self.command.to_wire(w)?;
        Ok(())
    }
}

wire_enum! {
    /// A Cerberus error.
    ///
    /// This enum represents all error types implemented by `manticore`.
    /// Because `manticore` does not mandate running the connection over MCTP,
    /// the MCTP-specific error codes are omited.
    #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
    pub enum ErrorCode: u8 {
        /// Represents a successful operation; this "error" code is used to
        /// turn an [`Error`] into an ACK.
        ///
        /// [`Error`]: struct.Error.html
        Ok = 0x00,
        /// Indicates that the device is "busy", usually meaning that other
        /// commands are being serviced.
        Busy = 0x03,
        /// Indicates an unspecified, vendor-defined error, which may include
        /// extra data in an [`Error`].
        ///
        /// [`Error`]: struct.Error.html
        Unspecified = 0x04,
    }
}

/// A generic error response.
///
/// This message is used to indicate that a request resulted in an error, or
/// as an ACK for commands that otherwise have no response message.
///
/// This command corresponds to [`CommandType::Error`] and does not have a
/// request counterpart.
///
/// [`CommandType::Error`]: enum.CommandType.html#variant.Error
#[derive(Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Error {
    /// What kind of error this is (or, it's merely an ACK).
    pub code: ErrorCode,
    /// A fixed array of "extra data" that can come with an error code.
    pub data: [u8; 4],
}

impl Error {
    /// Creates an "ACK" response, consisting of an `Error` with an
    /// [`ErrorCode::Ok`] code.
    ///
    /// [`ErrorCode::Ok`]: enum.ErrorCode.html#variant.Ok
    pub fn new_ack() -> Self {
        Self {
            code: ErrorCode::Ok,
            data: [0; 4],
        }
    }
}

impl Response<'_> for Error {
    const TYPE: CommandType = CommandType::Error;
}

impl<'a> FromWire<'a> for Error {
    fn from_wire<R: Read<'a>>(mut r: R) -> Result<Self, FromWireError> {
        let code = ErrorCode::from_wire(&mut r)?;
        let data: [u8; 4] = r
            .read_bytes(4)?
            .try_into()
            .map_err(|_| FromWireError::OutOfRange)?;

        Ok(Self { code, data })
    }
}

impl ToWire for Error {
    fn to_wire<W: Write>(&self, mut w: W) -> Result<(), ToWireError> {
        self.code.to_wire(&mut w)?;
        w.write_bytes(&self.data[..])?;
        Ok(())
    }
}
<commit_msg>[protocol] Add missing impl Debug for protocol::Error<commit_after>// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0

//! Cerberus protocol messages.
//!
//! A Cerberus protocol message (also called a "command") consists of three
//! parts:
//! - A *command type*, representing the type of command this is.
//! - A *request bit*, indicating whether this message is a request or a
//!   response.
//! - A *body*, which is a buffer that is interpreted according to the
//!   command type.
//!
//! There is also a special "error" response, with no corresponding request,
//! which is used both to indicate an error, and as an ACK-type response for
//! requests that do not otherwise have data to respond with.
//!
//! This module provides a way to convert a parsed `manticore` header
//! (consisting, at minimum, of a command type and a request bit) and a buffer
//! into a request or response, and, also, to reverse that process.
//!
//! If a message type contains a large buffer, the message type will have an
//! appropriate lifetime attached to it.
//!
//! In addition to providing (de)serialization to and from the Cerberus wire
//! format (via the [`wire` module]), the `serde` feature will provide relevant
//! implementations of [`serde`] traits, for (de)serialization to and from
//! human-readable formats, like JSON.
//!
//! ---
//!
//! This module provides a subset of required and optional commands specified
//! by Cerberus. In particular, `manticore` does not yet implement
//! authentication or secure channel negotiation.
//!
//! `manticore` also provides some additional protocol messages not specified
//! by Cerberus, encoded using command type bytes not allocated by Cerberus.
//!
//! Also, unlike Cerberus, `manticore` does not require that a session be
//! spoken over MCTP, and, as such, does not use the same header as Cerberus.
//!
//! [`wire` module]: wire/index.html
//! [`serde`]: https://serde.rs

// This is required due to the make_fuzz_safe! macro.
#![allow(unused_parens)]

use core::convert::TryInto;

use crate::io::Read;
use crate::io::Write;
use crate::protocol::wire::FromWire;
use crate::protocol::wire::FromWireError;
use crate::protocol::wire::ToWire;
use crate::protocol::wire::ToWireError;

#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};

#[macro_use]
mod macros;
#[cfg(feature = "arbitrary-derive")]
pub use macros::FuzzSafe;

#[macro_use]
pub mod wire;

pub mod device_id;
pub use device_id::DeviceId;

pub mod device_info;
pub use device_info::DeviceInfo;

pub mod device_uptime;
pub use device_uptime::DeviceUptime;

pub mod capabilities;
pub use capabilities::DeviceCapabilities;

pub mod firmware_version;
pub use firmware_version::FirmwareVersion;

pub mod reset_counter;
pub use reset_counter::ResetCounter;

pub mod request_counter;
pub use request_counter::RequestCounter;

#[cfg(test)]
pub(crate) mod test_util;

/// A Cerberus command.
///
/// A Cerberus command is identified by two types, each of which has a
/// corresponding [`CommandType`]:
/// - A unique request type.
/// - A response type, which may be [`Error`].
///
/// This trait is not implemented by any of the request or response types, but
/// is intead implemented by uninhabited types that represent pairs of requests
/// and responses, for use in generic programming.
///
/// [`CommandType`]: enum.CommandType.html
/// [`Error`]: struct.Error.html
pub trait Command<'req> {
    /// The unique request type for this `Command`.
    type Req: Request<'req>;
    /// The response type for this `Command`, which will either be unique or
    /// [`Error`].
    ///
    /// [`Error`]: struct.Error.html
    type Resp: Response<'req>;
}

/// A Cerberus request.
///
/// See [`Command`](trait.Command.html).
pub trait Request<'req>: FromWire<'req> + ToWire {
    /// The unique [`CommandType`] for this `Request`.
    ///
    /// [`CommandType`]: enum.CommandType.html
    const TYPE: CommandType;
}

/// A Cerberus response.
///
/// See [`Command`](trait.Command.html).
pub trait Response<'req>: FromWire<'req> + ToWire {
    /// The unique [`CommandType`] for this `Response`.
    ///
    /// [`CommandType`]: enum.CommandType.html
    const TYPE: CommandType;
}

wire_enum! {
    /// A Cerberus command type.
    ///
    /// This enum represents all command types implemented by `manticore`,
    /// including any `manticore`-specific messages not defined by Cerberus.
    #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
    pub enum CommandType: u8 {
        /// An error message (or a trivial command ACK).
        ///
        /// See [`Error`](struct.Error.html).
        Error = 0x7f,
        /// A request for the RoT's firmware version.
        ///
        /// See [`FirmwareVersion`].
        ///
        /// [`FirmwareVersion`]:
        ///     firmware_version/struct.FirmwareVersion.html
        FirmwareVersion = 0x01,
        /// A request to negotiate device capabilities.
        ///
        /// See [`DeviceCapabilities`].
        ///
        /// [`DeviceCapabilities`]:
        ///     capabilites/struct.DeviceCapabilities.html
        DeviceCapabilities = 0x02,
        /// A request for this device's identity.
        ///
        /// See [`DeviceId`].
        ///
        /// [`DeviceId`]:
        ///     device_id/struct.DeviceId.html
        DeviceId = 0x03,
        /// A request for information about this device.
        ///
        /// See [`DeviceInfo`].
        ///
        /// [`DeviceInfo`]:
        ///     device_info/struct.DeviceInfo.html
        DeviceInfo = 0x04,
        /// A request for the number of times the device has been reset since
        /// POR.
        ///
        /// See [`ResetCounter`].
        ///
        /// [`ResetCounter`]:
        ///     reset_counter/struct.ResetCounter.html
        ResetCounter = 0x87,
        /// A request for the uptime of the device since last reset.
        ///
        /// Note that this command is a `manticore` extension.
        ///
        /// See [`DeviceUptime`].
        ///
        /// [`DeviceUptime`]:
        ///     device_uptime/struct.DeviceUptime.html
        DeviceUptime = 0xa0,
        /// A request for an approximate number of requests the device has
        /// handled since last reset.
        ///
        /// Note that this command is a `manticore` extension.
        ///
        /// See [`RequestCounter`].
        ///
        /// [`RequestCounter`]:
        ///     device_uptime/struct.RequestCounter.html
        RequestCounter = 0xa1,
    }
}

impl CommandType {
    /// Returns `true` when `self` represents a `manticore` extension to the
    /// protocol.
    pub fn is_manticore_extension(self) -> bool {
        match self {
            Self::DeviceUptime => true,
            _ => false,
        }
    }
}

/// A parsed `manticore` header.
///
/// This struct represents all of the meaningful fields from a `manticore`
/// header; the actual format of the header is left up to an integration of
/// this library.
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Header {
    /// The [`CommandType`] for a request.
    ///
    /// [`CommandType`]: enum.CommandType.html
    pub command: CommandType,
    /// The "request bit", for interpreting whether the body is the
    /// request or response variant of a command.
    pub is_request: bool,
}

/// The length of a `manticore` header on the wire, in bytes.
pub const HEADER_LEN: usize = 5;

/// A magic number required at the start of each `manticore` header.
const HEADER_MAGIC: &[u8] = &[0b01111110, 0x14, 0x14];

impl<'a> FromWire<'a> for Header {
    fn from_wire<R: Read<'a>>(mut r: R) -> Result<Self, FromWireError> {
        if r.read_bytes(3)? != HEADER_MAGIC {
            return Err(FromWireError::OutOfRange);
        }

        let request_byte = r.read_le::<u8>()?;
        let is_request = match request_byte {
            0b0000_0000 => false,
            0b1000_0000 => true,
            _ => return Err(FromWireError::OutOfRange),
        };

        let command = CommandType::from_wire(r)?;
        Ok(Self {
            is_request,
            command,
        })
    }
}

impl ToWire for Header {
    fn to_wire<W: Write>(&self, mut w: W) -> Result<(), ToWireError> {
        w.write_bytes(HEADER_MAGIC)?;
        w.write_le((self.is_request as u8) << 7)?;
        self.command.to_wire(w)?;
        Ok(())
    }
}

wire_enum! {
    /// A Cerberus error.
    ///
    /// This enum represents all error types implemented by `manticore`.
    /// Because `manticore` does not mandate running the connection over MCTP,
    /// the MCTP-specific error codes are omited.
    #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
    pub enum ErrorCode: u8 {
        /// Represents a successful operation; this "error" code is used to
        /// turn an [`Error`] into an ACK.
        ///
        /// [`Error`]: struct.Error.html
        Ok = 0x00,
        /// Indicates that the device is "busy", usually meaning that other
        /// commands are being serviced.
        Busy = 0x03,
        /// Indicates an unspecified, vendor-defined error, which may include
        /// extra data in an [`Error`].
        ///
        /// [`Error`]: struct.Error.html
        Unspecified = 0x04,
    }
}

/// A generic error response.
///
/// This message is used to indicate that a request resulted in an error, or
/// as an ACK for commands that otherwise have no response message.
///
/// This command corresponds to [`CommandType::Error`] and does not have a
/// request counterpart.
///
/// [`CommandType::Error`]: enum.CommandType.html#variant.Error
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Error {
    /// What kind of error this is (or, it's merely an ACK).
    pub code: ErrorCode,
    /// A fixed array of "extra data" that can come with an error code.
    pub data: [u8; 4],
}

impl Error {
    /// Creates an "ACK" response, consisting of an `Error` with an
    /// [`ErrorCode::Ok`] code.
    ///
    /// [`ErrorCode::Ok`]: enum.ErrorCode.html#variant.Ok
    pub fn new_ack() -> Self {
        Self {
            code: ErrorCode::Ok,
            data: [0; 4],
        }
    }
}

impl Response<'_> for Error {
    const TYPE: CommandType = CommandType::Error;
}

impl<'a> FromWire<'a> for Error {
    fn from_wire<R: Read<'a>>(mut r: R) -> Result<Self, FromWireError> {
        let code = ErrorCode::from_wire(&mut r)?;
        let data: [u8; 4] = r
            .read_bytes(4)?
            .try_into()
            .map_err(|_| FromWireError::OutOfRange)?;

        Ok(Self { code, data })
    }
}

impl ToWire for Error {
    fn to_wire<W: Write>(&self, mut w: W) -> Result<(), ToWireError> {
        self.code.to_wire(&mut w)?;
        w.write_bytes(&self.data[..])?;
        Ok(())
    }
}
<|endoftext|>