<commit_before>//! Rope slices
//!
//! While we could just have the slicing methods on `Rope`s return `&str`s,
//! this would have some serious drawbacks: to slice a rope as `&str`, we'd have
//! to collect all the `Rope`'s characters into a `String` and then slice that
//! `String`. Creating an intermediate `String` would be slow, would cause
//! unnecessary allocations, and, in the case of `Rope::slice_mut()`, would
//! return a mutable slice of a _new `String`_ –- mutating the slice would _not_
//! mutate the underlying `Rope`.

use std::fmt;
use std::cmp;

use collections::range::RangeArgument;

use super::internals::Node;

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

    #[test]
    fn char_indices() {
        let string = "aaaaabbbbbbccccccccccccdefgdefgaabababab";
        let rope = Rope::from(string);
        let rope_slice = rope.slice(4..8);
        let string_slice = &string[4..8];
        let indices = rope_slice.char_indices().zip(string_slice.char_indices());
        for ((ridx, rch), (sidx, sch)) in indices {
            assert_eq!(rch, sch);
            assert_eq!(ridx, sidx);
        }
    }

    #[test]
    fn to() {
        let string = "aaaaabbbbbbccccccccccccdefgdefgaabababab";
        let rope = Rope::from(string);
        let rope_slice = rope.slice(1..10);
        let string_slice = &string[1..10];
        assert_eq!(&rope_slice, string_slice)
    }

    // #[test]
    // fn between() {
    //     let string = "aaaaabbbbbbccccccccccccdefgdefgaabababab";
    //     let rope = Rope::from(string);
    //     let rope_slice = rope.slice(1...10usize);
    //     let string_slice = &string[1...10];
    //     assert_eq!(&rope_slice, string_slice)
    // }

    #[test]
    fn until() {
        let string = "aaaaabbbbbbccccccccccdefgdefgaabababab";
        let rope = Rope::from(string);
        let rope_slice = rope.slice(..10);
        let string_slice = &string[..10];
        assert_eq!(&rope_slice, string_slice)
    }

    #[test]
    fn from() {
        let string = "aaaaabbbbbbccccccccccccdefgdefgaabababab";
        let rope = Rope::from(string);
        let rope_slice = rope.slice(5..);
        let string_slice = &string[5..];
        assert_eq!(&rope_slice, string_slice)
    }

    #[test]
    fn full() {
        let string = "aaaaabbbbbbccccccccccccdefgdefgaabababab";
        let rope = Rope::from(string);
        let rope_slice = rope.slice(..);
        let string_slice = &string[..];
        assert_eq!(&rope_slice, string_slice)
    }

}

/// An immutable borrowed slice of a `Rope`.
///
/// A RopeSlice represents an immutable borrowed slice of some or all the
/// characters in a `Rope`.
#[derive(Debug)]
pub struct RopeSlice<'a> { node: &'a Node
                         , offset: usize
                         , len: usize
                         }

impl<'a> fmt::Display for RopeSlice<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        // TODO: don't create an intermediate string?
        write!(f, "{}", self.chars().collect::<String>())
    }
}

/// An mutable borrowed slice of a `Rope`.
///
/// A `RopeSliceMut` represents a mutable borrowed slice of some or all the
/// characters in a `Rope`.
#[derive(Debug)]
pub struct RopeSliceMut<'a> { node: &'a mut Node
                            , offset: usize
                            , len: usize
                            }
impl<'a> RopeSliceMut<'a>  {

    pub fn new<R>(node: &'a mut Node, range: R) -> Self
    where R: RangeArgument<usize> {
        let len = node.len();

        // if the RangeArgument doesn't have a defined start index,
        // the slice begins at the 0th index.
        let start = *range.start().unwrap_or(&0);
        // similarly, if there's no defined end, then the end index
        // is the last index in the Rope.
        let end = *range.end().unwrap_or(&node.len());

        let slice_len = end - start;

        // find the lowest node that contains both the slice start index and
        // the end index
        let (node, offset) = if start == 0 && end == len {
            // if the slice contains the entire rope, then the spanning node
            // is the root node
            (node, 0)
        } else {
            node.spanning_mut(start, slice_len)
        };

        RopeSliceMut { node: node
                     , offset: offset
                     , len: slice_len }
    }
}

impl<'a> RopeSlice<'a> {
    pub fn new<R>(node: &'a Node, range: R) -> Self
    where R: RangeArgument<usize> {
        let len = node.len();

        // if the RangeArgument doesn't have a defined start index,
        // the slice begins at the 0th index.
        let start = *range.start().unwrap_or(&0);
        // similarly, if there's no defined end, then the end index
        // is the last index in the Rope.
        let end = *range.end().unwrap_or(&node.len());

        let slice_len = end - start;

        // find the lowest node that contains both the slice start index and
        // the end index
        let (node, offset) = if start == 0 && end == len {
            // if the slice contains the entire rope, then the spanning node
            // is the root node
            (node, 0)
        } else {
            node.spanning(start, slice_len)
        };

        RopeSlice { node: node
                  , offset: offset
                  , len: slice_len }
    }

    #[inline]
    fn slice_char_iter<I, T>(&'a self, i: I) -> impl Iterator<Item=T> + 'a
    where I: Iterator<Item=T>
        , I: 'a
        , T: Copy {
            i.skip(self.offset).take(self.len)
    }

    fn slice_strings_iter<I>(&'a self, i: I) -> impl Iterator<Item=&'a str> +'a
    where I: Iterator<Item=&'a str>
        , I: 'a {
        i.scan((self.offset, self.len), |curr, s| {
            match *curr {
                (0, 0) => None
              , (0, ref mut remaining) if *remaining < s.len() => {
                    let r = *remaining;
                    *remaining = 0;
                    Some(&s[..r])
                }
              , (0, ref mut remaining) => {
                    *remaining -= s.len();
                    Some(s)
                }
              , (ref mut offset, _) if *offset > s.len() => {
                    *offset -= s.len();
                    Some("")
                }
              , (ref mut offset, _) => {
                    let c = *offset;
                    *offset -= s.len();
                    Some(&s[c..])
                }
            }
            // if curr_len > 0 {
            //     let c = curr_offset;
            //     let res = if c > 0 {
            //         curr_offset -= s.len();
            //         if c < s.len()  {
            //             &s[c..]
            //         } else {
            //             ""
            //         }
            //     } else {
            //         s
            //     };
            //     Some(res)
            })
         .skip_while(|&s| s == "")
    }

    pub fn chars(&'a self) -> impl Iterator<Item=char> +'a  {
        self.slice_char_iter(self.node.chars())
    }

    pub fn bytes(&'a self) -> impl Iterator<Item=u8> + 'a  {
        self.slice_char_iter(self.node.bytes())
    }

    #[inline]
    pub fn split_whitespace(&'a self) -> impl Iterator<Item=&'a str> {
        self.slice_strings_iter(self.node.split_whitespace())
    }

    #[inline]
    pub fn lines(&'a self) -> impl Iterator<Item=&'a str> {
        self.slice_strings_iter(self.node.lines())
    }

    #[inline]
    pub fn char_indices(&'a self) -> impl Iterator<Item=(usize, char)> + 'a {
        self.chars().enumerate()
    }

    /// Returns true if the bytes in `self` equal the bytes in `other`
    #[inline]
    fn bytes_eq<I>(&self, other: I) -> bool
    where I: Iterator<Item=u8> {
        self.bytes().zip(other).all(|(a, b)| a == b)
    }

    #[inline]
    pub fn len(&self) -> usize { self.len }
}

//-- comparisons ----------------------------------------------------
impl<'a> cmp::Eq for RopeSlice<'a> {}
impl<'a> cmp::PartialEq for RopeSlice<'a> {
    /// A rope equals another rope if all the bytes in both are equal.

    #[inline]
    fn eq(&self, other: &RopeSlice<'a>) -> bool {
        if self.len() == other.len() {
            self.bytes_eq(other.bytes())
        } else {
            false
        }
    }
}

impl<'a> cmp::PartialEq<str> for RopeSlice<'a> {
    /// A rope equals another rope if all the bytes in both are equal.

    #[inline]
    fn eq(&self, other: &str) -> bool {
        if self.len() == other.len() {
            self.bytes_eq(other.bytes())
        } else {
            false
        }
    }
}
<commit_msg>rope: add tests for RopeSliceMut<commit_after>//! Rope slices
//!
//! While we could just have the slicing methods on `Rope`s return `&str`s,
//! this would have some serious drawbacks: to slice a rope as `&str`, we'd have
//! to collect all the `Rope`'s characters into a `String` and then slice that
//! `String`. Creating an intermediate `String` would be slow, would cause
//! unnecessary allocations, and, in the case of `Rope::slice_mut()`, would
//! return a mutable slice of a _new `String`_ –- mutating the slice would _not_
//! mutate the underlying `Rope`.

use std::fmt;
use std::cmp;

use collections::range::RangeArgument;

use super::internals::Node;

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

    #[test]
    fn char_indices() {
        let string = "aaaaabbbbbbccccccccccccdefgdefgaabababab";
        let rope = Rope::from(string);
        let rope_slice = rope.slice(4..8);
        let string_slice = &string[4..8];
        let indices = rope_slice.char_indices().zip(string_slice.char_indices());
        for ((ridx, rch), (sidx, sch)) in indices {
            assert_eq!(rch, sch);
            assert_eq!(ridx, sidx);
        }
    }

    #[test]
    fn to() {
        let string = "aaaaabbbbbbccccccccccccdefgdefgaabababab";
        let rope = Rope::from(string);
        let rope_slice = rope.slice(1..10);
        let string_slice = &string[1..10];
        assert_eq!(&rope_slice, string_slice)
    }

    // #[test]
    // fn between() {
    //     let string = "aaaaabbbbbbccccccccccccdefgdefgaabababab";
    //     let rope = Rope::from(string);
    //     let rope_slice = rope.slice(1...10usize);
    //     let string_slice = &string[1...10];
    //     assert_eq!(&rope_slice, string_slice)
    // }

    #[test]
    fn until() {
        let string = "aaaaabbbbbbccccccccccdefgdefgaabababab";
        let rope = Rope::from(string);
        let rope_slice = rope.slice(..10);
        let string_slice = &string[..10];
        assert_eq!(&rope_slice, string_slice)
    }

    #[test]
    fn from() {
        let string = "aaaaabbbbbbccccccccccccdefgdefgaabababab";
        let rope = Rope::from(string);
        let rope_slice = rope.slice(5..);
        let string_slice = &string[5..];
        assert_eq!(&rope_slice, string_slice)
    }

    #[test]
    fn full() {
        let string = "aaaaabbbbbbccccccccccccdefgdefgaabababab";
        let rope = Rope::from(string);
        let rope_slice = rope.slice(..);
        let string_slice = &string[..];
        assert_eq!(&rope_slice, string_slice)
    }

    #[test]
    fn mut_char_indices() {
        let string = "aaaaabbbbbbccccccccccccdefgdefgaabababab";
        let rope = Rope::from(string);
        let rope_slice = rope.slice_mut(4..8);
        let string_slice = &mut string[4..8];
        let indices = rope_slice.char_indices().zip(string_slice.char_indices());
        for ((ridx, rch), (sidx, sch)) in indices {
            assert_eq!(rch, sch);
            assert_eq!(ridx, sidx);
        }
    }

    #[test]
    fn mut_to() {
        let string = "aaaaabbbbbbccccccccccccdefgdefgaabababab";
        let rope = Rope::from(string);
        let rope_slice = rope.slice_mut(1..10);
        let string_slice = &mut string[1..10];
        assert_eq!(&rope_slice, string_slice)
    }

    #[test]
    fn mut_until() {
        let string = "aaaaabbbbbbccccccccccdefgdefgaabababab";
        let rope = Rope::from(string);
        let rope_slice = rope.slice_mut(..10);
        let string_slice = &string[..10];
        assert_eq!(&mut rope_slice, string_slice)
    }

    #[test]
    fn mut_from() {
        let string = "aaaaabbbbbbccccccccccccdefgdefgaabababab";
        let rope = Rope::from(string);
        let rope_slice = rope.slice_mut(5..);
        let string_slice = &mut string[5..];
        assert_eq!(&rope_slice, string_slice)
    }

    #[test]
    fn mut_full() {
        let string = "aaaaabbbbbbccccccccccccdefgdefgaabababab";
        let rope = Rope::from(string);
        let rope_slice = rope.slice_mut(..);
        let string_slice = &mut string[..];
        assert_eq!(&rope_slice, string_slice)
    }
}

/// An immutable borrowed slice of a `Rope`.
///
/// A RopeSlice represents an immutable borrowed slice of some or all the
/// characters in a `Rope`.
#[derive(Debug)]
pub struct RopeSlice<'a> { node: &'a Node
                         , offset: usize
                         , len: usize
                         }

impl<'a> fmt::Display for RopeSlice<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        // TODO: don't create an intermediate string?
        write!(f, "{}", self.chars().collect::<String>())
    }
}

/// An mutable borrowed slice of a `Rope`.
///
/// A `RopeSliceMut` represents a mutable borrowed slice of some or all the
/// characters in a `Rope`.
#[derive(Debug)]
pub struct RopeSliceMut<'a> { node: &'a mut Node
                            , offset: usize
                            , len: usize
                            }
impl<'a> RopeSliceMut<'a>  {

    pub fn new<R>(node: &'a mut Node, range: R) -> Self
    where R: RangeArgument<usize> {
        let len = node.len();

        // if the RangeArgument doesn't have a defined start index,
        // the slice begins at the 0th index.
        let start = *range.start().unwrap_or(&0);
        // similarly, if there's no defined end, then the end index
        // is the last index in the Rope.
        let end = *range.end().unwrap_or(&node.len());

        let slice_len = end - start;

        // find the lowest node that contains both the slice start index and
        // the end index
        let (node, offset) = if start == 0 && end == len {
            // if the slice contains the entire rope, then the spanning node
            // is the root node
            (node, 0)
        } else {
            node.spanning_mut(start, slice_len)
        };

        RopeSliceMut { node: node
                     , offset: offset
                     , len: slice_len }
    }
}

impl<'a> RopeSlice<'a> {
    pub fn new<R>(node: &'a Node, range: R) -> Self
    where R: RangeArgument<usize> {
        let len = node.len();

        // if the RangeArgument doesn't have a defined start index,
        // the slice begins at the 0th index.
        let start = *range.start().unwrap_or(&0);
        // similarly, if there's no defined end, then the end index
        // is the last index in the Rope.
        let end = *range.end().unwrap_or(&node.len());

        let slice_len = end - start;

        // find the lowest node that contains both the slice start index and
        // the end index
        let (node, offset) = if start == 0 && end == len {
            // if the slice contains the entire rope, then the spanning node
            // is the root node
            (node, 0)
        } else {
            node.spanning(start, slice_len)
        };

        RopeSlice { node: node
                  , offset: offset
                  , len: slice_len }
    }

    #[inline]
    fn slice_char_iter<I, T>(&'a self, i: I) -> impl Iterator<Item=T> + 'a
    where I: Iterator<Item=T>
        , I: 'a
        , T: Copy {
            i.skip(self.offset).take(self.len)
    }

    fn slice_strings_iter<I>(&'a self, i: I) -> impl Iterator<Item=&'a str> +'a
    where I: Iterator<Item=&'a str>
        , I: 'a {
        i.scan((self.offset, self.len), |curr, s| {
            match *curr {
                (0, 0) => None
              , (0, ref mut remaining) if *remaining < s.len() => {
                    let r = *remaining;
                    *remaining = 0;
                    Some(&s[..r])
                }
              , (0, ref mut remaining) => {
                    *remaining -= s.len();
                    Some(s)
                }
              , (ref mut offset, _) if *offset > s.len() => {
                    *offset -= s.len();
                    Some("")
                }
              , (ref mut offset, _) => {
                    let c = *offset;
                    *offset -= s.len();
                    Some(&s[c..])
                }
            }
            // if curr_len > 0 {
            //     let c = curr_offset;
            //     let res = if c > 0 {
            //         curr_offset -= s.len();
            //         if c < s.len()  {
            //             &s[c..]
            //         } else {
            //             ""
            //         }
            //     } else {
            //         s
            //     };
            //     Some(res)
            })
         .skip_while(|&s| s == "")
    }

    pub fn chars(&'a self) -> impl Iterator<Item=char> +'a  {
        self.slice_char_iter(self.node.chars())
    }

    pub fn bytes(&'a self) -> impl Iterator<Item=u8> + 'a  {
        self.slice_char_iter(self.node.bytes())
    }

    #[inline]
    pub fn split_whitespace(&'a self) -> impl Iterator<Item=&'a str> {
        self.slice_strings_iter(self.node.split_whitespace())
    }

    #[inline]
    pub fn lines(&'a self) -> impl Iterator<Item=&'a str> {
        self.slice_strings_iter(self.node.lines())
    }

    #[inline]
    pub fn char_indices(&'a self) -> impl Iterator<Item=(usize, char)> + 'a {
        self.chars().enumerate()
    }

    /// Returns true if the bytes in `self` equal the bytes in `other`
    #[inline]
    fn bytes_eq<I>(&self, other: I) -> bool
    where I: Iterator<Item=u8> {
        self.bytes().zip(other).all(|(a, b)| a == b)
    }

    #[inline]
    pub fn len(&self) -> usize { self.len }
}

//-- comparisons ----------------------------------------------------
impl<'a> cmp::Eq for RopeSlice<'a> {}
impl<'a> cmp::PartialEq for RopeSlice<'a> {
    /// A rope equals another rope if all the bytes in both are equal.

    #[inline]
    fn eq(&self, other: &RopeSlice<'a>) -> bool {
        if self.len() == other.len() {
            self.bytes_eq(other.bytes())
        } else {
            false
        }
    }
}

impl<'a> cmp::PartialEq<str> for RopeSlice<'a> {
    /// A rope equals another rope if all the bytes in both are equal.

    #[inline]
    fn eq(&self, other: &str) -> bool {
        if self.len() == other.len() {
            self.bytes_eq(other.bytes())
        } else {
            false
        }
    }
}
<|endoftext|>


<commit_before>#[macro_use] extern crate rustler;
#[macro_use] extern crate lazy_static;

// Disabled for now, but still
// wondering why rustler generated this in the first place
// #[macro_use] extern crate rustler_codegen;

use rustler::{Env, Term, NifResult, Encoder};
extern crate pact_mock_server;
extern crate libc;

use pact_mock_server::create_mock_server;
use pact_mock_server::MockServerError;
use pact_mock_server::mock_server_mismatches;
use pact_mock_server::mock_server_matched;
use pact_mock_server::write_pact_file;
use pact_mock_server::WritePactFileErr;
use pact_mock_server::cleanup_mock_server_ffi;
mod atoms {
    rustler_atoms! {
        atom ok;
        atom error;
        atom mock_server_failed_to_start;
        atom invalid_pact_json;
        atom io_error;
        atom no_mock_server_running_on_port;
        //atom __true__ = "true";
        //atom __false__ = "false";
    }
}

rustler_export_nifs! {
    "Elixir.PactElixir.RustPactMockServerFacade",
    [
        ("create_mock_server", 2, create_mock_server_call),
        ("mock_server_mismatches", 1, mock_server_mismatches_call),
        ("mock_server_matched", 1, mock_server_matched_call),
        ("write_pact_file", 2, write_pact_file_call),
        ("cleanup_mock_server", 1, cleanup_mock_server_call)
    ],
    None
}

fn create_mock_server_call<'a>(env: Env<'a>, args: &[Term<'a>]) -> NifResult<Term<'a>> {
    let pact_json: &str = try!(args[0].decode());
    let port_arg: i32 = try!(args[1].decode());

    match create_mock_server(pact_json, port_arg) {
        Ok(port) =>
            Ok((atoms::ok(), port).encode(env)),
        Err(MockServerError::MockServerFailedToStart) =>
            Ok((atoms::error(), atoms::mock_server_failed_to_start()).encode(env)),
        Err(MockServerError::InvalidPactJson) =>
            Ok((atoms::error(), atoms::invalid_pact_json()).encode(env))
    }
}

fn mock_server_mismatches_call<'a>(env: Env<'a>, args: &[Term<'a>]) -> NifResult<Term<'a>> {
    let port: i32 = try!(args[0].decode());
    // TODO: This works only because mock_server_mismatches returns a string now:
    // Calling to_string() on json!(mismatches) in pact-reference/rust/pact_mock_server/src/lib.rs:657
    Ok((atoms::ok(), mock_server_mismatches(port)).encode(env))
}

fn mock_server_matched_call<'a>(env: Env<'a>, args: &[Term<'a>]) -> NifResult<Term<'a>> {
    let port: i32 = try!(args[0].decode());

    let matched: bool = mock_server_matched(port);

    Ok((atoms::ok(), matched).encode(env))
}

fn write_pact_file_call<'a>(env: Env<'a>, args: &[Term<'a>]) -> NifResult<Term<'a>> {
    let port: i32 = try!(args[0].decode());
    let dir_path: String = try!(args[1].decode());

    match write_pact_file(port, Some(dir_path)) {
        Ok(_result) =>
            Ok((atoms::ok()).encode(env)),
        Err(WritePactFileErr::IOError) =>
            Ok((atoms::error(), atoms::io_error()).encode(env)),
        Err(WritePactFileErr::NoMockServer) =>
            Ok((atoms::error(), atoms::no_mock_server_running_on_port()).encode(env))
    }
}

fn cleanup_mock_server_call<'a>(env: Env<'a>, args: &[Term<'a>]) -> NifResult<Term<'a>> {
    let port: i32 = try!(args[0].decode());

    Ok((atoms::ok(), cleanup_mock_server_ffi(port)).encode(env))
}

<commit_msg>Removed TODO. <commit_after>#[macro_use] extern crate rustler;
#[macro_use] extern crate lazy_static;

// Disabled for now, but still
// wondering why rustler generated this in the first place
// #[macro_use] extern crate rustler_codegen;

use rustler::{Env, Term, NifResult, Encoder};
extern crate pact_mock_server;
extern crate libc;

use pact_mock_server::create_mock_server;
use pact_mock_server::MockServerError;
use pact_mock_server::mock_server_mismatches;
use pact_mock_server::mock_server_matched;
use pact_mock_server::write_pact_file;
use pact_mock_server::WritePactFileErr;
use pact_mock_server::cleanup_mock_server_ffi;
mod atoms {
    rustler_atoms! {
        atom ok;
        atom error;
        atom mock_server_failed_to_start;
        atom invalid_pact_json;
        atom io_error;
        atom no_mock_server_running_on_port;
        //atom __true__ = "true";
        //atom __false__ = "false";
    }
}

rustler_export_nifs! {
    "Elixir.PactElixir.RustPactMockServerFacade",
    [
        ("create_mock_server", 2, create_mock_server_call),
        ("mock_server_mismatches", 1, mock_server_mismatches_call),
        ("mock_server_matched", 1, mock_server_matched_call),
        ("write_pact_file", 2, write_pact_file_call),
        ("cleanup_mock_server", 1, cleanup_mock_server_call)
    ],
    None
}

fn create_mock_server_call<'a>(env: Env<'a>, args: &[Term<'a>]) -> NifResult<Term<'a>> {
    let pact_json: &str = try!(args[0].decode());
    let port_arg: i32 = try!(args[1].decode());

    match create_mock_server(pact_json, port_arg) {
        Ok(port) =>
            Ok((atoms::ok(), port).encode(env)),
        Err(MockServerError::MockServerFailedToStart) =>
            Ok((atoms::error(), atoms::mock_server_failed_to_start()).encode(env)),
        Err(MockServerError::InvalidPactJson) =>
            Ok((atoms::error(), atoms::invalid_pact_json()).encode(env))
    }
}

fn mock_server_mismatches_call<'a>(env: Env<'a>, args: &[Term<'a>]) -> NifResult<Term<'a>> {
    let port: i32 = try!(args[0].decode());
    Ok((atoms::ok(), mock_server_mismatches(port)).encode(env))
}

fn mock_server_matched_call<'a>(env: Env<'a>, args: &[Term<'a>]) -> NifResult<Term<'a>> {
    let port: i32 = try!(args[0].decode());

    let matched: bool = mock_server_matched(port);

    Ok((atoms::ok(), matched).encode(env))
}

fn write_pact_file_call<'a>(env: Env<'a>, args: &[Term<'a>]) -> NifResult<Term<'a>> {
    let port: i32 = try!(args[0].decode());
    let dir_path: String = try!(args[1].decode());

    match write_pact_file(port, Some(dir_path)) {
        Ok(_result) =>
            Ok((atoms::ok()).encode(env)),
        Err(WritePactFileErr::IOError) =>
            Ok((atoms::error(), atoms::io_error()).encode(env)),
        Err(WritePactFileErr::NoMockServer) =>
            Ok((atoms::error(), atoms::no_mock_server_running_on_port()).encode(env))
    }
}

fn cleanup_mock_server_call<'a>(env: Env<'a>, args: &[Term<'a>]) -> NifResult<Term<'a>> {
    let port: i32 = try!(args[0].decode());

    Ok((atoms::ok(), cleanup_mock_server_ffi(port)).encode(env))
}

<|endoftext|>


<commit_before>// Copyright (c) 2013-2015 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

use capnp::capability::{Server};
use capnp::Error;
use capnp::primitive_list;

use capnp_rpc::rpc::{LocalClient};

use calculator_capnp::calculator;
use gj::Promise;

struct ValueImpl {
    value: f64
}

impl ValueImpl {
    fn new(value: f64) -> ValueImpl {
        ValueImpl { value: value }
    }
}

impl calculator::value::Server for ValueImpl {
    fn read(&mut self,
            _params: calculator::value::ReadParams,
            mut results: calculator::value::ReadResults)
            -> Promise<calculator::value::ReadResults, Error>
    {
        results.get().set_value(self.value);
        Promise::ok(results)
    }
}

fn evaluate_impl(
    expression: calculator::expression::Reader,
    params: Option<primitive_list::Reader<f64>>) -> Promise<f64, Error> {

    match pry!(expression.which()) {
        calculator::expression::Literal(v) => {
            Promise::ok(v)
        },
        calculator::expression::PreviousResult(_p) => {
            unimplemented!()
//            Ok(try!(p.read_request().send().wait()).get_value())
        }
        calculator::expression::Parameter(p) => {
            match params {
                Some(params) if p < params.len() => {
                    Promise::ok(params.get(p))
                }
                _ => {
                    Promise::err(Error::failed(format!("bad parameter: {}", p)))
                }
            }
        }
        calculator::expression::Call(_call) => {
            unimplemented!()
        }
    }
}

struct FunctionImpl {
    param_count: u32,
    body: ::capnp::message::Builder<::capnp::message::HeapAllocator>,
}

impl FunctionImpl {
    fn new(param_count: u32, body: calculator::expression::Reader) -> FunctionImpl {
        let mut result = FunctionImpl {
            param_count: param_count,
            body: ::capnp::message::Builder::new_default()
        };
        result.body.set_root(body).unwrap();
        result
    }
}

impl calculator::function::Server for FunctionImpl {
    fn call(&mut self,
            params: calculator::function::CallParams,
            mut results: calculator::function::CallResults)
        -> Promise<calculator::function::CallResults, Error>
    {
        let params = pry!(params.get().get_params());
        if params.len() != self.param_count {
            Promise::err(Error::failed(
                format!("Expect {} parameters but got {}.", self.param_count, params.len())))
        } else {
            evaluate_impl(
                pry!(self.body.get_root::<calculator::expression::Builder>()).as_reader(),
                Some(params)).map(move |v| {
                    results.get().set_value(v);
                    Ok(results)
                })
        }
    }
}

#[derive(Clone, Copy)]
pub struct OperatorImpl {
    op: calculator::Operator,
}

impl calculator::function::Server for OperatorImpl {
    fn call(&mut self,
            params: calculator::function::CallParams,
            mut results: calculator::function::CallResults)
            -> Promise<calculator::function::CallResults, Error>
    {
        let params = pry!(params.get().get_params());
        if params.len() != 2 {
            Promise::err(Error::failed("Wrong number of paramters.".to_string()))
        } else {
            let v = match self.op {
                calculator::Operator::Add =>       params.get(0) + params.get(1),
                calculator::Operator::Subtract =>  params.get(0) - params.get(1),
                calculator::Operator::Multiply =>  params.get(0) * params.get(1),
                calculator::Operator::Divide =>    params.get(0) / params.get(1),
            };
            results.get().set_value(v);
            Promise::ok(results)
        }
    }
}

struct CalculatorImpl;

impl calculator::Server for CalculatorImpl {
    fn evaluate(&mut self,
                params: calculator::EvaluateParams,
                mut results: calculator::EvaluateResults)
                -> Promise<calculator::EvaluateResults, Error>
    {
        evaluate_impl(pry!(params.get().get_expression()), None).map(move |v| {
            results.get().set_value(
                calculator::value::ToClient::new(ValueImpl::new(v)).from_server::<LocalClient>());
            Ok(results)
        })
    }
    fn def_function(&mut self,
                    params: calculator::DefFunctionParams,
                    mut results: calculator::DefFunctionResults)
                    -> Promise<calculator::DefFunctionResults, Error>
    {
        results.get().set_func(
            calculator::function::ToClient::new(
                FunctionImpl::new(params.get().get_param_count() as u32, pry!(params.get().get_body())))
                .from_server::<LocalClient>());
        Promise::ok(results)
    }
    fn get_operator(&mut self,
                    params: calculator::GetOperatorParams,
                    mut results: calculator::GetOperatorResults)
                    -> Promise<calculator::GetOperatorResults, Error>
    {
        let op = pry!(params.get().get_op());
        results.get().set_func(
            calculator::function::ToClient::new(OperatorImpl {op : op}).from_server::<LocalClient>());
        Promise::ok(results)
    }
}

pub fn main() {
    let args : Vec<String> = ::std::env::args().collect();
    if args.len() != 3 {
        println!("usage: {} server ADDRESS[:PORT]", args[0]);
        return;
    }

    //let rpc_server = EzRpcServer::new(&*args[2]).unwrap();

    // There's got to be a better way to do this.
    //let calculator = Box::new(calculator::ServerDispatch { server : Box::new(CalculatorImpl)});

    //let _ = rpc_server.serve(calculator);
}
<commit_msg>progress on calculator-server<commit_after>// Copyright (c) 2013-2015 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

use capnp::capability::{Server};
use capnp::Error;
use capnp::primitive_list;

use capnp_rpc::rpc::{LocalClient};

use calculator_capnp::calculator;
use gj::Promise;

struct ValueImpl {
    value: f64
}

impl ValueImpl {
    fn new(value: f64) -> ValueImpl {
        ValueImpl { value: value }
    }
}

impl calculator::value::Server for ValueImpl {
    fn read(&mut self,
            _params: calculator::value::ReadParams,
            mut results: calculator::value::ReadResults)
            -> Promise<calculator::value::ReadResults, Error>
    {
        results.get().set_value(self.value);
        Promise::ok(results)
    }
}

fn evaluate_impl(
    expression: calculator::expression::Reader,
    params: Option<primitive_list::Reader<f64>>) -> Promise<f64, Error> {

    match pry!(expression.which()) {
        calculator::expression::Literal(v) => {
            Promise::ok(v)
        },
        calculator::expression::PreviousResult(p) => {
            pry!(p).read_request().send().promise.map(|v| {
                Ok(try!(v.get()).get_value())
            })
        }
        calculator::expression::Parameter(p) => {
            match params {
                Some(params) if p < params.len() => {
                    Promise::ok(params.get(p))
                }
                _ => {
                    Promise::err(Error::failed(format!("bad parameter: {}", p)))
                }
            }
        }
        calculator::expression::Call(call) => {
            let func = call.get_function();
            let params = call.get_params();
            unimplemented!()
        }
    }
}

struct FunctionImpl {
    param_count: u32,
    body: ::capnp::message::Builder<::capnp::message::HeapAllocator>,
}

impl FunctionImpl {
    fn new(param_count: u32, body: calculator::expression::Reader) -> FunctionImpl {
        let mut result = FunctionImpl {
            param_count: param_count,
            body: ::capnp::message::Builder::new_default()
        };
        result.body.set_root(body).unwrap();
        result
    }
}

impl calculator::function::Server for FunctionImpl {
    fn call(&mut self,
            params: calculator::function::CallParams,
            mut results: calculator::function::CallResults)
        -> Promise<calculator::function::CallResults, Error>
    {
        let params = pry!(params.get().get_params());
        if params.len() != self.param_count {
            Promise::err(Error::failed(
                format!("Expect {} parameters but got {}.", self.param_count, params.len())))
        } else {
            evaluate_impl(
                pry!(self.body.get_root::<calculator::expression::Builder>()).as_reader(),
                Some(params)).map(move |v| {
                    results.get().set_value(v);
                    Ok(results)
                })
        }
    }
}

#[derive(Clone, Copy)]
pub struct OperatorImpl {
    op: calculator::Operator,
}

impl calculator::function::Server for OperatorImpl {
    fn call(&mut self,
            params: calculator::function::CallParams,
            mut results: calculator::function::CallResults)
            -> Promise<calculator::function::CallResults, Error>
    {
        let params = pry!(params.get().get_params());
        if params.len() != 2 {
            Promise::err(Error::failed("Wrong number of paramters.".to_string()))
        } else {
            let v = match self.op {
                calculator::Operator::Add =>       params.get(0) + params.get(1),
                calculator::Operator::Subtract =>  params.get(0) - params.get(1),
                calculator::Operator::Multiply =>  params.get(0) * params.get(1),
                calculator::Operator::Divide =>    params.get(0) / params.get(1),
            };
            results.get().set_value(v);
            Promise::ok(results)
        }
    }
}

struct CalculatorImpl;

impl calculator::Server for CalculatorImpl {
    fn evaluate(&mut self,
                params: calculator::EvaluateParams,
                mut results: calculator::EvaluateResults)
                -> Promise<calculator::EvaluateResults, Error>
    {
        evaluate_impl(pry!(params.get().get_expression()), None).map(move |v| {
            results.get().set_value(
                calculator::value::ToClient::new(ValueImpl::new(v)).from_server::<LocalClient>());
            Ok(results)
        })
    }
    fn def_function(&mut self,
                    params: calculator::DefFunctionParams,
                    mut results: calculator::DefFunctionResults)
                    -> Promise<calculator::DefFunctionResults, Error>
    {
        results.get().set_func(
            calculator::function::ToClient::new(
                FunctionImpl::new(params.get().get_param_count() as u32, pry!(params.get().get_body())))
                .from_server::<LocalClient>());
        Promise::ok(results)
    }
    fn get_operator(&mut self,
                    params: calculator::GetOperatorParams,
                    mut results: calculator::GetOperatorResults)
                    -> Promise<calculator::GetOperatorResults, Error>
    {
        let op = pry!(params.get().get_op());
        results.get().set_func(
            calculator::function::ToClient::new(OperatorImpl {op : op}).from_server::<LocalClient>());
        Promise::ok(results)
    }
}

pub fn main() {
    let args : Vec<String> = ::std::env::args().collect();
    if args.len() != 3 {
        println!("usage: {} server ADDRESS[:PORT]", args[0]);
        return;
    }

    //let rpc_server = EzRpcServer::new(&*args[2]).unwrap();

    // There's got to be a better way to do this.
    //let calculator = Box::new(calculator::ServerDispatch { server : Box::new(CalculatorImpl)});

    //let _ = rpc_server.serve(calculator);
}
<|endoftext|>


<commit_before>use std::cmp::{max, min};

use cssparser::Parser;
use markup5ever::{expanded_name, local_name, namespace_url, ns};

use crate::drawing_ctx::DrawingCtx;
use crate::error::*;
use crate::node::{NodeResult, NodeTrait, RsvgNode};
use crate::parsers::{NumberOptionalNumber, Parse, ParseValue};
use crate::property_bag::PropertyBag;
use crate::rect::IRect;
use crate::surface_utils::{
    iterators::{PixelRectangle, Pixels},
    shared_surface::SharedImageSurface,
    EdgeMode, ImageSurfaceDataExt, Pixel,
};

use super::context::{FilterContext, FilterOutput, FilterResult};
use super::{FilterEffect, FilterError, PrimitiveWithInput};

/// Enumeration of the possible morphology operations.
enum Operator {
    Erode,
    Dilate,
}

/// The `feMorphology` filter primitive.
pub struct FeMorphology {
    base: PrimitiveWithInput,
    operator: Operator,
    radius: (f64, f64),
}

impl Default for FeMorphology {
    /// Constructs a new `Morphology` with empty properties.
    #[inline]
    fn default() -> FeMorphology {
        FeMorphology {
            base: PrimitiveWithInput::new::<Self>(),
            operator: Operator::Erode,
            radius: (0.0, 0.0),
        }
    }
}

impl NodeTrait for FeMorphology {
    impl_node_as_filter_effect!();

    fn set_atts(&mut self, parent: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> NodeResult {
        self.base.set_atts(parent, pbag)?;

        for (attr, value) in pbag.iter() {
            match attr.expanded() {
                expanded_name!("", "operator") => self.operator = attr.parse(value)?,
                expanded_name!("", "radius") => {
                    let NumberOptionalNumber(x, y) =
                        attr.parse_and_validate(value, |v: NumberOptionalNumber<f64>| {
                            if v.0 >= 0.0 && v.1 >= 0.0 {
                                Ok(v)
                            } else {
                                Err(ValueErrorKind::value_error("radius cannot be negative"))
                            }
                        })?;

                    self.radius = (x, y);
                }
                _ => (),
            }
        }

        Ok(())
    }
}

impl FilterEffect for FeMorphology {
    fn render(
        &self,
        _node: &RsvgNode,
        ctx: &FilterContext,
        draw_ctx: &mut DrawingCtx,
    ) -> Result<FilterResult, FilterError> {
        let input = self.base.get_input(ctx, draw_ctx)?;
        let bounds = self
            .base
            .get_bounds(ctx)
            .add_input(&input)
            .into_irect(draw_ctx);

        let (rx, ry) = self.radius;
        let (rx, ry) = ctx.paffine().transform_distance(rx, ry);

        // The radii can become negative here due to the transform.
        let (rx, ry) = (rx.abs(), ry.abs());

        let mut output_surface = cairo::ImageSurface::create(
            cairo::Format::ARgb32,
            ctx.source_graphic().width(),
            ctx.source_graphic().height(),
        )?;

        let output_stride = output_surface.get_stride() as usize;
        {
            let mut output_data = output_surface.get_data().unwrap();

            for (x, y, _pixel) in Pixels::within(input.surface(), bounds) {
                // Compute the kernel rectangle bounds.
                let kernel_bounds = IRect::new(
                    (f64::from(x) - rx).floor() as i32,
                    (f64::from(y) - ry).floor() as i32,
                    (f64::from(x) + rx).ceil() as i32 + 1,
                    (f64::from(y) + ry).ceil() as i32 + 1,
                );

                // Compute the new pixel values.
                let initial = match self.operator {
                    Operator::Erode => u8::max_value(),
                    Operator::Dilate => u8::min_value(),
                };

                let mut output_pixel = Pixel {
                    r: initial,
                    g: initial,
                    b: initial,
                    a: initial,
                };

                for (_x, _y, pixel) in
                    PixelRectangle::within(&input.surface(), bounds, kernel_bounds, EdgeMode::None)
                {
                    let op = match self.operator {
                        Operator::Erode => min,
                        Operator::Dilate => max,
                    };

                    output_pixel.r = op(output_pixel.r, pixel.r);
                    output_pixel.g = op(output_pixel.g, pixel.g);
                    output_pixel.b = op(output_pixel.b, pixel.b);
                    output_pixel.a = op(output_pixel.a, pixel.a);
                }

                output_data.set_pixel(output_stride, output_pixel, x, y);
            }
        }

        Ok(FilterResult {
            name: self.base.result.clone(),
            output: FilterOutput {
                surface: SharedImageSurface::wrap(output_surface, input.surface().surface_type())?,
                bounds,
            },
        })
    }

    #[inline]
    fn is_affected_by_color_interpolation_filters(&self) -> bool {
        false
    }
}

impl Parse for Operator {
    fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, ParseError<'i>> {
        Ok(parse_identifiers!(
            parser,
            "erode" => Operator::Erode,
            "dilate" => Operator::Dilate,
        )?)
    }
}
<commit_msg>filters: port morphology to ExclusiveImageSurface<commit_after>use std::cmp::{max, min};

use cssparser::Parser;
use markup5ever::{expanded_name, local_name, namespace_url, ns};

use crate::drawing_ctx::DrawingCtx;
use crate::error::*;
use crate::node::{NodeResult, NodeTrait, RsvgNode};
use crate::parsers::{NumberOptionalNumber, Parse, ParseValue};
use crate::property_bag::PropertyBag;
use crate::rect::IRect;
use crate::surface_utils::{
    iterators::{PixelRectangle, Pixels},
    shared_surface::ExclusiveImageSurface,
    EdgeMode, Pixel,
};

use super::context::{FilterContext, FilterOutput, FilterResult};
use super::{FilterEffect, FilterError, PrimitiveWithInput};

/// Enumeration of the possible morphology operations.
enum Operator {
    Erode,
    Dilate,
}

/// The `feMorphology` filter primitive.
pub struct FeMorphology {
    base: PrimitiveWithInput,
    operator: Operator,
    radius: (f64, f64),
}

impl Default for FeMorphology {
    /// Constructs a new `Morphology` with empty properties.
    #[inline]
    fn default() -> FeMorphology {
        FeMorphology {
            base: PrimitiveWithInput::new::<Self>(),
            operator: Operator::Erode,
            radius: (0.0, 0.0),
        }
    }
}

impl NodeTrait for FeMorphology {
    impl_node_as_filter_effect!();

    fn set_atts(&mut self, parent: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> NodeResult {
        self.base.set_atts(parent, pbag)?;

        for (attr, value) in pbag.iter() {
            match attr.expanded() {
                expanded_name!("", "operator") => self.operator = attr.parse(value)?,
                expanded_name!("", "radius") => {
                    let NumberOptionalNumber(x, y) =
                        attr.parse_and_validate(value, |v: NumberOptionalNumber<f64>| {
                            if v.0 >= 0.0 && v.1 >= 0.0 {
                                Ok(v)
                            } else {
                                Err(ValueErrorKind::value_error("radius cannot be negative"))
                            }
                        })?;

                    self.radius = (x, y);
                }
                _ => (),
            }
        }

        Ok(())
    }
}

impl FilterEffect for FeMorphology {
    fn render(
        &self,
        _node: &RsvgNode,
        ctx: &FilterContext,
        draw_ctx: &mut DrawingCtx,
    ) -> Result<FilterResult, FilterError> {
        let input = self.base.get_input(ctx, draw_ctx)?;
        let bounds = self
            .base
            .get_bounds(ctx)
            .add_input(&input)
            .into_irect(draw_ctx);

        let (rx, ry) = self.radius;
        let (rx, ry) = ctx.paffine().transform_distance(rx, ry);

        // The radii can become negative here due to the transform.
        let (rx, ry) = (rx.abs(), ry.abs());

        let mut surface = ExclusiveImageSurface::new(
            ctx.source_graphic().width(),
            ctx.source_graphic().height(),
            input.surface().surface_type(),
        )?;

        for (x, y, _pixel) in Pixels::within(input.surface(), bounds) {
            // Compute the kernel rectangle bounds.
            let kernel_bounds = IRect::new(
                (f64::from(x) - rx).floor() as i32,
                (f64::from(y) - ry).floor() as i32,
                (f64::from(x) + rx).ceil() as i32 + 1,
                (f64::from(y) + ry).ceil() as i32 + 1,
            );

            // Compute the new pixel values.
            let initial = match self.operator {
                Operator::Erode => u8::max_value(),
                Operator::Dilate => u8::min_value(),
            };

            let mut output_pixel = Pixel {
                r: initial,
                g: initial,
                b: initial,
                a: initial,
            };

            for (_x, _y, pixel) in
                PixelRectangle::within(&input.surface(), bounds, kernel_bounds, EdgeMode::None)
            {
                let op = match self.operator {
                    Operator::Erode => min,
                    Operator::Dilate => max,
                };

                output_pixel.r = op(output_pixel.r, pixel.r);
                output_pixel.g = op(output_pixel.g, pixel.g);
                output_pixel.b = op(output_pixel.b, pixel.b);
                output_pixel.a = op(output_pixel.a, pixel.a);
            }

            surface.set_pixel(output_pixel, x, y);
        }

        Ok(FilterResult {
            name: self.base.result.clone(),
            output: FilterOutput {
                surface: surface.share()?,
                bounds,
            },
        })
    }

    #[inline]
    fn is_affected_by_color_interpolation_filters(&self) -> bool {
        false
    }
}

impl Parse for Operator {
    fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, ParseError<'i>> {
        Ok(parse_identifiers!(
            parser,
            "erode" => Operator::Erode,
            "dilate" => Operator::Dilate,
        )?)
    }
}
<|endoftext|>


<commit_before>//! Checks the licenses of third-party dependencies by inspecting vendors.

use std::collections::{BTreeSet, HashSet, HashMap};
use std::fs;
use std::path::Path;
use std::process::Command;

use serde::Deserialize;
use serde_json;

const LICENSES: &[&str] = &[
    "MIT/Apache-2.0",
    "MIT / Apache-2.0",
    "Apache-2.0/MIT",
    "Apache-2.0 / MIT",
    "MIT OR Apache-2.0",
    "Apache-2.0 OR MIT",
    "Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT", // wasi license
    "MIT",
    "Unlicense/MIT",
    "Unlicense OR MIT",
];

/// These are exceptions to Rust's permissive licensing policy, and
/// should be considered bugs. Exceptions are only allowed in Rust
/// tooling. It is _crucial_ that no exception crates be dependencies
/// of the Rust runtime (std/test).
const EXCEPTIONS: &[&str] = &[
    "mdbook",             // MPL2, mdbook
    "openssl",            // BSD+advertising clause, cargo, mdbook
    "pest",               // MPL2, mdbook via handlebars
    "arrayref",           // BSD-2-Clause, mdbook via handlebars via pest
    "thread-id",          // Apache-2.0, mdbook
    "toml-query",         // MPL-2.0, mdbook
    "is-match",           // MPL-2.0, mdbook
    "cssparser",          // MPL-2.0, rustdoc
    "smallvec",           // MPL-2.0, rustdoc
    "rdrand",             // ISC, mdbook, rustfmt
    "fuchsia-cprng",      // BSD-3-Clause, mdbook, rustfmt
    "fuchsia-zircon-sys", // BSD-3-Clause, rustdoc, rustc, cargo
    "fuchsia-zircon",     // BSD-3-Clause, rustdoc, rustc, cargo (jobserver & tempdir)
    "cssparser-macros",   // MPL-2.0, rustdoc
    "selectors",          // MPL-2.0, rustdoc
    "clippy_lints",       // MPL-2.0, rls
    "colored",            // MPL-2.0, rustfmt
    "ordslice",           // Apache-2.0, rls
    "cloudabi",           // BSD-2-Clause, (rls -> crossbeam-channel 0.2 -> rand 0.5)
    "ryu",                // Apache-2.0, rls/cargo/... (because of serde)
    "bytesize",           // Apache-2.0, cargo
    "im-rc",              // MPL-2.0+, cargo
    "adler32",            // BSD-3-Clause AND Zlib, cargo dep that isn't used
    "constant_time_eq",   // CC0-1.0, rustfmt
    "utf8parse",          // Apache-2.0 OR MIT, cargo via strip-ansi-escapes
    "vte",                // Apache-2.0 OR MIT, cargo via strip-ansi-escapes
    "sized-chunks",       // MPL-2.0+, cargo via im-rc
    // FIXME: this dependency violates the documentation comment above:
    "fortanix-sgx-abi",   // MPL-2.0+, libstd but only for `sgx` target
];

/// Which crates to check against the whitelist?
const WHITELIST_CRATES: &[CrateVersion<'_>] = &[
    CrateVersion("rustc", "0.0.0"),
    CrateVersion("rustc_codegen_llvm", "0.0.0"),
];

/// Whitelist of crates rustc is allowed to depend on. Avoid adding to the list if possible.
const WHITELIST: &[Crate<'_>] = &[
    Crate("adler32"),
    Crate("aho-corasick"),
    Crate("annotate-snippets"),
    Crate("ansi_term"),
    Crate("arrayvec"),
    Crate("atty"),
    Crate("autocfg"),
    Crate("backtrace"),
    Crate("backtrace-sys"),
    Crate("bitflags"),
    Crate("build_const"),
    Crate("byteorder"),
    Crate("c2-chacha"),
    Crate("cc"),
    Crate("cfg-if"),
    Crate("chalk-engine"),
    Crate("chalk-macros"),
    Crate("cloudabi"),
    Crate("cmake"),
    Crate("compiler_builtins"),
    Crate("crc"),
    Crate("crc32fast"),
    Crate("crossbeam-deque"),
    Crate("crossbeam-epoch"),
    Crate("crossbeam-utils"),
    Crate("datafrog"),
    Crate("dlmalloc"),
    Crate("either"),
    Crate("ena"),
    Crate("env_logger"),
    Crate("filetime"),
    Crate("flate2"),
    Crate("fortanix-sgx-abi"),
    Crate("fuchsia-zircon"),
    Crate("fuchsia-zircon-sys"),
    Crate("getopts"),
    Crate("getrandom"),
    Crate("hashbrown"),
    Crate("humantime"),
    Crate("indexmap"),
    Crate("itertools"),
    Crate("jobserver"),
    Crate("kernel32-sys"),
    Crate("lazy_static"),
    Crate("libc"),
    Crate("libz-sys"),
    Crate("lock_api"),
    Crate("log"),
    Crate("log_settings"),
    Crate("measureme"),
    Crate("memchr"),
    Crate("memmap"),
    Crate("memoffset"),
    Crate("miniz-sys"),
    Crate("miniz_oxide"),
    Crate("miniz_oxide_c_api"),
    Crate("nodrop"),
    Crate("num_cpus"),
    Crate("owning_ref"),
    Crate("parking_lot"),
    Crate("parking_lot_core"),
    Crate("pkg-config"),
    Crate("polonius-engine"),
    Crate("ppv-lite86"),
    Crate("proc-macro2"),
    Crate("quick-error"),
    Crate("quote"),
    Crate("rand"),
    Crate("rand_chacha"),
    Crate("rand_core"),
    Crate("rand_hc"),
    Crate("rand_isaac"),
    Crate("rand_pcg"),
    Crate("rand_xorshift"),
    Crate("redox_syscall"),
    Crate("redox_termios"),
    Crate("regex"),
    Crate("regex-syntax"),
    Crate("remove_dir_all"),
    Crate("rustc-demangle"),
    Crate("rustc-hash"),
    Crate("rustc-rayon"),
    Crate("rustc-rayon-core"),
    Crate("rustc_version"),
    Crate("scoped-tls"),
    Crate("scopeguard"),
    Crate("semver"),
    Crate("semver-parser"),
    Crate("serde"),
    Crate("serde_derive"),
    Crate("smallvec"),
    Crate("stable_deref_trait"),
    Crate("syn"),
    Crate("synstructure"),
    Crate("tempfile"),
    Crate("termcolor"),
    Crate("terminon"),
    Crate("termion"),
    Crate("term_size"),
    Crate("thread_local"),
    Crate("ucd-util"),
    Crate("unicode-width"),
    Crate("unicode-xid"),
    Crate("unreachable"),
    Crate("utf8-ranges"),
    Crate("vcpkg"),
    Crate("version_check"),
    Crate("void"),
    Crate("wasi"),
    Crate("winapi"),
    Crate("winapi-build"),
    Crate("winapi-i686-pc-windows-gnu"),
    Crate("winapi-util"),
    Crate("winapi-x86_64-pc-windows-gnu"),
    Crate("wincolor"),
];

// Some types for Serde to deserialize the output of `cargo metadata` to.

#[derive(Deserialize)]
struct Output {
    resolve: Resolve,
}

#[derive(Deserialize)]
struct Resolve {
    nodes: Vec<ResolveNode>,
}

#[derive(Deserialize)]
struct ResolveNode {
    id: String,
    dependencies: Vec<String>,
}

/// A unique identifier for a crate.
#[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Debug, Hash)]
struct Crate<'a>(&'a str); // (name)

#[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Debug, Hash)]
struct CrateVersion<'a>(&'a str, &'a str); // (name, version)

impl Crate<'_> {
    pub fn id_str(&self) -> String {
        format!("{} ", self.0)
    }
}

impl<'a> CrateVersion<'a> {
    /// Returns the struct and whether or not the dependency is in-tree.
    pub fn from_str(s: &'a str) -> (Self, bool) {
        let mut parts = s.split(' ');
        let name = parts.next().unwrap();
        let version = parts.next().unwrap();
        let path = parts.next().unwrap();

        let is_path_dep = path.starts_with("(path+");

        (CrateVersion(name, version), is_path_dep)
    }

    pub fn id_str(&self) -> String {
        format!("{} {}", self.0, self.1)
    }
}

impl<'a> From<CrateVersion<'a>> for Crate<'a> {
    fn from(cv: CrateVersion<'a>) -> Crate<'a> {
        Crate(cv.0)
    }
}

/// Checks the dependency at the given path. Changes `bad` to `true` if a check failed.
///
/// Specifically, this checks that the license is correct.
pub fn check(path: &Path, bad: &mut bool) {
    // Check licences.
    let path = path.join("../vendor");
    assert!(path.exists(), "vendor directory missing");
    let mut saw_dir = false;
    for dir in t!(path.read_dir()) {
        saw_dir = true;
        let dir = t!(dir);

        // Skip our exceptions.
        let is_exception = EXCEPTIONS.iter().any(|exception| {
            dir.path()
                .to_str()
                .unwrap()
                .contains(&format!("vendor/{}", exception))
        });
        if is_exception {
            continue;
        }

        let toml = dir.path().join("Cargo.toml");
        *bad = !check_license(&toml) || *bad;
    }
    assert!(saw_dir, "no vendored source");
}

/// Checks the dependency of `WHITELIST_CRATES` at the given path. Changes `bad` to `true` if a
/// check failed.
///
/// Specifically, this checks that the dependencies are on the `WHITELIST`.
pub fn check_whitelist(path: &Path, cargo: &Path, bad: &mut bool) {
    // Get dependencies from Cargo metadata.
    let resolve = get_deps(path, cargo);

    // Get the whitelist in a convenient form.
    let whitelist: HashSet<_> = WHITELIST.iter().cloned().collect();

    // Check dependencies.
    let mut visited = BTreeSet::new();
    let mut unapproved = BTreeSet::new();
    for &krate in WHITELIST_CRATES.iter() {
        let mut bad = check_crate_whitelist(&whitelist, &resolve, &mut visited, krate, false);
        unapproved.append(&mut bad);
    }

    if !unapproved.is_empty() {
        println!("Dependencies not on the whitelist:");
        for dep in unapproved {
            println!("* {}", dep.id_str());
        }
        *bad = true;
    }

    check_crate_duplicate(&resolve, bad);
}

fn check_license(path: &Path) -> bool {
    if !path.exists() {
        panic!("{} does not exist", path.display());
    }
    let contents = t!(fs::read_to_string(&path));

    let mut found_license = false;
    for line in contents.lines() {
        if !line.starts_with("license") {
            continue;
        }
        let license = extract_license(line);
        if !LICENSES.contains(&&*license) {
            println!("invalid license {} in {}", license, path.display());
            return false;
        }
        found_license = true;
        break;
    }
    if !found_license {
        println!("no license in {}", path.display());
        return false;
    }

    true
}

fn extract_license(line: &str) -> String {
    let first_quote = line.find('"');
    let last_quote = line.rfind('"');
    if let (Some(f), Some(l)) = (first_quote, last_quote) {
        let license = &line[f + 1..l];
        license.into()
    } else {
        "bad-license-parse".into()
    }
}

/// Gets the dependencies of the crate at the given path using `cargo metadata`.
fn get_deps(path: &Path, cargo: &Path) -> Resolve {
    // Run `cargo metadata` to get the set of dependencies.
    let output = Command::new(cargo)
        .arg("metadata")
        .arg("--format-version")
        .arg("1")
        .arg("--manifest-path")
        .arg(path.join("../Cargo.toml"))
        .output()
        .expect("Unable to run `cargo metadata`")
        .stdout;
    let output = String::from_utf8_lossy(&output);
    let output: Output = serde_json::from_str(&output).unwrap();

    output.resolve
}

/// Checks the dependencies of the given crate from the given cargo metadata to see if they are on
/// the whitelist. Returns a list of illegal dependencies.
fn check_crate_whitelist<'a>(
    whitelist: &'a HashSet<Crate<'_>>,
    resolve: &'a Resolve,
    visited: &mut BTreeSet<CrateVersion<'a>>,
    krate: CrateVersion<'a>,
    must_be_on_whitelist: bool,
) -> BTreeSet<Crate<'a>> {
    // This will contain bad deps.
    let mut unapproved = BTreeSet::new();

    // Check if we have already visited this crate.
    if visited.contains(&krate) {
        return unapproved;
    }

    visited.insert(krate);

    // If this path is in-tree, we don't require it to be on the whitelist.
    if must_be_on_whitelist {
        // If this dependency is not on `WHITELIST`, add to bad set.
        if !whitelist.contains(&krate.into()) {
            unapproved.insert(krate.into());
        }
    }

    // Do a DFS in the crate graph (it's a DAG, so we know we have no cycles!).
    let to_check = resolve
        .nodes
        .iter()
        .find(|n| n.id.starts_with(&krate.id_str()))
        .expect("crate does not exist");

    for dep in to_check.dependencies.iter() {
        let (krate, is_path_dep) = CrateVersion::from_str(dep);

        let mut bad = check_crate_whitelist(whitelist, resolve, visited, krate, !is_path_dep);
        unapproved.append(&mut bad);
    }

    unapproved
}

fn check_crate_duplicate(resolve: &Resolve, bad: &mut bool) {
    const FORBIDDEN_TO_HAVE_DUPLICATES: &[&str] = &[
        // These two crates take quite a long time to build, so don't allow two versions of them
        // to accidentally sneak into our dependency graph, in order to ensure we keep our CI times
        // under control.

        "cargo",
        "rustc-ap-syntax",
    ];
    let mut name_to_id: HashMap<_, Vec<_>> = HashMap::new();
    for node in resolve.nodes.iter() {
        name_to_id.entry(node.id.split_whitespace().next().unwrap())
            .or_default()
            .push(&node.id);
    }

    for name in FORBIDDEN_TO_HAVE_DUPLICATES {
        if name_to_id[name].len() <= 1 {
            continue
        }
        println!("crate `{}` is duplicated in `Cargo.lock`", name);
        for id in name_to_id[name].iter() {
            println!("  * {}", id);
        }
        *bad = true;
    }
}
<commit_msg>add crossbeam-queue to the whitelist<commit_after>//! Checks the licenses of third-party dependencies by inspecting vendors.

use std::collections::{BTreeSet, HashSet, HashMap};
use std::fs;
use std::path::Path;
use std::process::Command;

use serde::Deserialize;
use serde_json;

const LICENSES: &[&str] = &[
    "MIT/Apache-2.0",
    "MIT / Apache-2.0",
    "Apache-2.0/MIT",
    "Apache-2.0 / MIT",
    "MIT OR Apache-2.0",
    "Apache-2.0 OR MIT",
    "Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT", // wasi license
    "MIT",
    "Unlicense/MIT",
    "Unlicense OR MIT",
];

/// These are exceptions to Rust's permissive licensing policy, and
/// should be considered bugs. Exceptions are only allowed in Rust
/// tooling. It is _crucial_ that no exception crates be dependencies
/// of the Rust runtime (std/test).
const EXCEPTIONS: &[&str] = &[
    "mdbook",             // MPL2, mdbook
    "openssl",            // BSD+advertising clause, cargo, mdbook
    "pest",               // MPL2, mdbook via handlebars
    "arrayref",           // BSD-2-Clause, mdbook via handlebars via pest
    "thread-id",          // Apache-2.0, mdbook
    "toml-query",         // MPL-2.0, mdbook
    "is-match",           // MPL-2.0, mdbook
    "cssparser",          // MPL-2.0, rustdoc
    "smallvec",           // MPL-2.0, rustdoc
    "rdrand",             // ISC, mdbook, rustfmt
    "fuchsia-cprng",      // BSD-3-Clause, mdbook, rustfmt
    "fuchsia-zircon-sys", // BSD-3-Clause, rustdoc, rustc, cargo
    "fuchsia-zircon",     // BSD-3-Clause, rustdoc, rustc, cargo (jobserver & tempdir)
    "cssparser-macros",   // MPL-2.0, rustdoc
    "selectors",          // MPL-2.0, rustdoc
    "clippy_lints",       // MPL-2.0, rls
    "colored",            // MPL-2.0, rustfmt
    "ordslice",           // Apache-2.0, rls
    "cloudabi",           // BSD-2-Clause, (rls -> crossbeam-channel 0.2 -> rand 0.5)
    "ryu",                // Apache-2.0, rls/cargo/... (because of serde)
    "bytesize",           // Apache-2.0, cargo
    "im-rc",              // MPL-2.0+, cargo
    "adler32",            // BSD-3-Clause AND Zlib, cargo dep that isn't used
    "constant_time_eq",   // CC0-1.0, rustfmt
    "utf8parse",          // Apache-2.0 OR MIT, cargo via strip-ansi-escapes
    "vte",                // Apache-2.0 OR MIT, cargo via strip-ansi-escapes
    "sized-chunks",       // MPL-2.0+, cargo via im-rc
    // FIXME: this dependency violates the documentation comment above:
    "fortanix-sgx-abi",   // MPL-2.0+, libstd but only for `sgx` target
];

/// Which crates to check against the whitelist?
const WHITELIST_CRATES: &[CrateVersion<'_>] = &[
    CrateVersion("rustc", "0.0.0"),
    CrateVersion("rustc_codegen_llvm", "0.0.0"),
];

/// Whitelist of crates rustc is allowed to depend on. Avoid adding to the list if possible.
const WHITELIST: &[Crate<'_>] = &[
    Crate("adler32"),
    Crate("aho-corasick"),
    Crate("annotate-snippets"),
    Crate("ansi_term"),
    Crate("arrayvec"),
    Crate("atty"),
    Crate("autocfg"),
    Crate("backtrace"),
    Crate("backtrace-sys"),
    Crate("bitflags"),
    Crate("build_const"),
    Crate("byteorder"),
    Crate("c2-chacha"),
    Crate("cc"),
    Crate("cfg-if"),
    Crate("chalk-engine"),
    Crate("chalk-macros"),
    Crate("cloudabi"),
    Crate("cmake"),
    Crate("compiler_builtins"),
    Crate("crc"),
    Crate("crc32fast"),
    Crate("crossbeam-deque"),
    Crate("crossbeam-epoch"),
    Crate("crossbeam-queue"),
    Crate("crossbeam-utils"),
    Crate("datafrog"),
    Crate("dlmalloc"),
    Crate("either"),
    Crate("ena"),
    Crate("env_logger"),
    Crate("filetime"),
    Crate("flate2"),
    Crate("fortanix-sgx-abi"),
    Crate("fuchsia-zircon"),
    Crate("fuchsia-zircon-sys"),
    Crate("getopts"),
    Crate("getrandom"),
    Crate("hashbrown"),
    Crate("humantime"),
    Crate("indexmap"),
    Crate("itertools"),
    Crate("jobserver"),
    Crate("kernel32-sys"),
    Crate("lazy_static"),
    Crate("libc"),
    Crate("libz-sys"),
    Crate("lock_api"),
    Crate("log"),
    Crate("log_settings"),
    Crate("measureme"),
    Crate("memchr"),
    Crate("memmap"),
    Crate("memoffset"),
    Crate("miniz-sys"),
    Crate("miniz_oxide"),
    Crate("miniz_oxide_c_api"),
    Crate("nodrop"),
    Crate("num_cpus"),
    Crate("owning_ref"),
    Crate("parking_lot"),
    Crate("parking_lot_core"),
    Crate("pkg-config"),
    Crate("polonius-engine"),
    Crate("ppv-lite86"),
    Crate("proc-macro2"),
    Crate("quick-error"),
    Crate("quote"),
    Crate("rand"),
    Crate("rand_chacha"),
    Crate("rand_core"),
    Crate("rand_hc"),
    Crate("rand_isaac"),
    Crate("rand_pcg"),
    Crate("rand_xorshift"),
    Crate("redox_syscall"),
    Crate("redox_termios"),
    Crate("regex"),
    Crate("regex-syntax"),
    Crate("remove_dir_all"),
    Crate("rustc-demangle"),
    Crate("rustc-hash"),
    Crate("rustc-rayon"),
    Crate("rustc-rayon-core"),
    Crate("rustc_version"),
    Crate("scoped-tls"),
    Crate("scopeguard"),
    Crate("semver"),
    Crate("semver-parser"),
    Crate("serde"),
    Crate("serde_derive"),
    Crate("smallvec"),
    Crate("stable_deref_trait"),
    Crate("syn"),
    Crate("synstructure"),
    Crate("tempfile"),
    Crate("termcolor"),
    Crate("terminon"),
    Crate("termion"),
    Crate("term_size"),
    Crate("thread_local"),
    Crate("ucd-util"),
    Crate("unicode-width"),
    Crate("unicode-xid"),
    Crate("unreachable"),
    Crate("utf8-ranges"),
    Crate("vcpkg"),
    Crate("version_check"),
    Crate("void"),
    Crate("wasi"),
    Crate("winapi"),
    Crate("winapi-build"),
    Crate("winapi-i686-pc-windows-gnu"),
    Crate("winapi-util"),
    Crate("winapi-x86_64-pc-windows-gnu"),
    Crate("wincolor"),
];

// Some types for Serde to deserialize the output of `cargo metadata` to.

#[derive(Deserialize)]
struct Output {
    resolve: Resolve,
}

#[derive(Deserialize)]
struct Resolve {
    nodes: Vec<ResolveNode>,
}

#[derive(Deserialize)]
struct ResolveNode {
    id: String,
    dependencies: Vec<String>,
}

/// A unique identifier for a crate.
#[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Debug, Hash)]
struct Crate<'a>(&'a str); // (name)

#[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Debug, Hash)]
struct CrateVersion<'a>(&'a str, &'a str); // (name, version)

impl Crate<'_> {
    pub fn id_str(&self) -> String {
        format!("{} ", self.0)
    }
}

impl<'a> CrateVersion<'a> {
    /// Returns the struct and whether or not the dependency is in-tree.
    pub fn from_str(s: &'a str) -> (Self, bool) {
        let mut parts = s.split(' ');
        let name = parts.next().unwrap();
        let version = parts.next().unwrap();
        let path = parts.next().unwrap();

        let is_path_dep = path.starts_with("(path+");

        (CrateVersion(name, version), is_path_dep)
    }

    pub fn id_str(&self) -> String {
        format!("{} {}", self.0, self.1)
    }
}

impl<'a> From<CrateVersion<'a>> for Crate<'a> {
    fn from(cv: CrateVersion<'a>) -> Crate<'a> {
        Crate(cv.0)
    }
}

/// Checks the dependency at the given path. Changes `bad` to `true` if a check failed.
///
/// Specifically, this checks that the license is correct.
pub fn check(path: &Path, bad: &mut bool) {
    // Check licences.
    let path = path.join("../vendor");
    assert!(path.exists(), "vendor directory missing");
    let mut saw_dir = false;
    for dir in t!(path.read_dir()) {
        saw_dir = true;
        let dir = t!(dir);

        // Skip our exceptions.
        let is_exception = EXCEPTIONS.iter().any(|exception| {
            dir.path()
                .to_str()
                .unwrap()
                .contains(&format!("vendor/{}", exception))
        });
        if is_exception {
            continue;
        }

        let toml = dir.path().join("Cargo.toml");
        *bad = !check_license(&toml) || *bad;
    }
    assert!(saw_dir, "no vendored source");
}

/// Checks the dependency of `WHITELIST_CRATES` at the given path. Changes `bad` to `true` if a
/// check failed.
///
/// Specifically, this checks that the dependencies are on the `WHITELIST`.
pub fn check_whitelist(path: &Path, cargo: &Path, bad: &mut bool) {
    // Get dependencies from Cargo metadata.
    let resolve = get_deps(path, cargo);

    // Get the whitelist in a convenient form.
    let whitelist: HashSet<_> = WHITELIST.iter().cloned().collect();

    // Check dependencies.
    let mut visited = BTreeSet::new();
    let mut unapproved = BTreeSet::new();
    for &krate in WHITELIST_CRATES.iter() {
        let mut bad = check_crate_whitelist(&whitelist, &resolve, &mut visited, krate, false);
        unapproved.append(&mut bad);
    }

    if !unapproved.is_empty() {
        println!("Dependencies not on the whitelist:");
        for dep in unapproved {
            println!("* {}", dep.id_str());
        }
        *bad = true;
    }

    check_crate_duplicate(&resolve, bad);
}

fn check_license(path: &Path) -> bool {
    if !path.exists() {
        panic!("{} does not exist", path.display());
    }
    let contents = t!(fs::read_to_string(&path));

    let mut found_license = false;
    for line in contents.lines() {
        if !line.starts_with("license") {
            continue;
        }
        let license = extract_license(line);
        if !LICENSES.contains(&&*license) {
            println!("invalid license {} in {}", license, path.display());
            return false;
        }
        found_license = true;
        break;
    }
    if !found_license {
        println!("no license in {}", path.display());
        return false;
    }

    true
}

fn extract_license(line: &str) -> String {
    let first_quote = line.find('"');
    let last_quote = line.rfind('"');
    if let (Some(f), Some(l)) = (first_quote, last_quote) {
        let license = &line[f + 1..l];
        license.into()
    } else {
        "bad-license-parse".into()
    }
}

/// Gets the dependencies of the crate at the given path using `cargo metadata`.
fn get_deps(path: &Path, cargo: &Path) -> Resolve {
    // Run `cargo metadata` to get the set of dependencies.
    let output = Command::new(cargo)
        .arg("metadata")
        .arg("--format-version")
        .arg("1")
        .arg("--manifest-path")
        .arg(path.join("../Cargo.toml"))
        .output()
        .expect("Unable to run `cargo metadata`")
        .stdout;
    let output = String::from_utf8_lossy(&output);
    let output: Output = serde_json::from_str(&output).unwrap();

    output.resolve
}

/// Checks the dependencies of the given crate from the given cargo metadata to see if they are on
/// the whitelist. Returns a list of illegal dependencies.
fn check_crate_whitelist<'a>(
    whitelist: &'a HashSet<Crate<'_>>,
    resolve: &'a Resolve,
    visited: &mut BTreeSet<CrateVersion<'a>>,
    krate: CrateVersion<'a>,
    must_be_on_whitelist: bool,
) -> BTreeSet<Crate<'a>> {
    // This will contain bad deps.
    let mut unapproved = BTreeSet::new();

    // Check if we have already visited this crate.
    if visited.contains(&krate) {
        return unapproved;
    }

    visited.insert(krate);

    // If this path is in-tree, we don't require it to be on the whitelist.
    if must_be_on_whitelist {
        // If this dependency is not on `WHITELIST`, add to bad set.
        if !whitelist.contains(&krate.into()) {
            unapproved.insert(krate.into());
        }
    }

    // Do a DFS in the crate graph (it's a DAG, so we know we have no cycles!).
    let to_check = resolve
        .nodes
        .iter()
        .find(|n| n.id.starts_with(&krate.id_str()))
        .expect("crate does not exist");

    for dep in to_check.dependencies.iter() {
        let (krate, is_path_dep) = CrateVersion::from_str(dep);

        let mut bad = check_crate_whitelist(whitelist, resolve, visited, krate, !is_path_dep);
        unapproved.append(&mut bad);
    }

    unapproved
}

fn check_crate_duplicate(resolve: &Resolve, bad: &mut bool) {
    const FORBIDDEN_TO_HAVE_DUPLICATES: &[&str] = &[
        // These two crates take quite a long time to build, so don't allow two versions of them
        // to accidentally sneak into our dependency graph, in order to ensure we keep our CI times
        // under control.

        "cargo",
        "rustc-ap-syntax",
    ];
    let mut name_to_id: HashMap<_, Vec<_>> = HashMap::new();
    for node in resolve.nodes.iter() {
        name_to_id.entry(node.id.split_whitespace().next().unwrap())
            .or_default()
            .push(&node.id);
    }

    for name in FORBIDDEN_TO_HAVE_DUPLICATES {
        if name_to_id[name].len() <= 1 {
            continue
        }
        println!("crate `{}` is duplicated in `Cargo.lock`", name);
        for id in name_to_id[name].iter() {
            println!("  * {}", id);
        }
        *bad = true;
    }
}
<|endoftext|>