<commit_before>extern crate glib;
extern crate cairo;
extern crate libc;

use self::glib::translate::*;

use std::cell::Cell;
use std::ptr;

use aspect_ratio::*;
use drawing_ctx::RsvgDrawingCtx;
use drawing_ctx;
use handle::RsvgHandle;
use length::*;
use node::*;
use property_bag;
use property_bag::*;
use util::*;
use viewbox::*;

use self::cairo::MatrixTrait;

/***** NodeGroup *****/

struct NodeGroup ();

impl NodeGroup {
    fn new () -> NodeGroup {
        NodeGroup ()
    }
}

impl NodeTrait for NodeGroup {
    fn set_atts (&self, _: &RsvgNode, _: *const RsvgHandle, _: *const RsvgPropertyBag) -> NodeResult {
        Ok (())
    }

    fn draw (&self, node: &RsvgNode, draw_ctx: *const RsvgDrawingCtx, dominate: i32) {
        node.draw_children (draw_ctx, dominate);
    }

    fn get_c_impl (&self) -> *const RsvgCNodeImpl {
        unreachable! ();
    }
}

/***** NodeDefs *****/

struct NodeDefs ();

impl NodeDefs {
    fn new () -> NodeDefs {
        NodeDefs ()
    }
}

impl NodeTrait for NodeDefs {
    fn set_atts (&self, _: &RsvgNode, _: *const RsvgHandle, _: *const RsvgPropertyBag) -> NodeResult {
        Ok (())
    }

    fn draw (&self, _: &RsvgNode, _: *const RsvgDrawingCtx, _: i32) {
        // nothing
    }

    fn get_c_impl (&self) -> *const RsvgCNodeImpl {
        unreachable! ();
    }
}

/***** NodeSwitch *****/

struct NodeSwitch ();

impl NodeSwitch {
    fn new () -> NodeSwitch {
        NodeSwitch ()
    }
}

impl NodeTrait for NodeSwitch {
    fn set_atts (&self, _: &RsvgNode, _: *const RsvgHandle, _: *const RsvgPropertyBag) -> NodeResult {
        Ok (())
    }

    fn draw (&self, node: &RsvgNode, draw_ctx: *const RsvgDrawingCtx, dominate: i32) {
        drawing_ctx::state_reinherit_top (draw_ctx, node.get_state (), dominate);

        drawing_ctx::push_discrete_layer (draw_ctx);

        for child in &*node.children.borrow () {
            if drawing_ctx::state_get_cond_true (child.get_state ()) {
                let boxed_child = box_node (child.clone ());

                drawing_ctx::draw_node_from_stack (draw_ctx, boxed_child, 0);

                rsvg_node_unref (boxed_child);

                break;
            }
        }

        drawing_ctx::pop_discrete_layer (draw_ctx);
    }

    fn get_c_impl (&self) -> *const RsvgCNodeImpl {
        unreachable! ();
    }
}

/***** NodeSvg *****/

struct NodeSvg {
    preserve_aspect_ratio: Cell<AspectRatio>,
    x:                     Cell<RsvgLength>,
    y:                     Cell<RsvgLength>,
    w:                     Cell<RsvgLength>,
    h:                     Cell<RsvgLength>,
    vbox:                  Cell<RsvgViewBox>,
    atts:                  Cell<*mut RsvgPropertyBag>
}

impl NodeSvg {
    fn new () -> NodeSvg {
        NodeSvg {
            preserve_aspect_ratio: Cell::new (AspectRatio::default ()),
            x:                     Cell::new (RsvgLength::parse ("0", LengthDir::Horizontal).unwrap ()),
            y:                     Cell::new (RsvgLength::parse ("0", LengthDir::Vertical).unwrap ()),
            w:                     Cell::new (RsvgLength::parse ("100%", LengthDir::Horizontal).unwrap ()),
            h:                     Cell::new (RsvgLength::parse ("100%", LengthDir::Vertical).unwrap ()),
            vbox:                  Cell::new (RsvgViewBox::default ()),
            atts:                  Cell::new (ptr::null_mut ())
        }
    }
}

impl NodeTrait for NodeSvg {
    fn set_atts (&self, node: &RsvgNode, _: *const RsvgHandle, pbag: *const RsvgPropertyBag) -> NodeResult {
        self.preserve_aspect_ratio.set (property_bag::parse_or_default (pbag, "preserveAspectRatio")?);

        // x & y attributes have no effect on outermost svg
        // http://www.w3.org/TR/SVG/struct.html#SVGElement
        if node.get_parent ().is_some () {
            self.x.set (property_bag::length_or_default (pbag, "x", LengthDir::Horizontal)?);
            self.y.set (property_bag::length_or_default (pbag, "y", LengthDir::Vertical)?);
        }

        self.w.set (property_bag::length_or_value (pbag, "width", LengthDir::Horizontal, "100%")?);
        self.h.set (property_bag::length_or_value (pbag, "height", LengthDir::Vertical, "100%")?);

        self.vbox.set (property_bag::parse_or_default (pbag, "viewBox")?);

        // The "style" sub-element is not loaded yet here, so we need
        // to store other attributes to be applied later.
        self.atts.set (property_bag::dup (pbag));

        Ok (())
    }

    fn draw (&self, node: &RsvgNode, draw_ctx: *const RsvgDrawingCtx, dominate: i32) {
        let nx = self.x.get ().normalize (draw_ctx);
        let ny = self.y.get ().normalize (draw_ctx);
        let nw = self.w.get ().normalize (draw_ctx);
        let nh = self.h.get ().normalize (draw_ctx);

        drawing_ctx::state_reinherit_top (draw_ctx, node.get_state (), dominate);

        let state = drawing_ctx::get_current_state (draw_ctx);

        let affine_old = drawing_ctx::get_current_state_affine (draw_ctx);

        let vbox = self.vbox.get ();

        if vbox.active {
            // viewBox width==0 or height==0 disables rendering of the element
            // https://www.w3.org/TR/SVG/coords.html#ViewBoxAttribute
            if double_equals (vbox.rect.width, 0.0) || double_equals (vbox.rect.height, 0.0) {
                return;
            }

            let (x, y, w, h) = self.preserve_aspect_ratio.get ().compute (vbox.rect.width, vbox.rect.height,
                                                                          nx, ny, nw, nh);

            let mut affine = affine_old;
            affine.translate (x, y);
            affine.scale (w / vbox.rect.width, h / vbox.rect.height);
            affine.translate (-vbox.rect.x, -vbox.rect.y);
            drawing_ctx::set_current_state_affine (draw_ctx, affine);

            drawing_ctx::push_view_box (draw_ctx, vbox.rect.width, vbox.rect.height);
        } else {
            let mut affine = affine_old;
            affine.translate (nx, ny);

            drawing_ctx::set_current_state_affine (draw_ctx, affine);
            drawing_ctx::push_view_box (draw_ctx, nw, nh);
        }

        let affine_new = drawing_ctx::get_current_state_affine (draw_ctx);

        drawing_ctx::push_discrete_layer (draw_ctx);

        // Bounding box addition must be AFTER the discrete layer
        // push, which must be AFTER the transformation happens.

        if !drawing_ctx::state_is_overflow (state) && node.get_parent ().is_some () {
            drawing_ctx::set_current_state_affine (draw_ctx, affine_old);
            drawing_ctx::add_clipping_rect (draw_ctx, nx, ny, nw, nh);
            drawing_ctx::set_current_state_affine (draw_ctx, affine_new);
        }

        node.draw_children (draw_ctx, -1); // dominate==-1 so it won't reinherit or push a layer

        drawing_ctx::pop_discrete_layer (draw_ctx);
        drawing_ctx::pop_view_box (draw_ctx);
    }

    fn get_c_impl (&self) -> *const RsvgCNodeImpl {
        unreachable! ();
    }
}

impl Drop for NodeSvg {
    fn drop (&mut self) {
        let pbag = self.atts.get ();

        if !pbag.is_null () {
            property_bag::free (pbag);
        }
    }
}

/***** C Prototypes *****/

#[no_mangle]
pub extern fn rsvg_node_group_new (_: *const libc::c_char, raw_parent: *const RsvgNode) -> *const RsvgNode {
    boxed_node_new (NodeType::Group,
                    raw_parent,
                    Box::new (NodeGroup::new ()))
}

#[no_mangle]
pub extern fn rsvg_node_defs_new (_: *const libc::c_char, raw_parent: *const RsvgNode) -> *const RsvgNode {
    boxed_node_new (NodeType::Defs,
                    raw_parent,
                    Box::new (NodeDefs::new ()))
}

#[no_mangle]
pub extern fn rsvg_node_switch_new (_: *const libc::c_char, raw_parent: *const RsvgNode) -> *const RsvgNode {
    boxed_node_new (NodeType::Switch,
                    raw_parent,
                    Box::new (NodeSwitch::new ()))
}

#[no_mangle]
pub extern fn rsvg_node_svg_new (_: *const libc::c_char, raw_parent: *const RsvgNode) -> *const RsvgNode {
    boxed_node_new (NodeType::Svg,
                    raw_parent,
                    Box::new (NodeSvg::new ()))
}

#[no_mangle]
pub extern fn rsvg_node_svg_get_size (raw_node: *const RsvgNode, out_width: *mut RsvgLength, out_height: *mut RsvgLength) {
    assert! (!raw_node.is_null ());
    let node: &RsvgNode = unsafe { & *raw_node };

    assert! (!out_width.is_null ());
    assert! (!out_height.is_null ());

    node.with_impl (|svg: &NodeSvg| {
        unsafe {
            *out_width  = svg.w.get ();
            *out_height = svg.h.get ();
        }
    });
}

#[no_mangle]
pub extern fn rsvg_node_svg_get_view_box (raw_node: *const RsvgNode) -> RsvgViewBox {
    assert! (!raw_node.is_null ());
    let node: &RsvgNode = unsafe { & *raw_node };

    let mut vbox = RsvgViewBox::default ();

    node.with_impl (|svg: &NodeSvg| {
        vbox = svg.vbox.get ();
    });

    vbox
}

extern "C" {
    fn rsvg_parse_style_attrs (handle: *const RsvgHandle,
                               node:   *const RsvgNode,
                               tag:    *const libc::c_char,
                               class:  *const libc::c_char,
                               id:     *const libc::c_char,
                               pbag:   *const RsvgPropertyBag);
}

#[no_mangle]
pub extern fn rsvg_node_svg_apply_atts (raw_node: *const RsvgNode, handle: *const RsvgHandle) {
    assert! (!raw_node.is_null ());
    let node: &RsvgNode = unsafe { & *raw_node };

    node.with_impl (|svg: &NodeSvg| {
        let pbag = svg.atts.get ();

        if !pbag.is_null () {
            let class = property_bag::lookup (pbag, "class");
            let id = property_bag::lookup (pbag, "id");

            unsafe { rsvg_parse_style_attrs (handle,
                                             raw_node,
                                             str::to_glib_none ("svg").0,
                                             class.map_or (ptr::null (), |s| String::to_glib_none (&s).0),
                                             id.map_or (ptr::null (), |s| String::to_glib_none (&s).0),
                                             pbag); }
        }
    });
}
<commit_msg>NodeSvg: validate the width and height attributes<commit_after>extern crate glib;
extern crate cairo;
extern crate libc;

use self::glib::translate::*;

use std::cell::Cell;
use std::ptr;

use aspect_ratio::*;
use drawing_ctx::RsvgDrawingCtx;
use drawing_ctx;
use error::*;
use handle::RsvgHandle;
use length::*;
use node::*;
use property_bag;
use property_bag::*;
use util::*;
use viewbox::*;

use self::cairo::MatrixTrait;

/***** NodeGroup *****/

struct NodeGroup ();

impl NodeGroup {
    fn new () -> NodeGroup {
        NodeGroup ()
    }
}

impl NodeTrait for NodeGroup {
    fn set_atts (&self, _: &RsvgNode, _: *const RsvgHandle, _: *const RsvgPropertyBag) -> NodeResult {
        Ok (())
    }

    fn draw (&self, node: &RsvgNode, draw_ctx: *const RsvgDrawingCtx, dominate: i32) {
        node.draw_children (draw_ctx, dominate);
    }

    fn get_c_impl (&self) -> *const RsvgCNodeImpl {
        unreachable! ();
    }
}

/***** NodeDefs *****/

struct NodeDefs ();

impl NodeDefs {
    fn new () -> NodeDefs {
        NodeDefs ()
    }
}

impl NodeTrait for NodeDefs {
    fn set_atts (&self, _: &RsvgNode, _: *const RsvgHandle, _: *const RsvgPropertyBag) -> NodeResult {
        Ok (())
    }

    fn draw (&self, _: &RsvgNode, _: *const RsvgDrawingCtx, _: i32) {
        // nothing
    }

    fn get_c_impl (&self) -> *const RsvgCNodeImpl {
        unreachable! ();
    }
}

/***** NodeSwitch *****/

struct NodeSwitch ();

impl NodeSwitch {
    fn new () -> NodeSwitch {
        NodeSwitch ()
    }
}

impl NodeTrait for NodeSwitch {
    fn set_atts (&self, _: &RsvgNode, _: *const RsvgHandle, _: *const RsvgPropertyBag) -> NodeResult {
        Ok (())
    }

    fn draw (&self, node: &RsvgNode, draw_ctx: *const RsvgDrawingCtx, dominate: i32) {
        drawing_ctx::state_reinherit_top (draw_ctx, node.get_state (), dominate);

        drawing_ctx::push_discrete_layer (draw_ctx);

        for child in &*node.children.borrow () {
            if drawing_ctx::state_get_cond_true (child.get_state ()) {
                let boxed_child = box_node (child.clone ());

                drawing_ctx::draw_node_from_stack (draw_ctx, boxed_child, 0);

                rsvg_node_unref (boxed_child);

                break;
            }
        }

        drawing_ctx::pop_discrete_layer (draw_ctx);
    }

    fn get_c_impl (&self) -> *const RsvgCNodeImpl {
        unreachable! ();
    }
}

/***** NodeSvg *****/

struct NodeSvg {
    preserve_aspect_ratio: Cell<AspectRatio>,
    x:                     Cell<RsvgLength>,
    y:                     Cell<RsvgLength>,
    w:                     Cell<RsvgLength>,
    h:                     Cell<RsvgLength>,
    vbox:                  Cell<RsvgViewBox>,
    atts:                  Cell<*mut RsvgPropertyBag>
}

impl NodeSvg {
    fn new () -> NodeSvg {
        NodeSvg {
            preserve_aspect_ratio: Cell::new (AspectRatio::default ()),
            x:                     Cell::new (RsvgLength::parse ("0", LengthDir::Horizontal).unwrap ()),
            y:                     Cell::new (RsvgLength::parse ("0", LengthDir::Vertical).unwrap ()),
            w:                     Cell::new (RsvgLength::parse ("100%", LengthDir::Horizontal).unwrap ()),
            h:                     Cell::new (RsvgLength::parse ("100%", LengthDir::Vertical).unwrap ()),
            vbox:                  Cell::new (RsvgViewBox::default ()),
            atts:                  Cell::new (ptr::null_mut ())
        }
    }
}

fn length_is_negative (length: &RsvgLength) -> bool {
    // This is more or less a hack.  We don't care about a correct
    // normalization; we just need to know if it would be negative.
    // So, we pass bogus values just to be able to normalize.
    length.hand_normalize (1.0, 1.0, 1.0) < 0.0
}

impl NodeTrait for NodeSvg {
    fn set_atts (&self, node: &RsvgNode, _: *const RsvgHandle, pbag: *const RsvgPropertyBag) -> NodeResult {
        self.preserve_aspect_ratio.set (property_bag::parse_or_default (pbag, "preserveAspectRatio")?);

        // x & y attributes have no effect on outermost svg
        // http://www.w3.org/TR/SVG/struct.html#SVGElement
        if node.get_parent ().is_some () {
            self.x.set (property_bag::length_or_default (pbag, "x", LengthDir::Horizontal)?);
            self.y.set (property_bag::length_or_default (pbag, "y", LengthDir::Vertical)?);
        }

        let w = property_bag::length_or_value (pbag, "width", LengthDir::Horizontal, "100%")?;
        if length_is_negative (&w) {
            return Err (NodeError::value_error ("width", "Must not be negative"));
        } else {
            self.w.set (w);
        }

        let h = property_bag::length_or_value (pbag, "height", LengthDir::Vertical, "100%")?;
        if length_is_negative (&h) {
            return Err (NodeError::value_error ("height", "Must not be negative"));
        } else {
            self.h.set (h);
        }

        self.vbox.set (property_bag::parse_or_default (pbag, "viewBox")?);

        // The "style" sub-element is not loaded yet here, so we need
        // to store other attributes to be applied later.
        self.atts.set (property_bag::dup (pbag));

        Ok (())
    }

    fn draw (&self, node: &RsvgNode, draw_ctx: *const RsvgDrawingCtx, dominate: i32) {
        let nx = self.x.get ().normalize (draw_ctx);
        let ny = self.y.get ().normalize (draw_ctx);
        let nw = self.w.get ().normalize (draw_ctx);
        let nh = self.h.get ().normalize (draw_ctx);

        // width or height set to 0 disables rendering of the element
        // https://www.w3.org/TR/SVG/struct.html#SVGElementWidthAttribute
        if double_equals (nw, 0.0) || double_equals (nh, 0.0) {
            return;
        }

        drawing_ctx::state_reinherit_top (draw_ctx, node.get_state (), dominate);

        let state = drawing_ctx::get_current_state (draw_ctx);

        let affine_old = drawing_ctx::get_current_state_affine (draw_ctx);

        let vbox = self.vbox.get ();

        if vbox.active {
            // viewBox width==0 or height==0 disables rendering of the element
            // https://www.w3.org/TR/SVG/coords.html#ViewBoxAttribute
            if double_equals (vbox.rect.width, 0.0) || double_equals (vbox.rect.height, 0.0) {
                return;
            }

            let (x, y, w, h) = self.preserve_aspect_ratio.get ().compute (vbox.rect.width, vbox.rect.height,
                                                                          nx, ny, nw, nh);

            let mut affine = affine_old;
            affine.translate (x, y);
            affine.scale (w / vbox.rect.width, h / vbox.rect.height);
            affine.translate (-vbox.rect.x, -vbox.rect.y);
            drawing_ctx::set_current_state_affine (draw_ctx, affine);

            drawing_ctx::push_view_box (draw_ctx, vbox.rect.width, vbox.rect.height);
        } else {
            let mut affine = affine_old;
            affine.translate (nx, ny);

            drawing_ctx::set_current_state_affine (draw_ctx, affine);
            drawing_ctx::push_view_box (draw_ctx, nw, nh);
        }

        let affine_new = drawing_ctx::get_current_state_affine (draw_ctx);

        drawing_ctx::push_discrete_layer (draw_ctx);

        // Bounding box addition must be AFTER the discrete layer
        // push, which must be AFTER the transformation happens.

        if !drawing_ctx::state_is_overflow (state) && node.get_parent ().is_some () {
            drawing_ctx::set_current_state_affine (draw_ctx, affine_old);
            drawing_ctx::add_clipping_rect (draw_ctx, nx, ny, nw, nh);
            drawing_ctx::set_current_state_affine (draw_ctx, affine_new);
        }

        node.draw_children (draw_ctx, -1); // dominate==-1 so it won't reinherit or push a layer

        drawing_ctx::pop_discrete_layer (draw_ctx);
        drawing_ctx::pop_view_box (draw_ctx);
    }

    fn get_c_impl (&self) -> *const RsvgCNodeImpl {
        unreachable! ();
    }
}

impl Drop for NodeSvg {
    fn drop (&mut self) {
        let pbag = self.atts.get ();

        if !pbag.is_null () {
            property_bag::free (pbag);
        }
    }
}

/***** C Prototypes *****/

#[no_mangle]
pub extern fn rsvg_node_group_new (_: *const libc::c_char, raw_parent: *const RsvgNode) -> *const RsvgNode {
    boxed_node_new (NodeType::Group,
                    raw_parent,
                    Box::new (NodeGroup::new ()))
}

#[no_mangle]
pub extern fn rsvg_node_defs_new (_: *const libc::c_char, raw_parent: *const RsvgNode) -> *const RsvgNode {
    boxed_node_new (NodeType::Defs,
                    raw_parent,
                    Box::new (NodeDefs::new ()))
}

#[no_mangle]
pub extern fn rsvg_node_switch_new (_: *const libc::c_char, raw_parent: *const RsvgNode) -> *const RsvgNode {
    boxed_node_new (NodeType::Switch,
                    raw_parent,
                    Box::new (NodeSwitch::new ()))
}

#[no_mangle]
pub extern fn rsvg_node_svg_new (_: *const libc::c_char, raw_parent: *const RsvgNode) -> *const RsvgNode {
    boxed_node_new (NodeType::Svg,
                    raw_parent,
                    Box::new (NodeSvg::new ()))
}

#[no_mangle]
pub extern fn rsvg_node_svg_get_size (raw_node: *const RsvgNode, out_width: *mut RsvgLength, out_height: *mut RsvgLength) {
    assert! (!raw_node.is_null ());
    let node: &RsvgNode = unsafe { & *raw_node };

    assert! (!out_width.is_null ());
    assert! (!out_height.is_null ());

    node.with_impl (|svg: &NodeSvg| {
        unsafe {
            *out_width  = svg.w.get ();
            *out_height = svg.h.get ();
        }
    });
}

#[no_mangle]
pub extern fn rsvg_node_svg_get_view_box (raw_node: *const RsvgNode) -> RsvgViewBox {
    assert! (!raw_node.is_null ());
    let node: &RsvgNode = unsafe { & *raw_node };

    let mut vbox = RsvgViewBox::default ();

    node.with_impl (|svg: &NodeSvg| {
        vbox = svg.vbox.get ();
    });

    vbox
}

extern "C" {
    fn rsvg_parse_style_attrs (handle: *const RsvgHandle,
                               node:   *const RsvgNode,
                               tag:    *const libc::c_char,
                               class:  *const libc::c_char,
                               id:     *const libc::c_char,
                               pbag:   *const RsvgPropertyBag);
}

#[no_mangle]
pub extern fn rsvg_node_svg_apply_atts (raw_node: *const RsvgNode, handle: *const RsvgHandle) {
    assert! (!raw_node.is_null ());
    let node: &RsvgNode = unsafe { & *raw_node };

    node.with_impl (|svg: &NodeSvg| {
        let pbag = svg.atts.get ();

        if !pbag.is_null () {
            let class = property_bag::lookup (pbag, "class");
            let id = property_bag::lookup (pbag, "id");

            unsafe { rsvg_parse_style_attrs (handle,
                                             raw_node,
                                             str::to_glib_none ("svg").0,
                                             class.map_or (ptr::null (), |s| String::to_glib_none (&s).0),
                                             id.map_or (ptr::null (), |s| String::to_glib_none (&s).0),
                                             pbag); }
        }
    });
}
<|endoftext|>


<commit_before>#![feature(try_trait)]
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      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.

// See LICENSE for licensing information.
//
// This build.rs script tries to generate low-level rust bindings for the current ICU library.
// Please refer to README.md for instructions on how to build the library for
// your use.

use {
    anyhow::{Context, Result},
    std::env,
    std::fs::File,
    std::io::Write,
    std::path::Path,
    std::process::Command,
};

// Captures the stdout of the command or panics.
fn stdout(c: &mut Command) -> Result<String> {
    let output = c
        .output()
        .with_context(|| format!("could not execute command: {}", 1))?;
    let result = String::from_utf8(output.stdout)
        .with_context(|| format!("could not convert output to UTF8"))?;
    Ok(result.trim().to_string())
}

fn icu_config_cmd() -> Command {
    // TODO: looks like .args() will persist on a command for some reason.
    Command::new("icu-config")
}

fn run(cmd: &mut Command, args: &[&str]) -> Result<String> {
    Ok(String::from(&stdout(cmd.args(args))?).trim().to_string())
}

fn icu_config_prefix() -> Result<String> {
    run(&mut icu_config_cmd(), &["--prefix"])
        .with_context(|| format!("could not get config prefix"))
}

fn lib_dir() -> Result<String> {
    run(&mut icu_config_cmd(), &["--libdir"])
        .with_context(|| format!("could not get library directory"))
}

fn ld_flags() -> Result<String> {
    // Replacements needed because of https://github.com/rust-lang/cargo/issues/7217
    let result = run(&mut icu_config_cmd(), &["--ldflags"])
        .with_context(|| format!("could not get the ld flags"))?;
    Ok(result.replace("-L", "-L ").replace("-l", "-l "))
}

/// Returns the C preprocessor flags used to build ICU
fn icu_cpp_flags() -> Result<String> {
    run(&mut icu_config_cmd(), &["--cppflags"])
        .with_context(|| format!("while getting the cpp flags"))
}

/// Returns true if the ICU library was compiled with renaming enabled.
fn has_renaming() -> Result<bool> {
    let cpp_flags = icu_cpp_flags()?;
    let found = cpp_flags.find("-DU_DISABLE_RENAMING=1");
    println!("flags: {}", cpp_flags);
    Ok(found.is_none())
}

fn icu_config_version() -> Result<String> {
    run(&mut icu_config_cmd(), &["--version"])
        .with_context(|| format!("while getting ICU version; is icu-config in $PATH?"))
}

fn install_dir() -> Result<String> {
    run(&mut icu_config_cmd(), &["--prefix"])
        .with_context(|| format!("while getting directory prefix"))
}

fn rustfmt_cmd() -> Command {
    Command::new("rustfmt")
}

fn rustfmt_version() -> Result<String> {
    run(&mut rustfmt_cmd(), &["--version"])
        .with_context(|| format!("while getting rustfmt version; is rustfmt in $PATH?"))
}

fn bindgen_cmd() -> Command {
    Command::new("bindgen")
}

fn bindgen_version() -> Result<String> {
    run(&mut bindgen_cmd(), &["--version"])
        .with_context(|| format!("while getting bindgen version; is bindgen in $PATH?"))
}

// Returns the config major number.  For example, will return "64" for
// version "64.2"
fn icu_config_major_number() -> Result<String> {
    let version = icu_config_version()?;
    let components = version.split(".");
    let last = components
        .take(1)
        .last()
        .with_context(|| format!("could not parse version number: {}", version))?;
    Ok(last.to_string())
}

/// Generates an additional include file which contains the linker directives.
/// This is done because cargo does not allow the rustc link directives to be
/// anything other than `-L` and `-l`.
fn generate_linker_file(out_dir_path: &Path, lib_dir: &str, lib_names: &Vec<&str>) {
    let file_path = out_dir_path.join("link.rs");
    let mut linker_file = File::create(&file_path).unwrap();
    let mut content: Vec<String> = vec![];
    for lib in lib_names {
        let linkopt: String = format!(r#"#[link_args="-Wl,-rpath={}/lib{}.so"]"#, lib_dir, lib);
        content.push(linkopt);
    }
    content.push(String::from(r#"extern "C" {}"#));
    linker_file
        .write_all(&content.join("\n").into_bytes())
        .expect("successful write into linker file");
}

/// Generates a wrapper header that includes all headers of interest for binding.
///
/// This is the recommended way to bind complex libraries at the moment.  Returns
/// the full path of the generated wrapper header file.
fn generate_wrapper_header(
    out_dir_path: &Path,
    bindgen_source_modules: &Vec<&str>,
    include_path: &Path,
) -> String {
    let wrapper_path = out_dir_path.join("wrapper.h");
    let mut wrapper_file = File::create(&wrapper_path).unwrap();
    wrapper_file
        .write_all(b"/* Generated file, do not edit. */ \n")
        .unwrap();
    let includes = bindgen_source_modules
        .iter()
        .map(|f| {
            let file_path = include_path.join(format!("{}.h", f));
            let file_path_str = format!("#include \"{}\"\n", file_path.to_str().unwrap());
            println!("include-file: '{}'", file_path.to_str().unwrap());
            file_path_str
        })
        .collect::<String>();
    wrapper_file.write_all(&includes.into_bytes()).unwrap();
    String::from(wrapper_path.to_str().unwrap())
}

fn commaify(s: &Vec<&str>) -> String {
    format!("{}", s.join("|"))
}

fn run_bindgen(header_file: &str, out_dir_path: &Path) -> Result<()> {
    let whitelist_types_regexes = commaify(&vec![
        "UBool",
        "UCalendar.*",
        "UChar.*",
        "UData.*",
        "UDate",
        "UDateFormat.*",
        "UEnumeration.*",
        "UErrorCode",
        "UText.*",
    ]);

    let whitelist_functions_regexes = commaify(&vec![
        "u_.*", "ucal_.*", "udata_*", "udat_.*", "uenum_.*", "uloc_.*", "utext_.*",
    ]);

    let opaque_types_regexes = commaify(&vec![]);

    // Common arguments for all bindgen invocations.
    let bindgen_generate_args = vec![
        "--default-enum-style=rust",
        "--no-doc-comments",
        "--with-derive-default",
        "--with-derive-hash",
        "--with-derive-partialord",
        "--with-derive-partialeq",
        "--whitelist-type",
        &whitelist_types_regexes,
        "--whitelist-function",
        &whitelist_functions_regexes,
        "--opaque-type",
        &opaque_types_regexes,
    ];

    let output_file_path = out_dir_path.join("lib.rs");
    let output_file = output_file_path.to_str().unwrap();
    let ld_flags = ld_flags()?;
    let cpp_flags = icu_cpp_flags()?;
    let mut file_args = vec![
        "-o",
        &output_file,
        &header_file,
        "--",
        &ld_flags,
        &cpp_flags,
    ];
    if !has_renaming()? {
        file_args.push("-DU_DISABLE_RENAMING=1");
    }
    let all_args = [&bindgen_generate_args[..], &file_args[..]].concat();
    println!("bindgen-cmdline: {:?}", all_args);
    Command::new("bindgen")
        .args(&all_args)
        .spawn()
        .with_context(|| format!("while running bindgen"))?;
    Ok(())
}

// Generates the library renaming macro: this allows us to use renamed function
// names in the resulting low-level bindings library.
fn run_renamegen(out_dir_path: &Path) -> Result<()> {
    let output_file_path = out_dir_path.join("macros.rs");
    let mut macro_file = File::create(&output_file_path)
        .with_context(|| format!("while opening {:?}", output_file_path))?;
    if has_renaming()? {
        println!("renaming: true");
        // The library names have been renamed, need to generate a macro that
        // converts the call of `foo()` into `foo_64()`.
        let icu_major_version = icu_config_major_number()?;
        let to_write = format!(
            r#"
// Macros for changing function names.

extern crate paste;

// This library was build with version renaming, so rewrite every function name
// with its name with version number appended.

// The macro below will rename a symbol `foo::bar` to `foo::bar_64` (where "64")
// may be some other number depending on the ICU library in use.
#[cfg(feature="renaming")]
#[macro_export]
macro_rules! versioned_function {{
    ($i:ident) => {{
      paste::expr! {{
        [< $i _{0} >]
      }}
    }}
}}
// This allows the user to override the renaming configuration detected from
// icu-config.
#[cfg(not(feature="renaming"))]
#[macro_export]
macro_rules! versioned_function {{
    ($func_name:path) => {{
        $func_name
    }}
}}
"#,
            icu_major_version
        );
        macro_file
            .write_all(&to_write.into_bytes())
            .with_context(|| format!("while writing macros.rs with renaming"))
    } else {
        // The library names have not been renamed, generating an empty macro
        println!("renaming: false");
        macro_file
            .write_all(
                &r#"
// Macros for changing function names.

// There was no renaming in this one, so just short-circuit this macro.
#[macro_export]
macro_rules! versioned_function {{
    ($func_name:path) => {{
        $func_name
    }}
}}
"#
                .to_string()
                .into_bytes(),
            )
            .with_context(|| format!("while writing macros.rs without renaming"))
    }
}

fn main() -> Result<()> {
    std::env::set_var("RUST_BACKTRACE", "full");
    println!("rustfmt: {}", rustfmt_version()?);
    println!("icu-config: {}", icu_config_version()?);
    println!("icu-config-cpp-flags: {}", icu_cpp_flags()?);
    println!("icu-config-has-renaming: {}", has_renaming()?);
    println!("bindgen: {}", bindgen_version()?);

    // The path to the directory where cargo will add the output artifacts.
    let out_dir = env::var("OUT_DIR").unwrap();
    let out_dir_path = Path::new(&out_dir);

    // The path where all unicode headers can be found.
    let include_dir_path = Path::new(&icu_config_prefix()?)
        .join("include")
        .join("unicode");

    generate_linker_file(
        out_dir_path,
        &lib_dir()?,
        &vec!["icui18n", "icuuc", "icudata"],
    );
    // The modules for which bindings will be generated.  Add more if you need
    // them.  The list should be topologicaly sorted based on the inclusion
    // relationship between the respective headers.
    // Any of these will fail if the required binaries are not present in $PATH.
    let bindgen_source_modules: Vec<&str> = vec![
        "ucal", "udat", "udata", "uenum", "ustring", "utext", "uclean",
    ];
    let header_file =
        generate_wrapper_header(&out_dir_path, &bindgen_source_modules, &include_dir_path);
    run_bindgen(&header_file, out_dir_path).with_context(|| format!("while running bindgen"))?;
    run_renamegen(out_dir_path).with_context(|| format!("while running renamegen"))?;

    println!("cargo:install-dir={}", install_dir()?);
    println!("cargo:rustc-link-search=native={}", lib_dir()?);
    println!("cargo:rustc-flags={}", ld_flags()?);
    println!("done:true");
    Ok(())
}
<commit_msg>Cleans up the command invocation a bit (#18)<commit_after>#![feature(try_trait)]
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      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.

// See LICENSE for licensing information.
//
// This build.rs script tries to generate low-level rust bindings for the current ICU library.
// Please refer to README.md for instructions on how to build the library for
// your use.

use {
    anyhow::{Context, Result},
    std::env,
    std::fs::File,
    std::io::Write,
    std::path::Path,
    std::process,
};

/// A `Command` that also knows its name.
struct Command {
    name: String,
    rep: process::Command,
}

impl Command {
    /// Creates a new command to run, with the executable `name`.
    pub fn new(name: &'static str) -> Self {
        let rep = process::Command::new(&name);
        let name = String::from(name);
        Command { name, rep }
    }

    /// Runs this command with `args` as arguments.
    pub fn run(&mut self, args: &[&str]) -> Result<String> {
        self.rep.args(args);
        let stdout = self.stdout()?;
        Ok(String::from(&stdout).trim().to_string())
    }

    // Captures the stdout of the command.
    fn stdout(&mut self) -> Result<String> {
        let output = self
            .rep
            .output()
            .with_context(|| format!("could not execute command: {}", self.name))?;
        let result = String::from_utf8(output.stdout)
            .with_context(|| format!("could not convert output to UTF8"))?;
        Ok(result.trim().to_string())
    }
}

/// A command representing an `icu-config` run.  Use `ICUConfig::new()` to create.
struct ICUConfig {
    rep: Command,
}

impl ICUConfig {
    /// Creates a new ICUConfig.
    fn new() -> Self {
        ICUConfig {
            rep: Command::new("icu-config"),
        }
    }
    /// Runs `icu-config --prefix`.
    fn prefix(&mut self) -> Result<String> {
        self.rep
            .run(&["--prefix"])
            .with_context(|| format!("could not get config prefix"))
    }

    /// Runs `icu-config --libdir`.
    fn libdir(&mut self) -> Result<String> {
        self.rep
            .run(&["--libdir"])
            .with_context(|| format!("could not get library directory"))
    }
    /// Runs `icu-config --ldflags`.
    fn ldflags(&mut self) -> Result<String> {
        // Replacements needed because of https://github.com/rust-lang/cargo/issues/7217
        let result = self
            .rep
            .run(&["--ldflags"])
            .with_context(|| format!("could not get the ld flags"))?;
        Ok(result.replace("-L", "-L ").replace("-l", "-l "))
    }

    /// Runs `icu-config --cppflags`.
    fn cppflags(&mut self) -> Result<String> {
        self.rep
            .run(&["--cppflags"])
            .with_context(|| format!("while getting the cpp flags"))
    }

    /// Runs `icu-config --version`.  Returns a string like `64.2`.
    fn version(&mut self) -> Result<String> {
        self.rep
            .run(&["--version"])
            .with_context(|| format!("while getting ICU version; is icu-config in $PATH?"))
    }

    fn install_dir(&mut self) -> Result<String> {
        self.prefix()
    }

    /// Returns the config major number.  For example, will return "64" for
    /// version "64.2"
    fn version_major() -> Result<String> {
        let version = ICUConfig::new().version()?;
        let components = version.split(".");
        let last = components
            .take(1)
            .last()
            .with_context(|| format!("could not parse version number: {}", version))?;
        Ok(last.to_string())
    }
}

/// Returns true if the ICU library was compiled with renaming enabled.
fn has_renaming() -> Result<bool> {
    let cpp_flags = ICUConfig::new().cppflags()?;
    let found = cpp_flags.find("-DU_DISABLE_RENAMING=1");
    println!("flags: {}", cpp_flags);
    Ok(found.is_none())
}

fn rustfmt_cmd() -> Command {
    Command::new("rustfmt")
}

fn rustfmt_version() -> Result<String> {
    rustfmt_cmd()
        .run(&["--version"])
        .with_context(|| format!("while getting rustfmt version; is rustfmt in $PATH?"))
}

fn bindgen_cmd() -> Command {
    Command::new("bindgen")
}

fn bindgen_version() -> Result<String> {
    bindgen_cmd()
        .run(&["--version"])
        .with_context(|| format!("while getting bindgen version; is bindgen in $PATH?"))
}

/// Generates an additional include file which contains the linker directives.
/// This is done because cargo does not allow the rustc link directives to be
/// anything other than `-L` and `-l`.
fn generate_linker_file(out_dir_path: &Path, lib_dir: &str, lib_names: &Vec<&str>) {
    let file_path = out_dir_path.join("link.rs");
    let mut linker_file = File::create(&file_path).unwrap();
    let mut content: Vec<String> = vec![];
    for lib in lib_names {
        let linkopt: String = format!(r#"#[link_args="-Wl,-rpath={}/lib{}.so"]"#, lib_dir, lib);
        content.push(linkopt);
    }
    content.push(String::from(r#"extern "C" {}"#));
    linker_file
        .write_all(&content.join("\n").into_bytes())
        .expect("successful write into linker file");
}

/// Generates a wrapper header that includes all headers of interest for binding.
///
/// This is the recommended way to bind complex libraries at the moment.  Returns
/// the full path of the generated wrapper header file.
fn generate_wrapper_header(
    out_dir_path: &Path,
    bindgen_source_modules: &Vec<&str>,
    include_path: &Path,
) -> String {
    let wrapper_path = out_dir_path.join("wrapper.h");
    let mut wrapper_file = File::create(&wrapper_path).unwrap();
    wrapper_file
        .write_all(b"/* Generated file, do not edit. */ \n")
        .unwrap();
    let includes = bindgen_source_modules
        .iter()
        .map(|f| {
            let file_path = include_path.join(format!("{}.h", f));
            let file_path_str = format!("#include \"{}\"\n", file_path.to_str().unwrap());
            println!("include-file: '{}'", file_path.to_str().unwrap());
            file_path_str
        })
        .collect::<String>();
    wrapper_file.write_all(&includes.into_bytes()).unwrap();
    String::from(wrapper_path.to_str().unwrap())
}

fn commaify(s: &Vec<&str>) -> String {
    format!("{}", s.join("|"))
}

fn run_bindgen(header_file: &str, out_dir_path: &Path) -> Result<()> {
    let whitelist_types_regexes = commaify(&vec![
        "UBool",
        "UCalendar.*",
        "UChar.*",
        "UData.*",
        "UDate",
        "UDateFormat.*",
        "UEnumeration.*",
        "UErrorCode",
        "UText.*",
    ]);

    let whitelist_functions_regexes = commaify(&vec![
        "u_.*", "ucal_.*", "udata_*", "udat_.*", "uenum_.*", "uloc_.*", "utext_.*",
    ]);

    let opaque_types_regexes = commaify(&vec![]);

    // Common arguments for all bindgen invocations.
    let bindgen_generate_args = vec![
        "--default-enum-style=rust",
        "--no-doc-comments",
        "--with-derive-default",
        "--with-derive-hash",
        "--with-derive-partialord",
        "--with-derive-partialeq",
        "--whitelist-type",
        &whitelist_types_regexes,
        "--whitelist-function",
        &whitelist_functions_regexes,
        "--opaque-type",
        &opaque_types_regexes,
    ];

    let output_file_path = out_dir_path.join("lib.rs");
    let output_file = output_file_path.to_str().unwrap();
    let ld_flags = ICUConfig::new().ldflags()?;
    let cpp_flags = ICUConfig::new().cppflags()?;
    let mut file_args = vec![
        "-o",
        &output_file,
        &header_file,
        "--",
        &ld_flags,
        &cpp_flags,
    ];
    if !has_renaming()? {
        file_args.push("-DU_DISABLE_RENAMING=1");
    }
    let all_args = [&bindgen_generate_args[..], &file_args[..]].concat();
    println!("bindgen-cmdline: {:?}", all_args);
    process::Command::new("bindgen")
        .args(&all_args)
        .spawn()
        .with_context(|| format!("while running bindgen"))?;
    Ok(())
}

// Generates the library renaming macro: this allows us to use renamed function
// names in the resulting low-level bindings library.
fn run_renamegen(out_dir_path: &Path) -> Result<()> {
    let output_file_path = out_dir_path.join("macros.rs");
    let mut macro_file = File::create(&output_file_path)
        .with_context(|| format!("while opening {:?}", output_file_path))?;
    if has_renaming()? {
        println!("renaming: true");
        // The library names have been renamed, need to generate a macro that
        // converts the call of `foo()` into `foo_64()`.
        let icu_major_version = ICUConfig::version_major()?;
        let to_write = format!(
            r#"
// Macros for changing function names.

extern crate paste;

// This library was build with version renaming, so rewrite every function name
// with its name with version number appended.

// The macro below will rename a symbol `foo::bar` to `foo::bar_64` (where "64")
// may be some other number depending on the ICU library in use.
#[cfg(feature="renaming")]
#[macro_export]
macro_rules! versioned_function {{
    ($i:ident) => {{
      paste::expr! {{
        [< $i _{0} >]
      }}
    }}
}}
// This allows the user to override the renaming configuration detected from
// icu-config.
#[cfg(not(feature="renaming"))]
#[macro_export]
macro_rules! versioned_function {{
    ($func_name:path) => {{
        $func_name
    }}
}}
"#,
            icu_major_version
        );
        macro_file
            .write_all(&to_write.into_bytes())
            .with_context(|| format!("while writing macros.rs with renaming"))
    } else {
        // The library names have not been renamed, generating an empty macro
        println!("renaming: false");
        macro_file
            .write_all(
                &r#"
// Macros for changing function names.

// There was no renaming in this one, so just short-circuit this macro.
#[macro_export]
macro_rules! versioned_function {{
    ($func_name:path) => {{
        $func_name
    }}
}}
"#
                .to_string()
                .into_bytes(),
            )
            .with_context(|| format!("while writing macros.rs without renaming"))
    }
}

fn main() -> Result<()> {
    std::env::set_var("RUST_BACKTRACE", "full");
    println!("rustfmt: {}", rustfmt_version()?);
    println!("icu-config: {}", ICUConfig::new().version()?);
    println!("icu-config-cpp-flags: {}", ICUConfig::new().cppflags()?);
    println!("icu-config-has-renaming: {}", has_renaming()?);
    println!("bindgen: {}", bindgen_version()?);

    // The path to the directory where cargo will add the output artifacts.
    let out_dir = env::var("OUT_DIR").unwrap();
    let out_dir_path = Path::new(&out_dir);

    // The path where all unicode headers can be found.
    let include_dir_path = Path::new(&ICUConfig::new().prefix()?)
        .join("include")
        .join("unicode");

    let lib_dir = ICUConfig::new().libdir()?;

    generate_linker_file(out_dir_path, &lib_dir, &vec!["icui18n", "icuuc", "icudata"]);
    // The modules for which bindings will be generated.  Add more if you need
    // them.  The list should be topologicaly sorted based on the inclusion
    // relationship between the respective headers.
    // Any of these will fail if the required binaries are not present in $PATH.
    let bindgen_source_modules: Vec<&str> = vec![
        "ucal", "udat", "udata", "uenum", "ustring", "utext", "uclean",
    ];
    let header_file =
        generate_wrapper_header(&out_dir_path, &bindgen_source_modules, &include_dir_path);
    run_bindgen(&header_file, out_dir_path).with_context(|| format!("while running bindgen"))?;
    run_renamegen(out_dir_path).with_context(|| format!("while running renamegen"))?;

    println!("cargo:install-dir={}", ICUConfig::new().install_dir()?);
    println!("cargo:rustc-link-search=native={}", lib_dir);
    println!("cargo:rustc-flags={}", ICUConfig::new().ldflags()?);
    println!("done:true");
    Ok(())
}
<|endoftext|>


<commit_before>use base_db::fixture::WithFixture;
use expect_test::{expect, Expect};

use crate::{db::DefDatabase, test_db::TestDB};

fn check(ra_fixture: &str, expect: Expect) {
    let (db, file_id) = TestDB::with_single_file(ra_fixture);
    let item_tree = db.file_item_tree(file_id.into());
    let pretty = item_tree.pretty_print();
    expect.assert_eq(&pretty);
}

#[test]
fn imports() {
    check(
        r#"
//! file comment
#![no_std]
//! another file comment

extern crate self as renamed;
pub(super) extern crate bli;

pub use crate::path::{nested, items as renamed, Trait as _};
use globs::*;

/// docs on import
use crate::{A, B};

use a::{c, d::{e}};
        "#,
        expect![[r##"
            #![doc = " file comment"]  // AttrId { is_doc_comment: true, ast_index: 0 }
            #![no_std]  // AttrId { is_doc_comment: false, ast_index: 0 }
            #![doc = " another file comment"]  // AttrId { is_doc_comment: true, ast_index: 1 }

            pub(self) extern crate self as renamed;

            pub(super) extern crate bli;

            pub use crate::path::{nested, items as renamed, Trait as _};

            pub(self) use globs::*;

            #[doc = " docs on import"]  // AttrId { is_doc_comment: true, ast_index: 0 }
            pub(self) use crate::{A, B};

            pub(self) use a::{c, d::{e}};
        "##]],
    );
}

#[test]
fn extern_blocks() {
    check(
        r#"
#[on_extern_block]
extern "C" {
    #[on_extern_type]
    type ExType;

    #[on_extern_static]
    static EX_STATIC: u8;

    #[on_extern_fn]
    fn ex_fn();
}
        "#,
        expect![[r##"
            #[on_extern_block]  // AttrId { is_doc_comment: false, ast_index: 0 }
            extern "C" {
                #[on_extern_type]  // AttrId { is_doc_comment: false, ast_index: 0 }
                pub(self) type ExType;  // extern

                #[on_extern_static]  // AttrId { is_doc_comment: false, ast_index: 0 }
                pub(self) static EX_STATIC: u8 = _;  // extern

                #[on_extern_fn]  // AttrId { is_doc_comment: false, ast_index: 0 }
                // flags = 0x60
                pub(self) fn ex_fn() -> ();
            }
        "##]],
    );
}

#[test]
fn adts() {
    check(
        r#"
struct Unit;

#[derive(Debug)]
struct Struct {
    /// fld docs
    fld: (),
}

struct Tuple(#[attr] u8);

union Ize {
    a: (),
    b: (),
}

enum E {
    /// comment on Unit
    Unit,
    /// comment on Tuple
    Tuple(u8),
    Struct {
        /// comment on a: u8
        a: u8,
    }
}
        "#,
        expect![[r##"
            pub(self) struct Unit;

            #[derive(Debug)]  // AttrId { is_doc_comment: false, ast_index: 0 }
            pub(self) struct Struct {
                #[doc = " fld docs"]  // AttrId { is_doc_comment: true, ast_index: 0 }
                pub(self) fld: (),
            }

            pub(self) struct Tuple(
                #[attr]  // AttrId { is_doc_comment: false, ast_index: 0 }
                pub(self) 0: u8,
            );

            pub(self) union Ize {
                pub(self) a: (),
                pub(self) b: (),
            }

            pub(self) enum E {
                #[doc = " comment on Unit"]  // AttrId { is_doc_comment: true, ast_index: 0 }
                Unit,
                #[doc = " comment on Tuple"]  // AttrId { is_doc_comment: true, ast_index: 0 }
                Tuple(
                    pub(self) 0: u8,
                ),
                Struct {
                    #[doc = " comment on a: u8"]  // AttrId { is_doc_comment: true, ast_index: 0 }
                    pub(self) a: u8,
                },
            }
        "##]],
    );
}

#[test]
fn misc() {
    check(
        r#"
pub static mut ST: () = ();

const _: Anon = ();

#[attr]
fn f(#[attr] arg: u8, _: ()) {
    #![inner_attr_in_fn]
}

trait Tr: SuperTrait + 'lifetime {
    type Assoc: AssocBound = Default;
    fn method(&self);
}
        "#,
        expect![[r##"
            pub static mut ST: () = _;

            pub(self) const _: Anon = _;

            #[attr]  // AttrId { is_doc_comment: false, ast_index: 0 }
            #[inner_attr_in_fn]  // AttrId { is_doc_comment: false, ast_index: 1 }
            // flags = 0x2
            pub(self) fn f(
                #[attr]  // AttrId { is_doc_comment: false, ast_index: 0 }
                _: u8,
                _: (),
            ) -> ();

            pub(self) trait Tr<Self>
            where
                Self: SuperTrait,
                Self: 'lifetime
            {
                pub(self) type Assoc: AssocBound = Default;

                // flags = 0x1
                pub(self) fn method(
                    _: &Self,
                ) -> ();
            }
        "##]],
    );
}

#[test]
fn modules() {
    check(
        r#"
/// outer
mod inline {
    //! inner

    use super::*;

    fn fn_in_module() {}
}

mod outline;
        "#,
        expect![[r##"
            #[doc = " outer"]  // AttrId { is_doc_comment: true, ast_index: 0 }
            #[doc = " inner"]  // AttrId { is_doc_comment: true, ast_index: 1 }
            pub(self) mod inline {
                pub(self) use super::*;

                // flags = 0x2
                pub(self) fn fn_in_module() -> ();
            }

            pub(self) mod outline;
        "##]],
    );
}

#[test]
fn macros() {
    check(
        r#"
macro_rules! m {
    () => {};
}

pub macro m2() {}

m!();
        "#,
        expect![[r#"
            macro_rules! m { ... }

            pub macro m2 { ... }

            m!(...);
        "#]],
    );
}

#[test]
fn mod_paths() {
    check(
        r#"
struct S {
    a: self::Ty,
    b: super::SuperTy,
    c: super::super::SuperSuperTy,
    d: ::abs::Path,
    e: crate::Crate,
    f: plain::path::Ty,
}
        "#,
        expect![[r#"
            pub(self) struct S {
                pub(self) a: self::Ty,
                pub(self) b: super::SuperTy,
                pub(self) c: super::super::SuperSuperTy,
                pub(self) d: ::abs::Path,
                pub(self) e: crate::Crate,
                pub(self) f: plain::path::Ty,
            }
        "#]],
    )
}

#[test]
fn types() {
    check(
        r#"
struct S {
    a: Mixed<'a, T, Item=(), OtherItem=u8>,
    b: <Fully as Qualified>::Syntax,
    c: <TypeAnchored>::Path::<'a>,
    d: dyn for<'a> Trait<'a>,
}
        "#,
        expect![[r#"
            pub(self) struct S {
                pub(self) a: Mixed<'a, T, Item = (), OtherItem = u8>,
                pub(self) b: Qualified<Self=Fully>::Syntax,
                pub(self) c: <TypeAnchored>::Path<'a>,
                pub(self) d: dyn for<'a> Trait<'a>,
            }
        "#]],
    )
}

#[test]
fn generics() {
    check(
        r#"
struct S<'a, 'b: 'a, T: Copy + 'a + 'b, const K: u8 = 0> {
    field: &'a &'b T,
}

struct Tuple<T: Copy, U: ?Sized>(T, U);

impl<'a, 'b: 'a, T: Copy + 'a + 'b, const K: u8 = 0> S<'a, 'b, T, K> {
    fn f<G: 'a>(arg: impl Copy) -> impl Copy {}
}

enum Enum<'a, T, const U: u8> {}
union Union<'a, T, const U: u8> {}

trait Tr<'a, T: 'a>: Super where Self: for<'a> Tr<'a, T> {}
        "#,
        expect![[r#"
            pub(self) struct S<'a, 'b, T, const K: u8>
            where
                T: Copy,
                T: 'a,
                T: 'b
            {
                pub(self) field: &'a &'b T,
            }

            pub(self) struct Tuple<T, U>(
                pub(self) 0: T,
                pub(self) 1: U,
            )
            where
                T: Copy,
                U: ?Sized;

            impl<'a, 'b, T, const K: u8> S<'a, 'b, T, K>
            where
                T: Copy,
                T: 'a,
                T: 'b
            {
                // flags = 0x2
                pub(self) fn f<G, _anon_1>(
                    _: impl Copy,
                ) -> impl Copy
                where
                    G: 'a,
                    _anon_1: Copy;
            }

            pub(self) enum Enum<'a, T, const U: u8> {
            }

            pub(self) union Union<'a, T, const U: u8> {
            }

            pub(self) trait Tr<'a, Self, T>
            where
                Self: Super,
                T: 'a,
                Self: for<'a> Tr<'a, T>
            {
            }
        "#]],
    )
}

#[test]
fn inherit_visibility() {
    check(
        r#"
pub(crate) enum En {
    Var1(u8),
    Var2 {
        fld: u8,
    },
}

pub(crate) trait Tr {
    fn f();
    fn method(&self) {}
}
        "#,
        expect![[r#"
            pub(crate) enum En {
                Var1(
                    pub(crate) 0: u8,
                ),
                Var2 {
                    pub(crate) fld: u8,
                },
            }

            pub(crate) trait Tr<Self> {
                pub(crate) fn f() -> ();

                // flags = 0x3
                pub(crate) fn method(
                    _: &Self,
                ) -> ();
            }
        "#]],
    )
}
<commit_msg>Update failing test<commit_after>use base_db::fixture::WithFixture;
use expect_test::{expect, Expect};

use crate::{db::DefDatabase, test_db::TestDB};

fn check(ra_fixture: &str, expect: Expect) {
    let (db, file_id) = TestDB::with_single_file(ra_fixture);
    let item_tree = db.file_item_tree(file_id.into());
    let pretty = item_tree.pretty_print();
    expect.assert_eq(&pretty);
}

#[test]
fn imports() {
    check(
        r#"
//! file comment
#![no_std]
//! another file comment

extern crate self as renamed;
pub(super) extern crate bli;

pub use crate::path::{nested, items as renamed, Trait as _};
use globs::*;

/// docs on import
use crate::{A, B};

use a::{c, d::{e}};
        "#,
        expect![[r##"
            #![doc = " file comment"]  // AttrId { is_doc_comment: true, ast_index: 0 }
            #![no_std]  // AttrId { is_doc_comment: false, ast_index: 0 }
            #![doc = " another file comment"]  // AttrId { is_doc_comment: true, ast_index: 1 }

            pub(self) extern crate self as renamed;

            pub(super) extern crate bli;

            pub use crate::path::{nested, items as renamed, Trait as _};

            pub(self) use globs::*;

            #[doc = " docs on import"]  // AttrId { is_doc_comment: true, ast_index: 0 }
            pub(self) use crate::{A, B};

            pub(self) use a::{c, d::{e}};
        "##]],
    );
}

#[test]
fn extern_blocks() {
    check(
        r#"
#[on_extern_block]
extern "C" {
    #[on_extern_type]
    type ExType;

    #[on_extern_static]
    static EX_STATIC: u8;

    #[on_extern_fn]
    fn ex_fn();
}
        "#,
        expect![[r##"
            #[on_extern_block]  // AttrId { is_doc_comment: false, ast_index: 0 }
            extern "C" {
                #[on_extern_type]  // AttrId { is_doc_comment: false, ast_index: 0 }
                pub(self) type ExType;  // extern

                #[on_extern_static]  // AttrId { is_doc_comment: false, ast_index: 0 }
                pub(self) static EX_STATIC: u8 = _;  // extern

                #[on_extern_fn]  // AttrId { is_doc_comment: false, ast_index: 0 }
                // flags = 0x60
                pub(self) fn ex_fn() -> ();
            }
        "##]],
    );
}

#[test]
fn adts() {
    check(
        r#"
struct Unit;

#[derive(Debug)]
struct Struct {
    /// fld docs
    fld: (),
}

struct Tuple(#[attr] u8);

union Ize {
    a: (),
    b: (),
}

enum E {
    /// comment on Unit
    Unit,
    /// comment on Tuple
    Tuple(u8),
    Struct {
        /// comment on a: u8
        a: u8,
    }
}
        "#,
        expect![[r##"
            pub(self) struct Unit;

            #[derive(Debug)]  // AttrId { is_doc_comment: false, ast_index: 0 }
            pub(self) struct Struct {
                #[doc = " fld docs"]  // AttrId { is_doc_comment: true, ast_index: 0 }
                pub(self) fld: (),
            }

            pub(self) struct Tuple(
                #[attr]  // AttrId { is_doc_comment: false, ast_index: 0 }
                pub(self) 0: u8,
            );

            pub(self) union Ize {
                pub(self) a: (),
                pub(self) b: (),
            }

            pub(self) enum E {
                #[doc = " comment on Unit"]  // AttrId { is_doc_comment: true, ast_index: 0 }
                Unit,
                #[doc = " comment on Tuple"]  // AttrId { is_doc_comment: true, ast_index: 0 }
                Tuple(
                    pub(self) 0: u8,
                ),
                Struct {
                    #[doc = " comment on a: u8"]  // AttrId { is_doc_comment: true, ast_index: 0 }
                    pub(self) a: u8,
                },
            }
        "##]],
    );
}

#[test]
fn misc() {
    check(
        r#"
pub static mut ST: () = ();

const _: Anon = ();

#[attr]
fn f(#[attr] arg: u8, _: ()) {
    #![inner_attr_in_fn]
}

trait Tr: SuperTrait + 'lifetime {
    type Assoc: AssocBound = Default;
    fn method(&self);
}
        "#,
        expect![[r##"
            pub static mut ST: () = _;

            pub(self) const _: Anon = _;

            #[attr]  // AttrId { is_doc_comment: false, ast_index: 0 }
            #[inner_attr_in_fn]  // AttrId { is_doc_comment: false, ast_index: 1 }
            // flags = 0x2
            pub(self) fn f(
                #[attr]  // AttrId { is_doc_comment: false, ast_index: 0 }
                _: u8,
                _: (),
            ) -> ();

            pub(self) trait Tr<Self>
            where
                Self: SuperTrait,
                Self: 'lifetime
            {
                pub(self) type Assoc: AssocBound = Default;

                // flags = 0x1
                pub(self) fn method(
                    _: &Self,
                ) -> ();
            }
        "##]],
    );
}

#[test]
fn modules() {
    check(
        r#"
/// outer
mod inline {
    //! inner

    use super::*;

    fn fn_in_module() {}
}

mod outline;
        "#,
        expect![[r##"
            #[doc = " outer"]  // AttrId { is_doc_comment: true, ast_index: 0 }
            #[doc = " inner"]  // AttrId { is_doc_comment: true, ast_index: 1 }
            pub(self) mod inline {
                pub(self) use super::*;

                // flags = 0x2
                pub(self) fn fn_in_module() -> ();
            }

            pub(self) mod outline;
        "##]],
    );
}

#[test]
fn macros() {
    check(
        r#"
macro_rules! m {
    () => {};
}

pub macro m2() {}

m!();
        "#,
        expect![[r#"
            macro_rules! m { ... }

            pub macro m2 { ... }

            m!(...);
        "#]],
    );
}

#[test]
fn mod_paths() {
    check(
        r#"
struct S {
    a: self::Ty,
    b: super::SuperTy,
    c: super::super::SuperSuperTy,
    d: ::abs::Path,
    e: crate::Crate,
    f: plain::path::Ty,
}
        "#,
        expect![[r#"
            pub(self) struct S {
                pub(self) a: self::Ty,
                pub(self) b: super::SuperTy,
                pub(self) c: super::super::SuperSuperTy,
                pub(self) d: ::abs::Path,
                pub(self) e: crate::Crate,
                pub(self) f: plain::path::Ty,
            }
        "#]],
    )
}

#[test]
fn types() {
    check(
        r#"
struct S {
    a: Mixed<'a, T, Item=(), OtherItem=u8>,
    b: <Fully as Qualified>::Syntax,
    c: <TypeAnchored>::Path::<'a>,
    d: dyn for<'a> Trait<'a>,
}
        "#,
        expect![[r#"
            pub(self) struct S {
                pub(self) a: Mixed<'a, T, Item = (), OtherItem = u8>,
                pub(self) b: Qualified<Self=Fully>::Syntax,
                pub(self) c: <TypeAnchored>::Path<'a>,
                pub(self) d: dyn for<'a> Trait<'a>,
            }
        "#]],
    )
}

#[test]
fn generics() {
    check(
        r#"
struct S<'a, 'b: 'a, T: Copy + 'a + 'b, const K: u8 = 0> {
    field: &'a &'b T,
}

struct Tuple<T: Copy, U: ?Sized>(T, U);

impl<'a, 'b: 'a, T: Copy + 'a + 'b, const K: u8 = 0> S<'a, 'b, T, K> {
    fn f<G: 'a>(arg: impl Copy) -> impl Copy {}
}

enum Enum<'a, T, const U: u8> {}
union Union<'a, T, const U: u8> {}

trait Tr<'a, T: 'a>: Super where Self: for<'a> Tr<'a, T> {}
        "#,
        expect![[r#"
            pub(self) struct S<'a, 'b, T, const K: u8>
            where
                T: Copy,
                T: 'a,
                T: 'b
            {
                pub(self) field: &'a &'b T,
            }

            pub(self) struct Tuple<T, U>(
                pub(self) 0: T,
                pub(self) 1: U,
            )
            where
                T: Copy,
                U: ?Sized;

            impl<'a, 'b, T, const K: u8> S<'a, 'b, T, K>
            where
                T: Copy,
                T: 'a,
                T: 'b
            {
                // flags = 0x2
                pub(self) fn f<G>(
                    _: impl Copy,
                ) -> impl Copy
                where
                    G: 'a;
            }

            pub(self) enum Enum<'a, T, const U: u8> {
            }

            pub(self) union Union<'a, T, const U: u8> {
            }

            pub(self) trait Tr<'a, Self, T>
            where
                Self: Super,
                T: 'a,
                Self: for<'a> Tr<'a, T>
            {
            }
        "#]],
    )
}

#[test]
fn inherit_visibility() {
    check(
        r#"
pub(crate) enum En {
    Var1(u8),
    Var2 {
        fld: u8,
    },
}

pub(crate) trait Tr {
    fn f();
    fn method(&self) {}
}
        "#,
        expect![[r#"
            pub(crate) enum En {
                Var1(
                    pub(crate) 0: u8,
                ),
                Var2 {
                    pub(crate) fld: u8,
                },
            }

            pub(crate) trait Tr<Self> {
                pub(crate) fn f() -> ();

                // flags = 0x3
                pub(crate) fn method(
                    _: &Self,
                ) -> ();
            }
        "#]],
    )
}
<|endoftext|>


<commit_before>//! Types for the [`m.room.create`] event.
//!
//! [`m.room.create`]: https://spec.matrix.org/v1.1/client-server-api/#mroomcreate

use ruma_events_macros::EventContent;
use ruma_identifiers::{EventId, RoomId, RoomVersionId, UserId};
use ruma_serde::StringEnum;
use serde::{Deserialize, Serialize};

use crate::PrivOwnedStr;

/// The content of an `m.room.create` event.
///
/// This is the first event in a room and cannot be changed.
///
/// It acts as the root of all other events.
#[derive(Clone, Debug, Deserialize, Serialize, EventContent)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
#[ruma_event(type = "m.room.create", kind = State)]
pub struct RoomCreateEventContent {
    /// The `user_id` of the room creator.
    ///
    /// This is set by the homeserver.
    #[ruma_event(skip_redaction)]
    pub creator: Box<UserId>,

    /// Whether or not this room's data should be transferred to other homeservers.
    #[serde(
        rename = "m.federate",
        default = "ruma_serde::default_true",
        skip_serializing_if = "ruma_serde::is_true"
    )]
    pub federate: bool,

    /// The version of the room.
    ///
    /// Defaults to `RoomVersionId::V1`.
    #[serde(default = "default_room_version_id")]
    pub room_version: RoomVersionId,

    /// A reference to the room this room replaces, if the previous room was upgraded.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub predecessor: Option<PreviousRoom>,

    /// The room type.
    ///
    /// This is currently only used for spaces.
    #[cfg(feature = "unstable-spec")]
    #[serde(skip_serializing_if = "Option::is_none", rename = "type")]
    pub room_type: Option<RoomType>,
}

impl RoomCreateEventContent {
    /// Creates a new `RoomCreateEventContent` with the given creator.
    pub fn new(creator: Box<UserId>) -> Self {
        Self {
            creator,
            federate: true,
            room_version: default_room_version_id(),
            predecessor: None,
            #[cfg(feature = "unstable-spec")]
            room_type: None,
        }
    }
}

/// An enum of possible room types.
///
/// This type can hold an arbitrary string. To check for formats that are not available as a
/// documented variant here, use its string representation, obtained through `.as_str()`.
#[derive(Clone, Debug, PartialEq, Eq, StringEnum)]
#[cfg(feature = "unstable-spec")]
#[non_exhaustive]
pub enum RoomType {
    /// Defines the room as a space.
    #[ruma_enum(rename = "m.space")]
    Space,

    /// Defines the room as a custom type.
    #[doc(hidden)]
    _Custom(PrivOwnedStr),
}

impl RoomType {
    /// Creates a string slice from this `RoomType`.
    pub fn as_str(&self) -> &str {
        self.as_ref()
    }
}

/// A reference to an old room replaced during a room version upgrade.
#[derive(Clone, Debug, Deserialize, Serialize)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub struct PreviousRoom {
    /// The ID of the old room.
    pub room_id: Box<RoomId>,

    /// The event ID of the last known event in the old room.
    pub event_id: Box<EventId>,
}

impl PreviousRoom {
    /// Creates a new `PreviousRoom` from the given room and event IDs.
    pub fn new(room_id: Box<RoomId>, event_id: Box<EventId>) -> Self {
        Self { room_id, event_id }
    }
}

/// Used to default the `room_version` field to room version 1.
fn default_room_version_id() -> RoomVersionId {
    RoomVersionId::V1
}

#[cfg(test)]
mod tests {
    use matches::assert_matches;
    use ruma_identifiers::{user_id, RoomVersionId};
    use serde_json::{from_value as from_json_value, json, to_value as to_json_value};

    use super::RoomCreateEventContent;

    #[cfg(feature = "unstable-spec")]
    use super::RoomType;

    #[test]
    fn serialization() {
        let content = RoomCreateEventContent {
            creator: user_id!("@carl:example.com").to_owned(),
            federate: false,
            room_version: RoomVersionId::V4,
            predecessor: None,
            #[cfg(feature = "unstable-spec")]
            room_type: None,
        };

        let json = json!({
            "creator": "@carl:example.com",
            "m.federate": false,
            "room_version": "4"
        });

        assert_eq!(to_json_value(&content).unwrap(), json);
    }

    #[cfg(feature = "unstable-spec")]
    #[test]
    fn space_serialization() {
        let content = RoomCreateEventContent {
            creator: user_id!("@carl:example.com").to_owned(),
            federate: false,
            room_version: RoomVersionId::V4,
            predecessor: None,
            room_type: Some(RoomType::Space),
        };

        let json = json!({
            "creator": "@carl:example.com",
            "m.federate": false,
            "room_version": "4",
            "type": "m.space"
        });

        assert_eq!(to_json_value(&content).unwrap(), json);
    }

    #[test]
    fn deserialization() {
        let json = json!({
            "creator": "@carl:example.com",
            "m.federate": true,
            "room_version": "4"
        });

        assert_matches!(
            from_json_value::<RoomCreateEventContent>(json).unwrap(),
            RoomCreateEventContent {
                creator,
                federate: true,
                room_version: RoomVersionId::V4,
                predecessor: None,
                #[cfg(feature = "unstable-spec")]
                room_type: None,
            } if creator == "@carl:example.com"
        );
    }

    #[cfg(feature = "unstable-pre-spec")]
    #[test]
    fn space_deserialization() {
        let json = json!({
            "creator": "@carl:example.com",
            "m.federate": true,
            "room_version": "4",
            "type": "m.space"
        });

        assert_matches!(
            from_json_value::<RoomCreateEventContent>(json).unwrap(),
            RoomCreateEventContent {
                creator,
                federate: true,
                room_version: RoomVersionId::V4,
                predecessor: None,
                room_type
            } if creator == "@carl:example.com" && room_type == Some(RoomType::Space)
        );
    }
}
<commit_msg>events: Fix missing feature flags<commit_after>//! Types for the [`m.room.create`] event.
//!
//! [`m.room.create`]: https://spec.matrix.org/v1.1/client-server-api/#mroomcreate

use ruma_events_macros::EventContent;
use ruma_identifiers::{EventId, RoomId, RoomVersionId, UserId};
#[cfg(feature = "unstable-spec")]
use ruma_serde::StringEnum;
use serde::{Deserialize, Serialize};

#[cfg(feature = "unstable-spec")]
use crate::PrivOwnedStr;

/// The content of an `m.room.create` event.
///
/// This is the first event in a room and cannot be changed.
///
/// It acts as the root of all other events.
#[derive(Clone, Debug, Deserialize, Serialize, EventContent)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
#[ruma_event(type = "m.room.create", kind = State)]
pub struct RoomCreateEventContent {
    /// The `user_id` of the room creator.
    ///
    /// This is set by the homeserver.
    #[ruma_event(skip_redaction)]
    pub creator: Box<UserId>,

    /// Whether or not this room's data should be transferred to other homeservers.
    #[serde(
        rename = "m.federate",
        default = "ruma_serde::default_true",
        skip_serializing_if = "ruma_serde::is_true"
    )]
    pub federate: bool,

    /// The version of the room.
    ///
    /// Defaults to `RoomVersionId::V1`.
    #[serde(default = "default_room_version_id")]
    pub room_version: RoomVersionId,

    /// A reference to the room this room replaces, if the previous room was upgraded.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub predecessor: Option<PreviousRoom>,

    /// The room type.
    ///
    /// This is currently only used for spaces.
    #[cfg(feature = "unstable-spec")]
    #[serde(skip_serializing_if = "Option::is_none", rename = "type")]
    pub room_type: Option<RoomType>,
}

impl RoomCreateEventContent {
    /// Creates a new `RoomCreateEventContent` with the given creator.
    pub fn new(creator: Box<UserId>) -> Self {
        Self {
            creator,
            federate: true,
            room_version: default_room_version_id(),
            predecessor: None,
            #[cfg(feature = "unstable-spec")]
            room_type: None,
        }
    }
}

/// An enum of possible room types.
///
/// This type can hold an arbitrary string. To check for formats that are not available as a
/// documented variant here, use its string representation, obtained through `.as_str()`.
#[derive(Clone, Debug, PartialEq, Eq, StringEnum)]
#[cfg(feature = "unstable-spec")]
#[non_exhaustive]
pub enum RoomType {
    /// Defines the room as a space.
    #[ruma_enum(rename = "m.space")]
    Space,

    /// Defines the room as a custom type.
    #[doc(hidden)]
    _Custom(PrivOwnedStr),
}

#[cfg(feature = "unstable-spec")]
impl RoomType {
    /// Creates a string slice from this `RoomType`.
    pub fn as_str(&self) -> &str {
        self.as_ref()
    }
}

/// A reference to an old room replaced during a room version upgrade.
#[derive(Clone, Debug, Deserialize, Serialize)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub struct PreviousRoom {
    /// The ID of the old room.
    pub room_id: Box<RoomId>,

    /// The event ID of the last known event in the old room.
    pub event_id: Box<EventId>,
}

impl PreviousRoom {
    /// Creates a new `PreviousRoom` from the given room and event IDs.
    pub fn new(room_id: Box<RoomId>, event_id: Box<EventId>) -> Self {
        Self { room_id, event_id }
    }
}

/// Used to default the `room_version` field to room version 1.
fn default_room_version_id() -> RoomVersionId {
    RoomVersionId::V1
}

#[cfg(test)]
mod tests {
    use matches::assert_matches;
    use ruma_identifiers::{user_id, RoomVersionId};
    use serde_json::{from_value as from_json_value, json, to_value as to_json_value};

    use super::RoomCreateEventContent;

    #[cfg(feature = "unstable-spec")]
    use super::RoomType;

    #[test]
    fn serialization() {
        let content = RoomCreateEventContent {
            creator: user_id!("@carl:example.com").to_owned(),
            federate: false,
            room_version: RoomVersionId::V4,
            predecessor: None,
            #[cfg(feature = "unstable-spec")]
            room_type: None,
        };

        let json = json!({
            "creator": "@carl:example.com",
            "m.federate": false,
            "room_version": "4"
        });

        assert_eq!(to_json_value(&content).unwrap(), json);
    }

    #[cfg(feature = "unstable-spec")]
    #[test]
    fn space_serialization() {
        let content = RoomCreateEventContent {
            creator: user_id!("@carl:example.com").to_owned(),
            federate: false,
            room_version: RoomVersionId::V4,
            predecessor: None,
            room_type: Some(RoomType::Space),
        };

        let json = json!({
            "creator": "@carl:example.com",
            "m.federate": false,
            "room_version": "4",
            "type": "m.space"
        });

        assert_eq!(to_json_value(&content).unwrap(), json);
    }

    #[test]
    fn deserialization() {
        let json = json!({
            "creator": "@carl:example.com",
            "m.federate": true,
            "room_version": "4"
        });

        assert_matches!(
            from_json_value::<RoomCreateEventContent>(json).unwrap(),
            RoomCreateEventContent {
                creator,
                federate: true,
                room_version: RoomVersionId::V4,
                predecessor: None,
                #[cfg(feature = "unstable-spec")]
                room_type: None,
            } if creator == "@carl:example.com"
        );
    }

    #[cfg(feature = "unstable-pre-spec")]
    #[test]
    fn space_deserialization() {
        let json = json!({
            "creator": "@carl:example.com",
            "m.federate": true,
            "room_version": "4",
            "type": "m.space"
        });

        assert_matches!(
            from_json_value::<RoomCreateEventContent>(json).unwrap(),
            RoomCreateEventContent {
                creator,
                federate: true,
                room_version: RoomVersionId::V4,
                predecessor: None,
                room_type
            } if creator == "@carl:example.com" && room_type == Some(RoomType::Space)
        );
    }
}
<|endoftext|>


<commit_before>use num::integer::Integer;
use memory::Memory;
use memory::low_nibble;
use memory::high_nibble;
use extensions::Incrementor;
use ops::{mod};
use std::num::One;

pub struct Cpu {
    reg: Registers,
    mem: Memory
}

impl Cpu {
    pub fn new(memory: Memory) -> Cpu {
        return Cpu{reg: Registers::new(), mem: memory}
    }

    /// Execute a cycle on the cpu
    pub fn tick(&mut self) {
        let instr = self.fetch_instruction();
        self.reg.pc.increment();

        match high_nibble(instr) {
            0x0 => self.zero(instr),
            _ => return
        }
    }

    /// Fetches the instruction pointed to by the program counter
    /// and increments the pc by 1
    fn fetch_instruction(&mut self) -> u8 {
        let instr = self.mem.read_byte(self.reg.pc.val);
        self.reg.pc.increment();
        return instr;
    }

    fn zero(&mut self, op_code: u8) {
        match low_nibble(op_code) {
            0x0 => ops::nop(),
            0x6 => ops::ld_next_byte_to_reg(self.mem, &mut self.reg.pc, &mut self.reg.b),
            _ => return
        }
    }
}

struct Registers {
    a: Register<u8>, b: Register<u8>, c: Register<u8>,
    d: Register<u8>, e: Register<u8>, f: Register<u8>,
    h: Register<u8>, l: Register<u8>, // 8-bit registers

    pc: Register<u16>, sp: Register<u16>, // 16-bit registers
    m: Register<u16>, t: Register<u16> // clock
}

impl Registers {
    fn new() -> Registers {
        return Registers{
            a: Register::new(0),
            b: Register::new(0),
            c: Register::new(0),
            d: Register::new(0),
            e: Register::new(0),
            f: Register::new(0),
            h: Register::new(0),
            l: Register::new(0),

            pc: Register::new(0),
            sp: Register::new(0xFFFE),

            m: Register::new(0),
            t: Register::new(0)
        }
    }
}

pub struct Register<T: Copy + Unsigned> {
    val: T
}

impl<T: Copy + Unsigned> Register<T> {
    pub fn new(i: T) -> Register<T> {
        return Register { val: i };
    }

    pub fn read(&self) -> T {
        return self.val;
    }

    pub fn write(&mut self, i: T) {
        self.val = i;
    }

    pub fn increment(&mut self) {
        let i = self.val;
        self.write(i + One::one());
    }
}

#[test]
fn test_Register_new() {
    let reg: Register<u8> = Register::new(10);
    assert!(reg.val == 10);
}

fn test_Register_read() {
    let reg: Register<u32> = Register::new(5);

    assert!(reg.read() == 5);
}

fn test_Register_write() {
    let mut reg: Register<u16> = Register::new(483);

    reg.write(5);
    assert!(reg.val == 5);
}
<commit_msg>Add tests for Register and Registers<commit_after>use num::integer::Integer;
use memory::Memory;
use memory::low_nibble;
use memory::high_nibble;
use extensions::Incrementor;
use ops::{mod};
use std::num::One;

pub struct Cpu {
    reg: Registers,
    mem: Memory
}

impl Cpu {
    pub fn new(memory: Memory) -> Cpu {
        return Cpu{reg: Registers::new(), mem: memory}
    }

    /// Execute a cycle on the cpu
    pub fn tick(&mut self) {
        let instr = self.fetch_instruction();
        self.reg.pc.increment();

        match high_nibble(instr) {
            0x0 => self.zero(instr),
            _ => return
        }
    }

    /// Fetches the instruction pointed to by the program counter
    /// and increments the pc by 1
    fn fetch_instruction(&mut self) -> u8 {
        let instr = self.mem.read_byte(self.reg.pc.val);
        self.reg.pc.increment();
        return instr;
    }

    fn zero(&mut self, op_code: u8) {
        match low_nibble(op_code) {
            0x0 => ops::nop(),
            0x6 => ops::ld_next_byte_to_reg(self.mem, &mut self.reg.pc, &mut self.reg.b),
            _ => return
        }
    }
}

struct Registers {
    a: Register<u8>, b: Register<u8>, c: Register<u8>,
    d: Register<u8>, e: Register<u8>, f: Register<u8>,
    h: Register<u8>, l: Register<u8>, // 8-bit registers

    pc: Register<u16>, sp: Register<u16>, // 16-bit registers
    m: Register<u16>, t: Register<u16> // clock
}

impl Registers {
    fn new() -> Registers {
        return Registers{
            a: Register::new(0),
            b: Register::new(0),
            c: Register::new(0),
            d: Register::new(0),
            e: Register::new(0),
            f: Register::new(0),
            h: Register::new(0),
            l: Register::new(0),

            pc: Register::new(0x100),
            sp: Register::new(0xFFFE),

            m: Register::new(0),
            t: Register::new(0)
        }
    }
}

pub struct Register<T: Copy + Unsigned> {
    val: T
}

impl<T: Copy + Unsigned> Register<T> {
    pub fn new(i: T) -> Register<T> {
        return Register { val: i };
    }

    pub fn read(&self) -> T {
        return self.val;
    }

    pub fn write(&mut self, i: T) {
        self.val = i;
    }

    pub fn increment(&mut self) {
        let i = self.val;
        self.write(i + One::one());
    }
}

#[test]
fn test_Register_new() {
    let reg: Register<u8> = Register::new(10);
    assert!(reg.val == 10);
}

#[test]
fn test_Register_read() {
    let reg: Register<u32> = Register::new(5);
    assert!(reg.read() == 5);
}

#[test]
fn test_Register_write() {
    let mut reg: Register<u16> = Register::new(483);

    reg.write(5);
    assert!(reg.val == 5);
}

#[test]
fn test_Registers_new() {
    let a = Registers::new();
    assert!(a.pc.read() == 0x100);
    assert!(a.sp.read() == 0xFFFE);
}
<|endoftext|>


<commit_before>use instruction::Instruction;
use instruction::Instruction::AddSub;

#[derive(Debug)]
pub struct ARM7TDMI {
    reg_gpr: [u32; 16],

    cprs: PSR,

    reg_banked: [[u32; 7]; 6],
    sprs_banked: [PSR; 6],

    privilege_mode: PrivilegeMode,
    execution_mode: ExecutionMode,
}

impl ARM7TDMI {
    pub fn new() -> ARM7TDMI {
        let reg_gpr = [0x00010001; 16];
        let reg_banked = [[0xdeadbeef; 7]; 6];
        let sprs_banked = [PSR {
            n: false,
            z: false,
            c: false,
            v: false,
            i: false,
            f: false,
            t: false,
        }; 6];

        ARM7TDMI {
            reg_gpr: reg_gpr,
            cprs: PSR {
                n: false,
                z: false,
                c: false,
                v: false,
                i: false,
                f: false,
                t: false,
            },
            reg_banked: reg_banked,
            sprs_banked: sprs_banked,
            privilege_mode: PrivilegeMode::User,
            execution_mode: ExecutionMode::ARM,
        }
    }

    pub fn execute_instruction(&mut self, instruction: Instruction) {
        match instruction {
            AddSub { opcode, operand, source_register, destination_register } => {
                match opcode {
                    AddRegister => {
                        self.register_instruction(operand,
                                                  source_register,
                                                  destination_register,
                                                  |rs, rn| rs + rn)
                    }
                }
            }
            _ => (),
        }
    }

    fn read_register(&mut self, register: u8) -> u32 {
        self.reg_gpr[register as usize]
    }

    fn write_register(&mut self, register: u8, value: u32) {
        self.reg_gpr[register as usize] = value;
    }

    fn register_instruction<F>(&mut self, operand: u8, source: u8, destination: u8, f: F)
        where F: FnOnce(u32, u32) -> u32
    {
        let rs = self.read_register(source);
        let rn = self.read_register(operand);

        let result = f(rs, rn);

        self.write_register(destination, result);
    }
}

#[derive(Debug, Copy, Clone)]
struct PSR {
    n: bool,
    z: bool,
    c: bool,
    v: bool,
    i: bool,
    f: bool,
    t: bool,
}

#[derive(Debug)]
enum PrivilegeMode {
    User = 0x10,
    FIQ = 0x11,
    IRQ = 0x12,
    Supervisor = 0x13,
    Abort = 0x17,
    Undefined = 0x1B,
    System = 0x1F,
}

#[derive(Debug)]
enum ExecutionMode {
    ARM,
    THUMB,
}

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

    #[test]
    fn do_instruction() {
        let add_immediate = Instruction::AddSub {
            opcode: OpcodeAddSub::AddRegister,
            operand: 7,
            source_register: 1,
            destination_register: 0,
        };

        let mut cpu = ARM7TDMI::new();
        cpu.execute_instruction(add_immediate);

        println!("{:?}", cpu);
    }
}
<commit_msg>Implement remaining AddSub instructions<commit_after>use instruction::Instruction;
use instruction::Instruction::AddSub;

#[derive(Debug)]
pub struct ARM7TDMI {
    reg_gpr: [u32; 16],

    cprs: PSR,

    reg_banked: [[u32; 7]; 6],
    sprs_banked: [PSR; 6],

    privilege_mode: PrivilegeMode,
    execution_mode: ExecutionMode,
}

impl ARM7TDMI {
    pub fn new() -> ARM7TDMI {
        let reg_gpr = [0x00000011; 16];
        let reg_banked = [[0xdeadbeef; 7]; 6];
        let sprs_banked = [PSR {
            n: false,
            z: false,
            c: false,
            v: false,
            i: false,
            f: false,
            t: false,
        }; 6];

        ARM7TDMI {
            reg_gpr: reg_gpr,
            cprs: PSR {
                n: false,
                z: false,
                c: false,
                v: false,
                i: false,
                f: false,
                t: false,
            },
            reg_banked: reg_banked,
            sprs_banked: sprs_banked,
            privilege_mode: PrivilegeMode::User,
            execution_mode: ExecutionMode::ARM,
        }
    }

    pub fn execute_instruction(&mut self, instruction: Instruction) {
        match instruction {
            AddSub { opcode, operand, source_register, destination_register } => {
                use opcode::OpcodeAddSub::*;
                match opcode {
                    AddRegister => {
                        self.register_instruction(operand,
                                                  source_register,
                                                  destination_register,
                                                  |rs, rn| rs + rn)
                    }
                    SubRegister => {
                        self.register_instruction(operand,
                                                  source_register,
                                                  destination_register,
                                                  |rs, rn| rs - rn)
                    }
                    AddImmediate => {
                        self.immediate_instruction(operand,
                                                   source_register,
                                                   destination_register,
                                                   |rs, nn| rs + nn)
                    }
                    SubImmediate => {
                        self.immediate_instruction(operand,
                                                   source_register,
                                                   destination_register,
                                                   |rs, nn| rs - nn)
                    }
                }
            }
            _ => (),
        }
    }

    pub fn get_registers(&mut self) -> [u32; 16] {
        self.reg_gpr
    }

    fn read_register(&mut self, register: u8) -> u32 {
        self.reg_gpr[register as usize]
    }

    fn write_register(&mut self, register: u8, value: u32) {
        self.reg_gpr[register as usize] = value;
    }

    fn immediate_instruction<F>(&mut self, immediate: u8, source: u8, destination: u8, f: F)
        where F: FnOnce(u32, u32) -> u32
    {
        let rs = self.read_register(source);
        let nn = immediate as u32;

        let result = f(rs, nn);

        self.write_register(destination, result);
    }

    fn register_instruction<F>(&mut self, operand: u8, source: u8, destination: u8, f: F)
        where F: FnOnce(u32, u32) -> u32
    {
        let rs = self.read_register(source);
        let rn = self.read_register(operand);

        let result = f(rs, rn);

        self.write_register(destination, result);
    }
}

#[derive(Debug, Copy, Clone)]
struct PSR {
    n: bool,
    z: bool,
    c: bool,
    v: bool,
    i: bool,
    f: bool,
    t: bool,
}

#[derive(Debug)]
enum PrivilegeMode {
    User = 0x10,
    FIQ = 0x11,
    IRQ = 0x12,
    Supervisor = 0x13,
    Abort = 0x17,
    Undefined = 0x1B,
    System = 0x1F,
}

#[derive(Debug)]
enum ExecutionMode {
    ARM,
    THUMB,
}

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

    #[test]
    fn do_instruction() {
        let add_register = Instruction::AddSub {
            opcode: OpcodeAddSub::AddRegister,
            operand: 7,
            source_register: 10,
            destination_register: 0,
        };
        let sub_register = Instruction::AddSub {
            opcode: OpcodeAddSub::SubRegister,
            operand: 7,
            source_register: 10,
            destination_register: 1,
        };
        let add_immediate = Instruction::AddSub {
            opcode: OpcodeAddSub::AddImmediate,
            operand: 7,
            source_register: 10,
            destination_register: 2,
        };
        let sub_immediate = Instruction::AddSub {
            opcode: OpcodeAddSub::SubImmediate,
            operand: 1,
            source_register: 10,
            destination_register: 3,
        };

        let mut cpu = ARM7TDMI::new();
        cpu.execute_instruction(add_register);
        cpu.execute_instruction(sub_register);
        cpu.execute_instruction(add_immediate);
        cpu.execute_instruction(sub_immediate);

        let registers = cpu.get_registers();
        assert_eq!(registers[0], 34); // rd = rs + rn
        assert_eq!(registers[1], 0); // rd = rs - rn
        assert_eq!(registers[2], 24); // rd = rs + nn
        assert_eq!(registers[3], 16); // rd = rs - nn
    }
}
<|endoftext|>


<commit_before>//! Compiles the profiler part of the `compiler-rt` library.
//!
//! See the build.rs for libcompiler_builtins crate for details.

use std::env;
use std::path::Path;

fn main() {
    let target = env::var("TARGET").expect("TARGET was not set");
    let cfg = &mut cc::Build::new();

    let mut profile_sources = vec![
        "GCDAProfiling.c",
        "InstrProfiling.c",
        "InstrProfilingBuffer.c",
        "InstrProfilingFile.c",
        "InstrProfilingMerge.c",
        "InstrProfilingMergeFile.c",
        "InstrProfilingNameVar.c",
        "InstrProfilingPlatformDarwin.c",
        "InstrProfilingPlatformLinux.c",
        "InstrProfilingPlatformOther.c",
        "InstrProfilingPlatformWindows.c",
        "InstrProfilingUtil.c",
        "InstrProfilingValue.c",
        "InstrProfilingWriter.c",
    ];

    if target.contains("msvc") {
        // Don't pull in extra libraries on MSVC
        cfg.flag("/Zl");
        profile_sources.push("WindowsMMap.c");
        cfg.define("strdup", Some("_strdup"));
        cfg.define("open", Some("_open"));
        cfg.define("fdopen", Some("_fdopen"));
        cfg.define("getpid", Some("_getpid"));
        cfg.define("fileno", Some("_fileno"));
    } else {
        // Turn off various features of gcc and such, mostly copying
        // compiler-rt's build system already
        cfg.flag("-fno-builtin");
        cfg.flag("-fvisibility=hidden");
        cfg.flag("-fomit-frame-pointer");
        cfg.flag("-ffreestanding");
        cfg.define("VISIBILITY_HIDDEN", None);
        if !target.contains("windows") {
            cfg.define("COMPILER_RT_HAS_UNAME", Some("1"));
        } else {
            profile_sources.push("WindowsMMap.c");
        }
    }

    // Assume that the Unixes we are building this for have fnctl() available
    if env::var_os("CARGO_CFG_UNIX").is_some() {
        cfg.define("COMPILER_RT_HAS_FCNTL_LCK", Some("1"));
    }

    // This should be a pretty good heuristic for when to set
    // COMPILER_RT_HAS_ATOMICS
    if env::var_os("CARGO_CFG_TARGET_HAS_ATOMIC")
        .map(|features| features.to_string_lossy().to_lowercase().contains("cas"))
        .unwrap_or(false)
    {
        cfg.define("COMPILER_RT_HAS_ATOMICS", Some("1"));
    }

    let root = env::var_os("RUST_COMPILER_RT_ROOT").unwrap();
    let root = Path::new(&root);

    let src_root = root.join("lib").join("profile");
    for src in profile_sources {
        cfg.file(src_root.join(src));
    }

    // The file was renamed in LLVM 10.
    let old_runtime_path = src_root.join("InstrProfilingRuntime.cc");
    let new_runtime_path = src_root.join("InstrProfilingRuntime.cpp");
    cfg.file(if old_runtime_path.exists() { old_runtime_path } else { new_runtime_path });

    cfg.include(root.join("include"));
    cfg.warnings(false);
    cfg.compile("profiler-rt");
}
<commit_msg>Remove -ffreestanding from libprofiler_builtins because we do need the standard library<commit_after>//! Compiles the profiler part of the `compiler-rt` library.
//!
//! See the build.rs for libcompiler_builtins crate for details.

use std::env;
use std::path::Path;

fn main() {
    let target = env::var("TARGET").expect("TARGET was not set");
    let cfg = &mut cc::Build::new();

    let mut profile_sources = vec![
        "GCDAProfiling.c",
        "InstrProfiling.c",
        "InstrProfilingBuffer.c",
        "InstrProfilingFile.c",
        "InstrProfilingMerge.c",
        "InstrProfilingMergeFile.c",
        "InstrProfilingNameVar.c",
        "InstrProfilingPlatformDarwin.c",
        "InstrProfilingPlatformLinux.c",
        "InstrProfilingPlatformOther.c",
        "InstrProfilingPlatformWindows.c",
        "InstrProfilingUtil.c",
        "InstrProfilingValue.c",
        "InstrProfilingWriter.c",
    ];

    if target.contains("msvc") {
        // Don't pull in extra libraries on MSVC
        cfg.flag("/Zl");
        profile_sources.push("WindowsMMap.c");
        cfg.define("strdup", Some("_strdup"));
        cfg.define("open", Some("_open"));
        cfg.define("fdopen", Some("_fdopen"));
        cfg.define("getpid", Some("_getpid"));
        cfg.define("fileno", Some("_fileno"));
    } else {
        // Turn off various features of gcc and such, mostly copying
        // compiler-rt's build system already
        cfg.flag("-fno-builtin");
        cfg.flag("-fvisibility=hidden");
        cfg.flag("-fomit-frame-pointer");
        cfg.define("VISIBILITY_HIDDEN", None);
        if !target.contains("windows") {
            cfg.define("COMPILER_RT_HAS_UNAME", Some("1"));
        } else {
            profile_sources.push("WindowsMMap.c");
        }
    }

    // Assume that the Unixes we are building this for have fnctl() available
    if env::var_os("CARGO_CFG_UNIX").is_some() {
        cfg.define("COMPILER_RT_HAS_FCNTL_LCK", Some("1"));
    }

    // This should be a pretty good heuristic for when to set
    // COMPILER_RT_HAS_ATOMICS
    if env::var_os("CARGO_CFG_TARGET_HAS_ATOMIC")
        .map(|features| features.to_string_lossy().to_lowercase().contains("cas"))
        .unwrap_or(false)
    {
        cfg.define("COMPILER_RT_HAS_ATOMICS", Some("1"));
    }

    let root = env::var_os("RUST_COMPILER_RT_ROOT").unwrap();
    let root = Path::new(&root);

    let src_root = root.join("lib").join("profile");
    for src in profile_sources {
        cfg.file(src_root.join(src));
    }

    // The file was renamed in LLVM 10.
    let old_runtime_path = src_root.join("InstrProfilingRuntime.cc");
    let new_runtime_path = src_root.join("InstrProfilingRuntime.cpp");
    cfg.file(if old_runtime_path.exists() { old_runtime_path } else { new_runtime_path });

    cfg.include(root.join("include"));
    cfg.warnings(false);
    cfg.compile("profiler-rt");
}
<|endoftext|>