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

use crate::{
    conversion::ConvertError,
    types::{make_ident, TypeName},
};
use indoc::indoc;
use lazy_static::lazy_static;
use std::collections::HashMap;
use syn::{parse_quote, GenericArgument, PathArguments, Type, TypePath, TypePtr};

/// Whether this type should be included in the 'prelude'
/// passed to bindgen, and if so, how.
#[derive(Debug)]
enum PreludePolicy {
    Exclude,
    IncludeNormal,
    IncludeTemplated,
}

//// The behavior of the type.
#[derive(Debug)]
enum Behavior {
    CxxContainerByValueSafe,
    CxxContainerNotByValueSafe,
    CxxString,
    RustStr,
    RustString,
    RustByValue,
    CByValue,
    CVariableLengthByValue,
    CVoid,
}

/// Details about known special types, mostly primitives.
#[derive(Debug)]
struct TypeDetails {
    /// The name used by cxx (in Rust code) for this type.
    rs_name: String,
    /// C++ equivalent name for a Rust type.
    cpp_name: String,
    //// The behavior of the type.
    behavior: Behavior,
    /// Whether and how to include this in the prelude given to bindgen.
    prelude_policy: PreludePolicy,
    /// Any extra non-canonical names
    extra_non_canonical_name: Option<String>,
}

impl TypeDetails {
    #[allow(clippy::too_many_arguments)]
    fn new(
        rs_name: String,
        cpp_name: String,
        behavior: Behavior,
        prelude_policy: PreludePolicy,
        extra_non_canonical_name: Option<String>,
    ) -> Self {
        TypeDetails {
            rs_name,
            cpp_name,
            behavior,
            prelude_policy,
            extra_non_canonical_name,
        }
    }

    fn get_prelude_entry(&self) -> Option<String> {
        match self.prelude_policy {
            PreludePolicy::Exclude => None,
            PreludePolicy::IncludeNormal | PreludePolicy::IncludeTemplated => {
                let tn = TypeName::new_from_user_input(&self.rs_name);
                let cxx_name = tn.get_final_ident();
                let (templating, payload) = match self.prelude_policy {
                    PreludePolicy::IncludeNormal => ("", "char* ptr"),
                    PreludePolicy::IncludeTemplated => ("template<typename T> ", "T* ptr"),
                    _ => unreachable!(),
                };
                Some(format!(
                    indoc! {"
                    /**
                    *

                    */
                    {}class {} {{
                        {};
                    }};

                    "},
                    self.cpp_name, templating, cxx_name, payload
                ))
            }
        }
    }

    fn to_type_path(&self) -> TypePath {
        let segs = self.rs_name.split("::").map(make_ident);
        parse_quote! {
            #(#segs)::*
        }
    }
}

/// Database of known types.
pub(crate) struct TypeDatabase {
    by_rs_name: HashMap<TypeName, TypeDetails>,
    canonical_names: HashMap<TypeName, TypeName>,
}

lazy_static! {
    /// Database of known types.
    pub(crate) static ref KNOWN_TYPES: TypeDatabase = create_type_database();
}

impl TypeDatabase {
    fn get(&self, ty: &TypeName) -> Option<&TypeDetails> {
        // The following line is important. It says that
        // when we encounter something like 'std::unique_ptr'
        // in the bindgen-generated bindings, we'll immediately
        // start to refer to that as 'UniquePtr' henceforth.
        let canonical_name = self.canonical_names.get(ty).unwrap_or(ty);
        self.by_rs_name.get(canonical_name)
    }

    /// Prelude of C++ for squirting into bindgen. This configures
    /// bindgen to output simpler types to replace some STL types
    /// that bindgen just can't cope with. Although we then replace
    /// those types with cxx types (e.g. UniquePtr), this intermediate
    /// step is still necessary because bindgen can't otherwise
    /// give us the templated types (e.g. when faced with the STL
    /// unique_ptr, bindgen would normally give us std_unique_ptr
    /// as opposed to std_unique_ptr<T>.)
    pub(crate) fn get_prelude(&self) -> String {
        itertools::join(
            self.by_rs_name
                .values()
                .filter_map(|t| t.get_prelude_entry()),
            "\n",
        )
    }

    /// Types which are known to be safe (or unsafe) to hold and pass by
    /// value in Rust.
    pub(crate) fn get_pod_safe_types(&self) -> impl Iterator<Item = (&TypeName, bool)> {
        self.by_rs_name.iter().map(|(tn, td)| {
            (
                tn,
                match td.behavior {
                    Behavior::CxxContainerByValueSafe
                    | Behavior::RustStr
                    | Behavior::RustString
                    | Behavior::RustByValue
                    | Behavior::CByValue
                    | Behavior::CVariableLengthByValue => true,
                    Behavior::CxxString
                    | Behavior::CxxContainerNotByValueSafe
                    | Behavior::CVoid => false,
                },
            )
        })
    }

    /// Whether this TypePath should be treated as a value in C++
    /// but a reference in Rust. This only applies to rust::Str
    /// (C++ name) which is &str in Rust.
    pub(crate) fn should_dereference_in_cpp(&self, typ: &TypePath) -> bool {
        let tn = TypeName::from_type_path(typ);
        self.get(&tn)
            .map(|td| matches!(td.behavior, Behavior::RustStr))
            .unwrap_or(false)
    }

    /// Here we substitute any names which we know are Special from
    /// our type database, e.g. std::unique_ptr -> UniquePtr.
    /// We strip off and ignore
    /// any PathArguments within this TypePath - callers should
    /// put them back again if needs be.
    pub(crate) fn known_type_substitute_path(&self, typ: &TypePath) -> Option<TypePath> {
        let tn = TypeName::from_type_path(typ);
        self.get(&tn).map(|td| td.to_type_path())
    }

    pub(crate) fn special_cpp_name(&self, rs: &TypeName) -> Option<String> {
        self.get(rs).map(|x| x.cpp_name.to_string())
    }

    pub(crate) fn is_known_type(&self, ty: &TypeName) -> bool {
        self.get(ty).is_some()
    }

    pub(crate) fn known_type_type_path(&self, ty: &TypeName) -> Option<TypePath> {
        self.get(ty).map(|td| td.to_type_path())
    }

    /// Whether this is one of the ctypes (mostly variable length integers)
    /// which we need to wrap.
    pub(crate) fn is_ctype(&self, ty: &TypeName) -> bool {
        self.get(ty)
            .map(|td| match td.behavior {
                Behavior::CVariableLengthByValue | Behavior::CVoid => true,
                _ => false,
            })
            .unwrap_or(false)
    }

    /// Whether this is a generic type acceptable to cxx. Otherwise,
    /// if we encounter a generic, we'll replace it with a synthesized concrete
    /// type.
    pub(crate) fn is_cxx_acceptable_generic(&self, ty: &TypeName) -> bool {
        self.get(ty)
            .map(|x| match x.behavior {
                Behavior::CxxContainerByValueSafe | Behavior::CxxContainerNotByValueSafe => true,
                _ => false,
            })
            .unwrap_or(false)
    }

    pub(crate) fn convertible_from_strs(&self, ty: &TypeName) -> bool {
        self.get(ty)
            .map(|x| matches!(x.behavior, Behavior::CxxString))
            .unwrap_or(false)
    }
}

fn create_type_database() -> TypeDatabase {
    let mut by_rs_name = HashMap::new();

    let mut do_insert =
        |td: TypeDetails| by_rs_name.insert(TypeName::new_from_user_input(&td.rs_name), td);

    do_insert(TypeDetails::new(
        "cxx::UniquePtr".into(),
        "std::unique_ptr".into(),
        Behavior::CxxContainerByValueSafe,
        PreludePolicy::IncludeTemplated,
        None,
    ));
    do_insert(TypeDetails::new(
        "cxx::CxxVector".into(),
        "std::vector".into(),
        Behavior::CxxContainerNotByValueSafe,
        PreludePolicy::IncludeTemplated,
        None,
    ));
    do_insert(TypeDetails::new(
        "cxx::SharedPtr".into(),
        "std::shared_ptr".into(),
        Behavior::CxxContainerByValueSafe,
        PreludePolicy::IncludeTemplated,
        None,
    ));
    do_insert(TypeDetails::new(
        "cxx::CxxString".into(),
        "std::string".into(),
        Behavior::CxxString,
        PreludePolicy::IncludeNormal,
        None,
    ));
    do_insert(TypeDetails::new(
        "str".into(),
        "rust::Str".into(),
        Behavior::RustStr,
        PreludePolicy::IncludeNormal,
        None,
    ));
    do_insert(TypeDetails::new(
        "String".into(),
        "rust::String".into(),
        Behavior::RustString,
        PreludePolicy::IncludeNormal,
        None,
    ));
    do_insert(TypeDetails::new(
        "i8".into(),
        "int8_t".into(),
        Behavior::CByValue,
        PreludePolicy::Exclude,
        Some("std::os::raw::c_schar".into()),
    ));
    do_insert(TypeDetails::new(
        "u8".into(),
        "uint8_t".into(),
        Behavior::CByValue,
        PreludePolicy::Exclude,
        Some("std::os::raw::c_uchar".into()),
    ));
    for (cpp_type, rust_type) in (4..7)
        .map(|x| 2i32.pow(x))
        .map(|x| {
            vec![
                (format!("uint{}_t", x), format!("u{}", x)),
                (format!("int{}_t", x), format!("i{}", x)),
            ]
        })
        .flatten()
    {
        do_insert(TypeDetails::new(
            rust_type,
            cpp_type,
            Behavior::CByValue,
            PreludePolicy::Exclude,
            None,
        ));
    }
    do_insert(TypeDetails::new(
        "bool".into(),
        "bool".into(),
        Behavior::CByValue,
        PreludePolicy::Exclude,
        None,
    ));

    do_insert(TypeDetails::new(
        "std::pin::Pin".into(),
        "Pin".into(),
        Behavior::RustByValue, // because this is actually Pin<&something>
        PreludePolicy::Exclude,
        None,
    ));

    let mut insert_ctype = |cname: &str| {
        let td = TypeDetails::new(
            format!("autocxx::c_{}", cname),
            cname.into(),
            Behavior::CVariableLengthByValue,
            PreludePolicy::Exclude,
            Some(format!("std::os::raw::c_{}", cname)),
        );
        by_rs_name.insert(TypeName::new_from_user_input(&td.rs_name), td);
        let td = TypeDetails::new(
            format!("autocxx::c_u{}", cname),
            format!("unsigned {}", cname),
            Behavior::CVariableLengthByValue,
            PreludePolicy::Exclude,
            Some(format!("std::os::raw::c_u{}", cname)),
        );
        by_rs_name.insert(TypeName::new_from_user_input(&td.rs_name), td);
    };

    insert_ctype("long");
    insert_ctype("int");
    insert_ctype("short");

    let td = TypeDetails::new(
        "f32".into(),
        "float".into(),
        Behavior::CByValue,
        PreludePolicy::Exclude,
        None,
    );
    by_rs_name.insert(TypeName::new_from_user_input(&td.rs_name), td);

    let td = TypeDetails::new(
        "f64".into(),
        "double".into(),
        Behavior::CByValue,
        PreludePolicy::Exclude,
        None,
    );
    by_rs_name.insert(TypeName::new_from_user_input(&td.rs_name), td);

    let td = TypeDetails::new(
        "std::os::raw::c_char".into(),
        "char".into(),
        Behavior::CByValue,
        PreludePolicy::Exclude,
        None,
    );
    by_rs_name.insert(TypeName::new_from_user_input(&td.rs_name), td);

    let td = TypeDetails::new(
        "autocxx::c_void".into(),
        "void".into(),
        Behavior::CVoid,
        PreludePolicy::Exclude,
        Some(format!("std::os::raw::c_void")),
    );
    by_rs_name.insert(TypeName::new_from_user_input(&td.rs_name), td);

    let mut by_cppname = HashMap::new();
    for td in by_rs_name.values() {
        let rs_name = TypeName::new_from_user_input(&td.rs_name);
        if let Some(extra_non_canonical_name) = &td.extra_non_canonical_name {
            by_cppname.insert(
                TypeName::new_from_user_input(extra_non_canonical_name),
                rs_name.clone(),
            );
        }
        by_cppname.insert(TypeName::new_from_user_input(&td.cpp_name), rs_name);
    }

    TypeDatabase {
        by_rs_name,
        canonical_names: by_cppname,
    }
}

/// This is worked out basically using trial and error.
/// Excluding std* and rust* is obvious, but the other items...
/// in theory bindgen ought to be smart enough to work out that
/// they're not used and therefore not generate code for them.
/// But it doesm unless we blocklist them. This is obviously
/// a bit sensitive to the particular STL in use so one day
/// it would be good to dig into bindgen's behavior here - TODO.
const BINDGEN_BLOCKLIST: &[&str] = &["std.*", "__gnu.*", ".*mbstate_t.*", "rust.*"];

/// Get the list of types to give to bindgen to ask it _not_ to
/// generate code for.
pub(crate) fn get_initial_blocklist() -> Vec<String> {
    BINDGEN_BLOCKLIST.iter().map(|s| s.to_string()).collect()
}

/// If a given type lacks a copy constructor, we should always use
/// std::move in wrapper functions.
pub(crate) fn type_lacks_copy_constructor(ty: &Type) -> bool {
    // In future we may wish to look this up in KNOWN_TYPES.
    match ty {
        Type::Path(typ) => {
            let tn = TypeName::from_type_path(typ);
            tn.to_cpp_name().starts_with("std::unique_ptr")
        }
        _ => false,
    }
}

pub(crate) fn confirm_inner_type_is_acceptable_generic_payload(
    path_args: &PathArguments,
    desc: &TypeName,
) -> Result<(), ConvertError> {
    // For now, all supported generics accept the same payloads. This
    // may change in future in which case we'll need to accept more arguments here.
    match path_args {
        PathArguments::None => Ok(()),
        PathArguments::Parenthesized(_) => Err(ConvertError::TemplatedTypeContainingNonPathArg(
            desc.clone(),
        )),
        PathArguments::AngleBracketed(ab) => {
            for inner in &ab.args {
                match inner {
                    GenericArgument::Type(Type::Path(typ)) => {
                        if let Some(more_generics) = typ.path.segments.last() {
                            confirm_inner_type_is_acceptable_generic_payload(
                                &more_generics.arguments,
                                desc,
                            )?;
                        }
                    }
                    _ => {
                        return Err(ConvertError::TemplatedTypeContainingNonPathArg(
                            desc.clone(),
                        ))
                    }
                }
            }
            Ok(())
        }
    }
}

pub(crate) fn ensure_pointee_is_valid(ptr: &TypePtr) -> Result<(), ConvertError> {
    match *ptr.elem {
        Type::Path(..) => Ok(()),
        _ => Err(ConvertError::InvalidPointee),
    }
}
<commit_msg>Remove PreludePolicy.<commit_after>// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use crate::{
    conversion::ConvertError,
    types::{make_ident, TypeName},
};
use indoc::indoc;
use lazy_static::lazy_static;
use std::collections::HashMap;
use syn::{parse_quote, GenericArgument, PathArguments, Type, TypePath, TypePtr};

//// The behavior of the type.
#[derive(Debug)]
enum Behavior {
    CxxContainerByValueSafe,
    CxxContainerNotByValueSafe,
    CxxString,
    RustStr,
    RustString,
    RustByValue,
    CByValue,
    CVariableLengthByValue,
    CVoid,
}

/// Details about known special types, mostly primitives.
#[derive(Debug)]
struct TypeDetails {
    /// The name used by cxx (in Rust code) for this type.
    rs_name: String,
    /// C++ equivalent name for a Rust type.
    cpp_name: String,
    //// The behavior of the type.
    behavior: Behavior,
    /// Any extra non-canonical names
    extra_non_canonical_name: Option<String>,
}

impl TypeDetails {
    fn new(
        rs_name: String,
        cpp_name: String,
        behavior: Behavior,
        extra_non_canonical_name: Option<String>,
    ) -> Self {
        TypeDetails {
            rs_name,
            cpp_name,
            behavior,
            extra_non_canonical_name,
        }
    }

    /// Whether and how to include this in the prelude given to bindgen.
    fn get_prelude_entry(&self) -> Option<String> {
        match self.behavior {
            Behavior::RustString
            | Behavior::RustStr
            | Behavior::CxxString
            | Behavior::CxxContainerByValueSafe
            | Behavior::CxxContainerNotByValueSafe => {
                let tn = TypeName::new_from_user_input(&self.rs_name);
                let cxx_name = tn.get_final_ident();
                let (templating, payload) = match self.behavior {
                    Behavior::CxxContainerByValueSafe | Behavior::CxxContainerNotByValueSafe => {
                        ("template<typename T> ", "T* ptr")
                    }
                    _ => ("", "char* ptr"),
                };
                Some(format!(
                    indoc! {"
                    /**
                    *

                    */
                    {}class {} {{
                        {};
                    }};

                    "},
                    self.cpp_name, templating, cxx_name, payload
                ))
            }
            _ => None,
        }
    }

    fn to_type_path(&self) -> TypePath {
        let segs = self.rs_name.split("::").map(make_ident);
        parse_quote! {
            #(#segs)::*
        }
    }
}

/// Database of known types.
pub(crate) struct TypeDatabase {
    by_rs_name: HashMap<TypeName, TypeDetails>,
    canonical_names: HashMap<TypeName, TypeName>,
}

lazy_static! {
    /// Database of known types.
    pub(crate) static ref KNOWN_TYPES: TypeDatabase = create_type_database();
}

impl TypeDatabase {
    fn get(&self, ty: &TypeName) -> Option<&TypeDetails> {
        // The following line is important. It says that
        // when we encounter something like 'std::unique_ptr'
        // in the bindgen-generated bindings, we'll immediately
        // start to refer to that as 'UniquePtr' henceforth.
        let canonical_name = self.canonical_names.get(ty).unwrap_or(ty);
        self.by_rs_name.get(canonical_name)
    }

    /// Prelude of C++ for squirting into bindgen. This configures
    /// bindgen to output simpler types to replace some STL types
    /// that bindgen just can't cope with. Although we then replace
    /// those types with cxx types (e.g. UniquePtr), this intermediate
    /// step is still necessary because bindgen can't otherwise
    /// give us the templated types (e.g. when faced with the STL
    /// unique_ptr, bindgen would normally give us std_unique_ptr
    /// as opposed to std_unique_ptr<T>.)
    pub(crate) fn get_prelude(&self) -> String {
        itertools::join(
            self.by_rs_name
                .values()
                .filter_map(|t| t.get_prelude_entry()),
            "\n",
        )
    }

    /// Types which are known to be safe (or unsafe) to hold and pass by
    /// value in Rust.
    pub(crate) fn get_pod_safe_types(&self) -> impl Iterator<Item = (&TypeName, bool)> {
        self.by_rs_name.iter().map(|(tn, td)| {
            (
                tn,
                match td.behavior {
                    Behavior::CxxContainerByValueSafe
                    | Behavior::RustStr
                    | Behavior::RustString
                    | Behavior::RustByValue
                    | Behavior::CByValue
                    | Behavior::CVariableLengthByValue => true,
                    Behavior::CxxString
                    | Behavior::CxxContainerNotByValueSafe
                    | Behavior::CVoid => false,
                },
            )
        })
    }

    /// Whether this TypePath should be treated as a value in C++
    /// but a reference in Rust. This only applies to rust::Str
    /// (C++ name) which is &str in Rust.
    pub(crate) fn should_dereference_in_cpp(&self, typ: &TypePath) -> bool {
        let tn = TypeName::from_type_path(typ);
        self.get(&tn)
            .map(|td| matches!(td.behavior, Behavior::RustStr))
            .unwrap_or(false)
    }

    /// Here we substitute any names which we know are Special from
    /// our type database, e.g. std::unique_ptr -> UniquePtr.
    /// We strip off and ignore
    /// any PathArguments within this TypePath - callers should
    /// put them back again if needs be.
    pub(crate) fn known_type_substitute_path(&self, typ: &TypePath) -> Option<TypePath> {
        let tn = TypeName::from_type_path(typ);
        self.get(&tn).map(|td| td.to_type_path())
    }

    pub(crate) fn special_cpp_name(&self, rs: &TypeName) -> Option<String> {
        self.get(rs).map(|x| x.cpp_name.to_string())
    }

    pub(crate) fn is_known_type(&self, ty: &TypeName) -> bool {
        self.get(ty).is_some()
    }

    pub(crate) fn known_type_type_path(&self, ty: &TypeName) -> Option<TypePath> {
        self.get(ty).map(|td| td.to_type_path())
    }

    /// Whether this is one of the ctypes (mostly variable length integers)
    /// which we need to wrap.
    pub(crate) fn is_ctype(&self, ty: &TypeName) -> bool {
        self.get(ty)
            .map(|td| match td.behavior {
                Behavior::CVariableLengthByValue | Behavior::CVoid => true,
                _ => false,
            })
            .unwrap_or(false)
    }

    /// Whether this is a generic type acceptable to cxx. Otherwise,
    /// if we encounter a generic, we'll replace it with a synthesized concrete
    /// type.
    pub(crate) fn is_cxx_acceptable_generic(&self, ty: &TypeName) -> bool {
        self.get(ty)
            .map(|x| match x.behavior {
                Behavior::CxxContainerByValueSafe | Behavior::CxxContainerNotByValueSafe => true,
                _ => false,
            })
            .unwrap_or(false)
    }

    pub(crate) fn convertible_from_strs(&self, ty: &TypeName) -> bool {
        self.get(ty)
            .map(|x| matches!(x.behavior, Behavior::CxxString))
            .unwrap_or(false)
    }
}

fn create_type_database() -> TypeDatabase {
    let mut by_rs_name = HashMap::new();

    let mut do_insert =
        |td: TypeDetails| by_rs_name.insert(TypeName::new_from_user_input(&td.rs_name), td);

    do_insert(TypeDetails::new(
        "cxx::UniquePtr".into(),
        "std::unique_ptr".into(),
        Behavior::CxxContainerByValueSafe,
        None,
    ));
    do_insert(TypeDetails::new(
        "cxx::CxxVector".into(),
        "std::vector".into(),
        Behavior::CxxContainerNotByValueSafe,
        None,
    ));
    do_insert(TypeDetails::new(
        "cxx::SharedPtr".into(),
        "std::shared_ptr".into(),
        Behavior::CxxContainerByValueSafe,
        None,
    ));
    do_insert(TypeDetails::new(
        "cxx::CxxString".into(),
        "std::string".into(),
        Behavior::CxxString,
        None,
    ));
    do_insert(TypeDetails::new(
        "str".into(),
        "rust::Str".into(),
        Behavior::RustStr,
        None,
    ));
    do_insert(TypeDetails::new(
        "String".into(),
        "rust::String".into(),
        Behavior::RustString,
        None,
    ));
    do_insert(TypeDetails::new(
        "i8".into(),
        "int8_t".into(),
        Behavior::CByValue,
        Some("std::os::raw::c_schar".into()),
    ));
    do_insert(TypeDetails::new(
        "u8".into(),
        "uint8_t".into(),
        Behavior::CByValue,
        Some("std::os::raw::c_uchar".into()),
    ));
    for (cpp_type, rust_type) in (4..7)
        .map(|x| 2i32.pow(x))
        .map(|x| {
            vec![
                (format!("uint{}_t", x), format!("u{}", x)),
                (format!("int{}_t", x), format!("i{}", x)),
            ]
        })
        .flatten()
    {
        do_insert(TypeDetails::new(
            rust_type,
            cpp_type,
            Behavior::CByValue,
            None,
        ));
    }
    do_insert(TypeDetails::new(
        "bool".into(),
        "bool".into(),
        Behavior::CByValue,
        None,
    ));

    do_insert(TypeDetails::new(
        "std::pin::Pin".into(),
        "Pin".into(),
        Behavior::RustByValue, // because this is actually Pin<&something>
        None,
    ));

    let mut insert_ctype = |cname: &str| {
        let td = TypeDetails::new(
            format!("autocxx::c_{}", cname),
            cname.into(),
            Behavior::CVariableLengthByValue,
            Some(format!("std::os::raw::c_{}", cname)),
        );
        by_rs_name.insert(TypeName::new_from_user_input(&td.rs_name), td);
        let td = TypeDetails::new(
            format!("autocxx::c_u{}", cname),
            format!("unsigned {}", cname),
            Behavior::CVariableLengthByValue,
            Some(format!("std::os::raw::c_u{}", cname)),
        );
        by_rs_name.insert(TypeName::new_from_user_input(&td.rs_name), td);
    };

    insert_ctype("long");
    insert_ctype("int");
    insert_ctype("short");

    let td = TypeDetails::new("f32".into(), "float".into(), Behavior::CByValue, None);
    by_rs_name.insert(TypeName::new_from_user_input(&td.rs_name), td);

    let td = TypeDetails::new("f64".into(), "double".into(), Behavior::CByValue, None);
    by_rs_name.insert(TypeName::new_from_user_input(&td.rs_name), td);

    let td = TypeDetails::new(
        "std::os::raw::c_char".into(),
        "char".into(),
        Behavior::CByValue,
        None,
    );
    by_rs_name.insert(TypeName::new_from_user_input(&td.rs_name), td);

    let td = TypeDetails::new(
        "autocxx::c_void".into(),
        "void".into(),
        Behavior::CVoid,
        Some(format!("std::os::raw::c_void")),
    );
    by_rs_name.insert(TypeName::new_from_user_input(&td.rs_name), td);

    let mut by_cppname = HashMap::new();
    for td in by_rs_name.values() {
        let rs_name = TypeName::new_from_user_input(&td.rs_name);
        if let Some(extra_non_canonical_name) = &td.extra_non_canonical_name {
            by_cppname.insert(
                TypeName::new_from_user_input(extra_non_canonical_name),
                rs_name.clone(),
            );
        }
        by_cppname.insert(TypeName::new_from_user_input(&td.cpp_name), rs_name);
    }

    TypeDatabase {
        by_rs_name,
        canonical_names: by_cppname,
    }
}

/// This is worked out basically using trial and error.
/// Excluding std* and rust* is obvious, but the other items...
/// in theory bindgen ought to be smart enough to work out that
/// they're not used and therefore not generate code for them.
/// But it doesm unless we blocklist them. This is obviously
/// a bit sensitive to the particular STL in use so one day
/// it would be good to dig into bindgen's behavior here - TODO.
const BINDGEN_BLOCKLIST: &[&str] = &["std.*", "__gnu.*", ".*mbstate_t.*", "rust.*"];

/// Get the list of types to give to bindgen to ask it _not_ to
/// generate code for.
pub(crate) fn get_initial_blocklist() -> Vec<String> {
    BINDGEN_BLOCKLIST.iter().map(|s| s.to_string()).collect()
}

/// If a given type lacks a copy constructor, we should always use
/// std::move in wrapper functions.
pub(crate) fn type_lacks_copy_constructor(ty: &Type) -> bool {
    // In future we may wish to look this up in KNOWN_TYPES.
    match ty {
        Type::Path(typ) => {
            let tn = TypeName::from_type_path(typ);
            tn.to_cpp_name().starts_with("std::unique_ptr")
        }
        _ => false,
    }
}

pub(crate) fn confirm_inner_type_is_acceptable_generic_payload(
    path_args: &PathArguments,
    desc: &TypeName,
) -> Result<(), ConvertError> {
    // For now, all supported generics accept the same payloads. This
    // may change in future in which case we'll need to accept more arguments here.
    match path_args {
        PathArguments::None => Ok(()),
        PathArguments::Parenthesized(_) => Err(ConvertError::TemplatedTypeContainingNonPathArg(
            desc.clone(),
        )),
        PathArguments::AngleBracketed(ab) => {
            for inner in &ab.args {
                match inner {
                    GenericArgument::Type(Type::Path(typ)) => {
                        if let Some(more_generics) = typ.path.segments.last() {
                            confirm_inner_type_is_acceptable_generic_payload(
                                &more_generics.arguments,
                                desc,
                            )?;
                        }
                    }
                    _ => {
                        return Err(ConvertError::TemplatedTypeContainingNonPathArg(
                            desc.clone(),
                        ))
                    }
                }
            }
            Ok(())
        }
    }
}

pub(crate) fn ensure_pointee_is_valid(ptr: &TypePtr) -> Result<(), ConvertError> {
    match *ptr.elem {
        Type::Path(..) => Ok(()),
        _ => Err(ConvertError::InvalidPointee),
    }
}
<|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/. */

extern crate serde_json;
extern crate collections;

use self::collections::vec::IntoIter;
use service::Service;
use std::collections::HashMap;
use std::io::Error;
use std::net::SocketAddr;
use std::net::ToSocketAddrs;
use std::sync::{ Arc, Mutex };

// The `global` context available to all.
pub struct Context {
    pub verbose: bool,

    hostname: String,
    http_port: u16,
    ws_port: u16,
    services: HashMap<String, Box<Service>>
}

const DEFAULT_HTTP_PORT: u16 = 3000;
const DEFAULT_WS_PORT: u16 = 4000;
const DEFAULT_HOSTNAME: &'static str = "0.0.0.0";

pub type SharedContext = Arc<Mutex<Context>>;

impl Context {
    pub fn new(verbose: bool, hostname: Option<String>) -> Context {
        Context { services: HashMap::new(),
                  verbose: verbose,
                  hostname:  hostname.unwrap_or(DEFAULT_HOSTNAME.to_owned()),
                  http_port: DEFAULT_HTTP_PORT,
                  ws_port: DEFAULT_WS_PORT }
    }

    pub fn shared(verbose: bool, hostname: Option<String>) -> SharedContext {
        Arc::new(Mutex::new(Context::new(verbose, hostname)))
    }

    pub fn add_service(&mut self, service: Box<Service>) {
        let service_id = service.get_properties().id;
        self.services.insert(service_id, service);
    }

    pub fn remove_service(&mut self, id: String) {
        self.services.remove(&id);
    }

    pub fn services_count(&self) -> usize {
        self.services.len()
    }

    pub fn get_service(&self, id: &str) -> Option<&Box<Service>> {
        self.services.get(id)
    }

    pub fn services_as_json(&self) -> Result<String, serde_json::error::Error> {
        serde_json::to_string(&self.services)
    }

    pub fn get_http_root_for_service(&self, service_id: String) -> String {
        format!("http://{}:{}/services/{}/", self.hostname, self.http_port, service_id)
    }

    pub fn get_ws_root_for_service(&self, service_id: String) -> String {
        format!("ws://{}:{}/services/{}/", self.hostname, self.ws_port, service_id)
    }

    pub fn http_as_addrs(&self) -> Result<IntoIter<SocketAddr>, Error> {
        (self.hostname.as_str(), self.http_port).to_socket_addrs()
    }
}
<commit_msg>switch to ipv6 default address<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/. */

extern crate serde_json;
extern crate collections;

use self::collections::vec::IntoIter;
use service::Service;
use std::collections::HashMap;
use std::io::Error;
use std::net::SocketAddr;
use std::net::ToSocketAddrs;
use std::sync::{ Arc, Mutex };

// The `global` context available to all.
pub struct Context {
    pub verbose: bool,

    hostname: String,
    http_port: u16,
    ws_port: u16,
    services: HashMap<String, Box<Service>>
}

const DEFAULT_HTTP_PORT: u16 = 3000;
const DEFAULT_WS_PORT: u16 = 4000;
const DEFAULT_HOSTNAME: &'static str = "::"; // ipv6 default.

pub type SharedContext = Arc<Mutex<Context>>;

impl Context {
    pub fn new(verbose: bool, hostname: Option<String>) -> Context {
        Context { services: HashMap::new(),
                  verbose: verbose,
                  hostname:  hostname.unwrap_or(DEFAULT_HOSTNAME.to_owned()),
                  http_port: DEFAULT_HTTP_PORT,
                  ws_port: DEFAULT_WS_PORT }
    }

    pub fn shared(verbose: bool, hostname: Option<String>) -> SharedContext {
        Arc::new(Mutex::new(Context::new(verbose, hostname)))
    }

    pub fn add_service(&mut self, service: Box<Service>) {
        let service_id = service.get_properties().id;
        self.services.insert(service_id, service);
    }

    pub fn remove_service(&mut self, id: String) {
        self.services.remove(&id);
    }

    pub fn services_count(&self) -> usize {
        self.services.len()
    }

    pub fn get_service(&self, id: &str) -> Option<&Box<Service>> {
        self.services.get(id)
    }

    pub fn services_as_json(&self) -> Result<String, serde_json::error::Error> {
        serde_json::to_string(&self.services)
    }

    pub fn get_http_root_for_service(&self, service_id: String) -> String {
        format!("http://{}:{}/services/{}/", self.hostname, self.http_port, service_id)
    }

    pub fn get_ws_root_for_service(&self, service_id: String) -> String {
        format!("ws://{}:{}/services/{}/", self.hostname, self.ws_port, service_id)
    }

    pub fn http_as_addrs(&self) -> Result<IntoIter<SocketAddr>, Error> {
        (self.hostname.as_str(), self.http_port).to_socket_addrs()
    }
}
<|endoftext|>


<commit_before>use vangers::{
    config,
    render::{ScreenTargets, COLOR_FORMAT, DEPTH_FORMAT},
};

use env_logger;
use futures::executor::{LocalPool, LocalSpawner};
use log::info;
use winit::{
    self,
    event,
    event_loop::{ControlFlow, EventLoop},
    window::{Window, WindowBuilder},
};

pub trait Application {
    fn on_key(&mut self, input: event::KeyboardInput) -> bool;
    fn on_mouse_wheel(&mut self, _delta: event::MouseScrollDelta) {}
    fn on_cursor_move(&mut self, _position: (f64, f64)) {}
    fn on_mouse_button(&mut self, _state: event::ElementState, _button: event::MouseButton) {}
    fn resize(&mut self, _device: &wgpu::Device, _extent: wgpu::Extent3d) {}
    fn reload(&mut self, device: &wgpu::Device);
    fn update(
        &mut self,
        device: &wgpu::Device,
        delta: f32,
        spawner: &LocalSpawner,
    ) -> Vec<wgpu::CommandBuffer>;
    fn draw(
        &mut self,
        device: &wgpu::Device,
        targets: ScreenTargets,
        spawner: &LocalSpawner,
    ) -> wgpu::CommandBuffer;
}

pub struct Harness {
    event_loop: EventLoop<()>,
    window: Window,
    pub device: wgpu::Device,
    pub queue: wgpu::Queue,
    surface: wgpu::Surface,
    swap_chain: wgpu::SwapChain,
    pub extent: wgpu::Extent3d,
    reload_on_focus: bool,
    depth_target: wgpu::TextureView,
}

impl Harness {
    pub fn init(title: &str) -> (Self, config::Settings) {
        env_logger::init();

        info!("Loading the settings");
        let settings = config::Settings::load("config/settings.ron");
        let extent = wgpu::Extent3d {
            width: settings.window.size[0],
            height: settings.window.size[1],
            depth: 1,
        };

        info!("Initializing the device");
        let adapter = wgpu::Adapter::request(
            &wgpu::RequestAdapterOptions {
                power_preference: wgpu::PowerPreference::Default,
            },
            settings.backend.to_wgpu(),
        ).expect("Unable to initialize GPU via the selected backend.");
        let (device, queue) = adapter.request_device(&wgpu::DeviceDescriptor {
            extensions: wgpu::Extensions {
                anisotropic_filtering: false,
            },
            limits: wgpu::Limits::default(),
        });

        info!("Initializing the window");
        let event_loop = EventLoop::new();
        let dpi = event_loop
            .primary_monitor()
            .hidpi_factor();
        let window = WindowBuilder::new()
            .with_title(title)
            .with_inner_size(
                winit::dpi::LogicalSize::from_physical((extent.width, extent.height), dpi),
            )
            .with_resizable(true)
            .build(&event_loop)
            .unwrap();

        let surface = wgpu::Surface::create(&window);
        let sc_desc = wgpu::SwapChainDescriptor {
            usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT,
            format: COLOR_FORMAT,
            width: extent.width,
            height: extent.height,
            present_mode: wgpu::PresentMode::Vsync,
        };
        let swap_chain = device.create_swap_chain(&surface, &sc_desc);
        let depth_target = device
            .create_texture(&wgpu::TextureDescriptor {
                size: extent,
                array_layer_count: 1,
                mip_level_count: 1,
                sample_count: 1,
                dimension: wgpu::TextureDimension::D2,
                format: DEPTH_FORMAT,
                usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT,
            })
            .create_default_view();

        let harness = Harness {
            event_loop,
            window,
            device,
            queue,
            surface,
            swap_chain,
            extent,
            reload_on_focus: settings.window.reload_on_focus,
            depth_target,
        };

        (harness, settings)
    }

    pub fn main_loop<A: 'static + Application>(self, mut app: A) {
        use std::time;

        let mut last_time = time::Instant::now();
        let mut task_pool = LocalPool::new();
        let mut needs_reload = false;
        let Harness {
            event_loop,
            window,
            device,
            queue,
            surface,
            mut swap_chain,
            mut extent,
            reload_on_focus,
            mut depth_target,
        } = self;

        event_loop.run(move |event, _, control_flow| {
            *control_flow = ControlFlow::Poll;
            task_pool.run_until_stalled();

            match event {
                event::Event::WindowEvent {
                    event: event::WindowEvent::Resized(size),
                    ..
                } => {
                    let physical = size.to_physical(window.hidpi_factor());
                    info!("Resizing to {:?}", physical);
                    extent = wgpu::Extent3d {
                        width: physical.width.round() as u32,
                        height: physical.height.round() as u32,
                        depth: 1,
                    };
                    let sc_desc = wgpu::SwapChainDescriptor {
                        usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT,
                        format: COLOR_FORMAT,
                        width: extent.width,
                        height: extent.height,
                        present_mode: wgpu::PresentMode::Vsync,
                    };
                    swap_chain = device.create_swap_chain(&surface, &sc_desc);
                    depth_target = device
                        .create_texture(&wgpu::TextureDescriptor {
                            size: extent,
                            array_layer_count: 1,
                            mip_level_count: 1,
                            sample_count: 1,
                            dimension: wgpu::TextureDimension::D2,
                            format: DEPTH_FORMAT,
                            usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT,
                        })
                        .create_default_view();
                    app.resize(&device, extent);
                }
                event::Event::WindowEvent { event, .. } => match event {
                    event::WindowEvent::Focused(false) => {
                        needs_reload = reload_on_focus;
                    }
                    event::WindowEvent::Focused(true) if needs_reload => {
                        info!("Reloading shaders");
                        app.reload(&device);
                        needs_reload = false;
                    }
                    event::WindowEvent::CloseRequested => {
                        *control_flow = ControlFlow::Exit;
                    }
                    event::WindowEvent::KeyboardInput { input, .. } => {
                        if !app.on_key(input) {
                            *control_flow = ControlFlow::Exit;
                        }
                    }
                    event::WindowEvent::MouseWheel {delta, ..} => {
                        app.on_mouse_wheel(delta)
                    }
                    event::WindowEvent::CursorMoved {position, ..} => {
                        let physical = position.to_physical(window.hidpi_factor());
                        app.on_cursor_move(physical.into())
                    }
                    event::WindowEvent::MouseInput {state, button, ..} => {
                        app.on_mouse_button(state, button)
                    }
                    _ => {}
                },
                event::Event::EventsCleared => {
                    let spawner = task_pool.spawner();
                    let duration = time::Instant::now() - last_time;
                    last_time += duration;
                    let delta = duration.as_secs() as f32 +
                        duration.subsec_nanos() as f32 * 1.0e-9;

                    let update_command_buffers = app.update(&device, delta, &spawner);
                    if !update_command_buffers.is_empty() {
                        queue.submit(&update_command_buffers);
                    }

                    let frame = swap_chain.get_next_texture().unwrap();
                    let targets = ScreenTargets {
                        extent,
                        color: &frame.view,
                        depth: &depth_target,
                    };
                    let render_commane_buffer = app.draw(&device, targets, &spawner);
                    queue.submit(&[render_commane_buffer]);
                }
                _ => (),
            }
        });
    }
}
<commit_msg>Don't crash when swap_chain.get_next_texture fails<commit_after>use vangers::{
    config,
    render::{ScreenTargets, COLOR_FORMAT, DEPTH_FORMAT},
};

use env_logger;
use futures::executor::{LocalPool, LocalSpawner};
use log::info;
use winit::{
    self,
    event,
    event_loop::{ControlFlow, EventLoop},
    window::{Window, WindowBuilder},
};

pub trait Application {
    fn on_key(&mut self, input: event::KeyboardInput) -> bool;
    fn on_mouse_wheel(&mut self, _delta: event::MouseScrollDelta) {}
    fn on_cursor_move(&mut self, _position: (f64, f64)) {}
    fn on_mouse_button(&mut self, _state: event::ElementState, _button: event::MouseButton) {}
    fn resize(&mut self, _device: &wgpu::Device, _extent: wgpu::Extent3d) {}
    fn reload(&mut self, device: &wgpu::Device);
    fn update(
        &mut self,
        device: &wgpu::Device,
        delta: f32,
        spawner: &LocalSpawner,
    ) -> Vec<wgpu::CommandBuffer>;
    fn draw(
        &mut self,
        device: &wgpu::Device,
        targets: ScreenTargets,
        spawner: &LocalSpawner,
    ) -> wgpu::CommandBuffer;
}

pub struct Harness {
    event_loop: EventLoop<()>,
    window: Window,
    pub device: wgpu::Device,
    pub queue: wgpu::Queue,
    surface: wgpu::Surface,
    swap_chain: wgpu::SwapChain,
    pub extent: wgpu::Extent3d,
    reload_on_focus: bool,
    depth_target: wgpu::TextureView,
}

impl Harness {
    pub fn init(title: &str) -> (Self, config::Settings) {
        env_logger::init();

        info!("Loading the settings");
        let settings = config::Settings::load("config/settings.ron");
        let extent = wgpu::Extent3d {
            width: settings.window.size[0],
            height: settings.window.size[1],
            depth: 1,
        };

        info!("Initializing the device");
        let adapter = wgpu::Adapter::request(
            &wgpu::RequestAdapterOptions {
                power_preference: wgpu::PowerPreference::Default,
            },
            settings.backend.to_wgpu(),
        ).expect("Unable to initialize GPU via the selected backend.");
        let (device, queue) = adapter.request_device(&wgpu::DeviceDescriptor {
            extensions: wgpu::Extensions {
                anisotropic_filtering: false,
            },
            limits: wgpu::Limits::default(),
        });

        info!("Initializing the window");
        let event_loop = EventLoop::new();
        let dpi = event_loop
            .primary_monitor()
            .hidpi_factor();
        let window = WindowBuilder::new()
            .with_title(title)
            .with_inner_size(
                winit::dpi::LogicalSize::from_physical((extent.width, extent.height), dpi),
            )
            .with_resizable(true)
            .build(&event_loop)
            .unwrap();

        let surface = wgpu::Surface::create(&window);
        let sc_desc = wgpu::SwapChainDescriptor {
            usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT,
            format: COLOR_FORMAT,
            width: extent.width,
            height: extent.height,
            present_mode: wgpu::PresentMode::Vsync,
        };
        let swap_chain = device.create_swap_chain(&surface, &sc_desc);
        let depth_target = device
            .create_texture(&wgpu::TextureDescriptor {
                size: extent,
                array_layer_count: 1,
                mip_level_count: 1,
                sample_count: 1,
                dimension: wgpu::TextureDimension::D2,
                format: DEPTH_FORMAT,
                usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT,
            })
            .create_default_view();

        let harness = Harness {
            event_loop,
            window,
            device,
            queue,
            surface,
            swap_chain,
            extent,
            reload_on_focus: settings.window.reload_on_focus,
            depth_target,
        };

        (harness, settings)
    }

    pub fn main_loop<A: 'static + Application>(self, mut app: A) {
        use std::time;

        let mut last_time = time::Instant::now();
        let mut task_pool = LocalPool::new();
        let mut needs_reload = false;
        let Harness {
            event_loop,
            window,
            device,
            queue,
            surface,
            mut swap_chain,
            mut extent,
            reload_on_focus,
            mut depth_target,
        } = self;

        event_loop.run(move |event, _, control_flow| {
            *control_flow = ControlFlow::Poll;
            task_pool.run_until_stalled();

            match event {
                event::Event::WindowEvent {
                    event: event::WindowEvent::Resized(size),
                    ..
                } => {
                    let physical = size.to_physical(window.hidpi_factor());
                    info!("Resizing to {:?}", physical);
                    extent = wgpu::Extent3d {
                        width: physical.width.round() as u32,
                        height: physical.height.round() as u32,
                        depth: 1,
                    };
                    let sc_desc = wgpu::SwapChainDescriptor {
                        usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT,
                        format: COLOR_FORMAT,
                        width: extent.width,
                        height: extent.height,
                        present_mode: wgpu::PresentMode::Vsync,
                    };
                    swap_chain = device.create_swap_chain(&surface, &sc_desc);
                    depth_target = device
                        .create_texture(&wgpu::TextureDescriptor {
                            size: extent,
                            array_layer_count: 1,
                            mip_level_count: 1,
                            sample_count: 1,
                            dimension: wgpu::TextureDimension::D2,
                            format: DEPTH_FORMAT,
                            usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT,
                        })
                        .create_default_view();
                    app.resize(&device, extent);
                }
                event::Event::WindowEvent { event, .. } => match event {
                    event::WindowEvent::Focused(false) => {
                        needs_reload = reload_on_focus;
                    }
                    event::WindowEvent::Focused(true) if needs_reload => {
                        info!("Reloading shaders");
                        app.reload(&device);
                        needs_reload = false;
                    }
                    event::WindowEvent::CloseRequested => {
                        *control_flow = ControlFlow::Exit;
                    }
                    event::WindowEvent::KeyboardInput { input, .. } => {
                        if !app.on_key(input) {
                            *control_flow = ControlFlow::Exit;
                        }
                    }
                    event::WindowEvent::MouseWheel {delta, ..} => {
                        app.on_mouse_wheel(delta)
                    }
                    event::WindowEvent::CursorMoved {position, ..} => {
                        let physical = position.to_physical(window.hidpi_factor());
                        app.on_cursor_move(physical.into())
                    }
                    event::WindowEvent::MouseInput {state, button, ..} => {
                        app.on_mouse_button(state, button)
                    }
                    _ => {}
                },
                event::Event::EventsCleared => {
                    let spawner = task_pool.spawner();
                    let duration = time::Instant::now() - last_time;
                    last_time += duration;
                    let delta = duration.as_secs() as f32 +
                        duration.subsec_nanos() as f32 * 1.0e-9;

                    let update_command_buffers = app.update(&device, delta, &spawner);
                    if !update_command_buffers.is_empty() {
                        queue.submit(&update_command_buffers);
                    }

                    match swap_chain.get_next_texture() {
                        Ok(frame) => {
                            let targets = ScreenTargets {
                                extent,
                                color: &frame.view,
                                depth: &depth_target,
                            };
                            let render_commane_buffer = app.draw(&device, targets, &spawner);
                            queue.submit(&[render_commane_buffer]);
                        },
                        Err(_) => {}
                    }
                }
                _ => (),
            }
        });
    }
}
<|endoftext|>


<commit_before>#[derive(Debug, PartialEq)]
pub enum LiteralValue {
    LiteralUndefined,
    LiteralNull,
    LiteralTrue,
    LiteralFalse,
    LiteralInteger(i32),
    LiteralFloat(f64),
    LiteralString(String),
    LiteralInvalid,
}
pub use self::LiteralValue::*;

impl LiteralValue {
    pub fn float_from_string(string: String) -> Self {
        match string.parse::<f64>() {
            Ok(float) => LiteralFloat(float),
            _         => LiteralInvalid,
        }
    }
}

#[derive(Debug, PartialEq)]
pub enum VariableDeclarationKind {
    Var,
    Let,
    Const,
}

#[derive(Debug, PartialEq)]
pub enum MemberKey {
    Literal(String),
    Computed(Expression),
}

#[derive(Debug, PartialEq)]
pub enum ObjectMember {
    Shorthand {
        key: String,
    },
    Literal {
        key: String,
        value: Expression,
    },
    Computed {
        key: Expression,
        value: Expression,
    },
}

#[derive(Debug, PartialEq)]
pub struct Parameter {
    pub name: String,
}

#[derive(Debug, PartialEq)]
pub enum OperatorType {
    FatArrow,         //   …  => …
    Accessor,         //   …  .  …
    New,              //     new …
    Increment,        //      ++ … | … ++
    Decrement,        //      -- … | … --
    LogicalNot,       //       ! …
    BitwiseNot,       //       ~ …
    Typeof,           //  typeof …
    Void,             //    void …
    Delete,           //  delete …
    Multiplication,   //   …  *  …
    Division,         //   …  /  …
    Remainder,        //   …  %  …
    Exponent,         //   …  ** …
    Addition,         //   …  +  … | + …
    Substraction,     //   …  -  … | - …
    BitShiftLeft,     //   …  << …
    BitShiftRight,    //   …  >> …
    UBitShiftRight,   //   … >>> …
    Lesser,           //   …  <  …
    LesserEquals,     //   …  <= …
    Greater,          //   …  >  …
    GreaterEquals,    //   …  >= …
    Instanceof,       //   … instanceof …
    In,               //   …  in …
    StrictEquality,   //   … === …
    StrictInequality, //   … !== …
    Equality,         //   …  == …
    Inequality,       //   …  != …
    BitwiseAnd,       //   …  &  …
    BitwiseXor,       //   …  ^  …
    BitwiseOr,        //   …  |  …
    LogicalAnd,       //   …  && …
    LogicalOr,        //   …  || …
    Conditional,      //   …  ?  …  :  …
    Assign,           //   …  =  …
    Spread,           //     ... …
}
use self::OperatorType::*;

impl OperatorType {
    /// According to the Operator Precedence Table
    pub fn binding_power(&self, prefix: bool) -> u8 {
        match *self {
            FatArrow         |
            Accessor         => 18,

            New              => 17,

            Increment        |
            Decrement        => if prefix { 15 } else { 16 },

            LogicalNot       |
            BitwiseNot       |
            Typeof           |
            Void             |
            Delete           => 15,

            Multiplication   |
            Division         |
            Remainder        |
            Exponent         => 14,

            Addition         |
            Substraction     => if prefix { 15 } else { 13 },

            BitShiftLeft     |
            BitShiftRight    |
            UBitShiftRight   => 12,

            Lesser           |
            LesserEquals     |
            Greater          |
            GreaterEquals    |
            Instanceof       |
            In               => 11,

            StrictEquality   |
            StrictInequality |
            Equality         |
            Inequality       => 10,

            BitwiseAnd       => 9,
            BitwiseXor       => 8,
            BitwiseOr        => 7,
            LogicalAnd       => 6,
            LogicalOr        => 5,
            Conditional      => 4,
            Assign           => 3,
            Spread           => 1,
        }
    }

    pub fn prefix(&self) -> bool {
        match *self {
            LogicalNot       |
            BitwiseNot       |
            Typeof           |
            Void             |
            Delete           |
            New              |
            Spread           |
            Increment        |
            Decrement        |
            Addition         |
            Substraction     => true,

            _                => false
        }
    }

    pub fn infix(&self) -> bool {
        match *self {
            FatArrow         |
            Assign           |
            Accessor         |
            Multiplication   |
            Division         |
            Remainder        |
            Exponent         |
            StrictEquality   |
            StrictInequality |
            Equality         |
            Inequality       |
            Lesser           |
            LesserEquals     |
            Greater          |
            GreaterEquals    |
            Instanceof       |
            In               |
            BitShiftLeft     |
            BitShiftRight    |
            UBitShiftRight   |
            BitwiseAnd       |
            BitwiseXor       |
            BitwiseOr        |
            LogicalAnd       |
            LogicalOr        |
            Conditional      |
            Addition         |
            Substraction     => true,

            _                => false
        }
    }
}

#[derive(Debug, PartialEq)]
pub enum Expression {
    IdentifierExpression(String),
    LiteralExpression(LiteralValue),
    ArrayExpression(Vec<Expression>),
    ObjectExpression(Vec<ObjectMember>),
    MemberExpression {
        object: Box<Expression>,
        property: Box<MemberKey>,
    },
    CallExpression {
        callee: Box<Expression>,
        arguments: Vec<Expression>,
    },
    BinaryExpression {
        left: Box<Expression>,
        operator: OperatorType,
        right: Box<Expression>,
    },
    PrefixExpression {
        operator: OperatorType,
        operand: Box<Expression>,
    },
    PostfixExpression {
        operator: OperatorType,
        operand: Box<Expression>,
    },
    ConditionalExpression {
        test: Box<Expression>,
        consequent: Box<Expression>,
        alternate: Box<Expression>,
    },
    ArrowFunctionExpression {
        params: Vec<Parameter>,
        body: Box<Statement>,
    },
    FunctionExpression {
        name: Option<String>,
        params: Vec<Parameter>,
        body: Vec<Statement>,
    }
}

#[derive(Debug, PartialEq)]
pub enum ClassMember {
    ClassConstructor {
        params: Vec<Parameter>,
        body: Vec<Statement>,
    },
    ClassMethod {
        is_static: bool,
        name: String,
        params: Vec<Parameter>,
        body: Vec<Statement>,
    },
    ClassProperty {
        is_static: bool,
        name: String,
        value: Expression,
    }
}

#[derive(Debug, PartialEq)]
pub enum Statement {
    VariableDeclarationStatement {
        kind: VariableDeclarationKind,
        declarations: Vec<(String, Expression)>,
    },
    ExpressionStatement(Expression),
    ReturnStatement(Expression),
    FunctionStatement {
        name: String,
        params: Vec<Parameter>,
        body: Vec<Statement>,
    },
    ClassStatement {
        name: String,
        extends: Option<String>,
        body: Vec<ClassMember>,
    },
    IfStatement {
        test: Expression,
        consequent: Box<Statement>,
        alternate: Option<Box<Statement>>,
    },
    WhileStatement {
        test: Expression,
        body: Box<Statement>,
    },
    BlockStatement {
        body: Vec<Statement>,
    },
}

#[derive(Debug, PartialEq)]
pub struct Program {
    pub body: Vec<Statement>,
}
<commit_msg>Implement Clone on Grammar structs<commit_after>#[derive(Debug, PartialEq, Clone)]
pub enum LiteralValue {
    LiteralUndefined,
    LiteralNull,
    LiteralTrue,
    LiteralFalse,
    LiteralInteger(i32),
    LiteralFloat(f64),
    LiteralString(String),
    LiteralInvalid,
}
pub use self::LiteralValue::*;

impl LiteralValue {
    pub fn float_from_string(string: String) -> Self {
        match string.parse::<f64>() {
            Ok(float) => LiteralFloat(float),
            _         => LiteralInvalid,
        }
    }
}

#[derive(Debug, PartialEq, Clone)]
pub enum VariableDeclarationKind {
    Var,
    Let,
    Const,
}

#[derive(Debug, PartialEq, Clone)]
pub enum MemberKey {
    Literal(String),
    Computed(Expression),
}

#[derive(Debug, PartialEq, Clone)]
pub enum ObjectMember {
    Shorthand {
        key: String,
    },
    Literal {
        key: String,
        value: Expression,
    },
    Computed {
        key: Expression,
        value: Expression,
    },
}

#[derive(Debug, PartialEq, Clone)]
pub struct Parameter {
    pub name: String,
}

#[derive(Debug, PartialEq, Clone)]
pub enum OperatorType {
    FatArrow,         //   …  => …
    Accessor,         //   …  .  …
    New,              //     new …
    Increment,        //      ++ … | … ++
    Decrement,        //      -- … | … --
    LogicalNot,       //       ! …
    BitwiseNot,       //       ~ …
    Typeof,           //  typeof …
    Void,             //    void …
    Delete,           //  delete …
    Multiplication,   //   …  *  …
    Division,         //   …  /  …
    Remainder,        //   …  %  …
    Exponent,         //   …  ** …
    Addition,         //   …  +  … | + …
    Substraction,     //   …  -  … | - …
    BitShiftLeft,     //   …  << …
    BitShiftRight,    //   …  >> …
    UBitShiftRight,   //   … >>> …
    Lesser,           //   …  <  …
    LesserEquals,     //   …  <= …
    Greater,          //   …  >  …
    GreaterEquals,    //   …  >= …
    Instanceof,       //   … instanceof …
    In,               //   …  in …
    StrictEquality,   //   … === …
    StrictInequality, //   … !== …
    Equality,         //   …  == …
    Inequality,       //   …  != …
    BitwiseAnd,       //   …  &  …
    BitwiseXor,       //   …  ^  …
    BitwiseOr,        //   …  |  …
    LogicalAnd,       //   …  && …
    LogicalOr,        //   …  || …
    Conditional,      //   …  ?  …  :  …
    Assign,           //   …  =  …
    Spread,           //     ... …
}
use self::OperatorType::*;

impl OperatorType {
    /// According to the Operator Precedence Table
    pub fn binding_power(&self, prefix: bool) -> u8 {
        match *self {
            FatArrow         |
            Accessor         => 18,

            New              => 17,

            Increment        |
            Decrement        => if prefix { 15 } else { 16 },

            LogicalNot       |
            BitwiseNot       |
            Typeof           |
            Void             |
            Delete           => 15,

            Multiplication   |
            Division         |
            Remainder        |
            Exponent         => 14,

            Addition         |
            Substraction     => if prefix { 15 } else { 13 },

            BitShiftLeft     |
            BitShiftRight    |
            UBitShiftRight   => 12,

            Lesser           |
            LesserEquals     |
            Greater          |
            GreaterEquals    |
            Instanceof       |
            In               => 11,

            StrictEquality   |
            StrictInequality |
            Equality         |
            Inequality       => 10,

            BitwiseAnd       => 9,
            BitwiseXor       => 8,
            BitwiseOr        => 7,
            LogicalAnd       => 6,
            LogicalOr        => 5,
            Conditional      => 4,
            Assign           => 3,
            Spread           => 1,
        }
    }

    pub fn prefix(&self) -> bool {
        match *self {
            LogicalNot       |
            BitwiseNot       |
            Typeof           |
            Void             |
            Delete           |
            New              |
            Spread           |
            Increment        |
            Decrement        |
            Addition         |
            Substraction     => true,

            _                => false
        }
    }

    pub fn infix(&self) -> bool {
        match *self {
            FatArrow         |
            Assign           |
            Accessor         |
            Multiplication   |
            Division         |
            Remainder        |
            Exponent         |
            StrictEquality   |
            StrictInequality |
            Equality         |
            Inequality       |
            Lesser           |
            LesserEquals     |
            Greater          |
            GreaterEquals    |
            Instanceof       |
            In               |
            BitShiftLeft     |
            BitShiftRight    |
            UBitShiftRight   |
            BitwiseAnd       |
            BitwiseXor       |
            BitwiseOr        |
            LogicalAnd       |
            LogicalOr        |
            Conditional      |
            Addition         |
            Substraction     => true,

            _                => false
        }
    }
}

#[derive(Debug, PartialEq, Clone)]
pub enum Expression {
    IdentifierExpression(String),
    LiteralExpression(LiteralValue),
    ArrayExpression(Vec<Expression>),
    ObjectExpression(Vec<ObjectMember>),
    MemberExpression {
        object: Box<Expression>,
        property: Box<MemberKey>,
    },
    CallExpression {
        callee: Box<Expression>,
        arguments: Vec<Expression>,
    },
    BinaryExpression {
        left: Box<Expression>,
        operator: OperatorType,
        right: Box<Expression>,
    },
    PrefixExpression {
        operator: OperatorType,
        operand: Box<Expression>,
    },
    PostfixExpression {
        operator: OperatorType,
        operand: Box<Expression>,
    },
    ConditionalExpression {
        test: Box<Expression>,
        consequent: Box<Expression>,
        alternate: Box<Expression>,
    },
    ArrowFunctionExpression {
        params: Vec<Parameter>,
        body: Box<Statement>,
    },
    FunctionExpression {
        name: Option<String>,
        params: Vec<Parameter>,
        body: Vec<Statement>,
    }
}

#[derive(Debug, PartialEq, Clone)]
pub enum ClassMember {
    ClassConstructor {
        params: Vec<Parameter>,
        body: Vec<Statement>,
    },
    ClassMethod {
        is_static: bool,
        name: String,
        params: Vec<Parameter>,
        body: Vec<Statement>,
    },
    ClassProperty {
        is_static: bool,
        name: String,
        value: Expression,
    }
}

#[derive(Debug, PartialEq, Clone)]
pub enum Statement {
    VariableDeclarationStatement {
        kind: VariableDeclarationKind,
        declarations: Vec<(String, Expression)>,
    },
    ExpressionStatement(Expression),
    ReturnStatement(Expression),
    FunctionStatement {
        name: String,
        params: Vec<Parameter>,
        body: Vec<Statement>,
    },
    ClassStatement {
        name: String,
        extends: Option<String>,
        body: Vec<ClassMember>,
    },
    IfStatement {
        test: Expression,
        consequent: Box<Statement>,
        alternate: Option<Box<Statement>>,
    },
    WhileStatement {
        test: Expression,
        body: Box<Statement>,
    },
    BlockStatement {
        body: Vec<Statement>,
    },
}

#[derive(Debug, PartialEq)]
pub struct Program {
    pub body: Vec<Statement>,
}
<|endoftext|>