<commit_before>use std::sync::atomic::{AtomicUsize, Ordering};

use chrono;
use serde::{Serialize, Serializer};

// The main design goals of this metrics system are:
// - Use lockless operations, preferably ones that don't require anything other than
// simple reads/writes being atomic.
// - Exploit interior mutability and atomics being Sync to allow all methods (including the ones
// which are effectively mutable) to be callable on a global non-mut static.
// - Rely on Serde to provide the actual serialization for logging the metrics.
// - Since all metrics start at 0, we implement the Default trait via derive for all of them,
// to avoid having to initialize everything by hand.

// Moreover, the value of a metric is currently NOT reset to 0 each time it's being logged. There
// are a number of advantages to this approach, including:
// - We don't have to introduce an additional write (to reset the value) from the thread which
// does to actual logging, so less synchronization effort is required.
// - We don't have to worry at all that much about losing some data if logging fails for a while
// (this could be a concern, I guess).
//
// If if turns out this approach is not really what we want, it's pretty easy to resort to something
// else, while working behind the same interface.

// This trait helps with writing less code. It has to be in scope (via an use directive) in order
// for its methods to be available to call on structs that implement it.
pub trait Metric {
    fn add(&self, value: usize);
    fn inc(&self) {
        self.add(1);
    }
    fn count(&self) -> usize;
}

// A simple metric is one meant to be incremented from a single thread, so it can use simple
// loads + stores. Loads are currently Relaxed everywhere, because we don't do anything besides
// logging the retrieved value (their outcome os not used to modify some memory location in a
// potentially inconsistent manner). There's no way currently to make sure a SimpleMetric is only
// incremented by a single thread, this has to be enforced via judicious use (although, every
// non-vCPU related metric is associated with a particular thread, so it shouldn't be that easy
// to misuse SimpleMetric fields).
#[derive(Default)]
pub struct SimpleMetric(AtomicUsize);

impl Metric for SimpleMetric {
    fn add(&self, value: usize) {
        let ref count = self.0;
        count.store(count.load(Ordering::Relaxed) + value, Ordering::Relaxed);
    }

    fn count(&self) -> usize {
        self.0.load(Ordering::Relaxed)
    }
}

impl Serialize for SimpleMetric {
    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
        // There's no serializer.serialize_usize() for some reason :(
        serializer.serialize_u64(self.0.load(Ordering::Relaxed) as u64)
    }
}

// A shared metric is one which is expected to be incremented from more than one thread, so more
// synchronization is necessary. It's currently used for vCPU metrics. An alternative here would be
// to have one instance of every metric for each thread (like a per-thread SimpleMetric), and to
// aggregate them when logging. However this probably overkill unless we have a lot of vCPUs
// incrementing metrics very often. Still, it's there if we ever need it :-s
#[derive(Default)]
pub struct SharedMetric(AtomicUsize);

impl Metric for SharedMetric {
    // While the order specified for this operation is still Relaxed, the actual instruction will
    // be an asm "LOCK; something" and thus atomic across multiple threads, simply because of the
    // fetch_and_add (as opposed to "store(load() + 1)") implementation for atomics.
    // TODO: would a stronger ordering make a difference here?
    fn add(&self, value: usize) {
        self.0.fetch_add(value, Ordering::Relaxed);
    }

    fn count(&self) -> usize {
        self.0.load(Ordering::Relaxed)
    }
}

impl Serialize for SharedMetric {
    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
        // There's no serializer.serialize_usize() for some reason :(
        serializer.serialize_u64(self.0.load(Ordering::Relaxed) as u64)
    }
}

// The following structs are used to define a certain organization for the set of metrics we
// are interested in. Whenever the name of a field differs from its ideal textual representation
// in the serialized form, we can use the #[serde(rename = "name")] attribute to, well, rename it.

// Metrics related to the internal api server
#[derive(Default, Serialize)]
pub struct ApiServerMetrics {
    pub async_missed_actions_count: SharedMetric,
    pub async_outcome_fails: SharedMetric,
    pub async_vmm_send_timeout_count: SharedMetric,
    pub instance_info_fails: SharedMetric,
    pub sync_outcome_fails: SharedMetric,
    pub sync_vmm_send_timeout_count: SharedMetric,
}

// Metrics on GET Api Requests
#[derive(Default, Serialize)]
pub struct GetRequestsMetrics {
    pub action_info_count: SharedMetric,
    pub actions_count: SharedMetric,
    pub actions_fails: SharedMetric,
    pub instance_info_count: SharedMetric,
    pub machine_cfg_count: SharedMetric,
    pub machine_cfg_fails: SharedMetric,
}

// Metrics on PUT Api Requests
#[derive(Default, Serialize)]
pub struct PutRequestsMetrics {
    pub actions_count: SharedMetric,
    pub actions_fails: SharedMetric,
    pub boot_source_count: SharedMetric,
    pub boot_source_fails: SharedMetric,
    pub drive_fails: SharedMetric,
    pub drive_count: SharedMetric,
    pub logger_count: SharedMetric,
    pub logger_fails: SharedMetric,
    pub machine_cfg_count: SharedMetric,
    pub machine_cfg_fails: SharedMetric,
    pub network_count: SharedMetric,
    pub network_fails: SharedMetric,
}

#[derive(Default, Serialize)]
pub struct BlockDeviceMetrics {
    pub activate_fails: SharedMetric,
    pub cfg_fails: SharedMetric,
    pub event_fails: SharedMetric,
    pub execute_fails: SharedMetric,
    pub invalid_reqs_count: SharedMetric,
    pub flush_count: SharedMetric,
    pub queue_event_count: SharedMetric,
    pub rate_limiter_event_count: SharedMetric,
    pub read_count: SharedMetric,
    pub write_count: SharedMetric,
}

#[derive(Default, Serialize)]
pub struct I8042DeviceMetrics {
    pub error_count: SharedMetric,
    pub missed_read_count: SharedMetric,
    pub missed_write_count: SharedMetric,
    pub read_count: SharedMetric,
    pub reset_count: SharedMetric,
    pub write_count: SharedMetric,
}

#[derive(Default, Serialize)]
pub struct NetDeviceMetrics {
    pub activate_fails: SharedMetric,
    pub cfg_fails: SharedMetric,
    pub event_fails: SharedMetric,
    pub rx_bytes_count: SharedMetric,
    pub rx_packets_count: SharedMetric,
    pub rx_event_rate_limiter_count: SharedMetric,
    pub rx_fails: SharedMetric,
    pub rx_queue_event_count: SharedMetric,
    pub rx_tap_event_count: SharedMetric,
    pub tx_bytes_count: SharedMetric,
    pub tx_packets_count: SharedMetric,
    pub tx_rate_limiter_event_count: SharedMetric,
    pub tx_fails: SharedMetric,
    pub tx_queue_event_count: SharedMetric,
}

#[derive(Default, Serialize)]
pub struct SerialDeviceMetrics {
    pub error_count: SharedMetric,
    pub flush_count: SharedMetric,
    pub missed_read_count: SharedMetric,
    pub missed_write_count: SharedMetric,
    pub read_count: SharedMetric,
    pub write_count: SharedMetric,
}

#[derive(Default, Serialize)]
pub struct VcpuMetrics {
    pub eagain: SharedMetric,
    pub eintr: SharedMetric,
    pub exit_io_in: SharedMetric,
    pub exit_io_out: SharedMetric,
    pub exit_mmio_read: SharedMetric,
    pub exit_mmio_write: SharedMetric,
    pub failures: SharedMetric,
}

#[derive(Default, Serialize)]
pub struct VmmMetrics {
    pub device_events: SharedMetric,
}

// The sole purpose of this struct is to produce an UTC timestamp when an instance is serialized.
#[derive(Default)]
struct SerializeToUtcTimestampMs;

impl Serialize for SerializeToUtcTimestampMs {
    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
        serializer.serialize_i64(chrono::Utc::now().timestamp_millis())
    }
}

#[derive(Default, Serialize)]
pub struct FirecrackerMetrics {
    utc_timestamp_ms: SerializeToUtcTimestampMs,
    pub api_server: ApiServerMetrics,
    pub block: BlockDeviceMetrics,
    pub get_api_requests: GetRequestsMetrics,
    pub i8042: I8042DeviceMetrics,
    pub net: NetDeviceMetrics,
    pub put_api_requests: PutRequestsMetrics,
    pub vcpu: VcpuMetrics,
    pub vmm: VmmMetrics,
    pub uart: SerialDeviceMetrics,
}

// The global variable used to increase the value of various metrics.
lazy_static! {
    pub static ref METRICS: FirecrackerMetrics = FirecrackerMetrics::default();
}

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

    use std::sync::Arc;
    use std::thread;

    #[test]
    fn test_metric() {
        let m1 = SimpleMetric::default();

        m1.inc();
        m1.inc();
        m1.add(5);
        m1.inc();

        assert_eq!(m1.count(), 8);

        let m2 = Arc::new(SharedMetric::default());

        // We're going to create a number of threads that will attempt to increase this metric
        // in parallel. If everything goes fine we still can't be sure the synchronization works,
        // but it something fails, then we definitely have a problem :-s

        const NUM_THREADS_TO_SPAWN: usize = 4;
        const NUM_INCREMENTS_PER_THREAD: usize = 100000;
        const M2_INITIAL_COUNT: usize = 123;

        m2.add(M2_INITIAL_COUNT);

        let mut v = Vec::with_capacity(NUM_THREADS_TO_SPAWN);

        for _ in 0..NUM_THREADS_TO_SPAWN {
            let r = m2.clone();
            v.push(thread::spawn(move || {
                for _ in 0..NUM_INCREMENTS_PER_THREAD {
                    r.inc();
                }
            }));
        }

        for handle in v {
            handle.join().unwrap();
        }

        assert_eq!(
            m2.count(),
            M2_INITIAL_COUNT + NUM_THREADS_TO_SPAWN * NUM_INCREMENTS_PER_THREAD
        );
    }
}
<commit_msg>metrics: reset counters with every flush<commit_after>use std::sync::atomic::{AtomicUsize, Ordering};

use chrono;
use serde::{Serialize, Serializer};

// The main design goals of this metrics system are:
// - Use lockless operations, preferably ones that don't require anything other than
// simple reads/writes being atomic.
// - Exploit interior mutability and atomics being Sync to allow all methods (including the ones
// which are effectively mutable) to be callable on a global non-mut static.
// - Rely on Serde to provide the actual serialization for logging the metrics.
// - Since all metrics start at 0, we implement the Default trait via derive for all of them,
// to avoid having to initialize everything by hand.

// Moreover, the value of a metric is currently NOT reset to 0 each time it's being logged. There
// are a number of advantages to this approach, including:
// - We don't have to introduce an additional write (to reset the value) from the thread which
// does to actual logging, so less synchronization effort is required.
// - We don't have to worry at all that much about losing some data if logging fails for a while
// (this could be a concern, I guess).
//
// If if turns out this approach is not really what we want, it's pretty easy to resort to something
// else, while working behind the same interface.

// This trait helps with writing less code. It has to be in scope (via an use directive) in order
// for its methods to be available to call on structs that implement it.
pub trait Metric {
    fn add(&self, value: usize);
    fn inc(&self) {
        self.add(1);
    }
    fn count(&self) -> usize;
}

// A simple metric is one meant to be incremented from a single thread, so it can use simple
// loads + stores. Loads are currently Relaxed everywhere, because we don't do anything besides
// logging the retrieved value (their outcome os not used to modify some memory location in a
// potentially inconsistent manner). There's no way currently to make sure a SimpleMetric is only
// incremented by a single thread, this has to be enforced via judicious use (although, every
// non-vCPU related metric is associated with a particular thread, so it shouldn't be that easy
// to misuse SimpleMetric fields).
#[derive(Default)]
pub struct SimpleMetric(AtomicUsize);

impl Metric for SimpleMetric {
    fn add(&self, value: usize) {
        let ref count = self.0;
        count.store(count.load(Ordering::Relaxed) + value, Ordering::Relaxed);
    }

    fn count(&self) -> usize {
        self.0.load(Ordering::Relaxed)
    }
}

impl Serialize for SimpleMetric {
    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
        // There's no serializer.serialize_usize() for some reason :(
        serializer.serialize_u64(self.0.load(Ordering::Relaxed) as u64)
    }
}

// A shared metric is one which is expected to be incremented from more than one thread, so more
// synchronization is necessary. It's currently used for vCPU metrics. An alternative here would be
// to have one instance of every metric for each thread (like a per-thread SimpleMetric), and to
// aggregate them when logging. However this probably overkill unless we have a lot of vCPUs
// incrementing metrics very often. Still, it's there if we ever need it :-s
#[derive(Default)]
// We will be keeping two values for each metric for being able to reset
// counters on each metric.
// 1st member - current value being updated
// 2nd member - old value that gets the current value whenever metrics is flushed to disk
pub struct SharedMetric(AtomicUsize, AtomicUsize);

impl Metric for SharedMetric {
    // While the order specified for this operation is still Relaxed, the actual instruction will
    // be an asm "LOCK; something" and thus atomic across multiple threads, simply because of the
    // fetch_and_add (as opposed to "store(load() + 1)") implementation for atomics.
    // TODO: would a stronger ordering make a difference here?
    fn add(&self, value: usize) {
        self.0.fetch_add(value, Ordering::Relaxed);
    }

    fn count(&self) -> usize {
        self.0.load(Ordering::Relaxed)
    }
}

impl Serialize for SharedMetric {
    /// Reset counters of each metrics. Here we suppose that Serialize's goal is to help with the
    /// flushing of metrics.
    /// !!! Any print of the metrics will also reset them. Use with caution !!!
    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
        // There's no serializer.serialize_usize() for some reason :(
        let snapshot = self.0.load(Ordering::Relaxed);
        let res = serializer.serialize_u64(snapshot as u64 - self.1.load(Ordering::Relaxed) as u64);

        if res.is_ok() {
            self.1.store(snapshot, Ordering::Relaxed);
        }
        res
    }
}

// The following structs are used to define a certain organization for the set of metrics we
// are interested in. Whenever the name of a field differs from its ideal textual representation
// in the serialized form, we can use the #[serde(rename = "name")] attribute to, well, rename it.

// Metrics related to the internal api server
#[derive(Default, Serialize)]
pub struct ApiServerMetrics {
    pub async_missed_actions_count: SharedMetric,
    pub async_outcome_fails: SharedMetric,
    pub async_vmm_send_timeout_count: SharedMetric,
    pub instance_info_fails: SharedMetric,
    pub sync_outcome_fails: SharedMetric,
    pub sync_vmm_send_timeout_count: SharedMetric,
}

// Metrics on GET Api Requests
#[derive(Default, Serialize)]
pub struct GetRequestsMetrics {
    pub action_info_count: SharedMetric,
    pub actions_count: SharedMetric,
    pub actions_fails: SharedMetric,
    pub instance_info_count: SharedMetric,
    pub machine_cfg_count: SharedMetric,
    pub machine_cfg_fails: SharedMetric,
}

// Metrics on PUT Api Requests
#[derive(Default, Serialize)]
pub struct PutRequestsMetrics {
    pub actions_count: SharedMetric,
    pub actions_fails: SharedMetric,
    pub boot_source_count: SharedMetric,
    pub boot_source_fails: SharedMetric,
    pub drive_fails: SharedMetric,
    pub drive_count: SharedMetric,
    pub logger_count: SharedMetric,
    pub logger_fails: SharedMetric,
    pub machine_cfg_count: SharedMetric,
    pub machine_cfg_fails: SharedMetric,
    pub network_count: SharedMetric,
    pub network_fails: SharedMetric,
}

#[derive(Default, Serialize)]
pub struct BlockDeviceMetrics {
    pub activate_fails: SharedMetric,
    pub cfg_fails: SharedMetric,
    pub event_fails: SharedMetric,
    pub execute_fails: SharedMetric,
    pub invalid_reqs_count: SharedMetric,
    pub flush_count: SharedMetric,
    pub queue_event_count: SharedMetric,
    pub rate_limiter_event_count: SharedMetric,
    pub read_count: SharedMetric,
    pub write_count: SharedMetric,
}

#[derive(Default, Serialize)]
pub struct I8042DeviceMetrics {
    pub error_count: SharedMetric,
    pub missed_read_count: SharedMetric,
    pub missed_write_count: SharedMetric,
    pub read_count: SharedMetric,
    pub reset_count: SharedMetric,
    pub write_count: SharedMetric,
}

#[derive(Default, Serialize)]
pub struct NetDeviceMetrics {
    pub activate_fails: SharedMetric,
    pub cfg_fails: SharedMetric,
    pub event_fails: SharedMetric,
    pub rx_bytes_count: SharedMetric,
    pub rx_packets_count: SharedMetric,
    pub rx_event_rate_limiter_count: SharedMetric,
    pub rx_fails: SharedMetric,
    pub rx_queue_event_count: SharedMetric,
    pub rx_tap_event_count: SharedMetric,
    pub tx_bytes_count: SharedMetric,
    pub tx_packets_count: SharedMetric,
    pub tx_rate_limiter_event_count: SharedMetric,
    pub tx_fails: SharedMetric,
    pub tx_queue_event_count: SharedMetric,
}

#[derive(Default, Serialize)]
pub struct SerialDeviceMetrics {
    pub error_count: SharedMetric,
    pub flush_count: SharedMetric,
    pub missed_read_count: SharedMetric,
    pub missed_write_count: SharedMetric,
    pub read_count: SharedMetric,
    pub write_count: SharedMetric,
}

#[derive(Default, Serialize)]
pub struct VcpuMetrics {
    pub eagain: SharedMetric,
    pub eintr: SharedMetric,
    pub exit_io_in: SharedMetric,
    pub exit_io_out: SharedMetric,
    pub exit_mmio_read: SharedMetric,
    pub exit_mmio_write: SharedMetric,
    pub failures: SharedMetric,
}

#[derive(Default, Serialize)]
pub struct VmmMetrics {
    pub device_events: SharedMetric,
}

// The sole purpose of this struct is to produce an UTC timestamp when an instance is serialized.
#[derive(Default)]
struct SerializeToUtcTimestampMs;

impl Serialize for SerializeToUtcTimestampMs {
    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
        serializer.serialize_i64(chrono::Utc::now().timestamp_millis())
    }
}

#[derive(Default, Serialize)]
pub struct FirecrackerMetrics {
    utc_timestamp_ms: SerializeToUtcTimestampMs,
    pub api_server: ApiServerMetrics,
    pub block: BlockDeviceMetrics,
    pub get_api_requests: GetRequestsMetrics,
    pub i8042: I8042DeviceMetrics,
    pub net: NetDeviceMetrics,
    pub put_api_requests: PutRequestsMetrics,
    pub vcpu: VcpuMetrics,
    pub vmm: VmmMetrics,
    pub uart: SerialDeviceMetrics,
}

// The global variable used to increase the value of various metrics.
lazy_static! {
    pub static ref METRICS: FirecrackerMetrics = FirecrackerMetrics::default();
}

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

    use std::sync::Arc;
    use std::thread;

    #[test]
    fn test_metric() {
        let m1 = SimpleMetric::default();

        m1.inc();
        m1.inc();
        m1.add(5);
        m1.inc();

        assert_eq!(m1.count(), 8);

        let m2 = Arc::new(SharedMetric::default());

        // We're going to create a number of threads that will attempt to increase this metric
        // in parallel. If everything goes fine we still can't be sure the synchronization works,
        // but it something fails, then we definitely have a problem :-s

        const NUM_THREADS_TO_SPAWN: usize = 4;
        const NUM_INCREMENTS_PER_THREAD: usize = 100000;
        const M2_INITIAL_COUNT: usize = 123;

        m2.add(M2_INITIAL_COUNT);

        let mut v = Vec::with_capacity(NUM_THREADS_TO_SPAWN);

        for _ in 0..NUM_THREADS_TO_SPAWN {
            let r = m2.clone();
            v.push(thread::spawn(move || {
                for _ in 0..NUM_INCREMENTS_PER_THREAD {
                    r.inc();
                }
            }));
        }

        for handle in v {
            handle.join().unwrap();
        }

        assert_eq!(
            m2.count(),
            M2_INITIAL_COUNT + NUM_THREADS_TO_SPAWN * NUM_INCREMENTS_PER_THREAD
        );
    }
}
<|endoftext|>


<commit_before>#[derive(Debug, PartialEq)]
pub struct RibonucleicAcid(String);

impl RibonucleicAcid {
    pub fn new(serial: &str) -> Self {
        RibonucleicAcid(serial.to_string())
    }
}

#[derive(Debug, PartialEq)]
pub struct DeoxyribonucleicAcid(String);

impl DeoxyribonucleicAcid {
    pub fn new(serial: &str) -> Self {
        DeoxyribonucleicAcid(serial.to_string())
    }

    pub fn to_rna(&self) -> Result<RibonucleicAcid, String> {
        let rna = self.0
            .chars()
            .filter_map(|c| match c {
                'A' => Some('U'),
                'C' => Some('G'),
                'G' => Some('C'),
                'T' => Some('A'),
                _ => None,
            })
            .collect::<String>();

        if rna.len() != self.0.len() {
            return Err("Invalid DNA serial".to_string());
        }

        Ok(RibonucleicAcid(rna))
    }
}
<commit_msg>Use loop instead of filter_map<commit_after>#[derive(Debug, PartialEq)]
pub struct RibonucleicAcid(String);

impl RibonucleicAcid {
    pub fn new(serial: &str) -> Self {
        RibonucleicAcid(serial.to_string())
    }
}

#[derive(Debug, PartialEq)]
pub struct DeoxyribonucleicAcid(String);

impl DeoxyribonucleicAcid {
    pub fn new(serial: &str) -> Self {
        DeoxyribonucleicAcid(serial.to_string())
    }

    pub fn to_rna(&self) -> Result<RibonucleicAcid, String> {
        let mut result = String::with_capacity(self.0.len());

        for c in self.0.chars() {
            match c {
                'A' => result.push('U'),
                'C' => result.push('G'),
                'G' => result.push('C'),
                'T' => result.push('A'),
                _ => return Err("Invalid DNA serial".to_string()),
            }
        }

        Ok(RibonucleicAcid(result))
    }
}
<|endoftext|>


<commit_before>//! See docs in build/expr/mod.rs

use crate::build::Builder;
use crate::thir::*;
use rustc_middle::mir::*;
use rustc_middle::ty::CanonicalUserTypeAnnotation;

impl<'a, 'tcx> Builder<'a, 'tcx> {
    /// Compile `expr`, yielding a compile-time constant. Assumes that
    /// `expr` is a valid compile-time constant!
    crate fn as_constant(&mut self, expr: &Expr<'_, 'tcx>) -> Constant<'tcx> {
        let this = self;
        let Expr { ty, temp_lifetime: _, span, ref kind } = *expr;
        match *kind {
            ExprKind::Scope { region_scope: _, lint_level: _, value } => this.as_constant(value),
            ExprKind::Literal { literal, user_ty, const_id: _ } => {
                let user_ty = user_ty.map(|user_ty| {
                    this.canonical_user_type_annotations.push(CanonicalUserTypeAnnotation {
                        span,
                        user_ty,
                        inferred_ty: ty,
                    })
                });
                assert_eq!(literal.ty, ty);
                Constant { span, user_ty, literal: literal.into() }
            }
            ExprKind::StaticRef { literal, .. } => Constant { span, user_ty: None, literal: literal.into() },
            ExprKind::ConstBlock { value } => {
                Constant { span: span, user_ty: None, literal: value.into() }
            }
            _ => span_bug!(span, "expression is not a valid constant {:?}", kind),
        }
    }
}
<commit_msg>Run rustfmt<commit_after>//! See docs in build/expr/mod.rs

use crate::build::Builder;
use crate::thir::*;
use rustc_middle::mir::*;
use rustc_middle::ty::CanonicalUserTypeAnnotation;

impl<'a, 'tcx> Builder<'a, 'tcx> {
    /// Compile `expr`, yielding a compile-time constant. Assumes that
    /// `expr` is a valid compile-time constant!
    crate fn as_constant(&mut self, expr: &Expr<'_, 'tcx>) -> Constant<'tcx> {
        let this = self;
        let Expr { ty, temp_lifetime: _, span, ref kind } = *expr;
        match *kind {
            ExprKind::Scope { region_scope: _, lint_level: _, value } => this.as_constant(value),
            ExprKind::Literal { literal, user_ty, const_id: _ } => {
                let user_ty = user_ty.map(|user_ty| {
                    this.canonical_user_type_annotations.push(CanonicalUserTypeAnnotation {
                        span,
                        user_ty,
                        inferred_ty: ty,
                    })
                });
                assert_eq!(literal.ty, ty);
                Constant { span, user_ty, literal: literal.into() }
            }
            ExprKind::StaticRef { literal, .. } => {
                Constant { span, user_ty: None, literal: literal.into() }
            }
            ExprKind::ConstBlock { value } => {
                Constant { span: span, user_ty: None, literal: value.into() }
            }
            _ => span_bug!(span, "expression is not a valid constant {:?}", kind),
        }
    }
}
<|endoftext|>


<commit_before>use std::cmp::max;
use std::io::Write;
use std::path::{Path, PathBuf};

use grep::matcher::{Captures, Matcher};
use grep::regex::{RegexMatcher, RegexMatcherBuilder};
use grep::searcher::{
    BinaryDetection, Searcher, SearcherBuilder, Sink, SinkContext, SinkContextKind, SinkMatch,
};
use ignore::Walk;
use lazy_static::lazy_static;
use regex::Regex;
use structopt::StructOpt;
use text_io::read;

#[derive(Debug, StructOpt)]
#[structopt(name = "fnr")]
/// Look for things, optionally replace them.
struct Opts {
    /// Run search in case insensitive mode
    #[structopt(short, long)]
    insensitive: bool,

    /// Print out replacements without actually performing them
    #[structopt(short, long)]
    dry_run: bool,

    #[structopt(short = "A", long, default_value = "0")]
    after: usize,
    #[structopt(short = "B", long, default_value = "0")]
    before: usize,
    #[structopt(short = "C", long, default_value = "0")]
    context: usize,

    #[structopt(short, long)]
    prompt: bool,

    /// What to search for.
    #[structopt(name = "FIND")]
    find: String,

    /// What to replace it with. Required unless `prompt` is set.
    #[structopt(name = "REPLACE")]
    replace: Option<String>,

    /// Locations to search. Current directory if not specified.
    #[structopt(name = "PATH", parse(from_os_str))]
    paths: Vec<PathBuf>,
}

#[derive(Debug, Clone)]
struct Line(u64, String);

#[derive(Debug)]
struct SearchMatch {
    line: Line,
    context_pre: Vec<Line>,
    context_post: Vec<Line>,
}

impl SearchMatch {
    fn as_change(&self) -> Change {
        return Change {
            line_number: self.line.0,
            new_line: self.line.1.clone(),
        };
    }
}

#[derive(Debug, Copy, Clone, PartialEq)]
enum MatchState {
    Init,
    Before,
    Match,
    After,
}

#[derive(Debug)]
struct SearchMatchCollector {
    state: MatchState,

    cur_context_pre: Vec<Line>,
    cur_context_post: Vec<Line>,
    cur_match_line: Option<Line>,

    matches: Vec<SearchMatch>,
}

impl SearchMatchCollector {
    fn new() -> SearchMatchCollector {
        return SearchMatchCollector {
            state: MatchState::Init,
            cur_match_line: None,
            cur_context_pre: Vec::new(),
            cur_context_post: Vec::new(),
            matches: Vec::new(),
        };
    }

    fn maybe_emit(&mut self) {
        if let Some(line) = &self.cur_match_line {
            // TODO: clones are wasteful here.
            let m = SearchMatch {
                line: line.clone(),
                context_pre: self.cur_context_pre.clone(),
                context_post: self.cur_context_post.clone(),
            };

            self.matches.push(m);
            self.reset();
        }
    }

    fn reset(&mut self) {
        self.state = MatchState::Init;

        self.cur_match_line = None;
        self.cur_context_pre.clear();
        self.cur_context_post.clear();
    }

    #[inline]
    fn transition(&mut self, next: MatchState) {
        println!("transition: {:?} -> {:?}", self, next);
        match (self.state, next) {
            // Beginning a new match or ending a previous one
            (MatchState::Match, MatchState::Before)       // Have before context, no after context
            | (MatchState::Match, MatchState::Match)      // No before context, no after context
            | (MatchState::After, MatchState::Before)     // Have before context, have after context
            | (MatchState::After, MatchState::Match) => { // Have after context, no before context
                self.maybe_emit();
            }

            (_prev, next) => {
                self.state = next;
            }
        }
    }

    fn collect(&mut self) -> &Vec<SearchMatch> {
        self.maybe_emit();
        return &self.matches;
    }
}

impl Drop for SearchMatchCollector {
    fn drop(&mut self) {
        self.maybe_emit();
    }
}

impl Sink for SearchMatchCollector {
    type Error = std::io::Error;

    fn matched(
        &mut self,
        _searcher: &Searcher,
        mat: &SinkMatch<'_>,
    ) -> Result<bool, std::io::Error> {
        self.transition(MatchState::Match);

        // TODO: handle errors
        let line = Line(
            mat.line_number().expect("grab line number"),
            String::from_utf8_lossy(mat.bytes()).to_string(),
        );

        self.cur_match_line = Some(line);

        Ok(true)
    }

    fn context(
        &mut self,
        _searcher: &Searcher,
        ctx: &SinkContext<'_>,
    ) -> Result<bool, std::io::Error> {
        // TODO: handle errors.
        // TODO: can we avoid the additional .to_string() by storing a COW instead?
        let line = Line(
            ctx.line_number().expect("grab line number"),
            String::from_utf8_lossy(ctx.bytes()).to_string(),
        );

        match ctx.kind() {
            &SinkContextKind::Before => {
                self.transition(MatchState::Before);
                self.cur_context_pre.push(line);
            }
            &SinkContextKind::After => {
                self.transition(MatchState::After);
                self.cur_context_post.push(line);
            }
            &SinkContextKind::Other => {}
        }

        Ok(true)
    }
}

trait Replacer {
    fn replace(&self, input: &str) -> Option<String>;
}

struct RegexReplacer<'a> {
    matcher: &'a RegexMatcher,
    template: &'a str,
}

#[derive(Debug)]
enum TemplatePart<'a> {
    Literal(&'a str),
    Capture(u64),
}

type Template<'a> = Vec<TemplatePart<'a>>;

// TODO: Clean this up
fn parse_template<'a>(t: &'a str) -> Template<'a> {
    lazy_static! {
        static ref RE: Regex = Regex::new(r"\$(\d+)").unwrap();
    }

    let capture_positions = RE.captures_iter(t).map(|cap| {
        let num = cap[1].parse::<u64>().expect("invalid capture number");
        let mat = cap.get(1).unwrap();
        (mat.start() - 1, mat.end(), num)
    });
    let mut pos = 0;
    let mut template = Vec::new();

    for (start, end, value) in capture_positions {
        if start > pos {
            template.push(TemplatePart::Literal(&t[pos..start]));
        }
        template.push(TemplatePart::Capture(value));
        pos = end;
    }

    if pos < t.len() {
        template.push(TemplatePart::Literal(&t[pos..t.len()]));
    }

    template
}

impl<'a> Replacer for RegexReplacer<'a> {
    fn replace(&self, input: &str) -> Option<String> {
        let mut caps = self.matcher.new_captures().unwrap();
        let mut dst = vec![];

        self.matcher
            .replace_with_captures(input.as_bytes(), &mut caps, &mut dst, |caps, dst| {
                caps.interpolate(
                    |name| self.matcher.capture_index(name),
                    input.as_bytes(),
                    self.template.as_bytes(),
                    dst,
                );
                false
            })
            .unwrap();

        Some(String::from_utf8_lossy(&dst).to_string())
    }
}

type MatchDecider = dyn Fn(&SearchMatch) -> ReplacementDecision;

struct Change {
    line_number: u64,
    // TODO: use slice here
    new_line: String,
}

struct SearchMatchProcessor<'a> {
    replacer: &'a dyn Replacer,
    acceptor: &'a MatchDecider,

    decide_all: Option<bool>,
}

impl<'a> SearchMatchProcessor<'a> {
    fn new(replacer: &'a dyn Replacer, acceptor: &'a MatchDecider) -> SearchMatchProcessor<'a> {
        return SearchMatchProcessor {
            replacer,
            acceptor,
            decide_all: None,
        };
    }

    fn handle_path(&mut self, path: &Path, matches: &Vec<SearchMatch>) {
        if matches.is_empty() || Some(false) == self.decide_all {
            return;
        }
        println!(
            "--- {}: {} match{}",
            path.display(),
            matches.len(),
            if matches.len() == 1 { "" } else { "es" }
        );
        let mut accept_remaining = false;

        let mut change_list = vec![];
        for m in matches {
            self.display(m);

            let change = if accept_remaining || Some(true) == self.decide_all {
                m.as_change()
            } else {
                match (self.acceptor)(m) {
                    ReplacementDecision::IgnoreThis => continue,
                    ReplacementDecision::IgnoreFile => break,
                    ReplacementDecision::IgnoreRest => {
                        self.decide_all = Some(false);
                        break;
                    }
                    ReplacementDecision::AcceptThis => m.as_change(),
                    ReplacementDecision::AcceptFile => {
                        accept_remaining = true;
                        m.as_change()
                    }
                    ReplacementDecision::EditThis => {
                        // TODO: implement this
                        m.as_change()
                    }
                }
            };
            change_list.push(change);
        }

        self.apply_changes(path, &change_list.into_iter());

        println!("");
    }

    fn display(&self, m: &SearchMatch) {
        for line in &m.context_pre {
            print!("    {}: {}", line.0, line.1);
        }

        // TODO: Multiple matches on same line
        print!("-   {}: {}", m.line.0, m.line.1);
        if let Some(replacement) = self.replacer.replace(&m.line.1) {
            print!("+   {}: {}", m.line.0, replacement);
        }

        // TODO: print gap between non-consecutive lines
        for line in &m.context_post {
            print!("    {}: {}", line.0, line.1);
        }
    }

    fn apply_changes(&self, path: &Path, changes: &dyn Iterator<Item = Change>) {}
}

#[derive(Debug)]
enum ReplacementDecision {
    IgnoreThis,
    IgnoreFile,
    IgnoreRest,
    AcceptThis,
    AcceptFile,
    EditThis,
}

fn prompt_for_decision() -> ReplacementDecision {
    loop {
        print!("Stage this replacement [Y,n,q,a,d,?] ");
        std::io::stdout().flush().unwrap();
        let line: String = read!("{}\n");
        // TODO: ^D should not result in acceptance

        return match line.as_str() {
            "y" | "Y" | "" => ReplacementDecision::AcceptThis,
            "n" => ReplacementDecision::IgnoreThis,
            // TODO: handle these somehow
            "q" => ReplacementDecision::IgnoreRest,
            "a" => ReplacementDecision::AcceptFile,
            "d" => ReplacementDecision::IgnoreFile,
            "e" => ReplacementDecision::EditThis,

            "?" | _ => {
                println!(
                    "
Y - replace this line
n - do not replace this line
q - quit; do not replace this line or any remaining ones
a - replace this line and all remaining ones in this file
d - do not replace this line nor any remaining ones in this file
e - edit this replacement
? - show help
"
                );
                continue;
            }
        };
    }
}

fn main() {
    let opts: Opts = Opts::from_args();
    println!("Parsed opts: {:?}", opts);

    let matcher = RegexMatcherBuilder::new()
        .case_insensitive(opts.insensitive)
        .build(&opts.find)
        .expect("bad pattern");

    let mut searcher = SearcherBuilder::new()
        .binary_detection(BinaryDetection::quit(0x00))
        .line_number(true)
        .before_context(max(opts.context, opts.before))
        .after_context(max(opts.context, opts.after))
        .build();

    // TODO: Confirm that template does not reference more capture groups than exist.
    let template_str = &opts.replace.unwrap();
    let replacer = RegexReplacer {
        matcher: &matcher,
        template: template_str,
    };

    let should_prompt = opts.prompt;
    let acceptor = if opts.dry_run {
        |_: &SearchMatch| ReplacementDecision::IgnoreThis
    } else if opts.prompt {
        |_: &SearchMatch| prompt_for_decision()
    } else {
        |_: &SearchMatch| ReplacementDecision::AcceptThis
    };
    let mut proc = SearchMatchProcessor::new(&replacer, &acceptor);

    let paths = if opts.paths.is_empty() {
        vec![Path::new("./").to_owned()]
    } else {
        opts.paths
    };

    for path in paths {
        // TODO: Support files as well as directories here
        for dir_entry in Walk::new(path) {
            let dir_entry = dir_entry.unwrap();
            let path = dir_entry.path();
            if !path.is_file() {
                continue;
            }

            let mut sink = SearchMatchCollector::new();

            searcher
                .search_path(&matcher, dir_entry.path(), &mut sink)
                .expect("search failed");

            let matches = sink.collect();
            proc.handle_path(path, matches);
        }
    }

    println!("All done.");
}
<commit_msg>Apply changes to temporary file<commit_after>use std::cmp::max;
use std::fs::File;
use std::io::{BufRead, BufReader, BufWriter, Write};
use std::path::{Path, PathBuf};

use grep::matcher::{Captures, Matcher};
use grep::regex::{RegexMatcher, RegexMatcherBuilder};
use grep::searcher::{
    BinaryDetection, Searcher, SearcherBuilder, Sink, SinkContext, SinkContextKind, SinkMatch,
};
use ignore::Walk;
use lazy_static::lazy_static;
use regex::Regex;
use structopt::StructOpt;
use text_io::read;

#[derive(Debug, StructOpt)]
#[structopt(name = "fnr")]
/// Look for things, optionally replace them.
struct Opts {
    /// Run search in case insensitive mode
    #[structopt(short, long)]
    insensitive: bool,

    /// Print out replacements without actually performing them
    #[structopt(short, long)]
    dry_run: bool,

    #[structopt(short = "A", long, default_value = "0")]
    after: usize,
    #[structopt(short = "B", long, default_value = "0")]
    before: usize,
    #[structopt(short = "C", long, default_value = "0")]
    context: usize,

    #[structopt(short, long)]
    prompt: bool,

    /// What to search for.
    #[structopt(name = "FIND")]
    find: String,

    /// What to replace it with. Required unless `prompt` is set.
    #[structopt(name = "REPLACE")]
    replace: Option<String>,

    /// Locations to search. Current directory if not specified.
    #[structopt(name = "PATH", parse(from_os_str))]
    paths: Vec<PathBuf>,
}

#[derive(Debug, Clone)]
struct Line(u64, String);

#[derive(Debug)]
struct SearchMatch {
    line: Line,
    context_pre: Vec<Line>,
    context_post: Vec<Line>,
}

impl SearchMatch {
    fn as_change(&self, line: String) -> Change {
        return Change {
            line_number: self.line.0,
            new_line: line,
        };
    }
}

#[derive(Debug, Copy, Clone, PartialEq)]
enum MatchState {
    Init,
    Before,
    Match,
    After,
}

#[derive(Debug)]
struct SearchMatchCollector {
    state: MatchState,

    cur_context_pre: Vec<Line>,
    cur_context_post: Vec<Line>,
    cur_match_line: Option<Line>,

    matches: Vec<SearchMatch>,
}

impl SearchMatchCollector {
    fn new() -> SearchMatchCollector {
        return SearchMatchCollector {
            state: MatchState::Init,
            cur_match_line: None,
            cur_context_pre: Vec::new(),
            cur_context_post: Vec::new(),
            matches: Vec::new(),
        };
    }

    fn maybe_emit(&mut self) {
        if let Some(line) = &self.cur_match_line {
            // TODO: clones are wasteful here.
            let m = SearchMatch {
                line: line.clone(),
                context_pre: self.cur_context_pre.clone(),
                context_post: self.cur_context_post.clone(),
            };

            self.matches.push(m);
            self.reset();
        }
    }

    fn reset(&mut self) {
        self.state = MatchState::Init;

        self.cur_match_line = None;
        self.cur_context_pre.clear();
        self.cur_context_post.clear();
    }

    #[inline]
    fn transition(&mut self, next: MatchState) {
        match (self.state, next) {
            // Beginning a new match or ending a previous one
            (MatchState::Match, MatchState::Before)       // Have before context, no after context
            | (MatchState::Match, MatchState::Match)      // No before context, no after context
            | (MatchState::After, MatchState::Before)     // Have before context, have after context
            | (MatchState::After, MatchState::Match) => { // Have after context, no before context
                self.maybe_emit();
            }

            (_prev, next) => {
                self.state = next;
            }
        }
    }

    fn collect(&mut self) -> &Vec<SearchMatch> {
        self.maybe_emit();
        return &self.matches;
    }
}

impl Drop for SearchMatchCollector {
    fn drop(&mut self) {
        self.maybe_emit();
    }
}

impl Sink for SearchMatchCollector {
    type Error = std::io::Error;

    fn matched(
        &mut self,
        _searcher: &Searcher,
        mat: &SinkMatch<'_>,
    ) -> Result<bool, std::io::Error> {
        self.transition(MatchState::Match);

        // TODO: handle errors
        let line = Line(
            mat.line_number().expect("grab line number"),
            String::from_utf8_lossy(mat.bytes()).to_string(),
        );

        self.cur_match_line = Some(line);

        Ok(true)
    }

    fn context(
        &mut self,
        _searcher: &Searcher,
        ctx: &SinkContext<'_>,
    ) -> Result<bool, std::io::Error> {
        // TODO: handle errors.
        // TODO: can we avoid the additional .to_string() by storing a COW instead?
        let line = Line(
            ctx.line_number().expect("grab line number"),
            String::from_utf8_lossy(ctx.bytes()).to_string(),
        );

        match ctx.kind() {
            &SinkContextKind::Before => {
                self.transition(MatchState::Before);
                self.cur_context_pre.push(line);
            }
            &SinkContextKind::After => {
                self.transition(MatchState::After);
                self.cur_context_post.push(line);
            }
            &SinkContextKind::Other => {}
        }

        Ok(true)
    }
}

trait Replacer {
    fn replace(&self, input: &str) -> Option<String>;
}

struct RegexReplacer<'a> {
    matcher: &'a RegexMatcher,
    template: &'a str,
}

#[derive(Debug)]
enum TemplatePart<'a> {
    Literal(&'a str),
    Capture(u64),
}

type Template<'a> = Vec<TemplatePart<'a>>;

// TODO: Clean this up
fn parse_template<'a>(t: &'a str) -> Template<'a> {
    lazy_static! {
        static ref RE: Regex = Regex::new(r"\$(\d+)").unwrap();
    }

    let capture_positions = RE.captures_iter(t).map(|cap| {
        let num = cap[1].parse::<u64>().expect("invalid capture number");
        let mat = cap.get(1).unwrap();
        (mat.start() - 1, mat.end(), num)
    });
    let mut pos = 0;
    let mut template = Vec::new();

    for (start, end, value) in capture_positions {
        if start > pos {
            template.push(TemplatePart::Literal(&t[pos..start]));
        }
        template.push(TemplatePart::Capture(value));
        pos = end;
    }

    if pos < t.len() {
        template.push(TemplatePart::Literal(&t[pos..t.len()]));
    }

    template
}

impl<'a> Replacer for RegexReplacer<'a> {
    fn replace(&self, input: &str) -> Option<String> {
        let mut caps = self.matcher.new_captures().unwrap();
        let mut dst = vec![];

        self.matcher
            .replace_with_captures(input.as_bytes(), &mut caps, &mut dst, |caps, dst| {
                caps.interpolate(
                    |name| self.matcher.capture_index(name),
                    input.as_bytes(),
                    self.template.as_bytes(),
                    dst,
                );
                false
            })
            .unwrap();

        Some(String::from_utf8_lossy(&dst).to_string())
    }
}

type MatchDecider = dyn Fn(&SearchMatch) -> ReplacementDecision;

#[derive(Debug)]
struct Change {
    line_number: u64,
    // TODO: use slice here
    new_line: String,
}

struct SearchMatchProcessor<'a> {
    replacer: &'a dyn Replacer,
    acceptor: &'a MatchDecider,

    decide_all: Option<bool>,
}

impl<'a> SearchMatchProcessor<'a> {
    fn new(replacer: &'a dyn Replacer, acceptor: &'a MatchDecider) -> SearchMatchProcessor<'a> {
        return SearchMatchProcessor {
            replacer,
            acceptor,
            decide_all: None,
        };
    }

    fn handle_path(&mut self, path: &Path, matches: &Vec<SearchMatch>) {
        if matches.is_empty() || Some(false) == self.decide_all {
            return;
        }
        println!(
            "--- {}: {} match{}",
            path.display(),
            matches.len(),
            if matches.len() == 1 { "" } else { "es" }
        );
        let mut accept_remaining = false;

        let mut change_list = vec![];
        for m in matches {
            let replacement = if let Some(r) = self.replacer.replace(&m.line.1) {
                r
            } else {
                println!("TODO: replacer failed on line");
                continue;
            };
            self.display(m, &replacement);

            let change = if accept_remaining || Some(true) == self.decide_all {
                m.as_change(replacement)
            } else {
                match (self.acceptor)(m) {
                    ReplacementDecision::IgnoreThis => continue,
                    ReplacementDecision::IgnoreFile => break,
                    ReplacementDecision::IgnoreRest => {
                        self.decide_all = Some(false);
                        break;
                    }
                    ReplacementDecision::AcceptThis => m.as_change(replacement),
                    ReplacementDecision::AcceptFile => {
                        accept_remaining = true;
                        m.as_change(replacement)
                    }
                    ReplacementDecision::EditThis => {
                        // TODO: implement this
                        m.as_change(replacement)
                    }
                }
            };
            change_list.push(change);
        }

        if !change_list.is_empty() {
            self.apply_changes(path, &mut change_list);
        }

        println!("");
    }

    fn display(&self, m: &SearchMatch, replacement: &str) {
        for line in &m.context_pre {
            print!("    {}: {}", line.0, line.1);
        }

        // TODO: Multiple matches on same line
        print!("-   {}: {}", m.line.0, m.line.1);
        print!("+   {}: {}", m.line.0, replacement);

        // TODO: print gap between non-consecutive lines
        for line in &m.context_post {
            print!("    {}: {}", line.0, line.1);
        }
    }

    fn apply_changes(&self, path: &Path, mut changes: &[Change]) {
        let src = File::open(path).expect("couldn't open file");
        let reader = BufReader::new(src);
        // TODO: fix this
        let dst = File::create("/tmp/replaceme").expect("couldn't create file");
        let mut writer = BufWriter::new(dst);

        let mut line_num = 0;
        for line in reader.lines() {
            let line = line.unwrap();
            line_num += 1;

            if !changes.is_empty() && changes[0].line_number == line_num {
                writer.write(changes[0].new_line.as_bytes());
                changes = &changes[1..];
            } else {
                // TODO: don't strip off newlines to begin with
                writer.write(line.as_bytes());
                writer.write(b"\n");
            };
        }
    }
}

#[derive(Debug)]
enum ReplacementDecision {
    IgnoreThis,
    IgnoreFile,
    IgnoreRest,
    AcceptThis,
    AcceptFile,
    EditThis,
}

fn prompt_for_decision() -> ReplacementDecision {
    loop {
        print!("Stage this replacement [Y,n,q,a,d,?] ");
        std::io::stdout().flush().unwrap();
        let line: String = read!("{}\n");
        // TODO: ^D should not result in acceptance

        return match line.as_str() {
            "y" | "Y" | "" => ReplacementDecision::AcceptThis,
            "n" => ReplacementDecision::IgnoreThis,
            "q" => ReplacementDecision::IgnoreRest,
            "a" => ReplacementDecision::AcceptFile,
            "d" => ReplacementDecision::IgnoreFile,
            "e" => ReplacementDecision::EditThis,

            "?" | _ => {
                println!(
                    "
Y - replace this line
n - do not replace this line
q - quit; do not replace this line or any remaining ones
a - replace this line and all remaining ones in this file
d - do not replace this line nor any remaining ones in this file
e - edit this replacement
? - show help
"
                );
                continue;
            }
        };
    }
}

fn main() {
    let opts: Opts = Opts::from_args();
    println!("Parsed opts: {:?}", opts);

    let matcher = RegexMatcherBuilder::new()
        .case_insensitive(opts.insensitive)
        .build(&opts.find)
        .expect("bad pattern");

    let mut searcher = SearcherBuilder::new()
        .binary_detection(BinaryDetection::quit(0x00))
        .line_number(true)
        .before_context(max(opts.context, opts.before))
        .after_context(max(opts.context, opts.after))
        .build();

    // TODO: Confirm that template does not reference more capture groups than exist.
    let template_str = &opts.replace.unwrap();
    let replacer = RegexReplacer {
        matcher: &matcher,
        template: template_str,
    };

    let should_prompt = opts.prompt;
    let acceptor = if opts.dry_run {
        |_: &SearchMatch| ReplacementDecision::IgnoreThis
    } else if opts.prompt {
        |_: &SearchMatch| prompt_for_decision()
    } else {
        |_: &SearchMatch| ReplacementDecision::AcceptThis
    };
    let mut proc = SearchMatchProcessor::new(&replacer, &acceptor);

    let paths = if opts.paths.is_empty() {
        vec![Path::new("./").to_owned()]
    } else {
        opts.paths
    };

    for path in paths {
        // TODO: Support files as well as directories here
        for dir_entry in Walk::new(path) {
            let dir_entry = dir_entry.unwrap();
            let path = dir_entry.path();
            if !path.is_file() {
                continue;
            }

            let mut sink = SearchMatchCollector::new();

            searcher
                .search_path(&matcher, dir_entry.path(), &mut sink)
                .expect("search failed");

            let matches = sink.collect();
            proc.handle_path(path, matches);
        }
    }

    println!("All done.");
}
<|endoftext|>


<commit_before>use byteorder::{BigEndian, ByteOrder};
use std::io::Cursor;

pub struct IPv4Packet<'a> {
    raw: &'a mut [u8],
    header: IPv4Header<'a>,
}

pub struct IPv4Header<'a> {
    raw: &'a mut [u8],
    data: IPv4HeaderData,
}

struct IPv4HeaderData {
    version: u8,
    header_length: u8,
    total_length: u16,
    protocol: Protocol,
    source: u32,
    destination: u32,
}

#[derive(Debug, PartialEq)]
enum Protocol {
    TCP,
    UDP,
    OTHER,
}

impl<'a> IPv4Packet<'a> {
    fn new(raw: &'a mut [u8]) -> IPv4Packet<'a> {
        let header = IPv4Header::new(raw);
        IPv4Packet {
            raw: raw,
            header: header,
        }
    }
}

impl<'a> IPv4Header<'a> {
    fn new(raw: &'a mut [u8]) -> IPv4Header<'a> {
        let data = IPv4Header::parse(raw);
        IPv4Header {
            raw: raw,
            data: data,
        }
    }

    fn parse(raw: &[u8]) -> IPv4HeaderData {
        IPv4HeaderData {
            version: raw[0] >> 4,
            header_length: (raw[0] & 0xf) << 2,
            total_length: BigEndian::read_u16(&raw[2..4]),
            protocol: match raw[9] {
                6 => Protocol::TCP,
                17 => Protocol::UDP,
                _ => Protocol::OTHER
            },
            source: BigEndian::read_u32(&raw[12..16]),
            destination: BigEndian::read_u32(&raw[16..20]),
        }
    }

    fn set_total_length(&mut self, total_length: u16) {
        self.data.total_length = total_length;
        BigEndian::write_u16(&mut self.raw[2..4], total_length);
    }

    fn set_source(&mut self, source: u32) {
        self.data.source = source;
        BigEndian::write_u32(&mut self.raw[12..16], source);
    }

    fn set_destination(&mut self, destination: u32) {
        self.data.destination = destination;
        BigEndian::write_u32(&mut self.raw[16..20], destination);
    }

    fn switch_source_and_destination(&mut self) {
        let source = self.data.source;
        let destination = self.data.destination;
        self.set_source(destination);
        self.set_destination(source);
    }

    fn compute_checksum(&mut self) {
        // reset checksum field
        self.set_checksum(0);

        let j = self.data.header_length as usize / 2;
        let mut sum = (0..j).map(|i| {
            let range = 2*i..2*(i+1);
            BigEndian::read_u16(&self.raw[range]) as u32
        }).sum::<u32>();
        while (sum & !0xffff) != 0 {
            sum = (sum & 0xffff) + (sum >> 16);
        }

        self.set_checksum(!sum as u16);
    }

    fn get_checksum(&mut self) -> u16 {
        BigEndian::read_u16(&self.raw[10..12])
    }

    fn set_checksum(&mut self, checksum: u16) {
        BigEndian::write_u16(&mut self.raw[10..12], checksum);
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::io::Cursor;
    use byteorder::{BigEndian, WriteBytesExt};

    fn create_header() -> Vec<u8> {
        let mut raw: Vec<u8> = vec![];
        raw.reserve(20);
        raw.write_u8(4u8 << 4 | 5).unwrap(); // version_and_ihl
        raw.write_u8(0).unwrap(); //ToS
        raw.write_u16::<BigEndian>(28).unwrap(); // total length
        raw.write_u32::<BigEndian>(0).unwrap(); // id_flags_fragment_offset
        raw.write_u8(0).unwrap(); // TTL
        raw.write_u8(17).unwrap(); // protocol (UDP)
        raw.write_u16::<BigEndian>(0).unwrap(); // checksum
        raw.write_u32::<BigEndian>(0x12345678).unwrap(); // source address
        raw.write_u32::<BigEndian>(0x42424242).unwrap(); // destination address
        raw
    }

    #[test]
    fn parse_packet_header() {
        let raw = create_header();
        let data = IPv4Header::parse(&raw[..]);
        assert_eq!(4, data.version);
        assert_eq!(20, data.header_length);
        assert_eq!(28, data.total_length);
        assert_eq!(Protocol::UDP, data.protocol);
        assert_eq!(0x12345678, data.source);
        assert_eq!(0x42424242, data.destination);
    }

    #[test]
    fn edit_packet_header() {
        let mut raw = create_header();
        let mut header = IPv4Header::new(&mut raw[..]);

        header.set_source(0x87654321);
        header.set_destination(0x24242424);
        header.set_total_length(42);
        assert_eq!(0x87654321, header.data.source);
        assert_eq!(0x24242424, header.data.destination);
        assert_eq!(42, header.data.total_length);

        // assert that the buffer has been modified
        let raw_source = BigEndian::read_u32(&header.raw[12..16]);
        let raw_destination = BigEndian::read_u32(&header.raw[16..20]);
        let raw_total_length = BigEndian::read_u16(&header.raw[2..4]);
        assert_eq!(0x87654321, raw_source);
        assert_eq!(0x24242424, raw_destination);
        assert_eq!(42, raw_total_length);

        header.switch_source_and_destination();

        assert_eq!(0x24242424, header.data.source);
        assert_eq!(0x87654321, header.data.destination);

        let raw_source = BigEndian::read_u32(&header.raw[12..16]);
        let raw_destination = BigEndian::read_u32(&header.raw[16..20]);
        assert_eq!(0x24242424, raw_source);
        assert_eq!(0x87654321, raw_destination);
    }

    #[test]
    fn compute_checksum() {
        let mut raw = create_header();
        let mut header = IPv4Header::new(&mut raw[..]);

        // set a fake checksum value to assert that it is correctly computed
        header.set_checksum(0x79);

        header.compute_checksum();

        let mut sum = 0x4500u32 + 0x001Cu32 + 0x0000u32 + 0x0000u32 + 0x0011u32
                    + 0x0000u32 + 0x1234u32 + 0x5678u32 + 0x4242u32 + 0x4242u32;
        while (sum & !0xffff) != 0 {
            sum = (sum & 0xffff) + (sum >> 16);
        }
        let sum = !sum as u16;
        assert_eq!(sum, header.get_checksum());
    }
}
<commit_msg>Do not loan raw to IPv4Header<commit_after>use byteorder::{BigEndian, ByteOrder};
use std::io::Cursor;

pub struct IPv4Packet<'a> {
    raw: &'a mut [u8],
    header: IPv4Header,
}

struct IPv4Header {
    version: u8,
    header_length: u8,
    total_length: u16,
    protocol: Protocol,
    source: u32,
    destination: u32,
}

#[derive(Debug, PartialEq)]
enum Protocol {
    TCP,
    UDP,
    OTHER,
}

impl<'a> IPv4Packet<'a> {
    fn new(raw: &'a mut [u8]) -> IPv4Packet<'a> {
        let header = IPv4Header::parse(raw);
        IPv4Packet {
            raw: raw,
            header: header,
        }
    }
}

impl IPv4Header {
    fn parse(raw: &[u8]) -> IPv4Header {
        IPv4Header {
            version: raw[0] >> 4,
            header_length: (raw[0] & 0xf) << 2,
            total_length: BigEndian::read_u16(&raw[2..4]),
            protocol: match raw[9] {
                6 => Protocol::TCP,
                17 => Protocol::UDP,
                _ => Protocol::OTHER
            },
            source: BigEndian::read_u32(&raw[12..16]),
            destination: BigEndian::read_u32(&raw[16..20]),
        }
    }

    fn set_total_length(&mut self, raw: &mut [u8], total_length: u16) {
        self.total_length = total_length;
        BigEndian::write_u16(&mut raw[2..4], total_length);
    }

    fn set_source(&mut self, raw: &mut [u8], source: u32) {
        self.source = source;
        BigEndian::write_u32(&mut raw[12..16], source);
    }

    fn set_destination(&mut self, raw: &mut [u8], destination: u32) {
        self.destination = destination;
        BigEndian::write_u32(&mut raw[16..20], destination);
    }

    fn switch_source_and_destination(&mut self, raw: &mut [u8]) {
        let source = self.source;
        let destination = self.destination;
        self.set_source(raw, destination);
        self.set_destination(raw, source);
    }

    fn compute_checksum(&mut self, raw: &mut [u8]) {
        // reset checksum field
        self.set_checksum(raw, 0);

        let j = self.header_length as usize / 2;
        let mut sum = (0..j).map(|i| {
            let range = 2*i..2*(i+1);
            BigEndian::read_u16(&raw[range]) as u32
        }).sum::<u32>();
        while (sum & !0xffff) != 0 {
            sum = (sum & 0xffff) + (sum >> 16);
        }

        self.set_checksum(raw, !sum as u16);
    }

    fn get_checksum(&mut self, raw: &mut [u8]) -> u16 {
        BigEndian::read_u16(&raw[10..12])
    }

    fn set_checksum(&mut self, raw: &mut [u8], checksum: u16) {
        BigEndian::write_u16(&mut raw[10..12], checksum);
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::io::Cursor;
    use byteorder::{BigEndian, WriteBytesExt};

    fn create_header() -> Vec<u8> {
        let mut raw: Vec<u8> = vec![];
        raw.reserve(20);
        raw.write_u8(4u8 << 4 | 5).unwrap(); // version_and_ihl
        raw.write_u8(0).unwrap(); //ToS
        raw.write_u16::<BigEndian>(28).unwrap(); // total length
        raw.write_u32::<BigEndian>(0).unwrap(); // id_flags_fragment_offset
        raw.write_u8(0).unwrap(); // TTL
        raw.write_u8(17).unwrap(); // protocol (UDP)
        raw.write_u16::<BigEndian>(0).unwrap(); // checksum
        raw.write_u32::<BigEndian>(0x12345678).unwrap(); // source address
        raw.write_u32::<BigEndian>(0x42424242).unwrap(); // destination address
        raw
    }

    #[test]
    fn parse_packet_header() {
        let raw = create_header();
        let data = IPv4Header::parse(&raw[..]);
        assert_eq!(4, data.version);
        assert_eq!(20, data.header_length);
        assert_eq!(28, data.total_length);
        assert_eq!(Protocol::UDP, data.protocol);
        assert_eq!(0x12345678, data.source);
        assert_eq!(0x42424242, data.destination);
    }

    #[test]
    fn edit_packet_header() {
        let raw = &mut create_header()[..];
        let mut header = IPv4Header::parse(raw);

        header.set_source(raw, 0x87654321);
        header.set_destination(raw, 0x24242424);
        header.set_total_length(raw, 42);
        assert_eq!(0x87654321, header.source);
        assert_eq!(0x24242424, header.destination);
        assert_eq!(42, header.total_length);

        // assert that the buffer has been modified
        let raw_source = BigEndian::read_u32(&raw[12..16]);
        let raw_destination = BigEndian::read_u32(&raw[16..20]);
        let raw_total_length = BigEndian::read_u16(&raw[2..4]);
        assert_eq!(0x87654321, raw_source);
        assert_eq!(0x24242424, raw_destination);
        assert_eq!(42, raw_total_length);

        header.switch_source_and_destination(raw);

        assert_eq!(0x24242424, header.source);
        assert_eq!(0x87654321, header.destination);

        let raw_source = BigEndian::read_u32(&raw[12..16]);
        let raw_destination = BigEndian::read_u32(&raw[16..20]);
        assert_eq!(0x24242424, raw_source);
        assert_eq!(0x87654321, raw_destination);
    }

    #[test]
    fn compute_checksum() {
        let mut raw = &mut create_header()[..];
        let mut header = IPv4Header::parse(raw);

        // set a fake checksum value to assert that it is correctly computed
        header.set_checksum(raw, 0x79);

        header.compute_checksum(raw);

        let mut sum = 0x4500u32 + 0x001Cu32 + 0x0000u32 + 0x0000u32 + 0x0011u32
                    + 0x0000u32 + 0x1234u32 + 0x5678u32 + 0x4242u32 + 0x4242u32;
        while (sum & !0xffff) != 0 {
            sum = (sum & 0xffff) + (sum >> 16);
        }
        let sum = !sum as u16;
        assert_eq!(sum, header.get_checksum(raw));
    }
}
<|endoftext|>


<commit_before>extern crate proc_macro;
extern crate syn;
#[macro_use]
extern crate quote;

use proc_macro::TokenStream;

#[proc_macro_derive(BitPattern, attributes(bits, bits_default, bits_errtype))]
pub fn bitpattern(input: TokenStream) -> TokenStream {
    // Parse the input tokens into a syntax tree
    let input: syn::DeriveInput = syn::parse(input).unwrap();

    let mut bits_default: Option<syn::Expr> = None;
    for attr in &input.attrs {
        if attr.path.leading_colon.is_none() && attr.path.segments.len() == 1 &&
           attr.path.segments[0].ident.as_ref() == "bits_default" {
            if bits_default.is_some() {
                panic!("Only one bits_default allowed");
            }

            let default_val = attr.interpret_meta().expect("Failed to parse bits_default attribute");

            if let syn::Meta::NameValue(nameval) = default_val {
                if let syn::Lit::Str(litstr) = nameval.lit {
                    bits_default = Some(litstr.parse().expect("Failed to parse bits_default attribute contents"));
                } else {
                    panic!("bits_default attribute must be set to a string")
                }
            } else {
                panic!("Malformed bits_default attribute");
            }
        }
    }

    let mut bits_errtype: Option<syn::TypeParam> = None;
    for attr in &input.attrs {
        if attr.path.leading_colon.is_none() && attr.path.segments.len() == 1 &&
           attr.path.segments[0].ident.as_ref() == "bits_errtype" {
            if bits_errtype.is_some() {
                panic!("Only one bits_errtype allowed");
            }

            let errtype_val = attr.interpret_meta().expect("Failed to parse bits_errtype attribute");

            if let syn::Meta::NameValue(nameval) = errtype_val {
                if let syn::Lit::Str(litstr) = nameval.lit {
                    bits_errtype = Some(litstr.parse().expect("Failed to parse bits_errtype attribute contents"));
                } else {
                    panic!("bits_errtype attribute must be set to a string")
                }
            } else {
                panic!("Malformed bits_errtype attribute");
            }
        }
    }

    if let (input_ident, syn::Data::Enum(dataenum)) = (input.ident, input.data) {
        // Ignore enums with no variants
        if dataenum.variants.len() == 0 {
            println!("Warning: BitPattern used on enum with no variants");
            return (quote!{}).into();
        }

        let mut var_bits = Vec::new();

        for var in &dataenum.variants {
            if var.fields != syn::Fields::Unit {
                panic!("All variants must be a unit variant");
            }

            let mut found_bits_attr = false;
            for attr in &var.attrs {
                if attr.path.leading_colon.is_none() && attr.path.segments.len() == 1 &&
                   attr.path.segments[0].ident.as_ref() == "bits" {

                    if found_bits_attr {
                        panic!("Only one bits attribute allowed");
                    }

                    found_bits_attr = true;

                    let bits_val = attr.interpret_meta().expect("Failed to parse bits attribute");

                    if let syn::Meta::NameValue(nameval) = bits_val {
                        if let syn::Lit::Str(litstr) = nameval.lit {
                            var_bits.push((var.ident, litstr.value()));
                        } else {
                            panic!("bits attribute must be set to a string")
                        }
                    } else {
                        panic!("Malformed bits attribute");
                    }
                }
            }

            if !found_bits_attr {
                panic!("All variants need a bits attribute");
            }
        }

        let bits_len = var_bits[0].1.len();
        for x in &var_bits[1..] {
            if x.1.len() != bits_len {
                panic!("All bits need to be the same length");
            }
        }

        for x in &var_bits {
            for c in x.1.chars() {
                if c != '0'  && c != '1' && c != 'x' && c != 'X' {
                    panic!("Illegal character in bits attribute");
                }
            }
        }

        // Literally a list of bool tokens, repeated <number of bits> times
        let bools = var_bits[0].1.chars().map(|_| quote! {bool});
        let bools2 = var_bits[0].1.chars().map(|_| quote! {bool});
        // The name of the enum, repeated <number of variants> times
        let idents_dummy_list = var_bits.iter().map(|_| input_ident);
        let idents_dummy_list2 = var_bits.iter().map(|_| input_ident);
        // The names of each variant
        let var_names = var_bits.iter().map(|x| x.0);
        let var_names2 = var_bits.iter().map(|x| x.0);

        // The list of values for encoding
        let encode_values = var_bits.iter().map(|x|
            x.1.chars().map(|c|
                match c {
                    '0'|'x' => quote! {false},
                    '1'|'X' => quote! {true},
                    _ => unreachable!(),
                }
            )
        );

        // The list of values for decoding
        let decode_values = var_bits.iter().map(|x|
            x.1.chars().map(|c|
                match c {
                    '0' => quote! {false},
                    '1' => quote! {true},
                    'x'|'X' => quote! {_},
                    _ => unreachable!(),
                }
            )
        );

        let mut encode_tokens = quote! {
            impl #input_ident {
                pub fn encode(&self) -> (#(#bools),*) {
                    match *self {
                        #(#idents_dummy_list::#var_names => (#(#encode_values),*)),*
                    }
                }
            }
        };

        let deocde_tokens = if bits_errtype.is_none() {
            quote! {
                impl #input_ident {
                    pub fn decode(x: (#(#bools2),*)) -> Self {
                        match x {
                            #((#(#decode_values),*) => #idents_dummy_list2::#var_names2),*
                            #(,_ => #bits_default)*
                        }
                    }
                }
            }
        } else {
            let bits_errtype = bits_errtype.unwrap();
            quote! {
                impl #input_ident {
                    pub fn decode(x: (#(#bools2),*)) -> Result<Self, #bits_errtype> {
                        Ok(match x {
                            #((#(#decode_values),*) => #idents_dummy_list2::#var_names2),*
                            #(,_ => return Err(#bits_default))*
                        })
                    }
                }
            }
        };

        encode_tokens.append_all(deocde_tokens);
        encode_tokens.into()
    } else {
        panic!("BitPattern must be used on an enum");
    }
}
<commit_msg>bittwidder: Refactor out attribute parsing<commit_after>extern crate proc_macro;
extern crate syn;
#[macro_use]
extern crate quote;

use proc_macro::TokenStream;

fn parse_string_attr_helper<T, F>(attrs: &[syn::Attribute], attr_name: &str, cb: F) -> Option<T>
    where F: FnOnce(syn::LitStr) -> T {

    let mut ret = None;
    for attr in attrs {
        if attr.path.leading_colon.is_none() && attr.path.segments.len() == 1 &&
           attr.path.segments[0].ident.as_ref() == attr_name {
            if ret.is_some() {
                panic!("Only one {} allowed", attr_name);
            }

            let attr_val = attr.interpret_meta();
            if attr_val.is_none() {
                panic!("Failed to parse {} attribute", attr_name);
            }

            if let syn::Meta::NameValue(nameval) = attr_val.unwrap() {
                if let syn::Lit::Str(litstr) = nameval.lit {
                    ret = Some(litstr);
                } else {
                    panic!("{} attribute must be set to a string", attr_name)
                }
            } else {
                panic!("Malformed {} attribute", attr_name);
            }
        }
    }

    ret.map(cb)
}

#[proc_macro_derive(BitPattern, attributes(bits, bits_default, bits_errtype))]
pub fn bitpattern(input: TokenStream) -> TokenStream {
    // Parse the input tokens into a syntax tree
    let input: syn::DeriveInput = syn::parse(input).unwrap();

    let bits_default: Option<syn::Expr> = parse_string_attr_helper(&input.attrs, "bits_default",
        |x| x.parse().expect("Failed to parse bits_default attribute contents"));

    let bits_errtype: Option<syn::TypeParam> = parse_string_attr_helper(&input.attrs, "bits_errtype",
        |x| x.parse().expect("Failed to parse bits_errtype attribute contents"));

    if let (input_ident, syn::Data::Enum(dataenum)) = (input.ident, input.data) {
        // Ignore enums with no variants
        if dataenum.variants.len() == 0 {
            println!("Warning: BitPattern used on enum with no variants");
            return (quote!{}).into();
        }

        let mut var_bits = Vec::new();

        for var in &dataenum.variants {
            if var.fields != syn::Fields::Unit {
                panic!("All variants must be a unit variant");
            }

            let bits_attr = parse_string_attr_helper(&var.attrs, "bits", |x| x)
                .expect("All variants need a bits attribute");
            var_bits.push((var.ident, bits_attr.value()));
        }

        let bits_len = var_bits[0].1.len();
        for x in &var_bits[1..] {
            if x.1.len() != bits_len {
                panic!("All bits need to be the same length");
            }
        }

        for x in &var_bits {
            for c in x.1.chars() {
                if c != '0'  && c != '1' && c != 'x' && c != 'X' {
                    panic!("Illegal character in bits attribute");
                }
            }
        }

        // Literally a list of bool tokens, repeated <number of bits> times
        let bools = var_bits[0].1.chars().map(|_| quote! {bool});
        let bools2 = var_bits[0].1.chars().map(|_| quote! {bool});
        // The name of the enum, repeated <number of variants> times
        let idents_dummy_list = var_bits.iter().map(|_| input_ident);
        let idents_dummy_list2 = var_bits.iter().map(|_| input_ident);
        // The names of each variant
        let var_names = var_bits.iter().map(|x| x.0);
        let var_names2 = var_bits.iter().map(|x| x.0);

        // The list of values for encoding
        let encode_values = var_bits.iter().map(|x|
            x.1.chars().map(|c|
                match c {
                    '0'|'x' => quote! {false},
                    '1'|'X' => quote! {true},
                    _ => unreachable!(),
                }
            )
        );

        // The list of values for decoding
        let decode_values = var_bits.iter().map(|x|
            x.1.chars().map(|c|
                match c {
                    '0' => quote! {false},
                    '1' => quote! {true},
                    'x'|'X' => quote! {_},
                    _ => unreachable!(),
                }
            )
        );

        let mut encode_tokens = quote! {
            impl #input_ident {
                pub fn encode(&self) -> (#(#bools),*) {
                    match *self {
                        #(#idents_dummy_list::#var_names => (#(#encode_values),*)),*
                    }
                }
            }
        };

        let deocde_tokens = if bits_errtype.is_none() {
            quote! {
                impl #input_ident {
                    pub fn decode(x: (#(#bools2),*)) -> Self {
                        match x {
                            #((#(#decode_values),*) => #idents_dummy_list2::#var_names2),*
                            #(,_ => #bits_default)*
                        }
                    }
                }
            }
        } else {
            let bits_errtype = bits_errtype.unwrap();
            quote! {
                impl #input_ident {
                    pub fn decode(x: (#(#bools2),*)) -> Result<Self, #bits_errtype> {
                        Ok(match x {
                            #((#(#decode_values),*) => #idents_dummy_list2::#var_names2),*
                            #(,_ => return Err(#bits_default))*
                        })
                    }
                }
            }
        };

        encode_tokens.append_all(deocde_tokens);
        encode_tokens.into()
    } else {
        panic!("BitPattern must be used on an enum");
    }
}
<|endoftext|>


<commit_before>use crate::core::resolver::ResolveOpts;
use crate::core::{Shell, Workspace};
use crate::ops;
use crate::util::CargoResult;
use failure::Fail;
use opener;
use std::collections::HashMap;
use std::error::Error;
use std::path::Path;
use std::process::Command;

/// Strongly typed options for the `cargo doc` command.
#[derive(Debug)]
pub struct DocOptions<'a> {
    /// Whether to attempt to open the browser after compiling the docs
    pub open_result: bool,
    /// Options to pass through to the compiler
    pub compile_opts: ops::CompileOptions<'a>,
}

/// Main method for `cargo doc`.
pub fn doc(ws: &Workspace<'_>, options: &DocOptions<'_>) -> CargoResult<()> {
    let specs = options.compile_opts.spec.to_package_id_specs(ws)?;
    let opts = ResolveOpts::new(
        /*dev_deps*/ true,
        &options.compile_opts.features,
        options.compile_opts.all_features,
        !options.compile_opts.no_default_features,
    );
    let ws_resolve = ops::resolve_ws_with_opts(ws, opts, &specs)?;

    let ids = specs
        .iter()
        .map(|s| s.query(ws_resolve.targeted_resolve.iter()))
        .collect::<CargoResult<Vec<_>>>()?;
    let pkgs = ws_resolve.pkg_set.get_many(ids)?;

    let mut lib_names = HashMap::new();
    let mut bin_names = HashMap::new();
    let mut names = Vec::new();
    for package in &pkgs {
        for target in package.targets().iter().filter(|t| t.documented()) {
            if target.is_lib() {
                if let Some(prev) = lib_names.insert(target.crate_name(), package) {
                    failure::bail!(
                        "The library `{}` is specified by packages `{}` and \
                         `{}` but can only be documented once. Consider renaming \
                         or marking one of the targets as `doc = false`.",
                        target.crate_name(),
                        prev,
                        package
                    );
                }
            } else if let Some(prev) = bin_names.insert(target.crate_name(), package) {
                failure::bail!(
                    "The binary `{}` is specified by packages `{}` and \
                     `{}` but can be documented only once. Consider renaming \
                     or marking one of the targets as `doc = false`.",
                    target.crate_name(),
                    prev,
                    package
                );
            }
            names.push(target.crate_name());
        }
    }

    let compilation = ops::compile(ws, &options.compile_opts)?;

    if options.open_result {
        let name = match names.first() {
            Some(s) => s.to_string(),
            None => return Ok(()),
        };
        let path = compilation
            .root_output
            .with_file_name("doc")
            .join(&name)
            .join("index.html");
        if path.exists() {
            let mut shell = options.compile_opts.config.shell();
            shell.status("Opening", path.display())?;
            open_docs(&path, &mut shell)?;
        }
    }

    Ok(())
}

fn open_docs(path: &Path, shell: &mut Shell) -> CargoResult<()> {
    match std::env::var_os("BROWSER") {
        Some(browser) => {
            if let Err(e) = Command::new(&browser).arg(path).status() {
                shell.warn(format!(
                    "Couldn't open docs with {}: {}",
                    browser.to_string_lossy(),
                    e.description()
                ))?;
            }
        }
        None => {
            if let Err(e) = opener::open(&path) {
                shell.warn(format!("Couldn't open docs: {}", e))?;
                for cause in (&e as &dyn Fail).iter_chain() {
                    shell.warn(format!("Caused by:\n {}", cause))?;
                }
            }
        }
    };

    Ok(())
}
<commit_msg>Remove use of deprecated Error::description method<commit_after>use crate::core::resolver::ResolveOpts;
use crate::core::{Shell, Workspace};
use crate::ops;
use crate::util::CargoResult;
use failure::Fail;
use opener;
use std::collections::HashMap;
use std::path::Path;
use std::process::Command;

/// Strongly typed options for the `cargo doc` command.
#[derive(Debug)]
pub struct DocOptions<'a> {
    /// Whether to attempt to open the browser after compiling the docs
    pub open_result: bool,
    /// Options to pass through to the compiler
    pub compile_opts: ops::CompileOptions<'a>,
}

/// Main method for `cargo doc`.
pub fn doc(ws: &Workspace<'_>, options: &DocOptions<'_>) -> CargoResult<()> {
    let specs = options.compile_opts.spec.to_package_id_specs(ws)?;
    let opts = ResolveOpts::new(
        /*dev_deps*/ true,
        &options.compile_opts.features,
        options.compile_opts.all_features,
        !options.compile_opts.no_default_features,
    );
    let ws_resolve = ops::resolve_ws_with_opts(ws, opts, &specs)?;

    let ids = specs
        .iter()
        .map(|s| s.query(ws_resolve.targeted_resolve.iter()))
        .collect::<CargoResult<Vec<_>>>()?;
    let pkgs = ws_resolve.pkg_set.get_many(ids)?;

    let mut lib_names = HashMap::new();
    let mut bin_names = HashMap::new();
    let mut names = Vec::new();
    for package in &pkgs {
        for target in package.targets().iter().filter(|t| t.documented()) {
            if target.is_lib() {
                if let Some(prev) = lib_names.insert(target.crate_name(), package) {
                    failure::bail!(
                        "The library `{}` is specified by packages `{}` and \
                         `{}` but can only be documented once. Consider renaming \
                         or marking one of the targets as `doc = false`.",
                        target.crate_name(),
                        prev,
                        package
                    );
                }
            } else if let Some(prev) = bin_names.insert(target.crate_name(), package) {
                failure::bail!(
                    "The binary `{}` is specified by packages `{}` and \
                     `{}` but can be documented only once. Consider renaming \
                     or marking one of the targets as `doc = false`.",
                    target.crate_name(),
                    prev,
                    package
                );
            }
            names.push(target.crate_name());
        }
    }

    let compilation = ops::compile(ws, &options.compile_opts)?;

    if options.open_result {
        let name = match names.first() {
            Some(s) => s.to_string(),
            None => return Ok(()),
        };
        let path = compilation
            .root_output
            .with_file_name("doc")
            .join(&name)
            .join("index.html");
        if path.exists() {
            let mut shell = options.compile_opts.config.shell();
            shell.status("Opening", path.display())?;
            open_docs(&path, &mut shell)?;
        }
    }

    Ok(())
}

fn open_docs(path: &Path, shell: &mut Shell) -> CargoResult<()> {
    match std::env::var_os("BROWSER") {
        Some(browser) => {
            if let Err(e) = Command::new(&browser).arg(path).status() {
                shell.warn(format!(
                    "Couldn't open docs with {}: {}",
                    browser.to_string_lossy(),
                    e
                ))?;
            }
        }
        None => {
            if let Err(e) = opener::open(&path) {
                shell.warn(format!("Couldn't open docs: {}", e))?;
                for cause in (&e as &dyn Fail).iter_chain() {
                    shell.warn(format!("Caused by:\n {}", cause))?;
                }
            }
        }
    };

    Ok(())
}
<|endoftext|>


<commit_before>use gl;
use super::vertex::{Attrib, VertexArray, VertexBuffer, IndexBuffer};

pub fn gen_cube(length: f32, offset: f32, a_position: Attrib) -> VertexArray {
    // 8 corners in a cube
    let corner: [(f32,f32,f32); 8] = {
        let l = offset;
        let m = length + offset;

        [
            (m, m, m),
            (m, m, l),
            (m, l, m),
            (m, l, l),
            (l, m, m),
            (l, m, l),
            (l, l, m),
            (l, l, l),
        ]
    };

    // Which corners to copy to the vertex buffer for each face.
    // In order to maintain distinct normals for each face,
    // corners cannot be shared among different faces.
    static VERT_IDX: [usize; 4*6] = [
        0,1,2,3,
        4,0,6,2,
        5,4,7,6,
        1,5,3,7,
        5,1,4,0,
        3,7,2,6
    ];

    // Which vertices to form triangle faces from
    static IDX: [u8; 6*6] = [
        0,1,2, 1,3,2,
        4,5,6, 5,7,6,
        8,9,10, 9,11,10,
        12,13,14, 13,15,14,
        16,17,18, 17,19,18,
        20,21,22, 21,23,22
    ];

    let mut buffer: Vec<f32> = Vec::new();
    for &i in VERT_IDX.iter() {
        let (x,y,z) = corner[i];
        buffer.push_all(&[x,y,z]);
    }

    VertexArray::new(|vao_ctx| {
        let ibo = IndexBuffer::rc_from_slice::<u8>(IDX.as_slice(), gl::UNSIGNED_BYTE);
        let vbo = VertexBuffer::rc_from_slice::<f32>(buffer.as_slice());

        vao_ctx.bind_ibo(ibo);
        vao_ctx.bind_vbo(vbo, |vbo_ctx| {
            vbo_ctx.attr_pointer(a_position, 3, gl::FLOAT, 0, 0);
        });
    })
}

pub fn gen_tileset(tiles_width: u32, tiles_height: u32, a_position: Attrib, a_texture_uv: Attrib) -> VertexArray {
    let num_tiles = tiles_width * tiles_height;
    let buffer: Vec<f32> = (0..num_tiles).flat_map(|_| {
        static POS: [f32; 12] = [0.0,0.0, 1.0,0.0, 0.0,1.0, 1.0,0.0, 1.0,1.0, 0.0,1.0];
        POS.iter().map(|o| *o)
    }).chain((0..num_tiles).flat_map(|i| {
        let (x, y) = (i % tiles_width, i / tiles_width);
        let (fx1, fy1) = (x as f32 / tiles_width as f32, y as f32 / tiles_height as f32);
        let (fx2, fy2) = ((x+1) as f32 / tiles_width as f32, (y+1) as f32 / tiles_height as f32);

        let tex = vec![fx1,fy1, fx2,fy1, fx1,fy2, fx2,fy1, fx2,fy2, fx1,fy2];
        tex.into_iter()
    })).collect();

    VertexArray::new(|vao_ctx| {
        let vbo = VertexBuffer::rc_from_slice::<f32>(buffer.as_slice());
        vao_ctx.bind_vbo(vbo, |vbo_ctx| {
            vbo_ctx.attr_pointer(a_position, 2, gl::FLOAT, 0, 0);
            vbo_ctx.attr_pointer(a_texture_uv, 2, gl::FLOAT, 0, num_tiles as usize*4*2*6);
        });
    })
}
<commit_msg>Use new slicing syntax<commit_after>use gl;
use super::vertex::{Attrib, VertexArray, VertexBuffer, IndexBuffer};

pub fn gen_cube(length: f32, offset: f32, a_position: Attrib) -> VertexArray {
    // 8 corners in a cube
    let corner: [(f32,f32,f32); 8] = {
        let l = offset;
        let m = length + offset;

        [
            (m, m, m),
            (m, m, l),
            (m, l, m),
            (m, l, l),
            (l, m, m),
            (l, m, l),
            (l, l, m),
            (l, l, l),
        ]
    };

    // Which corners to copy to the vertex buffer for each face.
    // In order to maintain distinct normals for each face,
    // corners cannot be shared among different faces.
    static VERT_IDX: [usize; 4*6] = [
        0,1,2,3,
        4,0,6,2,
        5,4,7,6,
        1,5,3,7,
        5,1,4,0,
        3,7,2,6
    ];

    // Which vertices to form triangle faces from
    static IDX: [u8; 6*6] = [
        0,1,2, 1,3,2,
        4,5,6, 5,7,6,
        8,9,10, 9,11,10,
        12,13,14, 13,15,14,
        16,17,18, 17,19,18,
        20,21,22, 21,23,22
    ];

    let mut buffer: Vec<f32> = Vec::new();
    for &i in VERT_IDX.iter() {
        let (x,y,z) = corner[i];
        buffer.push_all(&[x,y,z]);
    }

    VertexArray::new(|vao_ctx| {
        let ibo = IndexBuffer::rc_from_slice::<u8>(&IDX[], gl::UNSIGNED_BYTE);
        let vbo = VertexBuffer::rc_from_slice::<f32>(&buffer[]);

        vao_ctx.bind_ibo(ibo);
        vao_ctx.bind_vbo(vbo, |vbo_ctx| {
            vbo_ctx.attr_pointer(a_position, 3, gl::FLOAT, 0, 0);
        });
    })
}

pub fn gen_tileset(tiles_width: u32, tiles_height: u32, a_position: Attrib, a_texture_uv: Attrib) -> VertexArray {
    let num_tiles = tiles_width * tiles_height;
    let buffer: Vec<f32> = (0..num_tiles).flat_map(|_| {
        static POS: [f32; 12] = [0.0,0.0, 1.0,0.0, 0.0,1.0, 1.0,0.0, 1.0,1.0, 0.0,1.0];
        POS.iter().map(|o| *o)
    }).chain((0..num_tiles).flat_map(|i| {
        let (x, y) = (i % tiles_width, i / tiles_width);
        let (fx1, fy1) = (x as f32 / tiles_width as f32, y as f32 / tiles_height as f32);
        let (fx2, fy2) = ((x+1) as f32 / tiles_width as f32, (y+1) as f32 / tiles_height as f32);

        let tex = vec![fx1,fy1, fx2,fy1, fx1,fy2, fx2,fy1, fx2,fy2, fx1,fy2];
        tex.into_iter()
    })).collect();

    VertexArray::new(|vao_ctx| {
        let vbo = VertexBuffer::rc_from_slice::<f32>(&buffer[]);
        vao_ctx.bind_vbo(vbo, |vbo_ctx| {
            vbo_ctx.attr_pointer(a_position, 2, gl::FLOAT, 0, 0);
            vbo_ctx.attr_pointer(a_texture_uv, 2, gl::FLOAT, 0, num_tiles as usize*4*2*6);
        });
    })
}
<|endoftext|>