<commit_before>use super::{Region, RegionIndex};
use std::mem;
use rustc::infer::InferCtxt;
use rustc::mir::{Location, Mir};
use rustc_data_structures::indexed_vec::{Idx, IndexVec};
use rustc_data_structures::fx::FxHashSet;

pub struct InferenceContext {
    definitions: IndexVec<RegionIndex, VarDefinition>,
    constraints: IndexVec<ConstraintIndex, Constraint>,
    errors: IndexVec<InferenceErrorIndex, InferenceError>,
}

pub struct InferenceError {
    pub constraint_point: Location,
    pub name: (), // TODO(nashenas88) RegionName
}

newtype_index!(InferenceErrorIndex);

struct VarDefinition {
    name: (), // TODO(nashenas88) RegionName
    value: Region,
    capped: bool,
}

impl VarDefinition {
    pub fn new(value: Region) -> Self {
        Self {
            name: (),
            value,
            capped: false,
        }
    }
}

#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct Constraint {
    sub: RegionIndex,
    sup: RegionIndex,
    point: Location,
}

newtype_index!(ConstraintIndex);

impl InferenceContext {
    pub fn new(values: IndexVec<RegionIndex, Region>) -> Self {
        Self {
            definitions: values.into_iter().map(VarDefinition::new).collect(),
            constraints: IndexVec::new(),
            errors: IndexVec::new(),
        }
    }

    pub fn cap_var(&mut self, v: RegionIndex) {
        self.definitions[v].capped = true;
    }

    pub fn add_live_point(&mut self, v: RegionIndex, point: Location) {
        debug!("add_live_point({:?}, {:?})", v, point);
        let definition = &mut self.definitions[v];
        if definition.value.add_point(point) {
            if definition.capped {
                self.errors.push(InferenceError {
                    constraint_point: point,
                    name: definition.name,
                });
            }
        }
    }

    pub fn add_outlives(&mut self, sup: RegionIndex, sub: RegionIndex, point: Location) {
        debug!("add_outlives({:?}: {:?} @ {:?}", sup, sub, point);
        self.constraints.push(Constraint { sup, sub, point });
    }

    pub fn region(&self, v: RegionIndex) -> &Region {
        &self.definitions[v].value
    }

    pub fn solve<'a, 'gcx, 'tcx>(
        &mut self,
        infcx: InferCtxt<'a, 'gcx, 'tcx>,
        mir: &'a Mir<'tcx>,
    ) -> IndexVec<InferenceErrorIndex, InferenceError>
    where
        'gcx: 'tcx + 'a,
        'tcx: 'a,
    {
        let mut changed = true;
        let mut dfs = Dfs::new(infcx, mir);
        while changed {
            changed = false;
            for constraint in &self.constraints {
                let sub = &self.definitions[constraint.sub].value.clone();
                let sup_def = &self.definitions[constraint.sup];
                debug!("constraint: {:?}", constraint);
                debug!("    sub (before): {:?}", sub);
                debug!("    sup (before): {:?}", sup_def.value);

                if dfs.copy(sub, &mut sup_def.value, constraint.point) {
                    changed = true;
                    if sup_def.capped {
                        // This is kind of a hack, but when we add a
                        // constraint, the "point" is always the point
                        // AFTER the action that induced the
                        // constraint. So report the error on the
                        // action BEFORE that.
                        assert!(constraint.point.statement_index > 0);
                        let p = Location {
                            block: constraint.point.block,
                            statement_index: constraint.point.statement_index - 1,
                        };

                        self.errors.push(InferenceError {
                            constraint_point: p,
                            name: sup_def.name,
                        });
                    }
                }

                debug!("    sup (after) : {:?}", sup_def.value);
                debug!("    changed     : {:?}", changed);
            }
            debug!("\n");
        }

        mem::replace(&mut self.errors, IndexVec::new())
    }
}

struct Dfs<'a, 'gcx: 'tcx + 'a, 'tcx: 'a> {
    infcx: InferCtxt<'a, 'gcx, 'tcx>,
    mir: &'a Mir<'tcx>,
}

impl<'a, 'gcx: 'tcx, 'tcx: 'a> Dfs<'a, 'gcx, 'tcx> {
    fn new(infcx: InferCtxt<'a, 'gcx, 'tcx>, mir: &'a Mir<'tcx>) -> Self {
        Self { infcx, mir }
    }

    fn copy(
        &mut self,
        from_region: &Region,
        to_region: &mut Region,
        start_point: Location,
    ) -> bool {
        let mut changed = false;

        let mut stack = vec![];
        let mut visited = FxHashSet();

        stack.push(start_point);
        while let Some(p) = stack.pop() {
            debug!("        dfs: p={:?}", p);

            if !from_region.may_contain(p) {
                debug!("            not in from-region");
                continue;
            }

            if !visited.insert(p) {
                debug!("            already visited");
                continue;
            }

            changed |= to_region.add_point(p);

            let block_data = self.mir[p.block];
            let successor_points = if p.statement_index < block_data.statements.len() {
                vec![Location {
                    statement_index: p.statement_index + 1,
                    ..p
                }]
            } else {
                block_data.terminator()
                    .successors()
                    .iter()
                    .map(|&basic_block| Location {
                        statement_index: 0,
                        block: basic_block,
                    })
                    .collect::<Vec<_>>()
            };

            if successor_points.is_empty() {
                // TODO handle free regions
                // If we reach the END point in the graph, then copy
                // over any skolemized end points in the `from_region`
                // and make sure they are included in the `to_region`.
                // for region_decl in self.infcx.tcx.tables.borrow().free_region_map() {
                //     // TODO(nashenas88) figure out skolemized_end points
                //     let block = self.env.graph.skolemized_end(region_decl.name);
                //     let skolemized_end_point = Location {
                //         block,
                //         statement_index: 0,
                //     };
                //     changed |= to_region.add_point(skolemized_end_point);
                // }
            } else {
                stack.extend(successor_points);
            }
        }

        changed
    }
}
<commit_msg>sup_def is a mutable reference<commit_after>use super::{Region, RegionIndex};
use std::mem;
use rustc::infer::InferCtxt;
use rustc::mir::{Location, Mir};
use rustc_data_structures::indexed_vec::{Idx, IndexVec};
use rustc_data_structures::fx::FxHashSet;

pub struct InferenceContext {
    definitions: IndexVec<RegionIndex, VarDefinition>,
    constraints: IndexVec<ConstraintIndex, Constraint>,
    errors: IndexVec<InferenceErrorIndex, InferenceError>,
}

pub struct InferenceError {
    pub constraint_point: Location,
    pub name: (), // TODO(nashenas88) RegionName
}

newtype_index!(InferenceErrorIndex);

struct VarDefinition {
    name: (), // TODO(nashenas88) RegionName
    value: Region,
    capped: bool,
}

impl VarDefinition {
    pub fn new(value: Region) -> Self {
        Self {
            name: (),
            value,
            capped: false,
        }
    }
}

#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct Constraint {
    sub: RegionIndex,
    sup: RegionIndex,
    point: Location,
}

newtype_index!(ConstraintIndex);

impl InferenceContext {
    pub fn new(values: IndexVec<RegionIndex, Region>) -> Self {
        Self {
            definitions: values.into_iter().map(VarDefinition::new).collect(),
            constraints: IndexVec::new(),
            errors: IndexVec::new(),
        }
    }

    pub fn cap_var(&mut self, v: RegionIndex) {
        self.definitions[v].capped = true;
    }

    pub fn add_live_point(&mut self, v: RegionIndex, point: Location) {
        debug!("add_live_point({:?}, {:?})", v, point);
        let definition = &mut self.definitions[v];
        if definition.value.add_point(point) {
            if definition.capped {
                self.errors.push(InferenceError {
                    constraint_point: point,
                    name: definition.name,
                });
            }
        }
    }

    pub fn add_outlives(&mut self, sup: RegionIndex, sub: RegionIndex, point: Location) {
        debug!("add_outlives({:?}: {:?} @ {:?}", sup, sub, point);
        self.constraints.push(Constraint { sup, sub, point });
    }

    pub fn region(&self, v: RegionIndex) -> &Region {
        &self.definitions[v].value
    }

    pub fn solve<'a, 'gcx, 'tcx>(
        &mut self,
        infcx: InferCtxt<'a, 'gcx, 'tcx>,
        mir: &'a Mir<'tcx>,
    ) -> IndexVec<InferenceErrorIndex, InferenceError>
    where
        'gcx: 'tcx + 'a,
        'tcx: 'a,
    {
        let mut changed = true;
        let mut dfs = Dfs::new(infcx, mir);
        while changed {
            changed = false;
            for constraint in &self.constraints {
                let sub = &self.definitions[constraint.sub].value.clone();
                let sup_def = &mut self.definitions[constraint.sup];
                debug!("constraint: {:?}", constraint);
                debug!("    sub (before): {:?}", sub);
                debug!("    sup (before): {:?}", sup_def.value);

                if dfs.copy(sub, &mut sup_def.value, constraint.point) {
                    changed = true;
                    if sup_def.capped {
                        // This is kind of a hack, but when we add a
                        // constraint, the "point" is always the point
                        // AFTER the action that induced the
                        // constraint. So report the error on the
                        // action BEFORE that.
                        assert!(constraint.point.statement_index > 0);
                        let p = Location {
                            block: constraint.point.block,
                            statement_index: constraint.point.statement_index - 1,
                        };

                        self.errors.push(InferenceError {
                            constraint_point: p,
                            name: sup_def.name,
                        });
                    }
                }

                debug!("    sup (after) : {:?}", sup_def.value);
                debug!("    changed     : {:?}", changed);
            }
            debug!("\n");
        }

        mem::replace(&mut self.errors, IndexVec::new())
    }
}

struct Dfs<'a, 'gcx: 'tcx + 'a, 'tcx: 'a> {
    infcx: InferCtxt<'a, 'gcx, 'tcx>,
    mir: &'a Mir<'tcx>,
}

impl<'a, 'gcx: 'tcx, 'tcx: 'a> Dfs<'a, 'gcx, 'tcx> {
    fn new(infcx: InferCtxt<'a, 'gcx, 'tcx>, mir: &'a Mir<'tcx>) -> Self {
        Self { infcx, mir }
    }

    fn copy(
        &mut self,
        from_region: &Region,
        to_region: &mut Region,
        start_point: Location,
    ) -> bool {
        let mut changed = false;

        let mut stack = vec![];
        let mut visited = FxHashSet();

        stack.push(start_point);
        while let Some(p) = stack.pop() {
            debug!("        dfs: p={:?}", p);

            if !from_region.may_contain(p) {
                debug!("            not in from-region");
                continue;
            }

            if !visited.insert(p) {
                debug!("            already visited");
                continue;
            }

            changed |= to_region.add_point(p);

            let block_data = self.mir[p.block];
            let successor_points = if p.statement_index < block_data.statements.len() {
                vec![Location {
                    statement_index: p.statement_index + 1,
                    ..p
                }]
            } else {
                block_data.terminator()
                    .successors()
                    .iter()
                    .map(|&basic_block| Location {
                        statement_index: 0,
                        block: basic_block,
                    })
                    .collect::<Vec<_>>()
            };

            if successor_points.is_empty() {
                // TODO handle free regions
                // If we reach the END point in the graph, then copy
                // over any skolemized end points in the `from_region`
                // and make sure they are included in the `to_region`.
                // for region_decl in self.infcx.tcx.tables.borrow().free_region_map() {
                //     // TODO(nashenas88) figure out skolemized_end points
                //     let block = self.env.graph.skolemized_end(region_decl.name);
                //     let skolemized_end_point = Location {
                //         block,
                //         statement_index: 0,
                //     };
                //     changed |= to_region.add_point(skolemized_end_point);
                // }
            } else {
                stack.extend(successor_points);
            }
        }

        changed
    }
}
<|endoftext|>


<commit_before>//! A library for interacting with a HackRF device.
//!
//! ## Examples
//! ```
//! extern crate hackrf;
//!
//! use hackrf::{HackRF, HackRFResult};
//!
//! const SAMP_RATE: u64 = 2_000_000; // 2 MHz
//!
//! fn main() {
//!    if let Err(e) = run() {
//!        println!("{:?}", e);
//!    }
//! }
//!
//! fn run() -> HackRFResult<()> {
//!     let context = try!(hackrf::init());
//!
//!     let mut device = try!(HackRF::open(&context));
//!     try!(device.set_samp_rate(SAMP_RATE as f64));
//!     try!(device.set_freq(150_000_000));
//!
//!     let mut rx_stream = try!(device.rx_stream());
//!
//!     // Take 10 seconds worth of samples
//!     for _ in 0..10 * SAMP_RATE {
//!         let (i, q) = rx_stream.next_sample();
//!         // .. Do something with samples ..
//!     }
//!
//!     Ok(())
//! }
//! ```

extern crate hackrf_sys as ffi;

use std::ffi::CStr;
use std::os::raw::{c_int, c_void};

use std::sync::{Mutex, Condvar};
use std::{mem, slice, ops};
use std::rc::Rc;

use ffi::hackrf_error::*;

/// The maximum size of each of the buffers used for receiving.
/// Note: this must be greater than or equal to the buffer size chosen for libusb.
const BUFFER_SIZE: usize = 262144;

/// The number of buffers to use to better handle overrun.
const NUM_BUFFERS: usize = 5;

/// An internal macro for handling hackrf errors.
macro_rules! hackrf_try {
    ($e:expr) => ({
        match $e {
            HACKRF_SUCCESS => (),
            error => return Err(parse_error(error)),
        }
    });
}

/// A error that can occur when trying to interact with a HackRF device.
///
/// ## Possible Error Values
///
///     HACKRF_ERROR_INVALID_PARAM = -2
///     HACKRF_ERROR_NOT_FOUND = -5
///     HACKRF_ERROR_BUSY = -6
///     HACKRF_ERROR_NO_MEM = -11
///     HACKRF_ERROR_LIBUSB = -1000
///     HACKRF_ERROR_THREAD = -1001
///     HACKRF_ERROR_STREAMING_THREAD_ERR = -1002
///     HACKRF_ERROR_STREAMING_STOPPED = -1003
///     HACKRF_ERROR_STREAMING_EXIT_CALLED = -1004
///     HACKRF_ERROR_OTHER = -9999
///
/// See [libhackrf](https://github.com/mossmann/hackrf/tree/master/host/libhackrf) for more info.
#[derive(Debug)]
pub struct HackRFError {
    /// The description of the code.
    pub desc: String,

    /// The code returned by `libhackrf`.
    pub code: i32,
}

/// A specialized Result type for HackRF operations
pub type HackRFResult<T> = Result<T, HackRFError>;

/// An internal function for obtaining the description from a HackRF error code.
fn parse_error(error_code: ffi::hackrf_error) -> HackRFError {
    unsafe {
        let desc = CStr::from_ptr(ffi::hackrf_error_name(error_code)).to_string_lossy();
        HackRFError {
            desc: desc.to_string(),
            code: error_code as i32,
        }
    }
}

/// Adjust a gain value to the nearest valid step
fn adjust_gain(gain: u32, min: u32, max: u32, step: u32) -> u32 {
    if gain < min { min }
    else if gain > max { max }
    else { (gain as f32 / step as f32).round() as u32 * step }
}

/// The HackRF context.
///
/// This struct is used ensure that `libhackrf` is initialized before it can be used. A context can
/// be obtained by calling hackrf::init().
///
/// ## Example
/// ```
/// let context = try!(hackrf::init());
/// // Use context
/// ```
pub struct HackRFContext(Rc<HackRFContextInner>);

/// Value for keeping track of any object that contains a reference to the context.
///
/// When this value is dropped, `libhackrf` will be closed.
struct HackRFContextInner;

impl Drop for HackRFContextInner {
    fn drop(&mut self) {
        unsafe {
            ffi::hackrf_exit();
        }
    }
}

/// Initialize `libhackrf`, returning a context that can be used to connect to HackRF devices.
/// Once all references to the returned context go out of scope, the library will be closed
/// automatically.
pub fn init() -> HackRFResult<HackRFContext> {
    unsafe {
        hackrf_try!(ffi::hackrf_init());
    }
    Ok(HackRFContext(Rc::new(HackRFContextInner)))
}

/// A structure for managaing a HackRF device.
///
/// This structure contains methods for configuring the various settings of the HackRF. It also
/// provides a method for managing received samples as a blocking stream. Currently transmitting
/// samples and non-blocking streams are not supported.
///
/// ## Implementation notes
///
/// This library is a lightweight wrapper around the basic HackRF FFI interface. In addition to
/// the basic functionality it provides a blocking stream interface for receiving samples (See:
/// [`RxStream`](./struct.RxStream.html)).
///
/// Rust's type system is used to ensure that resources are not destroy or modified while the HackRF
/// is receiving samples (although it is possible there are problems with the implementation.). To
/// stop receiving samples it is sufficient to let the stream drop out of scope. For example:
///
/// ```
/// let context = try!(hackrf::init());
/// let mut device = try!(HackRF::open(&context));
///
/// // Configure HackRF
///
/// {
///     let mut rx_stream = try!(device.rx_stream());
///     // Get samples from rx stream
/// }
///
/// // Re-configure HackRF
///
/// {
///     let mut rx_stream = try!(device.rx_stream());
///     // Get samples from rx stream
/// }
/// ```
pub struct HackRF {
    inner: *mut ffi::hackrf_device,
    _context: Rc<HackRFContextInner>,
}

impl Drop for HackRF {
    fn drop(&mut self) {
        unsafe {
            ffi::hackrf_close(self.inner);
        }
    }
}

impl HackRF {
    /// Attempt to open a connected HackRF device.
    pub fn open(context: &HackRFContext) -> HackRFResult<HackRF> {
        unsafe {
            let mut device = std::mem::zeroed();
            hackrf_try!(ffi::hackrf_open(&mut device));
            Ok(HackRF { inner: device, _context: context.0.clone() })
        }
    }

    /// Sets the center frequency (in Hz) of the HackRF: 5-6800 MHz.
    pub fn set_freq(&mut self, freq: u64) -> HackRFResult<()> {
        // TODO: Consider checking that the frequency is in a valid range.
        unsafe {
            hackrf_try!(ffi::hackrf_set_freq(self.inner, freq));
        }
        Ok(())
    }

    /// Sets amp gain to on/off.
    ///
    /// Warning: Enabling this in the presence of high power signals could damage the HackRF.
    pub fn set_amp_enable(&mut self, amp_enable: bool) -> HackRFResult<()> {
        unsafe {
            let value = if amp_enable { 1 } else { 0 };
            hackrf_try!(ffi::hackrf_set_amp_enable(self.inner, value));
        }
        Ok(())
    }

    /// Set antenna enable to on/off.
    pub fn set_antenna_enable(&mut self, antenna_enable: bool) -> HackRFResult<()> {
        unsafe {
            let value = if antenna_enable { 1 } else { 0 };
            hackrf_try!(ffi::hackrf_set_antenna_enable(self.inner, value));
        }
        Ok(())
    }

    /// Set LNA gain: 0-40 dB, 8dB steps.
    /// If the specified gain falls outside of the valid range, the gain will be clamped.
    pub fn set_lna_gain(&mut self, gain: u32) -> HackRFResult<()> {
        unsafe {
            hackrf_try!(ffi::hackrf_set_lna_gain(self.inner, adjust_gain(gain, 0, 40, 8)));
        }
        Ok(())
    }

    /// Set VGA (IF) gain: 0-62 dB, with 2dB steps.
    /// If the specified gain falls outside of the valid range, the gain will be clamped.
    pub fn set_vga_gain(&mut self, gain: u32) -> HackRFResult<()> {
        unsafe {
            hackrf_try!(ffi::hackrf_set_vga_gain(self.inner, adjust_gain(gain, 0, 62, 2)));
        }
        Ok(())
    }

    /// Sets the sample rate in Hz: 2-20 MHz.
    pub fn set_samp_rate(&mut self, samp_rate: f64) -> HackRFResult<()> {
        unsafe {
            hackrf_try!(ffi::hackrf_set_sample_rate(self.inner, samp_rate));

            // Automatically compute a good baseband filter bandwidth
            let bandwidth = ffi::hackrf_compute_baseband_filter_bw_round_down_lt(samp_rate as u32);
            try!(self.set_baseband_filter_bw(bandwidth));
        }
        Ok(())
    }

    /// Sets the baseband filter bandwidth.
    ///
    /// Note: This must be performed *after* setting the sample rate.
    pub fn set_baseband_filter_bw(&mut self, filter_bw: u32) -> HackRFResult<()> {
        unsafe {
            let adjusted_filter_bw = ffi::hackrf_compute_baseband_filter_bw(filter_bw);
            hackrf_try!(ffi::hackrf_set_baseband_filter_bandwidth(self.inner, adjusted_filter_bw));
        }
        Ok(())
    }

    /// Start an RX stream.
    pub fn rx_stream(&mut self, bound: usize) -> HackRFResult<RxStream> {
        let rx_stream = RxStream::new(bound, self);
        unsafe {
            match ffi::hackrf_start_rx(self.inner, rx_callback, &rx_stream.sender as *mut c_void) {
                HACKRF_SUCCESS => Ok(rx_stream),
                error => Err(parse_error(error)),
            }
        }
    }
}

/// A structure that manages receiving I/Q samples from the HackRF.
pub struct RxStream<'a> {
    hackrf_device: &'a mut HackRF,
    lookup_table: [f32; 256],
    local_index: usize,
    local_buffer: StackVec,
    sender: UnsafeCell<SyncSender<StackVec>>
    receiver: Receiver<StackVec>,
}

impl<'a> RxStream<'a> {
    /// Create a new instance of a RxStream
    fn new(bound: usize, hackrf_device: &'a mut HackRF) -> RxStream<'a> {
        fn gen_lookup_table() -> [f32; 256] {
            let mut data = [0.0; 256];
            for i in 0..256 {
                data[i] = (i as i8) as f32 / 128.0;
            }
            data
        }

        let (sender, receiver) = sync_channel(bound);
        RxStream {
            hackrf_device: hackrf_device,
            lookup_table: gen_lookup_table()
            local_index: 0,
            local_buffer: StackVec::new(),
            sender: UnsafeCell::new(sender),
            receiver: receiver,
        }
    }

    /// Return the next I/Q sample converted to floating point numbers. If there is no data
    /// avaliable, then this function is blocking.
    pub fn next_sample(&mut self) -> Option<(f32, f32)> {
        let sample = self.next_sample_raw();
        sample.map(|(i, q)| (self.lookup_table[i as usize], self.lookup_table[q as usize]))
    }

    /// Return the next raw I/Q sample from the HackRF. If there is no data avaliable, then this
    /// function is blocking.
    pub fn next_sample_raw(&mut self) -> Option<(u8, u8)> {
        if self.local_buffer.len() < self.local_index + 1 {
            self.local_buffer = match self.receiver.recv() {
                Ok(buffer) => buffer,
                Err(_) => return None,
            };
            self.local_index = 0;
        }

        let i = self.local_index;
        self.local_index += 2;
        Ok((self.local_buffer[i], self.local_buffer[i + 1]))
    }
}

impl<'a> Drop for RxStream<'a> {
    fn drop(&mut self) {
        unsafe {
            // TODO: investigate if this can ever fail under reasonable conditions
            ffi::hackrf_stop_rx(self.hackrf_device.inner);
        }
    }
}

/// Data shared between the RxStream and callback context
struct RxSharedData(Mutex<TransferBuffers>, Condvar);

/// A stack allocated vector with a maximum size
struct StackVec {
    data: [u8; BUFFER_SIZE],
    len: usize,
}

impl StackVec {
    fn new() -> StackVec {
        StackVec {
            data: [0; BUFFER_SIZE],
            len: 0
        }
    }

    /// Copy a slice into the vector, setting the vectors length to the length of the slice.
    /// Panics if the slice is too large.
    fn copy_slice(&mut self, slice: &[u8]) {
        self.data[0..slice.len()].copy_from_slice(slice);
        self.len = slice.len();
    }
}

impl ops::Deref for StackVec {
    type Target = [u8];

    fn deref(&self) -> &[u8] {
        &self.data[0..self.len]
    }
}

/// The call back that is used in the stream abstraction
unsafe extern "C" fn rx_callback(transfer: *mut ffi::hackrf_transfer) -> c_int {
    let src_buffer = slice::from_raw_parts((*transfer).buffer, (*transfer).valid_length as usize);

    // If the input buffer is too long, then an error has occured somewhere
    if src_buffer.len() > BUFFER_SIZE {
        return -1;
    }

    // Copy the transferred data into an owned structure
    let mut data = StackVec::new();
    data.copy_slice(src_buffer);

    let sender_cell: &UnsafeCell<SyncSender<StackVec>> = mem::transmute((*transfer).rx_ctx);
    let sender = &mut *sender_cell.get();

    if let Err(e) = receiver.try_send(data) {
        match e {
            // If the buffer is full just drop the message, it might be useful to consider logging
            // in the future.
            Full(_) => {},

            // Report an error back to libhackrf
            Disconnected(_) => return -1,
        }
    }

    // Succesfully sent the data
    0
}
<commit_msg>Fix issues with implementation.<commit_after>//! A library for interacting with a HackRF device.
//!
//! ## Examples
//! ```
//! extern crate hackrf;
//!
//! use hackrf::{HackRF, HackRFResult};
//!
//! const SAMP_RATE: u64 = 2_000_000; // 2 MHz
//!
//! fn main() {
//!    if let Err(e) = run() {
//!        println!("{:?}", e);
//!    }
//! }
//!
//! fn run() -> HackRFResult<()> {
//!     let context = try!(hackrf::init());
//!
//!     let mut device = try!(HackRF::open(&context));
//!     try!(device.set_samp_rate(SAMP_RATE as f64));
//!     try!(device.set_freq(150_000_000));
//!
//!     let mut rx_stream = try!(device.rx_stream());
//!
//!     // Take 10 seconds worth of samples
//!     for _ in 0..10 * SAMP_RATE {
//!         let (i, q) = rx_stream.next_sample();
//!         // .. Do something with samples ..
//!     }
//!
//!     Ok(())
//! }
//! ```

extern crate hackrf_sys as ffi;

use std::ffi::CStr;
use std::os::raw::{c_int, c_void};

use std::sync::mpsc::{SyncSender, Receiver, TrySendError, sync_channel};
use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering};

use std::{mem, slice, ops};
use std::rc::Rc;

use ffi::hackrf_error::*;

/// The maximum size of each of the buffers used for receiving.
/// Note: this must be greater than or equal to the buffer size chosen for libusb.
const BUFFER_SIZE: usize = 262144;

/// An internal macro for handling hackrf errors.
macro_rules! hackrf_try {
    ($e:expr) => ({
        match $e {
            HACKRF_SUCCESS => (),
            error => return Err(parse_error(error)),
        }
    });
}

/// A error that can occur when trying to interact with a HackRF device.
///
/// ## Possible Error Values
///
///     HACKRF_ERROR_INVALID_PARAM = -2
///     HACKRF_ERROR_NOT_FOUND = -5
///     HACKRF_ERROR_BUSY = -6
///     HACKRF_ERROR_NO_MEM = -11
///     HACKRF_ERROR_LIBUSB = -1000
///     HACKRF_ERROR_THREAD = -1001
///     HACKRF_ERROR_STREAMING_THREAD_ERR = -1002
///     HACKRF_ERROR_STREAMING_STOPPED = -1003
///     HACKRF_ERROR_STREAMING_EXIT_CALLED = -1004
///     HACKRF_ERROR_OTHER = -9999
///
/// See [libhackrf](https://github.com/mossmann/hackrf/tree/master/host/libhackrf) for more info.
#[derive(Debug)]
pub struct HackRFError {
    /// The description of the code.
    pub desc: String,

    /// The code returned by `libhackrf`.
    pub code: i32,
}

/// A specialized Result type for HackRF operations
pub type HackRFResult<T> = Result<T, HackRFError>;

/// An internal function for obtaining the description from a HackRF error code.
fn parse_error(error_code: ffi::hackrf_error) -> HackRFError {
    unsafe {
        let desc = CStr::from_ptr(ffi::hackrf_error_name(error_code)).to_string_lossy();
        HackRFError {
            desc: desc.to_string(),
            code: error_code as i32,
        }
    }
}

/// Adjust a gain value to the nearest valid step
fn adjust_gain(gain: u32, min: u32, max: u32, step: u32) -> u32 {
    if gain < min { min }
    else if gain > max { max }
    else { (gain as f32 / step as f32).round() as u32 * step }
}

/// The HackRF context.
///
/// This struct is used ensure that `libhackrf` is initialized before it can be used. A context can
/// be obtained by calling hackrf::init().
///
/// ## Example
/// ```
/// let context = try!(hackrf::init());
/// // Use context
/// ```
pub struct HackRFContext(Rc<HackRFContextInner>);

/// Value for keeping track of any object that contains a reference to the context.
///
/// When this value is dropped, `libhackrf` will be closed.
struct HackRFContextInner;

impl Drop for HackRFContextInner {
    fn drop(&mut self) {
        unsafe {
            ffi::hackrf_exit();
        }
    }
}

/// Initialize `libhackrf`, returning a context that can be used to connect to HackRF devices.
/// Once all references to the returned context go out of scope, the library will be closed
/// automatically.
pub fn init() -> HackRFResult<HackRFContext> {
    unsafe {
        hackrf_try!(ffi::hackrf_init());
    }
    Ok(HackRFContext(Rc::new(HackRFContextInner)))
}

/// A structure for managaing a HackRF device.
///
/// This structure contains methods for configuring the various settings of the HackRF. It also
/// provides a method for managing received samples as a blocking stream. Currently transmitting
/// samples and non-blocking streams are not supported.
///
/// ## Implementation notes
///
/// This library is a lightweight wrapper around the basic HackRF FFI interface. In addition to
/// the basic functionality it provides a blocking stream interface for receiving samples (See:
/// [`RxStream`](./struct.RxStream.html)).
///
/// Rust's type system is used to ensure that resources are not destroy or modified while the HackRF
/// is receiving samples (although it is possible there are problems with the implementation.). To
/// stop receiving samples it is sufficient to let the stream drop out of scope. For example:
///
/// ```
/// let context = try!(hackrf::init());
/// let mut device = try!(HackRF::open(&context));
///
/// // Configure HackRF
///
/// {
///     let mut rx_stream = try!(device.rx_stream());
///     // Get samples from rx stream
/// }
///
/// // Re-configure HackRF
///
/// {
///     let mut rx_stream = try!(device.rx_stream());
///     // Get samples from rx stream
/// }
/// ```
pub struct HackRF {
    inner: *mut ffi::hackrf_device,
    _context: Rc<HackRFContextInner>,
}

impl Drop for HackRF {
    fn drop(&mut self) {
        unsafe {
            ffi::hackrf_close(self.inner);
        }
    }
}

impl HackRF {
    /// Attempt to open a connected HackRF device.
    pub fn open(context: &HackRFContext) -> HackRFResult<HackRF> {
        unsafe {
            let mut device = std::mem::zeroed();
            hackrf_try!(ffi::hackrf_open(&mut device));
            Ok(HackRF { inner: device, _context: context.0.clone() })
        }
    }

    /// Sets the center frequency (in Hz) of the HackRF: 5-6800 MHz.
    pub fn set_freq(&mut self, freq: u64) -> HackRFResult<()> {
        // TODO: Consider checking that the frequency is in a valid range.
        unsafe {
            hackrf_try!(ffi::hackrf_set_freq(self.inner, freq));
        }
        Ok(())
    }

    /// Sets amp gain to on/off.
    ///
    /// Warning: Enabling this in the presence of high power signals could damage the HackRF.
    pub fn set_amp_enable(&mut self, amp_enable: bool) -> HackRFResult<()> {
        unsafe {
            let value = if amp_enable { 1 } else { 0 };
            hackrf_try!(ffi::hackrf_set_amp_enable(self.inner, value));
        }
        Ok(())
    }

    /// Set antenna enable to on/off.
    pub fn set_antenna_enable(&mut self, antenna_enable: bool) -> HackRFResult<()> {
        unsafe {
            let value = if antenna_enable { 1 } else { 0 };
            hackrf_try!(ffi::hackrf_set_antenna_enable(self.inner, value));
        }
        Ok(())
    }

    /// Set LNA gain: 0-40 dB, 8dB steps.
    /// If the specified gain falls outside of the valid range, the gain will be clamped.
    pub fn set_lna_gain(&mut self, gain: u32) -> HackRFResult<()> {
        unsafe {
            hackrf_try!(ffi::hackrf_set_lna_gain(self.inner, adjust_gain(gain, 0, 40, 8)));
        }
        Ok(())
    }

    /// Set VGA (IF) gain: 0-62 dB, with 2dB steps.
    /// If the specified gain falls outside of the valid range, the gain will be clamped.
    pub fn set_vga_gain(&mut self, gain: u32) -> HackRFResult<()> {
        unsafe {
            hackrf_try!(ffi::hackrf_set_vga_gain(self.inner, adjust_gain(gain, 0, 62, 2)));
        }
        Ok(())
    }

    /// Sets the sample rate in Hz: 2-20 MHz.
    pub fn set_samp_rate(&mut self, samp_rate: f64) -> HackRFResult<()> {
        unsafe {
            hackrf_try!(ffi::hackrf_set_sample_rate(self.inner, samp_rate));

            // Automatically compute a good baseband filter bandwidth
            let bandwidth = ffi::hackrf_compute_baseband_filter_bw_round_down_lt(samp_rate as u32);
            try!(self.set_baseband_filter_bw(bandwidth));
        }
        Ok(())
    }

    /// Sets the baseband filter bandwidth.
    ///
    /// Note: This must be performed *after* setting the sample rate.
    pub fn set_baseband_filter_bw(&mut self, filter_bw: u32) -> HackRFResult<()> {
        unsafe {
            let adjusted_filter_bw = ffi::hackrf_compute_baseband_filter_bw(filter_bw);
            hackrf_try!(ffi::hackrf_set_baseband_filter_bandwidth(self.inner, adjusted_filter_bw));
        }
        Ok(())
    }

    /// Start an RX stream.
    pub fn rx_stream(&mut self, bound: usize) -> HackRFResult<RxStream> {
        let mut rx_stream = RxStream::new(bound, self);
        let sender = (&mut *rx_stream.sender) as *mut SyncSender<StackVec>;
        unsafe {
            match ffi::hackrf_start_rx(self.inner, rx_callback, sender as *mut c_void) {
                HACKRF_SUCCESS => Ok(rx_stream),
                error => Err(parse_error(error)),
            }
        }
    }
}

static OVERFLOW_COUNT: AtomicUsize = ATOMIC_USIZE_INIT;

/// A structure that manages receiving I/Q samples from the HackRF.
pub struct RxStream<'a> {
    hackrf_device: &'a HackRF,
    lookup_table: [f32; 256],
    local_index: usize,
    local_buffer: StackVec,
    sender: Box<SyncSender<StackVec>>,
    receiver: Receiver<StackVec>,
}

impl<'a> RxStream<'a> {
    /// Create a new instance of a RxStream
    fn new(bound: usize, hackrf_device: &'a HackRF) -> RxStream<'a> {
        fn gen_lookup_table() -> [f32; 256] {
            let mut data = [0.0; 256];
            for i in 0..256 {
                data[i] = (i as i8) as f32 / 128.0;
            }
            data
        }

        // Reset the overflow counter
        OVERFLOW_COUNT.store(0, Ordering::Relaxed);

        let (sender, receiver) = sync_channel(bound);
        RxStream {
            hackrf_device: hackrf_device,
            lookup_table: gen_lookup_table(),
            local_index: 0,
            local_buffer: StackVec::new(),
            sender: Box::new(sender),
            receiver: receiver,
        }
    }

    // Returns a reference to the internal receiver
    pub fn receiver(&mut self) -> &mut Receiver<StackVec> {
        &mut self.receiver
    }

    /// Return the next I/Q sample converted to floating point numbers. If there is no data
    /// avaliable, then this function is blocking.
    pub fn next_sample(&mut self) -> Option<(f32, f32)> {
        let sample = self.next_sample_raw();
        sample.map(|(i, q)| (self.lookup_table[i as usize], self.lookup_table[q as usize]))
    }

    /// Return the next raw I/Q sample from the HackRF. If there is no data avaliable, then this
    /// function is blocking.
    pub fn next_sample_raw(&mut self) -> Option<(u8, u8)> {
        if self.local_buffer.len() < self.local_index + 1 {
            self.local_buffer = match self.receiver.recv() {
                Ok(buffer) => buffer,
                Err(_) => return None,
            };
            self.local_index = 0;
        }

        let i = self.local_index;
        self.local_index += 2;
        Some((self.local_buffer[i], self.local_buffer[i + 1]))
    }

    /// Return how many times the stream dropped data frames
    pub fn overflow_count(&self) -> usize {
        OVERFLOW_COUNT.load(Ordering::Relaxed)
    }
}

impl<'a> Drop for RxStream<'a> {
    fn drop(&mut self) {
        unsafe {
            // TODO: investigate if this can ever fail under reasonable conditions
            ffi::hackrf_stop_rx(self.hackrf_device.inner);
        }
    }
}

/// A stack allocated vector with a maximum size
pub struct StackVec {
    data: [u8; BUFFER_SIZE],
    len: usize,
}

impl StackVec {
    fn new() -> StackVec {
        StackVec {
            data: [0; BUFFER_SIZE],
            len: 0
        }
    }

    /// Copy a slice into the vector, setting the vectors length to the length of the slice.
    /// Panics if the slice is too large.
    fn copy_slice(&mut self, slice: &[u8]) {
        self.data[0..slice.len()].copy_from_slice(slice);
        self.len = slice.len();
    }
}

impl ops::Deref for StackVec {
    type Target = [u8];

    fn deref(&self) -> &[u8] {
        &self.data[0..self.len]
    }
}

/// The call back that is used in the stream abstraction
unsafe extern "C" fn rx_callback(transfer: *mut ffi::hackrf_transfer) -> c_int {
    let src_buffer = slice::from_raw_parts((*transfer).buffer, (*transfer).valid_length as usize);

    // If the input buffer is too long, then an error has occured somewhere
    if src_buffer.len() > BUFFER_SIZE {
        return -1;
    }

    // Copy the transferred data into an owned structure
    let mut data = StackVec::new();
    data.copy_slice(src_buffer);

    let sender_raw: *mut SyncSender<StackVec> = mem::transmute((*transfer).rx_ctx);
    let sender = &mut *sender_raw;

    if let Err(e) = sender.try_send(data) {
        match e {
            // If the buffer is full drop the message and increment the overflow count
            TrySendError::Full(_) => { OVERFLOW_COUNT.fetch_add(1, Ordering::Relaxed); },

            // Report an error back to libhackrf
            TrySendError::Disconnected(_) => return -1,
        }
    }

    // Succesfully sent the data
    0
}
<|endoftext|>


<commit_before>// Code based on https://github.com/defuz/sublimate/blob/master/src/core/syntax/style.rs
// released under the MIT license by @defuz
use bitflags::bitflags;

/// The foreground, background and font style
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub struct Style {
    /// Foreground color.
    pub foreground: Color,
    /// Background color.
    pub background: Color,
    /// Style of the font.
    pub font_style: FontStyle,
}

/// A change to a `Style` applied incrementally by a theme rule.
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub struct StyleModifier {
    /// Foreground color.
    pub foreground: Option<Color>,
    /// Background color.
    pub background: Option<Color>,
    /// Style of the font.
    pub font_style: Option<FontStyle>,
}

/// RGBA color, these numbers come directly from the theme so
/// for now you might have to do your own color space conversion if you are outputting
/// a different color space from the theme. This can be a problem because some Sublime
/// themes use sRGB and some don't. This is specified in an attribute syntect doesn't parse yet.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub struct Color {
    /// Red component
    pub r: u8,
    /// Green component
    pub g: u8,
    /// Blue component
    pub b: u8,
    /// Alpha component
    pub a: u8,
}

bitflags! {
    /// This can be a combination of `BOLD`, `UNDERLINE` and `ITALIC`
    #[derive(Serialize, Deserialize)]
    pub struct FontStyle: u8 {
        /// Bold font style
        const BOLD = 1;
        /// Underline font style
        const UNDERLINE = 2;
        /// Italic font style
        const ITALIC = 4;
    }
}

impl Color {
    /// Black color (`#000000`)
    pub const BLACK: Color = Color {
        r: 0x00,
        g: 0x00,
        b: 0x00,
        a: 0xFF,
    };

    /// White color (`#FFFFFF`)
    pub const WHITE: Color = Color {
        r: 0xFF,
        g: 0xFF,
        b: 0xFF,
        a: 0xFF,
    };
}

impl Style {
    /// Applies a change to this style, yielding a new changed style
    pub fn apply(&self, modifier: StyleModifier) -> Style {
        Style {
            foreground: modifier.foreground.unwrap_or(self.foreground),
            background: modifier.background.unwrap_or(self.background),
            font_style: modifier.font_style.unwrap_or(self.font_style),
        }
    }
}

impl Default for Style {
    fn default() -> Style {
        Style {
            foreground: Color::BLACK,
            background: Color::WHITE,
            font_style: FontStyle::empty(),
        }
    }
}

impl StyleModifier {
    /// Applies the other modifier to this one, creating a new modifier.
    /// Values in `other` are preferred.
    pub fn apply(&self, other: StyleModifier) -> StyleModifier {
        StyleModifier {
            foreground: other.foreground.or(self.foreground),
            background: other.background.or(self.background),
            font_style: other.font_style.or(self.font_style),
        }
    }
}
<commit_msg>feat: Prepare for supporting named CSS colors<commit_after>// Code based on https://github.com/defuz/sublimate/blob/master/src/core/syntax/style.rs
// released under the MIT license by @defuz
use bitflags::bitflags;

/// The foreground, background and font style
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub struct Style {
    /// Foreground color.
    pub foreground: Color,
    /// Background color.
    pub background: Color,
    /// Style of the font.
    pub font_style: FontStyle,
}

/// A change to a `Style` applied incrementally by a theme rule.
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub struct StyleModifier {
    /// Foreground color.
    pub foreground: Option<Color>,
    /// Background color.
    pub background: Option<Color>,
    /// Style of the font.
    pub font_style: Option<FontStyle>,
}

/// RGBA color, these numbers come directly from the theme so
/// for now you might have to do your own color space conversion if you are outputting
/// a different color space from the theme. This can be a problem because some Sublime
/// themes use sRGB and some don't. This is specified in an attribute syntect doesn't parse yet.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub struct Color {
    /// Red component
    pub r: u8,
    /// Green component
    pub g: u8,
    /// Blue component
    pub b: u8,
    /// Alpha component
    pub a: u8,
}

bitflags! {
    /// This can be a combination of `BOLD`, `UNDERLINE` and `ITALIC`
    #[derive(Serialize, Deserialize)]
    pub struct FontStyle: u8 {
        /// Bold font style
        const BOLD = 1;
        /// Underline font style
        const UNDERLINE = 2;
        /// Italic font style
        const ITALIC = 4;
    }
}

impl Color {
    /// Black color (`#000000`)
    pub const BLACK: Color = Color {
        r: 0x00,
        g: 0x00,
        b: 0x00,
        a: 0xFF,
    };

    /// White color (`#FFFFFF`)
    pub const WHITE: Color = Color {
        r: 0xFF,
        g: 0xFF,
        b: 0xFF,
        a: 0xFF,
    };

    /// Red CSS colors /////////////////////////////////////////////////////////
    pub const LIGHTSALMON: Color = Color {
        r: 0xFF,
        g: 0xA0,
        b: 0x7A,
        a: 0xFF,
    };
    pub const SALMON: Color = Color {
        r: 0xFA,
        g: 0x80,
        b: 0x72,
        a: 0xFF,
    };
    pub const DARKSALMON: Color = Color {
        r: 0xE9,
        g: 0x96,
        b: 0x7A,
        a: 0xFF,
    };
    pub const LIGHTCORAL: Color = Color {
        r: 0xF0,
        g: 0x80,
        b: 0x80,
        a: 0xFF,
    };
    pub const INDIANRED: Color = Color {
        r: 0xCD,
        g: 0x5C,
        b: 0x5C,
        a: 0xFF,
    };
    pub const CRIMSON: Color = Color {
        r: 0xDC,
        g: 0x14,
        b: 0x3C,
        a: 0xFF,
    };
    pub const FIREBRICK: Color = Color {
        r: 0xB2,
        g: 0x22,
        b: 0x22,
        a: 0xFF,
    };
    pub const RED: Color = Color {
        r: 0xFF,
        g: 0x00,
        b: 0x00,
        a: 0xFF,
    };
    pub const DARKRED: Color = Color {
        r: 0x8B,
        g: 0x00,
        b: 0x00,
        a: 0xFF,
    };

    /// Orange CSS colors //////////////////////////////////////////////////////
    pub const CORAL: Color = Color {
        r: 0xFF,
        g: 0x7F,
        b: 0x50,
        a: 0xFF,
    };
    pub const TOMATO: Color = Color {
        r: 0xFF,
        g: 0x63,
        b: 0x47,
        a: 0xFF,
    };
    pub const ORANGERED: Color = Color {
        r: 0xFF,
        g: 0x45,
        b: 0x00,
        a: 0xFF,
    };
    pub const GOLD: Color = Color {
        r: 0xFF,
        g: 0xD7,
        b: 0x00,
        a: 0xFF,
    };
    pub const ORANGE: Color = Color {
        r: 0xFF,
        g: 0xA5,
        b: 0x00,
        a: 0xFF,
    };
    pub const DARKORANGE: Color = Color {
        r: 0xFF,
        g: 0x8C,
        b: 0x00,
        a: 0xFF,
    };

    /// Yellow CSS colors //////////////////////////////////////////////////////
    pub const LIGHTYELLOW: Color = Color {
        r: 0xFF,
        g: 0xFF,
        b: 0xE0,
        a: 0xFF,
    };
    pub const LEMONCHIFFON: Color = Color {
        r: 0xFF,
        g: 0xFA,
        b: 0xCD,
        a: 0xFF,
    };
    pub const LIGHTGOLDENRODYELLOW: Color = Color {
        r: 0xFA,
        g: 0xFA,
        b: 0xD2,
        a: 0xFF,
    };
    pub const PAPAYAWHIP: Color = Color {
        r: 0xFF,
        g: 0xEF,
        b: 0xD5,
        a: 0xFF,
    };
    pub const MOCCASIN: Color = Color {
        r: 0xFF,
        g: 0xE4,
        b: 0xB5,
        a: 0xFF,
    };
    pub const PEACHPUFF: Color = Color {
        r: 0xFF,
        g: 0xDA,
        b: 0xB9,
        a: 0xFF,
    };
    pub const PALEGOLDENROD: Color = Color {
        r: 0xEE,
        g: 0xE8,
        b: 0xAA,
        a: 0xFF,
    };
    pub const KHAKI: Color = Color {
        r: 0xF0,
        g: 0xE6,
        b: 0x8C,
        a: 0xFF,
    };
    pub const DARKKHAKI: Color = Color {
        r: 0xBD,
        g: 0xB7,
        b: 0x6B,
        a: 0xFF,
    };
    pub const YELLOW: Color = Color {
        r: 0xFF,
        g: 0xFF,
        b: 0x00,
        a: 0xFF,
    };

    /// Green CSS colors ///////////////////////////////////////////////////////
    pub const LAWNGREEN: Color = Color {
        r: 0x7C,
        g: 0xFC,
        b: 0x00,
        a: 0xFF,
    };
    pub const CHARTREUSE: Color = Color {
        r: 0x7F,
        g: 0xFF,
        b: 0x00,
        a: 0xFF,
    };
    pub const LIMEGREEN: Color = Color {
        r: 0x32,
        g: 0xCD,
        b: 0x32,
        a: 0xFF,
    };
    pub const LIME: Color = Color {
        r: 0x00,
        g: 0xFF,
        b: 0x00,
        a: 0xFF,
    };
    pub const FORESTGREEN: Color = Color {
        r: 0x22,
        g: 0x8B,
        b: 0x22,
        a: 0xFF,
    };
    pub const GREEN: Color = Color {
        r: 0x00,
        g: 0x80,
        b: 0x00,
        a: 0xFF,
    };
    pub const DARKGREEN: Color = Color {
        r: 0x00,
        g: 0x64,
        b: 0x00,
        a: 0xFF,
    };
    pub const GREENYELLOW: Color = Color {
        r: 0xAD,
        g: 0xFF,
        b: 0x2F,
        a: 0xFF,
    };
    pub const YELLOWGREEN: Color = Color {
        r: 0x9A,
        g: 0xCD,
        b: 0x32,
        a: 0xFF,
    };
    pub const SPRINGGREEN: Color = Color {
        r: 0x00,
        g: 0xFF,
        b: 0x7F,
        a: 0xFF,
    };
    pub const MEDIUMSPINGGREEN: Color = Color {
        r: 0x00,
        g: 0xFA,
        b: 0x9A,
        a: 0xFF,
    };
    pub const LIGHTGREEN: Color = Color {
        r: 0x90,
        g: 0xEE,
        b: 0x90,
        a: 0xFF,
    };
    pub const PALEGREEN: Color = Color {
        r: 0x98,
        g: 0xFB,
        b: 0x98,
        a: 0xFF,
    };
    pub const DARKSEAGREEN: Color = Color {
        r: 0x8F,
        g: 0xBC,
        b: 0x8F,
        a: 0xFF,
    };
    pub const MEDIUMSEAGREEN: Color = Color {
        r: 0x3C,
        g: 0xB3,
        b: 0x71,
        a: 0xFF,
    };
    pub const SEAGREEN: Color = Color {
        r: 0x2E,
        g: 0x8B,
        b: 0x57,
        a: 0xFF,
    };
    pub const OLIVE: Color = Color {
        r: 0x80,
        g: 0x80,
        b: 0x00,
        a: 0xFF,
    };
    pub const DARKOLIVE: Color = Color {
        r: 0x55,
        g: 0x6B,
        b: 0x2F,
        a: 0xFF,
    };
    pub const OLIVEDRAB: Color = Color {
        r: 0x6B,
        g: 0x8E,
        b: 0x23,
        a: 0xFF,
    };

    /// Cyan CSS colors ////////////////////////////////////////////////////////
    pub const LIGHTCYAN: Color = Color {
        r: 0xE0,
        g: 0xFF,
        b: 0xFF,
        a: 0xFF,
    };
    pub const CYAN: Color = Color {
        r: 0x00,
        g: 0xFF,
        b: 0xFF,
        a: 0xFF,
    };
    pub const AQUA: Color = Color {
        r: 0xE0,
        g: 0xFF,
        b: 0xFF,
        a: 0xFF,
    };
    pub const AQUAMARINE: Color = Color {
        r: 0x7F,
        g: 0xFF,
        b: 0xD4,
        a: 0xFF,
    };
    pub const MEDIUMAQUAMARINE: Color = Color {
        r: 0x66,
        g: 0xCD,
        b: 0xAA,
        a: 0xFF,
    };
    pub const PALETURQUOISE: Color = Color {
        r: 0xAF,
        g: 0xEE,
        b: 0xEE,
        a: 0xFF,
    };
    pub const TURQUOISE: Color = Color {
        r: 0x40,
        g: 0xE0,
        b: 0xD0,
        a: 0xFF,
    };
    pub const MEDIUMTURQUOISE: Color = Color {
        r: 0x48,
        g: 0xD1,
        b: 0xCC,
        a: 0xFF,
    };
    pub const DARKTURQUOISE: Color = Color {
        r: 0x00,
        g: 0xCE,
        b: 0xD1,
        a: 0xFF,
    };
    pub const LIGHTSEAGREEN: Color = Color {
        r: 0x20,
        g: 0xB2,
        b: 0xAA,
        a: 0xFF,
    };
    pub const CADETBLUE: Color = Color {
        r: 0x5F,
        g: 0x9E,
        b: 0xA0,
        a: 0xFF,
    };
    pub const DARKCYAN: Color = Color {
        r: 0x00,
        g: 0x8B,
        b: 0x8B,
        a: 0xFF,
    };
    pub const TEAL: Color = Color {
        r: 0x00,
        g: 0x80,
        b: 0x80,
        a: 0xFF,
    };

    /// Blue CSS colors ////////////////////////////////////////////////////////
    pub const POWDERBLUE: Color = Color {
        r: 0xB0,
        g: 0xE0,
        b: 0xE6,
        a: 0xFF,
    };
    pub const LIGHTBLUE: Color = Color {
        r: 0xAD,
        g: 0xD8,
        b: 0xE6,
        a: 0xFF,
    };
    pub const LIGHTSKYBLUE: Color = Color {
        r: 0x87,
        g: 0xCE,
        b: 0xFA,
        a: 0xFF,
    };
    pub const SKYBLUE: Color = Color {
        r: 0x87,
        g: 0xCE,
        b: 0xEB,
        a: 0xFF,
    };
    pub const DEEPSKYBLUE: Color = Color {
        r: 0x00,
        g: 0xBF,
        b: 0xFF,
        a: 0xFF,
    };
    pub const LIGHTSTEELBLUE: Color = Color {
        r: 0xB0,
        g: 0xC4,
        b: 0xDE,
        a: 0xFF,
    };
    pub const DODGERBLUE: Color = Color {
        r: 0x1E,
        g: 0x90,
        b: 0xFF,
        a: 0xFF,
    };
    pub const CORNFLOWERBLUE: Color = Color {
        r: 0x64,
        g: 0x95,
        b: 0xED,
        a: 0xFF,
    };
    pub const STEELBLUE: Color = Color {
        r: 0x46,
        g: 0x82,
        b: 0xB4,
        a: 0xFF,
    };
    pub const ROYALBLUE: Color = Color {
        r: 0x41,
        g: 0x69,
        b: 0xE1,
        a: 0xFF,
    };
    pub const BLUE: Color = Color {
        r: 0x00,
        g: 0x00,
        b: 0xFF,
        a: 0xFF,
    };
    pub const MEDIUMBLUE: Color = Color {
        r: 0x00,
        g: 0x00,
        b: 0xCD,
        a: 0xFF,
    };
    pub const DARKBLUE: Color = Color {
        r: 0x00,
        g: 0x00,
        b: 0x8B,
        a: 0xFF,
    };
    pub const NAVY: Color = Color {
        r: 0x00,
        g: 0x00,
        b: 0x80,
        a: 0xFF,
    };
    pub const MIDNIGHTBLUE: Color = Color {
        r: 0x19,
        g: 0x19,
        b: 0x70,
        a: 0xFF,
    };
    pub const MEDIUMSLATEBLUE: Color = Color {
        r: 0x7B,
        g: 0x68,
        b: 0xEE,
        a: 0xFF,
    };
    pub const SLATEBLUE: Color = Color {
        r: 0x6A,
        g: 0x5A,
        b: 0xCD,
        a: 0xFF,
    };
    pub const DARKSLATEBLUE: Color = Color {
        r: 0x48,
        g: 0x3D,
        b: 0x8B,
        a: 0xFF,
    };

    /// Purple CSS colors //////////////////////////////////////////////////////
    pub const LAVENDER: Color = Color {
        r: 0xE6,
        g: 0xE6,
        b: 0xFA,
        a: 0xFF,
    };
    pub const THISTLE: Color = Color {
        r: 0xD8,
        g: 0xBF,
        b: 0xD8,
        a: 0xFF,
    };
    pub const PLUM: Color = Color {
        r: 0xDD,
        g: 0xA0,
        b: 0xDD,
        a: 0xFF,
    };
    pub const VIOLET: Color = Color {
        r: 0xEE,
        g: 0x82,
        b: 0xEE,
        a: 0xFF,
    };
    pub const ORCHID: Color = Color {
        r: 0xDA,
        g: 0x70,
        b: 0xD6,
        a: 0xFF,
    };
    pub const FUCHSIA: Color = Color {
        r: 0xFF,
        g: 0x00,
        b: 0xFF,
        a: 0xFF,
    };
    pub const MAGENTA: Color = Color {
        r: 0xFF,
        g: 0x00,
        b: 0xFF,
        a: 0xFF,
    };
    pub const MEDIUMORCHID: Color = Color {
        r: 0xBA,
        g: 0x55,
        b: 0xD3,
        a: 0xFF,
    };
    pub const MEDIUMPURPLE: Color = Color {
        r: 0x93,
        g: 0x70,
        b: 0xDB,
        a: 0xFF,
    };
    pub const BLUEVIOLET: Color = Color {
        r: 0x8A,
        g: 0x2B,
        b: 0xE2,
        a: 0xFF,
    };
    pub const DARKVIOLET: Color = Color {
        r: 0x94,
        g: 0x00,
        b: 0xD3,
        a: 0xFF,
    };
    pub const DARKORCHID: Color = Color {
        r: 0x99,
        g: 0x32,
        b: 0xCC,
        a: 0xFF,
    };
    pub const DARKMAGENTA: Color = Color {
        r: 0x8B,
        g: 0x00,
        b: 0x8B,
        a: 0xFF,
    };
    pub const PURPLE: Color = Color {
        r: 0x80,
        g: 0x00,
        b: 0x80,
        a: 0xFF,
    };
    pub const INDIGO: Color = Color {
        r: 0x4B,
        g: 0x00,
        b: 0x82,
        a: 0xFF,
    };

    /// Pink CSS colors ////////////////////////////////////////////////////////
    pub const PINK: Color = Color {
        r: 0xFF,
        g: 0xC0,
        b: 0xCB,
        a: 0xFF,
    };
    pub const LIGHTPINK: Color = Color {
        r: 0xFF,
        g: 0xB6,
        b: 0xC1,
        a: 0xFF,
    };
    pub const HOTPINK: Color = Color {
        r: 0xFF,
        g: 0x69,
        b: 0xB4,
        a: 0xFF,
    };
    pub const DEEPPINK: Color = Color {
        r: 0xFF,
        g: 0x14,
        b: 0x93,
        a: 0xFF,
    };
    pub const PALEVIOLETRED: Color = Color {
        r: 0xDB,
        g: 0x70,
        b: 0x93,
        a: 0xFF,
    };
    pub const MEDIUMVIOLETRED: Color = Color {
        r: 0xC7,
        g: 0x15,
        b: 0x85,
        a: 0xFF,
    };

    /// White CSS colors ///////////////////////////////////////////////////////
    pub const WHITE: Color = Color {
        r: 0xFF,
        g: 0xFF,
        b: 0xFF,
        a: 0xFF,
    };
    pub const SNOW: Color = Color {
        r: 0xFF,
        g: 0xFA,
        b: 0xFA,
        a: 0xFF,
    };
    pub const HONEYDEW: Color = Color {
        r: 0xF0,
        g: 0xFF,
        b: 0xF0,
        a: 0xFF,
    };
    pub const MINTCREAM: Color = Color {
        r: 0xF5,
        g: 0xFF,
        b: 0xFA,
        a: 0xFF,
    };
    pub const AZURE: Color = Color {
        r: 0xF0,
        g: 0xFF,
        b: 0xFF,
        a: 0xFF,
    };
    pub const ALICEBLUE: Color = Color {
        r: 0xF0,
        g: 0xF8,
        b: 0xFF,
        a: 0xFF,
    };
    pub const GHOSTWHITE: Color = Color {
        r: 0xF8,
        g: 0xF8,
        b: 0xFF,
        a: 0xFF,
    };
    pub const WHITESMOKE: Color = Color {
        r: 0xF5,
        g: 0xF5,
        b: 0xF5,
        a: 0xFF,
    };
    pub const SEASHELL: Color = Color {
        r: 0xFF,
        g: 0xF5,
        b: 0xEE,
        a: 0xFF,
    };
    pub const BEIGE: Color = Color {
        r: 0xF5,
        g: 0xF5,
        b: 0xDC,
        a: 0xFF,
    };
    pub const OLDLACE: Color = Color {
        r: 0xFD,
        g: 0xF5,
        b: 0xE6,
        a: 0xFF,
    };
    pub const FLORALWHITE: Color = Color {
        r: 0xFF,
        g: 0xFA,
        b: 0xF0,
        a: 0xFF,
    };
    pub const IVORY: Color = Color {
        r: 0xFF,
        g: 0xFF,
        b: 0xF0,
        a: 0xFF,
    };
    pub const ANTIQUEWHITE: Color = Color {
        r: 0xFA,
        g: 0xEB,
        b: 0xD7,
        a: 0xFF,
    };
    pub const LINEN: Color = Color {
        r: 0xFA,
        g: 0xF0,
        b: 0xE6,
        a: 0xFF,
    };
    pub const LAVENDERBLUSH: Color = Color {
        r: 0xFF,
        g: 0xF0,
        b: 0xF5,
        a: 0xFF,
    };
    pub const MISTYROSE: Color = Color {
        r: 0xFF,
        g: 0xE4,
        b: 0xE1,
        a: 0xFF,
    };

    /// Gray CSS colors ////////////////////////////////////////////////////////
    pub const GAINSBORO: Color = Color {
        r: 0xDC,
        g: 0xDC,
        b: 0xDC,
        a: 0xFF,
    };
    pub const LIGHTGRAY: Color = Color {
        r: 0xD3,
        g: 0xD3,
        b: 0xD3,
        a: 0xFF,
    };
    pub const SILVER: Color = Color {
        r: 0xC0,
        g: 0xC0,
        b: 0xC0,
        a: 0xFF,
    };
    pub const DARKGRAY: Color = Color {
        r: 0xA9,
        g: 0xA9,
        b: 0xA9,
        a: 0xFF,
    };
    pub const GRAY: Color = Color {
        r: 0x80,
        g: 0x80,
        b: 0x80,
        a: 0xFF,
    };
    pub const DIMGRAY: Color = Color {
        r: 0x69,
        g: 0x69,
        b: 0x69,
        a: 0xFF,
    };
    pub const LIGHTSLATEGRAY: Color = Color {
        r: 0x77,
        g: 0x88,
        b: 0x99,
        a: 0xFF,
    };
    pub const SLATEGRAY: Color = Color {
        r: 0x70,
        g: 0x80,
        b: 0x90,
        a: 0xFF,
    };
    pub const DARKSLATEGRAY: Color = Color {
        r: 0x2F,
        g: 0x4F,
        b: 0x4F,
        a: 0xFF,
    };
    pub const BLACK: Color = Color {
        r: 0x00,
        g: 0x00,
        b: 0x00,
        a: 0xFF,
    };

    /// Brown CSS colors ///////////////////////////////////////////////////////
    pub const CORNSILK: Color = Color {
        r: 0xFF,
        g: 0xF8,
        b: 0xDC,
        a: 0xFF,
    };
    pub const BLANCHEDALMOND: Color = Color {
        r: 0xFF,
        g: 0xEB,
        b: 0xCD,
        a: 0xFF,
    };
    pub const BISQUE: Color = Color {
        r: 0xFF,
        g: 0xE4,
        b: 0xC4,
        a: 0xFF,
    };
    pub const NAVAJOWHITE: Color = Color {
        r: 0xFF,
        g: 0xDE,
        b: 0xAD,
        a: 0xFF,
    };
    pub const WHEAT: Color = Color {
        r: 0xF5,
        g: 0xDE,
        b: 0xB3,
        a: 0xFF,
    };
    pub const BURLYWOOD: Color = Color {
        r: 0xDE,
        g: 0xB8,
        b: 0x87,
        a: 0xFF,
    };
    pub const TAN: Color = Color {
        r: 0xD2,
        g: 0xB4,
        b: 0x8C,
        a: 0xFF,
    };
    pub const ROSYBROWN: Color = Color {
        r: 0xBC,
        g: 0x8F,
        b: 0x8F,
        a: 0xFF,
    };
    pub const SANDYBROWN: Color = Color {
        r: 0xF4,
        g: 0xA4,
        b: 0x60,
        a: 0xFF,
    };
    pub const GOLDENROD: Color = Color {
        r: 0xDA,
        g: 0xA5,
        b: 0x20,
        a: 0xFF,
    };
    pub const PERU: Color = Color {
        r: 0xCD,
        g: 0x85,
        b: 0x3F,
        a: 0xFF,
    };
    pub const CHOCOLATE: Color = Color {
        r: 0xD2,
        g: 0x69,
        b: 0x1E,
        a: 0xFF,
    };
    pub const SADDLEBROWN: Color = Color {
        r: 0x8B,
        g: 0x45,
        b: 0x13,
        a: 0xFF,
    };
    pub const SIENNA: Color = Color {
        r: 0xA0,
        g: 0x52,
        b: 0x2D,
        a: 0xFF,
    };
    pub const BROWN: Color = Color {
        r: 0xA5,
        g: 0x2A,
        b: 0x2A,
        a: 0xFF,
    };
    pub const MAROON: Color = Color {
        r: 0x80,
        g: 0x00,
        b: 0x00,
        a: 0xFF,
    };
}

impl Style {
    /// Applies a change to this style, yielding a new changed style
    pub fn apply(&self, modifier: StyleModifier) -> Style {
        Style {
            foreground: modifier.foreground.unwrap_or(self.foreground),
            background: modifier.background.unwrap_or(self.background),
            font_style: modifier.font_style.unwrap_or(self.font_style),
        }
    }
}

impl Default for Style {
    fn default() -> Style {
        Style {
            foreground: Color::BLACK,
            background: Color::WHITE,
            font_style: FontStyle::empty(),
        }
    }
}

impl StyleModifier {
    /// Applies the other modifier to this one, creating a new modifier.
    /// Values in `other` are preferred.
    pub fn apply(&self, other: StyleModifier) -> StyleModifier {
        StyleModifier {
            foreground: other.foreground.or(self.foreground),
            background: other.background.or(self.background),
            font_style: other.font_style.or(self.font_style),
        }
    }
}
<|endoftext|>


<commit_before>use std::path::Path;

/// Enum for representing the state of the package.
pub enum PackageState<'a> {
    /// This is the initial state of a package.
    NotDownloaded {
        /// The path where the package is going to be located at.
        location: &'a Path,
        /// The URL of where the package is going to be downloaded from.
        url: &'a str,
    },

    /// The state is switched to Downloaded when the download of the
    /// package is started and finished.
    Downloaded {
        /// Indicates whether the download process is interrupted.
        in_progress: bool,
        /// The path where the half-downloaded package is located at.
        location: &'a Path,
        /// The total file size of the package.
        file_size: u32,
    },

    /// The state is switched to Unpacked when the package is decompressed
    /// after it's been downloaded.
    Unpacked {
        /// The path where the unpacked package is located at.
        location: &'a Path,
        /// The total file size of the package.
        file_size: u32,
    },

    /// The final state of a package, which indicates that the unpacked package
    /// is moved into a directory and setup properly (i.e environment variables
    /// and other finishing touches)
    Installed {
        /// The path where the package is located at.
        location: &'a Path,
        /// The total file size of the package.
        file_size: u32,
    },
}

impl<'a> PackageState<'a> {
    /// Create a new PackageState.
    fn new(url: &'a str, location: &'a Path) -> Self {
        PackageState::NotDownloaded { location, url }
    }

    /// A bool to determine whether the package is downloaded.
    fn is_downloaded(&self) -> bool {
        match self {
            &PackageState::Downloaded { .. } => true,
            _ => false
        }
    }

    /// A bool to determine whether the package is downloaded.
    fn is_unpacked(&self) -> bool {
        match self {
            &PackageState::Unpacked { .. } => true,
            _ => false
        }
    }

    /// A bool to determine whether the package is downloaded.
    fn is_installed(&self) -> bool {
        match self {
            &PackageState::Installed { .. } => true,
            _ => false
        }
    }

    /// Returns the path of where the package is (or is going to be) located
    /// at.
    fn store_path(&self) -> &'a Path {
        match self {
            &PackageState::NotDownloaded { location: path, .. } => path,
            &PackageState::Downloaded { location: path, .. } => path,
            &PackageState::Unpacked { location: path, .. } => path,
            &PackageState::Installed { location: path, .. } => path,
        }
    }
}
<commit_msg>Make PackageState own the strings and paths<commit_after>use std::path::{ Path, PathBuf };

/// Enum for representing the state of the package.
pub enum PackageState {
    /// This is the initial state of a package.
    NotDownloaded {
        /// The path where the package is going to be located at.
        location: PathBuf,
        /// The URL of where the package is going to be downloaded from.
        url: String,
    },

    /// The state is switched to Downloaded when the download of the
    /// package is started and finished.
    Downloaded {
        /// Indicates whether the download process is interrupted.
        in_progress: bool,
        /// The path where the half-downloaded package is located at.
        location: PathBuf,
        /// The total file size of the package.
        file_size: u32,
    },

    /// The state is switched to Unpacked when the package is decompressed
    /// after it's been downloaded.
    Unpacked {
        /// The path where the unpacked package is located at.
        location: PathBuf,
        /// The total file size of the package.
        file_size: u32,
    },

    /// The final state of a package, which indicates that the unpacked package
    /// is moved into a directory and setup properly (i.e environment variables
    /// and other finishing touches)
    Installed {
        /// The path where the package is located at.
        location: PathBuf,
        /// The total file size of the package.
        file_size: u32,
    },
}

impl<'a> PackageState {
    /// Create a new PackageState.
    fn new(url: &'a str, location: &'a Path) -> Self {
        PackageState::NotDownloaded {
            location: location.to_path_buf(),
            url: url.to_owned(),
        }
    }

    /// A bool to determine whether the package is downloaded.
    fn is_downloaded(&self) -> bool {
        match self {
            &PackageState::Downloaded { .. } => true,
            _ => false
        }
    }

    /// A bool to determine whether the package is downloaded.
    fn is_unpacked(&self) -> bool {
        match self {
            &PackageState::Unpacked { .. } => true,
            _ => false
        }
    }

    /// A bool to determine whether the package is downloaded.
    fn is_installed(&self) -> bool {
        match self {
            &PackageState::Installed { .. } => true,
            _ => false
        }
    }

    /// Returns the path of where the package is (or is going to be) located
    /// at.
    fn store_path(&self) -> &Path {
        match self {
            &PackageState::NotDownloaded { location: ref path, .. } => path,
            &PackageState::Downloaded { location: ref path, .. } => path,
            &PackageState::Unpacked { location: ref path, .. } => path,
            &PackageState::Installed { location: ref path, .. } => path,
        }
    }
}
<|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::codegen::Bindings::CSSStyleDeclarationBinding::{self, CSSStyleDeclarationMethods};
use dom::bindings::codegen::InheritTypes::{NodeCast, ElementCast};
use dom::bindings::error::{Error, ErrorResult, Fallible};
use dom::bindings::global::GlobalRef;
use dom::bindings::js::{JS, Root};
use dom::bindings::utils::{Reflector, reflect_dom_object};
use dom::document::DocumentHelpers;
use dom::element::{ElementHelpers, StylePriority, Element};
use dom::node::{window_from_node, document_from_node, NodeDamage, NodeHelpers};
use dom::window::{Window, WindowHelpers};
use selectors::parser::PseudoElement;
use string_cache::Atom;
use style::properties::PropertyDeclaration;
use style::properties::{is_supported_property, longhands_from_shorthand, parse_one_declaration};
use util::str::DOMString;

use std::ascii::AsciiExt;
use std::borrow::ToOwned;
use std::cell::Ref;

// http://dev.w3.org/csswg/cssom/#the-cssstyledeclaration-interface
#[dom_struct]
#[derive(HeapSizeOf)]
pub struct CSSStyleDeclaration {
    reflector_: Reflector,
    owner: JS<Element>,
    readonly: bool,
    pseudo: Option<PseudoElement>,
}

#[derive(PartialEq, HeapSizeOf)]
pub enum CSSModificationAccess {
    ReadWrite,
    Readonly
}

macro_rules! css_properties(
    ( $([$getter:ident, $setter:ident, $cssprop:expr]),* ) => (
        $(
            fn $getter(self) -> DOMString {
                self.GetPropertyValue($cssprop.to_owned())
            }
            fn $setter(self, value: DOMString) -> ErrorResult {
                self.SetPropertyValue($cssprop.to_owned(), value)
            }
        )*
    );
);

fn serialize_list(list: &[Ref<PropertyDeclaration>]) -> DOMString {
    list.iter().fold(String::new(), |accum, ref declaration| {
        accum + &declaration.value() + " "
    })
}

impl CSSStyleDeclaration {
    pub fn new_inherited(owner: &Element,
                         pseudo: Option<PseudoElement>,
                         modification_access: CSSModificationAccess) -> CSSStyleDeclaration {
        CSSStyleDeclaration {
            reflector_: Reflector::new(),
            owner: JS::from_ref(owner),
            pseudo: pseudo,
            readonly: modification_access == CSSModificationAccess::Readonly,
        }
    }

    pub fn new(global: &Window, owner: &Element,
               pseudo: Option<PseudoElement>,
               modification_access: CSSModificationAccess) -> Root<CSSStyleDeclaration> {
        reflect_dom_object(box CSSStyleDeclaration::new_inherited(owner, pseudo, modification_access),
                           GlobalRef::Window(global),
                           CSSStyleDeclarationBinding::Wrap)
    }
}

trait PrivateCSSStyleDeclarationHelpers {
    fn get_declaration(&self, property: &Atom) -> Option<Ref<PropertyDeclaration>>;
    fn get_important_declaration(&self, property: &Atom) -> Option<Ref<PropertyDeclaration>>;
}

impl PrivateCSSStyleDeclarationHelpers for Element {
    fn get_declaration(&self, property: &Atom) -> Option<Ref<PropertyDeclaration>> {
        let element = ElementCast::from_ref(self);
        element.get_inline_style_declaration(property)
    }

    fn get_important_declaration(&self, property: &Atom) -> Option<Ref<PropertyDeclaration>> {
        let element = ElementCast::from_ref(self);
        element.get_important_inline_style_declaration(property)
    }
}

impl CSSStyleDeclaration {
    fn get_computed_style(&self, property: &Atom) -> Option<DOMString> {
        let owner = self.owner.root();
        let node = NodeCast::from_ref(owner.r());
        if !node.is_in_doc() {
            // TODO: Node should be matched against the style rules of this window.
            // Firefox is currently the only browser to implement this.
            return None;
        }
        let addr = node.to_trusted_node_address();
        window_from_node(owner.r()).resolved_style_query(addr, self.pseudo.clone(), property)
    }
}

impl<'a> CSSStyleDeclarationMethods for &'a CSSStyleDeclaration {
    // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-length
    fn Length(self) -> u32 {
        let owner = self.owner.root();
        let elem = ElementCast::from_ref(owner.r());
        let len = match *elem.style_attribute().borrow() {
            Some(ref declarations) => declarations.normal.len() + declarations.important.len(),
            None => 0
        };
        len as u32
    }

    // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-item
    fn Item(self, index: u32) -> DOMString {
        let index = index as usize;
        let owner = self.owner.root();
        let elem = ElementCast::from_ref(owner.r());
        let style_attribute = elem.style_attribute().borrow();
        let result = style_attribute.as_ref().and_then(|declarations| {
            if index > declarations.normal.len() {
                declarations.important
                            .get(index - declarations.normal.len())
                            .map(|decl| format!("{:?} !important", decl))
            } else {
                declarations.normal
                            .get(index)
                            .map(|decl| format!("{:?}", decl))
            }
        });

        result.unwrap_or("".to_owned())
    }

    // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-getpropertyvalue
    fn GetPropertyValue(self, property: DOMString) -> DOMString {
        let owner = self.owner.root();

        // Step 1
        let property = Atom::from_slice(&property.to_ascii_lowercase());

        if self.readonly {
            // Readonly style declarations are used for getComputedStyle.
            return self.get_computed_style(&property).unwrap_or("".to_owned());
        }

        // Step 2
        let longhand_properties = longhands_from_shorthand(&property);
        if let Some(longhand_properties) = longhand_properties {
            // Step 2.1
            let mut list = vec!();

            // Step 2.2
            for longhand in &*longhand_properties {
                // Step 2.2.1
                let declaration = owner.get_declaration(&Atom::from_slice(&longhand));

                // Step 2.2.2 & 2.2.3
                match declaration {
                    Some(declaration) => list.push(declaration),
                    None => return "".to_owned(),
                }
            }

            // Step 2.3
            return serialize_list(&list);
        }

        // Step 3 & 4
        // FIXME: redundant let binding https://github.com/rust-lang/rust/issues/22252
        let result = match owner.get_declaration(&property) {
            Some(declaration) => declaration.value(),
            None => "".to_owned(),
        };
        result
    }

    // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-getpropertypriority
    fn GetPropertyPriority(self, property: DOMString) -> DOMString {
        // Step 1
        let property = Atom::from_slice(&property.to_ascii_lowercase());

        // Step 2
        let longhand_properties = longhands_from_shorthand(&property);
        if let Some(longhand_properties) = longhand_properties {
            // Step 2.1 & 2.2 & 2.3
            if longhand_properties.iter()
                                  .map(|&longhand| self.GetPropertyPriority(longhand.to_owned()))
                                  .all(|priority| priority == "important") {

                return "important".to_owned();
            }
        // Step 3
        } else {
            // FIXME: extra let binding https://github.com/rust-lang/rust/issues/22323
            let owner = self.owner.root();
            if owner.get_important_declaration(&property).is_some() {
                return "important".to_owned();
            }
        }

        // Step 4
        "".to_owned()
    }

    // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-setproperty
    fn SetProperty(self, property: DOMString, value: DOMString,
                   priority: DOMString) -> ErrorResult {
        // Step 1
        if self.readonly {
            return Err(Error::NoModificationAllowed);
        }

        // Step 2
        let property = property.to_ascii_lowercase();

        // Step 3
        if !is_supported_property(&property) {
            return Ok(());
        }

        // Step 4
        if value.is_empty() {
            return self.RemoveProperty(property).map(|_| ());
        }

        // Step 5
        let priority = match &*priority {
            "" => StylePriority::Normal,
            p if p.eq_ignore_ascii_case("important") => StylePriority::Important,
            _ => return Ok(()),
        };

        // Step 6
        let owner = self.owner.root();
        let window = window_from_node(owner.r());
        let declarations = parse_one_declaration(&property, &value, &window.r().get_url());

        // Step 7
        let declarations = if let Ok(declarations) = declarations {
            declarations
        } else {
            return Ok(());
        };

        let owner = self.owner.root();
        let element = ElementCast::from_ref(owner.r());

        // Step 8
        for decl in declarations {
            // Step 9
            element.update_inline_style(decl, priority);
        }

        let document = document_from_node(element);
        let node = NodeCast::from_ref(element);
        document.r().content_changed(node, NodeDamage::NodeStyleDamaged);
        Ok(())
    }

    // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-setpropertypriority
    fn SetPropertyPriority(self, property: DOMString, priority: DOMString) -> ErrorResult {
        // Step 1
        if self.readonly {
            return Err(Error::NoModificationAllowed);
        }

        // Step 2 & 3
        if !is_supported_property(&property) {
            return Ok(());
        }

        // Step 4
        let priority = match &*priority {
            "" => StylePriority::Normal,
            p if p.eq_ignore_ascii_case("important") => StylePriority::Important,
            _ => return Ok(()),
        };

        let owner = self.owner.root();
        let element = ElementCast::from_ref(owner.r());

        // Step 5 & 6
        match longhands_from_shorthand(&property) {
            Some(properties) => element.set_inline_style_property_priority(properties, priority),
            None => element.set_inline_style_property_priority(&[&*property], priority)
        }

        let document = document_from_node(element);
        let node = NodeCast::from_ref(element);
        document.r().content_changed(node, NodeDamage::NodeStyleDamaged);
        Ok(())
    }

    // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-setpropertyvalue
    fn SetPropertyValue(self, property: DOMString, value: DOMString) -> ErrorResult {
        self.SetProperty(property, value, "".to_owned())
    }

    // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-removeproperty
    fn RemoveProperty(self, property: DOMString) -> Fallible<DOMString> {
        // Step 1
        if self.readonly {
            return Err(Error::NoModificationAllowed);
        }

        // Step 2
        let property = property.to_ascii_lowercase();

        // Step 3
        let value = self.GetPropertyValue(property.clone());

        let owner = self.owner.root();
        let elem = ElementCast::from_ref(owner.r());

        match longhands_from_shorthand(&property) {
            // Step 4
            Some(longhands) => {
                for longhand in &*longhands {
                    elem.remove_inline_style_property(longhand)
                }
            }
            // Step 5
            None => elem.remove_inline_style_property(&property)
        }

        // Step 6
        Ok(value)
    }

    // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-cssfloat
    fn CssFloat(self) -> DOMString {
        self.GetPropertyValue("float".to_owned())
    }

    // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-cssfloat
    fn SetCssFloat(self, value: DOMString) -> ErrorResult {
        self.SetPropertyValue("float".to_owned(), value)
    }

    // https://dev.w3.org/csswg/cssom/#the-cssstyledeclaration-interface
    fn IndexedGetter(self, index: u32, found: &mut bool) -> DOMString {
        let rval = self.Item(index);
        *found = index < self.Length();
        rval
    }

    css_properties_accessors!(css_properties);
}
<commit_msg>Auto merge of #7330 - jxs:master, 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::codegen::Bindings::CSSStyleDeclarationBinding::{self, CSSStyleDeclarationMethods};
use dom::bindings::codegen::InheritTypes::{NodeCast, ElementCast};
use dom::bindings::error::{Error, ErrorResult, Fallible};
use dom::bindings::global::GlobalRef;
use dom::bindings::js::{JS, Root};
use dom::bindings::utils::{Reflector, reflect_dom_object};
use dom::document::DocumentHelpers;
use dom::element::{ElementHelpers, StylePriority, Element};
use dom::node::{window_from_node, document_from_node, NodeDamage, NodeHelpers};
use dom::window::{Window, WindowHelpers};
use selectors::parser::PseudoElement;
use string_cache::Atom;
use style::properties::PropertyDeclaration;
use style::properties::{is_supported_property, longhands_from_shorthand, parse_one_declaration};
use util::str::DOMString;

use std::ascii::AsciiExt;
use std::borrow::ToOwned;
use std::cell::Ref;

// http://dev.w3.org/csswg/cssom/#the-cssstyledeclaration-interface
#[dom_struct]
#[derive(HeapSizeOf)]
pub struct CSSStyleDeclaration {
    reflector_: Reflector,
    owner: JS<Element>,
    readonly: bool,
    pseudo: Option<PseudoElement>,
}

#[derive(PartialEq, HeapSizeOf)]
pub enum CSSModificationAccess {
    ReadWrite,
    Readonly
}

macro_rules! css_properties(
    ( $([$getter:ident, $setter:ident, $cssprop:expr]),* ) => (
        $(
            fn $getter(self) -> DOMString {
                self.GetPropertyValue($cssprop.to_owned())
            }
            fn $setter(self, value: DOMString) -> ErrorResult {
                self.SetPropertyValue($cssprop.to_owned(), value)
            }
        )*
    );
);

fn serialize_list(list: &[Ref<PropertyDeclaration>]) -> DOMString {
    list.iter().fold(String::new(), |accum, ref declaration| {
        accum + &declaration.value() + " "
    })
}

impl CSSStyleDeclaration {
    pub fn new_inherited(owner: &Element,
                         pseudo: Option<PseudoElement>,
                         modification_access: CSSModificationAccess) -> CSSStyleDeclaration {
        CSSStyleDeclaration {
            reflector_: Reflector::new(),
            owner: JS::from_ref(owner),
            pseudo: pseudo,
            readonly: modification_access == CSSModificationAccess::Readonly,
        }
    }

    pub fn new(global: &Window, owner: &Element,
               pseudo: Option<PseudoElement>,
               modification_access: CSSModificationAccess) -> Root<CSSStyleDeclaration> {
        reflect_dom_object(box CSSStyleDeclaration::new_inherited(owner, pseudo, modification_access),
                           GlobalRef::Window(global),
                           CSSStyleDeclarationBinding::Wrap)
    }
}

impl CSSStyleDeclaration {
    fn get_computed_style(&self, property: &Atom) -> Option<DOMString> {
        let owner = self.owner.root();
        let node = NodeCast::from_ref(owner.r());
        if !node.is_in_doc() {
            // TODO: Node should be matched against the style rules of this window.
            // Firefox is currently the only browser to implement this.
            return None;
        }
        let addr = node.to_trusted_node_address();
        window_from_node(owner.r()).resolved_style_query(addr, self.pseudo.clone(), property)
    }
}

impl<'a> CSSStyleDeclarationMethods for &'a CSSStyleDeclaration {
    // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-length
    fn Length(self) -> u32 {
        let owner = self.owner.root();
        let elem = ElementCast::from_ref(owner.r());
        let len = match *elem.style_attribute().borrow() {
            Some(ref declarations) => declarations.normal.len() + declarations.important.len(),
            None => 0
        };
        len as u32
    }

    // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-item
    fn Item(self, index: u32) -> DOMString {
        let index = index as usize;
        let owner = self.owner.root();
        let elem = ElementCast::from_ref(owner.r());
        let style_attribute = elem.style_attribute().borrow();
        let result = style_attribute.as_ref().and_then(|declarations| {
            if index > declarations.normal.len() {
                declarations.important
                            .get(index - declarations.normal.len())
                            .map(|decl| format!("{:?} !important", decl))
            } else {
                declarations.normal
                            .get(index)
                            .map(|decl| format!("{:?}", decl))
            }
        });

        result.unwrap_or("".to_owned())
    }

    // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-getpropertyvalue
    fn GetPropertyValue(self, property: DOMString) -> DOMString {
        let owner = self.owner.root();

        // Step 1
        let property = Atom::from_slice(&property.to_ascii_lowercase());

        if self.readonly {
            // Readonly style declarations are used for getComputedStyle.
            return self.get_computed_style(&property).unwrap_or("".to_owned());
        }

        // Step 2
        let longhand_properties = longhands_from_shorthand(&property);
        if let Some(longhand_properties) = longhand_properties {
            // Step 2.1
            let mut list = vec!();

            // Step 2.2
            for longhand in &*longhand_properties {
                // Step 2.2.1
                let declaration = owner.get_inline_style_declaration(&Atom::from_slice(&longhand));

                // Step 2.2.2 & 2.2.3
                match declaration {
                    Some(declaration) => list.push(declaration),
                    None => return "".to_owned(),
                }
            }

            // Step 2.3
            return serialize_list(&list);
        }

        // Step 3 & 4
        // FIXME: redundant let binding https://github.com/rust-lang/rust/issues/22252
        let result = match owner.get_inline_style_declaration(&property) {
            Some(declaration) => declaration.value(),
            None => "".to_owned(),
        };
        result
    }

    // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-getpropertypriority
    fn GetPropertyPriority(self, property: DOMString) -> DOMString {
        // Step 1
        let property = Atom::from_slice(&property.to_ascii_lowercase());

        // Step 2
        let longhand_properties = longhands_from_shorthand(&property);
        if let Some(longhand_properties) = longhand_properties {
            // Step 2.1 & 2.2 & 2.3
            if longhand_properties.iter()
                                  .map(|&longhand| self.GetPropertyPriority(longhand.to_owned()))
                                  .all(|priority| priority == "important") {

                return "important".to_owned();
            }
        // Step 3
        } else {
            // FIXME: extra let binding https://github.com/rust-lang/rust/issues/22323
            let owner = self.owner.root();
            if owner.get_important_inline_style_declaration(&property).is_some() {
                return "important".to_owned();
            }
        }

        // Step 4
        "".to_owned()
    }

    // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-setproperty
    fn SetProperty(self, property: DOMString, value: DOMString,
                   priority: DOMString) -> ErrorResult {
        // Step 1
        if self.readonly {
            return Err(Error::NoModificationAllowed);
        }

        // Step 2
        let property = property.to_ascii_lowercase();

        // Step 3
        if !is_supported_property(&property) {
            return Ok(());
        }

        // Step 4
        if value.is_empty() {
            return self.RemoveProperty(property).map(|_| ());
        }

        // Step 5
        let priority = match &*priority {
            "" => StylePriority::Normal,
            p if p.eq_ignore_ascii_case("important") => StylePriority::Important,
            _ => return Ok(()),
        };

        // Step 6
        let owner = self.owner.root();
        let window = window_from_node(owner.r());
        let declarations = parse_one_declaration(&property, &value, &window.r().get_url());

        // Step 7
        let declarations = if let Ok(declarations) = declarations {
            declarations
        } else {
            return Ok(());
        };

        let owner = self.owner.root();
        let element = ElementCast::from_ref(owner.r());

        // Step 8
        for decl in declarations {
            // Step 9
            element.update_inline_style(decl, priority);
        }

        let document = document_from_node(element);
        let node = NodeCast::from_ref(element);
        document.r().content_changed(node, NodeDamage::NodeStyleDamaged);
        Ok(())
    }

    // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-setpropertypriority
    fn SetPropertyPriority(self, property: DOMString, priority: DOMString) -> ErrorResult {
        // Step 1
        if self.readonly {
            return Err(Error::NoModificationAllowed);
        }

        // Step 2 & 3
        if !is_supported_property(&property) {
            return Ok(());
        }

        // Step 4
        let priority = match &*priority {
            "" => StylePriority::Normal,
            p if p.eq_ignore_ascii_case("important") => StylePriority::Important,
            _ => return Ok(()),
        };

        let owner = self.owner.root();
        let element = ElementCast::from_ref(owner.r());

        // Step 5 & 6
        match longhands_from_shorthand(&property) {
            Some(properties) => element.set_inline_style_property_priority(properties, priority),
            None => element.set_inline_style_property_priority(&[&*property], priority)
        }

        let document = document_from_node(element);
        let node = NodeCast::from_ref(element);
        document.r().content_changed(node, NodeDamage::NodeStyleDamaged);
        Ok(())
    }

    // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-setpropertyvalue
    fn SetPropertyValue(self, property: DOMString, value: DOMString) -> ErrorResult {
        self.SetProperty(property, value, "".to_owned())
    }

    // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-removeproperty
    fn RemoveProperty(self, property: DOMString) -> Fallible<DOMString> {
        // Step 1
        if self.readonly {
            return Err(Error::NoModificationAllowed);
        }

        // Step 2
        let property = property.to_ascii_lowercase();

        // Step 3
        let value = self.GetPropertyValue(property.clone());

        let owner = self.owner.root();
        let elem = ElementCast::from_ref(owner.r());

        match longhands_from_shorthand(&property) {
            // Step 4
            Some(longhands) => {
                for longhand in &*longhands {
                    elem.remove_inline_style_property(longhand)
                }
            }
            // Step 5
            None => elem.remove_inline_style_property(&property)
        }

        // Step 6
        Ok(value)
    }

    // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-cssfloat
    fn CssFloat(self) -> DOMString {
        self.GetPropertyValue("float".to_owned())
    }

    // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-cssfloat
    fn SetCssFloat(self, value: DOMString) -> ErrorResult {
        self.SetPropertyValue("float".to_owned(), value)
    }

    // https://dev.w3.org/csswg/cssom/#the-cssstyledeclaration-interface
    fn IndexedGetter(self, index: u32, found: &mut bool) -> DOMString {
        let rval = self.Item(index);
        *found = index < self.Length();
        rval
    }

    css_properties_accessors!(css_properties);
}
<|endoftext|>