<commit_before>use std::default::Default;
use std::fs::File;
use std::io::prelude::*;
use std::io::{self, BufReader};
use std::path::PathBuf;
use std::str::FromStr;

use toml;

pub use bindgen::directive::*;
pub use bindgen::rename::*;

pub const VERSION: &'static str = env!("CARGO_PKG_VERSION");

#[derive(Debug, Clone, PartialEq)]
pub enum Language {
    Cxx,
    C,
}
#[derive(Debug, Clone, PartialEq)]
pub enum Braces {
    SameLine,
    NextLine,
}
#[derive(Debug, Clone, PartialEq)]
pub enum Layout {
    Horizontal,
    Vertical,
    Auto,
}

impl FromStr for Language {
    type Err = String;

    fn from_str(s: &str) -> Result<Language, Self::Err> {
        match s {
            "cxx" => Ok(Language::Cxx),
            "Cxx" => Ok(Language::Cxx),
            "CXX" => Ok(Language::Cxx),
            "cpp" => Ok(Language::Cxx),
            "Cpp" => Ok(Language::Cxx),
            "CPP" => Ok(Language::Cxx),
            "c++" => Ok(Language::Cxx),
            "C++" => Ok(Language::Cxx),
            "c" => Ok(Language::C),
            "C" => Ok(Language::C),
            _ => Err(format!("unrecognized Language: '{}'", s)),
        }
    }
}
impl FromStr for Braces {
    type Err = String;

    fn from_str(s: &str) -> Result<Braces, Self::Err> {
        match s {
            "SameLine" => Ok(Braces::SameLine),
            "same_line" => Ok(Braces::SameLine),
            "NextLine" => Ok(Braces::NextLine),
            "next_line" => Ok(Braces::NextLine),
            _ => Err(format!("unrecognized Braces: '{}'", s)),
        }
    }
}
impl FromStr for Layout {
    type Err = String;

    fn from_str(s: &str) -> Result<Layout, Self::Err> {
        match s {
            "Horizontal" => Ok(Layout::Horizontal),
            "horizontal" => Ok(Layout::Horizontal),
            "Vertical" => Ok(Layout::Vertical),
            "vertical" => Ok(Layout::Vertical),
            "Auto" => Ok(Layout::Auto),
            "auto" => Ok(Layout::Auto),
            _ => Err(format!("unrecognized Layout: '{}'", s)),
        }
    }
}

deserialize_enum_str!(Language);
deserialize_enum_str!(Braces);
deserialize_enum_str!(Layout);

#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "snake_case")]
#[serde(deny_unknown_fields)]
#[serde(default)]
pub struct Config {
    /// Optional text to output at the beginning of the file
    pub header: Option<String>,
    /// Optional text to output at the end of the file
    pub trailer: Option<String>,
    /// Option name to use for an include guard
    pub include_guard: Option<String>,
    /// Optional text to output at major sections to deter manual editing
    pub autogen_warning: Option<String>,
    /// Include a comment with the version of cbindgen used to generate the file
    pub include_version: bool,
    /// The style to use for braces
    pub braces: Braces,
    /// The preferred length of a line, used for auto breaking function arguments
    pub line_length: usize,
    /// The amount of spaces in a tab
    pub tab_width: usize,
    /// The language to output bindings for
    pub language: Language,
    /// The configuration options for functions
    #[serde(rename = "fn")]
    pub function: FunctionConfig,
    /// The configuration options for structs
    #[serde(rename = "struct")]
    pub structure: StructConfig,
    /// The configuration options for enums
    #[serde(rename = "enum")]
    pub enumeration: EnumConfig,
}

impl Default for Config {
    fn default() -> Config {
        Config {
            header: None,
            trailer: None,
            include_guard: None,
            autogen_warning: None,
            include_version: true,
            braces: Braces::SameLine,
            line_length: 100,
            tab_width: 2,
            language: Language::C,
            function: FunctionConfig::default(),
            structure: StructConfig::default(),
            enumeration: EnumConfig::default(),
        }
    }
}

#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "snake_case")]
#[serde(deny_unknown_fields)]
#[serde(default)]
pub struct FunctionConfig {
    /// Optional text to output before each function declaration
    pub prefix: Option<String>,
    /// Optional text to output after each function declaration
    pub postfix: Option<String>,
    /// The style to layout the args
    pub args: Layout,
    /// The rename rule to apply to function args
    pub rename_args: Option<RenameRule>,
}

impl Default for FunctionConfig {
    fn default() -> FunctionConfig {
        FunctionConfig {
            prefix: None,
            postfix: None,
            args: Layout::Auto,
            rename_args: None,
        }
    }
}

#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "snake_case")]
#[serde(deny_unknown_fields)]
#[serde(default)]
pub struct StructConfig {
    /// The rename rule to apply to the name of struct fields
    pub rename_fields: Option<RenameRule>,
    /// Whether to generate a piecewise equality operator
    pub derive_eq: bool,
    /// Whether to generate a piecewise inequality operator
    pub derive_neq: bool,
    /// Whether to generate a less than operator on structs with one field
    pub derive_lt: bool,
    /// Whether to generate a less than or equal to operator on structs with one field
    pub derive_lte: bool,
    /// Whether to generate a greater than operator on structs with one field
    pub derive_gt: bool,
    /// Whether to generate a greater than or equal to operator on structs with one field
    pub derive_gte: bool,
}

impl Default for StructConfig {
    fn default() -> StructConfig {
        StructConfig {
            rename_fields: None,
            derive_eq: false,
            derive_neq: false,
            derive_lt: false,
            derive_lte: false,
            derive_gt: false,
            derive_gte: false,
        }
    }
}

#[derive( Debug, Clone, Deserialize)]
#[serde(rename_all = "snake_case")]
#[serde(deny_unknown_fields)]
#[serde(default)]
pub struct EnumConfig {
    /// The rename rule to apply to the name of enum variants
    pub rename_variants: Option<RenameRule>,
    /// Whether to add a `Sentinel` value at the end of every enum
    /// This is useful in Gecko for IPC serialization
    pub add_sentinel: bool,
}

impl Default for EnumConfig {
    fn default() -> EnumConfig {
        EnumConfig {
            rename_variants: None,
            add_sentinel: false,
        }
    }
}

impl Config {
    pub fn from_file(file_name: &str) -> Result<Config, String> {
        fn read(file_name: &str) -> io::Result<String> {
            let file = File::open(file_name)?;
            let mut reader = BufReader::new(&file);
            let mut contents = String::new();
            reader.read_to_string(&mut contents)?;
            Ok(contents)
        }

        let config_text = read(file_name).unwrap();

        match toml::from_str::<Config>(&config_text) {
            Ok(x) => Ok(x),
            Err(e) => Err(format!("couldn't parse config file: {}", e)),
        }
    }

    pub fn from_root_or_default(root: &str) -> Config {
        let c = PathBuf::from(root).join("cbindgen.toml");

        if c.exists() {
            Config::from_file(c.to_str().unwrap()).unwrap()
        } else {
            Config::default()
        }
    }

    pub fn from_webrender() -> Config {
        let license = r###"/* 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/. */"###;

        let autogen = r###"/* DO NOT MODIFY THIS MANUALLY! This file was generated using cbindgen.
 * To generate this file, clone `https://github.com/rlhunt/cbindgen` or run `cargo install cbindgen`,
 * then run `cbindgen -c wr gfx/webrender_bindings/ gfx/webrender_bindings/webrender_ffi_generated.h` */"###;

        Config {
            header: Some(String::from(license)),
            trailer: None,
            include_guard: None,
            autogen_warning: Some(String::from(autogen)),
            include_version: true,
            braces: Braces::SameLine,
            line_length: 100,
            tab_width: 2,
            language: Language::Cxx,
            function: FunctionConfig {
                prefix: Some(String::from("WR_INLINE")),
                postfix: Some(String::from("WR_FUNC")),
                args: Layout::Vertical,
                rename_args: Some(RenameRule::GeckoCase),
            },
            structure: StructConfig {
                rename_fields: Some(RenameRule::GeckoCase),
                derive_eq: true,
                derive_neq: false,
                derive_lt: false,
                derive_lte: false,
                derive_gt: false,
                derive_gte: false,
            },
            enumeration: EnumConfig {
                rename_variants: None,
                add_sentinel: true,
            },
        }
    }

    pub fn load(config: &str) -> Config {
        match config {
            "default" => Config::default(),
            "wr" => Config::from_webrender(),
            file => Config::from_file(file).unwrap(),
        }
    }
}

impl FunctionConfig {
    pub fn prefix(&self, directives: &DirectiveSet) -> Option<String> {
        if let Some(x) = directives.atom("function-prefix") {
            return x;
        }
        if let Some(x) = directives.atom("prefix") {
            return x;
        }
        self.prefix.clone()
    }

    pub fn postfix(&self, directives: &DirectiveSet) -> Option<String> {
        if let Some(x) = directives.atom("function-postfix") {
            return x;
        }
        if let Some(x) = directives.atom("postfix") {
            return x;
        }
        self.postfix.clone()
    }
}

impl StructConfig {
    pub fn derive_eq(&self, directives: &DirectiveSet) -> bool {
        if let Some(x) = directives.bool("struct-gen-op-eq") {
            return x;
        }
        if let Some(x) = directives.bool("derive-eq") {
            return x;
        }
        self.derive_eq
    }
    pub fn derive_neq(&self, directives: &DirectiveSet) -> bool {
        if let Some(x) = directives.bool("struct-gen-op-neq") {
            return x;
        }
        if let Some(x) = directives.bool("derive-neq") {
            return x;
        }
        self.derive_neq
    }
    pub fn derive_lt(&self, directives: &DirectiveSet) -> bool {
        if let Some(x) = directives.bool("struct-gen-op-lt") {
            return x;
        }
        if let Some(x) = directives.bool("derive-lt") {
            return x;
        }
        self.derive_lt
    }
    pub fn derive_lte(&self, directives: &DirectiveSet) -> bool {
        if let Some(x) = directives.bool("struct-gen-op-lte") {
            return x;
        }
        if let Some(x) = directives.bool("derive-lte") {
            return x;
        }
        self.derive_lte
    }
    pub fn derive_gt(&self, directives: &DirectiveSet) -> bool {
        if let Some(x) = directives.bool("struct-gen-op-gt") {
            return x;
        }
        if let Some(x) = directives.bool("derive-gt") {
            return x;
        }
        self.derive_gt
    }
    pub fn derive_gte(&self, directives: &DirectiveSet) -> bool {
        if let Some(x) = directives.bool("struct-gen-op-gte") {
            return x;
        }
        if let Some(x) = directives.bool("derive-gte") {
            return x;
        }
        self.derive_gte
    }
}

impl EnumConfig {
    pub fn add_sentinel(&self, directives: &DirectiveSet) -> bool {
        if let Some(x) = directives.bool("enum-add-sentinel") {
            return x;
        }
        if let Some(x) = directives.bool("add-sentinel") {
            return x;
        }
        self.add_sentinel
    }
}
<commit_msg>Update WebRender config properties<commit_after>use std::default::Default;
use std::fs::File;
use std::io::prelude::*;
use std::io::{self, BufReader};
use std::path::PathBuf;
use std::str::FromStr;

use toml;

pub use bindgen::directive::*;
pub use bindgen::rename::*;

pub const VERSION: &'static str = env!("CARGO_PKG_VERSION");

#[derive(Debug, Clone, PartialEq)]
pub enum Language {
    Cxx,
    C,
}
#[derive(Debug, Clone, PartialEq)]
pub enum Braces {
    SameLine,
    NextLine,
}
#[derive(Debug, Clone, PartialEq)]
pub enum Layout {
    Horizontal,
    Vertical,
    Auto,
}

impl FromStr for Language {
    type Err = String;

    fn from_str(s: &str) -> Result<Language, Self::Err> {
        match s {
            "cxx" => Ok(Language::Cxx),
            "Cxx" => Ok(Language::Cxx),
            "CXX" => Ok(Language::Cxx),
            "cpp" => Ok(Language::Cxx),
            "Cpp" => Ok(Language::Cxx),
            "CPP" => Ok(Language::Cxx),
            "c++" => Ok(Language::Cxx),
            "C++" => Ok(Language::Cxx),
            "c" => Ok(Language::C),
            "C" => Ok(Language::C),
            _ => Err(format!("unrecognized Language: '{}'", s)),
        }
    }
}
impl FromStr for Braces {
    type Err = String;

    fn from_str(s: &str) -> Result<Braces, Self::Err> {
        match s {
            "SameLine" => Ok(Braces::SameLine),
            "same_line" => Ok(Braces::SameLine),
            "NextLine" => Ok(Braces::NextLine),
            "next_line" => Ok(Braces::NextLine),
            _ => Err(format!("unrecognized Braces: '{}'", s)),
        }
    }
}
impl FromStr for Layout {
    type Err = String;

    fn from_str(s: &str) -> Result<Layout, Self::Err> {
        match s {
            "Horizontal" => Ok(Layout::Horizontal),
            "horizontal" => Ok(Layout::Horizontal),
            "Vertical" => Ok(Layout::Vertical),
            "vertical" => Ok(Layout::Vertical),
            "Auto" => Ok(Layout::Auto),
            "auto" => Ok(Layout::Auto),
            _ => Err(format!("unrecognized Layout: '{}'", s)),
        }
    }
}

deserialize_enum_str!(Language);
deserialize_enum_str!(Braces);
deserialize_enum_str!(Layout);

#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "snake_case")]
#[serde(deny_unknown_fields)]
#[serde(default)]
pub struct Config {
    /// Optional text to output at the beginning of the file
    pub header: Option<String>,
    /// Optional text to output at the end of the file
    pub trailer: Option<String>,
    /// Option name to use for an include guard
    pub include_guard: Option<String>,
    /// Optional text to output at major sections to deter manual editing
    pub autogen_warning: Option<String>,
    /// Include a comment with the version of cbindgen used to generate the file
    pub include_version: bool,
    /// The style to use for braces
    pub braces: Braces,
    /// The preferred length of a line, used for auto breaking function arguments
    pub line_length: usize,
    /// The amount of spaces in a tab
    pub tab_width: usize,
    /// The language to output bindings for
    pub language: Language,
    /// The configuration options for functions
    #[serde(rename = "fn")]
    pub function: FunctionConfig,
    /// The configuration options for structs
    #[serde(rename = "struct")]
    pub structure: StructConfig,
    /// The configuration options for enums
    #[serde(rename = "enum")]
    pub enumeration: EnumConfig,
}

impl Default for Config {
    fn default() -> Config {
        Config {
            header: None,
            trailer: None,
            include_guard: None,
            autogen_warning: None,
            include_version: true,
            braces: Braces::SameLine,
            line_length: 100,
            tab_width: 2,
            language: Language::C,
            function: FunctionConfig::default(),
            structure: StructConfig::default(),
            enumeration: EnumConfig::default(),
        }
    }
}

#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "snake_case")]
#[serde(deny_unknown_fields)]
#[serde(default)]
pub struct FunctionConfig {
    /// Optional text to output before each function declaration
    pub prefix: Option<String>,
    /// Optional text to output after each function declaration
    pub postfix: Option<String>,
    /// The style to layout the args
    pub args: Layout,
    /// The rename rule to apply to function args
    pub rename_args: Option<RenameRule>,
}

impl Default for FunctionConfig {
    fn default() -> FunctionConfig {
        FunctionConfig {
            prefix: None,
            postfix: None,
            args: Layout::Auto,
            rename_args: None,
        }
    }
}

#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "snake_case")]
#[serde(deny_unknown_fields)]
#[serde(default)]
pub struct StructConfig {
    /// The rename rule to apply to the name of struct fields
    pub rename_fields: Option<RenameRule>,
    /// Whether to generate a piecewise equality operator
    pub derive_eq: bool,
    /// Whether to generate a piecewise inequality operator
    pub derive_neq: bool,
    /// Whether to generate a less than operator on structs with one field
    pub derive_lt: bool,
    /// Whether to generate a less than or equal to operator on structs with one field
    pub derive_lte: bool,
    /// Whether to generate a greater than operator on structs with one field
    pub derive_gt: bool,
    /// Whether to generate a greater than or equal to operator on structs with one field
    pub derive_gte: bool,
}

impl Default for StructConfig {
    fn default() -> StructConfig {
        StructConfig {
            rename_fields: None,
            derive_eq: false,
            derive_neq: false,
            derive_lt: false,
            derive_lte: false,
            derive_gt: false,
            derive_gte: false,
        }
    }
}

#[derive( Debug, Clone, Deserialize)]
#[serde(rename_all = "snake_case")]
#[serde(deny_unknown_fields)]
#[serde(default)]
pub struct EnumConfig {
    /// The rename rule to apply to the name of enum variants
    pub rename_variants: Option<RenameRule>,
    /// Whether to add a `Sentinel` value at the end of every enum
    /// This is useful in Gecko for IPC serialization
    pub add_sentinel: bool,
}

impl Default for EnumConfig {
    fn default() -> EnumConfig {
        EnumConfig {
            rename_variants: None,
            add_sentinel: false,
        }
    }
}

impl Config {
    pub fn from_file(file_name: &str) -> Result<Config, String> {
        fn read(file_name: &str) -> io::Result<String> {
            let file = File::open(file_name)?;
            let mut reader = BufReader::new(&file);
            let mut contents = String::new();
            reader.read_to_string(&mut contents)?;
            Ok(contents)
        }

        let config_text = read(file_name).unwrap();

        match toml::from_str::<Config>(&config_text) {
            Ok(x) => Ok(x),
            Err(e) => Err(format!("couldn't parse config file: {}", e)),
        }
    }

    pub fn from_root_or_default(root: &str) -> Config {
        let c = PathBuf::from(root).join("cbindgen.toml");

        if c.exists() {
            Config::from_file(c.to_str().unwrap()).unwrap()
        } else {
            Config::default()
        }
    }

    pub fn from_webrender() -> Config {
        let license = r###"/* 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/. */"###;

        let autogen = r###"/* DO NOT MODIFY THIS MANUALLY! This file was generated using cbindgen.
 * To generate this file, clone `https://github.com/rlhunt/cbindgen` or run `cargo install cbindgen`,
 * then run `cbindgen -c wr gfx/webrender_bindings/ gfx/webrender_bindings/webrender_ffi_generated.h` */"###;

        Config {
            header: Some(String::from(license)),
            trailer: None,
            include_guard: None,
            autogen_warning: Some(String::from(autogen)),
            include_version: true,
            braces: Braces::SameLine,
            line_length: 100,
            tab_width: 2,
            language: Language::Cxx,
            function: FunctionConfig {
                prefix: Some(String::from("WR_INLINE")),
                postfix: Some(String::from("WR_FUNC")),
                args: Layout::Vertical,
                rename_args: Some(RenameRule::GeckoCase),
            },
            structure: StructConfig {
                rename_fields: None,
                derive_eq: true,
                derive_neq: false,
                derive_lt: false,
                derive_lte: false,
                derive_gt: false,
                derive_gte: false,
            },
            enumeration: EnumConfig {
                rename_variants: None,
                add_sentinel: true,
            },
        }
    }

    pub fn load(config: &str) -> Config {
        match config {
            "default" => Config::default(),
            "wr" => Config::from_webrender(),
            file => Config::from_file(file).unwrap(),
        }
    }
}

impl FunctionConfig {
    pub fn prefix(&self, directives: &DirectiveSet) -> Option<String> {
        if let Some(x) = directives.atom("function-prefix") {
            return x;
        }
        if let Some(x) = directives.atom("prefix") {
            return x;
        }
        self.prefix.clone()
    }

    pub fn postfix(&self, directives: &DirectiveSet) -> Option<String> {
        if let Some(x) = directives.atom("function-postfix") {
            return x;
        }
        if let Some(x) = directives.atom("postfix") {
            return x;
        }
        self.postfix.clone()
    }
}

impl StructConfig {
    pub fn derive_eq(&self, directives: &DirectiveSet) -> bool {
        if let Some(x) = directives.bool("struct-gen-op-eq") {
            return x;
        }
        if let Some(x) = directives.bool("derive-eq") {
            return x;
        }
        self.derive_eq
    }
    pub fn derive_neq(&self, directives: &DirectiveSet) -> bool {
        if let Some(x) = directives.bool("struct-gen-op-neq") {
            return x;
        }
        if let Some(x) = directives.bool("derive-neq") {
            return x;
        }
        self.derive_neq
    }
    pub fn derive_lt(&self, directives: &DirectiveSet) -> bool {
        if let Some(x) = directives.bool("struct-gen-op-lt") {
            return x;
        }
        if let Some(x) = directives.bool("derive-lt") {
            return x;
        }
        self.derive_lt
    }
    pub fn derive_lte(&self, directives: &DirectiveSet) -> bool {
        if let Some(x) = directives.bool("struct-gen-op-lte") {
            return x;
        }
        if let Some(x) = directives.bool("derive-lte") {
            return x;
        }
        self.derive_lte
    }
    pub fn derive_gt(&self, directives: &DirectiveSet) -> bool {
        if let Some(x) = directives.bool("struct-gen-op-gt") {
            return x;
        }
        if let Some(x) = directives.bool("derive-gt") {
            return x;
        }
        self.derive_gt
    }
    pub fn derive_gte(&self, directives: &DirectiveSet) -> bool {
        if let Some(x) = directives.bool("struct-gen-op-gte") {
            return x;
        }
        if let Some(x) = directives.bool("derive-gte") {
            return x;
        }
        self.derive_gte
    }
}

impl EnumConfig {
    pub fn add_sentinel(&self, directives: &DirectiveSet) -> bool {
        if let Some(x) = directives.bool("enum-add-sentinel") {
            return x;
        }
        if let Some(x) = directives.bool("add-sentinel") {
            return x;
        }
        self.add_sentinel
    }
}
<|endoftext|>


<commit_before>// Copyright 2013 The CGMath Developers. For a full listing of the authors,
// refer to the AUTHORS file at the top-level directory of this distribution.
//
// 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.

//! Points are fixed positions in affine space with no length or direction. This
//! disinguishes them from vectors, which have a length and direction, but do
//! not have a fixed position.

use std::num::zero;

use array::*;
use vector::*;

/// A point in 2-dimensional space.
#[deriving(Eq, Zero, Clone)]
struct Point2<S> { x: S, y: S }

/// A point in 2-dimensional space.
#[deriving(Eq, Zero, Clone)]
struct Point3<S> { x: S, y: S, z: S }

approx_eq!(impl<S> Point2<S>)
approx_eq!(impl<S> Point3<S>)

impl<S: Num> Point2<S> {
    #[inline]
    pub fn new(x: S, y: S) -> Point2<S> {
        Point2 { x: x, y: y }
    }

    #[inline]
    pub fn origin() -> Point2<S> { zero() }
}

impl<S: Num> Point3<S> {
    #[inline]
    pub fn new(x: S, y: S, z: S) -> Point3<S> {
        Point3 { x: x, y: y, z: z }
    }

    #[inline]
    pub fn origin() -> Point3<S> { zero() }
}

/// Specifies the numeric operations for point types.
pub trait Point
<
    S: Primitive,
    V: Vector<S, Slice>,
    Slice
>
:   Array<S, Slice>
{
    #[inline] fn mul_s(&self, s: S) -> Self { build(|i| self.i(i).mul(&s)) }
    #[inline] fn div_s(&self, s: S) -> Self { build(|i| self.i(i).div(&s)) }
    #[inline] fn rem_s(&self, s: S) -> Self { build(|i| self.i(i).rem(&s)) }

    #[inline] fn add_v(&self, other: &V) -> Self { build(|i| self.i(i).add(other.i(i))) }
    #[inline] fn sub_p(&self, other: &Self) -> V { build(|i| self.i(i).sub(other.i(i))) }

    #[inline] fn mul_self_s(&mut self, s: S) { self.each_mut(|_, x| *x = x.mul(&s)) }
    #[inline] fn div_self_s(&mut self, s: S) { self.each_mut(|_, x| *x = x.div(&s)) }
    #[inline] fn rem_self_s(&mut self, s: S) { self.each_mut(|_, x| *x = x.rem(&s)) }

    #[inline] fn add_self_v(&mut self, other: &V) { self.each_mut(|i, x| *x = x.add(other.i(i))) }

    /// This is a weird one, but its useful for plane calculations
    #[inline]
    fn dot(&self, v: &V) -> S {
        build::<S, Slice, V>(|i| self.i(i).mul(v.i(i))).comp_add()
    }
}

array!(impl<S> Point2<S> -> [S, ..2] _2)
array!(impl<S> Point3<S> -> [S, ..3] _3)

impl<S: Primitive> Point<S, Vec2<S>, [S, ..2]> for Point2<S> {}
impl<S: Primitive> Point<S, Vec3<S>, [S, ..3]> for Point3<S> {}

impl<S> ToStr for Point2<S> {
    fn to_str(&self) -> ~str {
        fmt!("[%?, %?]", self.x, self.y)
    }
}

impl<S> ToStr for Point3<S> {
    fn to_str(&self) -> ~str {
        fmt!("[%?, %?, %?]", self.x, self.y, self.z)
    }
}
<commit_msg>Make point structs public<commit_after>// Copyright 2013 The CGMath Developers. For a full listing of the authors,
// refer to the AUTHORS file at the top-level directory of this distribution.
//
// 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.

//! Points are fixed positions in affine space with no length or direction. This
//! disinguishes them from vectors, which have a length and direction, but do
//! not have a fixed position.

use std::num::zero;

use array::*;
use vector::*;

/// A point in 2-dimensional space.
#[deriving(Eq, Zero, Clone)]
pub struct Point2<S> { x: S, y: S }

/// A point in 2-dimensional space.
#[deriving(Eq, Zero, Clone)]
pub struct Point3<S> { x: S, y: S, z: S }

approx_eq!(impl<S> Point2<S>)
approx_eq!(impl<S> Point3<S>)

impl<S: Num> Point2<S> {
    #[inline]
    pub fn new(x: S, y: S) -> Point2<S> {
        Point2 { x: x, y: y }
    }

    #[inline]
    pub fn origin() -> Point2<S> { zero() }
}

impl<S: Num> Point3<S> {
    #[inline]
    pub fn new(x: S, y: S, z: S) -> Point3<S> {
        Point3 { x: x, y: y, z: z }
    }

    #[inline]
    pub fn origin() -> Point3<S> { zero() }
}

/// Specifies the numeric operations for point types.
pub trait Point
<
    S: Primitive,
    V: Vector<S, Slice>,
    Slice
>
:   Array<S, Slice>
{
    #[inline] fn mul_s(&self, s: S) -> Self { build(|i| self.i(i).mul(&s)) }
    #[inline] fn div_s(&self, s: S) -> Self { build(|i| self.i(i).div(&s)) }
    #[inline] fn rem_s(&self, s: S) -> Self { build(|i| self.i(i).rem(&s)) }

    #[inline] fn add_v(&self, other: &V) -> Self { build(|i| self.i(i).add(other.i(i))) }
    #[inline] fn sub_p(&self, other: &Self) -> V { build(|i| self.i(i).sub(other.i(i))) }

    #[inline] fn mul_self_s(&mut self, s: S) { self.each_mut(|_, x| *x = x.mul(&s)) }
    #[inline] fn div_self_s(&mut self, s: S) { self.each_mut(|_, x| *x = x.div(&s)) }
    #[inline] fn rem_self_s(&mut self, s: S) { self.each_mut(|_, x| *x = x.rem(&s)) }

    #[inline] fn add_self_v(&mut self, other: &V) { self.each_mut(|i, x| *x = x.add(other.i(i))) }

    /// This is a weird one, but its useful for plane calculations
    #[inline]
    fn dot(&self, v: &V) -> S {
        build::<S, Slice, V>(|i| self.i(i).mul(v.i(i))).comp_add()
    }
}

array!(impl<S> Point2<S> -> [S, ..2] _2)
array!(impl<S> Point3<S> -> [S, ..3] _3)

impl<S: Primitive> Point<S, Vec2<S>, [S, ..2]> for Point2<S> {}
impl<S: Primitive> Point<S, Vec3<S>, [S, ..3]> for Point3<S> {}

impl<S> ToStr for Point2<S> {
    fn to_str(&self) -> ~str {
        fmt!("[%?, %?]", self.x, self.y)
    }
}

impl<S> ToStr for Point3<S> {
    fn to_str(&self) -> ~str {
        fmt!("[%?, %?, %?]", self.x, self.y, self.z)
    }
}
<|endoftext|>


<commit_before>//! Job management (mostly for windows)
//!
//! Most of the time when you're running cargo you expect Ctrl-C to actually
//! terminate the entire tree of processes in play, not just the one at the top
//! (cago). This currently works "by default" on Unix platforms because Ctrl-C
//! actually sends a signal to the *process group* rather than the parent
//! process, so everything will get torn down. On Windows, however, this does
//! not happen and Ctrl-C just kills cargo.
//!
//! To achieve the same semantics on Windows we use Job Objects to ensure that
//! all processes die at the same time. Job objects have a mode of operation
//! where when all handles to the object are closed it causes all child
//! processes associated with the object to be terminated immediately.
//! Conveniently whenever a process in the job object spawns a new process the
//! child will be associated with the job object as well. This means if we add
//! ourselves to the job object we create then everything will get torn down!

pub use self::imp::Setup;

pub fn setup() -> Option<Setup> {
    unsafe { imp::setup() }
}

#[cfg(unix)]
mod imp {
    use std::env;

    pub type Setup = ();

    pub unsafe fn setup() -> Option<()> {
        // There's a test case for the behavior of
        // when-cargo-is-killed-subprocesses-are-also-killed, but that requires
        // one cargo spawned to become its own session leader, so we do that
        // here.
        if env::var("__CARGO_TEST_SETSID_PLEASE_DONT_USE_ELSEWHERE").is_ok() {
            libc::setsid();
        }
        Some(())
    }
}

#[cfg(windows)]
mod imp {
    use std::io;
    use std::mem;
    use std::ptr;

    use log::info;

    use winapi::shared::minwindef::*;
    use winapi::um::handleapi::*;
    use winapi::um::jobapi2::*;
    use winapi::um::processthreadsapi::*;
    use winapi::um::winnt::HANDLE;
    use winapi::um::winnt::*;

    pub struct Setup {
        job: Handle,
    }

    pub struct Handle {
        inner: HANDLE,
    }

    fn last_err() -> io::Error {
        io::Error::last_os_error()
    }

    pub unsafe fn setup() -> Option<Setup> {
        // Creates a new job object for us to use and then adds ourselves to it.
        // Note that all errors are basically ignored in this function,
        // intentionally. Job objects are "relatively new" in Windows,
        // particularly the ability to support nested job objects. Older
        // Windows installs don't support this ability. We probably don't want
        // to force Cargo to abort in this situation or force others to *not*
        // use job objects, so we instead just ignore errors and assume that
        // we're otherwise part of someone else's job object in this case.

        let job = CreateJobObjectW(ptr::null_mut(), ptr::null());
        if job.is_null() {
            return None;
        }
        let job = Handle { inner: job };

        // Indicate that when all handles to the job object are gone that all
        // process in the object should be killed. Note that this includes our
        // entire process tree by default because we've added ourselves and
        // our children will reside in the job once we spawn a process.
        let mut info: JOBOBJECT_EXTENDED_LIMIT_INFORMATION;
        info = mem::zeroed();
        info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
        let r = SetInformationJobObject(
            job.inner,
            JobObjectExtendedLimitInformation,
            &mut info as *mut _ as LPVOID,
            mem::size_of_val(&info) as DWORD,
        );
        if r == 0 {
            return None;
        }

        // Assign our process to this job object, meaning that our children will
        // now live or die based on our existence.
        let me = GetCurrentProcess();
        let r = AssignProcessToJobObject(job.inner, me);
        if r == 0 {
            return None;
        }

        Some(Setup { job })
    }

    impl Drop for Setup {
        fn drop(&mut self) {
            // On normal exits (not ctrl-c), we don't want to kill any child
            // processes. The destructor here configures our job object to
            // **not** kill everything on close, then closes the job object.
            unsafe {
                let mut info: JOBOBJECT_EXTENDED_LIMIT_INFORMATION;
                info = mem::zeroed();
                let r = SetInformationJobObject(
                    self.job.inner,
                    JobObjectExtendedLimitInformation,
                    &mut info as *mut _ as LPVOID,
                    mem::size_of_val(&info) as DWORD,
                );
                if r == 0 {
                    info!("failed to configure job object to defaults: {}", last_err());
                }
            }
        }
    }

    impl Drop for Handle {
        fn drop(&mut self) {
            unsafe {
                CloseHandle(self.inner);
            }
        }
    }
}
<commit_msg>Correct mispelling of `cargo`.<commit_after>//! Job management (mostly for windows)
//!
//! Most of the time when you're running cargo you expect Ctrl-C to actually
//! terminate the entire tree of processes in play, not just the one at the top
//! (cargo). This currently works "by default" on Unix platforms because Ctrl-C
//! actually sends a signal to the *process group* rather than the parent
//! process, so everything will get torn down. On Windows, however, this does
//! not happen and Ctrl-C just kills cargo.
//!
//! To achieve the same semantics on Windows we use Job Objects to ensure that
//! all processes die at the same time. Job objects have a mode of operation
//! where when all handles to the object are closed it causes all child
//! processes associated with the object to be terminated immediately.
//! Conveniently whenever a process in the job object spawns a new process the
//! child will be associated with the job object as well. This means if we add
//! ourselves to the job object we create then everything will get torn down!

pub use self::imp::Setup;

pub fn setup() -> Option<Setup> {
    unsafe { imp::setup() }
}

#[cfg(unix)]
mod imp {
    use std::env;

    pub type Setup = ();

    pub unsafe fn setup() -> Option<()> {
        // There's a test case for the behavior of
        // when-cargo-is-killed-subprocesses-are-also-killed, but that requires
        // one cargo spawned to become its own session leader, so we do that
        // here.
        if env::var("__CARGO_TEST_SETSID_PLEASE_DONT_USE_ELSEWHERE").is_ok() {
            libc::setsid();
        }
        Some(())
    }
}

#[cfg(windows)]
mod imp {
    use std::io;
    use std::mem;
    use std::ptr;

    use log::info;

    use winapi::shared::minwindef::*;
    use winapi::um::handleapi::*;
    use winapi::um::jobapi2::*;
    use winapi::um::processthreadsapi::*;
    use winapi::um::winnt::HANDLE;
    use winapi::um::winnt::*;

    pub struct Setup {
        job: Handle,
    }

    pub struct Handle {
        inner: HANDLE,
    }

    fn last_err() -> io::Error {
        io::Error::last_os_error()
    }

    pub unsafe fn setup() -> Option<Setup> {
        // Creates a new job object for us to use and then adds ourselves to it.
        // Note that all errors are basically ignored in this function,
        // intentionally. Job objects are "relatively new" in Windows,
        // particularly the ability to support nested job objects. Older
        // Windows installs don't support this ability. We probably don't want
        // to force Cargo to abort in this situation or force others to *not*
        // use job objects, so we instead just ignore errors and assume that
        // we're otherwise part of someone else's job object in this case.

        let job = CreateJobObjectW(ptr::null_mut(), ptr::null());
        if job.is_null() {
            return None;
        }
        let job = Handle { inner: job };

        // Indicate that when all handles to the job object are gone that all
        // process in the object should be killed. Note that this includes our
        // entire process tree by default because we've added ourselves and
        // our children will reside in the job once we spawn a process.
        let mut info: JOBOBJECT_EXTENDED_LIMIT_INFORMATION;
        info = mem::zeroed();
        info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
        let r = SetInformationJobObject(
            job.inner,
            JobObjectExtendedLimitInformation,
            &mut info as *mut _ as LPVOID,
            mem::size_of_val(&info) as DWORD,
        );
        if r == 0 {
            return None;
        }

        // Assign our process to this job object, meaning that our children will
        // now live or die based on our existence.
        let me = GetCurrentProcess();
        let r = AssignProcessToJobObject(job.inner, me);
        if r == 0 {
            return None;
        }

        Some(Setup { job })
    }

    impl Drop for Setup {
        fn drop(&mut self) {
            // On normal exits (not ctrl-c), we don't want to kill any child
            // processes. The destructor here configures our job object to
            // **not** kill everything on close, then closes the job object.
            unsafe {
                let mut info: JOBOBJECT_EXTENDED_LIMIT_INFORMATION;
                info = mem::zeroed();
                let r = SetInformationJobObject(
                    self.job.inner,
                    JobObjectExtendedLimitInformation,
                    &mut info as *mut _ as LPVOID,
                    mem::size_of_val(&info) as DWORD,
                );
                if r == 0 {
                    info!("failed to configure job object to defaults: {}", last_err());
                }
            }
        }
    }

    impl Drop for Handle {
        fn drop(&mut self) {
            unsafe {
                CloseHandle(self.inner);
            }
        }
    }
}
<|endoftext|>


<commit_before><commit_msg>Auto merge of #3084 - pyfisch:remove-border-widths-au, r=nical<commit_after><|endoftext|>


<commit_before>use std::{
    borrow::Cow,
    collections::{BTreeMap, BTreeSet, HashMap},
    path::{Path, PathBuf},
    process::Stdio,
};

use anyhow::Context;
use cargo_metadata::{DependencyKind, Metadata, MetadataCommand};
use cargo_raze::{
    context::CrateContext,
    metadata::RazeMetadataFetcher,
    planning::{BuildPlanner, BuildPlannerImpl},
    settings::{GenMode, RazeSettings},
};
use log::trace;
use semver::{Version, VersionReq};
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
use url::Url;

use crate::{
    consolidator::{Consolidator, ConsolidatorConfig, ConsolidatorOverride},
    renderer::RenderConfig,
    serde_utils::{CargoConfig, Registry},
    NamedTempFile,
};

pub struct ResolverConfig {
    pub cargo: PathBuf,
    pub default_registry_index_url: Url,
    pub default_registry_download_url_template: String,
    pub additional_registries: BTreeMap<String, Url>,
}

pub struct Resolver {
    pub toml: toml::Value,
    pub resolver_config: ResolverConfig,
    pub consolidator_config: ConsolidatorConfig,
    pub render_config: RenderConfig,
    pub target_triples: BTreeSet<String>,
    pub label_to_crates: BTreeMap<String, BTreeSet<String>>,
    digest: Option<String>,
}

// TODO: Interesting edge cases
// - you can pass deps using: version number path on fs, git repo.
// - you can rename crates you depend on.
pub struct ResolvedArtifactsWithMetadata {
    pub resolved_packages: Vec<CrateContext>,
    pub member_packages_version_mapping: HashMap<String, Version>,
}

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
pub struct Dependencies {
    pub normal: BTreeMap<String, Version>,
    pub build: BTreeMap<String, Version>,
    pub dev: BTreeMap<String, Version>,
}

impl Resolver {
    pub fn new(
        toml: toml::Value,
        resolver_config: ResolverConfig,
        consolidator_config: ConsolidatorConfig,
        render_config: RenderConfig,
        target_triples: BTreeSet<String>,
        label_to_crates: BTreeMap<String, BTreeSet<String>>,
    ) -> Resolver {
        Resolver {
            toml,
            resolver_config,
            consolidator_config,
            render_config,
            target_triples,
            label_to_crates,
            digest: None,
        }
    }

    pub fn digest(&mut self) -> anyhow::Result<String> {
        // TODO: Ignore * .cargo config files outside of the workspace

        if self.digest.is_none() {
            // TODO: Combine values better
            let mut hasher = Sha256::new();
            // Mix in the version of this crate, which encompasses all logic and templates.
            // This is probably a wild over-estimate of what should go in the cache key.
            // NOTE: In debug mode, this mixes the digest of the executable, rather than the version number.
            hasher.update(version_for_hashing()?);
            hasher.update(b"\0");

            // If new fields are added, you should decide whether they need hashing.
            // Hint: They probably do. If not, please add a comment justifying why not.
            let Self {
                toml,
                render_config:
                    RenderConfig {
                        repo_rule_name,
                        rules_rust_workspace_name,
                    },
                consolidator_config: ConsolidatorConfig { overrides },
                resolver_config:
                    ResolverConfig {
                        cargo,
                        default_registry_index_url,
                        default_registry_download_url_template,
                        additional_registries,
                    },

                // This is what we're computing.
                digest: _ignored,
                target_triples,
                label_to_crates,
            } = &self;

            hasher.update(repo_rule_name.as_str().as_bytes());
            hasher.update(b"\0");
            hasher.update(rules_rust_workspace_name.as_bytes());
            hasher.update(b"\0");

            hasher.update(get_cargo_version(cargo)?);
            hasher.update(b"\0");
            hasher.update(default_registry_index_url.as_str().as_bytes());
            hasher.update(b"\0");
            hasher.update(default_registry_download_url_template.as_bytes());
            hasher.update(b"\0");
            for (name, url) in additional_registries {
                name.as_bytes();
                hasher.update("b\0");
                url.as_str().as_bytes();
                hasher.update("b\0");
            }
            for target_triple in target_triples {
                hasher.update(target_triple);
                hasher.update(b"\0");
            }
            hasher.update(b"\0");
            for (label, crates) in label_to_crates.iter() {
                hasher.update(label.as_bytes());
                hasher.update(b"\0");
                for krate in crates.iter() {
                    hasher.update(krate.as_bytes());
                    hasher.update(b"\0");
                }
            }

            // TODO: improve the caching by generating a lockfile over the resolve rather than over
            // the render. If the digest contains only input for the cargo dependency resolution
            // then we don't need to re-pin when making changes to things that only affect the
            // generated bazel file.
            for (
                crate_name,
                ConsolidatorOverride {
                    extra_rustc_env_vars,
                    extra_build_script_env_vars,
                    extra_bazel_deps,
                    extra_bazel_data_deps,
                    extra_build_script_bazel_deps,
                    extra_build_script_bazel_data_deps,
                    features_to_remove,
                },
            ) in overrides
            {
                hasher.update(crate_name);
                hasher.update(b"\0");
                for (env_key, env_val) in extra_rustc_env_vars {
                    hasher.update(env_key);
                    hasher.update(b"\0");
                    hasher.update(env_val);
                    hasher.update(b"\0");
                }
                for (env_key, env_val) in extra_build_script_env_vars {
                    hasher.update(env_key);
                    hasher.update(b"\0");
                    hasher.update(env_val);
                    hasher.update(b"\0");
                }
                for dep_map in &[
                    extra_bazel_deps,
                    extra_bazel_data_deps,
                    extra_build_script_bazel_deps,
                    extra_build_script_bazel_data_deps,
                ] {
                    for (target, deps) in *dep_map {
                        hasher.update(target);
                        hasher.update(b"\0");
                        for dep in deps {
                            hasher.update(dep);
                            hasher.update(b"\0");
                        }
                    }
                }
                for feature in features_to_remove {
                    hasher.update(feature);
                    hasher.update(b"\n");
                }
            }

            for (env_name, env_value) in std::env::vars() {
                // The CARGO_HOME variable changes where cargo writes and reads config, and caches.
                // We currently use the user's Cargo home (by not overwriting it) so we should
                // allow users to use a custom path to one.
                if env_name == "CARGO_HOME" {
                    continue;
                }
                if env_name == "CARGO" {
                    continue;
                }
                if env_name == "RUSTC" {
                    continue;
                }
                // We hope that other env vars don't cause problems...
                if env_name.starts_with("CARGO") && env_name != "CARGO_NET_GIT_FETCH_WITH_CLI" {
                    eprintln!("Warning: You have the {} environment variable set - this may affect your crate_universe output", env_name);
                    hasher.update(env_name);
                    hasher.update(b"\0");
                    hasher.update(env_value);
                    hasher.update(b"\0");
                }
            }

            hasher.update(toml.to_string().as_bytes());
            hasher.update(b"\0");

            // TODO: Include all files referenced by the toml.
            self.digest = Some(hex::encode(hasher.finalize()));
        }
        // UNWRAP: Guaranteed by above code.
        Ok(self.digest.clone().unwrap())
    }

    pub fn resolve(mut self) -> anyhow::Result<Consolidator> {
        let toml_str = self.toml.to_string();
        trace!("Resolving for generated Cargo.toml:\n{}", toml_str);
        let merged_cargo_toml = NamedTempFile::with_str_content("Cargo.toml", &toml_str)
            .context("Writing intermediate Cargo.toml")?;

        if !self.resolver_config.additional_registries.is_empty() {
            let dot_cargo = merged_cargo_toml.path().parent().unwrap().join(".cargo");
            std::fs::create_dir(&dot_cargo)?;

            let config = CargoConfig {
                registries: self
                    .resolver_config
                    .additional_registries
                    .iter()
                    .map(|(name, index)| {
                        (
                            name.to_owned(),
                            Registry {
                                index: index.clone(),
                            },
                        )
                    })
                    .collect(),
            };
            let generated_toml = toml::to_vec(&config)?;

            std::fs::write(dot_cargo.join("config.toml"), generated_toml)?;
        }

        // RazeMetadataFetcher only uses the scheme+host+port of this URL.
        // If it used the path, we'd run into issues escaping the {s and }s from the template,
        // but the scheme+host+port should be fine.
        let repository_template_url =
            Url::parse(&self.resolver_config.default_registry_download_url_template)
                .context("Parsing repository template URL")?;

        let md_fetcher = RazeMetadataFetcher::new(
            &self.resolver_config.cargo,
            repository_template_url,
            self.resolver_config.default_registry_index_url.clone(),
        );
        let metadata = md_fetcher
            .fetch_metadata(merged_cargo_toml.path().parent().unwrap(), None, None)
            .context("Failed fetching metadata")?;

        let raze_settings = RazeSettings {
            gen_workspace_prefix: self.render_config.repo_rule_name.clone(),
            genmode: GenMode::Remote,

            // TODO: These are ?all ignored
            workspace_path: "".to_string(),
            package_aliases_dir: "".to_string(),
            render_package_aliases: false,
            target: None,
            targets: Some(self.target_triples.iter().cloned().collect()),
            crates: HashMap::default(),
            output_buildfile_suffix: "".to_string(),
            default_gen_buildrs: true,
            registry: self
                .resolver_config
                .default_registry_download_url_template
                .clone(),
            index_url: self
                .resolver_config
                .default_registry_index_url
                .as_str()
                .to_owned(),
            rust_rules_workspace_name: self.render_config.rules_rust_workspace_name.clone(),
            vendor_dir: "".to_string(),
            experimental_api: false,
            binary_deps: HashMap::new(),
        };

        let planner = BuildPlannerImpl::new(metadata, raze_settings);

        let planned_build = planner.plan_build(None).context("Failed planning build")?;

        let mut resolved_packages = planned_build.crate_contexts;
        resolved_packages
            .sort_by(|l, r| (&l.pkg_name, &l.pkg_version).cmp(&(&r.pkg_name, &r.pkg_version)));

        let member_packages_version_mapping =
            self.get_member_packages_version_mapping(merged_cargo_toml.path(), &resolved_packages);

        // TODO: generate a cargo toml from metadata in the bazel rule, when no cargo toml is present.

        let digest = self.digest().context("Digesting Resolver inputs")?;
        Ok(Consolidator::new(
            self.consolidator_config,
            self.render_config,
            digest,
            self.target_triples,
            resolved_packages,
            member_packages_version_mapping?,
            self.label_to_crates,
        ))
    }

    fn get_member_packages_version_mapping(
        &self,
        merged_cargo_toml: &Path,
        resolved_artifacts: &[CrateContext],
    ) -> anyhow::Result<Dependencies> {
        let merged_cargo_metadata = MetadataCommand::new()
            .cargo_path(&self.resolver_config.cargo)
            .current_dir(merged_cargo_toml.parent().unwrap())
            .manifest_path(merged_cargo_toml)
            .no_deps()
            .exec()
            .context("Failed to run cargo metadata")?;

        Ok(Dependencies {
            normal: Self::build_version_mapping_for_kind(
                DependencyKind::Normal,
                &merged_cargo_metadata,
                resolved_artifacts,
            ),
            build: Self::build_version_mapping_for_kind(
                DependencyKind::Build,
                &merged_cargo_metadata,
                resolved_artifacts,
            ),
            dev: Self::build_version_mapping_for_kind(
                DependencyKind::Development,
                &merged_cargo_metadata,
                resolved_artifacts,
            ),
        })
    }

    fn build_version_mapping_for_kind(
        kind: DependencyKind,
        merged_cargo_metadata: &Metadata,
        resolved_artifacts: &[CrateContext],
    ) -> BTreeMap<String, Version> {
        // Build the intersection of version requirements for all the member (i.e. toplevel) packages
        // of our workspace.
        let mut member_package_version_reqs: HashMap<String, Vec<VersionReq>> = Default::default();
        for package in &merged_cargo_metadata.packages {
            for dep in &package.dependencies {
                if dep.kind == kind {
                    member_package_version_reqs
                        .entry(dep.name.clone())
                        .or_default()
                        .push(dep.req.clone());
                }
            }
        }

        let mut member_package_version_mapping = BTreeMap::new();
        for package in resolved_artifacts {
            // If the package name matches one of the member packages' direct dependencies, consider it
            // for the final version: insert it into the map if we didn't have one yet, take the highest
            // version so far if there was already one.
            if let Some(version_req) = member_package_version_reqs.get(&package.pkg_name) {
                if version_req
                    .iter()
                    .all(|req| req.matches(&package.pkg_version))
                {
                    let current_pkg_version = member_package_version_mapping
                        .entry(package.pkg_name.clone())
                        .or_insert_with(|| Version::new(0, 0, 0));
                    if *current_pkg_version < package.pkg_version {
                        *current_pkg_version = package.pkg_version.clone();
                    }
                }
            }
        }
        member_package_version_mapping
    }
}

fn get_cargo_version(cargo_path: &Path) -> anyhow::Result<Vec<u8>> {
    let output = std::process::Command::new(cargo_path)
        .arg("--version")
        .stderr(Stdio::inherit())
        .output()
        .context("Invoking cargo --version")?;
    if !output.status.success() {
        panic!(
            "TODO: cargo --version failed with exit code {:?}",
            output.status.code()
        );
    }
    Ok(output.stdout)
}

fn version_for_hashing() -> anyhow::Result<Cow<'static, [u8]>> {
    if cfg!(debug_assertions) {
        let current_exe =
            std::env::current_exe().context("Couldn't get current executable path")?;
        Ok(Cow::Owned(
            std::fs::read(current_exe).context("Couldn't read current executable path")?,
        ))
    } else {
        Ok(Cow::Borrowed(env!("CARGO_PKG_VERSION").as_bytes()))
    }
}
<commit_msg>Don't filter out pre-release versions for git deps (#1028)<commit_after>use std::{
    borrow::Cow,
    collections::{BTreeMap, BTreeSet, HashMap},
    path::{Path, PathBuf},
    process::Stdio,
};

use anyhow::Context;
use cargo_metadata::{DependencyKind, Metadata, MetadataCommand};
use cargo_raze::{
    context::CrateContext,
    metadata::RazeMetadataFetcher,
    planning::{BuildPlanner, BuildPlannerImpl},
    settings::{GenMode, RazeSettings},
};
use log::trace;
use semver::{Version, VersionReq};
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
use url::Url;

use crate::{
    consolidator::{Consolidator, ConsolidatorConfig, ConsolidatorOverride},
    renderer::RenderConfig,
    serde_utils::{CargoConfig, Registry},
    NamedTempFile,
};

pub struct ResolverConfig {
    pub cargo: PathBuf,
    pub default_registry_index_url: Url,
    pub default_registry_download_url_template: String,
    pub additional_registries: BTreeMap<String, Url>,
}

pub struct Resolver {
    pub toml: toml::Value,
    pub resolver_config: ResolverConfig,
    pub consolidator_config: ConsolidatorConfig,
    pub render_config: RenderConfig,
    pub target_triples: BTreeSet<String>,
    pub label_to_crates: BTreeMap<String, BTreeSet<String>>,
    digest: Option<String>,
}

// TODO: Interesting edge cases
// - you can pass deps using: version number path on fs, git repo.
// - you can rename crates you depend on.
pub struct ResolvedArtifactsWithMetadata {
    pub resolved_packages: Vec<CrateContext>,
    pub member_packages_version_mapping: HashMap<String, Version>,
}

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
pub struct Dependencies {
    pub normal: BTreeMap<String, Version>,
    pub build: BTreeMap<String, Version>,
    pub dev: BTreeMap<String, Version>,
}

impl Resolver {
    pub fn new(
        toml: toml::Value,
        resolver_config: ResolverConfig,
        consolidator_config: ConsolidatorConfig,
        render_config: RenderConfig,
        target_triples: BTreeSet<String>,
        label_to_crates: BTreeMap<String, BTreeSet<String>>,
    ) -> Resolver {
        Resolver {
            toml,
            resolver_config,
            consolidator_config,
            render_config,
            target_triples,
            label_to_crates,
            digest: None,
        }
    }

    pub fn digest(&mut self) -> anyhow::Result<String> {
        // TODO: Ignore * .cargo config files outside of the workspace

        if self.digest.is_none() {
            // TODO: Combine values better
            let mut hasher = Sha256::new();
            // Mix in the version of this crate, which encompasses all logic and templates.
            // This is probably a wild over-estimate of what should go in the cache key.
            // NOTE: In debug mode, this mixes the digest of the executable, rather than the version number.
            hasher.update(version_for_hashing()?);
            hasher.update(b"\0");

            // If new fields are added, you should decide whether they need hashing.
            // Hint: They probably do. If not, please add a comment justifying why not.
            let Self {
                toml,
                render_config:
                    RenderConfig {
                        repo_rule_name,
                        rules_rust_workspace_name,
                    },
                consolidator_config: ConsolidatorConfig { overrides },
                resolver_config:
                    ResolverConfig {
                        cargo,
                        default_registry_index_url,
                        default_registry_download_url_template,
                        additional_registries,
                    },

                // This is what we're computing.
                digest: _ignored,
                target_triples,
                label_to_crates,
            } = &self;

            hasher.update(repo_rule_name.as_str().as_bytes());
            hasher.update(b"\0");
            hasher.update(rules_rust_workspace_name.as_bytes());
            hasher.update(b"\0");

            hasher.update(get_cargo_version(cargo)?);
            hasher.update(b"\0");
            hasher.update(default_registry_index_url.as_str().as_bytes());
            hasher.update(b"\0");
            hasher.update(default_registry_download_url_template.as_bytes());
            hasher.update(b"\0");
            for (name, url) in additional_registries {
                name.as_bytes();
                hasher.update("b\0");
                url.as_str().as_bytes();
                hasher.update("b\0");
            }
            for target_triple in target_triples {
                hasher.update(target_triple);
                hasher.update(b"\0");
            }
            hasher.update(b"\0");
            for (label, crates) in label_to_crates.iter() {
                hasher.update(label.as_bytes());
                hasher.update(b"\0");
                for krate in crates.iter() {
                    hasher.update(krate.as_bytes());
                    hasher.update(b"\0");
                }
            }

            // TODO: improve the caching by generating a lockfile over the resolve rather than over
            // the render. If the digest contains only input for the cargo dependency resolution
            // then we don't need to re-pin when making changes to things that only affect the
            // generated bazel file.
            for (
                crate_name,
                ConsolidatorOverride {
                    extra_rustc_env_vars,
                    extra_build_script_env_vars,
                    extra_bazel_deps,
                    extra_bazel_data_deps,
                    extra_build_script_bazel_deps,
                    extra_build_script_bazel_data_deps,
                    features_to_remove,
                },
            ) in overrides
            {
                hasher.update(crate_name);
                hasher.update(b"\0");
                for (env_key, env_val) in extra_rustc_env_vars {
                    hasher.update(env_key);
                    hasher.update(b"\0");
                    hasher.update(env_val);
                    hasher.update(b"\0");
                }
                for (env_key, env_val) in extra_build_script_env_vars {
                    hasher.update(env_key);
                    hasher.update(b"\0");
                    hasher.update(env_val);
                    hasher.update(b"\0");
                }
                for dep_map in &[
                    extra_bazel_deps,
                    extra_bazel_data_deps,
                    extra_build_script_bazel_deps,
                    extra_build_script_bazel_data_deps,
                ] {
                    for (target, deps) in *dep_map {
                        hasher.update(target);
                        hasher.update(b"\0");
                        for dep in deps {
                            hasher.update(dep);
                            hasher.update(b"\0");
                        }
                    }
                }
                for feature in features_to_remove {
                    hasher.update(feature);
                    hasher.update(b"\n");
                }
            }

            for (env_name, env_value) in std::env::vars() {
                // The CARGO_HOME variable changes where cargo writes and reads config, and caches.
                // We currently use the user's Cargo home (by not overwriting it) so we should
                // allow users to use a custom path to one.
                if env_name == "CARGO_HOME" {
                    continue;
                }
                if env_name == "CARGO" {
                    continue;
                }
                if env_name == "RUSTC" {
                    continue;
                }
                // We hope that other env vars don't cause problems...
                if env_name.starts_with("CARGO") && env_name != "CARGO_NET_GIT_FETCH_WITH_CLI" {
                    eprintln!("Warning: You have the {} environment variable set - this may affect your crate_universe output", env_name);
                    hasher.update(env_name);
                    hasher.update(b"\0");
                    hasher.update(env_value);
                    hasher.update(b"\0");
                }
            }

            hasher.update(toml.to_string().as_bytes());
            hasher.update(b"\0");

            // TODO: Include all files referenced by the toml.
            self.digest = Some(hex::encode(hasher.finalize()));
        }
        // UNWRAP: Guaranteed by above code.
        Ok(self.digest.clone().unwrap())
    }

    pub fn resolve(mut self) -> anyhow::Result<Consolidator> {
        let toml_str = self.toml.to_string();
        trace!("Resolving for generated Cargo.toml:\n{}", toml_str);
        let merged_cargo_toml = NamedTempFile::with_str_content("Cargo.toml", &toml_str)
            .context("Writing intermediate Cargo.toml")?;

        if !self.resolver_config.additional_registries.is_empty() {
            let dot_cargo = merged_cargo_toml.path().parent().unwrap().join(".cargo");
            std::fs::create_dir(&dot_cargo)?;

            let config = CargoConfig {
                registries: self
                    .resolver_config
                    .additional_registries
                    .iter()
                    .map(|(name, index)| {
                        (
                            name.to_owned(),
                            Registry {
                                index: index.clone(),
                            },
                        )
                    })
                    .collect(),
            };
            let generated_toml = toml::to_vec(&config)?;

            std::fs::write(dot_cargo.join("config.toml"), generated_toml)?;
        }

        // RazeMetadataFetcher only uses the scheme+host+port of this URL.
        // If it used the path, we'd run into issues escaping the {s and }s from the template,
        // but the scheme+host+port should be fine.
        let repository_template_url =
            Url::parse(&self.resolver_config.default_registry_download_url_template)
                .context("Parsing repository template URL")?;

        let md_fetcher = RazeMetadataFetcher::new(
            &self.resolver_config.cargo,
            repository_template_url,
            self.resolver_config.default_registry_index_url.clone(),
        );
        let metadata = md_fetcher
            .fetch_metadata(merged_cargo_toml.path().parent().unwrap(), None, None)
            .context("Failed fetching metadata")?;

        let raze_settings = RazeSettings {
            gen_workspace_prefix: self.render_config.repo_rule_name.clone(),
            genmode: GenMode::Remote,

            // TODO: These are ?all ignored
            workspace_path: "".to_string(),
            package_aliases_dir: "".to_string(),
            render_package_aliases: false,
            target: None,
            targets: Some(self.target_triples.iter().cloned().collect()),
            crates: HashMap::default(),
            output_buildfile_suffix: "".to_string(),
            default_gen_buildrs: true,
            registry: self
                .resolver_config
                .default_registry_download_url_template
                .clone(),
            index_url: self
                .resolver_config
                .default_registry_index_url
                .as_str()
                .to_owned(),
            rust_rules_workspace_name: self.render_config.rules_rust_workspace_name.clone(),
            vendor_dir: "".to_string(),
            experimental_api: false,
            binary_deps: HashMap::new(),
        };

        let planner = BuildPlannerImpl::new(metadata, raze_settings);

        let planned_build = planner.plan_build(None).context("Failed planning build")?;

        let mut resolved_packages = planned_build.crate_contexts;
        resolved_packages
            .sort_by(|l, r| (&l.pkg_name, &l.pkg_version).cmp(&(&r.pkg_name, &r.pkg_version)));

        let member_packages_version_mapping =
            self.get_member_packages_version_mapping(merged_cargo_toml.path(), &resolved_packages);

        // TODO: generate a cargo toml from metadata in the bazel rule, when no cargo toml is present.

        let digest = self.digest().context("Digesting Resolver inputs")?;
        Ok(Consolidator::new(
            self.consolidator_config,
            self.render_config,
            digest,
            self.target_triples,
            resolved_packages,
            member_packages_version_mapping?,
            self.label_to_crates,
        ))
    }

    fn get_member_packages_version_mapping(
        &self,
        merged_cargo_toml: &Path,
        resolved_artifacts: &[CrateContext],
    ) -> anyhow::Result<Dependencies> {
        let merged_cargo_metadata = MetadataCommand::new()
            .cargo_path(&self.resolver_config.cargo)
            .current_dir(merged_cargo_toml.parent().unwrap())
            .manifest_path(merged_cargo_toml)
            .no_deps()
            .exec()
            .context("Failed to run cargo metadata")?;

        Ok(Dependencies {
            normal: Self::build_version_mapping_for_kind(
                DependencyKind::Normal,
                &merged_cargo_metadata,
                resolved_artifacts,
            ),
            build: Self::build_version_mapping_for_kind(
                DependencyKind::Build,
                &merged_cargo_metadata,
                resolved_artifacts,
            ),
            dev: Self::build_version_mapping_for_kind(
                DependencyKind::Development,
                &merged_cargo_metadata,
                resolved_artifacts,
            ),
        })
    }

    fn build_version_mapping_for_kind(
        kind: DependencyKind,
        merged_cargo_metadata: &Metadata,
        resolved_artifacts: &[CrateContext],
    ) -> BTreeMap<String, Version> {
        // Build the intersection of version requirements for all the member (i.e. toplevel) packages
        // of our workspace.
        let mut member_package_version_reqs: HashMap<String, Vec<VersionReq>> = Default::default();
        for package in &merged_cargo_metadata.packages {
            for dep in &package.dependencies {
                if dep.kind == kind {
                    let reqs = member_package_version_reqs
                        .entry(dep.name.clone())
                        .or_default();
                    if let Some(source) = &dep.source {
                        // If a git dependency is present, and doesn't specify any version requirements,
                        // treat it as something we're depending on but don't add an empty version requirement.
                        // Versions with pre-release tags do not match empty version requirements,
                        // so otherwise they will get filtered out by the version matching below,
                        // but we assume if you specifically added a git dep, you probably wanted it.
                        if source.starts_with("git+") && dep.req.comparators.is_empty() {
                            continue;
                        }
                    }
                    reqs.push(dep.req.clone());
                }
            }
        }

        let mut member_package_version_mapping = BTreeMap::new();
        for package in resolved_artifacts {
            // If the package name matches one of the member packages' direct dependencies, consider it
            // for the final version: insert it into the map if we didn't have one yet, take the highest
            // version so far if there was already one.
            if let Some(version_req) = member_package_version_reqs.get(&package.pkg_name) {
                if version_req
                    .iter()
                    .all(|req| req.matches(&package.pkg_version))
                {
                    let current_pkg_version = member_package_version_mapping
                        .entry(package.pkg_name.clone())
                        .or_insert_with(|| Version::new(0, 0, 0));
                    if *current_pkg_version < package.pkg_version {
                        *current_pkg_version = package.pkg_version.clone();
                    }
                }
            }
        }
        member_package_version_mapping
    }
}

fn get_cargo_version(cargo_path: &Path) -> anyhow::Result<Vec<u8>> {
    let output = std::process::Command::new(cargo_path)
        .arg("--version")
        .stderr(Stdio::inherit())
        .output()
        .context("Invoking cargo --version")?;
    if !output.status.success() {
        panic!(
            "TODO: cargo --version failed with exit code {:?}",
            output.status.code()
        );
    }
    Ok(output.stdout)
}

fn version_for_hashing() -> anyhow::Result<Cow<'static, [u8]>> {
    if cfg!(debug_assertions) {
        let current_exe =
            std::env::current_exe().context("Couldn't get current executable path")?;
        Ok(Cow::Owned(
            std::fs::read(current_exe).context("Couldn't read current executable path")?,
        ))
    } else {
        Ok(Cow::Borrowed(env!("CARGO_PKG_VERSION").as_bytes()))
    }
}
<|endoftext|>


<commit_before>//! FIXME: write short doc here

use std::cell::Cell;

use drop_bomb::DropBomb;

use crate::{
    event::Event,
    ParseError,
    SyntaxKind::{self, EOF, ERROR, TOMBSTONE},
    TokenSet, TokenSource, T,
};

/// `Parser` struct provides the low-level API for
/// navigating through the stream of tokens and
/// constructing the parse tree. The actual parsing
/// happens in the `grammar` module.
///
/// However, the result of this `Parser` is not a real
/// tree, but rather a flat stream of events of the form
/// "start expression, consume number literal,
/// finish expression". See `Event` docs for more.
pub(crate) struct Parser<'t> {
    token_source: &'t mut dyn TokenSource,
    events: Vec<Event>,
    steps: Cell<u32>,
}

impl<'t> Parser<'t> {
    pub(super) fn new(token_source: &'t mut dyn TokenSource) -> Parser<'t> {
        Parser { token_source, events: Vec::new(), steps: Cell::new(0) }
    }

    pub(crate) fn finish(self) -> Vec<Event> {
        self.events
    }

    /// Returns the kind of the current token.
    /// If parser has already reached the end of input,
    /// the special `EOF` kind is returned.
    pub(crate) fn current(&self) -> SyntaxKind {
        self.nth(0)
    }

    /// Lookahead operation: returns the kind of the next nth
    /// token.
    pub(crate) fn nth(&self, n: usize) -> SyntaxKind {
        assert!(n <= 3);

        let steps = self.steps.get();
        assert!(steps <= 10_000_000, "the parser seems stuck");
        self.steps.set(steps + 1);

        self.token_source.lookahead_nth(n).kind
    }

    /// Checks if the current token is `kind`.
    pub(crate) fn at(&self, kind: SyntaxKind) -> bool {
        self.nth_at(0, kind)
    }

    pub(crate) fn nth_at(&self, n: usize, kind: SyntaxKind) -> bool {
        match kind {
            T![-=] => self.at_composite2(n, T![-], T![=]),
            T![->] => self.at_composite2(n, T![-], T![>]),
            T![::] => self.at_composite2(n, T![:], T![:]),
            T![!=] => self.at_composite2(n, T![!], T![=]),
            T![..] => self.at_composite2(n, T![.], T![.]),
            T![*=] => self.at_composite2(n, T![*], T![=]),
            T![/=] => self.at_composite2(n, T![/], T![=]),
            T![&&] => self.at_composite2(n, T![&], T![&]),
            T![&=] => self.at_composite2(n, T![&], T![=]),
            T![%=] => self.at_composite2(n, T![%], T![=]),
            T![^=] => self.at_composite2(n, T![^], T![=]),
            T![+=] => self.at_composite2(n, T![+], T![=]),
            T![<<] => self.at_composite2(n, T![<], T![<]),
            T![<=] => self.at_composite2(n, T![<], T![=]),
            T![==] => self.at_composite2(n, T![=], T![=]),
            T![=>] => self.at_composite2(n, T![=], T![>]),
            T![>=] => self.at_composite2(n, T![>], T![=]),
            T![>>] => self.at_composite2(n, T![>], T![>]),
            T![|=] => self.at_composite2(n, T![|], T![=]),
            T![||] => self.at_composite2(n, T![|], T![|]),

            T![...] => self.at_composite3(n, T![.], T![.], T![.]),
            T![..=] => self.at_composite3(n, T![.], T![.], T![=]),
            T![<<=] => self.at_composite3(n, T![<], T![<], T![=]),
            T![>>=] => self.at_composite3(n, T![>], T![>], T![=]),

            _ => self.token_source.lookahead_nth(n).kind == kind,
        }
    }

    /// Consume the next token if `kind` matches.
    pub(crate) fn eat(&mut self, kind: SyntaxKind) -> bool {
        if !self.at(kind) {
            return false;
        }
        let n_raw_tokens = match kind {
            T![-=]
            | T![->]
            | T![::]
            | T![!=]
            | T![..]
            | T![*=]
            | T![/=]
            | T![&&]
            | T![&=]
            | T![%=]
            | T![^=]
            | T![+=]
            | T![<<]
            | T![<=]
            | T![==]
            | T![=>]
            | T![>=]
            | T![>>]
            | T![|=]
            | T![||] => 2,

            T![...] | T![..=] | T![<<=] | T![>>=] => 3,
            _ => 1,
        };
        self.do_bump(kind, n_raw_tokens);
        true
    }

    fn at_composite2(&self, n: usize, k1: SyntaxKind, k2: SyntaxKind) -> bool {
        let t1 = self.token_source.lookahead_nth(n);
        if t1.kind != k1 || !t1.is_jointed_to_next {
            return false;
        }
        let t2 = self.token_source.lookahead_nth(n + 1);
        t2.kind == k2
    }

    fn at_composite3(&self, n: usize, k1: SyntaxKind, k2: SyntaxKind, k3: SyntaxKind) -> bool {
        let t1 = self.token_source.lookahead_nth(n);
        if t1.kind != k1 || !t1.is_jointed_to_next {
            return false;
        }
        let t2 = self.token_source.lookahead_nth(n + 1);
        if t2.kind != k2 || !t2.is_jointed_to_next {
            return false;
        }
        let t3 = self.token_source.lookahead_nth(n + 2);
        t3.kind == k3
    }

    /// Checks if the current token is in `kinds`.
    pub(crate) fn at_ts(&self, kinds: TokenSet) -> bool {
        kinds.contains(self.current())
    }

    /// Checks if the current token is contextual keyword with text `t`.
    pub(crate) fn at_contextual_kw(&self, kw: &str) -> bool {
        self.token_source.is_keyword(kw)
    }

    /// Starts a new node in the syntax tree. All nodes and tokens
    /// consumed between the `start` and the corresponding `Marker::complete`
    /// belong to the same node.
    pub(crate) fn start(&mut self) -> Marker {
        let pos = self.events.len() as u32;
        self.push_event(Event::tombstone());
        Marker::new(pos)
    }

    /// Consume the next token if `kind` matches.
    pub(crate) fn bump(&mut self, kind: SyntaxKind) {
        assert!(self.eat(kind));
    }

    /// Advances the parser by one token
    pub(crate) fn bump_any(&mut self) {
        let kind = self.nth(0);
        if kind == EOF {
            return;
        }
        self.do_bump(kind, 1)
    }

    /// Advances the parser by one token, remapping its kind.
    /// This is useful to create contextual keywords from
    /// identifiers. For example, the lexer creates an `union`
    /// *identifier* token, but the parser remaps it to the
    /// `union` keyword, and keyword is what ends up in the
    /// final tree.
    pub(crate) fn bump_remap(&mut self, kind: SyntaxKind) {
        if self.nth(0) == EOF {
            // FIXME: panic!?
            return;
        }
        self.do_bump(kind, 1);
    }

    /// Emit error with the `message`
    /// FIXME: this should be much more fancy and support
    /// structured errors with spans and notes, like rustc
    /// does.
    pub(crate) fn error<T: Into<String>>(&mut self, message: T) {
        let msg = ParseError(Box::new(message.into()));
        self.push_event(Event::Error { msg })
    }

    /// Consume the next token if it is `kind` or emit an error
    /// otherwise.
    pub(crate) fn expect(&mut self, kind: SyntaxKind) -> bool {
        if self.eat(kind) {
            return true;
        }
        self.error(format!("expected {:?}", kind));
        false
    }

    /// Create an error node and consume the next token.
    pub(crate) fn err_and_bump(&mut self, message: &str) {
        self.err_recover(message, TokenSet::EMPTY);
    }

    /// Create an error node and consume the next token.
    pub(crate) fn err_recover(&mut self, message: &str, recovery: TokenSet) {
        match self.current() {
            T!['{'] | T!['}'] => {
                self.error(message);
                return;
            }
            _ => (),
        }

        if self.at_ts(recovery) {
            self.error(message);
            return;
        }

        let m = self.start();
        self.error(message);
        self.bump_any();
        m.complete(self, ERROR);
    }

    fn do_bump(&mut self, kind: SyntaxKind, n_raw_tokens: u8) {
        for _ in 0..n_raw_tokens {
            self.token_source.bump();
        }

        self.push_event(Event::Token { kind, n_raw_tokens });
    }

    fn push_event(&mut self, event: Event) {
        self.events.push(event)
    }
}

/// See `Parser::start`.
pub(crate) struct Marker {
    pos: u32,
    bomb: DropBomb,
}

impl Marker {
    fn new(pos: u32) -> Marker {
        Marker { pos, bomb: DropBomb::new("Marker must be either completed or abandoned") }
    }

    /// Finishes the syntax tree node and assigns `kind` to it,
    /// and mark the create a `CompletedMarker` for possible future
    /// operation like `.precede()` to deal with forward_parent.
    pub(crate) fn complete(mut self, p: &mut Parser, kind: SyntaxKind) -> CompletedMarker {
        self.bomb.defuse();
        let idx = self.pos as usize;
        match p.events[idx] {
            Event::Start { kind: ref mut slot, .. } => {
                *slot = kind;
            }
            _ => unreachable!(),
        }
        let finish_pos = p.events.len() as u32;
        p.push_event(Event::Finish);
        CompletedMarker::new(self.pos, finish_pos, kind)
    }

    /// Abandons the syntax tree node. All its children
    /// are attached to its parent instead.
    pub(crate) fn abandon(mut self, p: &mut Parser) {
        self.bomb.defuse();
        let idx = self.pos as usize;
        if idx == p.events.len() - 1 {
            match p.events.pop() {
                Some(Event::Start { kind: TOMBSTONE, forward_parent: None }) => (),
                _ => unreachable!(),
            }
        }
    }
}

pub(crate) struct CompletedMarker {
    start_pos: u32,
    finish_pos: u32,
    kind: SyntaxKind,
}

impl CompletedMarker {
    fn new(start_pos: u32, finish_pos: u32, kind: SyntaxKind) -> Self {
        CompletedMarker { start_pos, finish_pos, kind }
    }

    /// This method allows to create a new node which starts
    /// *before* the current one. That is, parser could start
    /// node `A`, then complete it, and then after parsing the
    /// whole `A`, decide that it should have started some node
    /// `B` before starting `A`. `precede` allows to do exactly
    /// that. See also docs about `forward_parent` in `Event::Start`.
    ///
    /// Given completed events `[START, FINISH]` and its corresponding
    /// `CompletedMarker(pos: 0, _)`.
    /// Append a new `START` events as `[START, FINISH, NEWSTART]`,
    /// then mark `NEWSTART` as `START`'s parent with saving its relative
    /// distance to `NEWSTART` into forward_parent(=2 in this case);
    pub(crate) fn precede(self, p: &mut Parser) -> Marker {
        let new_pos = p.start();
        let idx = self.start_pos as usize;
        match p.events[idx] {
            Event::Start { ref mut forward_parent, .. } => {
                *forward_parent = Some(new_pos.pos - self.start_pos);
            }
            _ => unreachable!(),
        }
        new_pos
    }

    /// Undo this completion and turns into a `Marker`
    pub(crate) fn undo_completion(self, p: &mut Parser) -> Marker {
        let start_idx = self.start_pos as usize;
        let finish_idx = self.finish_pos as usize;
        match p.events[start_idx] {
            Event::Start { ref mut kind, forward_parent: None } => *kind = TOMBSTONE,
            _ => unreachable!(),
        }
        match p.events[finish_idx] {
            ref mut slot @ Event::Finish => *slot = Event::tombstone(),
            _ => unreachable!(),
        }
        Marker::new(self.start_pos)
    }

    pub(crate) fn kind(&self) -> SyntaxKind {
        self.kind
    }
}
<commit_msg>Simplify<commit_after>//! FIXME: write short doc here

use std::cell::Cell;

use drop_bomb::DropBomb;

use crate::{
    event::Event,
    ParseError,
    SyntaxKind::{self, EOF, ERROR, TOMBSTONE},
    TokenSet, TokenSource, T,
};

/// `Parser` struct provides the low-level API for
/// navigating through the stream of tokens and
/// constructing the parse tree. The actual parsing
/// happens in the `grammar` module.
///
/// However, the result of this `Parser` is not a real
/// tree, but rather a flat stream of events of the form
/// "start expression, consume number literal,
/// finish expression". See `Event` docs for more.
pub(crate) struct Parser<'t> {
    token_source: &'t mut dyn TokenSource,
    events: Vec<Event>,
    steps: Cell<u32>,
}

impl<'t> Parser<'t> {
    pub(super) fn new(token_source: &'t mut dyn TokenSource) -> Parser<'t> {
        Parser { token_source, events: Vec::new(), steps: Cell::new(0) }
    }

    pub(crate) fn finish(self) -> Vec<Event> {
        self.events
    }

    /// Returns the kind of the current token.
    /// If parser has already reached the end of input,
    /// the special `EOF` kind is returned.
    pub(crate) fn current(&self) -> SyntaxKind {
        self.nth(0)
    }

    /// Lookahead operation: returns the kind of the next nth
    /// token.
    pub(crate) fn nth(&self, n: usize) -> SyntaxKind {
        assert!(n <= 3);

        let steps = self.steps.get();
        assert!(steps <= 10_000_000, "the parser seems stuck");
        self.steps.set(steps + 1);

        self.token_source.lookahead_nth(n).kind
    }

    /// Checks if the current token is `kind`.
    pub(crate) fn at(&self, kind: SyntaxKind) -> bool {
        self.nth_at(0, kind)
    }

    pub(crate) fn nth_at(&self, n: usize, kind: SyntaxKind) -> bool {
        match kind {
            T![-=] => self.at_composite2(n, T![-], T![=]),
            T![->] => self.at_composite2(n, T![-], T![>]),
            T![::] => self.at_composite2(n, T![:], T![:]),
            T![!=] => self.at_composite2(n, T![!], T![=]),
            T![..] => self.at_composite2(n, T![.], T![.]),
            T![*=] => self.at_composite2(n, T![*], T![=]),
            T![/=] => self.at_composite2(n, T![/], T![=]),
            T![&&] => self.at_composite2(n, T![&], T![&]),
            T![&=] => self.at_composite2(n, T![&], T![=]),
            T![%=] => self.at_composite2(n, T![%], T![=]),
            T![^=] => self.at_composite2(n, T![^], T![=]),
            T![+=] => self.at_composite2(n, T![+], T![=]),
            T![<<] => self.at_composite2(n, T![<], T![<]),
            T![<=] => self.at_composite2(n, T![<], T![=]),
            T![==] => self.at_composite2(n, T![=], T![=]),
            T![=>] => self.at_composite2(n, T![=], T![>]),
            T![>=] => self.at_composite2(n, T![>], T![=]),
            T![>>] => self.at_composite2(n, T![>], T![>]),
            T![|=] => self.at_composite2(n, T![|], T![=]),
            T![||] => self.at_composite2(n, T![|], T![|]),

            T![...] => self.at_composite3(n, T![.], T![.], T![.]),
            T![..=] => self.at_composite3(n, T![.], T![.], T![=]),
            T![<<=] => self.at_composite3(n, T![<], T![<], T![=]),
            T![>>=] => self.at_composite3(n, T![>], T![>], T![=]),

            _ => self.token_source.lookahead_nth(n).kind == kind,
        }
    }

    /// Consume the next token if `kind` matches.
    pub(crate) fn eat(&mut self, kind: SyntaxKind) -> bool {
        if !self.at(kind) {
            return false;
        }
        let n_raw_tokens = match kind {
            T![-=]
            | T![->]
            | T![::]
            | T![!=]
            | T![..]
            | T![*=]
            | T![/=]
            | T![&&]
            | T![&=]
            | T![%=]
            | T![^=]
            | T![+=]
            | T![<<]
            | T![<=]
            | T![==]
            | T![=>]
            | T![>=]
            | T![>>]
            | T![|=]
            | T![||] => 2,

            T![...] | T![..=] | T![<<=] | T![>>=] => 3,
            _ => 1,
        };
        self.do_bump(kind, n_raw_tokens);
        true
    }

    fn at_composite2(&self, n: usize, k1: SyntaxKind, k2: SyntaxKind) -> bool {
        let t1 = self.token_source.lookahead_nth(n);
        if t1.kind != k1 || !t1.is_jointed_to_next {
            return false;
        }
        let t2 = self.token_source.lookahead_nth(n + 1);
        t2.kind == k2
    }

    fn at_composite3(&self, n: usize, k1: SyntaxKind, k2: SyntaxKind, k3: SyntaxKind) -> bool {
        let t1 = self.token_source.lookahead_nth(n);
        if t1.kind != k1 || !t1.is_jointed_to_next {
            return false;
        }
        let t2 = self.token_source.lookahead_nth(n + 1);
        if t2.kind != k2 || !t2.is_jointed_to_next {
            return false;
        }
        let t3 = self.token_source.lookahead_nth(n + 2);
        t3.kind == k3
    }

    /// Checks if the current token is in `kinds`.
    pub(crate) fn at_ts(&self, kinds: TokenSet) -> bool {
        kinds.contains(self.current())
    }

    /// Checks if the current token is contextual keyword with text `t`.
    pub(crate) fn at_contextual_kw(&self, kw: &str) -> bool {
        self.token_source.is_keyword(kw)
    }

    /// Starts a new node in the syntax tree. All nodes and tokens
    /// consumed between the `start` and the corresponding `Marker::complete`
    /// belong to the same node.
    pub(crate) fn start(&mut self) -> Marker {
        let pos = self.events.len() as u32;
        self.push_event(Event::tombstone());
        Marker::new(pos)
    }

    /// Consume the next token if `kind` matches.
    pub(crate) fn bump(&mut self, kind: SyntaxKind) {
        assert!(self.eat(kind));
    }

    /// Advances the parser by one token
    pub(crate) fn bump_any(&mut self) {
        let kind = self.nth(0);
        if kind == EOF {
            return;
        }
        self.do_bump(kind, 1)
    }

    /// Advances the parser by one token, remapping its kind.
    /// This is useful to create contextual keywords from
    /// identifiers. For example, the lexer creates an `union`
    /// *identifier* token, but the parser remaps it to the
    /// `union` keyword, and keyword is what ends up in the
    /// final tree.
    pub(crate) fn bump_remap(&mut self, kind: SyntaxKind) {
        if self.nth(0) == EOF {
            // FIXME: panic!?
            return;
        }
        self.do_bump(kind, 1);
    }

    /// Emit error with the `message`
    /// FIXME: this should be much more fancy and support
    /// structured errors with spans and notes, like rustc
    /// does.
    pub(crate) fn error<T: Into<String>>(&mut self, message: T) {
        let msg = ParseError(Box::new(message.into()));
        self.push_event(Event::Error { msg })
    }

    /// Consume the next token if it is `kind` or emit an error
    /// otherwise.
    pub(crate) fn expect(&mut self, kind: SyntaxKind) -> bool {
        if self.eat(kind) {
            return true;
        }
        self.error(format!("expected {:?}", kind));
        false
    }

    /// Create an error node and consume the next token.
    pub(crate) fn err_and_bump(&mut self, message: &str) {
        self.err_recover(message, TokenSet::EMPTY);
    }

    /// Create an error node and consume the next token.
    pub(crate) fn err_recover(&mut self, message: &str, recovery: TokenSet) {
        match self.current() {
            T!['{'] | T!['}'] => {
                self.error(message);
                return;
            }
            _ => (),
        }

        if self.at_ts(recovery) {
            self.error(message);
            return;
        }

        let m = self.start();
        self.error(message);
        self.bump_any();
        m.complete(self, ERROR);
    }

    fn do_bump(&mut self, kind: SyntaxKind, n_raw_tokens: u8) {
        for _ in 0..n_raw_tokens {
            self.token_source.bump();
        }

        self.push_event(Event::Token { kind, n_raw_tokens });
    }

    fn push_event(&mut self, event: Event) {
        self.events.push(event)
    }
}

/// See `Parser::start`.
pub(crate) struct Marker {
    pos: u32,
    bomb: DropBomb,
}

impl Marker {
    fn new(pos: u32) -> Marker {
        Marker { pos, bomb: DropBomb::new("Marker must be either completed or abandoned") }
    }

    /// Finishes the syntax tree node and assigns `kind` to it,
    /// and mark the create a `CompletedMarker` for possible future
    /// operation like `.precede()` to deal with forward_parent.
    pub(crate) fn complete(mut self, p: &mut Parser, kind: SyntaxKind) -> CompletedMarker {
        self.bomb.defuse();
        let idx = self.pos as usize;
        match &mut p.events[idx] {
            Event::Start { kind: slot, .. } => {
                *slot = kind;
            }
            _ => unreachable!(),
        }
        let finish_pos = p.events.len() as u32;
        p.push_event(Event::Finish);
        CompletedMarker::new(self.pos, finish_pos, kind)
    }

    /// Abandons the syntax tree node. All its children
    /// are attached to its parent instead.
    pub(crate) fn abandon(mut self, p: &mut Parser) {
        self.bomb.defuse();
        let idx = self.pos as usize;
        if idx == p.events.len() - 1 {
            match p.events.pop() {
                Some(Event::Start { kind: TOMBSTONE, forward_parent: None }) => (),
                _ => unreachable!(),
            }
        }
    }
}

pub(crate) struct CompletedMarker {
    start_pos: u32,
    finish_pos: u32,
    kind: SyntaxKind,
}

impl CompletedMarker {
    fn new(start_pos: u32, finish_pos: u32, kind: SyntaxKind) -> Self {
        CompletedMarker { start_pos, finish_pos, kind }
    }

    /// This method allows to create a new node which starts
    /// *before* the current one. That is, parser could start
    /// node `A`, then complete it, and then after parsing the
    /// whole `A`, decide that it should have started some node
    /// `B` before starting `A`. `precede` allows to do exactly
    /// that. See also docs about `forward_parent` in `Event::Start`.
    ///
    /// Given completed events `[START, FINISH]` and its corresponding
    /// `CompletedMarker(pos: 0, _)`.
    /// Append a new `START` events as `[START, FINISH, NEWSTART]`,
    /// then mark `NEWSTART` as `START`'s parent with saving its relative
    /// distance to `NEWSTART` into forward_parent(=2 in this case);
    pub(crate) fn precede(self, p: &mut Parser) -> Marker {
        let new_pos = p.start();
        let idx = self.start_pos as usize;
        match &mut p.events[idx] {
            Event::Start { forward_parent, .. } => {
                *forward_parent = Some(new_pos.pos - self.start_pos);
            }
            _ => unreachable!(),
        }
        new_pos
    }

    /// Undo this completion and turns into a `Marker`
    pub(crate) fn undo_completion(self, p: &mut Parser) -> Marker {
        let start_idx = self.start_pos as usize;
        let finish_idx = self.finish_pos as usize;
        match &mut p.events[start_idx] {
            Event::Start { kind, forward_parent: None } => *kind = TOMBSTONE,
            _ => unreachable!(),
        }
        match &mut p.events[finish_idx] {
            slot @ Event::Finish => *slot = Event::tombstone(),
            _ => unreachable!(),
        }
        Marker::new(self.start_pos)
    }

    pub(crate) fn kind(&self) -> SyntaxKind {
        self.kind
    }
}
<|endoftext|>