<commit_before>extern crate rustc_serialize;
use self::rustc_serialize::json::Json;

use std::marker::PhantomData;

//
// Telemetry is a mechanism used to capture metrics in an application,
// and either store the data locally or upload to a server for
// statistical analysis.
//
// Examples of usage:
// - capturing the speed of an operation;
// - finding out if users are actually using a feature;
// - finding out how the duration of a session;
// - determine the operating system on which the application is executed;
// - determining the configuration of the application;
// - capturing the operations that slow down the application;
// - determining the amount of I/O performed by the application;
// - ...
//
// The abstraction used by this library is the Histogram. Each
// Histogram serves to capture a specific measurement, store it
// locally and/or upload it to the server. Several types of Histograms
// are provided, suited to distinct kinds of measures.
//
//
// Memory note: the memory used by a histogram is recollected only
// when its instance of `telemetry` is garbage-collected. In other words,
// if a histogram goes out of scope for some reason, its data remains
// in telemetry and will be stored and/or uploaded in accordance with the
// configuration of this telemetry instance.
//

//
// A software version, e.g. [2015, 10, 10, 0]
//
type Version = [u32;4];

//
// Metadata on a histogram.
//
struct Metadata {
    // A key used to identify the histogram. Must be unique to the instance
    // of `telemetry`.
    key: String,

    // Optionally, a version of the product at which this histogram expires.
    expires: Option<Version>,

    // A human-redable description of the histogram. Do not forget units
    // or explanations of enumeration labels.
    description: String,
}

trait Histogram<T> {
    //
    // Record a value in this histogram.
    //
    // The value is recorded only if all of the following conditions are met:
    // - `telemetry` is activated; and
    // - this histogram has not expired; and
    // - the histogram is active.
    //
    fn record(&self, telemetry: &mut Telemetry, value: T) {
        self.record_cb(telemetry, || Some(value))
    }

    //
    // Record a value in this histogram, as provided by a callback.
    //
    // The callback is triggered only if all of the following conditions are met:
    // - `telemetry` is activated; and
    // - this histogram has not expired; and
    // - the histogram is active.
    //
    // If the callback returns `None`, no value is recorded.
    //
    fn record_cb<F>(&self, telemetry: &mut Telemetry, _: F) where F: FnOnce() -> Option<T>;
}

pub trait Flatten<T> {
    fn as_u32(&self) -> u32;
}

impl Flatten<u32> for u32 {
    fn as_u32(&self) -> u32 {
        *self
    }
}

//
//
// Flag histograms.
//
// This histogram type allows you to record a single value. This type
// is useful if you need to track whether a feature was ever used
// during a session. You only need to add a single line of code which
// sets the flag when the feature is used because the histogram is
// initialized with a default value of false (flag not set).
//
//

struct FlagStorage {
    // `true` once we have called `record`, `false` until then.
    encountered: bool
}

impl FlagStorage {
    fn new() -> FlagStorage {
        FlagStorage {
            encountered: false
        }
    }
}

impl RawStorage for FlagStorage {
    fn store(&mut self, _: u32) {
        self.encountered = true;
    }
    fn serialize(&self) -> Json {
        unreachable!() // FIXME: Implement
    }
}

pub struct Flag {
    shared: Shared,
}

impl Histogram<()> for Flag {
    fn record_cb<F>(&self, telemetry: &mut Telemetry, cb: F) where F: FnOnce() -> Option<()>  {
        if !self.shared.should_record(telemetry) {
            return;
        }
        match cb() {
            None => {}
            Some(()) => telemetry.raw_record(&self.shared, 0)
        }
    }
}

impl Flag {
    fn new(telemetry: &mut Telemetry, meta: Metadata) -> Flag {
        let storage = Box::new(FlagStorage::new());
        let key = telemetry.register_storage(meta, storage).unwrap(); // FIXME: This will self-destruct if we have expired. We probably just want to neutralize the histogram.
        Flag {
            shared: Shared::new(key),
        }
    }
}

//
// Linear histograms.
//
//
// Linear histograms classify numeric integer values into same-sized
// buckets. This type is typically used for percentages.
//
struct LinearStorage {
    values: Vec<u32>// We cannot use an array here, as this would make the struct unsized
}

impl LinearStorage {
    fn new(buckets: usize) -> LinearStorage {
        LinearStorage {
            values: Vec::with_capacity(buckets)
        }
    }
}

impl RawStorage for LinearStorage {
    fn store(&mut self, index: u32) {
        self.values[index as usize] += 1;
    }
    fn serialize(&self) -> Json {
        unreachable!() // FIXME: Implement
    }
}

pub struct Linear<T> where T: Flatten<T> {
    witness: PhantomData<T>,
    shared: Shared,
    min: u32,
    max: u32, // Invariant: max > min
    buckets: u32 // FIXME: We assume that u32 <= usize. Assert?
}

impl<T> Histogram<T> for Linear<T> where T: Flatten<T> {
    fn record_cb<F>(&self, telemetry: &mut Telemetry, cb: F) where F: FnOnce() -> Option<T>  {
        if !self.shared.should_record(telemetry) {
            return;
        }
        match cb() {
            None => {}
            Some(v) => telemetry.raw_record(&self.shared, self.get_bucket(v))
        }
    }

}

impl<T> Linear<T> where T: Flatten<T> {
    fn new(telemetry: &mut Telemetry, meta: Metadata, min: u32, max: u32, buckets: usize) -> Linear<T> {
        let storage = Box::new(LinearStorage::new(buckets));
        let key = telemetry.register_storage(meta, storage).unwrap();  // FIXME: This will self-destruct if we have expired. We probably just want to neutralize the histogram.
        Linear {
            witness: PhantomData,
            shared: Shared::new(key),
            min: min,
            max: max,
            buckets: buckets as u32
        }
    }

    fn get_bucket(&self, value: T) -> u32 {
        let value = value.as_u32();
        if value >= self.max {
            0
        } else if value <= self.min {
            self.buckets - 1 as u32
        } else {
            let num = value as f32 - self.min as f32;
            let den = self.max as f32 - self.min as f32;
            let res = (num / den) * self.buckets as f32;
            res as u32
        }
    }
}

struct Telemetry {
    // The name of the product.
    product: String,

    // The version of the product. Some histograms may be limited to
    // specific versions of the product.
    version: Version,

    // Has telemetry been activated? `false` by default.
    is_active: bool,

    // The storage for all histograms.
    stores: Vec<Box<RawStorage>>,
}

struct StorageSettings; // FIXME: Define
struct ServerSettings; // FIXME: Define

struct StorageKey {
    index: usize,
    telemetry: *const Telemetry
}

impl Telemetry {
    //
    // Construct a new instance of telemetry.
    //
    // This instance is deactivated by default.
    //
    pub fn new(product: String,
               version: Version,
               _: Option<StorageSettings>,
               _: Option<ServerSettings>) -> Telemetry {
        Telemetry {
            product: product,
            version: version,
            stores: Vec::new(),
            is_active: false,
        }
    }

    //
    // Activate/deactivate Telemetry. If Telemetry is deactivated,
    // it will not record new data.
    //
    pub fn set_active(&mut self, value: bool) {
        self.is_active = value;
    }

    pub fn is_active(&self) -> bool {
        self.is_active
    }

    //
    // Register the `RawStorage` used by an histogram.
    //
    fn register_storage(&mut self, meta: Metadata, storage: Box<RawStorage>) -> Option<StorageKey> {
        {
            // Don't add the histogram if it is expired.
            match meta.expires {
                Some(v) if v <= self.version => return None,
                _ => {}
            }
        }
        self.stores.push(storage);
        Some(StorageKey {
            index: self.stores.len(), // Note: this won't be 0.
            telemetry: self
        })
    }

    //
    //
    //
    fn raw_record(&mut self, histogram: &Shared, value: u32) {
        assert!(histogram.key.index != 0);
        assert!(histogram.key.telemetry == self);
        let ref mut storage = self.stores[histogram.key.index];
        storage.store(value);
    }
}

//
// Low-level, untyped, implementation of histogram storage.
//
trait RawStorage {
    fn store(&mut self, value: u32);
    fn serialize(&self) -> Json;
}

//
// Features shared by all histograms
//
struct Shared {
    // A key used to map a histogram to its storage owned by telemetry.
    key: StorageKey,

    // `true` unless the histogram has been deactivated by user request.
    // If `false`, no data will be recorded for this histogram.
    is_active: bool,

    // `false` unless the histogram is designed to expire at some
    // version of the product and the current version is more recent.
    is_expired: bool
}

impl Shared {
    fn new(key: StorageKey) -> Shared {
        Shared {
            key: key,
            is_active: true,
            is_expired: false
        }
    }

    fn should_record(&self, telemetry: &Telemetry) -> bool {
        if !self.is_active {
            return false;
        }
        if self.is_expired {
            return false;
        }
        if !telemetry.is_active {
            return false;
        }
        return true;
    }
}
<commit_msg>Now with a thread<commit_after>extern crate rustc_serialize;
use self::rustc_serialize::json::Json;

use std::collections::HashMap;
use std::collections::hash_map::{Entry, VacantEntry, OccupiedEntry};
use std::collections::hash_map::Entry::{Occupied, Vacant};
use std::marker::PhantomData;

//
// Telemetry is a mechanism used to capture metrics in an application,
// and either store the data locally or upload to a server for
// statistical analysis.
//
// Examples of usage:
// - capturing the speed of an operation;
// - finding out if users are actually using a feature;
// - finding out how the duration of a session;
// - determine the operating system on which the application is executed;
// - determining the configuration of the application;
// - capturing the operations that slow down the application;
// - determining the amount of I/O performed by the application;
// - ...
//
// The abstraction used by this library is the Histogram. Each
// Histogram serves to capture a specific measurement, store it
// locally and/or upload it to the server. Several types of Histograms
// are provided, suited to distinct kinds of measures.
//
//
// Memory note: the memory used by a histogram is recollected only
// when its instance of `telemetry` is garbage-collected. In other words,
// if a histogram goes out of scope for some reason, its data remains
// in telemetry and will be stored and/or uploaded in accordance with the
// configuration of this telemetry instance.
//

//
// A software version, e.g. [2015, 10, 10, 0]
//
pub type Version = [u32;4];

//
// Metadata on a histogram.
//
pub struct Metadata {
    // A key used to identify the histogram. Must be unique to the instance
    // of `telemetry`.
    pub key: String,

    // Optionally, a version of the product at which this histogram expires.
    pub expires: Option<Version>,

    // A human-redable description of the histogram. Do not forget units
    // or explanations of enumeration labels.
    pub description: String,
}

trait Histogram<T> {
    //
    // Record a value in this histogram.
    //
    // The value is recorded only if all of the following conditions are met:
    // - `telemetry` is activated; and
    // - this histogram has not expired; and
    // - the histogram is active.
    //
    fn record(&mut self, telemetry: &mut Telemetry, value: T) {
        self.record_cb(telemetry, || Some(value))
    }

    //
    // Record a value in this histogram, as provided by a callback.
    //
    // The callback is triggered only if all of the following conditions are met:
    // - `telemetry` is activated; and
    // - this histogram has not expired; and
    // - the histogram is active.
    //
    // If the callback returns `None`, no value is recorded.
    //
    fn record_cb<F>(&mut self, telemetry: &mut Telemetry, _: F) where F: FnOnce() -> Option<T>;
}

pub trait Flatten<T> {
    fn as_u32(&self) -> u32;
}

impl Flatten<u32> for u32 {
    fn as_u32(&self) -> u32 {
        *self
    }
}

//
//
// Flag histograms.
//
// This histogram type allows you to record a single value. This type
// is useful if you need to track whether a feature was ever used
// during a session. You only need to add a single line of code which
// sets the flag when the feature is used because the histogram is
// initialized with a default value of false (flag not set).
//
//

struct FlagStorage {
    // `true` once we have called `record`, `false` until then.
    encountered: bool
}

impl FlagStorage {
    fn new() -> FlagStorage {
        FlagStorage {
            encountered: false
        }
    }
}

impl RawStorage for FlagStorage {
    fn store(&mut self, _: u32) {
        self.encountered = true;
    }
    fn serialize(&self) -> Json {
        unreachable!() // FIXME: Implement
    }
}

pub struct Flag {
    shared: Shared,
    // FIXME: We could cache the value to avoid sending messages across threads
}

impl Histogram<()> for Flag {
    fn record_cb<F>(&mut self, telemetry: &mut Telemetry, cb: F) where F: FnOnce() -> Option<()>  {
        self.shared.with_key(telemetry, |k| {
            match cb() {
                None => {}
                Some(()) => telemetry.raw_record(&k, 0)
            }
        });
    }
}

impl Flag {
    pub fn new(telemetry: &mut Telemetry, meta: Metadata) -> Flag {
        let storage = Box::new(FlagStorage::new());
        let key = telemetry.register(meta, storage);
        Flag {
            shared: Shared::new(key),
        }
    }
}

//
// Linear histograms.
//
//
// Linear histograms classify numeric integer values into same-sized
// buckets. This type is typically used for percentages.
//

struct LinearStorage {
    values: Vec<u32>// We cannot use an array here, as this would make the struct unsized
}

impl LinearStorage {
    fn new(buckets: usize) -> LinearStorage {
        LinearStorage {
            values: Vec::with_capacity(buckets)
        }
    }
}

impl RawStorage for LinearStorage {
    fn store(&mut self, index: u32) {
        self.values[index as usize] += 1;
    }
    fn serialize(&self) -> Json {
        unreachable!() // FIXME: Implement
    }
}

pub struct Linear<T> where T: Flatten<T> {
    witness: PhantomData<T>,
    shared: Shared,
    min: u32,
    max: u32, // Invariant: max > min
    buckets: u32 // FIXME: We assume that u32 <= usize. Assert?
}

impl<T> Histogram<T> for Linear<T> where T: Flatten<T> {
    fn record_cb<F>(&mut self, telemetry: &mut Telemetry, cb: F) where F: FnOnce() -> Option<T>  {
        self.shared.with_key(telemetry, |k| {
            match cb() {
                None => {}
                Some(v) => telemetry.raw_record(&k, self.get_bucket(v))
            }
        });
    }
}

impl<T> Linear<T> where T: Flatten<T> {
    fn new(telemetry: &mut Telemetry, meta: Metadata, min: u32, max: u32, buckets: usize) -> Linear<T> {
        let storage = Box::new(LinearStorage::new(buckets));
        let key = telemetry.register(meta, storage);
        Linear {
            witness: PhantomData,
            shared: Shared::new(key),
            min: min,
            max: max,
            buckets: buckets as u32
        }
    }

    fn get_bucket(&self, value: T) -> u32 {
        let value = value.as_u32();
        if value >= self.max {
            0
        } else if value <= self.min {
            self.buckets - 1 as u32
        } else {
            let num = value as f32 - self.min as f32;
            let den = self.max as f32 - self.min as f32;
            let res = (num / den) * self.buckets as f32;
            res as u32
        }
    }
}

impl Telemetry {
    fn new(product: String, version: Version) -> Telemetry {
        let (sender, receiver) = channel();
        thread::spawn(|| {
            let mut data = TelemetryTask::new();
            for msg in receiver {
                match msg {
                    Op::Register(index, storage) => {
                        data.data.insert(index, storage);
                    }
                    Op::Record(index, value) => {
                        let ref mut storage = data.data.get_mut(&index).unwrap();
                        storage.store(value);
                    }
                    Op::Serialize(chan) => {
                        unreachable!() // Implement
                    }
                }
            }
        });
        Telemetry {
            generator: KeyGenerator::new(),
            product: product,
            version: version,
            sender: sender,
            is_active: false,
        }
    }

    fn register(&self, meta: Metadata, storage: Box<RawStorage>) -> Option<Key<Telemetry>> {
        // Don't bother adding the histogram if it is expired.
        match meta.expires {
            Some(v) if v <= self.version => return None,
            _ => {}
        }

        let key = self.generator.next(self);
        self.sender.send(Op::Register(key.index, storage)).unwrap();
        Some(key)
    }

    fn raw_record(&self, key: &Key<Telemetry>, value: u32) {
        assert!(key.owner == self);
        self.sender.send(Op::Record(key.index, value)).unwrap();
    }
}

pub struct Telemetry {
    // The name of the product.
    product: String,

    // The version of the product. Some histograms may be limited to
    // specific versions of the product.
    version: Version,

    // Has telemetry been activated? `false` by default.
    is_active: bool,

    generator: KeyGenerator,

    sender: Sender<Op>,
}

/*
pub struct StorageSettings; // FIXME: Define
pub struct ServerSettings; // FIXME: Define

struct StorageKey {
    index: usize,
    telemetry: *const Telemetry
}

impl Telemetry {
    //
    // Construct a new instance of telemetry.
    //
    // This instance is deactivated by default.
    //
    pub fn new(product: String, version: Version) -> Telemetry {
        Telemetry {
            product: product,
            version: version,
            stores: Vec::new(),
            is_active: false,
            keyed_stores: Vec::new(),
        }
    }

    //
    // Activate/deactivate Telemetry. If Telemetry is deactivated,
    // it will not record new data.
    //
    pub fn set_active(&mut self, value: bool) {
        self.is_active = value;
    }

    pub fn is_active(&self) -> bool {
        self.is_active
    }

    //
    // Register the `RawStorage` used by an histogram.
    //
    fn register_storage(&mut self, meta: Metadata, storage: Box<RawStorage>) -> Option<StorageKey> {
        {
            // Don't add the histogram if it is expired.
            match meta.expires {
                Some(v) if v <= self.version => return None,
                _ => {}
            }
        }
        self.stores.push(storage);
        Some(StorageKey {
            index: self.stores.len(), // Note: this won't be 0.
            telemetry: self
        })
    }

    //
    // Record a value in an histogram.
    //
    // This call causes a panic if `histogram` has been created for
    // another instance of telemetry or if the histogram has expired.
    //
    fn raw_record(&mut self, histogram: &Shared, value: u32) {
        assert!(histogram.key.index != 0);
        assert!(histogram.key.telemetry == self);
        // FIXME: Assert that this is a non-keyed histogram
        let ref mut storage = self.stores[histogram.key.index];
        storage.store(value);
    }

    fn raw_record_keyed<F>(&mut self, histogram: &Shared, key: String, value: u32, cb: &Box<F>) where F: Fn() -> Box<RawStorage> {
        assert!(histogram.key.index != 0);
        assert!(histogram.key.telemetry == self);
        // FIXME: Assert that this is a keyed histogram
        let ref mut storage = self.keyed_stores[histogram.key.index];
        match storage.entry(key) {
            Occupied(mut e) => {
                e.get_mut().store(value)
            }
            Vacant(mut e) => {
                let mut instance = cb();
                instance.store(value);
                e.insert(instance);
            }
        }
    }
}

*/
//
// Low-level, untyped, implementation of histogram storage.
//
trait RawStorage: Send {
    fn store(&mut self, value: u32);
    fn serialize(&self) -> Json;
}

//
// Features shared by all histograms
//
struct Shared {
    // A key used to map a histogram to its storage owned by telemetry,
    // or None if the histogram has been rejected by telemetry because
    // it has expired.
    key: Option<Key<Telemetry>>,

    // `true` unless the histogram has been deactivated by user request.
    // If `false`, no data will be recorded for this histogram.
    is_active: bool,
}

impl Shared {
    fn new(key: Option<Key<Telemetry>>) -> Shared {
        Shared {
            key: key,
            is_active: true,
        }
    }

    fn with_key<F>(&self, telemetry: &Telemetry, cb: F)
        where F: FnOnce(&Key<Telemetry>) -> ()
    {
        if !self.is_active {
            return;
        }
        if !telemetry.is_active {
            return;
        }
        match self.key {
            None => return,
            Some(ref k) => cb(k)
        }
    }
}

///////////////// Experiments

/*
Perhaps we should unpair the stuff that stores histograms from the
stuff that handles saving/upload.

Have a Collector that can request (and merge) data from all instances
of Telemetry.
*/

/*
A histogram is:
- a front-end, owned by the creator (implements Histogram<T>);
- a back-end, owned by the global telemetry (implements Storage2);
- the front-end contains an Arc<Sender<Key, u32>> used to send the data to the back-end through `telemetry`;
- keys are generated through an atomic KeyGenerator (avoiding the need to &mut)
*/

use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::atomic::Ordering::Relaxed;
use std::sync::mpsc::{channel, Receiver, Sender};
use std::sync::Arc;
use std::rc::Rc;
use std::thread;

struct Key<T> {
    index: usize,
    owner: *const T,
}
struct KeyGenerator {
    counter: AtomicUsize,
}
impl KeyGenerator {
    fn new() -> KeyGenerator {
        KeyGenerator {
            counter: AtomicUsize::new(0),
        }
    }
    fn next<T>(&self, owner: &T) -> Key<T> {
        Key {
            index: self.counter.fetch_add(1, Ordering::Relaxed),
            owner: owner
        }
    }
}

enum Op {
    Register(usize, Box<RawStorage>),
    Record(usize, u32),
    Serialize(Sender<Json>),
}

struct TelemetryTask {
    // Note: if we decide to change this to a Vec, don't forget that
    // we have no guarantee that items will be added in the order they
    // were created, so don't forget to make it a Vec<Option<>> and to
    // resize as needed.
    data: HashMap<usize, Box<RawStorage>>
}

impl TelemetryTask {
    fn new() -> TelemetryTask {
        TelemetryTask {
            data: HashMap::new()
        }
    }
}

struct FlatStorage {
    data: Vec<u32>
}
impl FlatStorage {
    fn new() -> FlatStorage {
        FlatStorage {
            data: Vec::new()
        }
    }
}

impl RawStorage for FlatStorage {
    fn store(&mut self, index: u32) {
        self.data[index as usize] += 1
    }
    fn serialize(&self) -> Json {
        unreachable!() // FIXME: Implement
    }
}

pub struct Flat {
    shared: Shared,
}

impl Flat {
    fn new(meta: Metadata, telemetry: &Telemetry) -> Flat {
        let storage = Box::new(FlatStorage::new());
        let key = telemetry.register(meta, storage);
        Flat {
            shared: Shared::new(key),
        }
    }
}

impl Flat {
    // FIXME: Deactivate if necessary.
    fn record_cb<F>(&mut self, telemetry: &Telemetry, cb: F) where F: FnOnce() -> Option<u32> {
        match self.shared.key {
            None => {}
            Some(ref k) => {
                match cb() {
                    None => {}
                    Some(v) => telemetry.raw_record(&k, v)
                }
            }
        }
    }
}

/*

pub struct Linear2 {
    updater: Sender<u32>,
}

struct Linear2Store {
    values: Vec<u32>
}

pub struct Flag2 {
    updater: Sender<(StorageKey, u32>>
    encountered: bool
}

struct Flag2Store {
    encountered: bool
}

pub struct Telemetry;
impl Telemetry {
    fn register<F>(&self, meta: Metadata, chan: Receiver<(StorageKey, u32)>, cb: F) -> StorageKey {
        where F: FnMut(u32) -> () {
        //
        unreachable!()
    }
}

impl Flag2 {
    pub fn new(meta: Metadata, telemetry: &Telemetry) -> Flag2 {
        let (sender, receiver) = channel();

        // This store is updated whenever we receive a message on `receiver`.
        // How do I do that? With an index, as usual.
        let mut store = Flag2Store {
            encountered: false
        };
        let mut closure = |_: u32| store.encountered = true;
        telemetry.register(meta, receiver, closure);
        Flag2 {
            updater: sender,
            encountered: false,
        }
    }
}

impl Flag2 {
    fn record_cb<F>(&mut self, telemetry: &mut Telemetry, _: F) where F: FnOnce() -> Option<()>{
        if self.encountered {
            return;
        }
        // FIXME: use callback
        self.encountered = true;
        // FIXME: Send message
//        self.updater.send(0)
    }
}
*/
/*
struct TelemetryCollector;
impl TelemetryCollector {
    fn register_set(&self, _: &Telemetry) {
        unreachable!() // TODO
    }

    // Spawn a thread to asynchronously collect data from all
    // instances of `Telemetry` (how do we deal with dead tasks? sync
    // channels?), merge it (how?), then export it as Json.
    fn export<F>(&self, cb: F) where F: FnOnce(Json) {
        unreachable!()
    }
}
*/
/*
pub struct KeyedFlag {
    map: HashMap<String, Flag>,
    shared: Shared,
    make: Box<Fn() -> Box<Flag>>
}

impl Flag {
    pub fn new_keyed<K>(telemetry: &mut Telemetry, meta: Metadata) -> KeyedFlag {
        unreachable!()
    }
}

impl Histogram<(String, ())> for KeyedFlag {
    fn record_cb<F>(&self, telemetry: &mut Telemetry, cb: F) where F: FnOnce() -> Option<(String, ())> {
        if !self.shared.should_record(telemetry) {
            return;
        }
        match cb() {
            None => {}
            Some((k, ())) => {
                telemetry.raw_record_keyed(&self.shared, k, 0, &self.make)
            }
        }
    }
}

use std::cell::RefCell;

static mut tele : Option<Telemetry> = None;
*/
<|endoftext|>


<commit_before><commit_msg>anup: derive Clone for PlayerArgs<commit_after><|endoftext|>


<commit_before>// Copyright 2013-2015, The Rust-GNOME Project Developers.
// See the COPYRIGHT file at the top-level directory of this distribution.
// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>

//! A widget that displays a small to medium amount of text

use glib::translate::ToGlibPtr;
use ffi;

/// Label — A widget that displays a small to medium amount of text
/*
* # Available signals:
* * `activate-current-link` : Action
* * `activate-link` : Run Last
* * `copy-clipboard` : Action
* * `move-cursor` : Action
* * `populate-popup` : Run Last
*/
struct_Widget!(Label);

impl Label {
    pub fn new(text: &str) -> Option<Label> {
        let tmp_pointer = unsafe {
            ffi::gtk_label_new(text.to_glib_none().0)
        };
        check_pointer!(tmp_pointer, Label)
    }

    pub fn new_with_mnemonic(text: &str) -> Option<Label> {
        let tmp_pointer = unsafe {
            ffi::gtk_label_new_with_mnemonic(text.to_glib_none().0)
        };
        check_pointer!(tmp_pointer, Label)
    }
}

impl_drop!(Label);
impl_TraitWidget!(Label);

impl ::MiscTrait for Label {}
impl ::LabelTrait for Label {}
<commit_msg>Remove struct Label<commit_after><|endoftext|>


<commit_before>//! `rust_swig` is a Rust Simplified Wrapper and Interface Generator used
//! to connect other programming languages to Rust.
//! It is designed to be used from
//! [cargo build scripts](https://doc.rust-lang.org/cargo/reference/build-scripts.html).
//! The idea of this softwared based on [swig](http://www.swig.org).
//! More details can be found at
//! [README](https://github.com/Dushistov/rust_swig/blob/master/README.md)

#![recursion_limit = "128"]

macro_rules! parse_type {
    ($($tt:tt)*) => {{
        let ty: Type = parse_quote! { $($tt)* };
        ty
    }}
}

mod code_parse;
mod cpp;
mod error;
pub mod file_cache;
mod java_jni;
mod source_registry;
mod typemap;
mod types;

use std::{
    cell::RefCell,
    env,
    io::Write,
    mem,
    path::{Path, PathBuf},
    str::FromStr,
};

use log::debug;
use proc_macro2::TokenStream;
use rustc_hash::FxHashSet;
use syn::spanned::Spanned;

use crate::{
    error::{panic_on_parse_error, DiagnosticError, Result},
    source_registry::{SourceId, SourceRegistry},
    typemap::{ast::DisplayToTokens, TypeMap},
    types::ItemToExpand,
};

/// Calculate target pointer width from environment variable
/// that `cargo` inserts
pub fn target_pointer_width_from_env() -> Option<usize> {
    env::var("CARGO_CFG_TARGET_POINTER_WIDTH")
        .ok()
        .map(|p_width| {
            <usize>::from_str(&p_width)
                .expect("Can not convert CARGO_CFG_TARGET_POINTER_WIDTH to usize")
        })
}

/// `LanguageConfig` contains configuration for specific programming language
pub enum LanguageConfig {
    JavaConfig(JavaConfig),
    CppConfig(CppConfig),
}

/// Configuration for Java binding generation
pub struct JavaConfig {
    output_dir: PathBuf,
    package_name: String,
    null_annotation_package: Option<String>,
    optional_package: String,
}

impl JavaConfig {
    /// Create `JavaConfig`
    /// # Arguments
    /// * `output_dir` - directory where place generated java files
    /// * `package_name` - package name for generated java files
    pub fn new(output_dir: PathBuf, package_name: String) -> JavaConfig {
        JavaConfig {
            output_dir,
            package_name,
            null_annotation_package: None,
            optional_package: "java.util".to_string(),
        }
    }
    /// Use @NonNull for types where appropriate
    /// # Arguments
    /// * `import_annotation` - import statement for @NonNull,
    ///                         for example android.support.annotation.NonNull
    #[deprecated(note = "Use use_null_annotation_from_package instead")]
    pub fn use_null_annotation(mut self, import_annotation: String) -> JavaConfig {
        let suffix = ".NonNull";
        if !import_annotation.ends_with(suffix) {
            panic!(
                "import_annotation({}) should ends with {}",
                import_annotation, suffix
            );
        }
        let package = &import_annotation[0..import_annotation.len() - suffix.len()];
        self.null_annotation_package = Some(package.into());
        self
    }
    /// Use @NonNull/@Nullable for types where appropriate
    /// # Arguments
    /// * `null_annotation_package` - from which package import annotations like NonNull,
    ///                         for example for Android Studio
    ///                         you should pass android.support.annotation
    pub fn use_null_annotation_from_package(
        mut self,
        null_annotation_package: String,
    ) -> JavaConfig {
        self.null_annotation_package = Some(null_annotation_package);
        self
    }
    /// If you use JDK without java.util.Optional*, then you can provide
    /// name of custom package with Optional. Default value is "java.util"
    pub fn use_optional_package(mut self, optional_package: String) -> JavaConfig {
        self.optional_package = optional_package;
        self
    }
}

/// Configuration for C++ binding generation
pub struct CppConfig {
    output_dir: PathBuf,
    namespace_name: String,
    cpp_optional: CppOptional,
    cpp_variant: CppVariant,
    generated_helper_files: RefCell<FxHashSet<PathBuf>>,
    to_generate: RefCell<Vec<TokenStream>>,
    /// Create separate *_impl.hpp files with methods implementations.
    /// Can be necessary for the project with circular dependencies between classes.
    separate_impl_headers: bool,
}

/// To which `C++` type map `std::option::Option`
pub enum CppOptional {
    /// `std::optional` from C++17 standard
    Std17,
    /// `boost::optional`
    Boost,
}

/// To which `C++` type map `std::result::Result`
pub enum CppVariant {
    /// `std::variant` from C++17 standard
    Std17,
    /// `boost::variant`
    Boost,
}

impl CppConfig {
    /// Create `CppConfig`
    /// # Arguments
    /// * `output_dir` - directory where place generated c++ files
    /// * `namespace_name` - namespace name for generated c++ classes
    pub fn new(output_dir: PathBuf, namespace_name: String) -> CppConfig {
        CppConfig {
            output_dir,
            namespace_name,
            cpp_optional: CppOptional::Std17,
            cpp_variant: CppVariant::Std17,
            generated_helper_files: RefCell::new(FxHashSet::default()),
            to_generate: RefCell::new(vec![]),
            separate_impl_headers: false,
        }
    }
    pub fn cpp_optional(self, cpp_optional: CppOptional) -> CppConfig {
        CppConfig {
            cpp_optional,
            ..self
        }
    }
    pub fn cpp_variant(self, cpp_variant: CppVariant) -> CppConfig {
        CppConfig {
            cpp_variant,
            ..self
        }
    }
    pub fn use_boost(self) -> CppConfig {
        CppConfig {
            cpp_variant: CppVariant::Boost,
            cpp_optional: CppOptional::Boost,
            ..self
        }
    }
    /// Create separate *_impl.hpp files with methods' implementations.
    /// Can be necessary for the project with circular dependencies between classes.
    pub fn separate_impl_headers(self, separate_impl_headers: bool) -> CppConfig {
        CppConfig {
            separate_impl_headers,
            ..self
        }
    }
}

/// `Generator` is a main point of `rust_swig`.
/// It expands rust macroses and generates not rust code.
/// It designed to use inside `build.rs`.
pub struct Generator {
    init_done: bool,
    config: LanguageConfig,
    conv_map: TypeMap,
    conv_map_source: Vec<SourceId>,
    foreign_lang_helpers: Vec<SourceCode>,
    pointer_target_width: usize,
    src_reg: SourceRegistry,
}

struct SourceCode {
    id_of_code: String,
    code: String,
}

static FOREIGNER_CLASS: &str = "foreigner_class";
static FOREIGN_ENUM: &str = "foreign_enum";
static FOREIGN_INTERFACE: &str = "foreign_interface";
static FOREIGNER_CODE: &str = "foreigner_code";
static FOREIGN_CODE: &str = "foreign_code";

impl Generator {
    pub fn new(config: LanguageConfig) -> Generator {
        let pointer_target_width = target_pointer_width_from_env();
        let mut conv_map_source = Vec::new();
        let mut foreign_lang_helpers = Vec::new();
        let mut src_reg = SourceRegistry::default();
        match config {
            LanguageConfig::JavaConfig(ref java_cfg) => {
                conv_map_source.push(
                    src_reg.register(SourceCode {
                        id_of_code: "jni-include.rs".into(),
                        code: include_str!("java_jni/jni-include.rs")
                            .replace(
                                "java.util.Optional",
                                &format!("{}.Optional", java_cfg.optional_package),
                            )
                            .replace(
                                "java/util/Optional",
                                &format!(
                                    "{}/Optional",
                                    java_cfg.optional_package.replace('.', "/")
                                ),
                            ),
                    }),
                );
            }
            LanguageConfig::CppConfig(..) => {
                conv_map_source.push(src_reg.register(SourceCode {
                    id_of_code: "cpp-include.rs".into(),
                    code: include_str!("cpp/cpp-include.rs").into(),
                }));
                foreign_lang_helpers.push(SourceCode {
                    id_of_code: "rust_vec.h".into(),
                    code: include_str!("cpp/rust_vec.h").into(),
                });
                foreign_lang_helpers.push(SourceCode {
                    id_of_code: "rust_result.h".into(),
                    code: include_str!("cpp/rust_result.h").into(),
                });
                foreign_lang_helpers.push(SourceCode {
                    id_of_code: "rust_option.h".into(),
                    code: include_str!("cpp/rust_option.h").into(),
                });
                foreign_lang_helpers.push(SourceCode {
                    id_of_code: "rust_tuple.h".into(),
                    code: include_str!("cpp/rust_tuple.h").into(),
                });
            }
        }
        Generator {
            init_done: false,
            config,
            conv_map: TypeMap::default(),
            conv_map_source,
            foreign_lang_helpers,
            pointer_target_width: pointer_target_width.unwrap_or(0),
            src_reg,
        }
    }

    /// By default we get pointer_target_width via cargo (more exactly CARGO_CFG_TARGET_POINTER_WIDTH),
    /// but you can change default value via this method
    pub fn with_pointer_target_width(mut self, pointer_target_width: usize) -> Generator {
        self.pointer_target_width = pointer_target_width;
        self
    }

    /// Add new foreign langauge type <-> Rust mapping
    pub fn merge_type_map(mut self, id_of_code: &str, code: &str) -> Generator {
        self.conv_map_source.push(self.src_reg.register(SourceCode {
            id_of_code: id_of_code.into(),
            code: code.into(),
        }));
        self
    }

    /// process `src` and save result of macro expansion to `dst`
    ///
    /// # Panics
    /// Panics on error
    pub fn expand<S, D>(mut self, crate_name: &str, src: S, dst: D)
    where
        S: AsRef<Path>,
        D: AsRef<Path>,
    {
        let src_cnt = std::fs::read_to_string(src.as_ref()).unwrap_or_else(|err| {
            panic!(
                "Error during read for file {}: {}",
                src.as_ref().display(),
                err
            )
        });

        let src_id = self.src_reg.register(SourceCode {
            id_of_code: format!("{}: {}", crate_name, src.as_ref().display()),
            code: src_cnt,
        });

        if let Err(err) = self.expand_str(src_id, dst) {
            panic_on_parse_error(&self.src_reg, &err);
        }
    }

    /// process `src` and save result of macro expansion to `dst`
    ///
    /// # Panics
    /// Panics on I/O errors
    fn expand_str<D>(&mut self, src_id: SourceId, dst: D) -> Result<()>
    where
        D: AsRef<Path>,
    {
        if self.pointer_target_width == 0 {
            panic!(
                r#"pointer target width unknown,
 set env CARGO_CFG_TARGET_POINTER_WIDTH environment variable,
 or use `with_pointer_target_width` function
"#
            );
        }
        let items = self.init_types_map(self.pointer_target_width)?;

        let syn_file = syn::parse_file(self.src_reg.src(src_id))
            .map_err(|err| DiagnosticError::from_syn_err(src_id, err))?;

        let mut file = file_cache::FileWriteCache::new(dst.as_ref());

        for item in items {
            write!(&mut file, "{}", DisplayToTokens(&item)).expect("mem I/O failed");
        }

        // n / 2 - just guess
        let mut items_to_expand = Vec::with_capacity(syn_file.items.len() / 2);

        for item in syn_file.items {
            if let syn::Item::Macro(mut item_macro) = item {
                let is_our_macro = [FOREIGNER_CLASS, FOREIGN_ENUM, FOREIGN_INTERFACE]
                    .iter()
                    .any(|x| item_macro.mac.path.is_ident(x));
                if !is_our_macro {
                    writeln!(&mut file, "{}", DisplayToTokens(&item_macro))
                        .expect("mem I/O failed");
                    continue;
                }
                debug!("Found {}", DisplayToTokens(&item_macro.mac.path));
                if item_macro.mac.tts.is_empty() {
                    return Err(DiagnosticError::new(
                        src_id,
                        item_macro.span(),
                        format!(
                            "missing tokens in call of macro '{}'",
                            DisplayToTokens(&item_macro.mac.path)
                        ),
                    ));
                }
                let mut tts = TokenStream::new();
                mem::swap(&mut tts, &mut item_macro.mac.tts);
                if item_macro.mac.path.is_ident(FOREIGNER_CLASS) {
                    let fclass = code_parse::parse_foreigner_class(src_id, &self.config, tts)?;
                    debug!(
                        "expand_foreigner_class: self {:?}, constructor {:?}",
                        fclass.self_type, fclass.constructor_ret_type
                    );
                    self.conv_map.register_foreigner_class(&fclass);
                    items_to_expand.push(ItemToExpand::Class(fclass));
                } else if item_macro.mac.path.is_ident(FOREIGN_ENUM) {
                    let fenum = code_parse::parse_foreign_enum(src_id, tts)?;
                    items_to_expand.push(ItemToExpand::Enum(fenum));
                } else if item_macro.mac.path.is_ident(FOREIGN_INTERFACE) {
                    let finterface = code_parse::parse_foreign_interface(src_id, tts)?;
                    items_to_expand.push(ItemToExpand::Interface(finterface));
                } else {
                    unreachable!();
                }
            } else {
                writeln!(&mut file, "{}", DisplayToTokens(&item)).expect("mem I/O failed");
            }
        }

        let code = Generator::language_generator(&self.config).expand_items(
            &mut self.conv_map,
            self.pointer_target_width,
            &self.foreign_lang_helpers,
            items_to_expand,
        )?;
        for elem in code {
            writeln!(&mut file, "{}", elem.to_string()).expect("mem I/O failed");
        }

        file.update_file_if_necessary().unwrap_or_else(|err| {
            panic!(
                "Error during write to file {}: {}",
                dst.as_ref().display(),
                err
            );
        });
        Ok(())
    }

    fn init_types_map(&mut self, target_pointer_width: usize) -> Result<Vec<syn::Item>> {
        if self.init_done {
            return Ok(vec![]);
        }
        self.init_done = true;
        for code_id in &self.conv_map_source {
            let code = self.src_reg.src(*code_id);
            self.conv_map.merge(*code_id, code, target_pointer_width)?;
        }

        if self.conv_map.is_empty() {
            return Err(DiagnosticError::new_without_src_info(
                "After merge all \"types maps\" have no convertion code",
            ));
        }

        Ok(self.conv_map.take_utils_code())
    }

    fn language_generator(cfg: &LanguageConfig) -> &LanguageGenerator {
        match cfg {
            LanguageConfig::JavaConfig(ref java_cfg) => java_cfg,
            LanguageConfig::CppConfig(ref cpp_cfg) => cpp_cfg,
        }
    }
}

trait LanguageGenerator {
    fn expand_items(
        &self,
        conv_map: &mut TypeMap,
        pointer_target_width: usize,
        code: &[SourceCode],
        items: Vec<ItemToExpand>,
    ) -> Result<Vec<TokenStream>>;
}
<commit_msg>doc: add description for CppConfig::use_boost<commit_after>//! `rust_swig` is a Rust Simplified Wrapper and Interface Generator used
//! to connect other programming languages to Rust.
//! It is designed to be used from
//! [cargo build scripts](https://doc.rust-lang.org/cargo/reference/build-scripts.html).
//! The idea of this softwared based on [swig](http://www.swig.org).
//! More details can be found at
//! [README](https://github.com/Dushistov/rust_swig/blob/master/README.md)

#![recursion_limit = "128"]

macro_rules! parse_type {
    ($($tt:tt)*) => {{
        let ty: Type = parse_quote! { $($tt)* };
        ty
    }}
}

mod code_parse;
mod cpp;
mod error;
pub mod file_cache;
mod java_jni;
mod source_registry;
mod typemap;
mod types;

use std::{
    cell::RefCell,
    env,
    io::Write,
    mem,
    path::{Path, PathBuf},
    str::FromStr,
};

use log::debug;
use proc_macro2::TokenStream;
use rustc_hash::FxHashSet;
use syn::spanned::Spanned;

use crate::{
    error::{panic_on_parse_error, DiagnosticError, Result},
    source_registry::{SourceId, SourceRegistry},
    typemap::{ast::DisplayToTokens, TypeMap},
    types::ItemToExpand,
};

/// Calculate target pointer width from environment variable
/// that `cargo` inserts
pub fn target_pointer_width_from_env() -> Option<usize> {
    env::var("CARGO_CFG_TARGET_POINTER_WIDTH")
        .ok()
        .map(|p_width| {
            <usize>::from_str(&p_width)
                .expect("Can not convert CARGO_CFG_TARGET_POINTER_WIDTH to usize")
        })
}

/// `LanguageConfig` contains configuration for specific programming language
pub enum LanguageConfig {
    JavaConfig(JavaConfig),
    CppConfig(CppConfig),
}

/// Configuration for Java binding generation
pub struct JavaConfig {
    output_dir: PathBuf,
    package_name: String,
    null_annotation_package: Option<String>,
    optional_package: String,
}

impl JavaConfig {
    /// Create `JavaConfig`
    /// # Arguments
    /// * `output_dir` - directory where place generated java files
    /// * `package_name` - package name for generated java files
    pub fn new(output_dir: PathBuf, package_name: String) -> JavaConfig {
        JavaConfig {
            output_dir,
            package_name,
            null_annotation_package: None,
            optional_package: "java.util".to_string(),
        }
    }
    /// Use @NonNull for types where appropriate
    /// # Arguments
    /// * `import_annotation` - import statement for @NonNull,
    ///                         for example android.support.annotation.NonNull
    #[deprecated(note = "Use use_null_annotation_from_package instead")]
    pub fn use_null_annotation(mut self, import_annotation: String) -> JavaConfig {
        let suffix = ".NonNull";
        if !import_annotation.ends_with(suffix) {
            panic!(
                "import_annotation({}) should ends with {}",
                import_annotation, suffix
            );
        }
        let package = &import_annotation[0..import_annotation.len() - suffix.len()];
        self.null_annotation_package = Some(package.into());
        self
    }
    /// Use @NonNull/@Nullable for types where appropriate
    /// # Arguments
    /// * `null_annotation_package` - from which package import annotations like NonNull,
    ///                         for example for Android Studio
    ///                         you should pass android.support.annotation
    pub fn use_null_annotation_from_package(
        mut self,
        null_annotation_package: String,
    ) -> JavaConfig {
        self.null_annotation_package = Some(null_annotation_package);
        self
    }
    /// If you use JDK without java.util.Optional*, then you can provide
    /// name of custom package with Optional. Default value is "java.util"
    pub fn use_optional_package(mut self, optional_package: String) -> JavaConfig {
        self.optional_package = optional_package;
        self
    }
}

/// Configuration for C++ binding generation
pub struct CppConfig {
    output_dir: PathBuf,
    namespace_name: String,
    cpp_optional: CppOptional,
    cpp_variant: CppVariant,
    generated_helper_files: RefCell<FxHashSet<PathBuf>>,
    to_generate: RefCell<Vec<TokenStream>>,
    /// Create separate *_impl.hpp files with methods implementations.
    /// Can be necessary for the project with circular dependencies between classes.
    separate_impl_headers: bool,
}

/// To which `C++` type map `std::option::Option`
pub enum CppOptional {
    /// `std::optional` from C++17 standard
    Std17,
    /// `boost::optional`
    Boost,
}

/// To which `C++` type map `std::result::Result`
pub enum CppVariant {
    /// `std::variant` from C++17 standard
    Std17,
    /// `boost::variant`
    Boost,
}

impl CppConfig {
    /// Create `CppConfig`
    /// # Arguments
    /// * `output_dir` - directory where place generated c++ files
    /// * `namespace_name` - namespace name for generated c++ classes
    pub fn new(output_dir: PathBuf, namespace_name: String) -> CppConfig {
        CppConfig {
            output_dir,
            namespace_name,
            cpp_optional: CppOptional::Std17,
            cpp_variant: CppVariant::Std17,
            generated_helper_files: RefCell::new(FxHashSet::default()),
            to_generate: RefCell::new(vec![]),
            separate_impl_headers: false,
        }
    }
    pub fn cpp_optional(self, cpp_optional: CppOptional) -> CppConfig {
        CppConfig {
            cpp_optional,
            ..self
        }
    }
    pub fn cpp_variant(self, cpp_variant: CppVariant) -> CppConfig {
        CppConfig {
            cpp_variant,
            ..self
        }
    }
    /// Use boost for that fit: Result -> boost::variant,
    /// Option -> boost::optional, etc
    pub fn use_boost(self) -> CppConfig {
        CppConfig {
            cpp_variant: CppVariant::Boost,
            cpp_optional: CppOptional::Boost,
            ..self
        }
    }
    /// Create separate *_impl.hpp files with methods' implementations.
    /// Can be necessary for the project with circular dependencies between classes.
    pub fn separate_impl_headers(self, separate_impl_headers: bool) -> CppConfig {
        CppConfig {
            separate_impl_headers,
            ..self
        }
    }
}

/// `Generator` is a main point of `rust_swig`.
/// It expands rust macroses and generates not rust code.
/// It designed to use inside `build.rs`.
pub struct Generator {
    init_done: bool,
    config: LanguageConfig,
    conv_map: TypeMap,
    conv_map_source: Vec<SourceId>,
    foreign_lang_helpers: Vec<SourceCode>,
    pointer_target_width: usize,
    src_reg: SourceRegistry,
}

struct SourceCode {
    id_of_code: String,
    code: String,
}

static FOREIGNER_CLASS: &str = "foreigner_class";
static FOREIGN_ENUM: &str = "foreign_enum";
static FOREIGN_INTERFACE: &str = "foreign_interface";
static FOREIGNER_CODE: &str = "foreigner_code";
static FOREIGN_CODE: &str = "foreign_code";

impl Generator {
    pub fn new(config: LanguageConfig) -> Generator {
        let pointer_target_width = target_pointer_width_from_env();
        let mut conv_map_source = Vec::new();
        let mut foreign_lang_helpers = Vec::new();
        let mut src_reg = SourceRegistry::default();
        match config {
            LanguageConfig::JavaConfig(ref java_cfg) => {
                conv_map_source.push(
                    src_reg.register(SourceCode {
                        id_of_code: "jni-include.rs".into(),
                        code: include_str!("java_jni/jni-include.rs")
                            .replace(
                                "java.util.Optional",
                                &format!("{}.Optional", java_cfg.optional_package),
                            )
                            .replace(
                                "java/util/Optional",
                                &format!(
                                    "{}/Optional",
                                    java_cfg.optional_package.replace('.', "/")
                                ),
                            ),
                    }),
                );
            }
            LanguageConfig::CppConfig(..) => {
                conv_map_source.push(src_reg.register(SourceCode {
                    id_of_code: "cpp-include.rs".into(),
                    code: include_str!("cpp/cpp-include.rs").into(),
                }));
                foreign_lang_helpers.push(SourceCode {
                    id_of_code: "rust_vec.h".into(),
                    code: include_str!("cpp/rust_vec.h").into(),
                });
                foreign_lang_helpers.push(SourceCode {
                    id_of_code: "rust_result.h".into(),
                    code: include_str!("cpp/rust_result.h").into(),
                });
                foreign_lang_helpers.push(SourceCode {
                    id_of_code: "rust_option.h".into(),
                    code: include_str!("cpp/rust_option.h").into(),
                });
                foreign_lang_helpers.push(SourceCode {
                    id_of_code: "rust_tuple.h".into(),
                    code: include_str!("cpp/rust_tuple.h").into(),
                });
            }
        }
        Generator {
            init_done: false,
            config,
            conv_map: TypeMap::default(),
            conv_map_source,
            foreign_lang_helpers,
            pointer_target_width: pointer_target_width.unwrap_or(0),
            src_reg,
        }
    }

    /// By default we get pointer_target_width via cargo (more exactly CARGO_CFG_TARGET_POINTER_WIDTH),
    /// but you can change default value via this method
    pub fn with_pointer_target_width(mut self, pointer_target_width: usize) -> Generator {
        self.pointer_target_width = pointer_target_width;
        self
    }

    /// Add new foreign langauge type <-> Rust mapping
    pub fn merge_type_map(mut self, id_of_code: &str, code: &str) -> Generator {
        self.conv_map_source.push(self.src_reg.register(SourceCode {
            id_of_code: id_of_code.into(),
            code: code.into(),
        }));
        self
    }

    /// process `src` and save result of macro expansion to `dst`
    ///
    /// # Panics
    /// Panics on error
    pub fn expand<S, D>(mut self, crate_name: &str, src: S, dst: D)
    where
        S: AsRef<Path>,
        D: AsRef<Path>,
    {
        let src_cnt = std::fs::read_to_string(src.as_ref()).unwrap_or_else(|err| {
            panic!(
                "Error during read for file {}: {}",
                src.as_ref().display(),
                err
            )
        });

        let src_id = self.src_reg.register(SourceCode {
            id_of_code: format!("{}: {}", crate_name, src.as_ref().display()),
            code: src_cnt,
        });

        if let Err(err) = self.expand_str(src_id, dst) {
            panic_on_parse_error(&self.src_reg, &err);
        }
    }

    /// process `src` and save result of macro expansion to `dst`
    ///
    /// # Panics
    /// Panics on I/O errors
    fn expand_str<D>(&mut self, src_id: SourceId, dst: D) -> Result<()>
    where
        D: AsRef<Path>,
    {
        if self.pointer_target_width == 0 {
            panic!(
                r#"pointer target width unknown,
 set env CARGO_CFG_TARGET_POINTER_WIDTH environment variable,
 or use `with_pointer_target_width` function
"#
            );
        }
        let items = self.init_types_map(self.pointer_target_width)?;

        let syn_file = syn::parse_file(self.src_reg.src(src_id))
            .map_err(|err| DiagnosticError::from_syn_err(src_id, err))?;

        let mut file = file_cache::FileWriteCache::new(dst.as_ref());

        for item in items {
            write!(&mut file, "{}", DisplayToTokens(&item)).expect("mem I/O failed");
        }

        // n / 2 - just guess
        let mut items_to_expand = Vec::with_capacity(syn_file.items.len() / 2);

        for item in syn_file.items {
            if let syn::Item::Macro(mut item_macro) = item {
                let is_our_macro = [FOREIGNER_CLASS, FOREIGN_ENUM, FOREIGN_INTERFACE]
                    .iter()
                    .any(|x| item_macro.mac.path.is_ident(x));
                if !is_our_macro {
                    writeln!(&mut file, "{}", DisplayToTokens(&item_macro))
                        .expect("mem I/O failed");
                    continue;
                }
                debug!("Found {}", DisplayToTokens(&item_macro.mac.path));
                if item_macro.mac.tts.is_empty() {
                    return Err(DiagnosticError::new(
                        src_id,
                        item_macro.span(),
                        format!(
                            "missing tokens in call of macro '{}'",
                            DisplayToTokens(&item_macro.mac.path)
                        ),
                    ));
                }
                let mut tts = TokenStream::new();
                mem::swap(&mut tts, &mut item_macro.mac.tts);
                if item_macro.mac.path.is_ident(FOREIGNER_CLASS) {
                    let fclass = code_parse::parse_foreigner_class(src_id, &self.config, tts)?;
                    debug!(
                        "expand_foreigner_class: self {:?}, constructor {:?}",
                        fclass.self_type, fclass.constructor_ret_type
                    );
                    self.conv_map.register_foreigner_class(&fclass);
                    items_to_expand.push(ItemToExpand::Class(fclass));
                } else if item_macro.mac.path.is_ident(FOREIGN_ENUM) {
                    let fenum = code_parse::parse_foreign_enum(src_id, tts)?;
                    items_to_expand.push(ItemToExpand::Enum(fenum));
                } else if item_macro.mac.path.is_ident(FOREIGN_INTERFACE) {
                    let finterface = code_parse::parse_foreign_interface(src_id, tts)?;
                    items_to_expand.push(ItemToExpand::Interface(finterface));
                } else {
                    unreachable!();
                }
            } else {
                writeln!(&mut file, "{}", DisplayToTokens(&item)).expect("mem I/O failed");
            }
        }

        let code = Generator::language_generator(&self.config).expand_items(
            &mut self.conv_map,
            self.pointer_target_width,
            &self.foreign_lang_helpers,
            items_to_expand,
        )?;
        for elem in code {
            writeln!(&mut file, "{}", elem.to_string()).expect("mem I/O failed");
        }

        file.update_file_if_necessary().unwrap_or_else(|err| {
            panic!(
                "Error during write to file {}: {}",
                dst.as_ref().display(),
                err
            );
        });
        Ok(())
    }

    fn init_types_map(&mut self, target_pointer_width: usize) -> Result<Vec<syn::Item>> {
        if self.init_done {
            return Ok(vec![]);
        }
        self.init_done = true;
        for code_id in &self.conv_map_source {
            let code = self.src_reg.src(*code_id);
            self.conv_map.merge(*code_id, code, target_pointer_width)?;
        }

        if self.conv_map.is_empty() {
            return Err(DiagnosticError::new_without_src_info(
                "After merge all \"types maps\" have no convertion code",
            ));
        }

        Ok(self.conv_map.take_utils_code())
    }

    fn language_generator(cfg: &LanguageConfig) -> &LanguageGenerator {
        match cfg {
            LanguageConfig::JavaConfig(ref java_cfg) => java_cfg,
            LanguageConfig::CppConfig(ref cpp_cfg) => cpp_cfg,
        }
    }
}

trait LanguageGenerator {
    fn expand_items(
        &self,
        conv_map: &mut TypeMap,
        pointer_target_width: usize,
        code: &[SourceCode],
        items: Vec<ItemToExpand>,
    ) -> Result<Vec<TokenStream>>;
}
<|endoftext|>


<commit_before>use std::time::Duration;
use std::iter::Iterator;
use std::u64::MAX as U64_MAX;

/// A retry strategy driven by exponential back-off.
///
/// The power corresponds to the number of past attempts.
#[derive(Debug, Clone)]
pub struct ExponentialBackoff {
    current: u64,
    base: u64
}

impl ExponentialBackoff {
    /// Constructs a new exponential back-off strategy,
    /// given a base duration in milliseconds.
    ///
    /// The resulting duration is calculated by taking the base to the `n`-th power,
    /// where `n` denotes the number of past attempts.
    pub fn from_millis(base: u64) -> ExponentialBackoff {
        ExponentialBackoff{current: base, base: base}
    }
}

impl Iterator for ExponentialBackoff {
    type Item = Duration;

    fn next(&mut self) -> Option<Duration> {
        let duration = Duration::from_millis(self.current);

        if let Some(next) = self.current.checked_mul(self.base) {
            self.current = next;
        } else {
            self.current = U64_MAX;
        }

        return Some(duration);
    }
}

#[test]
fn returns_some_exponential_base_10() {
    let mut s = ExponentialBackoff::from_millis(10);

    assert_eq!(s.next(), Some(Duration::from_millis(10)));
    assert_eq!(s.next(), Some(Duration::from_millis(100)));
    assert_eq!(s.next(), Some(Duration::from_millis(1000)));
}

#[test]
fn returns_some_exponential_base_2() {
    let mut s = ExponentialBackoff::from_millis(2);

    assert_eq!(s.next(), Some(Duration::from_millis(2)));
    assert_eq!(s.next(), Some(Duration::from_millis(4)));
    assert_eq!(s.next(), Some(Duration::from_millis(8)));
}

#[test]
fn saturates_at_maximum_value() {
    let mut s = ExponentialBackoff::from_millis(U64_MAX - 1);

    assert_eq!(s.next(), Some(Duration::from_millis(U64_MAX - 1)));
    assert_eq!(s.next(), Some(Duration::from_millis(U64_MAX)));
    assert_eq!(s.next(), Some(Duration::from_millis(U64_MAX)));
}<commit_msg>feat(exponential_backoff): add factor<commit_after>use std::time::Duration;
use std::iter::Iterator;
use std::u64::MAX as U64_MAX;

/// A retry strategy driven by exponential back-off.
///
/// The power corresponds to the number of past attempts.
#[derive(Debug, Clone)]
pub struct ExponentialBackoff {
    current: u64,
    base: u64,
    factor: u64,
}

impl ExponentialBackoff {
    /// Constructs a new exponential back-off strategy,
    /// given a base duration in milliseconds.
    ///
    /// The resulting duration is calculated by taking the base to the `n`-th power,
    /// where `n` denotes the number of past attempts.
    pub fn from_millis(base: u64) -> ExponentialBackoff {
        ExponentialBackoff {
            current: base,
            base: base,
            factor: 1u64,
        }
    }

    /// A multiplicative factor that will be applied to the retry delay.
    ///
    /// For example, using a factor of `1000` will make each delay in units of seconds.
    ///
    /// Default factor is `1`.
    pub fn factor(mut self, factor: u64) -> ExponentialBackoff {
        self.factor = factor;
        self
    }
}

impl Iterator for ExponentialBackoff {
    type Item = Duration;

    fn next(&mut self) -> Option<Duration> {
        // set delay duration by applying factor
        let duration = if let Some(duration) = self.current.checked_mul(self.factor) {
            Duration::from_millis(duration)
        } else {
            Duration::from_millis(U64_MAX)
        };

        if let Some(next) = self.current.checked_mul(self.base) {
            self.current = next;
        } else {
            self.current = U64_MAX;
        }

        Some(duration)
    }
}

#[test]
fn returns_some_exponential_base_10() {
    let mut s = ExponentialBackoff::from_millis(10);

    assert_eq!(s.next(), Some(Duration::from_millis(10)));
    assert_eq!(s.next(), Some(Duration::from_millis(100)));
    assert_eq!(s.next(), Some(Duration::from_millis(1000)));
}

#[test]
fn returns_some_exponential_base_2() {
    let mut s = ExponentialBackoff::from_millis(2);

    assert_eq!(s.next(), Some(Duration::from_millis(2)));
    assert_eq!(s.next(), Some(Duration::from_millis(4)));
    assert_eq!(s.next(), Some(Duration::from_millis(8)));
}

#[test]
fn saturates_at_maximum_value() {
    let mut s = ExponentialBackoff::from_millis(U64_MAX - 1);

    assert_eq!(s.next(), Some(Duration::from_millis(U64_MAX - 1)));
    assert_eq!(s.next(), Some(Duration::from_millis(U64_MAX)));
    assert_eq!(s.next(), Some(Duration::from_millis(U64_MAX)));
}

#[test]
fn can_use_factor_to_get_seconds() {
    let factor = 1000;
    let mut s = ExponentialBackoff::from_millis(2).factor(factor);

    assert_eq!(s.next(), Some(Duration::from_secs(2)));
    assert_eq!(s.next(), Some(Duration::from_secs(4)));
    assert_eq!(s.next(), Some(Duration::from_secs(8)));
}
<|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 cssparser::Parser;
use media_queries::CSSErrorReporterTest;
use servo_url::ServoUrl;
use style::parser::ParserContext;
use style::stylesheets::Origin;
use style_traits::ToCss;
use style::properties::longhands;

#[test]
fn test_clip() {
    use style::properties::longhands::clip;

    assert_roundtrip_with_context!(clip::parse, "auto");
    assert_roundtrip_with_context!(clip::parse, "rect(1px, 2px, 3px, 4px)");
    assert_roundtrip_with_context!(clip::parse, "rect(1px, auto, auto, 4px)");
    assert_roundtrip_with_context!(clip::parse, "rect(auto, auto, auto, auto)");

    // Non-standard syntax
    assert_roundtrip_with_context!(clip::parse,
                                   "rect(1px 2px 3px 4px)",
                                   "rect(1px, 2px, 3px, 4px)");
    assert_roundtrip_with_context!(clip::parse,
                                   "rect(auto 2px 3px auto)",
                                   "rect(auto, 2px, 3px, auto)");
    assert_roundtrip_with_context!(clip::parse,
                                   "rect(1px auto auto 4px)",
                                   "rect(1px, auto, auto, 4px)");
    assert_roundtrip_with_context!(clip::parse,
                                   "rect(auto auto auto auto)",
                                   "rect(auto, auto, auto, auto)");
}

#[test]
fn test_longhands_parse_origin() {
    let url = ServoUrl::parse("http://localhost").unwrap();
    let context = ParserContext::new(Origin::Author, &url, Box::new(CSSErrorReporterTest));

    let mut parser = Parser::new("1px 2px rubbish");
    let parsed = longhands::parse_origin(&context, &mut parser);
    assert_eq!(parsed.is_ok(), true);
    assert_eq!(parser.is_exhausted(), false);

    let mut parser = Parser::new("1px 2px");
    let parsed = longhands::parse_origin(&context, &mut parser);
    assert_eq!(parsed.is_ok(), true);
    assert_eq!(parser.is_exhausted(), true);

    let mut parser = Parser::new("1px");
    let parsed = longhands::parse_origin(&context, &mut parser);
    assert_eq!(parsed.is_ok(), true);
    assert_eq!(parser.is_exhausted(), true);
}
<commit_msg>add test test_effects_parser_exhaustion<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 cssparser::Parser;
use media_queries::CSSErrorReporterTest;
use servo_url::ServoUrl;
use style::parser::ParserContext;
use style::stylesheets::Origin;
use style_traits::ToCss;
use style::properties::longhands;
use style::properties::longhands::{perspective_origin, transform_origin};

#[test]
fn test_clip() {
    use style::properties::longhands::clip;

    assert_roundtrip_with_context!(clip::parse, "auto");
    assert_roundtrip_with_context!(clip::parse, "rect(1px, 2px, 3px, 4px)");
    assert_roundtrip_with_context!(clip::parse, "rect(1px, auto, auto, 4px)");
    assert_roundtrip_with_context!(clip::parse, "rect(auto, auto, auto, auto)");

    // Non-standard syntax
    assert_roundtrip_with_context!(clip::parse,
                                   "rect(1px 2px 3px 4px)",
                                   "rect(1px, 2px, 3px, 4px)");
    assert_roundtrip_with_context!(clip::parse,
                                   "rect(auto 2px 3px auto)",
                                   "rect(auto, 2px, 3px, auto)");
    assert_roundtrip_with_context!(clip::parse,
                                   "rect(1px auto auto 4px)",
                                   "rect(1px, auto, auto, 4px)");
    assert_roundtrip_with_context!(clip::parse,
                                   "rect(auto auto auto auto)",
                                   "rect(auto, auto, auto, auto)");
}

#[test]
fn test_longhands_parse_origin() {
    let url = ServoUrl::parse("http://localhost").unwrap();
    let context = ParserContext::new(Origin::Author, &url, Box::new(CSSErrorReporterTest));

    let mut parser = Parser::new("1px some-rubbish");
    let parsed = longhands::parse_origin(&context, &mut parser);
    assert!(parsed.is_ok());
    assert_eq!(parser.is_exhausted(), false);

    let mut parser = Parser::new("1px 2px");
    let parsed = longhands::parse_origin(&context, &mut parser);
    assert!(parsed.is_ok());
    assert_eq!(parser.is_exhausted(), true);

    let mut parser = Parser::new("1px");
    let parsed = longhands::parse_origin(&context, &mut parser);
    assert!(parsed.is_ok());
    assert_eq!(parser.is_exhausted(), true);
}

#[test]
fn test_effects_parser_exhaustion() {
    assert_parser_exhausted!(perspective_origin, "1px 1px", true);
    assert_parser_exhausted!(transform_origin, "1px 1px", true);

    assert_parser_exhausted!(perspective_origin, "1px some-rubbish", false);
    assert_parser_exhausted!(transform_origin, "1px some-rubbish", false);
}
<|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/. */

/*!
A GPU based renderer for the web.

It serves as an experimental render backend for [Servo](https://servo.org/),
but it can also be used as such in a standalone application.

# External dependencies
WebRender currently depends on [FreeType](https://www.freetype.org/)

# Api Structure
The main entry point to WebRender is the [`crate::Renderer`].

By calling [`Renderer::new(...)`](crate::Renderer::new) you get a [`Renderer`], as well as
a [`RenderApiSender`](api::RenderApiSender). Your [`Renderer`] is responsible to render the
previously processed frames onto the screen.

By calling [`yourRenderApiSender.create_api()`](api::RenderApiSender::create_api), you'll
get a [`RenderApi`](api::RenderApi) instance, which is responsible for managing resources
and documents. A worker thread is used internally to untie the workload from the application
thread and therefore be able to make better use of multicore systems.

## Frame

What is referred to as a `frame`, is the current geometry on the screen.
A new Frame is created by calling [`set_display_list()`](api::Transaction::set_display_list)
on the [`RenderApi`](api::RenderApi). When the geometry is processed, the application will be
informed via a [`RenderNotifier`](api::RenderNotifier), a callback which you pass to
[`Renderer::new`].
More information about [stacking contexts][stacking_contexts].

[`set_display_list()`](api::Transaction::set_display_list) also needs to be supplied with
[`BuiltDisplayList`](api::BuiltDisplayList)s. These are obtained by finalizing a
[`DisplayListBuilder`](api::DisplayListBuilder). These are used to draw your geometry. But it
doesn't only contain trivial geometry, it can also store another
[`StackingContext`](api::StackingContext), as they're nestable.

[stacking_contexts]: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Positioning/Understanding_z_index/The_stacking_context
*/

// Cribbed from the |matches| crate, for simplicity.
macro_rules! matches {
    ($expression:expr, $($pattern:tt)+) => {
        match $expression {
            $($pattern)+ => true,
            _ => false
        }
    }
}

#[macro_use]
extern crate bitflags;
#[macro_use]
extern crate cfg_if;
#[macro_use]
extern crate cstr;
#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate log;
#[macro_use]
extern crate malloc_size_of_derive;
#[cfg(any(feature = "serde"))]
#[macro_use]
extern crate serde;
#[macro_use]
extern crate thread_profiler;

extern crate malloc_size_of;
extern crate svg_fmt;

#[macro_use]
mod profiler;

mod batch;
mod border;
mod box_shadow;
#[cfg(any(feature = "capture", feature = "replay"))]
mod capture;
mod clip;
mod clip_scroll_tree;
mod composite;
mod debug_colors;
mod debug_font_data;
mod debug_render;
#[cfg(feature = "debugger")]
mod debug_server;
mod device;
mod ellipse;
mod filterdata;
mod frame_builder;
mod freelist;
#[cfg(any(target_os = "macos", target_os = "windows"))]
mod gamma_lut;
mod glyph_cache;
mod glyph_rasterizer;
mod gpu_cache;
mod gpu_types;
mod hit_test;
mod image;
mod intern;
mod internal_types;
mod picture;
mod prim_store;
mod print_tree;
mod record;
mod render_backend;
mod render_target;
mod render_task_graph;
mod render_task_cache;
mod render_task;
mod renderer;
mod resource_cache;
mod scene;
mod scene_builder_thread;
mod scene_building;
mod screen_capture;
mod segment;
mod shade;
mod spatial_node;
mod storage;
mod texture_allocator;
mod texture_cache;
mod util;

mod shader_source {
    include!(concat!(env!("OUT_DIR"), "/shaders.rs"));
}

pub use crate::record::{ApiRecordingReceiver, BinaryRecorder, WEBRENDER_RECORDING_HEADER};

mod platform {
    #[cfg(target_os = "macos")]
    pub use crate::platform::macos::font;
    #[cfg(any(target_os = "android", all(unix, not(target_os = "macos"))))]
    pub use crate::platform::unix::font;
    #[cfg(target_os = "windows")]
    pub use crate::platform::windows::font;

    #[cfg(target_os = "macos")]
    pub mod macos {
        pub mod font;
    }
    #[cfg(any(target_os = "android", all(unix, not(target_os = "macos"))))]
    pub mod unix {
        pub mod font;
    }
    #[cfg(target_os = "windows")]
    pub mod windows {
        pub mod font;
    }
}

#[cfg(target_os = "macos")]
extern crate core_foundation;
#[cfg(target_os = "macos")]
extern crate core_graphics;
#[cfg(target_os = "macos")]
extern crate core_text;

#[cfg(all(unix, not(target_os = "macos")))]
extern crate freetype;
#[cfg(all(unix, not(target_os = "macos")))]
extern crate libc;

#[cfg(target_os = "windows")]
extern crate dwrote;

extern crate bincode;
extern crate byteorder;
pub extern crate euclid;
extern crate fxhash;
extern crate gleam;
extern crate num_traits;
extern crate plane_split;
extern crate rayon;
#[cfg(feature = "ron")]
extern crate ron;
#[cfg(feature = "debugger")]
extern crate serde_json;
extern crate sha2;
#[macro_use]
extern crate smallvec;
extern crate time;
#[cfg(feature = "debugger")]
extern crate ws;
#[cfg(feature = "debugger")]
extern crate image_loader;
#[cfg(feature = "debugger")]
extern crate base64;
#[cfg(all(feature = "capture", feature = "png"))]
extern crate png;
#[cfg(test)]
extern crate rand;

#[macro_use]
pub extern crate api;
extern crate webrender_build;

#[doc(hidden)]
pub use crate::composite::{CompositorConfig, Compositor, NativeSurfaceId, NativeSurfaceInfo};
pub use crate::device::{build_shader_strings, UploadMethod, VertexUsageHint, get_gl_target};
pub use crate::device::{ProgramBinary, ProgramCache, ProgramCacheObserver, FormatDesc};
pub use crate::device::Device;
pub use crate::frame_builder::ChasePrimitive;
pub use crate::prim_store::PrimitiveDebugId;
pub use crate::profiler::{ProfilerHooks, set_profiler_hooks};
pub use crate::renderer::{
    AsyncPropertySampler, CpuProfile, DebugFlags, OutputImageHandler, RendererKind, ExternalImage,
    ExternalImageHandler, ExternalImageSource, GpuProfile, GraphicsApi, GraphicsApiInfo,
    PipelineInfo, Renderer, RendererOptions, RenderResults, RendererStats, SceneBuilderHooks,
    ThreadListener, ShaderPrecacheFlags, MAX_VERTEX_TEXTURE_WIDTH,
};
pub use crate::screen_capture::{AsyncScreenshotHandle, RecordedFrameHandle};
pub use crate::shade::{Shaders, WrShaders};
pub use api as webrender_api;
pub use webrender_build::shader::ProgramSourceDigest;
<commit_msg>Bug 1571972 - Re-export RendererError as a public type r=jrmuizel<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/. */

/*!
A GPU based renderer for the web.

It serves as an experimental render backend for [Servo](https://servo.org/),
but it can also be used as such in a standalone application.

# External dependencies
WebRender currently depends on [FreeType](https://www.freetype.org/)

# Api Structure
The main entry point to WebRender is the [`crate::Renderer`].

By calling [`Renderer::new(...)`](crate::Renderer::new) you get a [`Renderer`], as well as
a [`RenderApiSender`](api::RenderApiSender). Your [`Renderer`] is responsible to render the
previously processed frames onto the screen.

By calling [`yourRenderApiSender.create_api()`](api::RenderApiSender::create_api), you'll
get a [`RenderApi`](api::RenderApi) instance, which is responsible for managing resources
and documents. A worker thread is used internally to untie the workload from the application
thread and therefore be able to make better use of multicore systems.

## Frame

What is referred to as a `frame`, is the current geometry on the screen.
A new Frame is created by calling [`set_display_list()`](api::Transaction::set_display_list)
on the [`RenderApi`](api::RenderApi). When the geometry is processed, the application will be
informed via a [`RenderNotifier`](api::RenderNotifier), a callback which you pass to
[`Renderer::new`].
More information about [stacking contexts][stacking_contexts].

[`set_display_list()`](api::Transaction::set_display_list) also needs to be supplied with
[`BuiltDisplayList`](api::BuiltDisplayList)s. These are obtained by finalizing a
[`DisplayListBuilder`](api::DisplayListBuilder). These are used to draw your geometry. But it
doesn't only contain trivial geometry, it can also store another
[`StackingContext`](api::StackingContext), as they're nestable.

[stacking_contexts]: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Positioning/Understanding_z_index/The_stacking_context
*/

// Cribbed from the |matches| crate, for simplicity.
macro_rules! matches {
    ($expression:expr, $($pattern:tt)+) => {
        match $expression {
            $($pattern)+ => true,
            _ => false
        }
    }
}

#[macro_use]
extern crate bitflags;
#[macro_use]
extern crate cfg_if;
#[macro_use]
extern crate cstr;
#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate log;
#[macro_use]
extern crate malloc_size_of_derive;
#[cfg(any(feature = "serde"))]
#[macro_use]
extern crate serde;
#[macro_use]
extern crate thread_profiler;

extern crate malloc_size_of;
extern crate svg_fmt;

#[macro_use]
mod profiler;

mod batch;
mod border;
mod box_shadow;
#[cfg(any(feature = "capture", feature = "replay"))]
mod capture;
mod clip;
mod clip_scroll_tree;
mod composite;
mod debug_colors;
mod debug_font_data;
mod debug_render;
#[cfg(feature = "debugger")]
mod debug_server;
mod device;
mod ellipse;
mod filterdata;
mod frame_builder;
mod freelist;
#[cfg(any(target_os = "macos", target_os = "windows"))]
mod gamma_lut;
mod glyph_cache;
mod glyph_rasterizer;
mod gpu_cache;
mod gpu_types;
mod hit_test;
mod image;
mod intern;
mod internal_types;
mod picture;
mod prim_store;
mod print_tree;
mod record;
mod render_backend;
mod render_target;
mod render_task_graph;
mod render_task_cache;
mod render_task;
mod renderer;
mod resource_cache;
mod scene;
mod scene_builder_thread;
mod scene_building;
mod screen_capture;
mod segment;
mod shade;
mod spatial_node;
mod storage;
mod texture_allocator;
mod texture_cache;
mod util;

mod shader_source {
    include!(concat!(env!("OUT_DIR"), "/shaders.rs"));
}

pub use crate::record::{ApiRecordingReceiver, BinaryRecorder, WEBRENDER_RECORDING_HEADER};

mod platform {
    #[cfg(target_os = "macos")]
    pub use crate::platform::macos::font;
    #[cfg(any(target_os = "android", all(unix, not(target_os = "macos"))))]
    pub use crate::platform::unix::font;
    #[cfg(target_os = "windows")]
    pub use crate::platform::windows::font;

    #[cfg(target_os = "macos")]
    pub mod macos {
        pub mod font;
    }
    #[cfg(any(target_os = "android", all(unix, not(target_os = "macos"))))]
    pub mod unix {
        pub mod font;
    }
    #[cfg(target_os = "windows")]
    pub mod windows {
        pub mod font;
    }
}

#[cfg(target_os = "macos")]
extern crate core_foundation;
#[cfg(target_os = "macos")]
extern crate core_graphics;
#[cfg(target_os = "macos")]
extern crate core_text;

#[cfg(all(unix, not(target_os = "macos")))]
extern crate freetype;
#[cfg(all(unix, not(target_os = "macos")))]
extern crate libc;

#[cfg(target_os = "windows")]
extern crate dwrote;

extern crate bincode;
extern crate byteorder;
pub extern crate euclid;
extern crate fxhash;
extern crate gleam;
extern crate num_traits;
extern crate plane_split;
extern crate rayon;
#[cfg(feature = "ron")]
extern crate ron;
#[cfg(feature = "debugger")]
extern crate serde_json;
extern crate sha2;
#[macro_use]
extern crate smallvec;
extern crate time;
#[cfg(feature = "debugger")]
extern crate ws;
#[cfg(feature = "debugger")]
extern crate image_loader;
#[cfg(feature = "debugger")]
extern crate base64;
#[cfg(all(feature = "capture", feature = "png"))]
extern crate png;
#[cfg(test)]
extern crate rand;

#[macro_use]
pub extern crate api;
extern crate webrender_build;

#[doc(hidden)]
pub use crate::composite::{CompositorConfig, Compositor, NativeSurfaceId, NativeSurfaceInfo};
pub use crate::device::{build_shader_strings, UploadMethod, VertexUsageHint, get_gl_target};
pub use crate::device::{ProgramBinary, ProgramCache, ProgramCacheObserver, FormatDesc};
pub use crate::device::Device;
pub use crate::frame_builder::ChasePrimitive;
pub use crate::prim_store::PrimitiveDebugId;
pub use crate::profiler::{ProfilerHooks, set_profiler_hooks};
pub use crate::renderer::{
    AsyncPropertySampler, CpuProfile, DebugFlags, OutputImageHandler, RendererKind, ExternalImage,
    ExternalImageHandler, ExternalImageSource, GpuProfile, GraphicsApi, GraphicsApiInfo,
    PipelineInfo, Renderer, RendererError, RendererOptions, RenderResults, RendererStats, SceneBuilderHooks,
    ThreadListener, ShaderPrecacheFlags, MAX_VERTEX_TEXTURE_WIDTH,
};
pub use crate::screen_capture::{AsyncScreenshotHandle, RecordedFrameHandle};
pub use crate::shade::{Shaders, WrShaders};
pub use api as webrender_api;
pub use webrender_build::shader::ProgramSourceDigest;
<|endoftext|>