<commit_before>//! Domain name-related traits.

use std::cmp;
use bytes::BytesMut;
use ::bits::compose::{Compose, Compress};
use super::dname::Dname;
use super::label::Label;
use super::relative::RelativeDname;

//------------ ToLabelIter ---------------------------------------------------

/// A type that can produce an iterator over its labels.
///
/// This trait is used as a trait bound for both [`ToDname`] and
/// [`ToRelativeDname`]. It is separate since it has to be generic over the
/// lifetime of the label reference but we don’t want to have this lifetime
/// parameter pollute those traits.
pub trait ToLabelIter<'a> {
    /// The type of the iterator over the labels.
    ///
    /// This iterator types needs to be double ended so that we can deal with
    /// name suffixes.
    type LabelIter: Iterator<Item=&'a Label> + DoubleEndedIterator;

    /// Returns an iterator over the labels.
    fn iter_labels(&'a self) -> Self::LabelIter;

    /// Determines whether `base` is a prefix of `self`.
    fn starts_with<N: ToLabelIter<'a>>(&'a self, base: &'a N) -> bool {
        let mut self_iter = self.iter_labels();
        let mut base_iter = base.iter_labels();
        loop {
            match (self_iter.next(), base_iter.next()) {
                (Some(sl), Some(bl)) => {
                    if sl != bl { return false }
                }
                (_, None) => return true,
                (None, Some(_)) => return false,
            }
        }
    }

    /// Determines whether `base` is a suffix of `self`.
    fn ends_with<N: ToLabelIter<'a>>(&'a self, base: &'a N) -> bool {
        let mut self_iter = self.iter_labels();
        let mut base_iter = base.iter_labels();
        loop {
            match (self_iter.next_back(), base_iter.next_back()) {
                (Some(sl), Some(bl)) => {
                    if sl != bl { return false }
                }
                (_, None) => return true,
                (None, Some(_)) =>  return false
            }
        }
    }
}

impl<'a, 'b, N: ToLabelIter<'b>> ToLabelIter<'b> for &'a N {
    type LabelIter = N::LabelIter;

    fn iter_labels(&'b self) -> Self::LabelIter {
        (*self).iter_labels()
    }
}

//------------ ToRelativeDname -----------------------------------------------

/// A type that represents a relative domain name.
///
/// In order to be a relative domain name, a type needs to be able to
/// provide a sequence of labels via an iterator where the last label is not
/// the root label. The type also needs to be able to compose the wire-format
/// representation of the domain name it represents which must not be longer
/// than 255 characters.
///
/// The most important types implementing this trait are [`RelativeDname`]
/// and [`Chain<L,R>`] where `R` is a `ToRelativeDname` itself.
///
/// [`Chain<L, R>`]: struct.Chain.html
/// [`RelativeDname`]: struct.RelativeDname.html
pub trait ToRelativeDname: Compose + for<'a> ToLabelIter<'a> {
    /// Creates an uncompressed value of the domain name.
    ///
    /// The method has a default implementation that composes the name into
    /// a new buffer and returns this buffer. If the implementing type can
    /// create a `RelativeDname` more efficiently, then it should provide its
    /// own implementation.
    fn to_name(&self) -> RelativeDname {
        let mut bytes = BytesMut::with_capacity(self.compose_len());
        self.compose(&mut bytes);
        unsafe {
            RelativeDname::from_bytes_unchecked(bytes.freeze())
        }
    }

    /// Returns a byte slice of the content if possible.
    ///
    /// This method can is used to optimize comparision operations between
    /// two values that are indeed flat names.
    fn as_flat_slice(&self) -> Option<&[u8]> {
        None
    }

    fn name_eq<N: ToRelativeDname>(&self, other: &N) -> bool {
        if let (Some(left), Some(right)) = (self.as_flat_slice(),
                                            other.as_flat_slice()) {
            left.eq_ignore_ascii_case(right)
        }
        else {
            self.iter_labels().eq(other.iter_labels())
        }
    }

    fn name_cmp<N: ToRelativeDname>(&self, other: &N) -> cmp::Ordering {
        let mut self_iter = self.iter_labels();
        let mut other_iter = other.iter_labels();
        loop {
            match (self_iter.next_back(), other_iter.next_back()) {
                (Some(left), Some(right)) => {
                    match left.cmp(right) {
                        cmp::Ordering::Equal => {}
                        res => return res
                    }
                }
                (None, Some(_)) => return cmp::Ordering::Less,
                (Some(_), None) => return cmp::Ordering::Greater,
                (None, None) => return cmp::Ordering::Equal
            }
        }
    }
}

impl<'a, N: ToRelativeDname + 'a> ToRelativeDname for &'a N { }

//------------ ToDname -------------------------------------------------------

/// A type that represents an absolute domain name.
///
/// An absolute domain name is a sequence of labels where the last label is
/// the root label and where the wire-format representation is not longer than
/// 255 characters. Implementers of this trait need to provide access to the
/// label sequence via an iterator and know how to compose the wire-format
/// representation into a buffer.
///
/// The most common types implementing this trait are [`Dname`],
/// [`ParsedDname`], and [`Chain<L, R>`] where `R` is `ToDname` itself.
///
/// [`Chain<L, R>`]: struct.Chain.html
/// [`Dname`]: struct.Dname.html
/// [`ParsedDname`]: struct.ParsedDname.html
pub trait ToDname: Compose + Compress + for<'a> ToLabelIter<'a> {
    /// Creates an uncompressed value of the domain name.
    ///
    /// The method has a default implementation that composes the name into
    /// a new buffer and returns this buffer. If the implementing type can
    /// create a `Dname` more efficiently, then it should provide its
    /// own implementation.
    fn to_name(&self) -> Dname {
        let mut bytes = BytesMut::with_capacity(self.compose_len());
        self.compose(&mut bytes);
        unsafe {
            Dname::from_bytes_unchecked(bytes.freeze())
        }
    }

    /// Returns a byte slice of the content if possible.
    ///
    /// This method can is used to optimize comparision operations between
    /// two values that are indeed flat names.
    fn as_flat_slice(&self) -> Option<&[u8]> {
        None
    }

    fn name_eq<N: ToDname>(&self, other: &N) -> bool {
        if let (Some(left), Some(right)) = (self.as_flat_slice(),
                                            other.as_flat_slice()) {
            left.eq_ignore_ascii_case(right)
        }
        else {
            self.iter_labels().eq(other.iter_labels())
        }
    }

    /// Returns the ordering between `self` and `other`.
    ///
    /// This method can be used to implement both `PartialOrd` and `Ord` on
    /// types implementing `ToDname` since a blanket implementation for all
    /// pairs of `ToDname`s is currently not possible.
    ///
    /// Domain name order is determined according to the ‘canonical DNS
    /// name order’ as defined in [section 6.1 of RFC 4034][RFC4034-6.1].
    ///
    /// [RFC4034-6.1]: https://tools.ietf.org/html/rfc4034#section-6.1
    fn name_cmp<N: ToDname>(&self, other: &N) -> cmp::Ordering {
        let mut self_iter = self.iter_labels();
        let mut other_iter = other.iter_labels();
        loop {
            match (self_iter.next_back(), other_iter.next_back()) {
                (Some(left), Some(right)) => {
                    match left.cmp(right) {
                        cmp::Ordering::Equal => {}
                        res => return res
                    }
                }
                (None, Some(_)) => return cmp::Ordering::Less,
                (Some(_), None) => return cmp::Ordering::Greater,
                (None, None) => return cmp::Ordering::Equal
            }
        }
    }
}

impl<'a, N: ToDname + 'a> ToDname for &'a N { }

<commit_msg>Document the name traits.<commit_after>//! Domain name-related traits.
//!
/// This is a private module. Its public traits are re-exported by the parent.

use std::cmp;
use bytes::BytesMut;
use ::bits::compose::{Compose, Compress};
use super::dname::Dname;
use super::label::Label;
use super::relative::RelativeDname;

//------------ ToLabelIter ---------------------------------------------------

/// A type that can produce an iterator over its labels.
///
/// This trait is used as a trait bound for both [`ToDname`] and
/// [`ToRelativeDname`]. It is separate since it has to be generic over the
/// lifetime of the label reference but we don’t want to have this lifetime
/// parameter pollute those traits.
///
/// [`ToDname`]: trait.ToDname.html
/// [`ToRelativeDname`]: trait ToRelativeDname.html
pub trait ToLabelIter<'a> {
    /// The type of the iterator over the labels.
    ///
    /// This iterator types needs to be double ended so that we can deal with
    /// name suffixes.
    type LabelIter: Iterator<Item=&'a Label> + DoubleEndedIterator;

    /// Returns an iterator over the labels.
    fn iter_labels(&'a self) -> Self::LabelIter;

    /// Determines whether `base` is a prefix of `self`.
    fn starts_with<N: ToLabelIter<'a>>(&'a self, base: &'a N) -> bool {
        let mut self_iter = self.iter_labels();
        let mut base_iter = base.iter_labels();
        loop {
            match (self_iter.next(), base_iter.next()) {
                (Some(sl), Some(bl)) => {
                    if sl != bl { return false }
                }
                (_, None) => return true,
                (None, Some(_)) => return false,
            }
        }
    }

    /// Determines whether `base` is a suffix of `self`.
    fn ends_with<N: ToLabelIter<'a>>(&'a self, base: &'a N) -> bool {
        let mut self_iter = self.iter_labels();
        let mut base_iter = base.iter_labels();
        loop {
            match (self_iter.next_back(), base_iter.next_back()) {
                (Some(sl), Some(bl)) => {
                    if sl != bl { return false }
                }
                (_, None) => return true,
                (None, Some(_)) =>  return false
            }
        }
    }
}

impl<'a, 'b, N: ToLabelIter<'b>> ToLabelIter<'b> for &'a N {
    type LabelIter = N::LabelIter;

    fn iter_labels(&'b self) -> Self::LabelIter {
        (*self).iter_labels()
    }
}

//------------ ToDname -------------------------------------------------------

/// A type that represents an absolute domain name.
///
/// An absolute domain name is a sequence of labels where the last label is
/// the root label and where the wire-format representation is not longer than
/// 255 characters. Implementers of this trait need to provide access to the
/// label sequence via an iterator and know how to compose the wire-format
/// representation into a buffer.
///
/// The most common types implementing this trait are [`Dname`],
/// [`ParsedDname`], and [`Chain<L, R>`] where `R` is `ToDname` itself.
///
/// [`Chain<L, R>`]: struct.Chain.html
/// [`Dname`]: struct.Dname.html
/// [`ParsedDname`]: struct.ParsedDname.html
pub trait ToDname: Compose + Compress + for<'a> ToLabelIter<'a> {
    /// Creates an uncompressed value of the domain name.
    ///
    /// The method has a default implementation that composes the name into
    /// a new buffer and returns this buffer. If the implementing type can
    /// create a `Dname` more efficiently, then it should provide its
    /// own implementation.
    fn to_name(&self) -> Dname {
        let mut bytes = BytesMut::with_capacity(self.compose_len());
        self.compose(&mut bytes);
        unsafe {
            Dname::from_bytes_unchecked(bytes.freeze())
        }
    }

    /// Returns a byte slice of the content if possible.
    ///
    /// If a value stores the domain name as one single byte sequence, it
    /// should return a reference to this sequence here. If the name is
    /// composed from multiple such sequences, it should return `None`.
    ///
    /// This method is used to optimize comparision operations between
    /// two values that are indeed flat names.
    fn as_flat_slice(&self) -> Option<&[u8]> {
        None
    }

    /// Tests whether `self` and `other` are equal.
    ///
    /// This method can be used to implement `PartialEq` on types implementing
    /// `ToDname` since a blanket implementation for all pairs of `ToDname`
    /// is currently impossible.
    ///
    /// Domain names are compared ignoring ASCII case.
    fn name_eq<N: ToDname>(&self, other: &N) -> bool {
        if let (Some(left), Some(right)) = (self.as_flat_slice(),
                                            other.as_flat_slice()) {
            left.eq_ignore_ascii_case(right)
        }
        else {
            self.iter_labels().eq(other.iter_labels())
        }
    }

    /// Returns the ordering between `self` and `other`.
    ///
    /// This method can be used to implement both `PartialOrd` and `Ord` on
    /// types implementing `ToDname` since a blanket implementation for all
    /// pairs of `ToDname`s is currently not possible.
    ///
    /// Domain name order is determined according to the ‘canonical DNS
    /// name order’ as defined in [section 6.1 of RFC 4034][RFC4034-6.1].
    ///
    /// [RFC4034-6.1]: https://tools.ietf.org/html/rfc4034#section-6.1
    fn name_cmp<N: ToDname>(&self, other: &N) -> cmp::Ordering {
        let mut self_iter = self.iter_labels();
        let mut other_iter = other.iter_labels();
        loop {
            match (self_iter.next_back(), other_iter.next_back()) {
                (Some(left), Some(right)) => {
                    match left.cmp(right) {
                        cmp::Ordering::Equal => {}
                        res => return res
                    }
                }
                (None, Some(_)) => return cmp::Ordering::Less,
                (Some(_), None) => return cmp::Ordering::Greater,
                (None, None) => return cmp::Ordering::Equal
            }
        }
    }
}

impl<'a, N: ToDname + 'a> ToDname for &'a N { }

//------------ ToRelativeDname -----------------------------------------------

/// A type that represents a relative domain name.
///
/// In order to be a relative domain name, a type needs to be able to
/// provide a sequence of labels via an iterator where the last label is not
/// the root label. The type also needs to be able to compose the wire-format
/// representation of the domain name it represents which must not be longer
/// than 254 characters. This limit has been chosen so that by attaching the
/// one character long root label, a valid absolute name can be constructed
/// from the relative name.
///
/// The most important types implementing this trait are [`RelativeDname`]
/// and [`Chain<L,R>`] where `R` is a `ToRelativeDname` itself.
///
/// [`Chain<L, R>`]: struct.Chain.html
/// [`RelativeDname`]: struct.RelativeDname.html
pub trait ToRelativeDname: Compose + for<'a> ToLabelIter<'a> {
    /// Creates an uncompressed value of the domain name.
    ///
    /// The method has a default implementation that composes the name into
    /// a new buffer and returns this buffer. If the implementing type can
    /// create a `RelativeDname` more efficiently, then it should provide its
    /// own implementation.
    fn to_name(&self) -> RelativeDname {
        let mut bytes = BytesMut::with_capacity(self.compose_len());
        self.compose(&mut bytes);
        unsafe {
            RelativeDname::from_bytes_unchecked(bytes.freeze())
        }
    }

    /// Returns a byte slice of the content if possible.
    ///
    /// This method can is used to optimize comparision operations between
    /// two values that are indeed flat names.
    fn as_flat_slice(&self) -> Option<&[u8]> {
        None
    }

    /// Tests whether `self` and `other` are equal.
    ///
    /// This method can be used to implement `PartialEq` on types implementing
    /// `ToDname` since a blanket implementation for all pairs of `ToDname`
    /// is currently impossible.
    ///
    /// Domain names are compared ignoring ASCII case.
    fn name_eq<N: ToRelativeDname>(&self, other: &N) -> bool {
        if let (Some(left), Some(right)) = (self.as_flat_slice(),
                                            other.as_flat_slice()) {
            left.eq_ignore_ascii_case(right)
        }
        else {
            self.iter_labels().eq(other.iter_labels())
        }
    }

    /// Returns the ordering between `self` and `other`.
    ///
    /// This method can be used to implement both `PartialOrd` and `Ord` on
    /// types implementing `ToDname` since a blanket implementation for all
    /// pairs of `ToDname`s is currently not possible.
    ///
    /// Domain name order is determined according to the ‘canonical DNS
    /// name order’ as defined in [section 6.1 of RFC 4034][RFC4034-6.1].
    /// This section describes how absolute domain names are ordered only.
    /// We will order relative domain names according to these rules as if
    /// they had the same origin, i.e., as if they were relative to the
    /// same name.
    ///
    /// [RFC4034-6.1]: https://tools.ietf.org/html/rfc4034#section-6.1
    fn name_cmp<N: ToRelativeDname>(&self, other: &N) -> cmp::Ordering {
        let mut self_iter = self.iter_labels();
        let mut other_iter = other.iter_labels();
        loop {
            match (self_iter.next_back(), other_iter.next_back()) {
                (Some(left), Some(right)) => {
                    match left.cmp(right) {
                        cmp::Ordering::Equal => {}
                        res => return res
                    }
                }
                (None, Some(_)) => return cmp::Ordering::Less,
                (Some(_), None) => return cmp::Ordering::Greater,
                (None, None) => return cmp::Ordering::Equal
            }
        }
    }
}

impl<'a, N: ToRelativeDname + 'a> ToRelativeDname for &'a N { }

<|endoftext|>


<commit_before>#![allow(deprecated)] // for SipHasher

use std::path::{Path, PathBuf};
use std::hash::{Hash, Hasher, SipHasher};
use std::collections::hash_map::{Entry, HashMap};
use std::sync::Mutex;
use std::env;

use serde_json;

use util::{self, internal, profile, CargoResult, ProcessBuilder};
use util::paths;

/// Information on the `rustc` executable
#[derive(Debug)]
pub struct Rustc {
    /// The location of the exe
    pub path: PathBuf,
    /// An optional program that will be passed the path of the rust exe as its first argument, and
    /// rustc args following this.
    pub wrapper: Option<PathBuf>,
    /// Verbose version information (the output of `rustc -vV`)
    pub verbose_version: String,
    /// The host triple (arch-platform-OS), this comes from verbose_version.
    pub host: String,
    cache: Mutex<Cache>,
}

impl Rustc {
    /// Run the compiler at `path` to learn various pieces of information about
    /// it, with an optional wrapper.
    ///
    /// If successful this function returns a description of the compiler along
    /// with a list of its capabilities.
    pub fn new(
        path: PathBuf,
        wrapper: Option<PathBuf>,
        rustup_rustc: &Path,
        cache_location: Option<PathBuf>,
    ) -> CargoResult<Rustc> {
        let _p = profile::start("Rustc::new");

        let mut cache = Cache::load(&path, rustup_rustc, cache_location);

        let mut cmd = util::process(&path);
        cmd.arg("-vV");
        let verbose_version = cache.cached_output(&cmd)?.0;

        let host = {
            let triple = verbose_version
                .lines()
                .find(|l| l.starts_with("host: "))
                .map(|l| &l[6..])
                .ok_or_else(|| internal("rustc -v didn't have a line for `host:`"))?;
            triple.to_string()
        };

        Ok(Rustc {
            path,
            wrapper,
            verbose_version,
            host,
            cache: Mutex::new(cache),
        })
    }

    /// Get a process builder set up to use the found rustc version, with a wrapper if Some
    pub fn process(&self) -> ProcessBuilder {
        if let Some(ref wrapper) = self.wrapper {
            let mut cmd = util::process(wrapper);
            {
                cmd.arg(&self.path);
            }
            cmd
        } else {
            util::process(&self.path)
        }
    }

    pub fn cached_output(&self, cmd: &ProcessBuilder) -> CargoResult<(String, String)> {
        self.cache.lock().unwrap().cached_output(cmd)
    }
}

/// It is a well known that `rustc` is not the fastest compiler in the world.
/// What is less known is that even `rustc --version --verbose` takes about a
/// hundred milliseconds! Because we need compiler version info even for no-op
/// builds, we cache it here, based on compiler's mtime and rustup's current
/// toolchain.
///
/// https://github.com/rust-lang/cargo/issues/5315
/// https://github.com/rust-lang/rust/issues/49761
#[derive(Debug)]
struct Cache {
    cache_location: Option<PathBuf>,
    dirty: bool,
    data: CacheData,
}

#[derive(Serialize, Deserialize, Debug, Default)]
struct CacheData {
    rustc_fingerprint: u64,
    outputs: HashMap<u64, (String, String)>,
}

impl Cache {
    fn load(rustc: &Path, rustup_rustc: &Path, cache_location: Option<PathBuf>) -> Cache {
        match (cache_location, rustc_fingerprint(rustc, rustup_rustc)) {
            (Some(cache_location), Ok(rustc_fingerprint)) => {
                let empty = CacheData {
                    rustc_fingerprint,
                    outputs: HashMap::new(),
                };
                let mut dirty = true;
                let data = match read(&cache_location) {
                    Ok(data) => {
                        if data.rustc_fingerprint == rustc_fingerprint {
                            info!("reusing existing rustc info cache");
                            dirty = false;
                            data
                        } else {
                            info!("different compiler, creating new rustc info cache");
                            empty
                        }
                    }
                    Err(e) => {
                        info!("failed to read rustc info cache: {}", e);
                        empty
                    }
                };
                return Cache {
                    cache_location: Some(cache_location),
                    dirty,
                    data,
                };

                fn read(path: &Path) -> CargoResult<CacheData> {
                    let json = paths::read(path)?;
                    Ok(serde_json::from_str(&json)?)
                }
            }
            (_, fingerprint) => {
                if let Err(e) = fingerprint {
                    warn!("failed to calculate rustc fingerprint: {}", e);
                }
                info!("rustc info cache disabled");
                Cache {
                    cache_location: None,
                    dirty: false,
                    data: CacheData::default(),
                }
            }
        }
    }

    fn cached_output(&mut self, cmd: &ProcessBuilder) -> CargoResult<(String, String)> {
        let calculate = || {
            let output = cmd.exec_with_output()?;
            let stdout = String::from_utf8(output.stdout)
                .map_err(|_| internal("rustc didn't return utf8 output"))?;
            let stderr = String::from_utf8(output.stderr)
                .map_err(|_| internal("rustc didn't return utf8 output"))?;
            Ok((stdout, stderr))
        };
        if self.cache_location.is_none() {
            info!("rustc info uncached");
            return calculate();
        }

        let key = process_fingerprint(cmd);
        match self.data.outputs.entry(key) {
            Entry::Occupied(entry) => {
                info!("rustc info cache hit");
                Ok(entry.get().clone())
            }
            Entry::Vacant(entry) => {
                info!("rustc info cache miss");
                let output = calculate()?;
                entry.insert(output.clone());
                self.dirty = true;
                Ok(output)
            }
        }
    }
}

impl Drop for Cache {
    fn drop(&mut self) {
        match (&self.cache_location, self.dirty) {
            (&Some(ref path), true) => {
                let json = serde_json::to_string(&self.data).unwrap();
                match paths::write(path, json.as_bytes()) {
                    Ok(()) => info!("updated rustc info cache"),
                    Err(e) => warn!("failed to update rustc info cache: {}", e),
                }
            }
            _ => (),
        }
    }
}

fn rustc_fingerprint(path: &Path, rustup_rustc: &Path) -> CargoResult<u64> {
    let mut hasher = SipHasher::new_with_keys(0, 0);

    let path = paths::resolve_executable(path)?;
    path.hash(&mut hasher);

    paths::mtime(&path)?.hash(&mut hasher);

    // Rustup can change the effective compiler without touching
    // the `rustc` binary, so we try to account for this here.
    // If we see rustup's env vars, we mix them into the fingerprint,
    // but we also mix in the mtime of the actual compiler (and not
    // the rustup shim at `~/.cargo/bin/rustup`), because `RUSTUP_TOOLCHAIN`
    // could be just `stable-x86_64-unknown-linux-gnu`, i.e, it could
    // not mention the version of Rust at all, which changes after
    // `rustup update`.
    //
    // If we don't see rustup env vars, but it looks like the compiler
    // is managed by rustup, we conservatively bail out.
    let maybe_rustup = rustup_rustc == path;
    match (maybe_rustup, env::var("RUSTUP_HOME"), env::var("RUSTUP_TOOLCHAIN")) {
        (_, Ok(rustup_home), Ok(rustup_toolchain)) => {
            debug!("adding rustup info to rustc fingerprint");
            rustup_toolchain.hash(&mut hasher);
            rustup_home.hash(&mut hasher);
            let rustup_rustc = Path::new(&rustup_home)
                .join("toolchains")
                .join(rustup_toolchain)
                .join("bin")
                .join("rustc");
            paths::mtime(&rustup_rustc)?.hash(&mut hasher);
        }
        (true, _, _) => {
            bail!("probably rustup rustc, but without rustup's env vars")
        }
        _ => (),
    }

    Ok(hasher.finish())
}

fn process_fingerprint(cmd: &ProcessBuilder) -> u64 {
    let mut hasher = SipHasher::new_with_keys(0, 0);
    cmd.get_args().hash(&mut hasher);
    let mut env = cmd.get_envs().iter().collect::<Vec<_>>();
    env.sort();
    env.hash(&mut hasher);
    hasher.finish()
}
<commit_msg>Simplify<commit_after>#![allow(deprecated)] // for SipHasher

use std::path::{Path, PathBuf};
use std::hash::{Hash, Hasher, SipHasher};
use std::collections::hash_map::{Entry, HashMap};
use std::sync::Mutex;
use std::env;

use serde_json;

use util::{self, internal, profile, CargoResult, ProcessBuilder};
use util::paths;

/// Information on the `rustc` executable
#[derive(Debug)]
pub struct Rustc {
    /// The location of the exe
    pub path: PathBuf,
    /// An optional program that will be passed the path of the rust exe as its first argument, and
    /// rustc args following this.
    pub wrapper: Option<PathBuf>,
    /// Verbose version information (the output of `rustc -vV`)
    pub verbose_version: String,
    /// The host triple (arch-platform-OS), this comes from verbose_version.
    pub host: String,
    cache: Mutex<Cache>,
}

impl Rustc {
    /// Run the compiler at `path` to learn various pieces of information about
    /// it, with an optional wrapper.
    ///
    /// If successful this function returns a description of the compiler along
    /// with a list of its capabilities.
    pub fn new(
        path: PathBuf,
        wrapper: Option<PathBuf>,
        rustup_rustc: &Path,
        cache_location: Option<PathBuf>,
    ) -> CargoResult<Rustc> {
        let _p = profile::start("Rustc::new");

        let mut cache = Cache::load(&path, rustup_rustc, cache_location);

        let mut cmd = util::process(&path);
        cmd.arg("-vV");
        let verbose_version = cache.cached_output(&cmd)?.0;

        let host = {
            let triple = verbose_version
                .lines()
                .find(|l| l.starts_with("host: "))
                .map(|l| &l[6..])
                .ok_or_else(|| internal("rustc -v didn't have a line for `host:`"))?;
            triple.to_string()
        };

        Ok(Rustc {
            path,
            wrapper,
            verbose_version,
            host,
            cache: Mutex::new(cache),
        })
    }

    /// Get a process builder set up to use the found rustc version, with a wrapper if Some
    pub fn process(&self) -> ProcessBuilder {
        if let Some(ref wrapper) = self.wrapper {
            let mut cmd = util::process(wrapper);
            {
                cmd.arg(&self.path);
            }
            cmd
        } else {
            util::process(&self.path)
        }
    }

    pub fn cached_output(&self, cmd: &ProcessBuilder) -> CargoResult<(String, String)> {
        self.cache.lock().unwrap().cached_output(cmd)
    }
}

/// It is a well known that `rustc` is not the fastest compiler in the world.
/// What is less known is that even `rustc --version --verbose` takes about a
/// hundred milliseconds! Because we need compiler version info even for no-op
/// builds, we cache it here, based on compiler's mtime and rustup's current
/// toolchain.
///
/// https://github.com/rust-lang/cargo/issues/5315
/// https://github.com/rust-lang/rust/issues/49761
#[derive(Debug)]
struct Cache {
    cache_location: Option<PathBuf>,
    dirty: bool,
    data: CacheData,
}

#[derive(Serialize, Deserialize, Debug, Default)]
struct CacheData {
    rustc_fingerprint: u64,
    outputs: HashMap<u64, (String, String)>,
}

impl Cache {
    fn load(rustc: &Path, rustup_rustc: &Path, cache_location: Option<PathBuf>) -> Cache {
        match (cache_location, rustc_fingerprint(rustc, rustup_rustc)) {
            (Some(cache_location), Ok(rustc_fingerprint)) => {
                let empty = CacheData {
                    rustc_fingerprint,
                    outputs: HashMap::new(),
                };
                let mut dirty = true;
                let data = match read(&cache_location) {
                    Ok(data) => {
                        if data.rustc_fingerprint == rustc_fingerprint {
                            info!("reusing existing rustc info cache");
                            dirty = false;
                            data
                        } else {
                            info!("different compiler, creating new rustc info cache");
                            empty
                        }
                    }
                    Err(e) => {
                        info!("failed to read rustc info cache: {}", e);
                        empty
                    }
                };
                return Cache {
                    cache_location: Some(cache_location),
                    dirty,
                    data,
                };

                fn read(path: &Path) -> CargoResult<CacheData> {
                    let json = paths::read(path)?;
                    Ok(serde_json::from_str(&json)?)
                }
            }
            (_, fingerprint) => {
                if let Err(e) = fingerprint {
                    warn!("failed to calculate rustc fingerprint: {}", e);
                }
                info!("rustc info cache disabled");
                Cache {
                    cache_location: None,
                    dirty: false,
                    data: CacheData::default(),
                }
            }
        }
    }

    fn cached_output(&mut self, cmd: &ProcessBuilder) -> CargoResult<(String, String)> {
        let calculate = || {
            let output = cmd.exec_with_output()?;
            let stdout = String::from_utf8(output.stdout)
                .map_err(|_| internal("rustc didn't return utf8 output"))?;
            let stderr = String::from_utf8(output.stderr)
                .map_err(|_| internal("rustc didn't return utf8 output"))?;
            Ok((stdout, stderr))
        };
        if self.cache_location.is_none() {
            info!("rustc info uncached");
            return calculate();
        }

        let key = process_fingerprint(cmd);
        match self.data.outputs.entry(key) {
            Entry::Occupied(entry) => {
                info!("rustc info cache hit");
                Ok(entry.get().clone())
            }
            Entry::Vacant(entry) => {
                info!("rustc info cache miss");
                let output = calculate()?;
                entry.insert(output.clone());
                self.dirty = true;
                Ok(output)
            }
        }
    }
}

impl Drop for Cache {
    fn drop(&mut self) {
        if !self.dirty {
            return;
        }
        if let Some(ref path) = self.cache_location {
            let json = serde_json::to_string(&self.data).unwrap();
            match paths::write(path, json.as_bytes()) {
                Ok(()) => info!("updated rustc info cache"),
                Err(e) => warn!("failed to update rustc info cache: {}", e),
            }
        }
    }
}

fn rustc_fingerprint(path: &Path, rustup_rustc: &Path) -> CargoResult<u64> {
    let mut hasher = SipHasher::new_with_keys(0, 0);

    let path = paths::resolve_executable(path)?;
    path.hash(&mut hasher);

    paths::mtime(&path)?.hash(&mut hasher);

    // Rustup can change the effective compiler without touching
    // the `rustc` binary, so we try to account for this here.
    // If we see rustup's env vars, we mix them into the fingerprint,
    // but we also mix in the mtime of the actual compiler (and not
    // the rustup shim at `~/.cargo/bin/rustup`), because `RUSTUP_TOOLCHAIN`
    // could be just `stable-x86_64-unknown-linux-gnu`, i.e, it could
    // not mention the version of Rust at all, which changes after
    // `rustup update`.
    //
    // If we don't see rustup env vars, but it looks like the compiler
    // is managed by rustup, we conservatively bail out.
    let maybe_rustup = rustup_rustc == path;
    match (maybe_rustup, env::var("RUSTUP_HOME"), env::var("RUSTUP_TOOLCHAIN")) {
        (_, Ok(rustup_home), Ok(rustup_toolchain)) => {
            debug!("adding rustup info to rustc fingerprint");
            rustup_toolchain.hash(&mut hasher);
            rustup_home.hash(&mut hasher);
            let rustup_rustc = Path::new(&rustup_home)
                .join("toolchains")
                .join(rustup_toolchain)
                .join("bin")
                .join("rustc");
            paths::mtime(&rustup_rustc)?.hash(&mut hasher);
        }
        (true, _, _) => {
            bail!("probably rustup rustc, but without rustup's env vars")
        }
        _ => (),
    }

    Ok(hasher.finish())
}

fn process_fingerprint(cmd: &ProcessBuilder) -> u64 {
    let mut hasher = SipHasher::new_with_keys(0, 0);
    cmd.get_args().hash(&mut hasher);
    let mut env = cmd.get_envs().iter().collect::<Vec<_>>();
    env.sort();
    env.hash(&mut hasher);
    hasher.finish()
}
<|endoftext|>


<commit_before>#[cfg(test)]
#[macro_use]
mod test_helpers {
    use vsl::{VslRecord, VslRecordTag, VslIdent};
    use std::sync::{Once, ONCE_INIT};

    pub use vsl::VslRecordTag::*;

    pub fn vsl(tag: VslRecordTag, ident: VslIdent, message: &str) -> VslRecord {
        VslRecord::from_str(tag, ident, message)
    }

    static LOGGER: Once = ONCE_INIT;

    pub fn log() {
        use env_logger;

        LOGGER.call_once(|| {
            env_logger::init().unwrap();
        });
    }

    macro_rules! assert_none {
        ($x:expr) => {{
            let opt: Option<_> = $x;
            assert!(opt.is_none(), "expected `{}` to be None", stringify!($x));
        }};
    }

    macro_rules! assert_some {
        ($x:expr) => {{
            let opt: Option<_> = $x;
            assert!(opt.is_some(), "expected `{}` to be Some", stringify!($x));
            opt.unwrap()
        }};
    }

    macro_rules! apply {
        ($state:ident, $ident:expr, $tag:ident, $message:expr) => {{
            let opt: Option<_> = $state.apply(&vsl($tag, $ident, $message));
            assert!(opt.is_none(), "expected apply to return None after applying: `{}, {:?}, {};`", $ident, $tag, $message);
        }};
    }

    macro_rules! apply_all {
        ($state:ident, $($t_ident:expr, $t_tag:ident, $t_message:expr;)+) => {{
            $(apply!($state, $t_ident, $t_tag, $t_message);)*
        }};
    }

    macro_rules! apply_final {
        ($state:ident, $ident:expr, $tag:ident, $message:expr) => {
            assert_some!($state.apply(&vsl($tag, $ident, $message)))
        };
    }
}

mod session_state;
mod record_state;

pub use self::record_state::*;
pub use self::session_state::SessionState;

include!(concat!(env!("OUT_DIR"), "/serde_types.rs"));

use std::io::Error as IoError;

use serde_json::error::Error as JsonError;
use serde_json::ser::to_writer as write_json;
use serde_json::ser::to_writer_pretty as write_json_pretty;
use std::io::Write;

quick_error! {
    #[derive(Debug)]
    pub enum OutputError {
        JsonSerialization(err: JsonError) {
            display("Failed to serialize to JSON: {}", err)
            from()
        }
        Io(err: IoError) {
            display("Failed to write to output: {}", err)
            from()
        }
    }
}

pub enum Format {
    Json,
    JsonPretty,
}

trait TransactionLookup {
    fn find_full<'a>(&'a self, restart_count: usize) -> Option<(&'a Self, usize)>;
}

impl TransactionLookup for ClientAccessRecord {
    fn find_full<'a>(&'a self, restart_count: usize) -> Option<(&'a Self, usize)> {
        match self.transaction {
            ClientAccessTransaction::Full { .. } => Some((self, restart_count)),
            ClientAccessTransaction::Restarted { ref restart_record, .. } => {
                if let Some(record) = restart_record.get_resolved() {
                    record.find_full(restart_count + 1)
                } else {
                    warn!("Found unresolved link {:?} in: {:?}", restart_record, self);
                    None
                }
            },
            ClientAccessTransaction::Piped { .. } => None,
        }
    }
}

trait AsSer<'a> {
    type Out;

    fn as_ser(&'a self) -> Self::Out;
}

impl<'a> AsSer<'a> for Address {
    type Out = (&'a str, u16);
    fn as_ser(&'a self) -> Self::Out {
        (self.0.as_str(), self.1)
    }
}

impl<'a> AsSer<'a> for Handling {
    type Out = &'a str;
    fn as_ser(&self) -> Self::Out {
        match self {
            &Handling::Hit(_) => "hit",
            &Handling::Miss => "miss",
            &Handling::Pass => "pass",
            &Handling::HitPass(_) => "hit_for_pass",
            &Handling::Synth => "synth",
            &Handling::Pipe => "pipe",
        }
    }
}

impl<'a> AsSer<'a> for HttpRequest {
    type Out = HttpRequestLogEntry<'a>;
    fn as_ser(&'a self) -> Self::Out {
        HttpRequestLogEntry {
            protocol: self.protocol.as_str(),
            method: self.method.as_str(),
            url: self.url.as_str(),
            headers: self.headers.as_slice(),
        }
    }
}

impl<'a> AsSer<'a> for HttpResponse {
    type Out = HttpResponseLogEntry<'a>;
    fn as_ser(&'a self) -> Self::Out {
        HttpResponseLogEntry {
            status: self.status,
            reason: self.reason.as_str(),
            protocol: self.protocol.as_str(),
            headers: self.headers.as_slice(),
        }
    }
}

impl<'a> AsSer<'a> for Vec<LogEntry> {
    type Out = LogBook<'a>;
    fn as_ser(&'a self) -> Self::Out {
        LogBook {
            entries: self.as_slice(),
        }
    }
}

impl<'a> AsSer<'a> for BackendConnection {
    type Out = BackendConnectionLogEntry<'a>;
    fn as_ser(&'a self) -> Self::Out {
        BackendConnectionLogEntry {
            fd: self.fd,
            name: self.name.as_str(),
            remote_address: self.remote.as_ser(),
            local_address: self.local.as_ser(),
        }
    }
}

pub trait AccessLog {
    fn access_log<W>(&self, format: &Format, out: &mut W) -> Result<(), OutputError> where W: Write;
}

impl AccessLog for SessionRecord {
    fn access_log<W>(&self, format: &Format, out: &mut W) -> Result<(), OutputError> where W: Write {
        fn write<W, E>(format: &Format, out: &mut W, log_entry: &E) -> Result<(), OutputError> where W: Write, E: EntryType {
            match format {
                &Format::Json | &Format::JsonPretty => {
                    let write = match format {
                        &Format::Json => write_json,
                        &Format::JsonPretty => write_json_pretty,
                    };

                    try!(write(out, &Entry {
                        record_type: E::type_name(),
                        record: &log_entry,
                    }));

                    try!(writeln!(out, ""));
                }
            }
            Ok(())
        }

        fn log_client_record<W>(
            format: &Format, out: &mut W,
            session_record: &SessionRecord,
            record_link: &Link<ClientAccessRecord>,
            request_type: &'static str) -> Result<(), OutputError> where W: Write {
            if let Some(record) = record_link.get_resolved() {
                match record.transaction {
                    ClientAccessTransaction::Full {
                        ref esi_records,
                        ref request,
                        ref response,
                        process,
                        fetch,
                        ttfb,
                        serve,
                        ref accounting,
                        ..
                    } => {
                        try!(write(format, out, &ClientAccessLogEntry {
                            request_type: request_type,
                            remote_address: session_record.remote.as_ser(),
                            session_timestamp: session_record.open,
                            start_timestamp: record.start,
                            end_timestamp: record.end,
                            handing: record.handling.as_ser(),
                            request: request.as_ser(),
                            response: response.as_ser(),
                            process: process,
                            fetch: fetch,
                            ttfb: ttfb,
                            serve: serve,
                            recv_header_bytes: accounting.recv_header,
                            recv_body_bytes: accounting.recv_body,
                            recv_total_bytes: accounting.recv_total,
                            sent_header_bytes: accounting.sent_header,
                            sent_body_bytes: accounting.sent_body,
                            sent_total_bytes: accounting.sent_total,
                            esi_count: esi_records.len(),
                            restart_count: 0,
                            restart_log: None,
                            log: record.log.as_ser(),
                        }));

                        for esi_record_link in esi_records {
                            try!(log_client_record(format, out, session_record, esi_record_link, "esi"))
                        }
                    }
                    ClientAccessTransaction::Restarted {
                        ref request,
                        process,
                        ..
                    } => {
                        if let Some((full_record, restart_count)) = record.find_full(0) {
                            if let ClientAccessTransaction::Full {
                                ref esi_records,
                                ref response,
                                fetch,
                                ttfb,
                                serve,
                                ref accounting,
                                ..
                            } = full_record.transaction {
                                try!(write(format, out, &ClientAccessLogEntry {
                                    request_type: request_type,
                                    remote_address: session_record.remote.as_ser(),
                                    session_timestamp: session_record.open,
                                    start_timestamp: record.start,
                                    end_timestamp: full_record.end,
                                    handing: full_record.handling.as_ser(),
                                    request: request.as_ser(),
                                    response: response.as_ser(),
                                    process: process,
                                    fetch: fetch,
                                    ttfb: ttfb,
                                    serve: serve,
                                    recv_header_bytes: accounting.recv_header,
                                    recv_body_bytes: accounting.recv_body,
                                    recv_total_bytes: accounting.recv_total,
                                    sent_header_bytes: accounting.sent_header,
                                    sent_body_bytes: accounting.sent_body,
                                    sent_total_bytes: accounting.sent_total,
                                    esi_count: esi_records.len(),
                                    restart_count: restart_count,
                                    restart_log: Some(record.log.as_ser()),
                                    //TODO: still will miss intermediate log entreis
                                    log: full_record.log.as_ser(),
                                }));

                                for esi_record_link in esi_records {
                                    try!(log_client_record(format, out, session_record, esi_record_link, "esi"))
                                }
                            } else {
                                panic!("ClientAccessRecord::find_full found not ClientAccessTransaction::Full: {:?}", record)
                            }
                        } else {
                            //TODO: what about piped? etc? need to find_leaf record and handle it somehow
                            warn!("Full transaction not found on restarted transaction chain: {:?}", record);
                        }
                    },
                    ClientAccessTransaction::Piped {
                        ref request,
                        ref backend_record,
                        process,
                        ttfb,
                        ..
                    } => {
                        if let Some(backend_record) = backend_record.get_resolved() {
                            if let BackendAccessTransaction::Piped {
                                request: ref backend_request,
                                ref backend_connection,
                                ..
                            } = backend_record.transaction {
                                try!(write(format, out, &PipeSessionLogEntry {
                                    remote_address: session_record.remote.as_ser(),
                                    session_timestamp: session_record.open,
                                    start_timestamp: record.start,
                                    end_timestamp: record.end,
                                    handing: record.handling.as_ser(),
                                    request: request.as_ser(),
                                    backend_request: backend_request.as_ser(),
                                    process: process,
                                    ttfb: ttfb,
                                    log: record.log.as_ser(),
                                    backend_connection: backend_connection.as_ser(),
                                }))
                            } else {
                                warn!("Piped ClientAccessRecord has not Piped BackendAccessTransaction linked: {:?} linked: {:?}", record, backend_record);
                            }
                        } else {
                            warn!("Found unresolved link {:?} in: {:?}", backend_record, record);
                        }
                    },
                }
            } else {
                warn!("Found unresolved link {:?} in: {:?}", record_link, session_record);
            }
            Ok(())
        }

        for record_link in self.client_records.iter() {
            try!(log_client_record(format, out, self, record_link, "remote"))
        }
        Ok(())
    }
}
<commit_msg>refactoring<commit_after>#[cfg(test)]
#[macro_use]
mod test_helpers {
    use vsl::{VslRecord, VslRecordTag, VslIdent};
    use std::sync::{Once, ONCE_INIT};

    pub use vsl::VslRecordTag::*;

    pub fn vsl(tag: VslRecordTag, ident: VslIdent, message: &str) -> VslRecord {
        VslRecord::from_str(tag, ident, message)
    }

    static LOGGER: Once = ONCE_INIT;

    pub fn log() {
        use env_logger;

        LOGGER.call_once(|| {
            env_logger::init().unwrap();
        });
    }

    macro_rules! assert_none {
        ($x:expr) => {{
            let opt: Option<_> = $x;
            assert!(opt.is_none(), "expected `{}` to be None", stringify!($x));
        }};
    }

    macro_rules! assert_some {
        ($x:expr) => {{
            let opt: Option<_> = $x;
            assert!(opt.is_some(), "expected `{}` to be Some", stringify!($x));
            opt.unwrap()
        }};
    }

    macro_rules! apply {
        ($state:ident, $ident:expr, $tag:ident, $message:expr) => {{
            let opt: Option<_> = $state.apply(&vsl($tag, $ident, $message));
            assert!(opt.is_none(), "expected apply to return None after applying: `{}, {:?}, {};`", $ident, $tag, $message);
        }};
    }

    macro_rules! apply_all {
        ($state:ident, $($t_ident:expr, $t_tag:ident, $t_message:expr;)+) => {{
            $(apply!($state, $t_ident, $t_tag, $t_message);)*
        }};
    }

    macro_rules! apply_final {
        ($state:ident, $ident:expr, $tag:ident, $message:expr) => {
            assert_some!($state.apply(&vsl($tag, $ident, $message)))
        };
    }
}

mod session_state;
mod record_state;

pub use self::record_state::*;
pub use self::session_state::SessionState;

include!(concat!(env!("OUT_DIR"), "/serde_types.rs"));

use std::io::Error as IoError;

use serde_json::error::Error as JsonError;
use serde_json::ser::to_writer as write_json;
use serde_json::ser::to_writer_pretty as write_json_pretty;
use std::io::Write;

quick_error! {
    #[derive(Debug)]
    pub enum OutputError {
        JsonSerialization(err: JsonError) {
            display("Failed to serialize to JSON: {}", err)
            from()
        }
        Io(err: IoError) {
            display("Failed to write to output: {}", err)
            from()
        }
    }
}

pub enum Format {
    Json,
    JsonPretty,
}

trait AsSer<'a> {
    type Out;

    fn as_ser(&'a self) -> Self::Out;
}

impl<'a> AsSer<'a> for Address {
    type Out = (&'a str, u16);
    fn as_ser(&'a self) -> Self::Out {
        (self.0.as_str(), self.1)
    }
}

impl<'a> AsSer<'a> for Handling {
    type Out = &'a str;
    fn as_ser(&self) -> Self::Out {
        match self {
            &Handling::Hit(_) => "hit",
            &Handling::Miss => "miss",
            &Handling::Pass => "pass",
            &Handling::HitPass(_) => "hit_for_pass",
            &Handling::Synth => "synth",
            &Handling::Pipe => "pipe",
        }
    }
}

impl<'a> AsSer<'a> for HttpRequest {
    type Out = HttpRequestLogEntry<'a>;
    fn as_ser(&'a self) -> Self::Out {
        HttpRequestLogEntry {
            protocol: self.protocol.as_str(),
            method: self.method.as_str(),
            url: self.url.as_str(),
            headers: self.headers.as_slice(),
        }
    }
}

impl<'a> AsSer<'a> for HttpResponse {
    type Out = HttpResponseLogEntry<'a>;
    fn as_ser(&'a self) -> Self::Out {
        HttpResponseLogEntry {
            status: self.status,
            reason: self.reason.as_str(),
            protocol: self.protocol.as_str(),
            headers: self.headers.as_slice(),
        }
    }
}

impl<'a> AsSer<'a> for Vec<LogEntry> {
    type Out = LogBook<'a>;
    fn as_ser(&'a self) -> Self::Out {
        LogBook {
            entries: self.as_slice(),
        }
    }
}

impl<'a> AsSer<'a> for BackendConnection {
    type Out = BackendConnectionLogEntry<'a>;
    fn as_ser(&'a self) -> Self::Out {
        BackendConnectionLogEntry {
            fd: self.fd,
            name: self.name.as_str(),
            remote_address: self.remote.as_ser(),
            local_address: self.local.as_ser(),
        }
    }
}

pub trait AccessLog {
    fn access_log<W>(&self, format: &Format, out: &mut W) -> Result<(), OutputError> where W: Write;
}

impl AccessLog for SessionRecord {
    fn access_log<W>(&self, format: &Format, out: &mut W) -> Result<(), OutputError> where W: Write {
        fn write<W, E>(format: &Format, out: &mut W, log_entry: &E) -> Result<(), OutputError> where W: Write, E: EntryType {
            match format {
                &Format::Json | &Format::JsonPretty => {
                    let write = match format {
                        &Format::Json => write_json,
                        &Format::JsonPretty => write_json_pretty,
                    };

                    try!(write(out, &Entry {
                        record_type: E::type_name(),
                        record: &log_entry,
                    }));

                    try!(writeln!(out, ""));
                }
            }
            Ok(())
        }

        fn find_final(record: &ClientAccessRecord, restart_count: usize) -> Option<(&ClientAccessRecord, usize)> {
            match record.transaction {
                ClientAccessTransaction::Full { .. } | ClientAccessTransaction::Piped { .. } => Some((record, restart_count)),
                ClientAccessTransaction::Restarted { ref restart_record, .. } => {
                    if let Some(record) = restart_record.get_resolved() {
                        find_final(record, restart_count + 1)
                    } else {
                        warn!("Found unresolved link {:?} in: {:?}", restart_record, record);
                        None
                    }
                },
            }
        }

        fn log_linked_client_access_record<W>(
            format: &Format,
            out: &mut W,
            session_record: &SessionRecord,
            record_link: &Link<ClientAccessRecord>,
            request_type: &'static str) -> Result<(), OutputError> where W: Write {
            if let Some(record) = record_link.get_resolved() {
                if let Some((final_record, restart_count)) = find_final(record, 0) {
                    // Note: we skip all the intermediate restart records
                    match (&record.transaction, &final_record.transaction) {
                        (&ClientAccessTransaction::Restarted {
                            ref request,
                            process,
                            ..
                        }, &ClientAccessTransaction::Full {
                            ref esi_records,
                            ref response,
                            fetch,
                            ttfb,
                            serve,
                            ref accounting,
                            ..
                        }) => try!(write(format, out, &ClientAccessLogEntry {
                            request_type: request_type,
                            remote_address: session_record.remote.as_ser(),
                            session_timestamp: session_record.open,
                            start_timestamp: record.start,
                            end_timestamp: final_record.end,
                            handing: final_record.handling.as_ser(),
                            request: request.as_ser(),
                            response: response.as_ser(),
                            process: process,
                            fetch: fetch,
                            ttfb: ttfb,
                            serve: serve,
                            recv_header_bytes: accounting.recv_header,
                            recv_body_bytes: accounting.recv_body,
                            recv_total_bytes: accounting.recv_total,
                            sent_header_bytes: accounting.sent_header,
                            sent_body_bytes: accounting.sent_body,
                            sent_total_bytes: accounting.sent_total,
                            esi_count: esi_records.len(),
                            restart_count: restart_count,
                            restart_log: Some(record.log.as_ser()),
                            log: final_record.log.as_ser(),
                        })),
                        (_, &ClientAccessTransaction::Full {
                            ref esi_records,
                            ref request,
                            ref response,
                            process,
                            fetch,
                            ttfb,
                            serve,
                            ref accounting,
                            ..
                        }) => try!(write(format, out, &ClientAccessLogEntry {
                            request_type: request_type,
                            remote_address: session_record.remote.as_ser(),
                            session_timestamp: session_record.open,
                            start_timestamp: final_record.start,
                            end_timestamp: final_record.end,
                            handing: final_record.handling.as_ser(),
                            request: request.as_ser(),
                            response: response.as_ser(),
                            process: process,
                            fetch: fetch,
                            ttfb: ttfb,
                            serve: serve,
                            recv_header_bytes: accounting.recv_header,
                            recv_body_bytes: accounting.recv_body,
                            recv_total_bytes: accounting.recv_total,
                            sent_header_bytes: accounting.sent_header,
                            sent_body_bytes: accounting.sent_body,
                            sent_total_bytes: accounting.sent_total,
                            esi_count: esi_records.len(),
                            restart_count: restart_count,
                            restart_log: None,
                            log: final_record.log.as_ser(),
                        })),
                        (_, &ClientAccessTransaction::Piped {
                            ref request,
                            ref backend_record,
                            process,
                            ttfb,
                            ..
                        }) => {
                            if let Some(backend_record) = backend_record.get_resolved() {
                                if let BackendAccessTransaction::Piped {
                                    request: ref backend_request,
                                    ref backend_connection,
                                    ..
                                } = backend_record.transaction {
                                    try!(write(format, out, &PipeSessionLogEntry {
                                        remote_address: session_record.remote.as_ser(),
                                        session_timestamp: session_record.open,
                                        start_timestamp: final_record.start,
                                        end_timestamp: final_record.end,
                                        handing: final_record.handling.as_ser(),
                                        request: request.as_ser(),
                                        backend_request: backend_request.as_ser(),
                                        process: process,
                                        ttfb: ttfb,
                                        log: final_record.log.as_ser(),
                                        backend_connection: backend_connection.as_ser(),
                                    }))
                                } else {
                                    warn!("Expected Piped ClientAccessRecord to link Piped BackendAccessTransaction; link {:?} in: {:?}",
                                          backend_record, final_record);
                                }
                            } else {
                                warn!("Found unresolved link {:?} in: {:?}", backend_record, final_record);
                            }
                        }
                        (_, &ClientAccessTransaction::Restarted { .. }) => panic!("got ClientAccessTransaction::Restarted as final final_record")
                    }

                    match &final_record.transaction {
                        &ClientAccessTransaction::Full {
                            ref esi_records,
                            ..
                        } => for esi_record_link in esi_records {
                            try!(log_linked_client_access_record(format, out, session_record, esi_record_link, "ESI"))
                        },
                        &ClientAccessTransaction::Restarted { .. } | &ClientAccessTransaction::Piped { .. } => (),
                    }
                } else {
                    warn!("Failed to find final record for: {:?}", record);
                }
            } else {
                warn!("Found unresolved link {:?} in: {:?}", record_link, session_record);
            }
            Ok(())
        }

        for record_link in self.client_records.iter() {
            try!(log_linked_client_access_record(format, out, self, record_link, "external"))
        }
        Ok(())
    }
}
<|endoftext|>


<commit_before>use crate::bounding_volume::AABB;
use crate::math::{Isometry, Point, Vector, DIM};
use crate::num::{Bounded, Zero};
use crate::query::{PointProjection, PointQuery};
use crate::shape::FeatureId;
use na::{self, RealField};

impl<N: RealField> AABB<N> {
    fn local_point_projection(
        &self,
        m: &Isometry<N>,
        pt: &Point<N>,
        solid: bool,
    ) -> (bool, Point<N>, Vector<N>) {
        let ls_pt = m.inverse_transform_point(pt);
        let mins_pt = *self.mins() - ls_pt;
        let pt_maxs = ls_pt - *self.maxs();
        let shift = na::sup(&na::zero(), &mins_pt) - na::sup(&na::zero(), &pt_maxs);

        let inside = shift == na::zero();

        if !inside {
            (false, ls_pt + shift, shift)
        } else if solid {
            (true, ls_pt, shift)
        } else {
            let _max: N = Bounded::max_value();
            let mut best = -_max;
            let mut is_mins = false;
            let mut best_id = 0;

            for i in 0..DIM {
                let mins_pt_i = mins_pt[i];
                let pt_maxs_i = pt_maxs[i];

                if mins_pt_i < pt_maxs_i {
                    if pt_maxs[i] > best {
                        best_id = i;
                        is_mins = false;
                        best = pt_maxs_i
                    }
                } else if mins_pt_i > best {
                    best_id = i;
                    is_mins = true;
                    best = mins_pt_i
                }
            }

            let mut shift: Vector<N> = na::zero();

            if is_mins {
                shift[best_id] = best;
            } else {
                shift[best_id] = -best;
            }

            (inside, ls_pt + shift, shift)
        }
    }
}

impl<N: RealField> PointQuery<N> for AABB<N> {
    #[inline]
    fn project_point(&self, m: &Isometry<N>, pt: &Point<N>, solid: bool) -> PointProjection<N> {
        let (inside, ls_pt, _) = self.local_point_projection(m, pt, solid);
        PointProjection::new(inside, m * ls_pt)
    }

    #[allow(unused_assignments)] // For last_zero_shift which is used only in 3D.
    #[allow(unused_variables)] // For last_zero_shift which is used only in 3D.
    #[inline]
    fn project_point_with_feature(
        &self,
        m: &Isometry<N>,
        pt: &Point<N>,
    ) -> (PointProjection<N>, FeatureId) {
        let (inside, ls_pt, shift) = self.local_point_projection(m, pt, false);
        let proj = PointProjection::new(inside, m * ls_pt);
        let mut nzero_shifts = 0;
        let mut last_zero_shift = 0;
        let mut last_not_zero_shift = 0;

        for i in 0..DIM {
            if shift[i].is_zero() {
                nzero_shifts += 1;
                last_zero_shift = i;
            } else {
                last_not_zero_shift = i;
            }
        }

        if nzero_shifts == DIM {
            for i in 0..DIM {
                if ls_pt[i] > self.maxs()[i] - N::default_epsilon() {
                    return (proj, FeatureId::Face(i));
                }
                if ls_pt[i] <= self.mins()[i] + N::default_epsilon() {
                    return (proj, FeatureId::Face(i + DIM));
                }
            }

            (proj, FeatureId::Unknown)
        } else if nzero_shifts == DIM - 1 {
            // On a 3D face.
            if ls_pt[last_not_zero_shift] < self.center()[last_not_zero_shift] {
                (proj, FeatureId::Face(last_not_zero_shift + DIM))
            } else {
                (proj, FeatureId::Face(last_not_zero_shift))
            }
        } else {
            // On a vertex or edge.
            let mut id = 0;
            let center = self.center();

            for i in 0..DIM {
                if ls_pt[i] < center[i] {
                    id |= 1 << i;
                }
            }

            #[cfg(feature = "dim3")]
            {
                if nzero_shifts == 0 {
                    (proj, FeatureId::Vertex(id))
                } else {
                    (proj, FeatureId::Edge((id << 2) | last_zero_shift))
                }
            }

            #[cfg(feature = "dim2")]
            {
                (proj, FeatureId::Vertex(id))
            }
        }
    }

    #[inline]
    fn distance_to_point(&self, m: &Isometry<N>, pt: &Point<N>, solid: bool) -> N {
        let ls_pt = m.inverse_transform_point(pt);
        let mins_pt = *self.mins() - ls_pt;
        let pt_maxs = ls_pt - *self.maxs();
        let shift = na::sup(&na::zero(), &na::sup(&mins_pt, &pt_maxs));

        if solid || !shift.is_zero() {
            shift.norm()
        } else {
            // FIXME: optimize that.
            -na::distance(pt, &self.project_point(m, pt, solid).point)
        }
    }

    #[inline]
    fn contains_point(&self, m: &Isometry<N>, pt: &Point<N>) -> bool {
        let ls_pt = m.inverse_transform_point(pt);
        self.contains_local_point(&ls_pt)
    }
}
<commit_msg>Update to the latest nalgebra.<commit_after>use crate::bounding_volume::AABB;
use crate::math::{Isometry, Point, Vector, DIM};
use crate::num::{Bounded, Zero};
use crate::query::{PointProjection, PointQuery};
use crate::shape::FeatureId;
use na::{self, RealField};

impl<N: RealField> AABB<N> {
    fn local_point_projection(
        &self,
        m: &Isometry<N>,
        pt: &Point<N>,
        solid: bool,
    ) -> (bool, Point<N>, Vector<N>) {
        let ls_pt = m.inverse_transform_point(pt);
        let mins_pt = *self.mins() - ls_pt;
        let pt_maxs = ls_pt - *self.maxs();
        let shift = na::sup(&na::zero(), &mins_pt) - na::sup(&na::zero(), &pt_maxs);

        let inside = shift.is_zero();

        if !inside {
            (false, ls_pt + shift, shift)
        } else if solid {
            (true, ls_pt, shift)
        } else {
            let _max: N = Bounded::max_value();
            let mut best = -_max;
            let mut is_mins = false;
            let mut best_id = 0;

            for i in 0..DIM {
                let mins_pt_i = mins_pt[i];
                let pt_maxs_i = pt_maxs[i];

                if mins_pt_i < pt_maxs_i {
                    if pt_maxs[i] > best {
                        best_id = i;
                        is_mins = false;
                        best = pt_maxs_i
                    }
                } else if mins_pt_i > best {
                    best_id = i;
                    is_mins = true;
                    best = mins_pt_i
                }
            }

            let mut shift: Vector<N> = na::zero();

            if is_mins {
                shift[best_id] = best;
            } else {
                shift[best_id] = -best;
            }

            (inside, ls_pt + shift, shift)
        }
    }
}

impl<N: RealField> PointQuery<N> for AABB<N> {
    #[inline]
    fn project_point(&self, m: &Isometry<N>, pt: &Point<N>, solid: bool) -> PointProjection<N> {
        let (inside, ls_pt, _) = self.local_point_projection(m, pt, solid);
        PointProjection::new(inside, m * ls_pt)
    }

    #[allow(unused_assignments)] // For last_zero_shift which is used only in 3D.
    #[allow(unused_variables)] // For last_zero_shift which is used only in 3D.
    #[inline]
    fn project_point_with_feature(
        &self,
        m: &Isometry<N>,
        pt: &Point<N>,
    ) -> (PointProjection<N>, FeatureId) {
        let (inside, ls_pt, shift) = self.local_point_projection(m, pt, false);
        let proj = PointProjection::new(inside, m * ls_pt);
        let mut nzero_shifts = 0;
        let mut last_zero_shift = 0;
        let mut last_not_zero_shift = 0;

        for i in 0..DIM {
            if shift[i].is_zero() {
                nzero_shifts += 1;
                last_zero_shift = i;
            } else {
                last_not_zero_shift = i;
            }
        }

        if nzero_shifts == DIM {
            for i in 0..DIM {
                if ls_pt[i] > self.maxs()[i] - N::default_epsilon() {
                    return (proj, FeatureId::Face(i));
                }
                if ls_pt[i] <= self.mins()[i] + N::default_epsilon() {
                    return (proj, FeatureId::Face(i + DIM));
                }
            }

            (proj, FeatureId::Unknown)
        } else if nzero_shifts == DIM - 1 {
            // On a 3D face.
            if ls_pt[last_not_zero_shift] < self.center()[last_not_zero_shift] {
                (proj, FeatureId::Face(last_not_zero_shift + DIM))
            } else {
                (proj, FeatureId::Face(last_not_zero_shift))
            }
        } else {
            // On a vertex or edge.
            let mut id = 0;
            let center = self.center();

            for i in 0..DIM {
                if ls_pt[i] < center[i] {
                    id |= 1 << i;
                }
            }

            #[cfg(feature = "dim3")]
            {
                if nzero_shifts == 0 {
                    (proj, FeatureId::Vertex(id))
                } else {
                    (proj, FeatureId::Edge((id << 2) | last_zero_shift))
                }
            }

            #[cfg(feature = "dim2")]
            {
                (proj, FeatureId::Vertex(id))
            }
        }
    }

    #[inline]
    fn distance_to_point(&self, m: &Isometry<N>, pt: &Point<N>, solid: bool) -> N {
        let ls_pt = m.inverse_transform_point(pt);
        let mins_pt = *self.mins() - ls_pt;
        let pt_maxs = ls_pt - *self.maxs();
        let shift = na::sup(&na::zero(), &na::sup(&mins_pt, &pt_maxs));

        if solid || !shift.is_zero() {
            shift.norm()
        } else {
            // FIXME: optimize that.
            -na::distance(pt, &self.project_point(m, pt, solid).point)
        }
    }

    #[inline]
    fn contains_point(&self, m: &Isometry<N>, pt: &Point<N>) -> bool {
        let ls_pt = m.inverse_transform_point(pt);
        self.contains_local_point(&ls_pt)
    }
}
<|endoftext|>


<commit_before>/// Asserts that types are equal.
///
/// # Examples
///
/// On stable Rust, using the macro requires a unique “label” when used in a
/// module scope:
///
#[cfg_attr(feature = "nightly", doc = "```ignore")]
#[cfg_attr(not(feature = "nightly"), doc = "```")]
/// # #[macro_use] extern crate static_assertions;
/// # fn main() {}
/// type A = u8;
/// type B = A;
///
/// assert_eq_type!(byte; u8, A, B);
/// ```
///
/// The [labeling limitation](index.html#limitations) is not necessary if
/// compiling on nightly Rust with the `nightly` feature enabled:
///
#[cfg_attr(feature = "nightly", doc = "```")]
#[cfg_attr(not(feature = "nightly"), doc = "```ignore")]
/// #![feature(underscore_const_names)]
/// # #[macro_use] extern crate static_assertions;
/// # type A = u8;
/// # type B = A;
///
/// assert_eq_type!(u8, A, B);
/// ```
///
/// The following produces a compilation failure because `str` and `String` do
/// not refer to the same type:
///
/// ```compile_fail
/// # #[macro_use] extern crate static_assertions;
/// # fn main() {
/// assert_eq_type!(str, String);
/// # }
/// ```
#[macro_export(local_inner_macros)]
macro_rules! assert_eq_type {
    ($($xs:tt)+) => { _assert_eq_type!($($xs)+); };
}

#[doc(hidden)]
#[cfg(feature = "nightly")]
#[macro_export(local_inner_macros)]
macro_rules! _assert_eq_type {
    ($x:ty, $($xs:ty),+ $(,)*) => {
        const _: fn() = || {
            fn assert_eq_type_gen<T: ?Sized>(a: &T) -> &T { a }
            $({
                // Test both ways to ensure that `Deref` coercions don't pass
                fn assert_eq_type0(a: &$xs) -> &$x { assert_eq_type_gen(a) }
                fn assert_eq_type1(a: &$x) -> &$xs { assert_eq_type_gen(a) }
            })+
        };
    };
}

#[doc(hidden)]
#[cfg(not(feature = "nightly"))]
#[macro_export(local_inner_macros)]
macro_rules! _assert_eq_type {
    ($x:ty, $($xs:ty),+ $(,)*) => { {
        fn assert_eq_type_gen<T: ?Sized>(a: &T) -> &T { a }
        $({
            // Test both ways to ensure that `Deref` coercions don't pass
            fn assert_eq_type0(a: &$xs) -> &$x { assert_eq_type_gen(a) }
            fn assert_eq_type1(a: &$x) -> &$xs { assert_eq_type_gen(a) }
        })+
    } };
    ($label:ident; $($xs:tt)+) => {
        #[allow(dead_code, non_snake_case)]
        fn $label() { assert_eq_type!($($xs)+); }
    };
}
<commit_msg>Change approach for `assert_eq_type!`<commit_after>/// Asserts that types are equal.
///
/// # Examples
///
/// On stable Rust, using the macro requires a unique “label” when used in a
/// module scope:
///
#[cfg_attr(feature = "nightly", doc = "```ignore")]
#[cfg_attr(not(feature = "nightly"), doc = "```")]
/// # #[macro_use] extern crate static_assertions;
/// # fn main() {}
/// type A = u8;
/// type B = A;
///
/// assert_eq_type!(byte; u8, A, B);
/// ```
///
/// The [labeling limitation](index.html#limitations) is not necessary if
/// compiling on nightly Rust with the `nightly` feature enabled:
///
#[cfg_attr(feature = "nightly", doc = "```")]
#[cfg_attr(not(feature = "nightly"), doc = "```ignore")]
/// #![feature(underscore_const_names)]
/// # #[macro_use] extern crate static_assertions;
/// # type A = u8;
/// # type B = A;
///
/// assert_eq_type!(u8, A, B);
/// ```
///
/// The following produces a compilation failure because `String` and `str` do
/// not refer to the same type:
///
/// ```compile_fail
/// # #[macro_use] extern crate static_assertions;
/// # fn main() {
/// assert_eq_type!(String, str);
/// # }
/// ```
#[macro_export(local_inner_macros)]
macro_rules! assert_eq_type {
    ($($xs:tt)+) => { _assert_eq_type!($($xs)+); };
}

#[doc(hidden)]
#[cfg(feature = "nightly")]
#[macro_export(local_inner_macros)]
macro_rules! _assert_eq_type {
    ($x:ty, $($xs:ty),+ $(,)*) => {
        const _: fn() = || { $( {
            struct Type<A: ?Sized>(A);
            fn assert_eq_type(x: &Type<$x>) -> &Type<$xs> { x }
        } )+ };
    };
}

#[doc(hidden)]
#[cfg(not(feature = "nightly"))]
#[macro_export(local_inner_macros)]
macro_rules! _assert_eq_type {
    ($x:ty, $($xs:ty),+ $(,)*) => { $( {
        struct Type<A: ?Sized>(A);
        fn assert_eq_type(x: &Type<$x>) -> &Type<$xs> { x }
    } )+ };
    ($label:ident; $($xs:tt)+) => {
        #[allow(dead_code, non_snake_case)]
        fn $label() { assert_eq_type!($($xs)+); }
    };
}
<|endoftext|>


<commit_before>// This file is @generated by no_atomic.sh.
// It is not intended for manual editing.

const NO_ATOMIC_CAS: &[&str] = &[
    "avr-unknown-gnu-atmega328",
    "bpfeb-unknown-none",
    "bpfel-unknown-none",
    "msp430-none-elf",
    "riscv32i-unknown-none-elf",
    "riscv32imc-unknown-none-elf",
    "thumbv4t-none-eabi",
    "thumbv6m-none-eabi",
];
#[allow(dead_code)]
const NO_ATOMIC_64: &[&str] = &[
    "arm-linux-androideabi",
    "armebv7r-none-eabi",
    "armebv7r-none-eabihf",
    "armv4t-unknown-linux-gnueabi",
    "armv5te-unknown-linux-gnueabi",
    "armv5te-unknown-linux-musleabi",
    "armv5te-unknown-linux-uclibceabi",
    "armv7r-none-eabi",
    "armv7r-none-eabihf",
    "hexagon-unknown-linux-musl",
    "mips-unknown-linux-gnu",
    "mips-unknown-linux-musl",
    "mips-unknown-linux-uclibc",
    "mipsel-unknown-linux-gnu",
    "mipsel-unknown-linux-musl",
    "mipsel-unknown-linux-uclibc",
    "mipsel-unknown-none",
    "mipsisa32r6-unknown-linux-gnu",
    "mipsisa32r6el-unknown-linux-gnu",
    "powerpc-unknown-freebsd",
    "powerpc-unknown-linux-gnu",
    "powerpc-unknown-linux-gnuspe",
    "powerpc-unknown-linux-musl",
    "powerpc-unknown-netbsd",
    "powerpc-unknown-openbsd",
    "powerpc-wrs-vxworks",
    "powerpc-wrs-vxworks-spe",
    "riscv32gc-unknown-linux-gnu",
    "riscv32gc-unknown-linux-musl",
    "riscv32imac-unknown-none-elf",
    "riscv32imc-esp-espidf",
    "thumbv7em-none-eabi",
    "thumbv7em-none-eabihf",
    "thumbv7m-none-eabi",
    "thumbv8m.base-none-eabi",
    "thumbv8m.main-none-eabi",
    "thumbv8m.main-none-eabihf",
    "mipsel-sony-psp",
    "thumbv4t-none-eabi",
    "thumbv6m-none-eabi",
];
#[allow(dead_code)]
const NO_ATOMIC: &[&str] = &[
    "avr-unknown-gnu-atmega328",
    "msp430-none-elf",
    "riscv32i-unknown-none-elf",
    "riscv32imc-unknown-none-elf",
];
<commit_msg>Update list of targets that do not support Atomic*64<commit_after>// This file is @generated by no_atomic.sh.
// It is not intended for manual editing.

const NO_ATOMIC_CAS: &[&str] = &[
    "avr-unknown-gnu-atmega328",
    "bpfeb-unknown-none",
    "bpfel-unknown-none",
    "msp430-none-elf",
    "riscv32i-unknown-none-elf",
    "riscv32imc-unknown-none-elf",
    "thumbv4t-none-eabi",
    "thumbv6m-none-eabi",
];
#[allow(dead_code)]
const NO_ATOMIC_64: &[&str] = &[
    "arm-linux-androideabi",
    "armebv7r-none-eabi",
    "armebv7r-none-eabihf",
    "armv4t-unknown-linux-gnueabi",
    "armv5te-unknown-linux-gnueabi",
    "armv5te-unknown-linux-musleabi",
    "armv5te-unknown-linux-uclibceabi",
    "armv7r-none-eabi",
    "armv7r-none-eabihf",
    "hexagon-unknown-linux-musl",
    "m68k-unknown-linux-gnu",
    "mips-unknown-linux-gnu",
    "mips-unknown-linux-musl",
    "mips-unknown-linux-uclibc",
    "mipsel-unknown-linux-gnu",
    "mipsel-unknown-linux-musl",
    "mipsel-unknown-linux-uclibc",
    "mipsel-unknown-none",
    "mipsisa32r6-unknown-linux-gnu",
    "mipsisa32r6el-unknown-linux-gnu",
    "powerpc-unknown-freebsd",
    "powerpc-unknown-linux-gnu",
    "powerpc-unknown-linux-gnuspe",
    "powerpc-unknown-linux-musl",
    "powerpc-unknown-netbsd",
    "powerpc-unknown-openbsd",
    "powerpc-wrs-vxworks",
    "powerpc-wrs-vxworks-spe",
    "riscv32gc-unknown-linux-gnu",
    "riscv32gc-unknown-linux-musl",
    "riscv32imac-unknown-none-elf",
    "riscv32imc-esp-espidf",
    "thumbv7em-none-eabi",
    "thumbv7em-none-eabihf",
    "thumbv7m-none-eabi",
    "thumbv8m.base-none-eabi",
    "thumbv8m.main-none-eabi",
    "thumbv8m.main-none-eabihf",
    "armv6k-nintendo-3ds",
    "mipsel-sony-psp",
    "thumbv4t-none-eabi",
    "thumbv6m-none-eabi",
];
#[allow(dead_code)]
const NO_ATOMIC: &[&str] = &[
    "avr-unknown-gnu-atmega328",
    "msp430-none-elf",
    "riscv32i-unknown-none-elf",
    "riscv32imc-unknown-none-elf",
];
<|endoftext|>


<commit_before>use serde_json::*;

use super::super::lex::*;

///
/// Creates a lexing tool for the scripting language
///
pub fn create_lex_script_tool() -> StringLexingTool {
    // Parse the lexer
    let script_json = from_str::<Vec<LexToolSymbol>>(include_str!("syntax_lexer.json")).unwrap();

    // The name isn't used here, but define it anyway
    let lex_defn = LexToolInput {
        new_tool_name:  String::from("lex-script"),
        symbols:        script_json
    };

    // Create the lexing tool with this definition
    StringLexingTool::from_lex_tool_input(&lex_defn)
}

#[cfg(test)]
mod test {
    use std::error::Error;
    use super::*;

    fn lex_tokens(input: &str) -> Vec<String> {
        let lex_tool = create_lex_script_tool();

        lex_tool
            .lex(input)
            .iter()
            .map(|x| x.token.clone())
            .collect()
    }

    #[test]
    fn can_parse_syntax_json() {
        let script_json = from_str::<Value>(include_str!("syntax_lexer.json"));

        if script_json.is_err() {
            println!("{:?}", script_json);
            println!("{:?}", script_json.unwrap_err().description());

            assert!(false);
        }
    }

    #[test]
    fn json_can_be_deserialized() {
        let script_json = from_str::<Vec<LexToolSymbol>>(include_str!("syntax_lexer.json"));

        if script_json.is_err() {
            println!("{:?}", script_json);
        }

        script_json.unwrap();
    }

    #[test]
    fn can_create_tool() {
        let _tool = create_lex_script_tool();
    }

    #[test]
    fn can_lex_identifier() {
        assert!(lex_tokens("something") == vec![ String::from("Identifier") ]);
    }

    #[test]
    fn can_lex_identifier_with_hyphen() {
        assert!(lex_tokens("something-something") == vec![ String::from("Identifier") ]);
    }

    #[test]
    fn can_lex_let_keyword() {
        assert!(lex_tokens("let") == vec![ String::from("let") ]);
    }

    #[test]
    fn can_lex_whitespace() {
        assert!(lex_tokens(" ") == vec![ String::from("Whitespace") ]);
    }

    #[test]
    fn can_lex_plus_symbol() {
        assert!(lex_tokens("+") == vec![ String::from("+") ]);
    }

    #[test]
    fn can_lex_dot_symbol() {
        assert!(lex_tokens(".") == vec![ String::from(".") ]);
    }

    #[test]
    fn can_lex_newline() {
        assert!(lex_tokens("\n") == vec![ String::from("Newline") ]);
    }

    #[test]
    fn can_lex_simple_number() {
        assert!(lex_tokens("1") == vec![ String::from("Number") ]);
    }

    #[test]
    fn can_lex_longer_number() {
        assert!(lex_tokens("123") == vec![ String::from("Number") ]);
    }

    #[test]
    fn can_lex_decimal_number() {
        assert!(lex_tokens("1.21") == vec![ String::from("Number") ]);
    }

    #[test]
    fn can_lex_decimal_with_exponent() {
        assert!(lex_tokens("1.2e10") == vec![ String::from("Number") ]);
    }

    #[test]
    fn can_lex_integer_with_exponent() {
        assert!(lex_tokens("1e10") == vec![ String::from("Number") ]);
    }

    #[test]
    fn can_lex_decimal_number_beginning_with_dot() {
        assert!(lex_tokens(".21") == vec![ String::from("Number") ]);
    }
}
<commit_msg>Seems that “Foo””Bar” is parsed as a single string, not two strings<commit_after>use serde_json::*;

use super::super::lex::*;

///
/// Creates a lexing tool for the scripting language
///
pub fn create_lex_script_tool() -> StringLexingTool {
    // Parse the lexer
    let script_json = from_str::<Vec<LexToolSymbol>>(include_str!("syntax_lexer.json")).unwrap();

    // The name isn't used here, but define it anyway
    let lex_defn = LexToolInput {
        new_tool_name:  String::from("lex-script"),
        symbols:        script_json
    };

    // Create the lexing tool with this definition
    StringLexingTool::from_lex_tool_input(&lex_defn)
}

#[cfg(test)]
mod test {
    use std::error::Error;
    use super::*;

    fn lex_tokens(input: &str) -> Vec<String> {
        let lex_tool = create_lex_script_tool();

        lex_tool
            .lex(input)
            .iter()
            .map(|x| x.token.clone())
            .collect()
    }

    #[test]
    fn can_parse_syntax_json() {
        let script_json = from_str::<Value>(include_str!("syntax_lexer.json"));

        if script_json.is_err() {
            println!("{:?}", script_json);
            println!("{:?}", script_json.unwrap_err().description());

            assert!(false);
        }
    }

    #[test]
    fn json_can_be_deserialized() {
        let script_json = from_str::<Vec<LexToolSymbol>>(include_str!("syntax_lexer.json"));

        if script_json.is_err() {
            println!("{:?}", script_json);
        }

        script_json.unwrap();
    }

    #[test]
    fn can_create_tool() {
        let _tool = create_lex_script_tool();
    }

    #[test]
    fn can_lex_identifier() {
        assert!(lex_tokens("something") == vec![ String::from("Identifier") ]);
    }

    #[test]
    fn can_lex_identifier_with_hyphen() {
        assert!(lex_tokens("something-something") == vec![ String::from("Identifier") ]);
    }

    #[test]
    fn can_lex_let_keyword() {
        assert!(lex_tokens("let") == vec![ String::from("let") ]);
    }

    #[test]
    fn can_lex_whitespace() {
        assert!(lex_tokens(" ") == vec![ String::from("Whitespace") ]);
    }

    #[test]
    fn can_lex_plus_symbol() {
        assert!(lex_tokens("+") == vec![ String::from("+") ]);
    }

    #[test]
    fn can_lex_dot_symbol() {
        assert!(lex_tokens(".") == vec![ String::from(".") ]);
    }

    #[test]
    fn can_lex_newline() {
        assert!(lex_tokens("\n") == vec![ String::from("Newline") ]);
    }

    #[test]
    fn can_lex_simple_number() {
        assert!(lex_tokens("1") == vec![ String::from("Number") ]);
    }

    #[test]
    fn can_lex_simple_string() {
        assert!(lex_tokens("\"Foo\"") == vec![ String::from("String") ]);
    }

    #[test]
    fn can_lex_two_strings() {
        assert!(lex_tokens("\"Foo\"\"Bar\"") == vec![ String::from("String"), String::from("String") ]);
    }

    #[test]
    fn can_lex_longer_number() {
        assert!(lex_tokens("123") == vec![ String::from("Number") ]);
    }

    #[test]
    fn can_lex_decimal_number() {
        assert!(lex_tokens("1.21") == vec![ String::from("Number") ]);
    }

    #[test]
    fn can_lex_decimal_with_exponent() {
        assert!(lex_tokens("1.2e10") == vec![ String::from("Number") ]);
    }

    #[test]
    fn can_lex_integer_with_exponent() {
        assert!(lex_tokens("1e10") == vec![ String::from("Number") ]);
    }

    #[test]
    fn can_lex_decimal_number_beginning_with_dot() {
        assert!(lex_tokens(".21") == vec![ String::from("Number") ]);
    }
}
<|endoftext|>


<commit_before>extern mod std;

extern mod crypto {
  fn SHA1(src: *u8, sz: libc::c_uint, out: *u8) -> *u8;
  fn MD5(src: *u8, sz: libc::c_uint, out: *u8) -> *u8;
  fn SHA224(src: *u8, sz: libc::c_uint, out: *u8) -> *u8;
  fn SHA256(src: *u8, sz: libc::c_uint, out: *u8) -> *u8;
  fn SHA384(src: *u8, sz: libc::c_uint, out: *u8) -> *u8;
  fn SHA512(src: *u8, sz: libc::c_uint, out: *u8) -> *u8;
}

enum HashMethod { MD5, SHA1, SHA224, SHA256, SHA384, SHA512 }

struct HashEngine {
  engine:      (extern unsafe fn(*u8, libc::c_uint, *u8) -> *u8),
  digest_size: uint,
  block_size:  uint
}

impl HashEngine {
  fn new(engine: HashMethod) -> ~HashEngine {
    unsafe {
      match engine {
        MD5    => ~HashEngine{ engine: crypto::MD5,    digest_size: 16, block_size: 64  },
        SHA1   => ~HashEngine{ engine: crypto::SHA1,   digest_size: 20, block_size: 64  },
        SHA224 => ~HashEngine{ engine: crypto::SHA224, digest_size: 28, block_size: 64  },
        SHA256 => ~HashEngine{ engine: crypto::SHA256, digest_size: 32, block_size: 64  },
        SHA384 => ~HashEngine{ engine: crypto::SHA384, digest_size: 48, block_size: 64  },
        SHA512 => ~HashEngine{ engine: crypto::SHA512, digest_size: 64, block_size: 128 }
      }
    }
  }

  fn hash(&self, data: ~[u8]) -> ~Digest {
    let hash_func = self.engine;
    Digest::new(
      unsafe {
        vec::from_buf(
          hash_func(
            vec::raw::to_ptr(data),
            data.len() as libc::c_uint, ptr::null()
          ),
          self.digest_size
        )
      }
    )
  }

  fn hmac(&self, key: ~str, message: ~str) -> ~Digest {
    let computed_key: &str = match key.len() {
      _ if key.len() > self.block_size => self.hash(key.to_bytes()).hexdigest(), // TODO this should be .digest more likely
      _ if key.len() < self.block_size => key + str::from_bytes(vec::from_elem(self.block_size - key.len(), 0)),
      _ => key
    };

    let o_key_pad = HashEngine::xor_with(computed_key, 0x5c);
    let i_key_pad = HashEngine::xor_with(computed_key, 0x36);
    let bytes = message.to_bytes();

    self.hash(o_key_pad + self.hash(i_key_pad + bytes).digest)
  }

  priv fn xor_with(subject: &str, val: u8) -> ~[u8] {
    do subject.to_bytes().map |&c| { val ^ c }
  }
}

struct Digest { digest: ~[u8] }

impl Digest {
  fn new(digest: ~[u8]) -> ~Digest { return ~Digest{ digest: digest } }

  fn hexdigest(&self) -> ~str {
    let mut acc = ~"";
    for self.digest.each |&byte| { acc += fmt!("%02x", byte as uint); }
    acc
  }
}

fn sha1(data: ~[u8]) -> ~Digest {
  unsafe {
    HashEngine::new(SHA1).hash(data)
  }
}

fn sha224(data: ~[u8]) -> ~Digest {
  unsafe {
    HashEngine::new(SHA224).hash(data)
  }
}

fn sha256(data: ~[u8]) -> ~Digest {
  unsafe {
    HashEngine::new(SHA256).hash(data)
  }
}

fn sha384(data: ~[u8]) -> ~Digest {
  unsafe {
    HashEngine::new(SHA384).hash(data)
  }
}

fn sha512(data: ~[u8]) -> ~Digest {
  unsafe {
    HashEngine::new(SHA512).hash(data)
  }
}

fn md5(data: ~[u8]) -> ~Digest {
  unsafe {
    HashEngine::new(MD5).hash(data)
  }
}

fn hmac_md5(key: ~str, message: ~str) -> ~Digest {
  HashEngine::new(MD5).hmac(key, message)
}

fn hmac_sha1(key: ~str, message: ~str) -> ~Digest {
  HashEngine::new(SHA1).hmac(key, message)
}

fn hmac_sha256(key: ~str, message: ~str) -> ~Digest {
  HashEngine::new(SHA256).hmac(key, message)
}

fn hmac_sha512(key: ~str, message: ~str) -> ~Digest {
  HashEngine::new(SHA512).hmac(key, message)
}

fn main() {
  if core::os::args().len() < 2 { fail!(~"Nothing to hash: supply an argument") }
  io::println(sha1(core::os::args()[1].clone().to_bytes()).hexdigest());
}

#[test]
fn test_sha1() {
  assert_eq!(sha1("testing SHA1".to_bytes()).hexdigest(), ~"a07bb2f7f56b7b47e54fd9e29b4629c8967dde4c");
}

#[test]
fn test_md5() {
  assert_eq!(md5("testing MD5".to_bytes()).hexdigest(), ~"da35df95d34f166890fa759f5e00e94c");
}

#[test]
fn test_hmac_sha1_with_a_40_byte_key() {
  assert_eq!(
    hmac_sha1(~"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", ~"some data to hmac").hexdigest(),
    ~"18f652f1bced11930d55a72ad60bb5e671573dec"
  )
}

#[test]
fn test_hmac_sha1_with_a_too_long_key() {
  assert_eq!(
    hmac_sha1(~"keyxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", ~"some data to hmac").hexdigest(),
    ~"64109bfc49893757afcc3c0c80b8eb61ce0f60a5"
  )
}

#[test]
fn test_hmac_sha1_with_a_too_short_key() {
  assert_eq!(
    hmac_sha1(~"my key", ~"some data to hmac").hexdigest(),
    ~"331c9bf4d7dc8ad7e9bab2f566c8612042a9f4e2"
  )
}

#[test]
fn test_hmac_sha256() {
  assert_eq!(
    hmac_sha256(~"my key", ~"some data to hmac").hexdigest(),
    ~"17764a8a2e8c23ae3b9e1781c5ebeb5754c7920806021032e9133051e10118be"
  );
}

#[test]
fn test_hmac_sha512() {
  assert_eq!(
    hmac_sha512(~"my key", ~"some data to hmac").hexdigest(),
    ~"b54973feba2e436c4f0911855c80e7320d72edd37b359d6474d714718c52775f163832b79214e9fa9d400208d21372a465b2cda2a1d5ab488bbc02b0daea9626"
  );
}

#[test]
fn test_hmac_md5() {
  assert_eq!(hmac_md5(~"my key", ~"some data to hmac").hexdigest(), ~"befd13e00ee07549520ade402c664a3a");
}
<commit_msg>These are no longer unsafe after refactoring.<commit_after>extern mod std;

extern mod crypto {
  fn SHA1(src: *u8, sz: libc::c_uint, out: *u8) -> *u8;
  fn MD5(src: *u8, sz: libc::c_uint, out: *u8) -> *u8;
  fn SHA224(src: *u8, sz: libc::c_uint, out: *u8) -> *u8;
  fn SHA256(src: *u8, sz: libc::c_uint, out: *u8) -> *u8;
  fn SHA384(src: *u8, sz: libc::c_uint, out: *u8) -> *u8;
  fn SHA512(src: *u8, sz: libc::c_uint, out: *u8) -> *u8;
}

enum HashMethod { MD5, SHA1, SHA224, SHA256, SHA384, SHA512 }

struct HashEngine {
  engine:      (extern unsafe fn(*u8, libc::c_uint, *u8) -> *u8),
  digest_size: uint,
  block_size:  uint
}

impl HashEngine {
  fn new(engine: HashMethod) -> ~HashEngine {
    unsafe {
      match engine {
        MD5    => ~HashEngine{ engine: crypto::MD5,    digest_size: 16, block_size: 64  },
        SHA1   => ~HashEngine{ engine: crypto::SHA1,   digest_size: 20, block_size: 64  },
        SHA224 => ~HashEngine{ engine: crypto::SHA224, digest_size: 28, block_size: 64  },
        SHA256 => ~HashEngine{ engine: crypto::SHA256, digest_size: 32, block_size: 64  },
        SHA384 => ~HashEngine{ engine: crypto::SHA384, digest_size: 48, block_size: 64  },
        SHA512 => ~HashEngine{ engine: crypto::SHA512, digest_size: 64, block_size: 128 }
      }
    }
  }

  fn hash(&self, data: ~[u8]) -> ~Digest {
    let hash_func = self.engine;
    Digest::new(
      unsafe {
        vec::from_buf(
          hash_func(
            vec::raw::to_ptr(data),
            data.len() as libc::c_uint, ptr::null()
          ),
          self.digest_size
        )
      }
    )
  }

  fn hmac(&self, key: ~str, message: ~str) -> ~Digest {
    let computed_key: &str = match key.len() {
      _ if key.len() > self.block_size => self.hash(key.to_bytes()).hexdigest(), // TODO this should be .digest more likely
      _ if key.len() < self.block_size => key + str::from_bytes(vec::from_elem(self.block_size - key.len(), 0)),
      _ => key
    };

    let o_key_pad = HashEngine::xor_with(computed_key, 0x5c);
    let i_key_pad = HashEngine::xor_with(computed_key, 0x36);
    let bytes = message.to_bytes();

    self.hash(o_key_pad + self.hash(i_key_pad + bytes).digest)
  }

  priv fn xor_with(subject: &str, val: u8) -> ~[u8] {
    do subject.to_bytes().map |&c| { val ^ c }
  }
}

struct Digest { digest: ~[u8] }

impl Digest {
  fn new(digest: ~[u8]) -> ~Digest { return ~Digest{ digest: digest } }

  fn hexdigest(&self) -> ~str {
    let mut acc = ~"";
    for self.digest.each |&byte| { acc += fmt!("%02x", byte as uint); }
    acc
  }
}

fn sha1(data: ~[u8]) -> ~Digest {
  HashEngine::new(SHA1).hash(data)
}

fn sha224(data: ~[u8]) -> ~Digest {
  HashEngine::new(SHA224).hash(data)
}

fn sha256(data: ~[u8]) -> ~Digest {
  HashEngine::new(SHA256).hash(data)
}

fn sha384(data: ~[u8]) -> ~Digest {
  HashEngine::new(SHA384).hash(data)
}

fn sha512(data: ~[u8]) -> ~Digest {
  HashEngine::new(SHA512).hash(data)
}

fn md5(data: ~[u8]) -> ~Digest {
  HashEngine::new(MD5).hash(data)
}

fn hmac_md5(key: ~str, message: ~str) -> ~Digest {
  HashEngine::new(MD5).hmac(key, message)
}

fn hmac_sha1(key: ~str, message: ~str) -> ~Digest {
  HashEngine::new(SHA1).hmac(key, message)
}

fn hmac_sha256(key: ~str, message: ~str) -> ~Digest {
  HashEngine::new(SHA256).hmac(key, message)
}

fn hmac_sha512(key: ~str, message: ~str) -> ~Digest {
  HashEngine::new(SHA512).hmac(key, message)
}

fn main() {
  if core::os::args().len() < 2 { fail!(~"Nothing to hash: supply an argument") }
  io::println(sha1(core::os::args()[1].clone().to_bytes()).hexdigest());
}

#[test]
fn test_sha1() {
  assert_eq!(sha1("testing SHA1".to_bytes()).hexdigest(), ~"a07bb2f7f56b7b47e54fd9e29b4629c8967dde4c");
}

#[test]
fn test_md5() {
  assert_eq!(md5("testing MD5".to_bytes()).hexdigest(), ~"da35df95d34f166890fa759f5e00e94c");
}

#[test]
fn test_hmac_sha1_with_a_40_byte_key() {
  assert_eq!(
    hmac_sha1(~"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", ~"some data to hmac").hexdigest(),
    ~"18f652f1bced11930d55a72ad60bb5e671573dec"
  )
}

#[test]
fn test_hmac_sha1_with_a_too_long_key() {
  assert_eq!(
    hmac_sha1(~"keyxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", ~"some data to hmac").hexdigest(),
    ~"64109bfc49893757afcc3c0c80b8eb61ce0f60a5"
  )
}

#[test]
fn test_hmac_sha1_with_a_too_short_key() {
  assert_eq!(
    hmac_sha1(~"my key", ~"some data to hmac").hexdigest(),
    ~"331c9bf4d7dc8ad7e9bab2f566c8612042a9f4e2"
  )
}

#[test]
fn test_hmac_sha256() {
  assert_eq!(
    hmac_sha256(~"my key", ~"some data to hmac").hexdigest(),
    ~"17764a8a2e8c23ae3b9e1781c5ebeb5754c7920806021032e9133051e10118be"
  );
}

#[test]
fn test_hmac_sha512() {
  assert_eq!(
    hmac_sha512(~"my key", ~"some data to hmac").hexdigest(),
    ~"b54973feba2e436c4f0911855c80e7320d72edd37b359d6474d714718c52775f163832b79214e9fa9d400208d21372a465b2cda2a1d5ab488bbc02b0daea9626"
  );
}

#[test]
fn test_hmac_md5() {
  assert_eq!(hmac_md5(~"my key", ~"some data to hmac").hexdigest(), ~"befd13e00ee07549520ade402c664a3a");
}
<|endoftext|>