<commit_before>#![cfg_attr(feature = "cargo-clippy", allow(many_single_char_names, ptr_arg))]
// This is an implementation of the Sutherland-Hodgman (1974) reentrant polygon
// clipping algorithm. It takes a polygon represented as a number of vertices
// and cuts it according to the given planes.

// The implementation borrows heavily from Pucella-Tov (2008). See that paper
// for more explanation.

extern crate rand;
extern crate session_types;

use session_types::*;

use rand::{Rand, Rng};

use std::thread::spawn;

#[derive(Debug, Copy, Clone)]
struct Point(f64, f64, f64);

impl Rand for Point {
    fn rand<R: Rng>(rng: &mut R) -> Self {
        Point(rng.gen(), rng.gen(), rng.gen())
    }
}

#[derive(Debug, Copy, Clone)]
struct Plane(f64, f64, f64, f64);

impl Rand for Plane {
    fn rand<R: Rng>(rng: &mut R) -> Self {
        Plane(
            rng.gen(),
            rng.gen(),
            rng.gen(),
            rng.gen(),
        )
    }
}

fn above(Point(x, y, z): Point, Plane(a, b, c, d): Plane) -> bool {
    (a * x + b * y + c * z + d) / (a * a + b * b + c * c).sqrt() > 0.0
}

fn intersect(p1: Point, p2: Point, plane: Plane) -> Option<Point> {
    let Point(x1, y1, z1) = p1;
    let Point(x2, y2, z2) = p2;
    let Plane(a, b, c, d) = plane;

    if above(p1, plane) == above(p2, plane) {
        None
    } else {
        let t = (a * x1 + b * y1 + c * z1 + d) / (a * (x1 - x2) + b * (y1 - y2) + c * (z1 - z2));
        let x = x1 + (x2 - x1) * t;
        let y = y1 + (y2 - y1) * t;
        let z = z1 + (z2 - z1) * t;
        Some(Point(x, y, z))
    }
}

type SendList<A> = Rec<Choose<Eps, Send<A, Var<Z>>>>;
type RecvList<A> = Rec<Offer<Eps, Recv<A, Var<Z>>>>;

fn sendlist<A: std::marker::Send + Copy + 'static>(c: Chan<(), SendList<A>>, xs: &Vec<A>) {
    let mut c = c.enter();
    for x in xs {
        let c1 = c.sel2().send(*x);
        c = c1.zero();
    }
    c.sel1().close();
}

fn recvlist<A: std::marker::Send + 'static>(c: Chan<(), RecvList<A>>) -> Vec<A> {
    let mut v = Vec::new();
    let mut c = c.enter();
    loop {
        c = match c.offer() {
            Left(c) => {
                c.close();
                break;
            }
            Right(c) => {
                let (c, x) = c.recv();
                v.push(x);
                c.zero()
            }
        }
    }

    v
}

fn clipper(plane: Plane, ic: Chan<(), RecvList<Point>>, oc: Chan<(), SendList<Point>>) {
    let mut oc = oc.enter();
    let mut ic = ic.enter();
    let (pt0, mut pt);

    match ic.offer() {
        Left(c) => {
            c.close();
            oc.sel1().close();
            return;
        }
        Right(c) => {
            let (c, ptz) = c.recv();
            ic = c.zero();
            pt0 = ptz;
            pt = ptz;
        }
    }

    loop {
        if above(pt, plane) {
            oc = oc.sel2().send(pt).zero();
        }
        ic = match ic.offer() {
            Left(c) => {
                if let Some(pt) = intersect(pt, pt0, plane) {
                    oc = oc.sel2().send(pt).zero();
                }
                c.close();
                oc.sel1().close();
                break;
            }
            Right(ic) => {
                let (ic, pt2) = ic.recv();
                if let Some(pt) = intersect(pt, pt2, plane) {
                    oc = oc.sel2().send(pt).zero();
                }
                pt = pt2;
                ic.zero()
            }
        }
    }
}

fn clipmany(planes: Vec<Plane>, points: Vec<Point>) -> Vec<Point> {
    let (tx, rx) = session_channel();
    spawn(move || sendlist(tx, &points));
    let mut rx = rx;

    for plane in planes {
        let (tx2, rx2) = session_channel();
        spawn(move || clipper(plane, rx, tx2));
        rx = rx2;
    }
    recvlist(rx)
}

fn normalize_point(Point(a, b, c): Point) -> Point {
    Point(10.0 * (a - 0.5), 10.0 * (b - 0.5), 10.0 * (c - 0.5))
}

fn normalize_plane(Plane(a, b, c, d): Plane) -> Plane {
    Plane(
        10.0 * (a - 0.5),
        10.0 * (b - 0.5),
        10.0 * (c - 0.5),
        10.0 * (d - 0.5),
    )
}

fn bench(n: usize, m: usize) {
    let mut g = rand::thread_rng();
    let points = (0..n)
        .map(|_| rand::Rand::rand(&mut g))
        .map(normalize_point)
        .collect();
    let planes = (0..m)
        .map(|_| rand::Rand::rand(&mut g))
        .map(normalize_plane)
        .collect();

    let points = clipmany(planes, points);
    println!("{}", points.len());
}

fn main() {
    bench(100, 5);
}
<commit_msg>rand::distributions instead of rand::Rand<commit_after>#![cfg_attr(feature = "cargo-clippy", allow(many_single_char_names, ptr_arg))]
// This is an implementation of the Sutherland-Hodgman (1974) reentrant polygon
// clipping algorithm. It takes a polygon represented as a number of vertices
// and cuts it according to the given planes.

// The implementation borrows heavily from Pucella-Tov (2008). See that paper
// for more explanation.

extern crate rand;
extern crate session_types;

use session_types::*;

use rand::{random, Rng};
use rand::distributions::{Distribution, Standard};

use std::thread::spawn;

#[derive(Debug, Copy, Clone)]
struct Point(f64, f64, f64);

impl Distribution<Point> for Standard {
    fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Point {
        Point(rng.gen(), rng.gen(), rng.gen())
    }
}

#[derive(Debug, Copy, Clone)]
struct Plane(f64, f64, f64, f64);

impl Distribution<Plane> for Standard {
    fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Plane {
        Plane(
            rng.gen(),
            rng.gen(),
            rng.gen(),
            rng.gen(),
        )
    }
}

fn above(Point(x, y, z): Point, Plane(a, b, c, d): Plane) -> bool {
    (a * x + b * y + c * z + d) / (a * a + b * b + c * c).sqrt() > 0.0
}

fn intersect(p1: Point, p2: Point, plane: Plane) -> Option<Point> {
    let Point(x1, y1, z1) = p1;
    let Point(x2, y2, z2) = p2;
    let Plane(a, b, c, d) = plane;

    if above(p1, plane) == above(p2, plane) {
        None
    } else {
        let t = (a * x1 + b * y1 + c * z1 + d) / (a * (x1 - x2) + b * (y1 - y2) + c * (z1 - z2));
        let x = x1 + (x2 - x1) * t;
        let y = y1 + (y2 - y1) * t;
        let z = z1 + (z2 - z1) * t;
        Some(Point(x, y, z))
    }
}

type SendList<A> = Rec<Choose<Eps, Send<A, Var<Z>>>>;
type RecvList<A> = Rec<Offer<Eps, Recv<A, Var<Z>>>>;

fn sendlist<A: std::marker::Send + Copy + 'static>(c: Chan<(), SendList<A>>, xs: &Vec<A>) {
    let mut c = c.enter();
    for x in xs {
        let c1 = c.sel2().send(*x);
        c = c1.zero();
    }
    c.sel1().close();
}

fn recvlist<A: std::marker::Send + 'static>(c: Chan<(), RecvList<A>>) -> Vec<A> {
    let mut v = Vec::new();
    let mut c = c.enter();
    loop {
        c = match c.offer() {
            Left(c) => {
                c.close();
                break;
            }
            Right(c) => {
                let (c, x) = c.recv();
                v.push(x);
                c.zero()
            }
        }
    }

    v
}

fn clipper(plane: Plane, ic: Chan<(), RecvList<Point>>, oc: Chan<(), SendList<Point>>) {
    let mut oc = oc.enter();
    let mut ic = ic.enter();
    let (pt0, mut pt);

    match ic.offer() {
        Left(c) => {
            c.close();
            oc.sel1().close();
            return;
        }
        Right(c) => {
            let (c, ptz) = c.recv();
            ic = c.zero();
            pt0 = ptz;
            pt = ptz;
        }
    }

    loop {
        if above(pt, plane) {
            oc = oc.sel2().send(pt).zero();
        }
        ic = match ic.offer() {
            Left(c) => {
                if let Some(pt) = intersect(pt, pt0, plane) {
                    oc = oc.sel2().send(pt).zero();
                }
                c.close();
                oc.sel1().close();
                break;
            }
            Right(ic) => {
                let (ic, pt2) = ic.recv();
                if let Some(pt) = intersect(pt, pt2, plane) {
                    oc = oc.sel2().send(pt).zero();
                }
                pt = pt2;
                ic.zero()
            }
        }
    }
}

fn clipmany(planes: Vec<Plane>, points: Vec<Point>) -> Vec<Point> {
    let (tx, rx) = session_channel();
    spawn(move || sendlist(tx, &points));
    let mut rx = rx;

    for plane in planes {
        let (tx2, rx2) = session_channel();
        spawn(move || clipper(plane, rx, tx2));
        rx = rx2;
    }
    recvlist(rx)
}

fn normalize_point(Point(a, b, c): Point) -> Point {
    Point(10.0 * (a - 0.5), 10.0 * (b - 0.5), 10.0 * (c - 0.5))
}

fn normalize_plane(Plane(a, b, c, d): Plane) -> Plane {
    Plane(
        10.0 * (a - 0.5),
        10.0 * (b - 0.5),
        10.0 * (c - 0.5),
        10.0 * (d - 0.5),
    )
}

fn bench(n: usize, m: usize) {
    let points = (0..n)
        .map(|_| random())
        .map(normalize_point)
        .collect();
    let planes = (0..m)
        .map(|_| random())
        .map(normalize_plane)
        .collect();

    let points = clipmany(planes, points);
    println!("{}", points.len());
}

fn main() {
    bench(100, 5);
}
<|endoftext|>


<commit_before>//! CSS font properties.

use cssparser::{BasicParseError, Parser, Token};

use crate::drawing_ctx::ViewParams;
use crate::error::*;
use crate::length::*;
use crate::parsers::Parse;
use crate::properties::ComputedValues;

// https://www.w3.org/TR/2008/REC-CSS2-20080411/fonts.html#propdef-font-size
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum FontSizeSpec {
    Smaller,
    Larger,
    XXSmall,
    XSmall,
    Small,
    Medium,
    Large,
    XLarge,
    XXLarge,
    Value(Length<Both>),
}

impl FontSizeSpec {
    pub fn value(&self) -> Length<Both> {
        match self {
            FontSizeSpec::Value(s) => *s,
            _ => unreachable!(),
        }
    }

    pub fn compute(&self, v: &ComputedValues) -> Self {
        let compute_points = |p| 12.0 * 1.2f64.powf(p) / POINTS_PER_INCH;

        let size = v.font_size.0.value();

        let new_size = match self {
            FontSizeSpec::Smaller => Length::<Both>::new(size.length / 1.2, size.unit),
            FontSizeSpec::Larger => Length::<Both>::new(size.length * 1.2, size.unit),
            FontSizeSpec::XXSmall => Length::<Both>::new(compute_points(-3.0), LengthUnit::In),
            FontSizeSpec::XSmall => Length::<Both>::new(compute_points(-2.0), LengthUnit::In),
            FontSizeSpec::Small => Length::<Both>::new(compute_points(-1.0), LengthUnit::In),
            FontSizeSpec::Medium => Length::<Both>::new(compute_points(0.0), LengthUnit::In),
            FontSizeSpec::Large => Length::<Both>::new(compute_points(1.0), LengthUnit::In),
            FontSizeSpec::XLarge => Length::<Both>::new(compute_points(2.0), LengthUnit::In),
            FontSizeSpec::XXLarge => Length::<Both>::new(compute_points(3.0), LengthUnit::In),
            FontSizeSpec::Value(s) if s.unit == LengthUnit::Percent => {
                Length::<Both>::new(size.length * s.length, size.unit)
            }
            FontSizeSpec::Value(s) => *s,
        };

        FontSizeSpec::Value(new_size)
    }

    pub fn normalize(&self, values: &ComputedValues, params: &ViewParams) -> f64 {
        self.value().normalize(values, params)
    }
}

impl Parse for FontSizeSpec {
    fn parse(parser: &mut Parser<'_, '_>) -> Result<FontSizeSpec, crate::error::ValueErrorKind> {
        parser.try_parse(|p| Length::<Both>::parse(p))
            .and_then(|l| Ok(FontSizeSpec::Value(l)))
            .or_else(|_| {
                parse_identifiers!(
                    parser,
                    "smaller" => FontSizeSpec::Smaller,
                    "larger" => FontSizeSpec::Larger,
                    "xx-small" => FontSizeSpec::XXSmall,
                    "x-small" => FontSizeSpec::XSmall,
                    "small" => FontSizeSpec::Small,
                    "medium" => FontSizeSpec::Medium,
                    "large" => FontSizeSpec::Large,
                    "x-large" => FontSizeSpec::XLarge,
                    "xx-large" => FontSizeSpec::XXLarge,
                )
            }).map_err(|_: ParseError| ValueErrorKind::parse_error("parse error"))
    }
}

// https://www.w3.org/TR/2008/REC-CSS2-20080411/fonts.html#propdef-font-weight
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum FontWeightSpec {
    Normal,
    Bold,
    Bolder,
    Lighter,
    W100,
    W200,
    W300,
    W400,
    W500,
    W600,
    W700,
    W800,
    W900,
}

impl Parse for FontWeightSpec {
    fn parse(parser: &mut Parser<'_, '_>) -> Result<FontWeightSpec, crate::error::ValueErrorKind> {
        if let Ok(r) = parser.try_parse(|p| {
            p.expect_ident()
                .map_err(|_| ())
                .and_then(|cow| match cow.as_ref() {
                    "normal" => Ok(FontWeightSpec::Normal),
                    "bold" => Ok(FontWeightSpec::Bold),
                    "bolder" => Ok(FontWeightSpec::Bolder),
                    "lighter" => Ok(FontWeightSpec::Lighter),
                    _ => Err(()),
                })
        }) {
            return Ok(r);
        }

        if let Ok(r) = parser
            .expect_integer()
            .map_err(|_| ())
            .and_then(|i| match i {
                100 => Ok(FontWeightSpec::W100),
                200 => Ok(FontWeightSpec::W200),
                300 => Ok(FontWeightSpec::W300),
                400 => Ok(FontWeightSpec::W400),
                500 => Ok(FontWeightSpec::W500),
                600 => Ok(FontWeightSpec::W600),
                700 => Ok(FontWeightSpec::W700),
                800 => Ok(FontWeightSpec::W800),
                900 => Ok(FontWeightSpec::W900),
                _ => Err(()),
            })
        {
            Ok(r)
        } else {
            Err(ValueErrorKind::parse_error(
                "invalid font-weight specification",
            ))
        }
    }
}

// https://www.w3.org/TR/css-text-3/#letter-spacing-property
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum LetterSpacingSpec {
    Normal,
    Value(Length<Horizontal>),
}

impl LetterSpacingSpec {
    pub fn value(&self) -> Length<Horizontal> {
        match self {
            LetterSpacingSpec::Value(s) => *s,
            _ => unreachable!(),
        }
    }

    pub fn compute(&self) -> Self {
        let spacing = match self {
            LetterSpacingSpec::Normal => Length::<Horizontal>::new(0.0, LengthUnit::Px),
            LetterSpacingSpec::Value(s) => *s,
        };

        LetterSpacingSpec::Value(spacing)
    }

    pub fn normalize(&self, values: &ComputedValues, params: &ViewParams) -> f64 {
        self.value().normalize(values, params)
    }
}

impl Parse for LetterSpacingSpec {
    fn parse(
        parser: &mut Parser<'_, '_>,
    ) -> Result<LetterSpacingSpec, crate::error::ValueErrorKind> {
        let parser_state = parser.state();

        Length::<Horizontal>::parse(parser)
            .and_then(|s| Ok(LetterSpacingSpec::Value(s)))
            .or_else(|e| {
                parser.reset(&parser_state);

                {
                    let token = parser.next().map_err(|_| {
                        crate::error::ValueErrorKind::parse_error("expected token")
                    })?;

                    if let Token::Ident(ref cow) = token {
                        if let "normal" = cow.as_ref() {
                            return Ok(LetterSpacingSpec::Normal);
                        }
                    }
                }

                parser.reset(&parser_state);

                Err(e)
            })
    }
}

/// https://www.w3.org/TR/2008/REC-CSS2-20080411/fonts.html#propdef-font-family
#[derive(Debug, Clone, PartialEq)]
pub struct SingleFontFamily(pub String);

impl Parse for SingleFontFamily {
    fn parse(parser: &mut Parser<'_, '_>) -> Result<SingleFontFamily, ValueErrorKind> {
        parse_single_font_family(parser)
            .map_err(|_| ValueErrorKind::parse_error("expected font family"))
    }
}

fn parse_single_font_family<'i>(
    parser: &'i mut Parser<'_, '_>,
) -> Result<SingleFontFamily, BasicParseError<'i>> {
    if let Ok(cow) = parser.try_parse(|p| p.expect_string_cloned()) {
        return Ok(SingleFontFamily((*cow).to_owned()));
    }

    let first_ident = parser.expect_ident()?.clone();

    let mut value = first_ident.as_ref().to_owned();

    while let Ok(cow) = parser.try_parse(|p| p.expect_ident_cloned()) {
        value.push(' ');
        value.push_str(&cow);
    }

    Ok(SingleFontFamily(value))
}

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

    #[test]
    fn detects_invalid_invalid_font_size() {
        assert!(is_parse_error(&FontSizeSpec::parse_str("furlong")));
    }

    #[test]
    fn parses_font_weight() {
        assert_eq!(
            <FontWeightSpec as Parse>::parse_str("normal"),
            Ok(FontWeightSpec::Normal)
        );
        assert_eq!(
            <FontWeightSpec as Parse>::parse_str("bold"),
            Ok(FontWeightSpec::Bold)
        );
        assert_eq!(
            <FontWeightSpec as Parse>::parse_str("100"),
            Ok(FontWeightSpec::W100)
        );
    }

    #[test]
    fn detects_invalid_font_weight() {
        assert!(<FontWeightSpec as Parse>::parse_str("").is_err());
        assert!(<FontWeightSpec as Parse>::parse_str("strange").is_err());
        assert!(<FontWeightSpec as Parse>::parse_str("314").is_err());
        assert!(<FontWeightSpec as Parse>::parse_str("3.14").is_err());
    }

    #[test]
    fn parses_letter_spacing() {
        assert_eq!(
            <LetterSpacingSpec as Parse>::parse_str("normal"),
            Ok(LetterSpacingSpec::Normal)
        );
        assert_eq!(
            <LetterSpacingSpec as Parse>::parse_str("10em"),
            Ok(LetterSpacingSpec::Value(Length::<Horizontal>::new(
                10.0,
                LengthUnit::Em,
            )))
        );
    }

    #[test]
    fn computes_letter_spacing() {
        assert_eq!(
            <LetterSpacingSpec as Parse>::parse_str("normal").map(|s| s.compute()),
            Ok(LetterSpacingSpec::Value(Length::<Horizontal>::new(
                0.0,
                LengthUnit::Px,
            )))
        );
        assert_eq!(
            <LetterSpacingSpec as Parse>::parse_str("10em").map(|s| s.compute()),
            Ok(LetterSpacingSpec::Value(Length::<Horizontal>::new(
                10.0,
                LengthUnit::Em,
            )))
        );
    }

    #[test]
    fn detects_invalid_invalid_letter_spacing() {
        assert!(is_parse_error(&LetterSpacingSpec::parse_str("furlong")));
    }

    #[test]
    fn parses_font_family() {
        assert_eq!(
            <SingleFontFamily as Parse>::parse_str("'Hello world'"),
            Ok(SingleFontFamily("Hello world".to_owned()))
        );

        assert_eq!(
            <SingleFontFamily as Parse>::parse_str("\"Hello world\""),
            Ok(SingleFontFamily("Hello world".to_owned()))
        );

        assert_eq!(
            <SingleFontFamily as Parse>::parse_str("  Hello  world  "),
            Ok(SingleFontFamily("Hello world".to_owned()))
        );

        assert_eq!(
            <SingleFontFamily as Parse>::parse_str("Plonk"),
            Ok(SingleFontFamily("Plonk".to_owned()))
        );
    }

    #[test]
    fn detects_invalid_font_family() {
        assert!(<SingleFontFamily as Parse>::parse_str("").is_err());

        // assert!(<SingleFontFamily as Parse>::parse_str("''").is_err());

        assert!(<SingleFontFamily as Parse>::parse_str("42").is_err());
    }
}
<commit_msg>LetterSpacingSpec: simplify parsing<commit_after>//! CSS font properties.

use cssparser::{BasicParseError, Parser};

use crate::drawing_ctx::ViewParams;
use crate::error::*;
use crate::length::*;
use crate::parsers::Parse;
use crate::properties::ComputedValues;

// https://www.w3.org/TR/2008/REC-CSS2-20080411/fonts.html#propdef-font-size
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum FontSizeSpec {
    Smaller,
    Larger,
    XXSmall,
    XSmall,
    Small,
    Medium,
    Large,
    XLarge,
    XXLarge,
    Value(Length<Both>),
}

impl FontSizeSpec {
    pub fn value(&self) -> Length<Both> {
        match self {
            FontSizeSpec::Value(s) => *s,
            _ => unreachable!(),
        }
    }

    pub fn compute(&self, v: &ComputedValues) -> Self {
        let compute_points = |p| 12.0 * 1.2f64.powf(p) / POINTS_PER_INCH;

        let size = v.font_size.0.value();

        let new_size = match self {
            FontSizeSpec::Smaller => Length::<Both>::new(size.length / 1.2, size.unit),
            FontSizeSpec::Larger => Length::<Both>::new(size.length * 1.2, size.unit),
            FontSizeSpec::XXSmall => Length::<Both>::new(compute_points(-3.0), LengthUnit::In),
            FontSizeSpec::XSmall => Length::<Both>::new(compute_points(-2.0), LengthUnit::In),
            FontSizeSpec::Small => Length::<Both>::new(compute_points(-1.0), LengthUnit::In),
            FontSizeSpec::Medium => Length::<Both>::new(compute_points(0.0), LengthUnit::In),
            FontSizeSpec::Large => Length::<Both>::new(compute_points(1.0), LengthUnit::In),
            FontSizeSpec::XLarge => Length::<Both>::new(compute_points(2.0), LengthUnit::In),
            FontSizeSpec::XXLarge => Length::<Both>::new(compute_points(3.0), LengthUnit::In),
            FontSizeSpec::Value(s) if s.unit == LengthUnit::Percent => {
                Length::<Both>::new(size.length * s.length, size.unit)
            }
            FontSizeSpec::Value(s) => *s,
        };

        FontSizeSpec::Value(new_size)
    }

    pub fn normalize(&self, values: &ComputedValues, params: &ViewParams) -> f64 {
        self.value().normalize(values, params)
    }
}

impl Parse for FontSizeSpec {
    fn parse(parser: &mut Parser<'_, '_>) -> Result<FontSizeSpec, crate::error::ValueErrorKind> {
        parser.try_parse(|p| Length::<Both>::parse(p))
            .and_then(|l| Ok(FontSizeSpec::Value(l)))
            .or_else(|_| {
                parse_identifiers!(
                    parser,
                    "smaller" => FontSizeSpec::Smaller,
                    "larger" => FontSizeSpec::Larger,
                    "xx-small" => FontSizeSpec::XXSmall,
                    "x-small" => FontSizeSpec::XSmall,
                    "small" => FontSizeSpec::Small,
                    "medium" => FontSizeSpec::Medium,
                    "large" => FontSizeSpec::Large,
                    "x-large" => FontSizeSpec::XLarge,
                    "xx-large" => FontSizeSpec::XXLarge,
                )
            }).map_err(|_: ParseError| ValueErrorKind::parse_error("parse error"))
    }
}

// https://www.w3.org/TR/2008/REC-CSS2-20080411/fonts.html#propdef-font-weight
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum FontWeightSpec {
    Normal,
    Bold,
    Bolder,
    Lighter,
    W100,
    W200,
    W300,
    W400,
    W500,
    W600,
    W700,
    W800,
    W900,
}

impl Parse for FontWeightSpec {
    fn parse(parser: &mut Parser<'_, '_>) -> Result<FontWeightSpec, crate::error::ValueErrorKind> {
        if let Ok(r) = parser.try_parse(|p| {
            p.expect_ident()
                .map_err(|_| ())
                .and_then(|cow| match cow.as_ref() {
                    "normal" => Ok(FontWeightSpec::Normal),
                    "bold" => Ok(FontWeightSpec::Bold),
                    "bolder" => Ok(FontWeightSpec::Bolder),
                    "lighter" => Ok(FontWeightSpec::Lighter),
                    _ => Err(()),
                })
        }) {
            return Ok(r);
        }

        if let Ok(r) = parser
            .expect_integer()
            .map_err(|_| ())
            .and_then(|i| match i {
                100 => Ok(FontWeightSpec::W100),
                200 => Ok(FontWeightSpec::W200),
                300 => Ok(FontWeightSpec::W300),
                400 => Ok(FontWeightSpec::W400),
                500 => Ok(FontWeightSpec::W500),
                600 => Ok(FontWeightSpec::W600),
                700 => Ok(FontWeightSpec::W700),
                800 => Ok(FontWeightSpec::W800),
                900 => Ok(FontWeightSpec::W900),
                _ => Err(()),
            })
        {
            Ok(r)
        } else {
            Err(ValueErrorKind::parse_error(
                "invalid font-weight specification",
            ))
        }
    }
}

// https://www.w3.org/TR/css-text-3/#letter-spacing-property
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum LetterSpacingSpec {
    Normal,
    Value(Length<Horizontal>),
}

impl LetterSpacingSpec {
    pub fn value(&self) -> Length<Horizontal> {
        match self {
            LetterSpacingSpec::Value(s) => *s,
            _ => unreachable!(),
        }
    }

    pub fn compute(&self) -> Self {
        let spacing = match self {
            LetterSpacingSpec::Normal => Length::<Horizontal>::new(0.0, LengthUnit::Px),
            LetterSpacingSpec::Value(s) => *s,
        };

        LetterSpacingSpec::Value(spacing)
    }

    pub fn normalize(&self, values: &ComputedValues, params: &ViewParams) -> f64 {
        self.value().normalize(values, params)
    }
}

impl Parse for LetterSpacingSpec {
    fn parse(
        parser: &mut Parser<'_, '_>,
    ) -> Result<LetterSpacingSpec, crate::error::ValueErrorKind> {
        parser.try_parse(|p| Length::<Horizontal>::parse(p))
            .and_then(|l| Ok(LetterSpacingSpec::Value(l)))
            .or_else(|_| {
                parse_identifiers!(
                    parser,
                    "normal" => LetterSpacingSpec::Normal,
                )
            }).map_err(|_: ParseError| ValueErrorKind::parse_error("parse error"))
    }
}

/// https://www.w3.org/TR/2008/REC-CSS2-20080411/fonts.html#propdef-font-family
#[derive(Debug, Clone, PartialEq)]
pub struct SingleFontFamily(pub String);

impl Parse for SingleFontFamily {
    fn parse(parser: &mut Parser<'_, '_>) -> Result<SingleFontFamily, ValueErrorKind> {
        parse_single_font_family(parser)
            .map_err(|_| ValueErrorKind::parse_error("expected font family"))
    }
}

fn parse_single_font_family<'i>(
    parser: &'i mut Parser<'_, '_>,
) -> Result<SingleFontFamily, BasicParseError<'i>> {
    if let Ok(cow) = parser.try_parse(|p| p.expect_string_cloned()) {
        return Ok(SingleFontFamily((*cow).to_owned()));
    }

    let first_ident = parser.expect_ident()?.clone();

    let mut value = first_ident.as_ref().to_owned();

    while let Ok(cow) = parser.try_parse(|p| p.expect_ident_cloned()) {
        value.push(' ');
        value.push_str(&cow);
    }

    Ok(SingleFontFamily(value))
}

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

    #[test]
    fn detects_invalid_invalid_font_size() {
        assert!(is_parse_error(&FontSizeSpec::parse_str("furlong")));
    }

    #[test]
    fn parses_font_weight() {
        assert_eq!(
            <FontWeightSpec as Parse>::parse_str("normal"),
            Ok(FontWeightSpec::Normal)
        );
        assert_eq!(
            <FontWeightSpec as Parse>::parse_str("bold"),
            Ok(FontWeightSpec::Bold)
        );
        assert_eq!(
            <FontWeightSpec as Parse>::parse_str("100"),
            Ok(FontWeightSpec::W100)
        );
    }

    #[test]
    fn detects_invalid_font_weight() {
        assert!(<FontWeightSpec as Parse>::parse_str("").is_err());
        assert!(<FontWeightSpec as Parse>::parse_str("strange").is_err());
        assert!(<FontWeightSpec as Parse>::parse_str("314").is_err());
        assert!(<FontWeightSpec as Parse>::parse_str("3.14").is_err());
    }

    #[test]
    fn parses_letter_spacing() {
        assert_eq!(
            <LetterSpacingSpec as Parse>::parse_str("normal"),
            Ok(LetterSpacingSpec::Normal)
        );
        assert_eq!(
            <LetterSpacingSpec as Parse>::parse_str("10em"),
            Ok(LetterSpacingSpec::Value(Length::<Horizontal>::new(
                10.0,
                LengthUnit::Em,
            )))
        );
    }

    #[test]
    fn computes_letter_spacing() {
        assert_eq!(
            <LetterSpacingSpec as Parse>::parse_str("normal").map(|s| s.compute()),
            Ok(LetterSpacingSpec::Value(Length::<Horizontal>::new(
                0.0,
                LengthUnit::Px,
            )))
        );
        assert_eq!(
            <LetterSpacingSpec as Parse>::parse_str("10em").map(|s| s.compute()),
            Ok(LetterSpacingSpec::Value(Length::<Horizontal>::new(
                10.0,
                LengthUnit::Em,
            )))
        );
    }

    #[test]
    fn detects_invalid_invalid_letter_spacing() {
        assert!(is_parse_error(&LetterSpacingSpec::parse_str("furlong")));
    }

    #[test]
    fn parses_font_family() {
        assert_eq!(
            <SingleFontFamily as Parse>::parse_str("'Hello world'"),
            Ok(SingleFontFamily("Hello world".to_owned()))
        );

        assert_eq!(
            <SingleFontFamily as Parse>::parse_str("\"Hello world\""),
            Ok(SingleFontFamily("Hello world".to_owned()))
        );

        assert_eq!(
            <SingleFontFamily as Parse>::parse_str("  Hello  world  "),
            Ok(SingleFontFamily("Hello world".to_owned()))
        );

        assert_eq!(
            <SingleFontFamily as Parse>::parse_str("Plonk"),
            Ok(SingleFontFamily("Plonk".to_owned()))
        );
    }

    #[test]
    fn detects_invalid_font_family() {
        assert!(<SingleFontFamily as Parse>::parse_str("").is_err());

        // assert!(<SingleFontFamily as Parse>::parse_str("''").is_err());

        assert!(<SingleFontFamily as Parse>::parse_str("42").is_err());
    }
}
<|endoftext|>


<commit_before>
use wca::*;
use wstring_tools::string::parse::OpType::{ Primitive/* , Vector */ };
use wtest_basic::*;
use std::collections::HashMap;

//

fn _basic()
{
  let instruction = instruction::instruction_parse()
  .instruction( "" )
  .perform();
  let exp = instruction::Instruction
  {
    err : Some( wtools::error::Error::new( "Invalid command" ) ),
    command_name : "".to_string(),
    subject : "".to_string(),
    properties_map : HashMap::new(),
  };
  assert_eq!( instruction, exp );

  let instruction = instruction::instruction_parse()
  .instruction( ".get" )
  .perform();
  let exp = instruction::Instruction
  {
    err : None,
    command_name : ".get".to_string(),
    subject : "".to_string(),
    properties_map : HashMap::new(),
  };
  assert_eq!( instruction, exp );

  let instruction = instruction::instruction_parse()
  .instruction( ".get some" )
  .perform();
  let exp = instruction::Instruction
  {
    err : None,
    command_name : ".get".to_string(),
    subject : "some".to_string(),
    properties_map : HashMap::new(),
  };
  assert_eq!( instruction, exp );

  let instruction = instruction::instruction_parse()
  .instruction( ".get v:1" )
  .perform();
  let exp = instruction::Instruction
  {
    err : None,
    command_name : ".get".to_string(),
    subject : "".to_string(),
    properties_map : HashMap::from([ ( "v".to_string(), Primitive( "1".to_string() ) ) ]),
  };
  assert_eq!( instruction, exp );

  let instruction = instruction::instruction_parse()
  .instruction( ".get some v:1" )
  .perform();
  let exp = instruction::Instruction
  {
    err : None,
    command_name : ".get".to_string(),
    subject : "some".to_string(),
    properties_map : HashMap::from([ ( "v".to_string(), Primitive( "1".to_string() ) ) ]),
  };
  assert_eq!( instruction, exp );

  let instruction = instruction::instruction_parse()
  .instruction( ".get some v:1 routine:some" )
  .perform();
  let properties_map = HashMap::from
  ([
    ( "v".to_string(), Primitive( "1".to_string() ) ),
    ( "routine".to_string(), Primitive( "some".to_string() ) ),
  ]);
  let exp = instruction::Instruction
  {
    err : None,
    command_name : ".get".to_string(),
    subject : "some".to_string(),
    properties_map,
  };
  assert_eq!( instruction, exp );

  /* */

  let aggregator_map = HashMap::new();
  let instruction = instruction::instruction_parse()
  .instruction( ".get some v:1 routine:some" )
  .properties_map( aggregator_map )
  .perform();
  let properties_map = HashMap::from
  ([
    ( "v".to_string(), Primitive( "1".to_string() ) ),
    ( "routine".to_string(), Primitive( "some".to_string() ) ),
  ]);
  let exp = instruction::Instruction
  {
    err : None,
    command_name : ".get".to_string(),
    subject : "some".to_string(),
    properties_map,
  };
  assert_eq!( instruction, exp );

  let mut aggregator_map = HashMap::new();
  aggregator_map.insert( "ne".to_string(), Primitive( "-2".to_string() ) );
  let instruction = instruction::instruction_parse()
  .instruction( ".get some v:1 routine:some" )
  .properties_map( aggregator_map )
  .perform();
  let properties_map = HashMap::from
  ([
    ( "v".to_string(), Primitive( "1".to_string() ) ),
    ( "routine".to_string(), Primitive( "some".to_string() ) ),
    ( "ne".to_string(), Primitive( "-2".to_string() ) ),
  ]);
  let exp = instruction::Instruction
  {
    err : None,
    command_name : ".get".to_string(),
    subject : "some".to_string(),
    properties_map,
  };
  assert_eq!( instruction, exp );
}

//

test_suite!
{
  basic,
}
<commit_msg>Write test routine `with_several_values`<commit_after>
use wca::*;
use wstring_tools::string::parse::OpType::{ Primitive, Vector };
use wtest_basic::*;
use std::collections::HashMap;

//

fn _basic()
{
  let instruction = instruction::instruction_parse()
  .instruction( "" )
  .perform();
  let exp = instruction::Instruction
  {
    err : Some( wtools::error::Error::new( "Invalid command" ) ),
    command_name : "".to_string(),
    subject : "".to_string(),
    properties_map : HashMap::new(),
  };
  assert_eq!( instruction, exp );

  let instruction = instruction::instruction_parse()
  .instruction( ".get" )
  .perform();
  let exp = instruction::Instruction
  {
    err : None,
    command_name : ".get".to_string(),
    subject : "".to_string(),
    properties_map : HashMap::new(),
  };
  assert_eq!( instruction, exp );

  let instruction = instruction::instruction_parse()
  .instruction( ".get some" )
  .perform();
  let exp = instruction::Instruction
  {
    err : None,
    command_name : ".get".to_string(),
    subject : "some".to_string(),
    properties_map : HashMap::new(),
  };
  assert_eq!( instruction, exp );

  let instruction = instruction::instruction_parse()
  .instruction( ".get v:1" )
  .perform();
  let exp = instruction::Instruction
  {
    err : None,
    command_name : ".get".to_string(),
    subject : "".to_string(),
    properties_map : HashMap::from([ ( "v".to_string(), Primitive( "1".to_string() ) ) ]),
  };
  assert_eq!( instruction, exp );

  let instruction = instruction::instruction_parse()
  .instruction( ".get some v:1" )
  .perform();
  let exp = instruction::Instruction
  {
    err : None,
    command_name : ".get".to_string(),
    subject : "some".to_string(),
    properties_map : HashMap::from([ ( "v".to_string(), Primitive( "1".to_string() ) ) ]),
  };
  assert_eq!( instruction, exp );

  let instruction = instruction::instruction_parse()
  .instruction( ".get some v:1 routine:some" )
  .perform();
  let properties_map = HashMap::from
  ([
    ( "v".to_string(), Primitive( "1".to_string() ) ),
    ( "routine".to_string(), Primitive( "some".to_string() ) ),
  ]);
  let exp = instruction::Instruction
  {
    err : None,
    command_name : ".get".to_string(),
    subject : "some".to_string(),
    properties_map,
  };
  assert_eq!( instruction, exp );

  /* */

  let aggregator_map = HashMap::new();
  let instruction = instruction::instruction_parse()
  .instruction( ".get some v:1 routine:some" )
  .properties_map( aggregator_map )
  .perform();
  let properties_map = HashMap::from
  ([
    ( "v".to_string(), Primitive( "1".to_string() ) ),
    ( "routine".to_string(), Primitive( "some".to_string() ) ),
  ]);
  let exp = instruction::Instruction
  {
    err : None,
    command_name : ".get".to_string(),
    subject : "some".to_string(),
    properties_map,
  };
  assert_eq!( instruction, exp );

  let mut aggregator_map = HashMap::new();
  aggregator_map.insert( "ne".to_string(), Primitive( "-2".to_string() ) );
  let instruction = instruction::instruction_parse()
  .instruction( ".get some v:1 routine:some" )
  .properties_map( aggregator_map )
  .perform();
  let properties_map = HashMap::from
  ([
    ( "v".to_string(), Primitive( "1".to_string() ) ),
    ( "routine".to_string(), Primitive( "some".to_string() ) ),
    ( "ne".to_string(), Primitive( "-2".to_string() ) ),
  ]);
  let exp = instruction::Instruction
  {
    err : None,
    command_name : ".get".to_string(),
    subject : "some".to_string(),
    properties_map,
  };
  assert_eq!( instruction, exp );
}

fn _with_several_values()
{
  let instruction = instruction::instruction_parse()
  .instruction( ".get some v:1 v:2" )
  .several_values( false )
  .perform();
  let exp = instruction::Instruction
  {
    err : None,
    command_name : ".get".to_string(),
    subject : "some".to_string(),
    properties_map : HashMap::from([ ( "v".to_string(), Primitive( "2".to_string() ) ) ]),
  };
  assert_eq!( instruction, exp );

  let instruction = instruction::instruction_parse()
  .instruction( ".get some v:[1,2]" )
  .several_values( false )
  .perform();
  let exp = instruction::Instruction
  {
    err : None,
    command_name : ".get".to_string(),
    subject : "some".to_string(),
    properties_map : HashMap::from([ ( "v".to_string(), Vector( vec![ "1".to_string(), "2".to_string() ] ) ) ]),
  };
  assert_eq!( instruction, exp );

  /* */

  let instruction = instruction::instruction_parse()
  .instruction( ".get some v:1 v:2" )
  .several_values( true )
  .perform();
  let exp = instruction::Instruction
  {
    err : None,
    command_name : ".get".to_string(),
    subject : "some".to_string(),
    properties_map : HashMap::from([ ( "v".to_string(), Vector( vec![ "1".to_string(), "2".to_string() ] ) ) ]),
  };
  assert_eq!( instruction, exp );

  let instruction = instruction::instruction_parse()
  .instruction( ".get some v:[1,2]" )
  .several_values( true )
  .perform();
  let exp = instruction::Instruction
  {
    err : None,
    command_name : ".get".to_string(),
    subject : "some".to_string(),
    properties_map : HashMap::from([ ( "v".to_string(), Vector( vec![ "1".to_string(), "2".to_string() ] ) ) ]),
  };
  assert_eq!( instruction, exp );

  /* */

  let instruction = instruction::instruction_parse()
  .instruction( ".get some v:[1,2] v:3" )
  .several_values( true )
  .perform();
  let exp = instruction::Instruction
  {
    err : None,
    command_name : ".get".to_string(),
    subject : "some".to_string(),
    properties_map : HashMap::from([ ( "v".to_string(), Vector( vec![ "1".to_string(), "2".to_string(), "3".to_string() ] ) ) ]),
  };
  assert_eq!( instruction, exp );

  let instruction = instruction::instruction_parse()
  .instruction( ".get some v:3 v:[1,2]" )
  .several_values( true )
  .perform();
  let exp = instruction::Instruction
  {
    err : None,
    command_name : ".get".to_string(),
    subject : "some".to_string(),
    properties_map : HashMap::from([ ( "v".to_string(), Vector( vec![ "3".to_string(), "1".to_string(), "2".to_string() ] ) ) ]),
  };
  assert_eq!( instruction, exp );
}

//

test_suite!
{
  basic,
  with_several_values,
}
<|endoftext|>


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

use codemap::{Pos, span};
use codemap;

use std::io;
use std::uint;
use extra::term;

pub type Emitter = @fn(cmsp: Option<(@codemap::CodeMap, span)>,
                       msg: &str,
                       lvl: level);

// a handler deals with errors; certain errors
// (fatal, bug, unimpl) may cause immediate exit,
// others log errors for later reporting.
pub trait handler {
    fn fatal(@mut self, msg: &str) -> !;
    fn err(@mut self, msg: &str);
    fn bump_err_count(@mut self);
    fn err_count(@mut self) -> uint;
    fn has_errors(@mut self) -> bool;
    fn abort_if_errors(@mut self);
    fn warn(@mut self, msg: &str);
    fn note(@mut self, msg: &str);
    // used to indicate a bug in the compiler:
    fn bug(@mut self, msg: &str) -> !;
    fn unimpl(@mut self, msg: &str) -> !;
    fn emit(@mut self,
            cmsp: Option<(@codemap::CodeMap, span)>,
            msg: &str,
            lvl: level);
}

// a span-handler is like a handler but also
// accepts span information for source-location
// reporting.
pub trait span_handler {
    fn span_fatal(@mut self, sp: span, msg: &str) -> !;
    fn span_err(@mut self, sp: span, msg: &str);
    fn span_warn(@mut self, sp: span, msg: &str);
    fn span_note(@mut self, sp: span, msg: &str);
    fn span_bug(@mut self, sp: span, msg: &str) -> !;
    fn span_unimpl(@mut self, sp: span, msg: &str) -> !;
    fn handler(@mut self) -> @handler;
}

struct HandlerT {
    err_count: uint,
    emit: Emitter,
}

struct CodemapT {
    handler: @handler,
    cm: @codemap::CodeMap,
}

impl span_handler for CodemapT {
    fn span_fatal(@mut self, sp: span, msg: &str) -> ! {
        self.handler.emit(Some((self.cm, sp)), msg, fatal);
        fail!();
    }
    fn span_err(@mut self, sp: span, msg: &str) {
        self.handler.emit(Some((self.cm, sp)), msg, error);
        self.handler.bump_err_count();
    }
    fn span_warn(@mut self, sp: span, msg: &str) {
        self.handler.emit(Some((self.cm, sp)), msg, warning);
    }
    fn span_note(@mut self, sp: span, msg: &str) {
        self.handler.emit(Some((self.cm, sp)), msg, note);
    }
    fn span_bug(@mut self, sp: span, msg: &str) -> ! {
        self.span_fatal(sp, ice_msg(msg));
    }
    fn span_unimpl(@mut self, sp: span, msg: &str) -> ! {
        self.span_bug(sp, ~"unimplemented " + msg);
    }
    fn handler(@mut self) -> @handler {
        self.handler
    }
}

impl handler for HandlerT {
    fn fatal(@mut self, msg: &str) -> ! {
        (self.emit)(None, msg, fatal);
        fail!();
    }
    fn err(@mut self, msg: &str) {
        (self.emit)(None, msg, error);
        self.bump_err_count();
    }
    fn bump_err_count(@mut self) {
        self.err_count += 1u;
    }
    fn err_count(@mut self) -> uint {
        self.err_count
    }
    fn has_errors(@mut self) -> bool {
        self.err_count > 0u
    }
    fn abort_if_errors(@mut self) {
        let s;
        match self.err_count {
          0u => return,
          1u => s = ~"aborting due to previous error",
          _  => {
            s = fmt!("aborting due to %u previous errors",
                     self.err_count);
          }
        }
        self.fatal(s);
    }
    fn warn(@mut self, msg: &str) {
        (self.emit)(None, msg, warning);
    }
    fn note(@mut self, msg: &str) {
        (self.emit)(None, msg, note);
    }
    fn bug(@mut self, msg: &str) -> ! {
        self.fatal(ice_msg(msg));
    }
    fn unimpl(@mut self, msg: &str) -> ! {
        self.bug(~"unimplemented " + msg);
    }
    fn emit(@mut self,
            cmsp: Option<(@codemap::CodeMap, span)>,
            msg: &str,
            lvl: level) {
        (self.emit)(cmsp, msg, lvl);
    }
}

pub fn ice_msg(msg: &str) -> ~str {
    fmt!("internal compiler error: %s", msg)
}

pub fn mk_span_handler(handler: @handler, cm: @codemap::CodeMap)
                    -> @span_handler {
    @mut CodemapT { handler: handler, cm: cm } as @span_handler
}

pub fn mk_handler(emitter: Option<Emitter>) -> @handler {
    let emit: Emitter = match emitter {
        Some(e) => e,
        None => {
            let emit: Emitter = |cmsp, msg, t| emit(cmsp, msg, t);
            emit
        }
    };

    @mut HandlerT { err_count: 0, emit: emit } as @handler
}

#[deriving(Eq)]
pub enum level {
    fatal,
    error,
    warning,
    note,
}

fn diagnosticstr(lvl: level) -> ~str {
    match lvl {
        fatal => ~"error",
        error => ~"error",
        warning => ~"warning",
        note => ~"note"
    }
}

fn diagnosticcolor(lvl: level) -> term::color::Color {
    match lvl {
        fatal => term::color::BRIGHT_RED,
        error => term::color::BRIGHT_RED,
        warning => term::color::BRIGHT_YELLOW,
        note => term::color::BRIGHT_GREEN
    }
}

fn print_maybe_colored(msg: &str, color: term::color::Color) {
    let stderr = io::stderr();

    let t = term::Terminal::new(stderr);

    match t {
        Ok(term) => {
            if stderr.get_type() == io::Screen {
                term.fg(color);
                stderr.write_str(msg);
                term.reset();
            } else {
                stderr.write_str(msg);
            }
        },
        _ => stderr.write_str(msg)
    }
}

fn print_diagnostic(topic: &str, lvl: level, msg: &str) {
    let stderr = io::stderr();

    if !topic.is_empty() {
        stderr.write_str(fmt!("%s ", topic));
    }

    print_maybe_colored(fmt!("%s: ", diagnosticstr(lvl)), diagnosticcolor(lvl));
    stderr.write_str(fmt!("%s\n", msg));
}

pub fn collect(messages: @mut ~[~str])
            -> @fn(Option<(@codemap::CodeMap, span)>, &str, level) {
    let f: @fn(Option<(@codemap::CodeMap, span)>, &str, level) =
        |_o, msg: &str, _l| { messages.push(msg.to_str()); };
    f
}

pub fn emit(cmsp: Option<(@codemap::CodeMap, span)>, msg: &str, lvl: level) {
    match cmsp {
      Some((cm, sp)) => {
        let sp = cm.adjust_span(sp);
        let ss = cm.span_to_str(sp);
        let lines = cm.span_to_lines(sp);
        print_diagnostic(ss, lvl, msg);
        highlight_lines(cm, sp, lvl, lines);
        print_macro_backtrace(cm, sp);
      }
      None => {
        print_diagnostic("", lvl, msg);
      }
    }
}

fn highlight_lines(cm: @codemap::CodeMap,
                   sp: span, lvl: level,
                   lines: @codemap::FileLines) {
    let fm = lines.file;

    // arbitrarily only print up to six lines of the error
    let max_lines = 6u;
    let mut elided = false;
    let mut display_lines = /* FIXME (#2543) */ copy lines.lines;
    if display_lines.len() > max_lines {
        display_lines = display_lines.slice(0u, max_lines).to_owned();
        elided = true;
    }
    // Print the offending lines
    for display_lines.iter().advance |line| {
        io::stderr().write_str(fmt!("%s:%u ", fm.name, *line + 1u));
        let s = fm.get_line(*line as int) + "\n";
        io::stderr().write_str(s);
    }
    if elided {
        let last_line = display_lines[display_lines.len() - 1u];
        let s = fmt!("%s:%u ", fm.name, last_line + 1u);
        let mut indent = s.len();
        let mut out = ~"";
        while indent > 0u {
            out.push_char(' ');
            indent -= 1u;
        }
        out.push_str("...\n");
        io::stderr().write_str(out);
    }

    // FIXME (#3260)
    // If there's one line at fault we can easily point to the problem
    if lines.lines.len() == 1u {
        let lo = cm.lookup_char_pos(sp.lo);
        let mut digits = 0u;
        let mut num = (lines.lines[0] + 1u) / 10u;

        // how many digits must be indent past?
        while num > 0u { num /= 10u; digits += 1u; }

        // indent past |name:## | and the 0-offset column location
        let left = fm.name.len() + digits + lo.col.to_uint() + 3u;
        let mut s = ~"";
        // Skip is the number of characters we need to skip because they are
        // part of the 'filename:line ' part of the previous line.
        let skip = fm.name.len() + digits + 3u;
        for skip.times() {
            s.push_char(' ');
        }
        let orig = fm.get_line(lines.lines[0] as int);
        for uint::range(0u,left-skip) |pos| {
            let curChar = (orig[pos] as char);
            // Whenever a tab occurs on the previous line, we insert one on
            // the error-point-squiggly-line as well (instead of a space).
            // That way the squiggly line will usually appear in the correct
            // position.
            match curChar {
                '\t' => s.push_char('\t'),
                _ => s.push_char(' '),
            };
        }
        io::stderr().write_str(s);
        let mut s = ~"^";
        let hi = cm.lookup_char_pos(sp.hi);
        if hi.col != lo.col {
            // the ^ already takes up one space
            let num_squigglies = hi.col.to_uint()-lo.col.to_uint()-1u;
            for num_squigglies.times() {
                s.push_char('~')
            }
        }
        print_maybe_colored(s + "\n", diagnosticcolor(lvl));
    }
}

fn print_macro_backtrace(cm: @codemap::CodeMap, sp: span) {
    for sp.expn_info.iter().advance |ei| {
        let ss = ei.callee.span.map_default(~"", |span| cm.span_to_str(*span));
        print_diagnostic(ss, note,
                         fmt!("in expansion of %s!", ei.callee.name));
        let ss = cm.span_to_str(ei.call_site);
        print_diagnostic(ss, note, "expansion site");
        print_macro_backtrace(cm, ei.call_site);
    }
}

pub fn expect<T:Copy>(diag: @span_handler,
                       opt: Option<T>,
                       msg: &fn() -> ~str) -> T {
    match opt {
       Some(ref t) => copy *t,
       None => diag.handler().bug(msg())
    }
}
<commit_msg>bright white for the message, similar to clang<commit_after>// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use codemap::{Pos, span};
use codemap;

use std::io;
use std::uint;
use extra::term;

pub type Emitter = @fn(cmsp: Option<(@codemap::CodeMap, span)>,
                       msg: &str,
                       lvl: level);

// a handler deals with errors; certain errors
// (fatal, bug, unimpl) may cause immediate exit,
// others log errors for later reporting.
pub trait handler {
    fn fatal(@mut self, msg: &str) -> !;
    fn err(@mut self, msg: &str);
    fn bump_err_count(@mut self);
    fn err_count(@mut self) -> uint;
    fn has_errors(@mut self) -> bool;
    fn abort_if_errors(@mut self);
    fn warn(@mut self, msg: &str);
    fn note(@mut self, msg: &str);
    // used to indicate a bug in the compiler:
    fn bug(@mut self, msg: &str) -> !;
    fn unimpl(@mut self, msg: &str) -> !;
    fn emit(@mut self,
            cmsp: Option<(@codemap::CodeMap, span)>,
            msg: &str,
            lvl: level);
}

// a span-handler is like a handler but also
// accepts span information for source-location
// reporting.
pub trait span_handler {
    fn span_fatal(@mut self, sp: span, msg: &str) -> !;
    fn span_err(@mut self, sp: span, msg: &str);
    fn span_warn(@mut self, sp: span, msg: &str);
    fn span_note(@mut self, sp: span, msg: &str);
    fn span_bug(@mut self, sp: span, msg: &str) -> !;
    fn span_unimpl(@mut self, sp: span, msg: &str) -> !;
    fn handler(@mut self) -> @handler;
}

struct HandlerT {
    err_count: uint,
    emit: Emitter,
}

struct CodemapT {
    handler: @handler,
    cm: @codemap::CodeMap,
}

impl span_handler for CodemapT {
    fn span_fatal(@mut self, sp: span, msg: &str) -> ! {
        self.handler.emit(Some((self.cm, sp)), msg, fatal);
        fail!();
    }
    fn span_err(@mut self, sp: span, msg: &str) {
        self.handler.emit(Some((self.cm, sp)), msg, error);
        self.handler.bump_err_count();
    }
    fn span_warn(@mut self, sp: span, msg: &str) {
        self.handler.emit(Some((self.cm, sp)), msg, warning);
    }
    fn span_note(@mut self, sp: span, msg: &str) {
        self.handler.emit(Some((self.cm, sp)), msg, note);
    }
    fn span_bug(@mut self, sp: span, msg: &str) -> ! {
        self.span_fatal(sp, ice_msg(msg));
    }
    fn span_unimpl(@mut self, sp: span, msg: &str) -> ! {
        self.span_bug(sp, ~"unimplemented " + msg);
    }
    fn handler(@mut self) -> @handler {
        self.handler
    }
}

impl handler for HandlerT {
    fn fatal(@mut self, msg: &str) -> ! {
        (self.emit)(None, msg, fatal);
        fail!();
    }
    fn err(@mut self, msg: &str) {
        (self.emit)(None, msg, error);
        self.bump_err_count();
    }
    fn bump_err_count(@mut self) {
        self.err_count += 1u;
    }
    fn err_count(@mut self) -> uint {
        self.err_count
    }
    fn has_errors(@mut self) -> bool {
        self.err_count > 0u
    }
    fn abort_if_errors(@mut self) {
        let s;
        match self.err_count {
          0u => return,
          1u => s = ~"aborting due to previous error",
          _  => {
            s = fmt!("aborting due to %u previous errors",
                     self.err_count);
          }
        }
        self.fatal(s);
    }
    fn warn(@mut self, msg: &str) {
        (self.emit)(None, msg, warning);
    }
    fn note(@mut self, msg: &str) {
        (self.emit)(None, msg, note);
    }
    fn bug(@mut self, msg: &str) -> ! {
        self.fatal(ice_msg(msg));
    }
    fn unimpl(@mut self, msg: &str) -> ! {
        self.bug(~"unimplemented " + msg);
    }
    fn emit(@mut self,
            cmsp: Option<(@codemap::CodeMap, span)>,
            msg: &str,
            lvl: level) {
        (self.emit)(cmsp, msg, lvl);
    }
}

pub fn ice_msg(msg: &str) -> ~str {
    fmt!("internal compiler error: %s", msg)
}

pub fn mk_span_handler(handler: @handler, cm: @codemap::CodeMap)
                    -> @span_handler {
    @mut CodemapT { handler: handler, cm: cm } as @span_handler
}

pub fn mk_handler(emitter: Option<Emitter>) -> @handler {
    let emit: Emitter = match emitter {
        Some(e) => e,
        None => {
            let emit: Emitter = |cmsp, msg, t| emit(cmsp, msg, t);
            emit
        }
    };

    @mut HandlerT { err_count: 0, emit: emit } as @handler
}

#[deriving(Eq)]
pub enum level {
    fatal,
    error,
    warning,
    note,
}

fn diagnosticstr(lvl: level) -> ~str {
    match lvl {
        fatal => ~"error",
        error => ~"error",
        warning => ~"warning",
        note => ~"note"
    }
}

fn diagnosticcolor(lvl: level) -> term::color::Color {
    match lvl {
        fatal => term::color::BRIGHT_RED,
        error => term::color::BRIGHT_RED,
        warning => term::color::BRIGHT_YELLOW,
        note => term::color::BRIGHT_GREEN
    }
}

fn print_maybe_colored(msg: &str, color: term::color::Color) {
    let stderr = io::stderr();

    let t = term::Terminal::new(stderr);

    match t {
        Ok(term) => {
            if stderr.get_type() == io::Screen {
                term.fg(color);
                stderr.write_str(msg);
                term.reset();
            } else {
                stderr.write_str(msg);
            }
        },
        _ => stderr.write_str(msg)
    }
}

fn print_diagnostic(topic: &str, lvl: level, msg: &str) {
    let stderr = io::stderr();

    if !topic.is_empty() {
        stderr.write_str(fmt!("%s ", topic));
    }

    print_maybe_colored(fmt!("%s: ", diagnosticstr(lvl)), diagnosticcolor(lvl));
    print_maybe_colored(fmt!("%s\n", msg), term::color::BRIGHT_WHITE);
}

pub fn collect(messages: @mut ~[~str])
            -> @fn(Option<(@codemap::CodeMap, span)>, &str, level) {
    let f: @fn(Option<(@codemap::CodeMap, span)>, &str, level) =
        |_o, msg: &str, _l| { messages.push(msg.to_str()); };
    f
}

pub fn emit(cmsp: Option<(@codemap::CodeMap, span)>, msg: &str, lvl: level) {
    match cmsp {
      Some((cm, sp)) => {
        let sp = cm.adjust_span(sp);
        let ss = cm.span_to_str(sp);
        let lines = cm.span_to_lines(sp);
        print_diagnostic(ss, lvl, msg);
        highlight_lines(cm, sp, lvl, lines);
        print_macro_backtrace(cm, sp);
      }
      None => {
        print_diagnostic("", lvl, msg);
      }
    }
}

fn highlight_lines(cm: @codemap::CodeMap,
                   sp: span, lvl: level,
                   lines: @codemap::FileLines) {
    let fm = lines.file;

    // arbitrarily only print up to six lines of the error
    let max_lines = 6u;
    let mut elided = false;
    let mut display_lines = /* FIXME (#2543) */ copy lines.lines;
    if display_lines.len() > max_lines {
        display_lines = display_lines.slice(0u, max_lines).to_owned();
        elided = true;
    }
    // Print the offending lines
    for display_lines.iter().advance |line| {
        io::stderr().write_str(fmt!("%s:%u ", fm.name, *line + 1u));
        let s = fm.get_line(*line as int) + "\n";
        io::stderr().write_str(s);
    }
    if elided {
        let last_line = display_lines[display_lines.len() - 1u];
        let s = fmt!("%s:%u ", fm.name, last_line + 1u);
        let mut indent = s.len();
        let mut out = ~"";
        while indent > 0u {
            out.push_char(' ');
            indent -= 1u;
        }
        out.push_str("...\n");
        io::stderr().write_str(out);
    }

    // FIXME (#3260)
    // If there's one line at fault we can easily point to the problem
    if lines.lines.len() == 1u {
        let lo = cm.lookup_char_pos(sp.lo);
        let mut digits = 0u;
        let mut num = (lines.lines[0] + 1u) / 10u;

        // how many digits must be indent past?
        while num > 0u { num /= 10u; digits += 1u; }

        // indent past |name:## | and the 0-offset column location
        let left = fm.name.len() + digits + lo.col.to_uint() + 3u;
        let mut s = ~"";
        // Skip is the number of characters we need to skip because they are
        // part of the 'filename:line ' part of the previous line.
        let skip = fm.name.len() + digits + 3u;
        for skip.times() {
            s.push_char(' ');
        }
        let orig = fm.get_line(lines.lines[0] as int);
        for uint::range(0u,left-skip) |pos| {
            let curChar = (orig[pos] as char);
            // Whenever a tab occurs on the previous line, we insert one on
            // the error-point-squiggly-line as well (instead of a space).
            // That way the squiggly line will usually appear in the correct
            // position.
            match curChar {
                '\t' => s.push_char('\t'),
                _ => s.push_char(' '),
            };
        }
        io::stderr().write_str(s);
        let mut s = ~"^";
        let hi = cm.lookup_char_pos(sp.hi);
        if hi.col != lo.col {
            // the ^ already takes up one space
            let num_squigglies = hi.col.to_uint()-lo.col.to_uint()-1u;
            for num_squigglies.times() {
                s.push_char('~')
            }
        }
        print_maybe_colored(s + "\n", diagnosticcolor(lvl));
    }
}

fn print_macro_backtrace(cm: @codemap::CodeMap, sp: span) {
    for sp.expn_info.iter().advance |ei| {
        let ss = ei.callee.span.map_default(~"", |span| cm.span_to_str(*span));
        print_diagnostic(ss, note,
                         fmt!("in expansion of %s!", ei.callee.name));
        let ss = cm.span_to_str(ei.call_site);
        print_diagnostic(ss, note, "expansion site");
        print_macro_backtrace(cm, ei.call_site);
    }
}

pub fn expect<T:Copy>(diag: @span_handler,
                       opt: Option<T>,
                       msg: &fn() -> ~str) -> T {
    match opt {
       Some(ref t) => copy *t,
       None => diag.handler().bug(msg())
    }
}
<|endoftext|>


<commit_before>use super::*;

use redox::*;

use orbital::*;

/// The current state of the editor, including the file, the cursor, the scrolling info, etc.
pub struct Editor {
    /// The current cursor
    pub current_cursor: u8,
    /// The cursors
    pub cursors: Vec<Cursor>,
    /// The text (document)
    pub text: VecDeque<VecDeque<char>>,
    /// The x coordinate of the scroll
    pub scroll_x: usize,
    /// The y coordinate of the scroll
    pub scroll_y: usize,
    /// The window
    pub window: Window,
    /// The status bar
    pub status_bar: StatusBar,
    /// The prompt
    pub prompt: String,
    /// The settings
    pub options: Options,
    /// The key state
    pub key_state: KeyState,
    /// Redraw
    pub redraw_task: RedrawTask,
}

impl Editor {
    /// Create new default state editor
    pub fn new() -> Editor {

        let window = Window::new((rand() % 400 + 50) as isize,
                                 (rand() % 300 + 50) as isize,
                                 700,
                                 500,
                                 &"Sodium").unwrap();

        let mut editor = Editor {
            current_cursor: 0,
            cursors: vec![Cursor::new()],
            text: VecDeque::new(),
            scroll_x: 0,
            scroll_y: 0,
            window: *window,
            status_bar: StatusBar::new(),
            prompt: String::new(),
            options: Options::new(),
            key_state: KeyState::new(),
            redraw_task: RedrawTask::Null,
        };

        editor.text.push_back(VecDeque::new());

        editor.redraw();

        loop {
            let inp = editor.get_inst();
            editor.exec(inp);
            editor.redraw();
            editor.status_bar.mode = editor.cursor().mode.to_string();
        }

        editor
    }

    /// Get a slice of the current line
    pub fn get_ln(&self, n: usize) -> &[char] {
        self.text[n].as_slices().0
    }

    /// Get the leading whitespaces of the current line. Used for autoindenting.
    pub fn get_indent(&self, n: usize) -> &[char] {
        let ln = self.get_ln(n);
        let mut len = 0;
        for &c in ln {
            match c {
                '\t' | ' ' => len += 1,
                _ => break,
            }
        }
        &ln[..len]
    }
}
<commit_msg>A gift from :bdfl: Windows will now cascade sensibly!<commit_after>use super::*;

use redox::*;

use orbital::*;

/// The current state of the editor, including the file, the cursor, the scrolling info, etc.
pub struct Editor {
    /// The current cursor
    pub current_cursor: u8,
    /// The cursors
    pub cursors: Vec<Cursor>,
    /// The text (document)
    pub text: VecDeque<VecDeque<char>>,
    /// The x coordinate of the scroll
    pub scroll_x: usize,
    /// The y coordinate of the scroll
    pub scroll_y: usize,
    /// The window
    pub window: Window,
    /// The status bar
    pub status_bar: StatusBar,
    /// The prompt
    pub prompt: String,
    /// The settings
    pub options: Options,
    /// The key state
    pub key_state: KeyState,
    /// Redraw
    pub redraw_task: RedrawTask,
}

impl Editor {
    /// Create new default state editor
    pub fn new() -> Editor {

        let window = Window::new(-1,
                                 -1,
                                 700,
                                 500,
                                 &"Sodium").unwrap();

        let mut editor = Editor {
            current_cursor: 0,
            cursors: vec![Cursor::new()],
            text: VecDeque::new(),
            scroll_x: 0,
            scroll_y: 0,
            window: *window,
            status_bar: StatusBar::new(),
            prompt: String::new(),
            options: Options::new(),
            key_state: KeyState::new(),
            redraw_task: RedrawTask::Null,
        };

        editor.text.push_back(VecDeque::new());

        editor.redraw();

        loop {
            let inp = editor.get_inst();
            editor.exec(inp);
            editor.redraw();
            editor.status_bar.mode = editor.cursor().mode.to_string();
        }

        editor
    }

    /// Get a slice of the current line
    pub fn get_ln(&self, n: usize) -> &[char] {
        self.text[n].as_slices().0
    }

    /// Get the leading whitespaces of the current line. Used for autoindenting.
    pub fn get_indent(&self, n: usize) -> &[char] {
        let ln = self.get_ln(n);
        let mut len = 0;
        for &c in ln {
            match c {
                '\t' | ' ' => len += 1,
                _ => break,
            }
        }
        &ln[..len]
    }
}
<|endoftext|>


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

extern crate time as std_time;

use energy::read_energy_uj;
use ipc_channel::ipc::IpcSender;
use self::std_time::precise_time_ns;
use signpost;
use util::opts;

#[derive(PartialEq, Clone, PartialOrd, Eq, Ord, Debug, Deserialize, Serialize)]
pub struct TimerMetadata {
    pub url:         String,
    pub iframe:      TimerMetadataFrameType,
    pub incremental: TimerMetadataReflowType,
}

#[derive(Clone, Deserialize, Serialize)]
pub struct ProfilerChan(pub IpcSender<ProfilerMsg>);

impl ProfilerChan {
    pub fn send(&self, msg: ProfilerMsg) {
        self.0.send(msg).unwrap();
    }
}

#[derive(Clone, Deserialize, Serialize)]
pub enum ProfilerMsg {
    /// Normal message used for reporting time
    Time((ProfilerCategory, Option<TimerMetadata>), (u64, u64), (u64, u64)),
    /// Message used to force print the profiling metrics
    Print,
    /// Tells the profiler to shut down.
    Exit(IpcSender<()>),
}

#[repr(u32)]
#[derive(PartialEq, Clone, Copy, PartialOrd, Eq, Ord, Deserialize, Serialize, Debug, Hash)]
pub enum ProfilerCategory {
    Compositing = 0x00,
    LayoutPerform = 0x10,
    LayoutStyleRecalc = 0x11,
    LayoutTextShaping = 0x12,
    LayoutRestyleDamagePropagation = 0x13,
    LayoutNonIncrementalReset = 0x14,
    LayoutSelectorMatch = 0x15,
    LayoutTreeBuilder = 0x16,
    LayoutDamagePropagate = 0x17,
    LayoutGeneratedContent = 0x18,
    LayoutDisplayListSorting = 0x19,
    LayoutFloatPlacementSpeculation = 0x1a,
    LayoutMain = 0x1b,
    LayoutStoreOverflow = 0x1c,
    LayoutParallelWarmup = 0x1d,
    LayoutDispListBuild = 0x1e,
    NetHTTPRequestResponse = 0x30,
    PaintingPerTile = 0x41,
    PaintingPrepBuff = 0x42,
    Painting = 0x43,
    ImageDecoding = 0x50,
    ImageSaving = 0x51,
    ScriptAttachLayout = 0x60,
    ScriptConstellationMsg = 0x61,
    ScriptDevtoolsMsg = 0x62,
    ScriptDocumentEvent = 0x63,
    ScriptDomEvent = 0x64,
    ScriptEvaluate = 0x65,
    ScriptEvent = 0x66,
    ScriptFileRead = 0x67,
    ScriptImageCacheMsg = 0x68,
    ScriptInputEvent = 0x69,
    ScriptNetworkEvent = 0x6a,
    ScriptParseHTML = 0x6b,
    ScriptPlannedNavigation = 0x6c,
    ScriptResize = 0x6d,
    ScriptSetScrollState = 0x6e,
    ScriptSetViewport = 0x6f,
    ScriptTimerEvent = 0x70,
    ScriptStylesheetLoad = 0x71,
    ScriptUpdateReplacedElement = 0x72,
    ScriptWebSocketEvent = 0x73,
    ScriptWorkerEvent = 0x74,
    ScriptServiceWorkerEvent = 0x75,
    ScriptParseXML = 0x76,
    ApplicationHeartbeat = 0x90,
}

#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Deserialize, Serialize)]
pub enum TimerMetadataFrameType {
    RootWindow,
    IFrame,
}

#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Deserialize, Serialize)]
pub enum TimerMetadataReflowType {
    Incremental,
    FirstReflow,
}

pub fn profile<T, F>(category: ProfilerCategory,
                     meta: Option<TimerMetadata>,
                     profiler_chan: ProfilerChan,
                     callback: F)
                  -> T
    where F: FnOnce() -> T
{
    if opts::get().signpost {
        signpost::start(category as u32, &[0, 0, 0, (category as usize) >> 4]);
    }
    let start_energy = read_energy_uj();
    let start_time = precise_time_ns();

    let val = callback();

    let end_time = precise_time_ns();
    let end_energy = read_energy_uj();
    if opts::get().signpost {
        signpost::end(category as u32, &[0, 0, 0, (category as usize) >> 4]);
    }

    send_profile_data(category,
                      meta,
                      profiler_chan,
                      start_time,
                      end_time,
                      start_energy,
                      end_energy);
    val
}

pub fn send_profile_data(category: ProfilerCategory,
                         meta: Option<TimerMetadata>,
                         profiler_chan: ProfilerChan,
                         start_time: u64,
                         end_time: u64,
                         start_energy: u64,
                         end_energy: u64) {
    profiler_chan.send(ProfilerMsg::Time((category, meta),
                                         (start_time, end_time),
                                         (start_energy, end_energy)));
}
<commit_msg>Silently ignore errors sending time profiling data.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

extern crate time as std_time;

use energy::read_energy_uj;
use ipc_channel::ipc::IpcSender;
use self::std_time::precise_time_ns;
use signpost;
use util::opts;

#[derive(PartialEq, Clone, PartialOrd, Eq, Ord, Debug, Deserialize, Serialize)]
pub struct TimerMetadata {
    pub url:         String,
    pub iframe:      TimerMetadataFrameType,
    pub incremental: TimerMetadataReflowType,
}

#[derive(Clone, Deserialize, Serialize)]
pub struct ProfilerChan(pub IpcSender<ProfilerMsg>);

impl ProfilerChan {
    pub fn send(&self, msg: ProfilerMsg) {
        let _ = self.0.send(msg);
    }
}

#[derive(Clone, Deserialize, Serialize)]
pub enum ProfilerMsg {
    /// Normal message used for reporting time
    Time((ProfilerCategory, Option<TimerMetadata>), (u64, u64), (u64, u64)),
    /// Message used to force print the profiling metrics
    Print,
    /// Tells the profiler to shut down.
    Exit(IpcSender<()>),
}

#[repr(u32)]
#[derive(PartialEq, Clone, Copy, PartialOrd, Eq, Ord, Deserialize, Serialize, Debug, Hash)]
pub enum ProfilerCategory {
    Compositing = 0x00,
    LayoutPerform = 0x10,
    LayoutStyleRecalc = 0x11,
    LayoutTextShaping = 0x12,
    LayoutRestyleDamagePropagation = 0x13,
    LayoutNonIncrementalReset = 0x14,
    LayoutSelectorMatch = 0x15,
    LayoutTreeBuilder = 0x16,
    LayoutDamagePropagate = 0x17,
    LayoutGeneratedContent = 0x18,
    LayoutDisplayListSorting = 0x19,
    LayoutFloatPlacementSpeculation = 0x1a,
    LayoutMain = 0x1b,
    LayoutStoreOverflow = 0x1c,
    LayoutParallelWarmup = 0x1d,
    LayoutDispListBuild = 0x1e,
    NetHTTPRequestResponse = 0x30,
    PaintingPerTile = 0x41,
    PaintingPrepBuff = 0x42,
    Painting = 0x43,
    ImageDecoding = 0x50,
    ImageSaving = 0x51,
    ScriptAttachLayout = 0x60,
    ScriptConstellationMsg = 0x61,
    ScriptDevtoolsMsg = 0x62,
    ScriptDocumentEvent = 0x63,
    ScriptDomEvent = 0x64,
    ScriptEvaluate = 0x65,
    ScriptEvent = 0x66,
    ScriptFileRead = 0x67,
    ScriptImageCacheMsg = 0x68,
    ScriptInputEvent = 0x69,
    ScriptNetworkEvent = 0x6a,
    ScriptParseHTML = 0x6b,
    ScriptPlannedNavigation = 0x6c,
    ScriptResize = 0x6d,
    ScriptSetScrollState = 0x6e,
    ScriptSetViewport = 0x6f,
    ScriptTimerEvent = 0x70,
    ScriptStylesheetLoad = 0x71,
    ScriptUpdateReplacedElement = 0x72,
    ScriptWebSocketEvent = 0x73,
    ScriptWorkerEvent = 0x74,
    ScriptServiceWorkerEvent = 0x75,
    ScriptParseXML = 0x76,
    ApplicationHeartbeat = 0x90,
}

#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Deserialize, Serialize)]
pub enum TimerMetadataFrameType {
    RootWindow,
    IFrame,
}

#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Deserialize, Serialize)]
pub enum TimerMetadataReflowType {
    Incremental,
    FirstReflow,
}

pub fn profile<T, F>(category: ProfilerCategory,
                     meta: Option<TimerMetadata>,
                     profiler_chan: ProfilerChan,
                     callback: F)
                  -> T
    where F: FnOnce() -> T
{
    if opts::get().signpost {
        signpost::start(category as u32, &[0, 0, 0, (category as usize) >> 4]);
    }
    let start_energy = read_energy_uj();
    let start_time = precise_time_ns();

    let val = callback();

    let end_time = precise_time_ns();
    let end_energy = read_energy_uj();
    if opts::get().signpost {
        signpost::end(category as u32, &[0, 0, 0, (category as usize) >> 4]);
    }

    send_profile_data(category,
                      meta,
                      profiler_chan,
                      start_time,
                      end_time,
                      start_energy,
                      end_energy);
    val
}

pub fn send_profile_data(category: ProfilerCategory,
                         meta: Option<TimerMetadata>,
                         profiler_chan: ProfilerChan,
                         start_time: u64,
                         end_time: u64,
                         start_energy: u64,
                         end_energy: u64) {
    profiler_chan.send(ProfilerMsg::Time((category, meta),
                                         (start_time, end_time),
                                         (start_energy, end_energy)));
}
<|endoftext|>


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

use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::FormDataBinding;
use dom::bindings::codegen::Bindings::FormDataBinding::FormDataMethods;
use dom::bindings::codegen::InheritTypes::FileCast;
use dom::bindings::codegen::UnionTypes::FileOrString;
use dom::bindings::codegen::UnionTypes::FileOrString::{eFile, eString};
use dom::bindings::error::{Fallible};
use dom::bindings::global::{GlobalRef, GlobalField};
use dom::bindings::js::{JS, Root};
use dom::bindings::utils::{Reflector, reflect_dom_object};
use dom::blob::Blob;
use dom::file::File;
use dom::htmlformelement::HTMLFormElement;
use util::str::DOMString;

use std::borrow::ToOwned;
use std::collections::HashMap;
use std::collections::hash_map::Entry::{Occupied, Vacant};

#[derive(JSTraceable, Clone)]
#[must_root]
#[derive(HeapSizeOf)]
pub enum FormDatum {
    StringData(DOMString),
    FileData(JS<File>)
}

#[dom_struct]
pub struct FormData {
    reflector_: Reflector,
    data: DOMRefCell<HashMap<DOMString, Vec<FormDatum>>>,
    global: GlobalField,
    form: Option<JS<HTMLFormElement>>
}

impl FormData {
    fn new_inherited(form: Option<&HTMLFormElement>, global: GlobalRef) -> FormData {
        FormData {
            reflector_: Reflector::new(),
            data: DOMRefCell::new(HashMap::new()),
            global: GlobalField::from_rooted(&global),
            form: form.map(|f| JS::from_ref(f)),
        }
    }

    pub fn new(form: Option<&HTMLFormElement>, global: GlobalRef) -> Root<FormData> {
        reflect_dom_object(box FormData::new_inherited(form, global),
                           global, FormDataBinding::Wrap)
    }

    pub fn Constructor(global: GlobalRef, form: Option<&HTMLFormElement>) -> Fallible<Root<FormData>> {
        Ok(FormData::new(form, global))
    }
}

impl FormDataMethods for FormData {
    #[allow(unrooted_must_root)]
    // https://xhr.spec.whatwg.org/#dom-formdata-append
    fn Append(&self, name: DOMString, value: &Blob, filename: Option<DOMString>) {
        let file = FormDatum::FileData(JS::from_rooted(&self.get_file_from_blob(value, filename)));
        let mut data = self.data.borrow_mut();
        match data.entry(name) {
            Occupied(entry) => entry.into_mut().push(file),
            Vacant(entry) => {
                entry.insert(vec!(file));
            }
        }
    }

    // https://xhr.spec.whatwg.org/#dom-formdata-append
    fn Append_(&self, name: DOMString, value: DOMString) {
        let mut data = self.data.borrow_mut();
        match data.entry(name) {
            Occupied(entry) => entry.into_mut().push(FormDatum::StringData(value)),
            Vacant  (entry) => { entry.insert(vec!(FormDatum::StringData(value))); },
        }
    }

    // https://xhr.spec.whatwg.org/#dom-formdata-delete
    fn Delete(&self, name: DOMString) {
        self.data.borrow_mut().remove(&name);
    }

    #[allow(unsafe_code)]
    // https://xhr.spec.whatwg.org/#dom-formdata-get
    fn Get(&self, name: DOMString) -> Option<FileOrString> {
        // FIXME(https://github.com/rust-lang/rust/issues/23338)
        let data = self.data.borrow();
        if data.contains_key(&name) {
            match data[&name][0].clone() {
                FormDatum::StringData(ref s) => Some(eString(s.clone())),
                FormDatum::FileData(ref f) => {
                    Some(eFile(f.root()))
                }
            }
        } else {
            None
        }
    }

    // https://xhr.spec.whatwg.org/#dom-formdata-has
    fn Has(&self, name: DOMString) -> bool {
        self.data.borrow().contains_key(&name)
    }

    // https://xhr.spec.whatwg.org/#dom-formdata-set
    fn Set_(&self, name: DOMString, value: DOMString) {
        self.data.borrow_mut().insert(name, vec!(FormDatum::StringData(value)));
    }

    #[allow(unrooted_must_root)]
    // https://xhr.spec.whatwg.org/#dom-formdata-set
    fn Set(&self, name: DOMString, value: &Blob, filename: Option<DOMString>) {
        let file = FormDatum::FileData(JS::from_rooted(&self.get_file_from_blob(value, filename)));
        self.data.borrow_mut().insert(name, vec!(file));
    }
}

impl FormData {
    fn get_file_from_blob(&self, value: &Blob, filename: Option<DOMString>) -> Root<File> {
        let global = self.global.root();
        let f: Option<&File> = FileCast::to_ref(value);
        let name = filename.unwrap_or(f.map(|inner| inner.name().clone()).unwrap_or("blob".to_owned()));
        File::new(global.r(), value, name)
    }
}
<commit_msg>Auto merge of #7433 - frewsxcv:formdata-get, r=nox<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::FormDataBinding;
use dom::bindings::codegen::Bindings::FormDataBinding::FormDataMethods;
use dom::bindings::codegen::InheritTypes::FileCast;
use dom::bindings::codegen::UnionTypes::FileOrString;
use dom::bindings::codegen::UnionTypes::FileOrString::{eFile, eString};
use dom::bindings::error::{Fallible};
use dom::bindings::global::{GlobalRef, GlobalField};
use dom::bindings::js::{JS, Root};
use dom::bindings::utils::{Reflector, reflect_dom_object};
use dom::blob::Blob;
use dom::file::File;
use dom::htmlformelement::HTMLFormElement;
use util::str::DOMString;

use std::borrow::ToOwned;
use std::collections::HashMap;
use std::collections::hash_map::Entry::{Occupied, Vacant};

#[derive(JSTraceable, Clone)]
#[must_root]
#[derive(HeapSizeOf)]
pub enum FormDatum {
    StringData(DOMString),
    FileData(JS<File>)
}

#[dom_struct]
pub struct FormData {
    reflector_: Reflector,
    data: DOMRefCell<HashMap<DOMString, Vec<FormDatum>>>,
    global: GlobalField,
    form: Option<JS<HTMLFormElement>>
}

impl FormData {
    fn new_inherited(form: Option<&HTMLFormElement>, global: GlobalRef) -> FormData {
        FormData {
            reflector_: Reflector::new(),
            data: DOMRefCell::new(HashMap::new()),
            global: GlobalField::from_rooted(&global),
            form: form.map(|f| JS::from_ref(f)),
        }
    }

    pub fn new(form: Option<&HTMLFormElement>, global: GlobalRef) -> Root<FormData> {
        reflect_dom_object(box FormData::new_inherited(form, global),
                           global, FormDataBinding::Wrap)
    }

    pub fn Constructor(global: GlobalRef, form: Option<&HTMLFormElement>) -> Fallible<Root<FormData>> {
        Ok(FormData::new(form, global))
    }
}

impl FormDataMethods for FormData {
    #[allow(unrooted_must_root)]
    // https://xhr.spec.whatwg.org/#dom-formdata-append
    fn Append(&self, name: DOMString, value: &Blob, filename: Option<DOMString>) {
        let file = FormDatum::FileData(JS::from_rooted(&self.get_file_from_blob(value, filename)));
        let mut data = self.data.borrow_mut();
        match data.entry(name) {
            Occupied(entry) => entry.into_mut().push(file),
            Vacant(entry) => {
                entry.insert(vec!(file));
            }
        }
    }

    // https://xhr.spec.whatwg.org/#dom-formdata-append
    fn Append_(&self, name: DOMString, value: DOMString) {
        let mut data = self.data.borrow_mut();
        match data.entry(name) {
            Occupied(entry) => entry.into_mut().push(FormDatum::StringData(value)),
            Vacant  (entry) => { entry.insert(vec!(FormDatum::StringData(value))); },
        }
    }

    // https://xhr.spec.whatwg.org/#dom-formdata-delete
    fn Delete(&self, name: DOMString) {
        self.data.borrow_mut().remove(&name);
    }

    // https://xhr.spec.whatwg.org/#dom-formdata-get
    fn Get(&self, name: DOMString) -> Option<FileOrString> {
        self.data.borrow()
                 .get(&name)
                 .map(|entry| match entry[0] {
                     FormDatum::StringData(ref s) => eString(s.clone()),
                     FormDatum::FileData(ref f) => eFile(f.root()),
                 })
    }

    // https://xhr.spec.whatwg.org/#dom-formdata-has
    fn Has(&self, name: DOMString) -> bool {
        self.data.borrow().contains_key(&name)
    }

    // https://xhr.spec.whatwg.org/#dom-formdata-set
    fn Set_(&self, name: DOMString, value: DOMString) {
        self.data.borrow_mut().insert(name, vec!(FormDatum::StringData(value)));
    }

    #[allow(unrooted_must_root)]
    // https://xhr.spec.whatwg.org/#dom-formdata-set
    fn Set(&self, name: DOMString, value: &Blob, filename: Option<DOMString>) {
        let file = FormDatum::FileData(JS::from_rooted(&self.get_file_from_blob(value, filename)));
        self.data.borrow_mut().insert(name, vec!(file));
    }
}

impl FormData {
    fn get_file_from_blob(&self, value: &Blob, filename: Option<DOMString>) -> Root<File> {
        let global = self.global.root();
        let f: Option<&File> = FileCast::to_ref(value);
        let name = filename.unwrap_or(f.map(|inner| inner.name().clone()).unwrap_or("blob".to_owned()));
        File::new(global.r(), value, name)
    }
}
<|endoftext|>


<commit_before>extern crate libmodbus_rs;

use libmodbus_rs::{Modbus, ModbusClient, ModbusServer, ModbusMapping, FunctionCode, ModbusTCP};
use std::thread;
use std::time::Duration;

fn start_server() {
    thread::spawn(move || {
        match Modbus::new_tcp("127.0.0.1", 1502) {
            Ok(mut server) => {
                let mapping = ModbusMapping::new(10, 10, 10, 10).expect("could not create modbus mapping");
                let mut socket = server.tcp_listen(1).expect("could not listen");
                server.tcp_accept(&mut socket).expect("unable to accept TCP socket");
                let mut query = vec![0; Modbus::MAX_ADU_LENGTH];
                match server.receive(&mut query) {
                    Ok(request) => {
                        server.reply(&mut query, 1, &mapping).expect(&format!("could not reply requeset: {}", request));
                        Ok(())
                    },
                    Err(err) => Err(err),
                }
            },
            Err(err) => panic!("Could not create server: {}", err),
        }
    });
    // give server some time to come up
    thread::sleep(Duration::from_millis(100));
}

// FIXME: Find way to stop the server between the test
#[test]
#[ignore]
fn read_bits() {
    // Start modbus server
    start_server();

    // connect client
    match Modbus::new_tcp("127.0.0.1", 1502) {
        Ok(client) => {
            let mut dest = vec![0u8; 100];
            client.connect().expect("could not connect");
            assert!(client.read_bits(0, 1, &mut dest).is_ok());
        },
        _ => panic!("could not connect"),
    }
}

// FIXME: Find way to stop the server between the test
#[test]
#[ignore]
fn read_bits_too_many() {
    // Start modbus server
    start_server();

    // connect client
    match Modbus::new_tcp("127.0.0.1", 1502) {
        Ok(client) => {
            let mut dest = vec![0u8; 100];
            client.connect().expect("could not connect");
            assert!(client.read_bits(0, 101, &mut dest).is_err()); // => ErrorKind::TooManyDataBits
        },
        _ => panic!("could not connect"),
    }
}

// FIXME: Find way to stop the server between the test
#[test]
#[ignore]
fn read_input_bits() {
    // Start modbus server
    start_server();

    // connect client
    match Modbus::new_tcp("127.0.0.1", 1502) {
        Ok(client) => {
            let mut dest = vec![0u8; 100];
            client.connect().expect("could not connect");
            assert!(client.read_input_bits(0, 1, &mut dest).is_ok());
        },
        _ => panic!("could not connect"),
    }
}

// FIXME: Find way to stop the server between the test
#[test]
#[ignore]
fn read_registers() {
    // Start modbus server
    start_server();

    // connect client
    match Modbus::new_tcp("127.0.0.1", 1502) {
        Ok(client) => {
            let mut dest = vec![0u16; 100];
            client.connect().expect("could not connect");
            assert!(client.read_registers(0, 1, &mut dest).is_ok());
        },
        _ => panic!("could not connect"),
    }
}

// FIXME: Find way to stop the server between the test
#[test]
#[ignore]
fn read_input_registers() {
    // Start modbus server
    start_server();

    // connect client
    match Modbus::new_tcp("127.0.0.1", 1502) {
        Ok(client) => {
            let mut dest = vec![0u16; 100];
            client.connect().expect("could not connect");
            assert!(client.read_input_registers(0, 1, &mut dest).is_ok());
        },
        _ => panic!("could not connect"),
    }
}

// FIXME: Find way to stop the server between the test
#[test]
#[ignore]
fn report_slave_id() {
    // Start modbus server
    start_server();

    // connect client
    match Modbus::new_tcp("127.0.0.1", 1502) {
        Ok(client) => {
            let mut bytes = vec![0u8; Modbus::MAX_PDU_LENGTH];
            client.connect().expect("could not connect");
            // println!("{:?}", str::from_utf8(&client.report_slave_id[2..])) # => Ok("LMB3.1.4")
            assert!(client.report_slave_id(Modbus::MAX_PDU_LENGTH, &mut bytes).is_ok());
            // assert_eq!(client.report_slave_id().unwrap(), vec![180, 255, 76, 77, 66, 51, 46, 49, 46, 52]);
        },
        _ => panic!("could not connect"),
    }
}

#[test]
#[ignore]
fn write_bit() {
    // Start modbus server
    start_server();

    // connect client
    match Modbus::new_tcp("127.0.0.1", 1502) {
        Ok(client) => {
            client.connect().expect("could not connect");
            assert!(client.write_bit(0, true).is_ok());
        },
        _ => panic!("could not connect"),
    }
}

#[test]
#[ignore]
fn write_bits() {
    // Start modbus server
    start_server();

    // connect client
    match Modbus::new_tcp("127.0.0.1", 1502) {
        Ok(client) => {
            client.connect().expect("could not connect");
            let src = vec![1u8];
            assert_eq!(client.write_bits(0, 1, &src).unwrap(), 1);
        },
        _ => panic!("could not connect"),
    }
}

#[test]
#[ignore]
fn write_register() {
    // Start modbus server
    start_server();

    // connect client
    match Modbus::new_tcp("127.0.0.1", 1502) {
        Ok(client) => {
            client.connect().expect("could not connect");
            let address: u16 = 1;
            let value = u16::max_value();
            assert!(client.write_register(address, value).is_ok());
        },
        _ => panic!("could not connect"),
    }
}

#[test]
#[ignore]
fn write_registers() {
    // Start modbus server
    start_server();

    // connect client
    match Modbus::new_tcp("127.0.0.1", 1502) {
        Ok(client) => {
            client.connect().expect("could not connect");
            let address = 1;
            let tab_bytes = vec![0u16];
            assert_eq!(client.write_registers(address, 1, &tab_bytes).unwrap(), 1);
        },
        _ => panic!("could not connect"),
    }
}

#[test]
#[ignore]
fn write_and_read_registers() {
    // Start modbus server
    start_server();

    // connect client
    match Modbus::new_tcp("127.0.0.1", 1502) {
        Ok(client) => {
            client.connect().expect("could not connect");
        },
        _ => panic!("could not connect"),
    }
}

#[test]
#[ignore]
fn send_raw_request() {
    // Start modbus server
    start_server();

    // connect client
    match Modbus::new_tcp("127.0.0.1", 1502) {
        Ok(client) => {
            client.connect().expect("could not connect");
            let mut raw_request: Vec<u8> = vec![0xFF, FunctionCode::ReadHoldingRegisters as u8, 0x00, 0x01, 0x0, 0x05];
            let mut response = vec![0u8; Modbus::MAX_ADU_LENGTH];
            assert_eq!(client.send_raw_request(&mut raw_request, 12).unwrap(), 12);
            assert!(client.receive_confirmation(&mut response).is_ok());
            assert_eq!(response,
                      vec![0, 0, 0, 0, 0, 13, 255, 3, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
        },
        _ => panic!("could not connect"),
    }
}

#[test]
#[ignore]
fn receive_confirmation() {
    // Start modbus server
    start_server();

    // connect client
    match Modbus::new_tcp("127.0.0.1", 1502) {
        Ok(client) => {
            client.connect().expect("could not connect");
            let mut raw_request: Vec<u8> = vec![0xFF, FunctionCode::ReadHoldingRegisters as u8, 0x00, 0x01, 0x0, 0x05];
            let mut response = vec![0u8; Modbus::MAX_ADU_LENGTH];
            assert_eq!(client.send_raw_request(&mut raw_request, 12).unwrap(), 12);
            assert!(client.receive_confirmation(&mut response).is_ok());
            assert_eq!(response,
                      vec![0, 0, 0, 0, 0, 13, 255, 3, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
        },
        _ => panic!("could not connect"),
    }
}
<commit_msg>Modbus Client Tests are running now<commit_after>extern crate libmodbus_rs;

use libmodbus_rs::{Modbus, ModbusClient, ModbusServer, ModbusMapping, FunctionCode, ModbusTCP};
use std::thread;
use std::time::Duration;

fn start_server(port: i32) -> thread::JoinHandle<()> {
    thread::spawn(move || {
        let mut modbus = Modbus::new_tcp("127.0.0.1", port).expect("Could not create TCP Server context");
        let mut socket = modbus.tcp_listen(1).expect("Could not listen to TCP socket");
        modbus.tcp_accept(&mut socket).expect("Could not accept connection");

        let mb_mapping = ModbusMapping::new(Modbus::MAX_READ_BITS as i32, Modbus::MAX_READ_BITS as i32,
            Modbus::MAX_READ_REGISTERS as i32, Modbus::MAX_READ_REGISTERS as i32).expect("Failed to allocate the mapping");

            loop {
                let mut query = vec![0u8; Modbus::TCP_MAX_ADU_LENGTH];

                match modbus.receive(&mut query) {
                    Ok(rc) => modbus.reply(&query, rc, &mb_mapping),
                    Err(_err) => break,
                }.expect("Could not receive");
            }
    })
}

#[test]
fn read_bits() {
    let port = 1502;
    // Start modbus server
    let server_thread = start_server(port);
    thread::sleep(Duration::from_millis(200));

    // connect client
    match Modbus::new_tcp("127.0.0.1", port) {
        Ok(client) => {
            let mut dest = vec![0u8; 100];
            client.connect().expect("could not connect");
            assert!(client.read_bits(0, 1, &mut dest).is_ok());
        },
        _ => panic!("could not connect"),
    }

    let _ = server_thread.join();
}

#[test]
fn read_input_bits() {
    let port = 1503;
    // Start modbus server
    let server_thread = start_server(port);
    thread::sleep(Duration::from_millis(200));

    // connect client
    match Modbus::new_tcp("127.0.0.1", port) {
        Ok(client) => {
            let mut dest = vec![0u8; 100];
            client.connect().expect("could not connect");
            assert!(client.read_input_bits(0, 1, &mut dest).is_ok());
        },
        _ => panic!("could not connect"),
    }

    let _ = server_thread.join();
}

#[test]
fn read_registers() {
    let port = 1504;
    // Start modbus server
    let server_thread = start_server(port);
    thread::sleep(Duration::from_millis(200));

    // connect client
    match Modbus::new_tcp("127.0.0.1", port) {
        Ok(client) => {
            let mut dest = vec![0u16; 100];
            client.connect().expect("could not connect");
            assert!(client.read_registers(0, 1, &mut dest).is_ok());
        },
        _ => panic!("could not connect"),
    }

    let _ = server_thread.join();
}

#[test]
fn read_input_registers() {
    let port = 1505;
    // Start modbus server
    let server_thread = start_server(port);
    thread::sleep(Duration::from_millis(200));

    // connect client
    match Modbus::new_tcp("127.0.0.1", port) {
        Ok(client) => {
            let mut dest = vec![0u16; 100];
            client.connect().expect("could not connect");
            assert!(client.read_input_registers(0, 1, &mut dest).is_ok());
        },
        _ => panic!("could not connect"),
    }

    let _ = server_thread.join();
}

#[test]
fn report_slave_id() {
    let port = 1506;
    // Start modbus server
    let server_thread = start_server(port);
    thread::sleep(Duration::from_millis(200));

    // connect client
    match Modbus::new_tcp("127.0.0.1", port) {
        Ok(client) => {
            let mut bytes = vec![0u8; Modbus::MAX_PDU_LENGTH];
            client.connect().expect("could not connect");
            // println!("{:?}", str::from_utf8(&client.report_slave_id[2..])) # => Ok("LMB3.1.4")
            assert!(client.report_slave_id(Modbus::MAX_PDU_LENGTH, &mut bytes).is_ok());
            // assert_eq!(client.report_slave_id().unwrap(), vec![180, 255, 76, 77, 66, 51, 46, 49, 46, 52]);
        },
        _ => panic!("could not connect"),
    }

    let _ = server_thread.join();
}

#[test]
fn write_bit() {
    let port = 1507;
    // Start modbus server
    let server_thread = start_server(port);
    thread::sleep(Duration::from_millis(200));

    // connect client
    match Modbus::new_tcp("127.0.0.1", port) {
        Ok(client) => {
            client.connect().expect("could not connect");
            assert!(client.write_bit(0, true).is_ok());
        },
        _ => panic!("could not connect"),
    }

    let _ = server_thread.join();
}

#[test]
fn write_bits() {
    let port = 1508;
    // Start modbus server
    let server_thread = start_server(port);
    thread::sleep(Duration::from_millis(200));

    // connect client
    match Modbus::new_tcp("127.0.0.1", port) {
        Ok(client) => {
            client.connect().expect("could not connect");
            let src = vec![1u8];
            assert_eq!(client.write_bits(0, 1, &src).unwrap(), 1);
        },
        _ => panic!("could not connect"),
    }

    let _ = server_thread.join();
}

#[test]
fn write_register() {
    let port = 1509;
    // Start modbus server
    let server_thread = start_server(port);
    thread::sleep(Duration::from_millis(200));

    // connect client
    match Modbus::new_tcp("127.0.0.1", port) {
        Ok(client) => {
            client.connect().expect("could not connect");
            let address: u16 = 1;
            let value = u16::max_value();
            assert!(client.write_register(address, value).is_ok());
        },
        _ => panic!("could not connect"),
    }

    let _ = server_thread.join();
}

#[test]
fn write_registers() {
    let port = 1510;
    // Start modbus server
    let server_thread = start_server(port);
    thread::sleep(Duration::from_millis(200));

    // connect client
    match Modbus::new_tcp("127.0.0.1", port) {
        Ok(client) => {
            client.connect().expect("could not connect");
            let address = 1;
            let tab_bytes = vec![0u16];
            assert_eq!(client.write_registers(address, 1, &tab_bytes).unwrap(), 1);
        },
        _ => panic!("could not connect"),
    }

    let _ = server_thread.join();
}

#[test]
fn write_and_read_registers() {
    let port = 1511;
    // Start modbus server
    let server_thread = start_server(port);
    thread::sleep(Duration::from_millis(200));

    // connect client
    match Modbus::new_tcp("127.0.0.1", port) {
        Ok(client) => {
            client.connect().expect("could not connect");
        },
        _ => panic!("could not connect"),
    }

    let _ = server_thread.join();
}

#[test]
fn send_raw_request() {
    let port = 1512;
    // Start modbus server
    let server_thread = start_server(port);
    thread::sleep(Duration::from_millis(200));

    // connect client
    match Modbus::new_tcp("127.0.0.1", port) {
        Ok(client) => {
            client.connect().expect("could not connect");
            let mut raw_request: Vec<u8> = vec![0xFF, FunctionCode::ReadHoldingRegisters as u8, 0x00, 0x01, 0x0, 0x05];
            let mut response = vec![0u8; Modbus::MAX_ADU_LENGTH];
            assert_eq!(client.send_raw_request(&mut raw_request, 12).unwrap(), 18);
            assert!(client.receive_confirmation(&mut response).is_ok());
        },
        _ => panic!("could not connect"),
    }

    let _ = server_thread.join();
}

#[test]
fn receive_confirmation() {
    let port = 1513;
    // Start modbus server
    let server_thread = start_server(port);
    thread::sleep(Duration::from_millis(200));

    // connect client
    match Modbus::new_tcp("127.0.0.1", port) {
        Ok(client) => {
            client.connect().expect("could not connect");
            let mut raw_request: Vec<u8> = vec![0xFF, FunctionCode::ReadHoldingRegisters as u8, 0x00, 0x01, 0x0, 0x05];
            let mut response = vec![0u8; Modbus::MAX_ADU_LENGTH];
            assert_eq!(client.send_raw_request(&mut raw_request, 12).unwrap(), 18);
            assert!(client.receive_confirmation(&mut response).is_ok());
        },
        _ => panic!("could not connect"),
    }

    let _ = server_thread.join();
}
<|endoftext|>