<commit_before>use crate::storage::mvcc::{
    GcInfo, MvccTxn, Result as MvccResult, GC_DELETE_VERSIONS_HISTOGRAM, MAX_TXN_WRITE_SIZE,
    MVCC_VERSIONS_HISTOGRAM,
};
use crate::storage::Snapshot;
use txn_types::{Key, TimeStamp, WriteType};

pub fn gc<S: Snapshot>(
    txn: &mut MvccTxn<S>,
    key: Key,
    safe_point: TimeStamp,
) -> MvccResult<GcInfo> {
    let mut remove_older = false;
    let mut ts = TimeStamp::max();
    let mut found_versions = 0;
    let mut deleted_versions = 0;
    let mut latest_delete = None;
    let mut is_completed = true;
    while let Some((commit, write)) = txn.reader.seek_write(&key, ts)? {
        ts = commit.prev();
        found_versions += 1;

        if txn.write_size >= MAX_TXN_WRITE_SIZE {
            // Cannot remove latest delete when we haven't iterate all versions.
            latest_delete = None;
            is_completed = false;
            break;
        }

        if remove_older {
            txn.delete_write(key.clone(), commit);
            if write.write_type == WriteType::Put && write.short_value.is_none() {
                txn.delete_value(key.clone(), write.start_ts);
            }
            deleted_versions += 1;
            continue;
        }

        if commit > safe_point {
            continue;
        }

        // Set `remove_older` after we find the latest value.
        match write.write_type {
            WriteType::Put | WriteType::Delete => {
                remove_older = true;
            }
            WriteType::Rollback | WriteType::Lock => {}
        }

        // Latest write before `safe_point` can be deleted if its type is Delete,
        // Rollback or Lock.
        match write.write_type {
            WriteType::Delete => {
                latest_delete = Some(commit);
            }
            WriteType::Rollback | WriteType::Lock => {
                txn.delete_write(key.clone(), commit);
                deleted_versions += 1;
            }
            WriteType::Put => {}
        }
    }
    if let Some(commit) = latest_delete {
        txn.delete_write(key, commit);
        deleted_versions += 1;
    }
    MVCC_VERSIONS_HISTOGRAM.observe(found_versions as f64);
    if deleted_versions > 0 {
        GC_DELETE_VERSIONS_HISTOGRAM.observe(deleted_versions as f64);
    }
    Ok(GcInfo {
        found_versions,
        deleted_versions,
        is_completed,
    })
}

pub mod tests {
    use super::*;
    use crate::storage::kv::SnapContext;
    use crate::storage::mvcc::tests::write;
    use crate::storage::{Engine, ScanMode};
    use concurrency_manager::ConcurrencyManager;
    use kvproto::kvrpcpb::Context;

    #[cfg(test)]
    use crate::storage::{
        mvcc::tests::{must_get, must_get_none},
        txn::tests::*,
        RocksEngine, TestEngineBuilder,
    };
    #[cfg(test)]
    use txn_types::SHORT_VALUE_MAX_LEN;

    pub fn must_succeed<E: Engine>(engine: &E, key: &[u8], safe_point: impl Into<TimeStamp>) {
        let ctx = SnapContext::default();
        let snapshot = engine.snapshot(ctx).unwrap();
        let cm = ConcurrencyManager::new(1.into());
        let mut txn = MvccTxn::for_scan(
            snapshot,
            Some(ScanMode::Forward),
            TimeStamp::zero(),
            true,
            cm,
        );
        gc(&mut txn, Key::from_raw(key), safe_point.into()).unwrap();
        write(engine, &Context::default(), txn.into_modifies());
    }

    #[cfg(test)]
    fn test_gc_imp<F>(k: &[u8], v1: &[u8], v2: &[u8], v3: &[u8], v4: &[u8], gc: F)
    where
        F: Fn(&RocksEngine, &[u8], u64),
    {
        let engine = TestEngineBuilder::new().build().unwrap();

        must_prewrite_put(&engine, k, v1, k, 5);
        must_commit(&engine, k, 5, 10);
        must_prewrite_put(&engine, k, v2, k, 15);
        must_commit(&engine, k, 15, 20);
        must_prewrite_delete(&engine, k, k, 25);
        must_commit(&engine, k, 25, 30);
        must_prewrite_put(&engine, k, v3, k, 35);
        must_commit(&engine, k, 35, 40);
        must_prewrite_lock(&engine, k, k, 45);
        must_commit(&engine, k, 45, 50);
        must_prewrite_put(&engine, k, v4, k, 55);
        must_rollback(&engine, k, 55);

        // Transactions:
        // startTS commitTS Command
        // --
        // 55      -        PUT "x55" (Rollback)
        // 45      50       LOCK
        // 35      40       PUT "x35"
        // 25      30       DELETE
        // 15      20       PUT "x15"
        //  5      10       PUT "x5"

        // CF data layout:
        // ts CFDefault   CFWrite
        // --
        // 55             Rollback(PUT,50)
        // 50             Commit(LOCK,45)
        // 45
        // 40             Commit(PUT,35)
        // 35   x35
        // 30             Commit(Delete,25)
        // 25
        // 20             Commit(PUT,15)
        // 15   x15
        // 10             Commit(PUT,5)
        // 5    x5

        gc(&engine, k, 12);
        must_get(&engine, k, 12, v1);

        gc(&engine, k, 22);
        must_get(&engine, k, 22, v2);
        must_get_none(&engine, k, 12);

        gc(&engine, k, 32);
        must_get_none(&engine, k, 22);
        must_get_none(&engine, k, 35);

        gc(&engine, k, 60);
        must_get(&engine, k, 62, v3);
    }

    #[test]
    fn test_gc() {
        test_gc_imp(b"k1", b"v1", b"v2", b"v3", b"v4", must_succeed);

        let v1 = "x".repeat(SHORT_VALUE_MAX_LEN + 1).into_bytes();
        let v2 = "y".repeat(SHORT_VALUE_MAX_LEN + 1).into_bytes();
        let v3 = "z".repeat(SHORT_VALUE_MAX_LEN + 1).into_bytes();
        let v4 = "v".repeat(SHORT_VALUE_MAX_LEN + 1).into_bytes();
        test_gc_imp(b"k2", &v1, &v2, &v3, &v4, must_succeed);
    }

    #[test]
    fn test_gc_with_compaction_filter() {
        use crate::server::gc_worker::gc_by_compact;

        test_gc_imp(b"zk1", b"v1", b"v2", b"v3", b"v4", gc_by_compact);

        let v1 = "x".repeat(SHORT_VALUE_MAX_LEN + 1).into_bytes();
        let v2 = "y".repeat(SHORT_VALUE_MAX_LEN + 1).into_bytes();
        let v3 = "z".repeat(SHORT_VALUE_MAX_LEN + 1).into_bytes();
        let v4 = "v".repeat(SHORT_VALUE_MAX_LEN + 1).into_bytes();
        test_gc_imp(b"zk2", &v1, &v2, &v3, &v4, gc_by_compact);
    }
}
<commit_msg>txn: refactor GC (#9168)<commit_after>use crate::storage::mvcc::{
    GcInfo, MvccTxn, Result as MvccResult, GC_DELETE_VERSIONS_HISTOGRAM, MAX_TXN_WRITE_SIZE,
    MVCC_VERSIONS_HISTOGRAM,
};
use crate::storage::Snapshot;
use txn_types::{Key, TimeStamp, Write, WriteType};

pub fn gc(txn: &mut MvccTxn<impl Snapshot>, key: Key, safe_point: TimeStamp) -> MvccResult<GcInfo> {
    let gc = Gc::new(txn, key);
    let info = gc.run(safe_point)?;
    info.report_metrics();

    Ok(info)
}

/// Iterates over the versions of `key`, see the `run` method.
struct Gc<'a, S: Snapshot> {
    key: Key,
    cur_ts: TimeStamp,
    info: GcInfo,
    txn: &'a mut MvccTxn<S>,
}

impl<'a, S: Snapshot> Gc<'a, S> {
    fn new(txn: &'a mut MvccTxn<S>, key: Key) -> Gc<'a, S> {
        Gc {
            key,
            cur_ts: TimeStamp::max(),
            info: GcInfo {
                found_versions: 0,
                deleted_versions: 0,
                is_completed: false,
            },
            txn,
        }
    }

    fn delete_write(&mut self, write: Write, ts: TimeStamp) {
        self.txn.delete_write(self.key.clone(), ts);
        if write.write_type == WriteType::Put && write.short_value.is_none() {
            self.txn.delete_value(self.key.clone(), write.start_ts);
        }
        self.info.deleted_versions += 1;
    }

    fn next_write(&mut self) -> MvccResult<Option<(TimeStamp, Write)>> {
        let result = self.txn.reader.seek_write(&self.key, self.cur_ts)?;
        if let Some((commit, _)) = result {
            self.cur_ts = commit.prev();
            self.info.found_versions += 1;
        }
        Ok(result)
    }

    fn run(mut self, safe_point: TimeStamp) -> MvccResult<GcInfo> {
        let mut state = State::Rewind(safe_point);

        while let Some((commit, write)) = self.next_write()? {
            if self.txn.write_size >= MAX_TXN_WRITE_SIZE {
                return Ok(self.info);
            }

            state.step(&mut self, write, commit);
        }

        if let State::RemoveAll(Some((commit, write))) = state {
            self.delete_write(write, commit);
        }

        self.info.is_completed = true;
        Ok(self.info)
    }
}

enum State {
    // Rewind to TimeStamp.
    Rewind(TimeStamp),
    // Remove locks and rollbacks until we get to a put or delete.
    RemoveIdempotent,
    // Parameter is the latest delete which can be removed if we complete removal of
    // everything else.
    RemoveAll(Option<(TimeStamp, Write)>),
}

impl State {
    /// Process a single version of a key/value.
    fn step(&mut self, gc: &mut Gc<'_, impl Snapshot>, write: Write, commit_ts: TimeStamp) {
        match self {
            State::Rewind(safe_point) => {
                if commit_ts <= *safe_point {
                    *self = State::RemoveIdempotent;
                    self.step(gc, write, commit_ts);
                }
            }
            State::RemoveIdempotent => match write.write_type {
                WriteType::Put => {
                    *self = State::RemoveAll(None);
                }
                WriteType::Delete => {
                    *self = State::RemoveAll(Some((commit_ts, write)));
                }
                WriteType::Rollback | WriteType::Lock => {
                    gc.delete_write(write, commit_ts);
                }
            },
            State::RemoveAll(_) => {
                gc.delete_write(write, commit_ts);
            }
        }
    }
}

impl GcInfo {
    fn report_metrics(&self) {
        MVCC_VERSIONS_HISTOGRAM.observe(self.found_versions as f64);
        if self.deleted_versions > 0 {
            GC_DELETE_VERSIONS_HISTOGRAM.observe(self.deleted_versions as f64);
        }
    }
}

pub mod tests {
    use super::*;
    use crate::storage::kv::SnapContext;
    use crate::storage::mvcc::tests::write;
    use crate::storage::{Engine, ScanMode};
    use concurrency_manager::ConcurrencyManager;
    use kvproto::kvrpcpb::Context;

    #[cfg(test)]
    use crate::storage::{
        mvcc::tests::{must_get, must_get_none},
        txn::tests::*,
        RocksEngine, TestEngineBuilder,
    };
    #[cfg(test)]
    use txn_types::SHORT_VALUE_MAX_LEN;

    pub fn must_succeed<E: Engine>(engine: &E, key: &[u8], safe_point: impl Into<TimeStamp>) {
        let ctx = SnapContext::default();
        let snapshot = engine.snapshot(ctx).unwrap();
        let cm = ConcurrencyManager::new(1.into());
        let mut txn = MvccTxn::for_scan(
            snapshot,
            Some(ScanMode::Forward),
            TimeStamp::zero(),
            true,
            cm,
        );
        gc(&mut txn, Key::from_raw(key), safe_point.into()).unwrap();
        write(engine, &Context::default(), txn.into_modifies());
    }

    #[cfg(test)]
    fn test_gc_imp<F>(k: &[u8], v1: &[u8], v2: &[u8], v3: &[u8], v4: &[u8], gc: F)
    where
        F: Fn(&RocksEngine, &[u8], u64),
    {
        let engine = TestEngineBuilder::new().build().unwrap();

        must_prewrite_put(&engine, k, v1, k, 5);
        must_commit(&engine, k, 5, 10);
        must_prewrite_put(&engine, k, v2, k, 15);
        must_commit(&engine, k, 15, 20);
        must_prewrite_delete(&engine, k, k, 25);
        must_commit(&engine, k, 25, 30);
        must_prewrite_put(&engine, k, v3, k, 35);
        must_commit(&engine, k, 35, 40);
        must_prewrite_lock(&engine, k, k, 45);
        must_commit(&engine, k, 45, 50);
        must_prewrite_put(&engine, k, v4, k, 55);
        must_rollback(&engine, k, 55);

        // Transactions:
        // startTS commitTS Command
        // --
        // 55      -        PUT "x55" (Rollback)
        // 45      50       LOCK
        // 35      40       PUT "x35"
        // 25      30       DELETE
        // 15      20       PUT "x15"
        //  5      10       PUT "x5"

        // CF data layout:
        // ts CFDefault   CFWrite
        // --
        // 55             Rollback(PUT,50)
        // 50             Commit(LOCK,45)
        // 45
        // 40             Commit(PUT,35)
        // 35   x35
        // 30             Commit(Delete,25)
        // 25
        // 20             Commit(PUT,15)
        // 15   x15
        // 10             Commit(PUT,5)
        // 5    x5

        gc(&engine, k, 12);
        must_get(&engine, k, 12, v1);

        gc(&engine, k, 22);
        must_get(&engine, k, 22, v2);
        must_get_none(&engine, k, 12);

        gc(&engine, k, 32);
        must_get_none(&engine, k, 22);
        must_get_none(&engine, k, 35);

        gc(&engine, k, 60);
        must_get(&engine, k, 62, v3);
    }

    #[test]
    fn test_gc() {
        test_gc_imp(b"k1", b"v1", b"v2", b"v3", b"v4", must_succeed);

        let v1 = "x".repeat(SHORT_VALUE_MAX_LEN + 1).into_bytes();
        let v2 = "y".repeat(SHORT_VALUE_MAX_LEN + 1).into_bytes();
        let v3 = "z".repeat(SHORT_VALUE_MAX_LEN + 1).into_bytes();
        let v4 = "v".repeat(SHORT_VALUE_MAX_LEN + 1).into_bytes();
        test_gc_imp(b"k2", &v1, &v2, &v3, &v4, must_succeed);
    }

    #[test]
    fn test_gc_with_compaction_filter() {
        use crate::server::gc_worker::gc_by_compact;

        test_gc_imp(b"zk1", b"v1", b"v2", b"v3", b"v4", gc_by_compact);

        let v1 = "x".repeat(SHORT_VALUE_MAX_LEN + 1).into_bytes();
        let v2 = "y".repeat(SHORT_VALUE_MAX_LEN + 1).into_bytes();
        let v3 = "z".repeat(SHORT_VALUE_MAX_LEN + 1).into_bytes();
        let v4 = "v".repeat(SHORT_VALUE_MAX_LEN + 1).into_bytes();
        test_gc_imp(b"zk2", &v1, &v2, &v3, &v4, gc_by_compact);
    }
}
<|endoftext|>


<commit_before>// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// PrometheusReporter aggregates metrics into metricsfamilies and passes them every second to the
// attached prometheus reporter at a regular basis.

extern crate prometheus_reporter;
extern crate protobuf;
use self::prometheus_reporter::PrometheusReporter as Pr;
use self::prometheus_reporter::promo_proto;

use std::time::Duration;
use std::thread;
use metrics::Metric;
use time;
use std::collections::HashMap;
use std::sync::mpsc;
use self::protobuf::repeated::RepeatedField;

struct PrometheusMetricEntry {
    name: &'static str,
    metric: Metric,
    labels: HashMap<String, String>,
}

// TODO perhaps we autodiscover the host and port
//
pub struct PrometheusReporter {
    reporter_name: &'static str,
    tx: Option<mpsc::Sender<PrometheusMetricEntry>>,
}

impl PrometheusReporter {
    pub fn new(reporter_name: &'static str) -> Self {
        PrometheusReporter {
            reporter_name: reporter_name,
            tx: None,
        }
    }

    pub fn add(&mut self,
               name: &'static str,
               metric: Metric,
               labels: HashMap<String, String>)
               -> Result<(), String> {
        // TODO return error
        match self.tx {
            Some(ref mut tx) => {
                match tx.send(PrometheusMetricEntry {
                    name: name,
                    metric: metric,
                    labels: labels,
                }) {
                    Ok(x) => Ok(x),
                    Err(y) => Err(format!("Unable to send {}", y)),
                }
            }
            None => Err(format!("Please start the reporter before trying to add to it")),
        }
    }

    pub fn start(&mut self, delay_ms: u64) {
        let (tx, rx) = mpsc::channel();
        self.tx = Some(tx);
        thread::spawn(move || {
            // TODO programmable port
            let mut prometheus_reporter = Pr::new("0.0.0.0:80");
            prometheus_reporter.start().unwrap();
            loop {
                prometheus_reporter.add(collect_to_send(&rx));
                thread::sleep(Duration::from_millis(delay_ms));
            }
        });
    }
}

fn to_repeated_fields_labels(labels: HashMap<String, String>)
                             -> RepeatedField<promo_proto::LabelPair> {
    let mut repeated_fields = Vec::new();
    // name/value is what they call it in the protobufs *shrug*
    for (name, value) in labels {
        let mut label_pair = promo_proto::LabelPair::new();
        label_pair.set_name(name);
        label_pair.set_value(value);
        repeated_fields.push(label_pair);
    }
    RepeatedField::from_vec(repeated_fields)
}

fn make_metric(metric: &Metric,
               labels: &HashMap<String, String>)
               -> (promo_proto::Metric, promo_proto::MetricType) {

    let mut pb_metric = promo_proto::Metric::new();
    let ts = time::now().to_timespec().sec;

    pb_metric.set_timestamp_ms(ts);
    pb_metric.set_label(to_repeated_fields_labels(labels.clone()));
    match *metric {
        Metric::Counter(ref x) => {
            let snapshot = x.snapshot();
            let mut counter = promo_proto::Counter::new();
            counter.set_value(snapshot.value as f64);
            pb_metric.set_counter(counter);
            (pb_metric, promo_proto::MetricType::COUNTER)
        }
        Metric::Gauge(ref x) => {
            let snapshot = x.snapshot();
            let mut gauge = promo_proto::Gauge::new();
            gauge.set_value(snapshot.value as f64);
            pb_metric.set_gauge(gauge);
            (pb_metric, promo_proto::MetricType::GAUGE)
        }
        Metric::Meter(_) => {
            // TODO ask the Prometheus guys what we want to do
            pb_metric.set_summary(promo_proto::Summary::new());
            (pb_metric, promo_proto::MetricType::SUMMARY)

        }
        Metric::Histogram(_) => {
            pb_metric.set_histogram(promo_proto::Histogram::new());
            (pb_metric, promo_proto::MetricType::HISTOGRAM)
        }
    }
}

fn collect_to_send(metric_entries: &mpsc::Receiver<PrometheusMetricEntry>)
                   -> Vec<promo_proto::MetricFamily> {
    let mut entries_group = HashMap::<&'static str, Vec<PrometheusMetricEntry>>::new();

    // Group them by name TODO we shold include tags and types in the grouping
    for entry in metric_entries {
        let name = entry.name;
        let mut entries = entries_group.remove(name).unwrap_or(vec![]);
        entries.push(entry);
        entries_group.insert(name, entries);
    }

    let mut families = Vec::new();
    for (name, metric_entries) in &entries_group {
        let formatted_metric = format!("{}_{}_{}", "application_name", name, "bytes");
        // TODO check for 0 length

        let ref e1: PrometheusMetricEntry = metric_entries[0];
        let (_, pb_metric_type) = make_metric(&e1.metric, &e1.labels);

        let mut family = promo_proto::MetricFamily::new();
        let mut pb_metrics = Vec::new();

        for metric_entry in metric_entries {
            // TODO maybe don't assume they have the same type
            let (pb_metric, _) = make_metric(&metric_entry.metric, &metric_entry.labels);
            pb_metrics.push(pb_metric);
        }

        family.set_name(String::from(formatted_metric));
        family.set_field_type(pb_metric_type);
        family.set_metric(RepeatedField::from_vec(pb_metrics));
        families.push(family);
    }
    families
}

#[cfg(test)]
mod test {
    use histogram::Histogram;
    use std::collections::HashMap;
    use metrics::{Counter, Gauge, Meter, Metric, StdCounter, StdGauge, StdMeter};
    use super::PrometheusReporter;
    use std::thread;

    #[test]
    fn meter() {
        let m = StdMeter::new();
        m.mark(100);

        let c = StdCounter::new();
        c.inc();

        let g = StdGauge::new();
        g.set(2);

        let mut h = Histogram::configure()
            .max_value(100)
            .precision(1)
            .build()
            .unwrap();

        h.increment_by(1, 1).unwrap();

        let mut reporter = PrometheusReporter::new("test");
        reporter.start(1024);
        let labels = HashMap::new();
        reporter.add("meter1", Metric::Meter(m.clone()), labels.clone());
        reporter.add("counter1", Metric::Counter(c.clone()), labels.clone());
        reporter.add("gauge1", Metric::Gauge(g.clone()), labels.clone());
        reporter.add("histogram", Metric::Histogram(h), labels);
    }
}
<commit_msg>Make port configurable<commit_after>// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// PrometheusReporter aggregates metrics into metricsfamilies and passes them every second to the
// attached prometheus reporter at a regular basis.

extern crate prometheus_reporter;
extern crate protobuf;
use self::prometheus_reporter::PrometheusReporter as Pr;
use self::prometheus_reporter::promo_proto;

use std::time::Duration;
use std::thread;
use metrics::Metric;
use time;
use std::collections::HashMap;
use std::sync::mpsc;
use self::protobuf::repeated::RepeatedField;

struct PrometheusMetricEntry {
    name: &'static str,
    metric: Metric,
    labels: HashMap<String, String>,
}

// TODO perhaps we autodiscover the host and port
//
pub struct PrometheusReporter {
    reporter_name: &'static str,
    host_and_port: &'static str,
    tx: Option<mpsc::Sender<PrometheusMetricEntry>>,
}

impl PrometheusReporter {
    pub fn new(reporter_name: &'static str, host_and_port: &'static str) -> Self {
        PrometheusReporter {
            reporter_name: reporter_name,
            host_and_port: host_and_port,
            tx: None,
        }
    }

    pub fn add(&mut self,
               name: &'static str,
               metric: Metric,
               labels: HashMap<String, String>)
               -> Result<(), String> {
        // TODO return error
        match self.tx {
            Some(ref mut tx) => {
                match tx.send(PrometheusMetricEntry {
                    name: name,
                    metric: metric,
                    labels: labels,
                }) {
                    Ok(x) => Ok(x),
                    Err(y) => Err(format!("Unable to send {}", y)),
                }
            }
            None => Err(format!("Please start the reporter before trying to add to it")),
        }
    }

    pub fn start(&mut self, delay_ms: u64) {
        let (tx, rx) = mpsc::channel();
        self.tx = Some(tx);
        let host_and_port = self.host_and_port.clone();
        thread::spawn(move || {
            let mut prometheus_reporter = Pr::new(host_and_port);
            prometheus_reporter.start().unwrap();
            loop {
                prometheus_reporter.add(collect_to_send(&rx));
                thread::sleep(Duration::from_millis(delay_ms));
            }
        });
    }
}

fn to_repeated_fields_labels(labels: HashMap<String, String>)
                             -> RepeatedField<promo_proto::LabelPair> {
    let mut repeated_fields = Vec::new();
    // name/value is what they call it in the protobufs *shrug*
    for (name, value) in labels {
        let mut label_pair = promo_proto::LabelPair::new();
        label_pair.set_name(name);
        label_pair.set_value(value);
        repeated_fields.push(label_pair);
    }
    RepeatedField::from_vec(repeated_fields)
}

fn make_metric(metric: &Metric,
               labels: &HashMap<String, String>)
               -> (promo_proto::Metric, promo_proto::MetricType) {

    let mut pb_metric = promo_proto::Metric::new();
    let ts = time::now().to_timespec().sec;

    pb_metric.set_timestamp_ms(ts);
    pb_metric.set_label(to_repeated_fields_labels(labels.clone()));
    match *metric {
        Metric::Counter(ref x) => {
            let snapshot = x.snapshot();
            let mut counter = promo_proto::Counter::new();
            counter.set_value(snapshot.value as f64);
            pb_metric.set_counter(counter);
            (pb_metric, promo_proto::MetricType::COUNTER)
        }
        Metric::Gauge(ref x) => {
            let snapshot = x.snapshot();
            let mut gauge = promo_proto::Gauge::new();
            gauge.set_value(snapshot.value as f64);
            pb_metric.set_gauge(gauge);
            (pb_metric, promo_proto::MetricType::GAUGE)
        }
        Metric::Meter(_) => {
            pb_metric.set_summary(promo_proto::Summary::new());
            (pb_metric, promo_proto::MetricType::SUMMARY)

        }
        Metric::Histogram(_) => {
            pb_metric.set_histogram(promo_proto::Histogram::new());
            (pb_metric, promo_proto::MetricType::HISTOGRAM)
        }
    }
}

fn collect_to_send(metric_entries: &mpsc::Receiver<PrometheusMetricEntry>)
                   -> Vec<promo_proto::MetricFamily> {
    let mut entries_group = HashMap::<&'static str, Vec<PrometheusMetricEntry>>::new();

    // Group them by name TODO we shold include tags and types in the grouping
    for entry in metric_entries {
        let name = entry.name;
        let mut entries = entries_group.remove(name).unwrap_or(vec![]);
        entries.push(entry);
        entries_group.insert(name, entries);
    }

    let mut families = Vec::new();
    for (name, metric_entries) in &entries_group {
        let formatted_metric = format!("{}_{}_{}", "application_name", name, "bytes");
        // TODO check for 0 length

        let ref e1: PrometheusMetricEntry = metric_entries[0];
        let (_, pb_metric_type) = make_metric(&e1.metric, &e1.labels);

        let mut family = promo_proto::MetricFamily::new();
        let mut pb_metrics = Vec::new();

        for metric_entry in metric_entries {
            // TODO maybe don't assume they have the same type
            let (pb_metric, _) = make_metric(&metric_entry.metric, &metric_entry.labels);
            pb_metrics.push(pb_metric);
        }

        family.set_name(String::from(formatted_metric));
        family.set_field_type(pb_metric_type);
        family.set_metric(RepeatedField::from_vec(pb_metrics));
        families.push(family);
    }
    families
}

#[cfg(test)]
mod test {
    use histogram::Histogram;
    use std::collections::HashMap;
    use metrics::{Counter, Gauge, Meter, Metric, StdCounter, StdGauge, StdMeter};
    use super::PrometheusReporter;

    #[test]
    fn meter() {
        let m = StdMeter::new();
        m.mark(100);

        let c = StdCounter::new();
        c.inc();

        let g = StdGauge::new();
        g.set(2);

        let mut h = Histogram::configure()
            .max_value(100)
            .precision(1)
            .build()
            .unwrap();

        h.increment_by(1, 1).unwrap();

        let mut reporter = PrometheusReporter::new("test", "0.0.0.0:80");
        reporter.start(1024);
        let labels = HashMap::new();
        reporter.add("meter1", Metric::Meter(m.clone()), labels.clone());
        reporter.add("counter1", Metric::Counter(c.clone()), labels.clone());
        reporter.add("gauge1", Metric::Gauge(g.clone()), labels.clone());
        reporter.add("histogram", Metric::Histogram(h), labels);
    }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::collections::hash_map;
use tensor::*;
use autograd::variable::Variable;
use nn::Parameter;

// placeholder
struct TorchBackend {}

pub trait ModuleStruct {
    fn init_module(&mut self);
}

pub struct Module<T> {
    pub _name: String,
    pub training: bool,
    _backend: TorchBackend,

    _buffers: HashMap<String, Tensor<T>>,
    //	_backward_hooks:
    //	_forward_hooks:
    _params: HashMap<String, *mut Parameter<T>>,
    _modules: HashMap<String, *mut Module<T>>,
}
pub struct PtrIterMut<'a, T: 'a> {
    //mod_iter: linked_hash_map::IterMut<'a, &'a str, *mut T>,
    mod_iter: hash_map::IterMut<'a, String, *mut T>,
}
pub struct PtrIter<'a, T: 'a> {
    //mod_iter: linked_hash_map::IterMut<'a, &'a str, *mut T>,
    mod_iter: hash_map::Iter<'a, String, *mut T>,
}

impl<'a, T> Iterator for PtrIterMut<'a, T> {
    type Item = (&'a String, &'a mut T);
    fn next(&mut self) -> Option<Self::Item> {
        if let Some((name, t)) = self.mod_iter.next() {
            Some((name, unsafe { &mut **t as &mut T }))
        } else {
            None
        }
    }
}

impl<'a, T> Iterator for PtrIter<'a, T> {
    type Item = (&'a str, &'a T);
    fn next(&mut self) -> Option<Self::Item> {
        if let Some((name, t)) = self.mod_iter.next() {
            Some((name, unsafe { &**t as &T }))
        } else {
            None
        }
    }
}

impl<T> Module<T> {
    pub fn new() -> Module<T> {
        Module {
            _name: String::from(""),
            _backend: TorchBackend {},
            _buffers: HashMap::new(),
            _params: HashMap::new(),
            _modules: HashMap::new(),
            training: true,
        }
    }
    #[inline]
    fn as_mut_ptr(&mut self) -> *mut Module<T> {
        self as *mut Module<T>
    }
    pub fn add_module(&mut self, module: &mut ModIntf<T>) {
        let m = module.delegate();
        self._modules.insert(m._name.clone(), m.as_mut_ptr());

    }
    pub fn add_param(&mut self, name: &str, param: &mut Parameter<T>) {
        let s = String::from(name);
        self._params.insert(s, param.as_mut_ptr());

    }
    pub fn modules_iter_mut(&mut self) -> PtrIterMut<Module<T>> {
        PtrIterMut { mod_iter: self._modules.iter_mut() }
    }
    pub fn modules_iter(&mut self) -> PtrIter<Module<T>> {
        PtrIter { mod_iter: self._modules.iter() }
    }
    pub fn params_iter_mut(&mut self) -> PtrIterMut<Parameter<T>> {
        PtrIterMut { mod_iter: self._params.iter_mut() }
    }
    pub fn register_buffer(&mut self, name: &str, tensor: &mut Tensor<T>) {
        self._buffers.insert(String::from(name), tensor.clone());
    }
    fn _apply(&mut self, callback: fn(&mut Tensor<T>)) {
        for (_, module) in self.modules_iter_mut() {
            module._apply(callback)
        }
        for (_, param) in self.params_iter_mut() {
            param.apply(callback)
            /*
			if let Some(g) = p._grad {
					callback(g.data)
			}
			*/
            /* see also _buffers */
        }
    }
    fn apply(&mut self, callback: fn(&mut Self)) {
        for (_, module) in self.modules_iter_mut() {
            module.apply(callback)
        }
        callback(self)
    }
    pub fn repr(&mut self) -> String {
        let mut tmpstr = format!("{} (\n", self._name);
        for (key, module) in self.modules_iter_mut() {
            let modstr = module.repr();
            let modstr = format!("  {}", modstr);
            tmpstr = format!("{} ({}): {}\n", tmpstr, key, modstr);
        }
        tmpstr = format!("{})", tmpstr);
        tmpstr
    }
    pub fn eval(&mut self) {
        self.train(false)
    }
    fn train(&mut self, mode: bool) {
        self.training = mode;
        for (_, module) in self.modules_iter_mut() {
            module.train(mode)
        }

    }
}

pub trait ModIntf<T> {
    fn delegate(&mut self) -> &mut Module<T>;
    fn forward(&mut self, input: &mut Variable<T>) -> Variable<T>;
    fn forwardv(&mut self, input: &mut Vec<Variable<T>>) -> Vec<Variable<T>>;
}
<commit_msg>add f/fv stubs<commit_after>use std::collections::HashMap;
use std::collections::hash_map;
use tensor::*;
use autograd::variable::Variable;
use nn::Parameter;

// placeholder
struct TorchBackend {}

pub trait ModuleStruct {
    fn init_module(&mut self);
}

pub struct Module<T> {
    pub _name: String,
    pub training: bool,
    _backend: TorchBackend,

    _buffers: HashMap<String, Tensor<T>>,
    //	_backward_hooks:
    //	_forward_hooks:
    _params: HashMap<String, *mut Parameter<T>>,
    _modules: HashMap<String, *mut Module<T>>,
}
pub struct PtrIterMut<'a, T: 'a> {
    //mod_iter: linked_hash_map::IterMut<'a, &'a str, *mut T>,
    mod_iter: hash_map::IterMut<'a, String, *mut T>,
}
pub struct PtrIter<'a, T: 'a> {
    //mod_iter: linked_hash_map::IterMut<'a, &'a str, *mut T>,
    mod_iter: hash_map::Iter<'a, String, *mut T>,
}

impl<'a, T> Iterator for PtrIterMut<'a, T> {
    type Item = (&'a String, &'a mut T);
    fn next(&mut self) -> Option<Self::Item> {
        if let Some((name, t)) = self.mod_iter.next() {
            Some((name, unsafe { &mut **t as &mut T }))
        } else {
            None
        }
    }
}

impl<'a, T> Iterator for PtrIter<'a, T> {
    type Item = (&'a str, &'a T);
    fn next(&mut self) -> Option<Self::Item> {
        if let Some((name, t)) = self.mod_iter.next() {
            Some((name, unsafe { &**t as &T }))
        } else {
            None
        }
    }
}

impl<T> Module<T> {
    pub fn new() -> Module<T> {
        Module {
            _name: String::from(""),
            _backend: TorchBackend {},
            _buffers: HashMap::new(),
            _params: HashMap::new(),
            _modules: HashMap::new(),
            training: true,
        }
    }
    #[inline]
    fn as_mut_ptr(&mut self) -> *mut Module<T> {
        self as *mut Module<T>
    }
    pub fn add_module(&mut self, module: &mut ModIntf<T>) {
        let m = module.delegate();
        self._modules.insert(m._name.clone(), m.as_mut_ptr());

    }
    pub fn add_param(&mut self, name: &str, param: &mut Parameter<T>) {
        let s = String::from(name);
        self._params.insert(s, param.as_mut_ptr());

    }
    pub fn modules_iter_mut(&mut self) -> PtrIterMut<Module<T>> {
        PtrIterMut { mod_iter: self._modules.iter_mut() }
    }
    pub fn modules_iter(&mut self) -> PtrIter<Module<T>> {
        PtrIter { mod_iter: self._modules.iter() }
    }
    pub fn params_iter_mut(&mut self) -> PtrIterMut<Parameter<T>> {
        PtrIterMut { mod_iter: self._params.iter_mut() }
    }
    pub fn register_buffer(&mut self, name: &str, tensor: &mut Tensor<T>) {
        self._buffers.insert(String::from(name), tensor.clone());
    }
    fn _apply(&mut self, callback: fn(&mut Tensor<T>)) {
        for (_, module) in self.modules_iter_mut() {
            module._apply(callback)
        }
        for (_, param) in self.params_iter_mut() {
            param.apply(callback)
            /*
			if let Some(g) = p._grad {
					callback(g.data)
			}
			*/
            /* see also _buffers */
        }
    }
    fn apply(&mut self, callback: fn(&mut Self)) {
        for (_, module) in self.modules_iter_mut() {
            module.apply(callback)
        }
        callback(self)
    }
    pub fn repr(&mut self) -> String {
        let mut tmpstr = format!("{} (\n", self._name);
        for (key, module) in self.modules_iter_mut() {
            let modstr = module.repr();
            let modstr = format!("  {}", modstr);
            tmpstr = format!("{} ({}): {}\n", tmpstr, key, modstr);
        }
        tmpstr = format!("{})", tmpstr);
        tmpstr
    }
    pub fn eval(&mut self) {
        self.train(false)
    }
    fn train(&mut self, mode: bool) {
        self.training = mode;
        for (_, module) in self.modules_iter_mut() {
            module.train(mode)
        }

    }
}

pub trait ModIntf<T> {
    fn delegate(&mut self) -> &mut Module<T>;
    fn forward(&mut self, input: &mut Variable<T>) -> Variable<T>;
    fn forwardv(&mut self, input: &mut Vec<Variable<T>>) -> Vec<Variable<T>>;
    fn f(&mut self, input: &mut Variable<T>) -> Variable<T> {
        {
            let mut m = self.delegate();
            // do pre-forward hooks
        }
        let output = self.forward(input);
        {
            let mut m = self.delegate();
            // do post-forward hooks
        }
        output
    }
    fn fv(&mut self, input: &mut Vec<Variable<T>>) -> Vec<Variable<T>> {
        {
            let mut m = self.delegate();
            // do pre-forward hooks
        }
        let output = self.forwardv(input);
        {
            let mut m = self.delegate();
            // do post-forward hooks
        }
        output
    }
}
<|endoftext|>


<commit_before>use au = gfx::geometry;
use geom::point::Point2D;
use geom::size::Size2D;
use gfx::geometry::au;
use libc::{c_void};
use font_cache::FontCache;
use font::Font;
use glyph::Glyph;
use shaper::shape_text;

/// A single, unbroken line of text
struct TextRun {
    text: ~str,
    priv glyphs: ~[Glyph],
    priv size_: Size2D<au>,
    priv min_break_width_: au,
}

impl TextRun {
    /// The size of the entire TextRun
    pure fn size() -> Size2D<au> { self.size_ }
    pure fn min_break_width() -> au { self.min_break_width_ }

    /// Split a run of text in two
    // FIXME: Should be storing a reference to the Font inside
    // of the TextRun, but I'm hitting cycle collector bugs
    fn split(font: &Font, h_offset: au) -> (TextRun, TextRun) {
        assert h_offset >= self.min_break_width();
        assert h_offset <= self.size_.width;

        let mut curr_run = ~"";

        for iter_indivisible_slices(font, self.text) |slice| {
            let mut candidate = copy curr_run;

            if candidate.is_not_empty() {
                str::push_str(candidate, " "); // FIXME: just inserting spaces between words can't be right
            }

            str::push_str(candidate, slice);

            let glyphs = shape_text(font, candidate);
            let size = glyph_run_size(glyphs);
            if size.width <= h_offset {
                curr_run = move candidate;
            } else {
                break;
            }
        }

        assert curr_run.is_not_empty();

        let first = move curr_run;
        let second_start = match str::find_from(self.text, first.len(), |c| !char::is_whitespace(c)) {
            Some(idx) => idx,
            None => {
                // This will be an empty string
                self.text.len()
            }
        };
        let second = str::slice(self.text, second_start, self.text.len());
        return (TextRun(font, first), TextRun(font, second));
    }
}

fn TextRun(font: &Font, +text: ~str) -> TextRun {
    let glyphs = shape_text(font, text);
    let size = glyph_run_size(glyphs);
    let min_break_width = calc_min_break_width(font, text);

    TextRun {
        text: text,
        glyphs: shape_text(font, text),
        size_: size,
        min_break_width_: min_break_width
    }
}

fn glyph_run_size(glyphs: &[Glyph]) -> Size2D<au> {
    let height = au::from_px(20);
    let pen_start_x = au::from_px(0);
    let pen_start_y = height;
    let pen_start = Point2D(pen_start_x, pen_start_y);
    let pen_end = glyphs.foldl(pen_start, |cur, glyph| {
        Point2D(cur.x.add(glyph.pos.offset.x).add(glyph.pos.advance.x),
                cur.y.add(glyph.pos.offset.y).add(glyph.pos.advance.y))
    });
    return Size2D(pen_end.x, pen_end.y);
}

/// Discovers the width of the largest indivisible substring
fn calc_min_break_width(font: &Font, text: &str) -> au {
    let mut max_piece_width = au(0);
    for iter_indivisible_slices(font, text) |slice| {
        let glyphs = shape_text(font, slice);
        let size = glyph_run_size(glyphs);
        if size.width > max_piece_width {
            max_piece_width = size.width
        }
    }
    return max_piece_width;
}

/// Iterates over all the indivisible substrings
fn iter_indivisible_slices(_font: &Font, text: &r/str,
                           f: fn((&r/str)) -> bool) {

    let mut curr = text;
    loop {
        match str::find(curr, |c| !char::is_whitespace(c) ) {
          Some(idx) => {
            curr = str::view(curr, idx, curr.len());
          }
          None => {
            // Everything else is whitespace
            break
          }
        }

        match str::find(curr, |c| char::is_whitespace(c) ) {
          Some(idx) => {
            let piece = str::view(curr, 0, idx);
            if !f(piece) { break }
            curr = str::view(curr, idx, curr.len());
          }
          None => {
            assert curr.is_not_empty();
            if !f(curr) { break }
            // This is the end of the string
            break;
          }
        }
    }
}

#[test]
fn test_calc_min_break_width1() {
    let flib = FontCache();
    let font = flib.get_test_font();
    let actual = calc_min_break_width(font, ~"firecracker");
    let expected = au::from_px(84);
    assert expected == actual;
}

#[test]
fn test_calc_min_break_width2() {
    let flib = FontCache();
    let font = flib.get_test_font();
    let actual = calc_min_break_width(font, ~"firecracker yumyum");
    let expected = au::from_px(84);
    assert expected == actual;
}

#[test]
fn test_calc_min_break_width3() {
    let flib = FontCache();
    let font = flib.get_test_font();
    let actual = calc_min_break_width(font, ~"yumyum firecracker");
    let expected = au::from_px(84);
    assert expected == actual;
}

#[test]
fn test_calc_min_break_width4() {
    let flib = FontCache();
    let font = flib.get_test_font();
    let actual = calc_min_break_width(font, ~"yumyum firecracker yumyum");
    let expected = au::from_px(84);
    assert expected == actual;
}

#[test]
fn test_iter_indivisible_slices() {
    let flib = FontCache();
    let font = flib.get_test_font();
    let mut slices = ~[];
    for iter_indivisible_slices(font, "firecracker yumyum woopwoop") |slice| {
        slices += [slice];
    }
    assert slices == ~["firecracker", "yumyum", "woopwoop"];
}

#[test]
fn test_iter_indivisible_slices_trailing_whitespace() {
    let flib = FontCache();
    let font = flib.get_test_font();
    let mut slices = ~[];
    for iter_indivisible_slices(font, "firecracker  ") |slice| {
        slices += [slice];
    }
    assert slices == ~["firecracker"];
}

#[test]
fn test_iter_indivisible_slices_leading_whitespace() {
    let flib = FontCache();
    let font = flib.get_test_font();
    let mut slices = ~[];
    for iter_indivisible_slices(font, "  firecracker") |slice| {
        slices += [slice];
    }
    assert slices == ~["firecracker"];
}

#[test]
fn test_iter_indivisible_slices_empty() {
    let flib = FontCache();
    let font = flib.get_test_font();
    let mut slices = ~[];
    for iter_indivisible_slices(font, "") |slice| {
        slices += [slice];
    }
    assert slices == ~[];
}

#[test]
fn test_split() {
    let flib = FontCache();
    let font = flib.get_test_font();
    let run = TextRun(font, ~"firecracker yumyum");
    let break_runs = run.split(font, run.min_break_width());
    assert break_runs.first().text == ~"firecracker";
    assert break_runs.second().text == ~"yumyum";
}

#[test]
fn test_split2() {
    let flib = FontCache();
    let font = flib.get_test_font();
    let run = TextRun(font, ~"firecracker yum yum yum yum yum");
    let break_runs = run.split(font, run.min_break_width());
    assert break_runs.first().text == ~"firecracker";
    assert break_runs.second().text == ~"yum yum yum yum yum";
}

#[test]
fn test_split3() {
    let flib = FontCache();
    let font = flib.get_test_font();
    let run = TextRun(font, ~"firecracker firecracker");
    let break_runs = run.split(font, run.min_break_width() + au::from_px(10));
    assert break_runs.first().text == ~"firecracker";
    assert break_runs.second().text == ~"firecracker";

}

#[test]
#[ignore(cfg(target_os = "macos"))]
fn should_calculate_the_total_size() {
    let flib = FontCache();
    let font = flib.get_test_font();
    let run = TextRun(font, ~"firecracker");
    let expected = Size2D(au::from_px(84), au::from_px(20));
    assert run.size() == expected;
}

<commit_msg>Comment out the text run test that causes ICE at compile time. Workaround for Rust Issue #3592<commit_after>use au = gfx::geometry;
use geom::point::Point2D;
use geom::size::Size2D;
use gfx::geometry::au;
use libc::{c_void};
use font_cache::FontCache;
use font::Font;
use glyph::Glyph;
use shaper::shape_text;

/// A single, unbroken line of text
struct TextRun {
    text: ~str,
    priv glyphs: ~[Glyph],
    priv size_: Size2D<au>,
    priv min_break_width_: au,
}

impl TextRun {
    /// The size of the entire TextRun
    pure fn size() -> Size2D<au> { self.size_ }
    pure fn min_break_width() -> au { self.min_break_width_ }

    /// Split a run of text in two
    // FIXME: Should be storing a reference to the Font inside
    // of the TextRun, but I'm hitting cycle collector bugs
    fn split(font: &Font, h_offset: au) -> (TextRun, TextRun) {
        assert h_offset >= self.min_break_width();
        assert h_offset <= self.size_.width;

        let mut curr_run = ~"";

        for iter_indivisible_slices(font, self.text) |slice| {
            let mut candidate = copy curr_run;

            if candidate.is_not_empty() {
                str::push_str(candidate, " "); // FIXME: just inserting spaces between words can't be right
            }

            str::push_str(candidate, slice);

            let glyphs = shape_text(font, candidate);
            let size = glyph_run_size(glyphs);
            if size.width <= h_offset {
                curr_run = move candidate;
            } else {
                break;
            }
        }

        assert curr_run.is_not_empty();

        let first = move curr_run;
        let second_start = match str::find_from(self.text, first.len(), |c| !char::is_whitespace(c)) {
            Some(idx) => idx,
            None => {
                // This will be an empty string
                self.text.len()
            }
        };
        let second = str::slice(self.text, second_start, self.text.len());
        return (TextRun(font, first), TextRun(font, second));
    }
}

fn TextRun(font: &Font, +text: ~str) -> TextRun {
    let glyphs = shape_text(font, text);
    let size = glyph_run_size(glyphs);
    let min_break_width = calc_min_break_width(font, text);

    TextRun {
        text: text,
        glyphs: shape_text(font, text),
        size_: size,
        min_break_width_: min_break_width
    }
}

fn glyph_run_size(glyphs: &[Glyph]) -> Size2D<au> {
    let height = au::from_px(20);
    let pen_start_x = au::from_px(0);
    let pen_start_y = height;
    let pen_start = Point2D(pen_start_x, pen_start_y);
    let pen_end = glyphs.foldl(pen_start, |cur, glyph| {
        Point2D(cur.x.add(glyph.pos.offset.x).add(glyph.pos.advance.x),
                cur.y.add(glyph.pos.offset.y).add(glyph.pos.advance.y))
    });
    return Size2D(pen_end.x, pen_end.y);
}

/// Discovers the width of the largest indivisible substring
fn calc_min_break_width(font: &Font, text: &str) -> au {
    let mut max_piece_width = au(0);
    for iter_indivisible_slices(font, text) |slice| {
        let glyphs = shape_text(font, slice);
        let size = glyph_run_size(glyphs);
        if size.width > max_piece_width {
            max_piece_width = size.width
        }
    }
    return max_piece_width;
}

/// Iterates over all the indivisible substrings
fn iter_indivisible_slices(_font: &Font, text: &r/str,
                           f: fn((&r/str)) -> bool) {

    let mut curr = text;
    loop {
        match str::find(curr, |c| !char::is_whitespace(c) ) {
          Some(idx) => {
            curr = str::view(curr, idx, curr.len());
          }
          None => {
            // Everything else is whitespace
            break
          }
        }

        match str::find(curr, |c| char::is_whitespace(c) ) {
          Some(idx) => {
            let piece = str::view(curr, 0, idx);
            if !f(piece) { break }
            curr = str::view(curr, idx, curr.len());
          }
          None => {
            assert curr.is_not_empty();
            if !f(curr) { break }
            // This is the end of the string
            break;
          }
        }
    }
}

#[test]
fn test_calc_min_break_width1() {
    let flib = FontCache();
    let font = flib.get_test_font();
    let actual = calc_min_break_width(font, ~"firecracker");
    let expected = au::from_px(84);
    assert expected == actual;
}

#[test]
fn test_calc_min_break_width2() {
    let flib = FontCache();
    let font = flib.get_test_font();
    let actual = calc_min_break_width(font, ~"firecracker yumyum");
    let expected = au::from_px(84);
    assert expected == actual;
}

#[test]
fn test_calc_min_break_width3() {
    let flib = FontCache();
    let font = flib.get_test_font();
    let actual = calc_min_break_width(font, ~"yumyum firecracker");
    let expected = au::from_px(84);
    assert expected == actual;
}

#[test]
fn test_calc_min_break_width4() {
    let flib = FontCache();
    let font = flib.get_test_font();
    let actual = calc_min_break_width(font, ~"yumyum firecracker yumyum");
    let expected = au::from_px(84);
    assert expected == actual;
}

#[test]
fn test_iter_indivisible_slices() {
    let flib = FontCache();
    let font = flib.get_test_font();
    let mut slices = ~[];
    for iter_indivisible_slices(font, "firecracker yumyum woopwoop") |slice| {
        slices += [slice];
    }
    assert slices == ~["firecracker", "yumyum", "woopwoop"];
}

#[test]
fn test_iter_indivisible_slices_trailing_whitespace() {
    let flib = FontCache();
    let font = flib.get_test_font();
    let mut slices = ~[];
    for iter_indivisible_slices(font, "firecracker  ") |slice| {
        slices += [slice];
    }
    assert slices == ~["firecracker"];
}

#[test]
fn test_iter_indivisible_slices_leading_whitespace() {
    let flib = FontCache();
    let font = flib.get_test_font();
    let mut slices = ~[];
    for iter_indivisible_slices(font, "  firecracker") |slice| {
        slices += [slice];
    }
    assert slices == ~["firecracker"];
}

#[test]
fn test_iter_indivisible_slices_empty() {
    let flib = FontCache();
    let font = flib.get_test_font();
    let mut slices = ~[];
    for iter_indivisible_slices(font, "") |slice| {
        slices += [slice];
    }
    assert slices == ~[];
}

#[test]
fn test_split() {
    let flib = FontCache();
    let font = flib.get_test_font();
    let run = TextRun(font, ~"firecracker yumyum");
    let break_runs = run.split(font, run.min_break_width());
    assert break_runs.first().text == ~"firecracker";
    assert break_runs.second().text == ~"yumyum";
}

#[test]
fn test_split2() {
    let flib = FontCache();
    let font = flib.get_test_font();
    let run = TextRun(font, ~"firecracker yum yum yum yum yum");
    let break_runs = run.split(font, run.min_break_width());
    assert break_runs.first().text == ~"firecracker";
    assert break_runs.second().text == ~"yum yum yum yum yum";
}

/* Causes ICE during compilation. See Rust Issue #3592 */
/*
#[test]
fn test_split3() {
    let flib = FontCache();
    let font = flib.get_test_font();
    let run = TextRun(font, ~"firecracker firecracker");
    let px = au::from_px(10);
    let break_runs = run.split(font, run.min_break_width() + px);
    assert break_runs.first().text == ~"firecracker";
    assert break_runs.second().text == ~"firecracker";

}*/

#[test]
#[ignore(cfg(target_os = "macos"))]
fn should_calculate_the_total_size() {
    let flib = FontCache();
    let font = flib.get_test_font();
    let run = TextRun(font, ~"firecracker");
    let expected = Size2D(au::from_px(84), au::from_px(20));
    assert run.size() == expected;
}

<|endoftext|>


<commit_before>extern crate pkg_config;
#[cfg(target_env = "msvc")]
extern crate vcpkg;
extern crate cc;

use std::env;
use std::fs;
use std::path::{PathBuf, Path};
use std::process::Command;

fn main() {
    let target = env::var("TARGET").unwrap();
    let windows = target.contains("windows");

    // OSX and Haiku ships libcurl by default, so we just use that version
    // unconditionally.
    if target.contains("apple") || target.contains("haiku") {
        return println!("cargo:rustc-flags=-l curl");
    }

    // Next, fall back and try to use pkg-config if its available.
    if windows {
        if try_vcpkg() {
            return
        }
    } else {
        if try_pkg_config() {
            return
        }
    }

    if !Path::new("curl/.git").exists() {
        let _ = Command::new("git").args(&["submodule", "update", "--init"])
                                   .status();
    }

    let dst = PathBuf::from(env::var_os("OUT_DIR").unwrap());
    let include = dst.join("include");
    let build = dst.join("build");
    println!("cargo:root={}", dst.display());
    println!("cargo:include={}", include.display());
    fs::create_dir_all(include.join("curl")).unwrap();
    fs::copy("curl/include/curl/curl.h", include.join("curl/curl.h")).unwrap();
    fs::copy("curl/include/curl/curlver.h", include.join("curl/curlver.h")).unwrap();
    fs::copy("curl/include/curl/easy.h", include.join("curl/easy.h")).unwrap();
    fs::copy("curl/include/curl/mprintf.h", include.join("curl/mprintf.h")).unwrap();
    fs::copy("curl/include/curl/multi.h", include.join("curl/multi.h")).unwrap();
    fs::copy("curl/include/curl/stdcheaders.h", include.join("curl/stdcheaders.h")).unwrap();
    fs::copy("curl/include/curl/system.h", include.join("curl/system.h")).unwrap();
    fs::copy("curl/include/curl/typecheck-gcc.h", include.join("curl/typecheck-gcc.h")).unwrap();

    let pkgconfig = dst.join("lib/pkgconfig");
    fs::create_dir_all(&pkgconfig).unwrap();
    let contents = fs::read_to_string("curl/libcurl.pc.in").unwrap();
    fs::write(
        pkgconfig.join("libcurl.pc"),
        contents
            .replace("@prefix@", dst.to_str().unwrap())
            .replace("@exec_prefix@", "")
            .replace("@libdir@", dst.join("lib").to_str().unwrap())
            .replace("@includedir@", include.to_str().unwrap())
            .replace("@CPPFLAG_CURL_STATICLIB@", "")
            .replace("@LIBCURL_LIBS@", "")
            .replace("@SUPPORT_FEATURES@", "")
            .replace("@SUPPORT_PROTOCOLS@", "")
            .replace("@CURLVERSION@", "7.61.1"),
    ).unwrap();

    let mut cfg = cc::Build::new();
    cfg.out_dir(&build)
        .include("curl/lib")
        .include("curl/include")
        .define("BUILDING_LIBCURL", None)
        .define("CURL_DISABLE_CRYPTO_AUTH", None)
        .define("CURL_DISABLE_DICT", None)
        .define("CURL_DISABLE_FTP", None)
        .define("CURL_DISABLE_GOPHER", None)
        .define("CURL_DISABLE_IMAP", None)
        .define("CURL_DISABLE_LDAP", None)
        .define("CURL_DISABLE_LDAPS", None)
        .define("CURL_DISABLE_NTLM", None)
        .define("CURL_DISABLE_POP3", None)
        .define("CURL_DISABLE_RTSP", None)
        .define("CURL_DISABLE_SMB", None)
        .define("CURL_DISABLE_SMTP", None)
        .define("CURL_DISABLE_TELNET", None)
        .define("CURL_DISABLE_TFTP", None)
        .define("CURL_STATICLIB", None)
        .define("ENABLE_IPV6", None)
        .define("HAVE_ASSERT_H", None)
        .define("OS", "\"unknown\"") // TODO
        .define("HAVE_ZLIB_H", None)
        .define("HAVE_LIBZ", None)

        .file("curl/lib/asyn-thread.c")
        .file("curl/lib/base64.c")
        .file("curl/lib/conncache.c")
        .file("curl/lib/connect.c")
        .file("curl/lib/content_encoding.c")
        .file("curl/lib/cookie.c")
        .file("curl/lib/curl_addrinfo.c")
        .file("curl/lib/curl_ctype.c")
        .file("curl/lib/curl_memrchr.c")
        .file("curl/lib/curl_range.c")
        .file("curl/lib/curl_threads.c")
        .file("curl/lib/dotdot.c")
        .file("curl/lib/easy.c")
        .file("curl/lib/escape.c")
        .file("curl/lib/file.c")
        .file("curl/lib/fileinfo.c")
        .file("curl/lib/formdata.c")
        .file("curl/lib/getenv.c")
        .file("curl/lib/getinfo.c")
        .file("curl/lib/hash.c")
        .file("curl/lib/hostasyn.c")
        .file("curl/lib/hostcheck.c")
        .file("curl/lib/hostip.c")
        .file("curl/lib/hostip6.c")
        .file("curl/lib/http.c")
        .file("curl/lib/http2.c")
        .file("curl/lib/http_chunks.c")
        .file("curl/lib/http_proxy.c")
        .file("curl/lib/if2ip.c")
        .file("curl/lib/inet_ntop.c")
        .file("curl/lib/inet_pton.c")
        .file("curl/lib/llist.c")
        .file("curl/lib/mime.c")
        .file("curl/lib/mprintf.c")
        .file("curl/lib/multi.c")
        .file("curl/lib/netrc.c")
        .file("curl/lib/nonblock.c")
        .file("curl/lib/parsedate.c")
        .file("curl/lib/pipeline.c")
        .file("curl/lib/progress.c")
        .file("curl/lib/rand.c")
        .file("curl/lib/select.c")
        .file("curl/lib/sendf.c")
        .file("curl/lib/setopt.c")
        .file("curl/lib/share.c")
        .file("curl/lib/slist.c")
        .file("curl/lib/socks.c")
        .file("curl/lib/speedcheck.c")
        .file("curl/lib/splay.c")
        .file("curl/lib/strcase.c")
        .file("curl/lib/strdup.c")
        .file("curl/lib/strerror.c")
        .file("curl/lib/strtok.c")
        .file("curl/lib/strtoofft.c")
        .file("curl/lib/timeval.c")
        .file("curl/lib/transfer.c")
        .file("curl/lib/url.c")
        .file("curl/lib/version.c")
        .file("curl/lib/vtls/vtls.c")
        .file("curl/lib/warnless.c")
        .file("curl/lib/wildcard.c")

        .define("HAVE_GETADDRINFO", None)

        .warnings(false);

    if cfg!(feature = "http2") {
        cfg.define("USE_NGHTTP2", None)
            .define("NGHTTP2_STATICLIB", None);

        if let Some(path) = env::var_os("DEP_NGHTTP2_ROOT") {
            let path = PathBuf::from(path);
            cfg.include(path.join("include"));
        }
    }

    if let Some(path) = env::var_os("DEP_Z_INCLUDE") {
        cfg.include(path);
    }

    if windows {
        cfg.define("USE_THREADS_WIN32", None)
            .define("HAVE_IOCTLSOCKET_FIONBIO", None)
            .define("USE_WINSOCK", None)
            .define("USE_WINDOWS_SSPI", None)
            .define("USE_SCHANNEL", None)
            .file("curl/lib/x509asn1.c")
            .file("curl/lib/curl_sspi.c")
            .file("curl/lib/socks_sspi.c")
            .file("curl/lib/system_win32.c")
            .file("curl/lib/vtls/schannel.c")
            .file("curl/lib/vtls/schannel_verify.c");
    } else {
        cfg.define("RECV_TYPE_ARG1", "int")
            .define("HAVE_PTHREAD_H", None)
            .define("HAVE_ARPA_INET_H", None)
            .define("HAVE_ERRNO_H", None)
            .define("HAVE_FCNTL_H", None)
            .define("HAVE_NETDB_H", None)
            .define("HAVE_NETINET_IN_H", None)
            .define("HAVE_POLL_FINE", None)
            .define("HAVE_POLL_H", None)
            .define("HAVE_FCNTL_O_NONBLOCK", None)
            .define("HAVE_SYS_SELECT_H", None)
            .define("HAVE_SYS_STAT_H", None)
            .define("HAVE_UNISTD_H", None)
            .define("HAVE_RECV", None)
            .define("HAVE_SELECT", None)
            .define("HAVE_SEND", None)
            .define("HAVE_SOCKET", None)
            .define("HAVE_STERRROR_R", None)
            .define("HAVE_STRUCT_TIMEVAL", None)
            .define("USE_THREADS_POSIX", None)
            .define("USE_OPENSSL", None)
            .define("RECV_TYPE_ARG2", "void*")
            .define("RECV_TYPE_ARG3", "size_t")
            .define("RECV_TYPE_ARG4", "int")
            .define("RECV_TYPE_RETV", "ssize_t")
            .define("SEND_QUAL_ARG2", "const")
            .define("SEND_TYPE_ARG1", "int")
            .define("SEND_TYPE_ARG2", "void*")
            .define("SEND_TYPE_ARG3", "size_t")
            .define("SEND_TYPE_ARG4", "int")
            .define("SEND_TYPE_RETV", "ssize_t")
            .define("SIZEOF_CURL_OFF_T", "8")
            .define("SIZEOF_INT", "4")
            .define("SIZEOF_SHORT", "2")
            .file("curl/lib/vtls/openssl.c");

        let width = env::var("CARGO_CFG_TARGET_POINTER_WIDTH")
            .unwrap()
            .parse::<usize>()
            .unwrap();
        cfg.define("SIZEOF_SSIZE_T", Some(&(width / 8).to_string()[..]));
        cfg.define("SIZEOF_SIZE_T", Some(&(width / 8).to_string()[..]));
        cfg.define("SIZEOF_LONG", Some(&(width / 8).to_string()[..]));

        if let Some(path) = env::var_os("DEP_OPENSSL_INCLUDE") {
            cfg.include(path);
        }

        cfg.flag("-fvisibility=hidden");
    }

    cfg.compile("curl");

    if windows {
        println!("cargo:rustc-link-lib=ws2_32");
        println!("cargo:rustc-link-lib=crypt32");
    }

    // Illumos/Solaris requires explicit linking with libnsl
    if target.contains("solaris") {
        println!("cargo:rustc-link-lib=nsl");
    }
}

#[cfg(not(target_env = "msvc"))]
fn try_vcpkg() -> bool {
    false
}

#[cfg(target_env = "msvc")]
fn try_vcpkg() -> bool {

    // the import library for the dll is called libcurl_imp
    let mut successful_probe_details =
        match vcpkg::Config::new().lib_names("libcurl_imp", "libcurl")
                .emit_includes(true).probe("curl") {
            Ok(details) => Some(details),
            Err(e) => {
                println!("first run of vcpkg did not find libcurl: {}", e);
                None
            }
        };

    if successful_probe_details.is_none() {
        match vcpkg::Config::new().lib_name("libcurl")
                .emit_includes(true).probe("curl") {
            Ok(details) => successful_probe_details = Some(details),
            Err(e) => println!("second run of vcpkg did not find libcurl: {}", e),
        }
    }

    if successful_probe_details.is_some() {
        // Found libcurl which depends on openssl, libssh2 and zlib
        // in the a default vcpkg installation. Probe for them
        // but do not fail if they are not present as we may be working
        // with a customized vcpkg installation.
        vcpkg::Config::new()
            .lib_name("libeay32")
            .lib_name("ssleay32")
            .probe("openssl").ok();

        vcpkg::probe_package("libssh2").ok();

        vcpkg::Config::new()
            .lib_names("zlib", "zlib1")
            .probe("zlib").ok();

        println!("cargo:rustc-link-lib=crypt32");
        println!("cargo:rustc-link-lib=gdi32");
        println!("cargo:rustc-link-lib=user32");
        println!("cargo:rustc-link-lib=wldap32");
        return true;
    }
    false
}

fn try_pkg_config() -> bool {
    let mut cfg = pkg_config::Config::new();
    cfg.cargo_metadata(false);
    let lib = match cfg.probe("libcurl") {
        Ok(lib) => lib,
        Err(e) => {
            println!("Couldn't find libcurl from pkgconfig ({:?}), \
                      compiling it from source...", e);
            return false
        }
    };

    // Not all system builds of libcurl have http2 features enabled, so if we've
    // got a http2-requested build then we may fall back to a build from source.
    if cfg!(feature = "http2") {
        let output = Command::new("curl-config")
            .arg("--features")
            .output();
        let output = match output {
            Ok(out) => out,
            Err(e) => {
                println!("failed to run curl-config ({}), building from source", e);
                return false
            }
        };
        if !output.status.success() {
            println!("curl-config failed: {}", output.status);
            return false
        }
        let stdout = String::from_utf8_lossy(&output.stdout);
        if !stdout.contains("HTTP2") {
            println!("failed to find http-2 feature enabled in pkg-config-found \
                      libcurl, building from source");
            return false
        }
    }

    // Re-find the library to print cargo's metadata, then print some extra
    // metadata as well.
    cfg.cargo_metadata(true)
        .probe("libcurl")
        .unwrap();
    for path in lib.include_paths.iter() {
        println!("cargo:include={}", path.display());
    }
    return true
}
<commit_msg>Include -DCURL_STATICLIB in pkg-config<commit_after>extern crate pkg_config;
#[cfg(target_env = "msvc")]
extern crate vcpkg;
extern crate cc;

use std::env;
use std::fs;
use std::path::{PathBuf, Path};
use std::process::Command;

fn main() {
    let target = env::var("TARGET").unwrap();
    let windows = target.contains("windows");

    // OSX and Haiku ships libcurl by default, so we just use that version
    // unconditionally.
    if target.contains("apple") || target.contains("haiku") {
        return println!("cargo:rustc-flags=-l curl");
    }

    // Next, fall back and try to use pkg-config if its available.
    if windows {
        if try_vcpkg() {
            return
        }
    } else {
        if try_pkg_config() {
            return
        }
    }

    if !Path::new("curl/.git").exists() {
        let _ = Command::new("git").args(&["submodule", "update", "--init"])
                                   .status();
    }

    let dst = PathBuf::from(env::var_os("OUT_DIR").unwrap());
    let include = dst.join("include");
    let build = dst.join("build");
    println!("cargo:root={}", dst.display());
    println!("cargo:include={}", include.display());
    fs::create_dir_all(include.join("curl")).unwrap();
    fs::copy("curl/include/curl/curl.h", include.join("curl/curl.h")).unwrap();
    fs::copy("curl/include/curl/curlver.h", include.join("curl/curlver.h")).unwrap();
    fs::copy("curl/include/curl/easy.h", include.join("curl/easy.h")).unwrap();
    fs::copy("curl/include/curl/mprintf.h", include.join("curl/mprintf.h")).unwrap();
    fs::copy("curl/include/curl/multi.h", include.join("curl/multi.h")).unwrap();
    fs::copy("curl/include/curl/stdcheaders.h", include.join("curl/stdcheaders.h")).unwrap();
    fs::copy("curl/include/curl/system.h", include.join("curl/system.h")).unwrap();
    fs::copy("curl/include/curl/typecheck-gcc.h", include.join("curl/typecheck-gcc.h")).unwrap();

    let pkgconfig = dst.join("lib/pkgconfig");
    fs::create_dir_all(&pkgconfig).unwrap();
    let contents = fs::read_to_string("curl/libcurl.pc.in").unwrap();
    fs::write(
        pkgconfig.join("libcurl.pc"),
        contents
            .replace("@prefix@", dst.to_str().unwrap())
            .replace("@exec_prefix@", "")
            .replace("@libdir@", dst.join("lib").to_str().unwrap())
            .replace("@includedir@", include.to_str().unwrap())
            .replace("@CPPFLAG_CURL_STATICLIB@", "-DCURL_STATICLIB")
            .replace("@LIBCURL_LIBS@", "")
            .replace("@SUPPORT_FEATURES@", "")
            .replace("@SUPPORT_PROTOCOLS@", "")
            .replace("@CURLVERSION@", "7.61.1"),
    ).unwrap();

    let mut cfg = cc::Build::new();
    cfg.out_dir(&build)
        .include("curl/lib")
        .include("curl/include")
        .define("BUILDING_LIBCURL", None)
        .define("CURL_DISABLE_CRYPTO_AUTH", None)
        .define("CURL_DISABLE_DICT", None)
        .define("CURL_DISABLE_FTP", None)
        .define("CURL_DISABLE_GOPHER", None)
        .define("CURL_DISABLE_IMAP", None)
        .define("CURL_DISABLE_LDAP", None)
        .define("CURL_DISABLE_LDAPS", None)
        .define("CURL_DISABLE_NTLM", None)
        .define("CURL_DISABLE_POP3", None)
        .define("CURL_DISABLE_RTSP", None)
        .define("CURL_DISABLE_SMB", None)
        .define("CURL_DISABLE_SMTP", None)
        .define("CURL_DISABLE_TELNET", None)
        .define("CURL_DISABLE_TFTP", None)
        .define("CURL_STATICLIB", None)
        .define("ENABLE_IPV6", None)
        .define("HAVE_ASSERT_H", None)
        .define("OS", "\"unknown\"") // TODO
        .define("HAVE_ZLIB_H", None)
        .define("HAVE_LIBZ", None)

        .file("curl/lib/asyn-thread.c")
        .file("curl/lib/base64.c")
        .file("curl/lib/conncache.c")
        .file("curl/lib/connect.c")
        .file("curl/lib/content_encoding.c")
        .file("curl/lib/cookie.c")
        .file("curl/lib/curl_addrinfo.c")
        .file("curl/lib/curl_ctype.c")
        .file("curl/lib/curl_memrchr.c")
        .file("curl/lib/curl_range.c")
        .file("curl/lib/curl_threads.c")
        .file("curl/lib/dotdot.c")
        .file("curl/lib/easy.c")
        .file("curl/lib/escape.c")
        .file("curl/lib/file.c")
        .file("curl/lib/fileinfo.c")
        .file("curl/lib/formdata.c")
        .file("curl/lib/getenv.c")
        .file("curl/lib/getinfo.c")
        .file("curl/lib/hash.c")
        .file("curl/lib/hostasyn.c")
        .file("curl/lib/hostcheck.c")
        .file("curl/lib/hostip.c")
        .file("curl/lib/hostip6.c")
        .file("curl/lib/http.c")
        .file("curl/lib/http2.c")
        .file("curl/lib/http_chunks.c")
        .file("curl/lib/http_proxy.c")
        .file("curl/lib/if2ip.c")
        .file("curl/lib/inet_ntop.c")
        .file("curl/lib/inet_pton.c")
        .file("curl/lib/llist.c")
        .file("curl/lib/mime.c")
        .file("curl/lib/mprintf.c")
        .file("curl/lib/multi.c")
        .file("curl/lib/netrc.c")
        .file("curl/lib/nonblock.c")
        .file("curl/lib/parsedate.c")
        .file("curl/lib/pipeline.c")
        .file("curl/lib/progress.c")
        .file("curl/lib/rand.c")
        .file("curl/lib/select.c")
        .file("curl/lib/sendf.c")
        .file("curl/lib/setopt.c")
        .file("curl/lib/share.c")
        .file("curl/lib/slist.c")
        .file("curl/lib/socks.c")
        .file("curl/lib/speedcheck.c")
        .file("curl/lib/splay.c")
        .file("curl/lib/strcase.c")
        .file("curl/lib/strdup.c")
        .file("curl/lib/strerror.c")
        .file("curl/lib/strtok.c")
        .file("curl/lib/strtoofft.c")
        .file("curl/lib/timeval.c")
        .file("curl/lib/transfer.c")
        .file("curl/lib/url.c")
        .file("curl/lib/version.c")
        .file("curl/lib/vtls/vtls.c")
        .file("curl/lib/warnless.c")
        .file("curl/lib/wildcard.c")

        .define("HAVE_GETADDRINFO", None)

        .warnings(false);

    if cfg!(feature = "http2") {
        cfg.define("USE_NGHTTP2", None)
            .define("NGHTTP2_STATICLIB", None);

        if let Some(path) = env::var_os("DEP_NGHTTP2_ROOT") {
            let path = PathBuf::from(path);
            cfg.include(path.join("include"));
        }
    }

    if let Some(path) = env::var_os("DEP_Z_INCLUDE") {
        cfg.include(path);
    }

    if windows {
        cfg.define("USE_THREADS_WIN32", None)
            .define("HAVE_IOCTLSOCKET_FIONBIO", None)
            .define("USE_WINSOCK", None)
            .define("USE_WINDOWS_SSPI", None)
            .define("USE_SCHANNEL", None)
            .file("curl/lib/x509asn1.c")
            .file("curl/lib/curl_sspi.c")
            .file("curl/lib/socks_sspi.c")
            .file("curl/lib/system_win32.c")
            .file("curl/lib/vtls/schannel.c")
            .file("curl/lib/vtls/schannel_verify.c");
    } else {
        cfg.define("RECV_TYPE_ARG1", "int")
            .define("HAVE_PTHREAD_H", None)
            .define("HAVE_ARPA_INET_H", None)
            .define("HAVE_ERRNO_H", None)
            .define("HAVE_FCNTL_H", None)
            .define("HAVE_NETDB_H", None)
            .define("HAVE_NETINET_IN_H", None)
            .define("HAVE_POLL_FINE", None)
            .define("HAVE_POLL_H", None)
            .define("HAVE_FCNTL_O_NONBLOCK", None)
            .define("HAVE_SYS_SELECT_H", None)
            .define("HAVE_SYS_STAT_H", None)
            .define("HAVE_UNISTD_H", None)
            .define("HAVE_RECV", None)
            .define("HAVE_SELECT", None)
            .define("HAVE_SEND", None)
            .define("HAVE_SOCKET", None)
            .define("HAVE_STERRROR_R", None)
            .define("HAVE_STRUCT_TIMEVAL", None)
            .define("USE_THREADS_POSIX", None)
            .define("USE_OPENSSL", None)
            .define("RECV_TYPE_ARG2", "void*")
            .define("RECV_TYPE_ARG3", "size_t")
            .define("RECV_TYPE_ARG4", "int")
            .define("RECV_TYPE_RETV", "ssize_t")
            .define("SEND_QUAL_ARG2", "const")
            .define("SEND_TYPE_ARG1", "int")
            .define("SEND_TYPE_ARG2", "void*")
            .define("SEND_TYPE_ARG3", "size_t")
            .define("SEND_TYPE_ARG4", "int")
            .define("SEND_TYPE_RETV", "ssize_t")
            .define("SIZEOF_CURL_OFF_T", "8")
            .define("SIZEOF_INT", "4")
            .define("SIZEOF_SHORT", "2")
            .file("curl/lib/vtls/openssl.c");

        let width = env::var("CARGO_CFG_TARGET_POINTER_WIDTH")
            .unwrap()
            .parse::<usize>()
            .unwrap();
        cfg.define("SIZEOF_SSIZE_T", Some(&(width / 8).to_string()[..]));
        cfg.define("SIZEOF_SIZE_T", Some(&(width / 8).to_string()[..]));
        cfg.define("SIZEOF_LONG", Some(&(width / 8).to_string()[..]));

        if let Some(path) = env::var_os("DEP_OPENSSL_INCLUDE") {
            cfg.include(path);
        }

        cfg.flag("-fvisibility=hidden");
    }

    cfg.compile("curl");

    if windows {
        println!("cargo:rustc-link-lib=ws2_32");
        println!("cargo:rustc-link-lib=crypt32");
    }

    // Illumos/Solaris requires explicit linking with libnsl
    if target.contains("solaris") {
        println!("cargo:rustc-link-lib=nsl");
    }
}

#[cfg(not(target_env = "msvc"))]
fn try_vcpkg() -> bool {
    false
}

#[cfg(target_env = "msvc")]
fn try_vcpkg() -> bool {

    // the import library for the dll is called libcurl_imp
    let mut successful_probe_details =
        match vcpkg::Config::new().lib_names("libcurl_imp", "libcurl")
                .emit_includes(true).probe("curl") {
            Ok(details) => Some(details),
            Err(e) => {
                println!("first run of vcpkg did not find libcurl: {}", e);
                None
            }
        };

    if successful_probe_details.is_none() {
        match vcpkg::Config::new().lib_name("libcurl")
                .emit_includes(true).probe("curl") {
            Ok(details) => successful_probe_details = Some(details),
            Err(e) => println!("second run of vcpkg did not find libcurl: {}", e),
        }
    }

    if successful_probe_details.is_some() {
        // Found libcurl which depends on openssl, libssh2 and zlib
        // in the a default vcpkg installation. Probe for them
        // but do not fail if they are not present as we may be working
        // with a customized vcpkg installation.
        vcpkg::Config::new()
            .lib_name("libeay32")
            .lib_name("ssleay32")
            .probe("openssl").ok();

        vcpkg::probe_package("libssh2").ok();

        vcpkg::Config::new()
            .lib_names("zlib", "zlib1")
            .probe("zlib").ok();

        println!("cargo:rustc-link-lib=crypt32");
        println!("cargo:rustc-link-lib=gdi32");
        println!("cargo:rustc-link-lib=user32");
        println!("cargo:rustc-link-lib=wldap32");
        return true;
    }
    false
}

fn try_pkg_config() -> bool {
    let mut cfg = pkg_config::Config::new();
    cfg.cargo_metadata(false);
    let lib = match cfg.probe("libcurl") {
        Ok(lib) => lib,
        Err(e) => {
            println!("Couldn't find libcurl from pkgconfig ({:?}), \
                      compiling it from source...", e);
            return false
        }
    };

    // Not all system builds of libcurl have http2 features enabled, so if we've
    // got a http2-requested build then we may fall back to a build from source.
    if cfg!(feature = "http2") {
        let output = Command::new("curl-config")
            .arg("--features")
            .output();
        let output = match output {
            Ok(out) => out,
            Err(e) => {
                println!("failed to run curl-config ({}), building from source", e);
                return false
            }
        };
        if !output.status.success() {
            println!("curl-config failed: {}", output.status);
            return false
        }
        let stdout = String::from_utf8_lossy(&output.stdout);
        if !stdout.contains("HTTP2") {
            println!("failed to find http-2 feature enabled in pkg-config-found \
                      libcurl, building from source");
            return false
        }
    }

    // Re-find the library to print cargo's metadata, then print some extra
    // metadata as well.
    cfg.cargo_metadata(true)
        .probe("libcurl")
        .unwrap();
    for path in lib.include_paths.iter() {
        println!("cargo:include={}", path.display());
    }
    return true
}
<|endoftext|>


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

use hyper::client::response::Response;
use hyper::status::StatusCode;
use utils::say::{say, sayln};
use errors::{DeliveryError, Kind};
use std::path::{PathBuf};
use http::APIClient;
use git;
use config::Config;

fn call_api<F>(closure: F) where F : Fn() -> Result<Response, Kind> {
    match closure() {
        Ok(_) => {
            sayln("white", "done");
        },
        Err(e) => {
            match e {
                Kind::ApiError(StatusCode::Conflict, _) => {
                    sayln("white", " already exists.");
                },
                Kind::ApiError(code, Ok(msg)) => {
                    sayln("red", &format!("{} {}", code, msg));
                },
                _ => {
                    sayln("red", &format!("Other error: {:?}", e));
                }
            }
        }
    }
}

pub fn import(config: &Config, path: &PathBuf) -> Result<(), DeliveryError> {
    let org = try!(config.organization());
    let proj = try!(config.project());

    // Init && config local repo if necessary
    try!(git::init_repo(path));
    let url = try!(config.delivery_git_ssh_url());
    if try!(git::config_repo(&url, path)) {
        sayln("white", "Remote 'delivery' added to git config!");
    } else {
        sayln("red", "Remote named 'delivery' already exists - not modifying");
    }

    let client = try!(APIClient::from_config(config));

    if ! client.project_exists(&org, &proj) {
        say("white", "Creating project: ");
        sayln("magenta", &format!("{} ", proj));
        let _ = client.create_project(&org, &proj);
    } else {
        say("white", "Project ");
        say("magenta", &format!("{} ", proj));
        sayln("white", "already exists.");
    }

    say("white", "Checking for content on the git remote ");
    say("magenta", "delivery: ");
    if git::server_content() {
        sayln("red", "Found commits upstream, not pushing local commits.");
    } else {
        sayln("white", "No upstream content; pushing local content to server.");
        let _ = git::git_push_master();
    }

    say("white", "Creating master pipeline for project: ");
    say("magenta", &format!("{} ", proj));
    say("white", "... ");
    call_api(|| client.create_pipeline(&org, &proj, "master"));
    return Ok(())
}

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

use hyper::client::response::Response;
use hyper::status::StatusCode;
use utils::say::{say, sayln};
use errors::{DeliveryError, Kind};
use std::path::{PathBuf};
use http::APIClient;
use git;
use config::Config;

fn call_api<F>(closure: F) where F : Fn() -> Result<Response, Kind> {
    match closure() {
        Ok(_) => {
            sayln("white", "done");
        },
        Err(e) => {
            match e {
                Kind::ApiError(StatusCode::Conflict, _) => {
                    sayln("white", " already exists.");
                },
                Kind::ApiError(code, Ok(msg)) => {
                    sayln("red", &format!("{} {}", code, msg));
                },
                _ => {
                    sayln("red", &format!("Other error: {:?}", e));
                }
            }
        }
    }
}

pub fn import(config: &Config, path: &PathBuf) -> Result<(), DeliveryError> {
    let org = try!(config.organization());
    let proj = try!(config.project());

    // Init && config local repo if necessary
    try!(git::init_repo(path));
    let url = try!(config.delivery_git_ssh_url());
    if try!(git::config_repo(&url, path)) {
        sayln("white", "Remote 'delivery' added to git config!");
    } else {
        sayln("red", "Remote named 'delivery' already exists - not modifying");
    }

    let client = try!(APIClient::from_config(config));

    if ! client.project_exists(&org, &proj) {
        say("white", "Creating project: ");
        say("magenta", &format!("{} ", proj));
        call_api(|| client.create_project(&org, &proj));
    } else {
        say("white", "Project ");
        say("magenta", &format!("{} ", proj));
        sayln("white", "already exists.");
    }

    say("white", "Checking for content on the git remote ");
    say("magenta", "delivery: ");
    if git::server_content() {
        sayln("red", "Found commits upstream, not pushing local commits.");
    } else {
        sayln("white", "No upstream content; pushing local content to server.");
        let _ = git::git_push_master();
    }

    say("white", "Creating master pipeline for project: ");
    say("magenta", &format!("{} ", proj));
    say("white", "... ");
    call_api(|| client.create_pipeline(&org, &proj, "master"));
    return Ok(())
}

<|endoftext|>


<commit_before>pub type NodeId = usize;

pub trait Edge {
    fn get_source_id(&self) -> NodeId;
    fn get_dest_id(&self) -> NodeId;
}

#[derive(PartialEq,Debug)]
struct EdgeTest {
    source: NodeId,
    dest: NodeId,
}

impl Edge for EdgeTest {
    fn get_source_id(&self) -> NodeId {
        self.source
    }
    fn get_dest_id(&self) -> NodeId {
        self.dest
    }
}
impl EdgeTest {
    fn new(source: NodeId, dest: NodeId) -> EdgeTest {
        EdgeTest {
            source: source,
            dest: dest,
        }
    }
}
#[derive(Clone,PartialEq,Debug)]
struct NodeOffset(usize);

pub struct Graph<E: Edge> {
    nodes: Vec<NodeOffset>,
    edges: Vec<E>,
}

impl<E: Edge> Graph<E> {
    pub fn new(node_count: usize, mut edges: Vec<E>) -> Graph<E> {
        edges.sort_by(|a, b| {
                          let ord = a.get_source_id().cmp(&b.get_source_id());
                          match ord {
                              ::std::cmp::Ordering::Equal => a.get_dest_id().cmp(&b.get_dest_id()),
                              _ => ord,
                          }
                      });

        Graph {
            nodes: Graph::calc_node_offsets(node_count, &edges),
            edges: edges,
        }

    }

    pub fn outgoing_edges_for(&self, id: NodeId) -> &[E] {
        &self.edges[self.nodes[id].0..self.nodes[id + 1].0]
    }

    fn calc_node_offsets(node_count: usize, edges: &Vec<E>) -> Vec<NodeOffset> {

        let mut nodes = vec![NodeOffset(0); node_count +1];
        let mut last_source = 0;
        for (index, edge) in edges.iter().enumerate() {
            let cur_source = edge.get_source_id();
            for id in last_source..cur_source {
                nodes[id + 1].0 = index;
            }
            last_source = cur_source;
        }

        for id in (last_source + 1)..(node_count + 1) {
            nodes[id].0 = edges.len();
        }
        nodes

    }
}

#[test]
fn graph_creation() {
    let g = Graph::new(5,
                       vec![EdgeTest::new(0, 1),
                            EdgeTest::new(0, 2),
                            EdgeTest::new(2, 3),
                            EdgeTest::new(0, 3),
                            EdgeTest::new(2, 4)]);
    let exp = vec![NodeOffset(0),
                   NodeOffset(3),
                   NodeOffset(3),
                   NodeOffset(5),
                   NodeOffset(5),
                   NodeOffset(5)];
    assert_eq!(g.nodes.len(), exp.len());
    assert_eq!(g.nodes, exp);

    assert_eq!(g.outgoing_edges_for(0).len(), 3);
    assert_eq!(g.outgoing_edges_for(2),
               &[EdgeTest::new(2, 3), EdgeTest::new(2, 4)]);
}
<commit_msg>Implement loading of graphs from file<commit_after>pub use self::load::load_graph;

pub type NodeId = usize;
pub type OsmNodeId = usize;
pub type Latitude = f64;
pub type Longitude = f64;
pub type Length = usize;
pub type Speed = usize;
pub type Height = usize;

pub struct NodeInfo {
    osm_id: OsmNodeId,
    lat: Latitude,
    long: Longitude,
    height: Height,
}

impl NodeInfo {
    fn new(osm_id: OsmNodeId, lat: Latitude, long: Longitude, height: Height) -> NodeInfo {
        NodeInfo {
            osm_id: osm_id,
            lat: lat,
            long: long,
            height: height,
        }
    }
}

pub trait Edge {
    fn get_source_id(&self) -> NodeId;
    fn get_dest_id(&self) -> NodeId;
    fn get_travel_time(&self) -> f64;
}

#[derive(PartialEq,Debug)]
pub struct EdgeInfo {
    source: NodeId,
    dest: NodeId,
    length: Length,
    speed: Speed,
}

impl Edge for EdgeInfo {
    fn get_source_id(&self) -> NodeId {
        self.source
    }
    fn get_dest_id(&self) -> NodeId {
        self.dest
    }
    fn get_travel_time(&self) -> f64 {
        (self.length as f64) / (self.speed as f64)
    }
}
impl EdgeInfo {
    fn new(source: NodeId, dest: NodeId, length: Length, speed: Speed) -> EdgeInfo {
        EdgeInfo {
            source: source,
            dest: dest,
            length: length,
            speed: speed,
        }
    }
}
#[derive(Clone,PartialEq,Debug)]
struct NodeOffset(usize);

pub struct Graph<E: Edge> {
    node_info: Vec<NodeInfo>,
    node_offsets: Vec<NodeOffset>,
    edges: Vec<E>,
}

impl<E: Edge> Graph<E> {
    pub fn new(node_info: Vec<NodeInfo>, mut edges: Vec<E>) -> Graph<E> {
        edges.sort_by(|a, b| {
                          let ord = a.get_source_id().cmp(&b.get_source_id());
                          match ord {
                              ::std::cmp::Ordering::Equal => a.get_dest_id().cmp(&b.get_dest_id()),
                              _ => ord,
                          }
                      });

        let node_count = node_info.len();
        Graph {
            node_info: node_info,
            node_offsets: Graph::calc_node_offsets(node_count, &edges),
            edges: edges,
        }

    }

    pub fn outgoing_edges_for(&self, id: NodeId) -> &[E] {
        &self.edges[self.node_offsets[id].0..self.node_offsets[id + 1].0]
    }

    fn calc_node_offsets(node_count: usize, edges: &Vec<E>) -> Vec<NodeOffset> {

        let mut node_offsets = vec![NodeOffset(0); node_count +1];
        let mut last_source = 0;
        for (index, edge) in edges.iter().enumerate() {
            let cur_source = edge.get_source_id();
            for id in last_source..cur_source {
                node_offsets[id + 1].0 = index;
            }
            last_source = cur_source;
        }

        for id in (last_source + 1)..(node_count + 1) {
            node_offsets[id].0 = edges.len();
        }
        node_offsets

    }
}

#[test]
fn graph_creation() {
    let g = Graph::new(vec![NodeInfo::new(23, 3.4, 2.3, 12),
                            NodeInfo::new(27, 4.4, 2.3, 12),
                            NodeInfo::new(53, 6.4, 1.3, 12),
                            NodeInfo::new(36, 3.8, 2.4, 12),
                            NodeInfo::new(78, 9.2, 2.3, 12)],
                       vec![EdgeInfo::new(0, 1, 1, 1),
                            EdgeInfo::new(0, 2, 1, 1),
                            EdgeInfo::new(2, 3, 1, 1),
                            EdgeInfo::new(0, 3, 1, 1),
                            EdgeInfo::new(2, 4, 1, 1)]);
    let exp = vec![NodeOffset(0),
                   NodeOffset(3),
                   NodeOffset(3),
                   NodeOffset(5),
                   NodeOffset(5),
                   NodeOffset(5)];
    assert_eq!(g.node_offsets.len(), exp.len());
    assert_eq!(g.node_offsets, exp);

    assert_eq!(g.outgoing_edges_for(0).len(), 3);
    assert_eq!(g.outgoing_edges_for(2),
               &[EdgeInfo::new(2, 3, 1, 1), EdgeInfo::new(2, 4, 1, 1)]);
}

mod load {

    use ae1::*;

    use std::path::Path;
    use std::fs::File;
    use std::io::Read;

    fn load_file<P: AsRef<Path>>(file: P) -> (Vec<NodeInfo>, Vec<EdgeInfo>) {
        let mut buffer = String::new();

        let mut file = File::open(file).expect("File could not be opened");
        file.read_to_string(&mut buffer)
            .expect("Could not read file");
        let mut line_iter = buffer
            .lines()
            .skip_while(|l| l.starts_with("#") || l.is_empty());
        let node_count: usize = line_iter
            .next()
            .map(str::parse)
            .expect("did not find node count")
            .expect("Node Count could not be parsed");
        let edge_count: usize = line_iter
            .next()
            .map(str::parse)
            .expect("did not find edge count")
            .expect("edge Count could not be parsed");

        let mut nodes = Vec::with_capacity(node_count);
        let mut edges = Vec::with_capacity(edge_count);
        for node_id in 0..node_count {
            let mut raw_node_data = line_iter
                .next()
                .map(|l| l.split(' '))
                .expect(&format!("No more node data for {}", node_id));
            raw_node_data.next(); // id is not necessary
            let osm_id: OsmNodeId = raw_node_data
                .next()
                .map(str::parse)
                .expect("No OSM ID Data")
                .expect("OSM ID not parse-able");
            let lat: Latitude = raw_node_data
                .next()
                .map(str::parse)
                .expect("No Latitude Data")
                .expect("Latitude not parse-able");
            let long: Longitude = raw_node_data
                .next()
                .map(str::parse)
                .expect("No Longitude Data")
                .expect("Longitude not parse-able");
            let height: Height = raw_node_data
                .next()
                .map(str::parse)
                .expect("No Height Data")
                .expect("Height not parse-able");
            nodes.push(NodeInfo::new(osm_id, lat, long, height))

        }
        for edge_id in 0..edge_count {
            let mut raw_node_data = line_iter
                .next()
                .map(|l| l.split(' '))
                .expect(&format!("No more node data for {}", edge_id));
            let source: NodeId = raw_node_data
                .next()
                .map(str::parse)
                .expect("No source Id found")
                .expect("Source id not parse-able");
            let dest: NodeId = raw_node_data
                .next()
                .map(str::parse)
                .expect("No destination Id found")
                .expect("Destination id not parse-able");
            let length: Length = raw_node_data
                .next()
                .map(str::parse)
                .expect("No length found")
                .expect("Length id not parse-able");
            raw_node_data.next(); //ignore type

            let speed: Speed = raw_node_data
                .next()
                .map(str::parse)
                .expect("No speed found")
                .expect("Speed id not parse-able");
            edges.push(EdgeInfo::new(source, dest, length, speed))

        }

        assert!(None == line_iter.next(), "file still contains data");

        (nodes, edges)
    }
    pub fn load_graph<P: AsRef<Path>>(file: &P) -> Graph<EdgeInfo> {
        let (nodes, edges) = load_file(file);
        Graph::new(nodes, edges)
    }

    #[test]
    fn load_test() {
        let (nodes, edges) = load_file("/home/flo/workspaces/rust/graphdata/saarland.graph");
        assert_eq!(nodes.len(), 595294);
        assert_eq!(edges.len(), 1241741);
    }

}
<|endoftext|>


<commit_before>use crate::Action;
use alloc::boxed::Box;
use core::convert::Infallible;

/// An action that can be any type.
pub struct AnyAction<T, O = (), E = Infallible> {
    action: Box<dyn Action<Target = T, Output = O, Error = E>>,
}

impl<T, O, E> AnyAction<T, O, E> {
    /// Creates an `AnyAction` from the provided action.
    pub fn new<A>(action: A) -> AnyAction<T, O, E>
    where
        A: Action<Target = T, Output = O, Error = E>,
        A: 'static,
    {
        AnyAction {
            action: Box::new(action),
        }
    }
}

impl<T, O, E> Action for AnyAction<T, O, E> {
    type Target = T;
    type Output = O;
    type Error = E;

    fn apply(&mut self, target: &mut Self::Target) -> crate::Result<Self> {
        self.action.apply(target)
    }

    fn undo(&mut self, target: &mut Self::Target) -> crate::Result<Self> {
        self.action.undo(target)
    }

    fn redo(&mut self, target: &mut Self::Target) -> crate::Result<Self> {
        self.action.redo(target)
    }
}

#[cfg(test)]
mod tests {
    use crate::{Action, AnyAction, Record, Result};
    use alloc::string::String;

    struct Add(char);

    impl Action for Add {
        type Target = String;
        type Output = ();
        type Error = &'static str;

        fn apply(&mut self, s: &mut String) -> Result<Add> {
            s.push(self.0);
            Ok(())
        }

        fn undo(&mut self, s: &mut String) -> Result<Add> {
            self.0 = s.pop().ok_or("s is empty")?;
            Ok(())
        }
    }

    #[test]
    fn any() {
        let any = AnyAction::new(Add('a'));
        let mut target = String::new();
        let mut record = Record::new();
        record.apply(&mut target, any).unwrap();
        assert_eq!(target, "a");
    }
}
<commit_msg>Test undo and redo calls in any test<commit_after>use crate::Action;
use alloc::boxed::Box;

/// An action that can be any type.
pub struct AnyAction<T, O, E> {
    action: Box<dyn Action<Target = T, Output = O, Error = E>>,
}

impl<T, O, E> AnyAction<T, O, E> {
    /// Creates an `AnyAction` from the provided action.
    pub fn new<A>(action: A) -> AnyAction<T, O, E>
    where
        A: Action<Target = T, Output = O, Error = E>,
        A: 'static,
    {
        AnyAction {
            action: Box::new(action),
        }
    }
}

impl<T, O, E> Action for AnyAction<T, O, E> {
    type Target = T;
    type Output = O;
    type Error = E;

    fn apply(&mut self, target: &mut Self::Target) -> crate::Result<Self> {
        self.action.apply(target)
    }

    fn undo(&mut self, target: &mut Self::Target) -> crate::Result<Self> {
        self.action.undo(target)
    }

    fn redo(&mut self, target: &mut Self::Target) -> crate::Result<Self> {
        self.action.redo(target)
    }
}

#[cfg(test)]
mod tests {
    use crate::{Action, AnyAction, Record, Result};
    use alloc::string::String;

    struct Add(char);

    impl Action for Add {
        type Target = String;
        type Output = ();
        type Error = &'static str;

        fn apply(&mut self, s: &mut String) -> Result<Add> {
            s.push(self.0);
            Ok(())
        }

        fn undo(&mut self, s: &mut String) -> Result<Add> {
            self.0 = s.pop().ok_or("s is empty")?;
            Ok(())
        }
    }

    #[test]
    fn any() {
        let mut target = String::new();
        let mut record = Record::new();
        record.apply(&mut target, AnyAction::new(Add('a'))).unwrap();
        assert_eq!(target, "a");
        record.undo(&mut target).unwrap().unwrap();
        assert_eq!(target, "");
        record.redo(&mut target).unwrap().unwrap();
        assert_eq!(target, "a");
    }
}
<|endoftext|>