<commit_before>use utils::time;
use super::heartbeat_rpc::*;
use super::raft::*;
use super::*;
use raft::{RaftService, LogEntry, RaftMsg, Service as raft_svr_trait};
use raft::state_machine::StateMachineCtl;
use parking_lot::Mutex;
use std::collections::{HashMap, HashSet};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::{thread, time as std_time};

pub static DEFAULT_SERVICE_ID: u64 = hash_ident!(BIFROST_MEMBERSHIP_SERVICE) as u64;

static MAX_TIMEOUT: i64 = 5000; //5 secs for 500ms heartbeat

struct HBStatus {
    alive: bool,
    last_updated: i64,
}

pub struct HeartbeatService {
    status: Mutex<HashMap<u64, HBStatus>>,
    member_addresses: Mutex<HashMap<String, u64>>,
    raft_service: Arc<RaftService>,
    closed: AtomicBool,
}

impl Service for HeartbeatService {
    fn ping(&self, id: u64) -> Result<(), ()> {
        let mut stat_map = self.status.lock();
        let current_time = time::get_time();
        let mut stat = stat_map.entry(id).or_insert_with(|| HBStatus {
            alive: false,
            last_updated: current_time,
            //orthodoxy info will trigger the watcher thread to update
        });
        stat.last_updated = current_time;
        // only update the timestamp, let the watcher thread to decide
        Ok(())
    }
}
impl HeartbeatService {
    fn update_raft(&self, online: Vec<u64>, offline: Vec<u64>) {
        let log = commands::hb_online_changed {
            online: online,
            offline: offline
        };
        let (fn_id, _, data) = log.encode();
        self.raft_service.c_command(LogEntry {
            id: 0,
            term: 0,
            sm_id: DEFAULT_SERVICE_ID,
            fn_id: fn_id,
            data: data
        });
    }
}
dispatch_rpc_service_functions!(HeartbeatService);

pub struct MemberGroup {
    name: String,
    id: u64,
    members: HashSet<u64>
}

pub struct Membership {
    heartbeat: Arc<HeartbeatService>,
    groups: HashMap<u64, MemberGroup>,
}
impl Drop for Membership {
    fn drop(&mut self) {
        self.heartbeat.closed.store(true, Ordering::Relaxed)
    }
}

impl Membership {
    pub fn new(raft_service: Arc<RaftService>) {
        let service = Arc::new(HeartbeatService {
            status: Mutex::new(HashMap::new()),
            member_addresses: Mutex::new(HashMap::new()),
            closed: AtomicBool::new(false),
            raft_service: raft_service.clone(),
        });
        let service_clone = service.clone();
        thread::spawn(move || {
            while !service_clone.closed.load(Ordering::Relaxed) {
                if service_clone.raft_service.is_leader() {
                    let current_time = time::get_time();
                    let mut outdated_members: Vec<u64> = Vec::new();
                    let mut backedin_members: Vec<u64> = Vec::new();
                    {
                        let mut status_map = service_clone.status.lock();
                        let mut members_to_update: HashMap<u64, bool> = HashMap::new();
                        for (id, status) in status_map.iter() {
                            let alive = (current_time - status.last_updated) < MAX_TIMEOUT;
                            if status.alive && !alive {
                                outdated_members.push(id.clone());
                                members_to_update.insert(id.clone(), alive);
                            }
                            if !status.alive && alive {
                                backedin_members.push(id.clone());
                                members_to_update.insert(id.clone(), alive);
                            }
                        }
                        for (id, alive) in members_to_update.iter() {
                            let mut status = status_map.get_mut(&id).unwrap();
                            status.alive = alive.clone();
                        }

                    }
                    service_clone.update_raft(backedin_members, outdated_members);
                }
                thread::sleep(std_time::Duration::from_secs(1));
            }
        });
        raft_service.register_state_machine(Box::new(Membership {
            heartbeat: service,
            groups: HashMap::new()
        }))
    }
}

impl StateMachineCmds for Membership {
    fn hb_online_changed(&mut self, online: Vec<u64>, offline: Vec<u64>) -> Result<(), ()> {
        Err(())
    }
    fn join(&mut self, group: u64, address: String) -> Result<(), ()> {
        Err(())
    }
    fn leave(&mut self, group: u64, address: String) -> Result<(), ()> {
        Err(())
    }
    fn members(&self, group: u64) -> Result<Vec<Member>, ()> {
        Err(())
    }
    fn leader(&self, group: u64) -> Result<Member, ()> {
        Err(())
    }
    fn group_members(&self, group: u64) -> Result<Vec<Member>, ()> {
        Err(())
    }
    fn all_members(&self) -> Result<Vec<Member>, ()> {
        Err(())
    }
}
impl StateMachineCtl for Membership {
    sm_complete!();
    fn snapshot(&self) -> Option<Vec<u8>> {
        //Some(serialize!(&self.map))
        None // TODO: Backup members
    }
    fn recover(&mut self, data: Vec<u8>) {
        //self.map = deserialize!(&data);
    }
    fn id(&self) -> u64 {DEFAULT_SERVICE_ID}
}<commit_msg>ensure leader transfer for membership heartbeat service<commit_after>use utils::time;
use super::heartbeat_rpc::*;
use super::raft::*;
use super::*;
use raft::{RaftService, LogEntry, RaftMsg, Service as raft_svr_trait};
use raft::state_machine::StateMachineCtl;
use parking_lot::Mutex;
use std::collections::{HashMap, HashSet};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::{thread, time as std_time};

pub static DEFAULT_SERVICE_ID: u64 = hash_ident!(BIFROST_MEMBERSHIP_SERVICE) as u64;

static MAX_TIMEOUT: i64 = 5000; //5 secs for 500ms heartbeat

struct HBStatus {
    alive: bool,
    last_updated: i64,
}

pub struct HeartbeatService {
    status: Mutex<HashMap<u64, HBStatus>>,
    member_addresses: Mutex<HashMap<String, u64>>,
    raft_service: Arc<RaftService>,
    closed: AtomicBool,
    was_leader: AtomicBool,
}

impl Service for HeartbeatService {
    fn ping(&self, id: u64) -> Result<(), ()> {
        let mut stat_map = self.status.lock();
        let current_time = time::get_time();
        let mut stat = stat_map.entry(id).or_insert_with(|| HBStatus {
            alive: false,
            last_updated: current_time,
            //orthodoxy info will trigger the watcher thread to update
        });
        stat.last_updated = current_time;
        // only update the timestamp, let the watcher thread to decide
        Ok(())
    }
}
impl HeartbeatService {
    fn update_raft(&self, online: Vec<u64>, offline: Vec<u64>) {
        let log = commands::hb_online_changed {
            online: online,
            offline: offline
        };
        let (fn_id, _, data) = log.encode();
        self.raft_service.c_command(LogEntry {
            id: 0,
            term: 0,
            sm_id: DEFAULT_SERVICE_ID,
            fn_id: fn_id,
            data: data
        });
    }
    fn transfer_leadership(&self) { //update timestamp for every alive server
        let mut stat_map = self.status.lock();
        let current_time = time::get_time();
        for stat in stat_map.values_mut() {
            if stat.alive {
                stat.last_updated = current_time;
            }
        }
    }
}
dispatch_rpc_service_functions!(HeartbeatService);

pub struct MemberGroup {
    name: String,
    id: u64,
    members: HashSet<u64>
}

pub struct Membership {
    heartbeat: Arc<HeartbeatService>,
    groups: HashMap<u64, MemberGroup>,
}
impl Drop for Membership {
    fn drop(&mut self) {
        self.heartbeat.closed.store(true, Ordering::Relaxed)
    }
}

impl Membership {
    pub fn new(raft_service: Arc<RaftService>) {
        let service = Arc::new(HeartbeatService {
            status: Mutex::new(HashMap::new()),
            member_addresses: Mutex::new(HashMap::new()),
            closed: AtomicBool::new(false),
            raft_service: raft_service.clone(),
            was_leader: AtomicBool::new(false),
        });
        let service_clone = service.clone();
        thread::spawn(move || {
            while !service_clone.closed.load(Ordering::Relaxed) {
                let is_leader = service_clone.raft_service.is_leader();
                let was_leader = service_clone.was_leader.load(Ordering::Relaxed);
                if !was_leader && is_leader {service_clone.transfer_leadership()}
                if was_leader != is_leader {service_clone.was_leader.store(is_leader, Ordering::Relaxed);}
                if is_leader {
                    let current_time = time::get_time();
                    let mut outdated_members: Vec<u64> = Vec::new();
                    let mut backedin_members: Vec<u64> = Vec::new();
                    {
                        let mut status_map = service_clone.status.lock();
                        let mut members_to_update: HashMap<u64, bool> = HashMap::new();
                        for (id, status) in status_map.iter() {
                            let alive = (current_time - status.last_updated) < MAX_TIMEOUT;
                            if status.alive && !alive {
                                outdated_members.push(id.clone());
                                members_to_update.insert(id.clone(), alive);
                            }
                            if !status.alive && alive {
                                backedin_members.push(id.clone());
                                members_to_update.insert(id.clone(), alive);
                            }
                        }
                        for (id, alive) in members_to_update.iter() {
                            let mut status = status_map.get_mut(&id).unwrap();
                            status.alive = alive.clone();
                        }

                    }
                    service_clone.update_raft(backedin_members, outdated_members);
                }
                thread::sleep(std_time::Duration::from_secs(1));
            }
        });
        raft_service.register_state_machine(Box::new(Membership {
            heartbeat: service,
            groups: HashMap::new()
        }))
    }
}

impl StateMachineCmds for Membership {
    fn hb_online_changed(&mut self, online: Vec<u64>, offline: Vec<u64>) -> Result<(), ()> {
        Err(())
    }
    fn join(&mut self, group: u64, address: String) -> Result<(), ()> {
        Err(())
    }
    fn leave(&mut self, group: u64, address: String) -> Result<(), ()> {
        Err(())
    }
    fn members(&self, group: u64) -> Result<Vec<Member>, ()> {
        Err(())
    }
    fn leader(&self, group: u64) -> Result<Member, ()> {
        Err(())
    }
    fn group_members(&self, group: u64) -> Result<Vec<Member>, ()> {
        Err(())
    }
    fn all_members(&self) -> Result<Vec<Member>, ()> {
        Err(())
    }
}
impl StateMachineCtl for Membership {
    sm_complete!();
    fn snapshot(&self) -> Option<Vec<u8>> {
        //Some(serialize!(&self.map))
        None // TODO: Backup members
    }
    fn recover(&mut self, data: Vec<u8>) {
        //self.map = deserialize!(&data);
    }
    fn id(&self) -> u64 {DEFAULT_SERVICE_ID}
}<|endoftext|>


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

use std::iter::repeat;

use gl;

use super::super::util::slice_to_string;
use super::Program;

/// See the `type` argument of glGetActiveAttrib (the sixth one) for the set of values this enum's
/// variants correspond to. Notice the UnrecognizedType that handles the cases this library
/// doesn't know of yet.
#[derive(Debug)]
pub enum ShaderAttributeType {
    Float,
    FloatVec2,
    FloatVec3,
    FloatVec4,
    FloatMat2,
    FloatMat3,
    FloatMat4,
    FloatMat2x3,
    FloatMat2x4,
    FloatMat3x2,
    FloatMat3x4,
    FloatMat4x2,
    FloatMat4x3,
    Int,
    IntVec2,
    IntVec3,
    IntVec4,
    UnsignedInt,
    UnsignedIntVec2,
    UnsignedIntVec3,
    UnsignedIntVec4,
    UnrecognizedType(u32)
}

/// Contains information on shader program's (vertex) attributes.
#[derive(Debug)]
pub struct ShaderAttributeInfo {
    /// List of attributes.
    pub attributes: Vec<ShaderAttribute>
}

impl ShaderAttributeInfo {
    /// A convenience method to find an attribute by name. Not particularly optimized. It might be
    /// a good idea to only do one lookup by name and use the integer indices, borrows, or
    /// something similar from there on.
    pub fn get_attribute(&self, name: &str) -> Option<&ShaderAttribute> {
        for attribute in self.attributes.iter() {
            if attribute.name == name {
                return Some(attribute);
            }
        }
        None
    }
}

/// Describes an (active) attribute of a shader program.
#[derive(Debug)]
pub struct ShaderAttribute {
    /// Name of the attribute
    pub name: String,
    /// Index of the attribute
    pub location: i32,
    /// Data type of the attribute
    pub attribute_type: ShaderAttributeType,
    pub size: i32
}

pub fn make_attribute_info_vec(program: &Program) -> ShaderAttributeInfo {
    let attr_count = program.get_value(gl::ACTIVE_ATTRIBUTES);
    let max_length = program.get_value(gl::ACTIVE_ATTRIBUTE_MAX_LENGTH);
    let mut name_vec: Vec<u8> = repeat(0u8).take(max_length as usize).collect();
    ShaderAttributeInfo { attributes: (0..attr_count as usize).map(|i| {
        let mut actual_length = 0;
        let mut size = 0;
        let mut gl_type = 0;
        let name_vec_ptr = name_vec.as_mut_ptr() as *mut i8;
        unsafe {
            gl::GetActiveAttrib(program.id, i as u32, name_vec.len() as i32, &mut actual_length, &mut size, &mut gl_type, name_vec_ptr);
        }
        let name = slice_to_string(&name_vec[0..actual_length as usize]);
        let attribute_type = attribute_type_from_u32(gl_type);
        let location = program.get_attribute_location(&name[..]);
        ShaderAttribute {
            name: name,
            location: location,
            attribute_type: attribute_type,
            size: size
        }
    }).collect()}
}

fn attribute_type_from_u32(gl_type: u32) -> ShaderAttributeType {
    match gl_type {
        gl::FLOAT => ShaderAttributeType::Float,
        gl::FLOAT_VEC2 => ShaderAttributeType::FloatVec2,
        gl::FLOAT_VEC3 => ShaderAttributeType::FloatVec3,
        gl::FLOAT_VEC4 => ShaderAttributeType::FloatVec4,
        gl::FLOAT_MAT2 => ShaderAttributeType::FloatMat2,
        gl::FLOAT_MAT3 => ShaderAttributeType::FloatMat3,
        gl::FLOAT_MAT4 => ShaderAttributeType::FloatMat4,
        gl::FLOAT_MAT2x3 => ShaderAttributeType::FloatMat2x3,
        gl::FLOAT_MAT2x4 => ShaderAttributeType::FloatMat2x4,
        gl::FLOAT_MAT3x2 => ShaderAttributeType::FloatMat3x2,
        gl::FLOAT_MAT3x4 => ShaderAttributeType::FloatMat3x4,
        gl::FLOAT_MAT4x2 => ShaderAttributeType::FloatMat4x2,
        gl::FLOAT_MAT4x3 => ShaderAttributeType::FloatMat4x3,
        gl::INT => ShaderAttributeType::Int,
        gl::INT_VEC2 => ShaderAttributeType::IntVec2,
        gl::INT_VEC3 => ShaderAttributeType::IntVec3,
        gl::INT_VEC4 => ShaderAttributeType::IntVec4,
        gl::UNSIGNED_INT => ShaderAttributeType::UnsignedInt,
        gl::UNSIGNED_INT_VEC2 => ShaderAttributeType::UnsignedIntVec2,
        gl::UNSIGNED_INT_VEC3 => ShaderAttributeType::UnsignedIntVec3,
        gl::UNSIGNED_INT_VEC4 => ShaderAttributeType::UnsignedIntVec4,
        _ => ShaderAttributeType::UnrecognizedType(gl_type)
    }
}<commit_msg>Add some more attribute documentation.<commit_after>// Copyright 2015 Ilkka Rauta
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::iter::repeat;

use gl;

use super::super::util::slice_to_string;
use super::Program;

/// See the `type` argument of glGetActiveAttrib (the sixth one) for the set of values this enum's
/// variants correspond to. Notice the UnrecognizedType that handles the cases this library
/// doesn't know of yet.
#[derive(Debug)]
pub enum ShaderAttributeType {
    Float,
    FloatVec2,
    FloatVec3,
    FloatVec4,
    FloatMat2,
    FloatMat3,
    FloatMat4,
    FloatMat2x3,
    FloatMat2x4,
    FloatMat3x2,
    FloatMat3x4,
    FloatMat4x2,
    FloatMat4x3,
    Int,
    IntVec2,
    IntVec3,
    IntVec4,
    UnsignedInt,
    UnsignedIntVec2,
    UnsignedIntVec3,
    UnsignedIntVec4,
    UnrecognizedType(u32)
}

/// Contains information on shader program's (vertex) attributes.
#[derive(Debug)]
pub struct ShaderAttributeInfo {
    /// List of attributes.
    pub attributes: Vec<ShaderAttribute>
}

impl ShaderAttributeInfo {
    /// A convenience method to find an attribute by name. Not particularly optimized. It might be
    /// a good idea to only do one lookup by name and use the integer indices, borrows, or
    /// something similar from there on.
    pub fn get_attribute(&self, name: &str) -> Option<&ShaderAttribute> {
        for attribute in self.attributes.iter() {
            if attribute.name == name {
                return Some(attribute);
            }
        }
        None
    }
}

/// Describes an (active) attribute of a shader program.
#[derive(Debug)]
pub struct ShaderAttribute {
    /// Name of the attribute
    pub name: String,
    /// Index of the attribute
    pub location: i32,
    /// Data type of the attribute
    pub attribute_type: ShaderAttributeType,
    /// Size of the attribute, counted as instances of the shaderattributetype
    pub size: i32
}

/// Read all the attributes and build a ShaderAttributeInfo structure from them - makes lots of GL
/// calls, so don't call repeatedly!
pub fn make_attribute_info_vec(program: &Program) -> ShaderAttributeInfo {
    let attr_count = program.get_value(gl::ACTIVE_ATTRIBUTES);
    let max_length = program.get_value(gl::ACTIVE_ATTRIBUTE_MAX_LENGTH);
    let mut name_vec: Vec<u8> = repeat(0u8).take(max_length as usize).collect();
    ShaderAttributeInfo { attributes: (0..attr_count as usize).map(|i| {
        let mut actual_length = 0;
        let mut size = 0;
        let mut gl_type = 0;
        let name_vec_ptr = name_vec.as_mut_ptr() as *mut i8;
        unsafe {
            gl::GetActiveAttrib(program.id, i as u32, name_vec.len() as i32, &mut actual_length, &mut size, &mut gl_type, name_vec_ptr);
        }
        let name = slice_to_string(&name_vec[0..actual_length as usize]);
        let attribute_type = attribute_type_from_u32(gl_type);
        let location = program.get_attribute_location(&name[..]);
        ShaderAttribute {
            name: name,
            location: location,
            attribute_type: attribute_type,
            size: size
        }
    }).collect()}
}

fn attribute_type_from_u32(gl_type: u32) -> ShaderAttributeType {
    match gl_type {
        gl::FLOAT => ShaderAttributeType::Float,
        gl::FLOAT_VEC2 => ShaderAttributeType::FloatVec2,
        gl::FLOAT_VEC3 => ShaderAttributeType::FloatVec3,
        gl::FLOAT_VEC4 => ShaderAttributeType::FloatVec4,
        gl::FLOAT_MAT2 => ShaderAttributeType::FloatMat2,
        gl::FLOAT_MAT3 => ShaderAttributeType::FloatMat3,
        gl::FLOAT_MAT4 => ShaderAttributeType::FloatMat4,
        gl::FLOAT_MAT2x3 => ShaderAttributeType::FloatMat2x3,
        gl::FLOAT_MAT2x4 => ShaderAttributeType::FloatMat2x4,
        gl::FLOAT_MAT3x2 => ShaderAttributeType::FloatMat3x2,
        gl::FLOAT_MAT3x4 => ShaderAttributeType::FloatMat3x4,
        gl::FLOAT_MAT4x2 => ShaderAttributeType::FloatMat4x2,
        gl::FLOAT_MAT4x3 => ShaderAttributeType::FloatMat4x3,
        gl::INT => ShaderAttributeType::Int,
        gl::INT_VEC2 => ShaderAttributeType::IntVec2,
        gl::INT_VEC3 => ShaderAttributeType::IntVec3,
        gl::INT_VEC4 => ShaderAttributeType::IntVec4,
        gl::UNSIGNED_INT => ShaderAttributeType::UnsignedInt,
        gl::UNSIGNED_INT_VEC2 => ShaderAttributeType::UnsignedIntVec2,
        gl::UNSIGNED_INT_VEC3 => ShaderAttributeType::UnsignedIntVec3,
        gl::UNSIGNED_INT_VEC4 => ShaderAttributeType::UnsignedIntVec4,
        _ => ShaderAttributeType::UnrecognizedType(gl_type)
    }
}<|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/. */

//! Wrapper around Gecko's CSS error reporting mechanism.

#![allow(unsafe_code)]

use cssparser::{CowRcStr, serialize_identifier, ToCss};
use cssparser::{SourceLocation, ParseError, ParseErrorKind, Token, BasicParseErrorKind};
use selectors::parser::SelectorParseErrorKind;
use std::ptr;
use style::error_reporting::{ParseErrorReporter, ContextualParseError};
use style::gecko_bindings::bindings::{Gecko_CreateCSSErrorReporter, Gecko_DestroyCSSErrorReporter};
use style::gecko_bindings::bindings::Gecko_ReportUnexpectedCSSError;
use style::gecko_bindings::structs::{Loader, ServoStyleSheet, nsIURI};
use style::gecko_bindings::structs::ErrorReporter as GeckoErrorReporter;
use style::gecko_bindings::structs::URLExtraData as RawUrlExtraData;
use style::gecko_bindings::sugar::refptr::RefPtr;
use style::stylesheets::UrlExtraData;
use style_traits::StyleParseErrorKind;

pub type ErrorKind<'i> = ParseErrorKind<'i, StyleParseErrorKind<'i>>;

/// Wrapper around an instance of Gecko's CSS error reporter.
pub struct ErrorReporter(*mut GeckoErrorReporter);

impl ErrorReporter {
    /// Create a new instance of the Gecko error reporter.
    pub fn new(sheet: *mut ServoStyleSheet,
               loader: *mut Loader,
               url: *mut RawUrlExtraData) -> ErrorReporter {
        unsafe {
            let url = RefPtr::from_ptr_ref(&url);
            ErrorReporter(Gecko_CreateCSSErrorReporter(sheet, loader, url.mBaseURI.raw::<nsIURI>()))
        }
    }
}

impl Drop for ErrorReporter {
    fn drop(&mut self) {
        unsafe {
            Gecko_DestroyCSSErrorReporter(self.0);
        }
    }
}

enum ErrorString<'a> {
    Snippet(CowRcStr<'a>),
    Ident(CowRcStr<'a>),
    UnexpectedToken(Token<'a>),
}

impl<'a> ErrorString<'a> {
    fn into_str(self) -> CowRcStr<'a> {
        match self {
            ErrorString::Snippet(s) => s,
            ErrorString::UnexpectedToken(t) => t.to_css_string().into(),
            ErrorString::Ident(i) => {
                let mut s = String::new();
                serialize_identifier(&i, &mut s).unwrap();
                s.into()
            }
        }
    }
}

enum Action {
    Nothing,
    Skip,
    Drop,
}

trait ErrorHelpers<'a> {
    fn error_data(self) -> (CowRcStr<'a>, ErrorKind<'a>);
    fn error_params(self) -> ErrorParams<'a>;
    fn to_gecko_message(&self) -> (Option<&'static [u8]>, &'static [u8], Action);
}

fn extract_error_param<'a>(err: ErrorKind<'a>) -> Option<ErrorString<'a>> {
    Some(match err {
        ParseErrorKind::Basic(BasicParseErrorKind::UnexpectedToken(t)) => {
            ErrorString::UnexpectedToken(t)
        }

        ParseErrorKind::Basic(BasicParseErrorKind::AtRuleInvalid(i)) |
        ParseErrorKind::Custom(StyleParseErrorKind::UnsupportedAtRule(i)) => {
            let mut s = String::from("@");
            serialize_identifier(&i, &mut s).unwrap();
            ErrorString::Snippet(s.into())
        }

        ParseErrorKind::Custom(StyleParseErrorKind::OtherInvalidValue(property)) => {
            ErrorString::Snippet(property)
        }

        ParseErrorKind::Custom(
            StyleParseErrorKind::SelectorError(
                SelectorParseErrorKind::UnexpectedIdent(ident)
            )
        ) => {
            ErrorString::Ident(ident)
        }

        ParseErrorKind::Custom(StyleParseErrorKind::UnknownProperty(property)) => {
            ErrorString::Ident(property)
        }

        ParseErrorKind::Custom(
            StyleParseErrorKind::UnexpectedTokenWithinNamespace(token)
        ) => {
            ErrorString::UnexpectedToken(token)
        }

        _ => return None,
    })
}

struct ErrorParams<'a> {
    prefix_param: Option<ErrorString<'a>>,
    main_param: Option<ErrorString<'a>>,
}

/// If an error parameter is present in the given error, return it. Additionally return
/// a second parameter if it exists, for use in the prefix for the eventual error message.
fn extract_error_params<'a>(err: ErrorKind<'a>) -> Option<ErrorParams<'a>> {
    let (main, prefix) = match err {
        ParseErrorKind::Custom(StyleParseErrorKind::InvalidColor(property, token)) |
        ParseErrorKind::Custom(StyleParseErrorKind::InvalidFilter(property, token)) => {
            (Some(ErrorString::Snippet(property.into())), Some(ErrorString::UnexpectedToken(token)))
        }

        ParseErrorKind::Custom(
            StyleParseErrorKind::MediaQueryExpectedFeatureName(ident)
        ) => {
            (Some(ErrorString::Ident(ident)), None)
        }

        ParseErrorKind::Custom(
            StyleParseErrorKind::ExpectedIdentifier(token)
        ) => {
            (Some(ErrorString::UnexpectedToken(token)), None)
        }

        ParseErrorKind::Custom(StyleParseErrorKind::SelectorError(err)) => match err {
            SelectorParseErrorKind::UnexpectedTokenInAttributeSelector(t) |
            SelectorParseErrorKind::BadValueInAttr(t) |
            SelectorParseErrorKind::ExpectedBarInAttr(t) |
            SelectorParseErrorKind::NoQualifiedNameInAttributeSelector(t) |
            SelectorParseErrorKind::InvalidQualNameInAttr(t) |
            SelectorParseErrorKind::ExplicitNamespaceUnexpectedToken(t) |
            SelectorParseErrorKind::PseudoElementExpectedIdent(t) |
            SelectorParseErrorKind::NoIdentForPseudo(t) |
            SelectorParseErrorKind::ClassNeedsIdent(t) |
            SelectorParseErrorKind::PseudoElementExpectedColon(t) => {
                (None, Some(ErrorString::UnexpectedToken(t)))
            }
            SelectorParseErrorKind::ExpectedNamespace(namespace) => {
                (None, Some(ErrorString::Ident(namespace)))
            }
            SelectorParseErrorKind::UnsupportedPseudoClassOrElement(p) => {
                (None, Some(ErrorString::Ident(p)))
            }
            SelectorParseErrorKind::EmptySelector |
            SelectorParseErrorKind::DanglingCombinator => {
                (None, None)
            }
            SelectorParseErrorKind::EmptyNegation => {
                (None, Some(ErrorString::Snippet(")".into())))
            }
            err => match extract_error_param(ParseErrorKind::Custom(StyleParseErrorKind::SelectorError(err))) {
                Some(e) => (Some(e), None),
                None => return None,
            }
        },
        err => match extract_error_param(err) {
            Some(e) => (Some(e), None),
            None => return None,
        }
    };
    Some(ErrorParams {
        main_param: main,
        prefix_param: prefix,
    })
}

impl<'a> ErrorHelpers<'a> for ContextualParseError<'a> {
    fn error_data(self) -> (CowRcStr<'a>, ErrorKind<'a>) {
        match self {
            ContextualParseError::UnsupportedPropertyDeclaration(s, err) |
            ContextualParseError::UnsupportedFontFaceDescriptor(s, err) |
            ContextualParseError::UnsupportedFontFeatureValuesDescriptor(s, err) |
            ContextualParseError::InvalidKeyframeRule(s, err) |
            ContextualParseError::InvalidFontFeatureValuesRule(s, err) |
            ContextualParseError::UnsupportedKeyframePropertyDeclaration(s, err) |
            ContextualParseError::InvalidRule(s, err) |
            ContextualParseError::UnsupportedRule(s, err) |
            ContextualParseError::UnsupportedViewportDescriptorDeclaration(s, err) |
            ContextualParseError::UnsupportedCounterStyleDescriptorDeclaration(s, err) |
            ContextualParseError::InvalidMediaRule(s, err) => {
                (s.into(), err.kind)
            }
            ContextualParseError::InvalidCounterStyleWithoutSymbols(s) |
            ContextualParseError::InvalidCounterStyleNotEnoughSymbols(s) => {
                (s.into(), ParseErrorKind::Custom(StyleParseErrorKind::UnspecifiedError.into()))
            }
            ContextualParseError::InvalidCounterStyleWithoutAdditiveSymbols |
            ContextualParseError::InvalidCounterStyleExtendsWithSymbols |
            ContextualParseError::InvalidCounterStyleExtendsWithAdditiveSymbols => {
                ("".into(), ParseErrorKind::Custom(StyleParseErrorKind::UnspecifiedError.into()))
            }
        }
    }

    fn error_params(self) -> ErrorParams<'a> {
        let (s, error) = self.error_data();
        extract_error_params(error).unwrap_or_else(|| ErrorParams {
            main_param: Some(ErrorString::Snippet(s)),
            prefix_param: None
        })
    }

    fn to_gecko_message(&self) -> (Option<&'static [u8]>, &'static [u8], Action) {
        let (msg, action): (&[u8], Action) = match *self {
            ContextualParseError::UnsupportedPropertyDeclaration(
                _, ParseError { kind: ParseErrorKind::Basic(BasicParseErrorKind::UnexpectedToken(_)), .. }
            ) |
            ContextualParseError::UnsupportedPropertyDeclaration(
                _, ParseError { kind: ParseErrorKind::Basic(BasicParseErrorKind::AtRuleInvalid(_)), .. }
            ) => {
                (b"PEParseDeclarationDeclExpected\0", Action::Skip)
            }
            ContextualParseError::UnsupportedPropertyDeclaration(
                _, ParseError { kind: ParseErrorKind::Custom(ref err), .. }
            ) => {
                match *err {
                    StyleParseErrorKind::InvalidColor(_, _) => {
                        return (Some(b"PEColorNotColor\0"),
                                b"PEValueParsingError\0", Action::Drop)
                    }
                    StyleParseErrorKind::InvalidFilter(_, _) => {
                        return (Some(b"PEExpectedNoneOrURLOrFilterFunction\0"),
                                b"PEValueParsingError\0", Action::Drop)
                    }
                    StyleParseErrorKind::OtherInvalidValue(_) => {
                        (b"PEValueParsingError\0", Action::Drop)
                    }
                    _ => (b"PEUnknownProperty\0", Action::Drop)
                }
            }
            ContextualParseError::UnsupportedPropertyDeclaration(..) =>
                (b"PEUnknownProperty\0", Action::Drop),
            ContextualParseError::UnsupportedFontFaceDescriptor(..) =>
                (b"PEUnknwnFontDesc\0", Action::Skip),
            ContextualParseError::InvalidKeyframeRule(..) =>
                (b"PEKeyframeBadName\0", Action::Nothing),
            ContextualParseError::UnsupportedKeyframePropertyDeclaration(..) =>
                (b"PEBadSelectorKeyframeRuleIgnored\0", Action::Nothing),
            ContextualParseError::InvalidRule(
                _, ParseError { kind: ParseErrorKind::Custom(
                    StyleParseErrorKind::UnexpectedTokenWithinNamespace(_)
                ), .. }
            ) => {
                (b"PEAtNSUnexpected\0", Action::Nothing)
            }
            ContextualParseError::InvalidRule(
                _, ParseError { kind: ParseErrorKind::Basic(BasicParseErrorKind::AtRuleInvalid(_)), .. }
            ) |
            ContextualParseError::InvalidRule(
                _, ParseError { kind: ParseErrorKind::Custom(
                    StyleParseErrorKind::UnsupportedAtRule(_)
                ), .. }
            ) => {
                (b"PEUnknownAtRule\0", Action::Nothing)
            }
            ContextualParseError::InvalidRule(_, ref err) => {
                let prefix = match err.kind {
                    ParseErrorKind::Custom(StyleParseErrorKind::SelectorError(ref err)) => match *err {
                        SelectorParseErrorKind::UnexpectedTokenInAttributeSelector(_) => {
                            Some(&b"PEAttSelUnexpected\0"[..])
                        }
                        SelectorParseErrorKind::ExpectedBarInAttr(_) => {
                            Some(&b"PEAttSelNoBar\0"[..])
                        }
                        SelectorParseErrorKind::BadValueInAttr(_) => {
                            Some(&b"PEAttSelBadValue\0"[..])
                        }
                        SelectorParseErrorKind::NoQualifiedNameInAttributeSelector(_) => {
                            Some(&b"PEAttributeNameOrNamespaceExpected\0"[..])
                        }
                        SelectorParseErrorKind::InvalidQualNameInAttr(_) => {
                            Some(&b"PEAttributeNameExpected\0"[..])
                        }
                        SelectorParseErrorKind::ExplicitNamespaceUnexpectedToken(_) => {
                            Some(&b"PETypeSelNotType\0"[..])
                        }
                        SelectorParseErrorKind::ExpectedNamespace(_) => {
                           Some(&b"PEUnknownNamespacePrefix\0"[..])
                        }
                        SelectorParseErrorKind::EmptySelector => {
                            Some(&b"PESelectorGroupNoSelector\0"[..])
                        }
                        SelectorParseErrorKind::DanglingCombinator => {
                            Some(&b"PESelectorGroupExtraCombinator\0"[..])
                        }
                        SelectorParseErrorKind::UnsupportedPseudoClassOrElement(_) => {
                            Some(&b"PEPseudoSelUnknown\0"[..])
                        }
                        SelectorParseErrorKind::PseudoElementExpectedColon(_) => {
                            Some(&b"PEPseudoSelEndOrUserActionPC\0"[..])
                        }
                        SelectorParseErrorKind::NoIdentForPseudo(_) => {
                            Some(&b"PEPseudoClassArgNotIdent\0"[..])
                        }
                        SelectorParseErrorKind::PseudoElementExpectedIdent(_) => {
                            Some(&b"PEPseudoSelBadName\0"[..])
                        }
                        SelectorParseErrorKind::ClassNeedsIdent(_) => {
                            Some(&b"PEClassSelNotIdent\0"[..])
                        }
                        SelectorParseErrorKind::EmptyNegation => {
                            Some(&b"PENegationBadArg\0"[..])
                        }
                        _ => None,
                    },
                    _ => None,
                };
                return (prefix, b"PEBadSelectorRSIgnored\0", Action::Nothing);
            }
            ContextualParseError::InvalidMediaRule(_, ref err) => {
                let err: &[u8] = match err.kind {
                    ParseErrorKind::Custom(StyleParseErrorKind::ExpectedIdentifier(..)) => {
                        b"PEGatherMediaNotIdent\0"
                    },
                    ParseErrorKind::Custom(StyleParseErrorKind::MediaQueryExpectedFeatureName(..)) => {
                        b"PEMQExpectedFeatureName\0"
                    },
                    ParseErrorKind::Custom(StyleParseErrorKind::MediaQueryExpectedFeatureValue) => {
                        b"PEMQExpectedFeatureValue\0"
                    },
                    ParseErrorKind::Custom(StyleParseErrorKind::RangedExpressionWithNoValue) => {
                        b"PEMQNoMinMaxWithoutValue\0"
                    },
                    _ => {
                        b"PEDeclDropped\0"
                    },
                };
                (err, Action::Nothing)
            }
            ContextualParseError::UnsupportedRule(..) =>
                (b"PEDeclDropped\0", Action::Nothing),
            ContextualParseError::UnsupportedViewportDescriptorDeclaration(..) |
            ContextualParseError::UnsupportedCounterStyleDescriptorDeclaration(..) |
            ContextualParseError::InvalidCounterStyleWithoutSymbols(..) |
            ContextualParseError::InvalidCounterStyleNotEnoughSymbols(..) |
            ContextualParseError::InvalidCounterStyleWithoutAdditiveSymbols |
            ContextualParseError::InvalidCounterStyleExtendsWithSymbols |
            ContextualParseError::InvalidCounterStyleExtendsWithAdditiveSymbols |
            ContextualParseError::UnsupportedFontFeatureValuesDescriptor(..) |
            ContextualParseError::InvalidFontFeatureValuesRule(..) =>
                (b"PEUnknownAtRule\0", Action::Skip),
        };
        (None, msg, action)
    }
}

impl ParseErrorReporter for ErrorReporter {
    fn report_error(&self,
                    _url: &UrlExtraData,
                    location: SourceLocation,
                    error: ContextualParseError) {
        let (pre, name, action) = error.to_gecko_message();
        let suffix = match action {
            Action::Nothing => ptr::null(),
            Action::Skip => b"PEDeclSkipped\0".as_ptr(),
            Action::Drop => b"PEDeclDropped\0".as_ptr(),
        };
        let params = error.error_params();
        let param = params.main_param;
        let pre_param = params.prefix_param;
        let param = param.map(|p| p.into_str());
        let pre_param = pre_param.map(|p| p.into_str());
        let param_ptr = param.as_ref().map_or(ptr::null(), |p| p.as_ptr());
        let pre_param_ptr = pre_param.as_ref().map_or(ptr::null(), |p| p.as_ptr());
        // The CSS source text is unused and will be removed in bug 1381188.
        let source = "";
        unsafe {
            Gecko_ReportUnexpectedCSSError(self.0,
                                           name.as_ptr() as *const _,
                                           param_ptr as *const _,
                                           param.as_ref().map_or(0, |p| p.len()) as u32,
                                           pre.map_or(ptr::null(), |p| p.as_ptr()) as *const _,
                                           pre_param_ptr as *const _,
                                           pre_param.as_ref().map_or(0, |p| p.len()) as u32,
                                           suffix as *const _,
                                           source.as_ptr() as *const _,
                                           source.len() as u32,
                                           location.line,
                                           location.column);
        }
    }
}
<commit_msg>geckolib: Allow an ErrorReporter to be created with null URL 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/. */

//! Wrapper around Gecko's CSS error reporting mechanism.

#![allow(unsafe_code)]

use cssparser::{CowRcStr, serialize_identifier, ToCss};
use cssparser::{SourceLocation, ParseError, ParseErrorKind, Token, BasicParseErrorKind};
use selectors::parser::SelectorParseErrorKind;
use std::ptr;
use style::error_reporting::{ParseErrorReporter, ContextualParseError};
use style::gecko_bindings::bindings::{Gecko_CreateCSSErrorReporter, Gecko_DestroyCSSErrorReporter};
use style::gecko_bindings::bindings::Gecko_ReportUnexpectedCSSError;
use style::gecko_bindings::structs::{Loader, ServoStyleSheet, nsIURI};
use style::gecko_bindings::structs::ErrorReporter as GeckoErrorReporter;
use style::gecko_bindings::structs::URLExtraData as RawUrlExtraData;
use style::stylesheets::UrlExtraData;
use style_traits::StyleParseErrorKind;

pub type ErrorKind<'i> = ParseErrorKind<'i, StyleParseErrorKind<'i>>;

/// Wrapper around an instance of Gecko's CSS error reporter.
pub struct ErrorReporter(*mut GeckoErrorReporter);

impl ErrorReporter {
    /// Create a new instance of the Gecko error reporter.
    pub fn new(sheet: *mut ServoStyleSheet,
               loader: *mut Loader,
               extra_data: *mut RawUrlExtraData) -> ErrorReporter {
        unsafe {
            let url = extra_data.as_ref()
                .map(|d| d.mBaseURI.raw::<nsIURI>())
                .unwrap_or(ptr::null_mut());
            ErrorReporter(Gecko_CreateCSSErrorReporter(sheet, loader, url))
        }
    }
}

impl Drop for ErrorReporter {
    fn drop(&mut self) {
        unsafe {
            Gecko_DestroyCSSErrorReporter(self.0);
        }
    }
}

enum ErrorString<'a> {
    Snippet(CowRcStr<'a>),
    Ident(CowRcStr<'a>),
    UnexpectedToken(Token<'a>),
}

impl<'a> ErrorString<'a> {
    fn into_str(self) -> CowRcStr<'a> {
        match self {
            ErrorString::Snippet(s) => s,
            ErrorString::UnexpectedToken(t) => t.to_css_string().into(),
            ErrorString::Ident(i) => {
                let mut s = String::new();
                serialize_identifier(&i, &mut s).unwrap();
                s.into()
            }
        }
    }
}

enum Action {
    Nothing,
    Skip,
    Drop,
}

trait ErrorHelpers<'a> {
    fn error_data(self) -> (CowRcStr<'a>, ErrorKind<'a>);
    fn error_params(self) -> ErrorParams<'a>;
    fn to_gecko_message(&self) -> (Option<&'static [u8]>, &'static [u8], Action);
}

fn extract_error_param<'a>(err: ErrorKind<'a>) -> Option<ErrorString<'a>> {
    Some(match err {
        ParseErrorKind::Basic(BasicParseErrorKind::UnexpectedToken(t)) => {
            ErrorString::UnexpectedToken(t)
        }

        ParseErrorKind::Basic(BasicParseErrorKind::AtRuleInvalid(i)) |
        ParseErrorKind::Custom(StyleParseErrorKind::UnsupportedAtRule(i)) => {
            let mut s = String::from("@");
            serialize_identifier(&i, &mut s).unwrap();
            ErrorString::Snippet(s.into())
        }

        ParseErrorKind::Custom(StyleParseErrorKind::OtherInvalidValue(property)) => {
            ErrorString::Snippet(property)
        }

        ParseErrorKind::Custom(
            StyleParseErrorKind::SelectorError(
                SelectorParseErrorKind::UnexpectedIdent(ident)
            )
        ) => {
            ErrorString::Ident(ident)
        }

        ParseErrorKind::Custom(StyleParseErrorKind::UnknownProperty(property)) => {
            ErrorString::Ident(property)
        }

        ParseErrorKind::Custom(
            StyleParseErrorKind::UnexpectedTokenWithinNamespace(token)
        ) => {
            ErrorString::UnexpectedToken(token)
        }

        _ => return None,
    })
}

struct ErrorParams<'a> {
    prefix_param: Option<ErrorString<'a>>,
    main_param: Option<ErrorString<'a>>,
}

/// If an error parameter is present in the given error, return it. Additionally return
/// a second parameter if it exists, for use in the prefix for the eventual error message.
fn extract_error_params<'a>(err: ErrorKind<'a>) -> Option<ErrorParams<'a>> {
    let (main, prefix) = match err {
        ParseErrorKind::Custom(StyleParseErrorKind::InvalidColor(property, token)) |
        ParseErrorKind::Custom(StyleParseErrorKind::InvalidFilter(property, token)) => {
            (Some(ErrorString::Snippet(property.into())), Some(ErrorString::UnexpectedToken(token)))
        }

        ParseErrorKind::Custom(
            StyleParseErrorKind::MediaQueryExpectedFeatureName(ident)
        ) => {
            (Some(ErrorString::Ident(ident)), None)
        }

        ParseErrorKind::Custom(
            StyleParseErrorKind::ExpectedIdentifier(token)
        ) => {
            (Some(ErrorString::UnexpectedToken(token)), None)
        }

        ParseErrorKind::Custom(StyleParseErrorKind::SelectorError(err)) => match err {
            SelectorParseErrorKind::UnexpectedTokenInAttributeSelector(t) |
            SelectorParseErrorKind::BadValueInAttr(t) |
            SelectorParseErrorKind::ExpectedBarInAttr(t) |
            SelectorParseErrorKind::NoQualifiedNameInAttributeSelector(t) |
            SelectorParseErrorKind::InvalidQualNameInAttr(t) |
            SelectorParseErrorKind::ExplicitNamespaceUnexpectedToken(t) |
            SelectorParseErrorKind::PseudoElementExpectedIdent(t) |
            SelectorParseErrorKind::NoIdentForPseudo(t) |
            SelectorParseErrorKind::ClassNeedsIdent(t) |
            SelectorParseErrorKind::PseudoElementExpectedColon(t) => {
                (None, Some(ErrorString::UnexpectedToken(t)))
            }
            SelectorParseErrorKind::ExpectedNamespace(namespace) => {
                (None, Some(ErrorString::Ident(namespace)))
            }
            SelectorParseErrorKind::UnsupportedPseudoClassOrElement(p) => {
                (None, Some(ErrorString::Ident(p)))
            }
            SelectorParseErrorKind::EmptySelector |
            SelectorParseErrorKind::DanglingCombinator => {
                (None, None)
            }
            SelectorParseErrorKind::EmptyNegation => {
                (None, Some(ErrorString::Snippet(")".into())))
            }
            err => match extract_error_param(ParseErrorKind::Custom(StyleParseErrorKind::SelectorError(err))) {
                Some(e) => (Some(e), None),
                None => return None,
            }
        },
        err => match extract_error_param(err) {
            Some(e) => (Some(e), None),
            None => return None,
        }
    };
    Some(ErrorParams {
        main_param: main,
        prefix_param: prefix,
    })
}

impl<'a> ErrorHelpers<'a> for ContextualParseError<'a> {
    fn error_data(self) -> (CowRcStr<'a>, ErrorKind<'a>) {
        match self {
            ContextualParseError::UnsupportedPropertyDeclaration(s, err) |
            ContextualParseError::UnsupportedFontFaceDescriptor(s, err) |
            ContextualParseError::UnsupportedFontFeatureValuesDescriptor(s, err) |
            ContextualParseError::InvalidKeyframeRule(s, err) |
            ContextualParseError::InvalidFontFeatureValuesRule(s, err) |
            ContextualParseError::UnsupportedKeyframePropertyDeclaration(s, err) |
            ContextualParseError::InvalidRule(s, err) |
            ContextualParseError::UnsupportedRule(s, err) |
            ContextualParseError::UnsupportedViewportDescriptorDeclaration(s, err) |
            ContextualParseError::UnsupportedCounterStyleDescriptorDeclaration(s, err) |
            ContextualParseError::InvalidMediaRule(s, err) => {
                (s.into(), err.kind)
            }
            ContextualParseError::InvalidCounterStyleWithoutSymbols(s) |
            ContextualParseError::InvalidCounterStyleNotEnoughSymbols(s) => {
                (s.into(), ParseErrorKind::Custom(StyleParseErrorKind::UnspecifiedError.into()))
            }
            ContextualParseError::InvalidCounterStyleWithoutAdditiveSymbols |
            ContextualParseError::InvalidCounterStyleExtendsWithSymbols |
            ContextualParseError::InvalidCounterStyleExtendsWithAdditiveSymbols => {
                ("".into(), ParseErrorKind::Custom(StyleParseErrorKind::UnspecifiedError.into()))
            }
        }
    }

    fn error_params(self) -> ErrorParams<'a> {
        let (s, error) = self.error_data();
        extract_error_params(error).unwrap_or_else(|| ErrorParams {
            main_param: Some(ErrorString::Snippet(s)),
            prefix_param: None
        })
    }

    fn to_gecko_message(&self) -> (Option<&'static [u8]>, &'static [u8], Action) {
        let (msg, action): (&[u8], Action) = match *self {
            ContextualParseError::UnsupportedPropertyDeclaration(
                _, ParseError { kind: ParseErrorKind::Basic(BasicParseErrorKind::UnexpectedToken(_)), .. }
            ) |
            ContextualParseError::UnsupportedPropertyDeclaration(
                _, ParseError { kind: ParseErrorKind::Basic(BasicParseErrorKind::AtRuleInvalid(_)), .. }
            ) => {
                (b"PEParseDeclarationDeclExpected\0", Action::Skip)
            }
            ContextualParseError::UnsupportedPropertyDeclaration(
                _, ParseError { kind: ParseErrorKind::Custom(ref err), .. }
            ) => {
                match *err {
                    StyleParseErrorKind::InvalidColor(_, _) => {
                        return (Some(b"PEColorNotColor\0"),
                                b"PEValueParsingError\0", Action::Drop)
                    }
                    StyleParseErrorKind::InvalidFilter(_, _) => {
                        return (Some(b"PEExpectedNoneOrURLOrFilterFunction\0"),
                                b"PEValueParsingError\0", Action::Drop)
                    }
                    StyleParseErrorKind::OtherInvalidValue(_) => {
                        (b"PEValueParsingError\0", Action::Drop)
                    }
                    _ => (b"PEUnknownProperty\0", Action::Drop)
                }
            }
            ContextualParseError::UnsupportedPropertyDeclaration(..) =>
                (b"PEUnknownProperty\0", Action::Drop),
            ContextualParseError::UnsupportedFontFaceDescriptor(..) =>
                (b"PEUnknwnFontDesc\0", Action::Skip),
            ContextualParseError::InvalidKeyframeRule(..) =>
                (b"PEKeyframeBadName\0", Action::Nothing),
            ContextualParseError::UnsupportedKeyframePropertyDeclaration(..) =>
                (b"PEBadSelectorKeyframeRuleIgnored\0", Action::Nothing),
            ContextualParseError::InvalidRule(
                _, ParseError { kind: ParseErrorKind::Custom(
                    StyleParseErrorKind::UnexpectedTokenWithinNamespace(_)
                ), .. }
            ) => {
                (b"PEAtNSUnexpected\0", Action::Nothing)
            }
            ContextualParseError::InvalidRule(
                _, ParseError { kind: ParseErrorKind::Basic(BasicParseErrorKind::AtRuleInvalid(_)), .. }
            ) |
            ContextualParseError::InvalidRule(
                _, ParseError { kind: ParseErrorKind::Custom(
                    StyleParseErrorKind::UnsupportedAtRule(_)
                ), .. }
            ) => {
                (b"PEUnknownAtRule\0", Action::Nothing)
            }
            ContextualParseError::InvalidRule(_, ref err) => {
                let prefix = match err.kind {
                    ParseErrorKind::Custom(StyleParseErrorKind::SelectorError(ref err)) => match *err {
                        SelectorParseErrorKind::UnexpectedTokenInAttributeSelector(_) => {
                            Some(&b"PEAttSelUnexpected\0"[..])
                        }
                        SelectorParseErrorKind::ExpectedBarInAttr(_) => {
                            Some(&b"PEAttSelNoBar\0"[..])
                        }
                        SelectorParseErrorKind::BadValueInAttr(_) => {
                            Some(&b"PEAttSelBadValue\0"[..])
                        }
                        SelectorParseErrorKind::NoQualifiedNameInAttributeSelector(_) => {
                            Some(&b"PEAttributeNameOrNamespaceExpected\0"[..])
                        }
                        SelectorParseErrorKind::InvalidQualNameInAttr(_) => {
                            Some(&b"PEAttributeNameExpected\0"[..])
                        }
                        SelectorParseErrorKind::ExplicitNamespaceUnexpectedToken(_) => {
                            Some(&b"PETypeSelNotType\0"[..])
                        }
                        SelectorParseErrorKind::ExpectedNamespace(_) => {
                           Some(&b"PEUnknownNamespacePrefix\0"[..])
                        }
                        SelectorParseErrorKind::EmptySelector => {
                            Some(&b"PESelectorGroupNoSelector\0"[..])
                        }
                        SelectorParseErrorKind::DanglingCombinator => {
                            Some(&b"PESelectorGroupExtraCombinator\0"[..])
                        }
                        SelectorParseErrorKind::UnsupportedPseudoClassOrElement(_) => {
                            Some(&b"PEPseudoSelUnknown\0"[..])
                        }
                        SelectorParseErrorKind::PseudoElementExpectedColon(_) => {
                            Some(&b"PEPseudoSelEndOrUserActionPC\0"[..])
                        }
                        SelectorParseErrorKind::NoIdentForPseudo(_) => {
                            Some(&b"PEPseudoClassArgNotIdent\0"[..])
                        }
                        SelectorParseErrorKind::PseudoElementExpectedIdent(_) => {
                            Some(&b"PEPseudoSelBadName\0"[..])
                        }
                        SelectorParseErrorKind::ClassNeedsIdent(_) => {
                            Some(&b"PEClassSelNotIdent\0"[..])
                        }
                        SelectorParseErrorKind::EmptyNegation => {
                            Some(&b"PENegationBadArg\0"[..])
                        }
                        _ => None,
                    },
                    _ => None,
                };
                return (prefix, b"PEBadSelectorRSIgnored\0", Action::Nothing);
            }
            ContextualParseError::InvalidMediaRule(_, ref err) => {
                let err: &[u8] = match err.kind {
                    ParseErrorKind::Custom(StyleParseErrorKind::ExpectedIdentifier(..)) => {
                        b"PEGatherMediaNotIdent\0"
                    },
                    ParseErrorKind::Custom(StyleParseErrorKind::MediaQueryExpectedFeatureName(..)) => {
                        b"PEMQExpectedFeatureName\0"
                    },
                    ParseErrorKind::Custom(StyleParseErrorKind::MediaQueryExpectedFeatureValue) => {
                        b"PEMQExpectedFeatureValue\0"
                    },
                    ParseErrorKind::Custom(StyleParseErrorKind::RangedExpressionWithNoValue) => {
                        b"PEMQNoMinMaxWithoutValue\0"
                    },
                    _ => {
                        b"PEDeclDropped\0"
                    },
                };
                (err, Action::Nothing)
            }
            ContextualParseError::UnsupportedRule(..) =>
                (b"PEDeclDropped\0", Action::Nothing),
            ContextualParseError::UnsupportedViewportDescriptorDeclaration(..) |
            ContextualParseError::UnsupportedCounterStyleDescriptorDeclaration(..) |
            ContextualParseError::InvalidCounterStyleWithoutSymbols(..) |
            ContextualParseError::InvalidCounterStyleNotEnoughSymbols(..) |
            ContextualParseError::InvalidCounterStyleWithoutAdditiveSymbols |
            ContextualParseError::InvalidCounterStyleExtendsWithSymbols |
            ContextualParseError::InvalidCounterStyleExtendsWithAdditiveSymbols |
            ContextualParseError::UnsupportedFontFeatureValuesDescriptor(..) |
            ContextualParseError::InvalidFontFeatureValuesRule(..) =>
                (b"PEUnknownAtRule\0", Action::Skip),
        };
        (None, msg, action)
    }
}

impl ErrorReporter {
    pub fn report(&self, location: SourceLocation, error: ContextualParseError) {
        let (pre, name, action) = error.to_gecko_message();
        let suffix = match action {
            Action::Nothing => ptr::null(),
            Action::Skip => b"PEDeclSkipped\0".as_ptr(),
            Action::Drop => b"PEDeclDropped\0".as_ptr(),
        };
        let params = error.error_params();
        let param = params.main_param;
        let pre_param = params.prefix_param;
        let param = param.map(|p| p.into_str());
        let pre_param = pre_param.map(|p| p.into_str());
        let param_ptr = param.as_ref().map_or(ptr::null(), |p| p.as_ptr());
        let pre_param_ptr = pre_param.as_ref().map_or(ptr::null(), |p| p.as_ptr());
        // The CSS source text is unused and will be removed in bug 1381188.
        let source = "";
        unsafe {
            Gecko_ReportUnexpectedCSSError(self.0,
                                           name.as_ptr() as *const _,
                                           param_ptr as *const _,
                                           param.as_ref().map_or(0, |p| p.len()) as u32,
                                           pre.map_or(ptr::null(), |p| p.as_ptr()) as *const _,
                                           pre_param_ptr as *const _,
                                           pre_param.as_ref().map_or(0, |p| p.len()) as u32,
                                           suffix as *const _,
                                           source.as_ptr() as *const _,
                                           source.len() as u32,
                                           location.line,
                                           location.column);
        }
    }
}

impl ParseErrorReporter for ErrorReporter {
    fn report_error(
        &self,
        _url: &UrlExtraData,
        location: SourceLocation,
        error: ContextualParseError
    ) {
        self.report(location, error)
    }
}
<|endoftext|>


<commit_before>enum OpType{
    Add,             // +
    Sub,             // -
    Mul,             // *
    Div,             // /
    Modulus,         // %
    Pow,             // ^
}

enum Element{
    Operator(OpType),
    Value(f32),
    Variable(String)
}

use stack::List;
use calc::OpType::*;
use calc::Element::*;
use std::str;

fn get_op_prioroty(op_ch: &char) -> i32{
    match *op_ch{
        '('              => 0,
        ')'              => 1,
        '+' | '-'        => 2,
        '*' | '/' | '%'  => 3,
        '^'              => 4,
        _                => -1
    }
}

fn read_char(option: Option<char>) -> char{
    match option{
        Some(x) => x,
        None => panic!("Ohh, we got end of stack!")
    }
}

fn get_polen_notation(expression: &String) -> Result<String, String>{
    let mut result = String::new();
    let mut stack = String::new();

    let mut operand = false;
    let mut prev_space = false;

    'process: for c in expression.chars(){

        if prev_space && c != ' ' {
            result.push(' ');
            prev_space = false;
        }

        match c{
            '0'...'9' | '.' =>{
                result.push(c);
                operand = true;
            },
            '(' => {
                stack.push(c);},
            ')' => {
                loop{
                    if(stack.is_empty()){
                        return Err("Can't find closing parenthesis or unexpected delimiter.".to_string());
                    }

                    let stack_char = read_char(stack.pop());

                    if(stack_char != '('){
                        result.push(stack_char);
                    }else{
                        break;
                    }
                }
            },
            ' ' => {
                prev_space = true;
            },
            _ => {

                if !operand && c == '-' {
                    operand = true;
                    result.push(c);

                    continue 'process;
                }

                let prior = get_op_prioroty(&c);
                if(prior != -1){
                    operand = false;
                    loop{
                        if !stack.is_empty() {
                            //get last operator priority
                            let stack_operator = &(stack.as_bytes()[stack.len() - 1] as char);
                            if prior <= get_op_prioroty(stack_operator) {
                                stack.pop();
                                result.push(*stack_operator);
                            }else{break;}
                        }else{ //return Err("Stack is empty".to_string());
                            break;
                        }
                    }
                    stack.push(c);
                    prev_space = true;
                }else{
                    return Err(format!("Unexpected char: <{}>, expected operator", c));
                }
            }
        }
    }

    while !stack.is_empty(){
        result.push(read_char(stack.pop()));
    }

    return Ok(result);
}

fn read_f32(option: Option<f32>) -> f32{
    match option{
        Some(x) => x,
        None => panic!("Ohh, we got end of stack!")
    }
}

fn calc_expression(expr_stack: List<Element>) -> f32{
    let mut val_stack: List<f32> = List::new();

    for x in expr_stack.iter(){
        match x{
            &Operator(ref op) => {
                let val2 = read_f32(val_stack.pop());
                let val1 = read_f32(val_stack.pop());

                val_stack.push( match *op {
                    Add => val1 + val2,
                    Sub => val1 - val2,
                    Mul => val1 * val2,
                    Div => val1 / val2,
                    Modulus => val1 % val2,
                    Pow => val1.powf(val2)
                });
            },

            &Value(ref val) => {
                val_stack.push(*val);
            },

            &Variable(ref var) => {
                unimplemented!()
            }
        }
    }

    return read_f32(val_stack.pop());
}

fn get_f32_from_string(val: &String) -> f32{
    match val.parse::<f32>(){
        Ok(x)  => x,
        Err(_) => 0.0
    }
}

fn get_element_by_string(token: &String) -> Element{
    match (token.as_bytes()[0] as char){
        '+' => Operator(Add),
        '-' => if token.len() > 1 {
            Value(get_f32_from_string(token))
        } else {
            Operator(Sub)
        },
        '*' => Operator(Mul),
        '/' => Operator(Div),
        '%' => Operator(Modulus),
        '^' => Operator(Pow),
         _ => {
             Value(get_f32_from_string(token))
        }
    }
}

fn print_stack(stack: &List<Element>){
    for x in stack.iter(){
        print!("{} ",
        match *x{
             Operator(Add) => "+".to_string(),
            Operator(Sub) => "-".to_string(),
            Operator(Mul) => "*".to_string(),
            Operator(Div) => "/".to_string(),
            Operator(Modulus) => "%".to_string(),
            Operator(Pow) => "^".to_string(),
            Value(val) => val.to_string(),
            Variable(_) => "ITS VAR".to_string()
        });
    }
    print!("/n");
}

fn string_to_list(str_expr: &String) -> List<Element>{
    let mut tmp: List<Element> = List::new();
    let mut token: String = String::new();

    let mut operand = false;

    'tokenize: for ch in str_expr.chars(){
        match ch{
            '0'...'9' | '.' => {
                token.push(ch);
                operand = true;
            },
            '+'|'-'|'*'|'/'|'%'|'^' => {

                if !operand && ch == '-'{
                    token.push(ch);
                    operand = true;

                    continue 'tokenize;
                }

                if !token.is_empty(){
                    tmp.push(get_element_by_string( &(token) ));
                    token.clear();
                }

                token.push(ch);

                tmp.push(get_element_by_string( &(token) ));
                token.clear();

                operand = false;
            },
            ' ' => if !token.is_empty(){
                operand = false;
                tmp.push(get_element_by_string( &(token) ));
                token.clear();
            },
            _ => {println!("unexpected situation")}
        }
    }

    print_stack(&tmp);

    let mut stack: List<Element> = List::new();

    while let Some(el) = tmp.pop(){
        stack.push(el);
    }

    return stack;
}

pub fn calc(infix_str: &String) -> Result<f32, String>{
    let stack2;
    match get_polen_notation(infix_str) {
        Ok(x) => {
            stack2 = string_to_list(&x);
            Ok(calc_expression(stack2))
        },
        Err(x) => Err(x)
    }
}

#[cfg(test)]
mod calc_test {
    use super::calc;

    #[test]
    fn calc_basics() {

        let tests_result_4 = vec![
            "2+2",
            "2 +2",
            "2 + 2",
            " 2 + 2 ",
            "(2+2)",
            "( 2+2)",
            " ( 2 +2)",
            "(2+ 2)",
            "(2+ 2 )",
            "(2+ 2 ) ",
            "(((2+2)))",
            "(((2)) + 2)",
            "4-0",
            "8/2",
            "8*0.5",
            "9.5 - 5.5",
            "9 % 5",
            "-4 + 8",
            "8 + -4",
            "-2 + 6"
        ];

        for test in tests_result_4{
            match calc(&test.to_string()) {
                Ok(x) => assert_eq!(x, 4.0),
                Err(x) => {
                    println!("FAILED TEST: {}, error: {} \n", test, x);
                    assert!(false);
                }
            }
        }

    }

    #[test]
    fn calc_long_expression(){
        let tests_result_2648 = vec![
            "55 * (3552 / 74) + 2^3",
            "55 * (3552 / (37 * 2)) + 2^3",
            "(54 + 1) * (3552 / (37 * 2)) + 2^3",
            "55 * (3552 / (147 % 74 + 1)) + 2^3",
            "55 * (3552 / 74) + 2^(2+1)",
            "55 * (3552 / 74) + 2^(4-1)",
            "55 * (3552 / 74) + 2^(0.5 * 6)",
            "55 * (3552 / 74) + 2^(6 * 0.5)",
            "2^3 + 55 / (1/(3552 * (1/74)))",
        ];

        for test in tests_result_2648{
            match calc(&test.to_string()) {
                Ok(x) => assert_eq!(x, 2648.0),
                Err(x) => {
                    println!("FAILED TEST: {}, error: {} \n", test, x);
                    assert!(false);
                }
            }
        }
    }
}
<commit_msg>More tests and some fixes added<commit_after>enum OpType {
    Add, // +
    Sub, // -
    Mul, // *
    Div, // /
    Modulus, // %
    Pow, // ^
}

enum Element {
    Operator(OpType),
    Value(f32),
    Variable(String),
}

use stack::List;
use calc::OpType::*;
use calc::Element::*;
use std::str;

fn get_op_prioroty(op_ch: &char) -> i32 {
    match *op_ch {
        '(' => 0,
        ')' => 1,
        '+' | '-' => 2,
        '*' | '/' | '%' => 3,
        '^' => 4,
        _ => -1,
    }
}

fn get_polen_notation(expression: &String) -> Result<String, String> {
    let mut result = String::new();
    let mut stack = String::new();

    let mut operand = false;
    let mut prev_space = false;

    let mut idx = -1;

    'process: for c in expression.chars() {
        idx += 1;

        if prev_space && c != ' ' {
            result.push(' ');
            prev_space = false;
        }

        match c {
            '0'...'9' | '.' => {
                result.push(c);
                operand = true;
            }
            '(' => {
                stack.push(c);
            }
            ')' => {
                loop {
                    if (stack.is_empty()) {
                        return Err("Can't find closing parenthesis!".to_string());
                    }

                    let stack_char: char;
                    match (stack.pop()) {
                        Some(x) => stack_char = x,
                        None => return Err("End of stack is reached!".to_string()),
                    }

                    if (stack_char != '(') {
                        result.push(stack_char);
                    } else {
                        break;
                    }
                }
            }
            ' ' => {
                prev_space = true;
            }
            _ => {

                if !operand && c == '-' {
                    operand = true;
                    result.push(c);

                    continue 'process;
                }

                let prior = get_op_prioroty(&c);
                if (prior != -1) {
                    operand = false;
                    loop {
                        if !stack.is_empty() {
                            // get last operator priority
                            let stack_operator = &(stack.as_bytes()[stack.len() - 1] as char);

                            if prior <= get_op_prioroty(stack_operator) {
                                stack.pop();
                                result.push(*stack_operator);
                            } else {
                                break;
                            }

                        } else {
                            // return Err("Stack is empty".to_string());
                            break;
                        }
                    }
                    stack.push(c);
                    prev_space = true;
                } else {
                    return Err(format!("Unexpected char: <{}> at pos {}", c, &idx));
                }
            }
        }
    }

    while !stack.is_empty() {
        match stack.pop() {
            Some(x) => result.push(x),
            None => return Err("unexpected".to_string()),
        }
    }

    return Ok(result);
}

// fn read_f32(option: Option<f32>) -> f32 {
//     match option {
//         Some(x) => x,
//         None => panic!("Ohh, we got end of stack!"),
//     }
// }

fn calc_expression(expr_stack: List<Element>) -> Option<f32> {
    let mut val_stack: List<f32> = List::new();

    for x in expr_stack.iter() {
        match x {
            &Operator(ref op) => {
                let val2: f32;// = read_f32(val_stack.pop());
                let val1: f32;// = read_f32(val_stack.pop());

                match val_stack.pop() {
                    Some(x) => val2 = x,
                    None => return None,
                }

                match val_stack.pop() {
                    Some(x) => val1 = x,
                    None => return None,
                }

                val_stack.push(match *op {
                    Add => val1 + val2,
                    Sub => val1 - val2,
                    Mul => val1 * val2,
                    Div => val1 / val2,
                    Modulus => val1 % val2,
                    Pow => val1.powf(val2),
                });
            }

            &Value(ref val) => {
                val_stack.push(*val);
            }

            &Variable(ref var) => unimplemented!(),
        }
    }

    return match (val_stack.pop()) {
        Some(x) => {
            match val_stack.pop() {
                // not all values were used, it means that expression looks like this  "10 /(2 4)"
                Some(y) => {
                    return None;
                }

                None => Some(x),
            }
        }
        None => None,
    };
}

fn get_f32_from_string(val: &String) -> f32 {
    match val.parse::<f32>() {
        Ok(x) => x,
        Err(_) => 0.0,
    }
}

fn get_element_by_string(token: &String) -> Element {
    match (token.as_bytes()[0] as char) {
        '+' => Operator(Add),
        '-' => {
            if token.len() > 1 {
                Value(get_f32_from_string(token))
            } else {
                Operator(Sub)
            }
        }
        '*' => Operator(Mul),
        '/' => Operator(Div),
        '%' => Operator(Modulus),
        '^' => Operator(Pow),
        _ => Value(get_f32_from_string(token)),
    }
}

// fn print_stack(stack: &List<Element>) {
//     for x in stack.iter() {
//         print!("{} ",
//                match *x {
//                    Operator(Add) => "+".to_string(),
//                    Operator(Sub) => "-".to_string(),
//                    Operator(Mul) => "*".to_string(),
//                    Operator(Div) => "/".to_string(),
//                    Operator(Modulus) => "%".to_string(),
//                    Operator(Pow) => "^".to_string(),
//                    Value(val) => val.to_string(),
//                    Variable(_) => "ITS VAR".to_string(),
//                });
//     }
//     print!("/n");
// }

fn string_to_list(str_expr: &String) -> List<Element> {
    let mut tmp: List<Element> = List::new();
    let mut token: String = String::new();

    let mut operand = false;
    let mut prev_operator = false;

    'tokenize: for ch in str_expr.chars() {
        match ch {
            '0'...'9' | '.' => {
                token.push(ch);
                operand = true;
                prev_operator = false;
            }
            '+' | '-' | '*' | '/' | '%' | '^' => {

                if !operand && ch == '-' && !prev_operator {
                    token.push(ch);
                    operand = true;

                    continue 'tokenize;
                }

                if !token.is_empty() {
                    tmp.push(get_element_by_string(&(token)));
                    token.clear();
                }

                token.push(ch);

                tmp.push(get_element_by_string(&(token)));
                token.clear();

                operand = false;
                prev_operator = true;
            }
            ' ' => {
                prev_operator = false;
                if !token.is_empty() {
                    operand = false;
                    tmp.push(get_element_by_string(&(token)));
                    token.clear();
                }
            }
            _ => println!("unexpected situation"),
        }
    }

    // print_stack(&tmp);

    let mut stack: List<Element> = List::new();

    while let Some(el) = tmp.pop() {
        stack.push(el);
    }

    return stack;
}

pub fn calc(infix_str: &String) -> Result<f32, String> {
    let stack2;
    match get_polen_notation(infix_str) {
        Ok(x) => {
            stack2 = string_to_list(&x);

            match calc_expression(stack2) {
                Some(x) => Ok(x),
                None => Err("Incorrect expression!".to_string()),
            }
        }
        Err(x) => Err(x),
    }
}

#[cfg(test)]
mod calc_test {
    use super::calc;

    #[test]
    fn calc_basics() {

        let tests_result_4 = vec!["2+2",
                                  "2 +2",
                                  "2 + 2",
                                  " 2 + 2 ",
                                  "(2+2)",
                                  "( 2+2)",
                                  " ( 2 +2)",
                                  "(2+ 2)",
                                  "(2+ 2 )",
                                  "(2+ 2 ) ",
                                  "(((2+2)))",
                                  "(((2)) + 2)",
                                  "4-0",
                                  "8/2",
                                  "8*0.5",
                                  "9.5 - 5.5",
                                  "9 % 5",
                                  "-4 + 8",
                                  "8 + -4",
                                  "-2 + 6"];

        for test in tests_result_4 {
            match calc(&test.to_string()) {
                Ok(x) => assert_eq!(x, 4.0),
                Err(x) => {
                    println!("FAILED TEST: {}, error: {} \n", test, x);
                    assert!(false);
                }
            }
        }

    }

    #[test]
    fn calc_long_expression() {
        let tests_result_2648 = vec!["55 * (3552 / 74) + 2^3",
                                     "55 * (3552 / (37 * 2)) + 2^3",
                                     "(54 + 1) * (3552 / (37 * 2)) + 2^3",
                                     "55 * (3552 / (147 % 74 + 1)) + 2^3",
                                     "55 * (3552 / 74) + 2^(2+1)",
                                     "55 * (3552 / 74) + 2^(4-1)",
                                     "55 * (3552 / 74) + 2^(0.5 * 6)",
                                     "55 * (3552 / 74) + 2^(6 * 0.5)",
                                     "2^3 + 55 / (1/(3552 * (1/74)))",
                                     "55 * (3552 / (74-(-74+74)) ) + 2^3",
                                     "55 * (3552 / 74) + 2^3 - (-69 - 9) + (-69 - 9)"];

        for test in tests_result_2648 {
            match calc(&test.to_string()) {
                Ok(x) => assert_eq!(x, 2648.0),
                Err(x) => {
                    println!("FAILED TEST: {}, error: {} \n", test, x);
                    assert!(false);
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>//! System tests for compiling Rust code with cargo.
//!
//! Any copyright is dedicated to the Public Domain.
//! http://creativecommons.org/publicdomain/zero/1.0/

#![deny(rust_2018_idioms)]

#[cfg(not(target_os = "macos"))]
#[macro_use]
extern crate log;

/// Test that building a simple Rust crate with cargo using sccache results in a cache hit
/// when built a second time.
#[test]
#[cfg(not(target_os = "macos"))] // test currently fails on macos
fn test_rust_cargo() {
    test_rust_cargo_cmd("check");
    test_rust_cargo_cmd("build");
}

#[cfg(not(target_os = "macos"))] // test currently fails on macos
fn test_rust_cargo_cmd(cmd: &str) {
    use assert_cmd::prelude::*;
    use chrono::Local;
    use escargot::CargoBuild;
    use predicates::prelude::*;
    use std::env;
    use std::fs;
    use std::io::Write;
    use std::path::Path;
    use std::process::{Command, Stdio};

    fn sccache_command() -> Command {
        CargoBuild::new()
            .bin("sccache")
            .current_release()
            .current_target()
            .run()
            .unwrap()
            .command()
    }

    fn stop() {
        trace!("sccache --stop-server");
        drop(
            sccache_command()
                .arg("--stop-server")
                .stdout(Stdio::null())
                .stderr(Stdio::null())
                .status(),
        );
    }

    drop(
        env_logger::Builder::new()
            .format(|f, record| {
                write!(
                    f,
                    "{} [{}] - {}",
                    Local::now().format("%Y-%m-%dT%H:%M:%S%.3f"),
                    record.level(),
                    record.args()
                )
            })
            .parse(&env::var("RUST_LOG").unwrap_or_default())
            .try_init(),
    );
    let cargo = env!("CARGO");
    debug!("cargo: {}", cargo);
    #[allow(deprecated)]
    let sccache = assert_cmd::cargo::main_binary_path().unwrap();
    debug!("sccache: {:?}", sccache);
    let crate_dir = Path::new(file!()).parent().unwrap().join("test-crate");
    // Ensure there's no existing sccache server running.
    stop();
    // Create a temp directory to use for the disk cache.
    let tempdir = tempfile::Builder::new()
        .prefix("sccache_test_rust_cargo")
        .tempdir()
        .unwrap();
    let cache_dir = tempdir.path().join("cache");
    fs::create_dir(&cache_dir).unwrap();
    let cargo_dir = tempdir.path().join("cargo");
    fs::create_dir(&cargo_dir).unwrap();
    // Start a new sccache server.
    trace!("sccache --start-server");
    sccache_command()
        .arg("--start-server")
        .env("SCCACHE_DIR", &cache_dir)
        .assert()
        .success();
    // `cargo clean` first, just to be sure there's no leftover build objects.
    let envs = vec![
        ("RUSTC_WRAPPER", &sccache),
        ("CARGO_TARGET_DIR", &cargo_dir),
    ];
    Command::new(&cargo)
        .args(&["clean"])
        .envs(envs.iter().map(|v| *v))
        .current_dir(&crate_dir)
        .assert()
        .success();
    // Now build the crate with cargo.
    Command::new(&cargo)
        .args(&[cmd, "--color=never"])
        .envs(envs.iter().map(|v| *v))
        .current_dir(&crate_dir)
        .assert()
        .stderr(predicates::str::contains("\x1b[").from_utf8().not())
        .success();
    // Clean it so we can build it again.
    Command::new(&cargo)
        .args(&["clean"])
        .envs(envs.iter().map(|v| *v))
        .current_dir(&crate_dir)
        .assert()
        .success();
    Command::new(&cargo)
        .args(&[cmd, "--color=always"])
        .envs(envs.iter().map(|v| *v))
        .current_dir(&crate_dir)
        .assert()
        .stderr(predicates::str::contains("\x1b[").from_utf8())
        .success();
    // Now get the stats and ensure that we had a cache hit for the second build.
    // Ideally we'd check the stats more usefully here--the test crate has one dependency (itoa)
    // so there are two separate compilations, but cargo will build the test crate with
    // incremental compilation enabled, so sccache will not cache it.
    trace!("sccache --show-stats");
    sccache_command()
        .args(&["--show-stats", "--stats-format=json"])
        .assert()
        .stdout(predicates::str::contains(r#""cache_hits":{"counts":{"Rust":1}}"#).from_utf8())
        .success();
    stop();
}
<commit_msg>disable sccache_cargo tests on windows<commit_after>//! System tests for compiling Rust code with cargo.
//!
//! Any copyright is dedicated to the Public Domain.
//! http://creativecommons.org/publicdomain/zero/1.0/

#![deny(rust_2018_idioms)]

#[cfg(all(not(target_os = "windows"), not(target_os = "macos")))]
#[macro_use]
extern crate log;

/// Test that building a simple Rust crate with cargo using sccache results in a cache hit
/// when built a second time.
#[test]
#[cfg(all(not(target_os = "windows"), not(target_os = "macos")))]
fn test_rust_cargo() {
    test_rust_cargo_cmd("check");
    test_rust_cargo_cmd("build");
}

#[cfg(all(not(target_os = "windows"), not(target_os = "macos")))]
fn test_rust_cargo_cmd(cmd: &str) {
    use assert_cmd::prelude::*;
    use chrono::Local;
    use escargot::CargoBuild;
    use predicates::prelude::*;
    use std::env;
    use std::fs;
    use std::io::Write;
    use std::path::Path;
    use std::process::{Command, Stdio};

    fn sccache_command() -> Command {
        CargoBuild::new()
            .bin("sccache")
            .current_release()
            .current_target()
            .run()
            .unwrap()
            .command()
    }

    fn stop() {
        trace!("sccache --stop-server");
        drop(
            sccache_command()
                .arg("--stop-server")
                .stdout(Stdio::null())
                .stderr(Stdio::null())
                .status(),
        );
    }

    drop(
        env_logger::Builder::new()
            .format(|f, record| {
                write!(
                    f,
                    "{} [{}] - {}",
                    Local::now().format("%Y-%m-%dT%H:%M:%S%.3f"),
                    record.level(),
                    record.args()
                )
            })
            .parse(&env::var("RUST_LOG").unwrap_or_default())
            .try_init(),
    );
    let cargo = env!("CARGO");
    debug!("cargo: {}", cargo);
    #[allow(deprecated)]
    let sccache = assert_cmd::cargo::main_binary_path().unwrap();
    debug!("sccache: {:?}", sccache);
    let crate_dir = Path::new(file!()).parent().unwrap().join("test-crate");
    // Ensure there's no existing sccache server running.
    stop();
    // Create a temp directory to use for the disk cache.
    let tempdir = tempfile::Builder::new()
        .prefix("sccache_test_rust_cargo")
        .tempdir()
        .unwrap();
    let cache_dir = tempdir.path().join("cache");
    fs::create_dir(&cache_dir).unwrap();
    let cargo_dir = tempdir.path().join("cargo");
    fs::create_dir(&cargo_dir).unwrap();
    // Start a new sccache server.
    trace!("sccache --start-server");
    sccache_command()
        .arg("--start-server")
        .env("SCCACHE_DIR", &cache_dir)
        .assert()
        .success();
    // `cargo clean` first, just to be sure there's no leftover build objects.
    let envs = vec![
        ("RUSTC_WRAPPER", &sccache),
        ("CARGO_TARGET_DIR", &cargo_dir),
    ];
    Command::new(&cargo)
        .args(&["clean"])
        .envs(envs.iter().map(|v| *v))
        .current_dir(&crate_dir)
        .assert()
        .success();
    // Now build the crate with cargo.
    Command::new(&cargo)
        .args(&[cmd, "--color=never"])
        .envs(envs.iter().map(|v| *v))
        .current_dir(&crate_dir)
        .assert()
        .stderr(predicates::str::contains("\x1b[").from_utf8().not())
        .success();
    // Clean it so we can build it again.
    Command::new(&cargo)
        .args(&["clean"])
        .envs(envs.iter().map(|v| *v))
        .current_dir(&crate_dir)
        .assert()
        .success();
    Command::new(&cargo)
        .args(&[cmd, "--color=always"])
        .envs(envs.iter().map(|v| *v))
        .current_dir(&crate_dir)
        .assert()
        .stderr(predicates::str::contains("\x1b[").from_utf8())
        .success();
    // Now get the stats and ensure that we had a cache hit for the second build.
    // Ideally we'd check the stats more usefully here--the test crate has one dependency (itoa)
    // so there are two separate compilations, but cargo will build the test crate with
    // incremental compilation enabled, so sccache will not cache it.
    trace!("sccache --show-stats");
    sccache_command()
        .args(&["--show-stats", "--stats-format=json"])
        .assert()
        .stdout(predicates::str::contains(r#""cache_hits":{"counts":{"Rust":1}}"#).from_utf8())
        .success();
    stop();
}
<|endoftext|>