<commit_before>//! Service in request multiplexing mode.

use std::collections::{HashMap, VecDeque};
use std::io;
use std::mem;
use std::time::{Duration, Instant};
use futures::{Async, Future, Poll};
use futures::stream::Stream;
use rand;
use tokio_core::reactor::{self, Timeout};
use ::resolv::error::Error;
use ::resolv::request::{RequestReceiver, ServiceRequest};
use ::resolv::transport::{Transport, Write};
use super::ExpiringService;

//------------ Service -------------------------------------------------------

/// A service in multiplex mode.
///
/// In multiplex mode, the service sends out any incoming request with a
/// unique ID and matches responses on that ID.
pub struct Service<T: Transport> {
    /// The receiver for new requests.
    ///
    /// This gets switched to `None` when the receiver gets disconnected or
    /// taken away by the expiring wrapper. If that has happened, we wait
    /// until all pending request have expired and then end the stream.
    receiver: Option<RequestReceiver>,

    /// A writer of requests.
    write: Writer<T>,

    /// A reader of responses.
    read: T::Read,

    /// A map with all the requests that are currently in flight.
    pending: PendingRequests
}

//--- ExpiringService

impl<T: Transport> ExpiringService<T> for Service<T> {
    fn create(rd: T::Read, wr: T::Write, receiver: RequestReceiver,
              request: ServiceRequest, reactor: reactor::Handle,
              request_timeout: Duration) -> Self {
        let mut pending = PendingRequests::new(reactor.clone(),
                                               request_timeout);
        let mut write = Writer::new(wr);
        write.write(request, &mut pending);
        Service {
            receiver: Some(receiver),
            write: write,
            read: rd,
            pending: pending
        }
    }

    fn take(&mut self) -> Option<RequestReceiver> {
        self.receiver.take()
    }
}

//--- Stream and Helper Methods for Stream

impl<T: Transport> Stream for Service<T> {
    type Item = ();
    type Error = io::Error;

    /// Polls the stream.
    ///
    /// This needs to return some ready whenever the outer timeout needs to
    /// be reset, ie., whenever we got a new request from the receiver. It
    /// needs to return ready none when the socket closed, ie., either the
    /// `self.read` or `self.write` are done.
    fn poll(&mut self) -> Poll<Option<()>, io::Error> {
        self.pending.expire();
        if let Async::Ready(item) = try!(self.poll_read()) {
            return Ok(Async::Ready(item))
        }
        self.poll_send()
    }
}

impl<T: Transport> Service<T> {
    /// Does the reading.
    ///
    /// Reads until there is nothing more to read and processes any messages
    /// received that way. Returns ready or an error if the outer function
    /// should return ready or an error and not ready if the outer function
    /// should continue with its business.
    fn poll_read(&mut self) -> Poll<Option<()>, io::Error> {
        while let Some(response) = try_ready!(self.read.poll()) {
            let id = response.header().id();
            if let Some(request) = self.pending.pop(id) {
                request.response(response)
            }
        }
        Ok(Async::Ready(None))
    }

    /// Does everything related to sending out requests.
    ///
    /// Returns whatever the outer poll ought to return.
    ///
    /// Tries to finish writing if necessary, then fetches a new request and
    /// starts again. If we did fetch a new request, we need to return some
    /// ready. If the receiver went away, we need to pretend we are still
    /// writing until all pending requests are either resolved or have
    /// expired.
    fn poll_send(&mut self) -> Poll<Option<()>, io::Error> {
        let mut res = Async::NotReady;
        loop {
            match try!(self.poll_write()) {
                Async::NotReady => return Ok(res),
                Async::Ready(Some(())) => { }
                Async::Ready(None) => return Ok(Async::Ready(None))
            }
            match try_ready!(self.poll_receiver()) {
                Some(()) => res = Async::Ready(Some(())),
                None => return Ok(Async::Ready(None))
            }
        }
    }

    /// Does the writing.
    ///
    /// Returns some ready if it is done writing or doesn’t have anything to
    /// write, none ready if the writer is gone and all requests are done,
    /// not ready if it needs to write some more but can’t and error
    /// if there is an error.
    fn poll_write(&mut self) -> Poll<Option<()>, io::Error> {
        let (wr, request) = match self.write {
            Writer::Idle(..) => return Ok(Async::Ready(Some(()))),
            Writer::Writing(ref mut fut) => {
                match fut.poll() {
                    Ok(Async::Ready(item)) => item,
                    Ok(Async::NotReady) => return Ok(Async::NotReady),
                    Err((err, request)) => {
                        self.pending.unreserve(request.id());
                        request.fail(Error::Timeout);
                        return Err(err)
                    }
                }
            }
            Writer::Closed => {
                if self.pending.is_empty() { return Ok(Async::Ready(None)) }
                else { return Ok(Async::NotReady) }
            }
        };
        self.write = Writer::Idle(wr);
        self.pending.push(request);
        Ok(Async::Ready(Some(())))
    }

    /// Polls the receiver if necessary.
    ///
    /// Tries to get a new request from the receiver if we don’t
    /// currently have anything to write. Or, if we don’t have a receiver
    /// anymore and no more pending requests either, we are done.
    ///
    /// Returns some ready if there is something to write, returns
    /// none ready if the receiver is disconnected and everything has
    /// expired, not ready if we are waiting for a new request (which sorta
    /// is also the case if the receiver has disconnected and there is still
    /// pending requests left), and error for an error.
    fn poll_receiver(&mut self) -> Poll<Option<()>, io::Error> {
        let request = if let Some(ref mut receiver) = self.receiver {
            try_ready!(receiver.poll())
        }
        else { None };
        let request = if let Some(request) = request { request }
        else {
            self.receiver = None;
            if self.pending.is_empty() { return Ok(Async::Ready(None)) }
            else { return Ok(Async::NotReady) }
        };
        self.write.write(request, &mut self.pending);
        Ok(Async::Ready(Some(())))
    }
}

//------------ Writer -------------------------------------------------------

/// A state machine for writing requests.
///
/// This wraps the states `Transport::Write` can go through in one handy
/// value.
enum Writer<T: Transport> {
    /// Not writing anything right now.
    Idle(T::Write),

    /// We are writing.
    ///
    /// The future resolves into a `(T::Write, ServiceRequest)`.
    Writing(<T::Write as Write>::Future),

    /// The socket has been closed.
    Closed
}

impl<T: Transport> Writer<T> {
    /// Creates a new idle writer.
    fn new(wr: T::Write) -> Self {
        Writer::Idle(wr)
    }

    /// Starts writing `requests`.
    fn write(&mut self, mut request: ServiceRequest,
           pending: &mut PendingRequests) {
        let wr = match mem::replace(self, Writer::Closed) {
            Writer::Idle(wr) => wr,
            _ => unreachable!()
        };
        *self = match pending.reserve() {
            Ok(id) => {
                request.set_id(id);
                Writer::Writing(wr.write(request))
            }
            Err(_) => {
                request.fail(io::Error::new(io::ErrorKind::Other,
                                            "too many requests").into());
                Writer::Idle(wr)
            }
        }
    }
}

//------------ PendingRequests -----------------------------------------------

/// A collection of pending requests.
pub struct PendingRequests {
    /// A map from DNS message IDs to requests.
    ///
    /// If an ID is reserved, it maps to `None`, if a request has been
    /// pushed for it, it maps to `Some(_)`.
    requests: HashMap<u16, Option<ServiceRequest>>,

    /// An ordered list of message IDs and when they expire.
    ///
    /// Since we have a fixed duration and monotone time, we can use a
    /// simple deque here and push new requests to its end.
    expires: VecDeque<(u16, Instant)>,

    /// The optional future for the next time a request expires.
    timeout: Option<Timeout>,

    /// A handle to a reactor for creating timeout futures.
    reactor: reactor::Handle,

    /// The duration until a request expires.
    duration: Duration,
}

impl PendingRequests {
    /// Creates a new collection.
    pub fn new(reactor: reactor::Handle, expire: Duration) -> Self {
        PendingRequests {
            requests: HashMap::new(),
            expires: VecDeque::new(),
            timeout: None,
            reactor: reactor,
            duration: expire
        }
    }

    /// Returns whether there are no more pending requests.
    pub fn is_empty(&self) -> bool {
        self.requests.is_empty()
    }

    /// Reserves a spot in the map and returns its ID.
    pub fn reserve(&mut self) -> Result<u16, ReserveError> {
        use std::collections::hash_map::Entry;

        // Pick a reasonably low number here so that we won’t hang too long
        // below.
        if self.requests.len() > 0xA000 {
            return Err(ReserveError);
        }
        // XXX I suppose there is a better way to do this. Anyone?
        loop {
            let id = rand::random();
            if let Entry::Vacant(entry) = self.requests.entry(id) {
                entry.insert(None);
                return Ok(id)
            }
        }
    }

    /// Drop a previously reserved id.
    pub fn unreserve(&mut self, id: u16) {
        match self.requests.remove(&id) {
            Some(Some(_)) => panic!("unreserving pushed ID"),
            Some(None) => { }
            None => panic!("unreserving unreserved ID")
        }
    }

    /// Adds the requests with the given ID to the collection.
    ///
    /// The `id` must have been reserved before and nothing been pushed
    /// for this ID since. Panics otherwise.
    pub fn push(&mut self, request: ServiceRequest) {
        let id = request.id();
        {
            let entry = self.requests.get_mut(&id)
                                     .expect("pushed unreserved ID");
            if entry.is_some() {
                panic!("pushed over existing ID");
            }
            *entry = Some(request);
        }
        self.expires.push_back((id, Instant::now() + self.duration));
        if self.timeout.is_none() {
            self.update_timeout();
        }
    }

    /// Removes and returns the request with the given ID.
    pub fn pop(&mut self, id: u16) -> Option<ServiceRequest> {
        if let Some(request) = self.requests.remove(&id) {
            if self.expires.front().unwrap().0 == id {
                self.expires.pop_front();
                self.update_timeout();
            }
            request
        }
        else { None }
    }

    /// Updates the timeout.
    ///
    /// Since we don’t delete the IDs in `pop()` (which could be expensive
    /// if they are somewhere in the middle), we need to pop items from the
    /// front until we find one that is actually still valid.
    fn update_timeout(&mut self) {
        while let Some(&(id, at)) = self.expires.front() {
            if self.requests.contains_key(&id) {
                // XXX What’s the best thing to do when getting a timeout
                //     fails?
                self.timeout = Timeout::new_at(at, &self.reactor).ok();
                return;
            }
            else {
                self.expires.pop_front();
            }
        }
        self.timeout = None
    }

    /// Removes and fails all expired requests.
    ///
    /// This method polls `self`’s timeout so it can only be called from
    /// within a task.
    pub fn expire(&mut self) {
        match self.timeout {
            Some(ref mut timeout) => {
                match timeout.poll() {
                    Ok(Async::NotReady) => return,
                    Ok(Async::Ready(())) => {
                        loop {
                            match self.expires.front() {
                                Some(&(_, at)) if at > Instant::now() => { }
                                _ => break
                            }
                            let id = self.expires.pop_front().unwrap().0;
                            if let Some(Some(item)) = self.requests
                                                          .remove(&id) {
                                item.fail(Error::Timeout)
                            }
                        }
                    }
                    Err(_) => {
                        // Fall through to update_timeout to perhaps fix
                        // the broken timeout.
                    }
                }
            }
            None => return
        }
        self.update_timeout();
        // Once more to register the timeout.
        self.expire()
    }
}

//--- Drop

impl Drop for PendingRequests {
    fn drop(&mut self) {
        for (_, item) in self.requests.drain() {
            if let Some(item) = item {
                item.fail(Error::Timeout)
            }
        }
    }
}

//------------ ReserveError --------------------------------------------------

/// An error happened while reserving an ID.
///
/// The only thing that can happen is that we run out of space.
pub struct ReserveError;

<commit_msg>Remove expired items in multiplex service instead of unexpired ones.<commit_after>//! Service in request multiplexing mode.

use std::collections::{HashMap, VecDeque};
use std::io;
use std::mem;
use std::time::{Duration, Instant};
use futures::{Async, Future, Poll};
use futures::stream::Stream;
use rand;
use tokio_core::reactor::{self, Timeout};
use ::resolv::error::Error;
use ::resolv::request::{RequestReceiver, ServiceRequest};
use ::resolv::transport::{Transport, Write};
use super::ExpiringService;

//------------ Service -------------------------------------------------------

/// A service in multiplex mode.
///
/// In multiplex mode, the service sends out any incoming request with a
/// unique ID and matches responses on that ID.
pub struct Service<T: Transport> {
    /// The receiver for new requests.
    ///
    /// This gets switched to `None` when the receiver gets disconnected or
    /// taken away by the expiring wrapper. If that has happened, we wait
    /// until all pending request have expired and then end the stream.
    receiver: Option<RequestReceiver>,

    /// A writer of requests.
    write: Writer<T>,

    /// A reader of responses.
    read: T::Read,

    /// A map with all the requests that are currently in flight.
    pending: PendingRequests
}

//--- ExpiringService

impl<T: Transport> ExpiringService<T> for Service<T> {
    fn create(rd: T::Read, wr: T::Write, receiver: RequestReceiver,
              request: ServiceRequest, reactor: reactor::Handle,
              request_timeout: Duration) -> Self {
        let mut pending = PendingRequests::new(reactor.clone(),
                                               request_timeout);
        let mut write = Writer::new(wr);
        write.write(request, &mut pending);
        Service {
            receiver: Some(receiver),
            write: write,
            read: rd,
            pending: pending
        }
    }

    fn take(&mut self) -> Option<RequestReceiver> {
        self.receiver.take()
    }
}

//--- Stream and Helper Methods for Stream

impl<T: Transport> Stream for Service<T> {
    type Item = ();
    type Error = io::Error;

    /// Polls the stream.
    ///
    /// This needs to return some ready whenever the outer timeout needs to
    /// be reset, ie., whenever we got a new request from the receiver. It
    /// needs to return ready none when the socket closed, ie., either the
    /// `self.read` or `self.write` are done.
    fn poll(&mut self) -> Poll<Option<()>, io::Error> {
        self.pending.expire();
        if let Async::Ready(item) = try!(self.poll_read()) {
            return Ok(Async::Ready(item))
        }
        self.poll_send()
    }
}

impl<T: Transport> Service<T> {
    /// Does the reading.
    ///
    /// Reads until there is nothing more to read and processes any messages
    /// received that way. Returns ready or an error if the outer function
    /// should return ready or an error and not ready if the outer function
    /// should continue with its business.
    fn poll_read(&mut self) -> Poll<Option<()>, io::Error> {
        while let Some(response) = try_ready!(self.read.poll()) {
            let id = response.header().id();
            if let Some(request) = self.pending.pop(id) {
                request.response(response)
            }
        }
        Ok(Async::Ready(None))
    }

    /// Does everything related to sending out requests.
    ///
    /// Returns whatever the outer poll ought to return.
    ///
    /// Tries to finish writing if necessary, then fetches a new request and
    /// starts again. If we did fetch a new request, we need to return some
    /// ready. If the receiver went away, we need to pretend we are still
    /// writing until all pending requests are either resolved or have
    /// expired.
    fn poll_send(&mut self) -> Poll<Option<()>, io::Error> {
        let mut res = Async::NotReady;
        loop {
            match try!(self.poll_write()) {
                Async::NotReady => return Ok(res),
                Async::Ready(Some(())) => { }
                Async::Ready(None) => return Ok(Async::Ready(None))
            }
            match try_ready!(self.poll_receiver()) {
                Some(()) => res = Async::Ready(Some(())),
                None => return Ok(Async::Ready(None))
            }
        }
    }

    /// Does the writing.
    ///
    /// Returns some ready if it is done writing or doesn’t have anything to
    /// write, none ready if the writer is gone and all requests are done,
    /// not ready if it needs to write some more but can’t and error
    /// if there is an error.
    fn poll_write(&mut self) -> Poll<Option<()>, io::Error> {
        let (wr, request) = match self.write {
            Writer::Idle(..) => return Ok(Async::Ready(Some(()))),
            Writer::Writing(ref mut fut) => {
                match fut.poll() {
                    Ok(Async::Ready(item)) => item,
                    Ok(Async::NotReady) => return Ok(Async::NotReady),
                    Err((err, request)) => {
                        self.pending.unreserve(request.id());
                        request.fail(Error::Timeout);
                        return Err(err)
                    }
                }
            }
            Writer::Closed => {
                if self.pending.is_empty() { return Ok(Async::Ready(None)) }
                else { return Ok(Async::NotReady) }
            }
        };
        self.write = Writer::Idle(wr);
        self.pending.push(request);
        Ok(Async::Ready(Some(())))
    }

    /// Polls the receiver if necessary.
    ///
    /// Tries to get a new request from the receiver if we don’t
    /// currently have anything to write. Or, if we don’t have a receiver
    /// anymore and no more pending requests either, we are done.
    ///
    /// Returns some ready if there is something to write, returns
    /// none ready if the receiver is disconnected and everything has
    /// expired, not ready if we are waiting for a new request (which sorta
    /// is also the case if the receiver has disconnected and there is still
    /// pending requests left), and error for an error.
    fn poll_receiver(&mut self) -> Poll<Option<()>, io::Error> {
        let request = if let Some(ref mut receiver) = self.receiver {
            try_ready!(receiver.poll())
        }
        else { None };
        let request = if let Some(request) = request { request }
        else {
            self.receiver = None;
            if self.pending.is_empty() { return Ok(Async::Ready(None)) }
            else { return Ok(Async::NotReady) }
        };
        self.write.write(request, &mut self.pending);
        Ok(Async::Ready(Some(())))
    }
}

//------------ Writer -------------------------------------------------------

/// A state machine for writing requests.
///
/// This wraps the states `Transport::Write` can go through in one handy
/// value.
enum Writer<T: Transport> {
    /// Not writing anything right now.
    Idle(T::Write),

    /// We are writing.
    ///
    /// The future resolves into a `(T::Write, ServiceRequest)`.
    Writing(<T::Write as Write>::Future),

    /// The socket has been closed.
    Closed
}

impl<T: Transport> Writer<T> {
    /// Creates a new idle writer.
    fn new(wr: T::Write) -> Self {
        Writer::Idle(wr)
    }

    /// Starts writing `requests`.
    fn write(&mut self, mut request: ServiceRequest,
           pending: &mut PendingRequests) {
        let wr = match mem::replace(self, Writer::Closed) {
            Writer::Idle(wr) => wr,
            _ => unreachable!()
        };
        *self = match pending.reserve() {
            Ok(id) => {
                request.set_id(id);
                Writer::Writing(wr.write(request))
            }
            Err(_) => {
                request.fail(io::Error::new(io::ErrorKind::Other,
                                            "too many requests").into());
                Writer::Idle(wr)
            }
        }
    }
}

//------------ PendingRequests -----------------------------------------------

/// A collection of pending requests.
pub struct PendingRequests {
    /// A map from DNS message IDs to requests.
    ///
    /// If an ID is reserved, it maps to `None`, if a request has been
    /// pushed for it, it maps to `Some(_)`.
    requests: HashMap<u16, Option<ServiceRequest>>,

    /// An ordered list of message IDs and when they expire.
    ///
    /// Since we have a fixed duration and monotone time, we can use a
    /// simple deque here and push new requests to its end.
    expires: VecDeque<(u16, Instant)>,

    /// The optional future for the next time a request expires.
    timeout: Option<Timeout>,

    /// A handle to a reactor for creating timeout futures.
    reactor: reactor::Handle,

    /// The duration until a request expires.
    duration: Duration,
}

impl PendingRequests {
    /// Creates a new collection.
    pub fn new(reactor: reactor::Handle, expire: Duration) -> Self {
        PendingRequests {
            requests: HashMap::new(),
            expires: VecDeque::new(),
            timeout: None,
            reactor: reactor,
            duration: expire
        }
    }

    /// Returns whether there are no more pending requests.
    pub fn is_empty(&self) -> bool {
        self.requests.is_empty()
    }

    /// Reserves a spot in the map and returns its ID.
    pub fn reserve(&mut self) -> Result<u16, ReserveError> {
        use std::collections::hash_map::Entry;

        // Pick a reasonably low number here so that we won’t hang too long
        // below.
        if self.requests.len() > 0xA000 {
            return Err(ReserveError);
        }
        // XXX I suppose there is a better way to do this. Anyone?
        loop {
            let id = rand::random();
            if let Entry::Vacant(entry) = self.requests.entry(id) {
                entry.insert(None);
                return Ok(id)
            }
        }
    }

    /// Drop a previously reserved id.
    pub fn unreserve(&mut self, id: u16) {
        match self.requests.remove(&id) {
            Some(Some(_)) => panic!("unreserving pushed ID"),
            Some(None) => { }
            None => panic!("unreserving unreserved ID")
        }
    }

    /// Adds the requests with the given ID to the collection.
    ///
    /// The `id` must have been reserved before and nothing been pushed
    /// for this ID since. Panics otherwise.
    pub fn push(&mut self, request: ServiceRequest) {
        let id = request.id();
        {
            let entry = self.requests.get_mut(&id)
                                     .expect("pushed unreserved ID");
            if entry.is_some() {
                panic!("pushed over existing ID");
            }
            *entry = Some(request);
        }
        self.expires.push_back((id, Instant::now() + self.duration));
        if self.timeout.is_none() {
            self.update_timeout();
        }
    }

    /// Removes and returns the request with the given ID.
    pub fn pop(&mut self, id: u16) -> Option<ServiceRequest> {
        if let Some(request) = self.requests.remove(&id) {
            if self.expires.front().unwrap().0 == id {
                self.expires.pop_front();
                self.update_timeout();
            }
            request
        }
        else { None }
    }

    /// Updates the timeout.
    ///
    /// Since we don’t delete the IDs in `pop()` (which could be expensive
    /// if they are somewhere in the middle), we need to pop items from the
    /// front until we find one that is actually still valid.
    fn update_timeout(&mut self) {
        while let Some(&(id, at)) = self.expires.front() {
            if self.requests.contains_key(&id) {
                // XXX What’s the best thing to do when getting a timeout
                //     fails?
                self.timeout = Timeout::new_at(at, &self.reactor).ok();
                return;
            }
            else {
                self.expires.pop_front();
            }
        }
        self.timeout = None
    }

    /// Removes and fails all expired requests.
    ///
    /// This method polls `self`’s timeout so it can only be called from
    /// within a task.
    pub fn expire(&mut self) {
        match self.timeout {
            Some(ref mut timeout) => {
                match timeout.poll() {
                    Ok(Async::NotReady) => return,
                    Ok(Async::Ready(())) => {
                        loop {
                            match self.expires.front() {
                                Some(&(_, at)) if at < Instant::now() => { }
                                _ => break
                            }
                            let id = self.expires.pop_front().unwrap().0;
                            if let Some(Some(item)) = self.requests
                                                          .remove(&id) {
                                item.fail(Error::Timeout)
                            }
                        }
                    }
                    Err(_) => {
                        // Fall through to update_timeout to perhaps fix
                        // the broken timeout.
                    }
                }
            }
            None => return
        }
        self.update_timeout();
        // Once more to register the timeout.
        self.expire()
    }
}

//--- Drop

impl Drop for PendingRequests {
    fn drop(&mut self) {
        for (_, item) in self.requests.drain() {
            if let Some(item) = item {
                item.fail(Error::Timeout)
            }
        }
    }
}

//------------ ReserveError --------------------------------------------------

/// An error happened while reserving an ID.
///
/// The only thing that can happen is that we run out of space.
pub struct ReserveError;

<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

extern crate cc;
extern crate glsl_to_cxx;
extern crate webrender_build;

use std::collections::HashSet;
use std::fmt::Write;
use webrender_build::shader::{ShaderFeatureFlags, get_shader_features};

// Shader key is in "name feature,feature" format.
// File name needs to be formatted as "name_feature_feature".
fn shader_file(shader_key: &str) -> String {
    shader_key.replace(' ', "_").replace(',', "_")
}

fn write_load_shader(shader_keys: &[String]) {
    let mut load_shader = String::new();
    for s in shader_keys {
        let _ = write!(load_shader, "#include \"{}.h\"\n", shader_file(s));
    }
    load_shader.push_str("ProgramLoader load_shader(const char* name) {\n");
    for s in shader_keys {
        let _ = write!(load_shader, "  if (!strcmp(name, \"{}\")) {{ return {}_program::loader; }}\n",
                       s, shader_file(s));
    }
    load_shader.push_str("  return nullptr;\n}\n");
    std::fs::write(std::env::var("OUT_DIR").unwrap() + "/load_shader.h", load_shader).unwrap();
}

fn process_imports(shader_dir: &str, shader: &str, included: &mut HashSet<String>, output: &mut String) {
    if !included.insert(shader.into()) {
        return;
    }
    println!("cargo:rerun-if-changed={}/{}.glsl", shader_dir, shader);
    let source = std::fs::read_to_string(format!("{}/{}.glsl", shader_dir, shader)).unwrap();
    for line in source.lines() {
        if line.starts_with("#include ") {
            let imports = line["#include ".len() ..].split(',');
            for import in imports {
                process_imports(shader_dir, import, included, output);
            }
        } else if line.starts_with("#version ") || line.starts_with("#extension ") {
            // ignore
        } else {
            output.push_str(line);
            output.push('\n');
        }
    }
}

fn translate_shader(shader_key: &str, shader_dir: &str) {
    let mut imported = String::from("#define SWGL 1\n");
    let _ = write!(imported, "#define WR_MAX_VERTEX_TEXTURE_WIDTH {}U\n",
                   webrender_build::MAX_VERTEX_TEXTURE_WIDTH);

    let (basename, features) =
        shader_key.split_at(shader_key.find(' ').unwrap_or(shader_key.len()));
    if !features.is_empty() {
        for feature in features.trim().split(',') {
            let _ = write!(imported, "#define WR_FEATURE_{}\n", feature);
        }
    }

    process_imports(shader_dir, basename, &mut HashSet::new(), &mut imported);

    let shader = shader_file(shader_key);

    let out_dir = std::env::var("OUT_DIR").unwrap();
    let imp_name = format!("{}/{}.c", out_dir, shader);
    std::fs::write(&imp_name, imported).unwrap();

    let mut build = cc::Build::new();
    if build.get_compiler().is_like_msvc() {
        build.flag("/EP");
    } else {
        build.flag("-xc").flag("-P");
    }
    build.file(&imp_name);
    let vs = build.clone()
        .define("WR_VERTEX_SHADER", Some("1"))
        .expand();
    let fs = build.clone()
        .define("WR_FRAGMENT_SHADER", Some("1"))
        .expand();
    let vs_name = format!("{}/{}.vert", out_dir, shader);
    let fs_name = format!("{}/{}.frag", out_dir, shader);
    std::fs::write(&vs_name, vs).unwrap();
    std::fs::write(&fs_name, fs).unwrap();

    let args = vec![
        "glsl_to_cxx".to_string(),
        vs_name,
        fs_name,
    ];
    let result = glsl_to_cxx::translate(&mut args.into_iter());
    std::fs::write(format!("{}/{}.h", out_dir, shader), result).unwrap();
}

fn main() {
    let shader_dir = match std::env::var("MOZ_SRC") {
        Ok(dir) => dir + "/gfx/wr/webrender/res",
        Err(_) => std::env::var("CARGO_MANIFEST_DIR").unwrap() + "/../webrender/res",
    };

    let shader_flags =
        ShaderFeatureFlags::GL |
        ShaderFeatureFlags::DUAL_SOURCE_BLENDING |
        ShaderFeatureFlags::ADVANCED_BLEND_EQUATION |
        ShaderFeatureFlags::DEBUG;
    let mut shaders: Vec<String> = Vec::new();
    for (name, features) in get_shader_features(shader_flags) {
        shaders.extend(features.iter().map(|f| {
            if f.is_empty() { name.to_owned() } else { format!("{} {}", name, f) }
        }));
    }

    shaders.sort();

    for shader in &shaders {
        translate_shader(shader, &shader_dir);
    }

    write_load_shader(&shaders);

    println!("cargo:rerun-if-changed=src/blend.h");
    println!("cargo:rerun-if-changed=src/composite.h");
    println!("cargo:rerun-if-changed=src/gl_defs.h");
    println!("cargo:rerun-if-changed=src/glsl.h");
    println!("cargo:rerun-if-changed=src/program.h");
    println!("cargo:rerun-if-changed=src/rasterize.h");
    println!("cargo:rerun-if-changed=src/swgl_ext.h");
    println!("cargo:rerun-if-changed=src/texture.h");
    println!("cargo:rerun-if-changed=src/vector_type.h");
    println!("cargo:rerun-if-changed=src/gl.cc");
    cc::Build::new()
        .cpp(true)
        .file("src/gl.cc")
        .flag("-std=c++17")
        .flag("-UMOZILLA_CONFIG_H")
        .flag("-fno-exceptions")
        .flag("-fno-rtti")
        .flag("-fno-math-errno")
        .define("_GLIBCXX_USE_CXX11_ABI", Some("0"))
        .include(shader_dir)
        .include("src")
        .include(std::env::var("OUT_DIR").unwrap())
        .compile("gl_cc");
}
<commit_msg>Bug 1697502. Override -Oz with -O2 when building SWGL. r=lsalzman<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

extern crate cc;
extern crate glsl_to_cxx;
extern crate webrender_build;

use std::collections::HashSet;
use std::fmt::Write;
use webrender_build::shader::{ShaderFeatureFlags, get_shader_features};

// Shader key is in "name feature,feature" format.
// File name needs to be formatted as "name_feature_feature".
fn shader_file(shader_key: &str) -> String {
    shader_key.replace(' ', "_").replace(',', "_")
}

fn write_load_shader(shader_keys: &[String]) {
    let mut load_shader = String::new();
    for s in shader_keys {
        let _ = write!(load_shader, "#include \"{}.h\"\n", shader_file(s));
    }
    load_shader.push_str("ProgramLoader load_shader(const char* name) {\n");
    for s in shader_keys {
        let _ = write!(load_shader, "  if (!strcmp(name, \"{}\")) {{ return {}_program::loader; }}\n",
                       s, shader_file(s));
    }
    load_shader.push_str("  return nullptr;\n}\n");
    std::fs::write(std::env::var("OUT_DIR").unwrap() + "/load_shader.h", load_shader).unwrap();
}

fn process_imports(shader_dir: &str, shader: &str, included: &mut HashSet<String>, output: &mut String) {
    if !included.insert(shader.into()) {
        return;
    }
    println!("cargo:rerun-if-changed={}/{}.glsl", shader_dir, shader);
    let source = std::fs::read_to_string(format!("{}/{}.glsl", shader_dir, shader)).unwrap();
    for line in source.lines() {
        if line.starts_with("#include ") {
            let imports = line["#include ".len() ..].split(',');
            for import in imports {
                process_imports(shader_dir, import, included, output);
            }
        } else if line.starts_with("#version ") || line.starts_with("#extension ") {
            // ignore
        } else {
            output.push_str(line);
            output.push('\n');
        }
    }
}

fn translate_shader(shader_key: &str, shader_dir: &str) {
    let mut imported = String::from("#define SWGL 1\n");
    let _ = write!(imported, "#define WR_MAX_VERTEX_TEXTURE_WIDTH {}U\n",
                   webrender_build::MAX_VERTEX_TEXTURE_WIDTH);

    let (basename, features) =
        shader_key.split_at(shader_key.find(' ').unwrap_or(shader_key.len()));
    if !features.is_empty() {
        for feature in features.trim().split(',') {
            let _ = write!(imported, "#define WR_FEATURE_{}\n", feature);
        }
    }

    process_imports(shader_dir, basename, &mut HashSet::new(), &mut imported);

    let shader = shader_file(shader_key);

    let out_dir = std::env::var("OUT_DIR").unwrap();
    let imp_name = format!("{}/{}.c", out_dir, shader);
    std::fs::write(&imp_name, imported).unwrap();

    let mut build = cc::Build::new();
    if build.get_compiler().is_like_msvc() {
        build.flag("/EP");
    } else {
        build.flag("-xc").flag("-P");
    }
    build.file(&imp_name);
    let vs = build.clone()
        .define("WR_VERTEX_SHADER", Some("1"))
        .expand();
    let fs = build.clone()
        .define("WR_FRAGMENT_SHADER", Some("1"))
        .expand();
    let vs_name = format!("{}/{}.vert", out_dir, shader);
    let fs_name = format!("{}/{}.frag", out_dir, shader);
    std::fs::write(&vs_name, vs).unwrap();
    std::fs::write(&fs_name, fs).unwrap();

    let args = vec![
        "glsl_to_cxx".to_string(),
        vs_name,
        fs_name,
    ];
    let result = glsl_to_cxx::translate(&mut args.into_iter());
    std::fs::write(format!("{}/{}.h", out_dir, shader), result).unwrap();
}

fn main() {
    let shader_dir = match std::env::var("MOZ_SRC") {
        Ok(dir) => dir + "/gfx/wr/webrender/res",
        Err(_) => std::env::var("CARGO_MANIFEST_DIR").unwrap() + "/../webrender/res",
    };

    let shader_flags =
        ShaderFeatureFlags::GL |
        ShaderFeatureFlags::DUAL_SOURCE_BLENDING |
        ShaderFeatureFlags::ADVANCED_BLEND_EQUATION |
        ShaderFeatureFlags::DEBUG;
    let mut shaders: Vec<String> = Vec::new();
    for (name, features) in get_shader_features(shader_flags) {
        shaders.extend(features.iter().map(|f| {
            if f.is_empty() { name.to_owned() } else { format!("{} {}", name, f) }
        }));
    }

    shaders.sort();

    for shader in &shaders {
        translate_shader(shader, &shader_dir);
    }

    write_load_shader(&shaders);

    println!("cargo:rerun-if-changed=src/blend.h");
    println!("cargo:rerun-if-changed=src/composite.h");
    println!("cargo:rerun-if-changed=src/gl_defs.h");
    println!("cargo:rerun-if-changed=src/glsl.h");
    println!("cargo:rerun-if-changed=src/program.h");
    println!("cargo:rerun-if-changed=src/rasterize.h");
    println!("cargo:rerun-if-changed=src/swgl_ext.h");
    println!("cargo:rerun-if-changed=src/texture.h");
    println!("cargo:rerun-if-changed=src/vector_type.h");
    println!("cargo:rerun-if-changed=src/gl.cc");
    let mut build = cc::Build::new();
    build.cpp(true);

    // SWGL relies heavily on inlining for performance so override -Oz with -O2
    if build.get_compiler().args().contains(&std::ffi::OsString::from("-Oz")) {
        build.flag("-O2");
    }

    build.file("src/gl.cc")
        .flag("-std=c++17")
        .flag("-UMOZILLA_CONFIG_H")
        .flag("-fno-exceptions")
        .flag("-fno-rtti")
        .flag("-fno-math-errno")
        .define("_GLIBCXX_USE_CXX11_ABI", Some("0"))
        .include(shader_dir)
        .include("src")
        .include(std::env::var("OUT_DIR").unwrap())
        .compile("gl_cc");
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

extern crate cc;
extern crate glsl_to_cxx;
extern crate webrender_build;

use std::collections::HashSet;
use std::fmt::Write;
use webrender_build::shader::{ShaderFeatureFlags, get_shader_features};

// Shader key is in "name feature,feature" format.
// File name needs to be formatted as "name_feature_feature".
fn shader_file(shader_key: &str) -> String {
    shader_key.replace(' ', "_").replace(',', "_")
}

fn write_load_shader(shader_keys: &[String]) {
    let mut load_shader = String::new();
    for s in shader_keys {
        let _ = write!(load_shader, "#include \"{}.h\"\n", shader_file(s));
    }
    load_shader.push_str("ProgramLoader load_shader(const char* name) {\n");
    for s in shader_keys {
        let _ = write!(load_shader, "  if (!strcmp(name, \"{}\")) {{ return {}_program::loader; }}\n",
                       s, shader_file(s));
    }
    load_shader.push_str("  return nullptr;\n}\n");
    std::fs::write(std::env::var("OUT_DIR").unwrap() + "/load_shader.h", load_shader).unwrap();
}

fn process_imports(shader_dir: &str, shader: &str, included: &mut HashSet<String>, output: &mut String) {
    if !included.insert(shader.into()) {
        return;
    }
    println!("cargo:rerun-if-changed={}/{}.glsl", shader_dir, shader);
    let source = std::fs::read_to_string(format!("{}/{}.glsl", shader_dir, shader)).unwrap();
    for line in source.lines() {
        if line.starts_with("#include ") {
            let imports = line["#include ".len() ..].split(',');
            for import in imports {
                process_imports(shader_dir, import, included, output);
            }
        } else if line.starts_with("#version ") || line.starts_with("#extension ") {
            // ignore
        } else {
            output.push_str(line);
            output.push('\n');
        }
    }
}

fn translate_shader(shader_key: &str, shader_dir: &str) {
    let mut imported = String::from("#define SWGL 1\n#define __VERSION__ 150\n");
    let _ = write!(imported, "#define WR_MAX_VERTEX_TEXTURE_WIDTH {}U\n",
                   webrender_build::MAX_VERTEX_TEXTURE_WIDTH);

    let (basename, features) =
        shader_key.split_at(shader_key.find(' ').unwrap_or(shader_key.len()));
    if !features.is_empty() {
        for feature in features.trim().split(',') {
            let _ = write!(imported, "#define WR_FEATURE_{}\n", feature);
        }
    }

    process_imports(shader_dir, basename, &mut HashSet::new(), &mut imported);

    let shader = shader_file(shader_key);

    let out_dir = std::env::var("OUT_DIR").unwrap();
    let imp_name = format!("{}/{}.c", out_dir, shader);
    std::fs::write(&imp_name, imported).unwrap();

    let mut build = cc::Build::new();
    // Use SWGLPP target to avoid pulling CFLAGS/CXXFLAGS.
    build.target("SWGLPP");
    build.no_default_flags(true);
    if build.get_compiler().is_like_msvc() {
        build.flag("/EP").flag("/clang:-undef");
    } else {
        build.flag("-xc").flag("-P").flag("-undef");
    }
    build.file(&imp_name);
    let vs = build.clone()
        .define("WR_VERTEX_SHADER", Some("1"))
        .expand();
    let fs = build.clone()
        .define("WR_FRAGMENT_SHADER", Some("1"))
        .expand();
    let vs_name = format!("{}/{}.vert", out_dir, shader);
    let fs_name = format!("{}/{}.frag", out_dir, shader);
    std::fs::write(&vs_name, vs).unwrap();
    std::fs::write(&fs_name, fs).unwrap();

    let args = vec![
        "glsl_to_cxx".to_string(),
        vs_name,
        fs_name,
    ];
    let result = glsl_to_cxx::translate(&mut args.into_iter());
    std::fs::write(format!("{}/{}.h", out_dir, shader), result).unwrap();
}

fn main() {
    let shader_dir = match std::env::var("MOZ_SRC") {
        Ok(dir) => dir + "/gfx/wr/webrender/res",
        Err(_) => std::env::var("CARGO_MANIFEST_DIR").unwrap() + "/../webrender/res",
    };

    let shader_flags =
        ShaderFeatureFlags::GL |
        ShaderFeatureFlags::DUAL_SOURCE_BLENDING |
        ShaderFeatureFlags::ADVANCED_BLEND_EQUATION |
        ShaderFeatureFlags::DEBUG;
    let mut shaders: Vec<String> = Vec::new();
    for (name, features) in get_shader_features(shader_flags) {
        shaders.extend(features.iter().map(|f| {
            if f.is_empty() { name.to_owned() } else { format!("{} {}", name, f) }
        }));
    }

    shaders.sort();

    // We need to ensure that the C preprocessor does not pull compiler flags from
    // the host or target environment. Set up a SWGLPP target with empty flags to
    // work around this.
    std::env::set_var("CFLAGS_SWGLPP", "");
    std::env::set_var("CXXFLAGS_SWGLPP", "");

    for shader in &shaders {
        translate_shader(shader, &shader_dir);
    }

    write_load_shader(&shaders);

    println!("cargo:rerun-if-changed=src/blend.h");
    println!("cargo:rerun-if-changed=src/composite.h");
    println!("cargo:rerun-if-changed=src/gl_defs.h");
    println!("cargo:rerun-if-changed=src/glsl.h");
    println!("cargo:rerun-if-changed=src/program.h");
    println!("cargo:rerun-if-changed=src/rasterize.h");
    println!("cargo:rerun-if-changed=src/swgl_ext.h");
    println!("cargo:rerun-if-changed=src/texture.h");
    println!("cargo:rerun-if-changed=src/vector_type.h");
    println!("cargo:rerun-if-changed=src/gl.cc");
    let mut build = cc::Build::new();
    build.cpp(true);

    // SWGL relies heavily on inlining for performance so override -Oz with -O2
    if build.get_compiler().args().contains(&std::ffi::OsString::from("-Oz")) {
        build.flag("-O2");
    }

    build.file("src/gl.cc")
        .flag("-std=c++17")
        .flag("-UMOZILLA_CONFIG_H")
        .flag("-fno-exceptions")
        .flag("-fno-rtti")
        .flag("-fno-math-errno")
        .define("_GLIBCXX_USE_CXX11_ABI", Some("0"))
        .include(shader_dir)
        .include("src")
        .include(std::env::var("OUT_DIR").unwrap())
        .compile("gl_cc");
}
<commit_msg>Backed out changeset 38a9b0040e8f (bug 1698156) for build bustages. CLOSED TREE<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

extern crate cc;
extern crate glsl_to_cxx;
extern crate webrender_build;

use std::collections::HashSet;
use std::fmt::Write;
use webrender_build::shader::{ShaderFeatureFlags, get_shader_features};

// Shader key is in "name feature,feature" format.
// File name needs to be formatted as "name_feature_feature".
fn shader_file(shader_key: &str) -> String {
    shader_key.replace(' ', "_").replace(',', "_")
}

fn write_load_shader(shader_keys: &[String]) {
    let mut load_shader = String::new();
    for s in shader_keys {
        let _ = write!(load_shader, "#include \"{}.h\"\n", shader_file(s));
    }
    load_shader.push_str("ProgramLoader load_shader(const char* name) {\n");
    for s in shader_keys {
        let _ = write!(load_shader, "  if (!strcmp(name, \"{}\")) {{ return {}_program::loader; }}\n",
                       s, shader_file(s));
    }
    load_shader.push_str("  return nullptr;\n}\n");
    std::fs::write(std::env::var("OUT_DIR").unwrap() + "/load_shader.h", load_shader).unwrap();
}

fn process_imports(shader_dir: &str, shader: &str, included: &mut HashSet<String>, output: &mut String) {
    if !included.insert(shader.into()) {
        return;
    }
    println!("cargo:rerun-if-changed={}/{}.glsl", shader_dir, shader);
    let source = std::fs::read_to_string(format!("{}/{}.glsl", shader_dir, shader)).unwrap();
    for line in source.lines() {
        if line.starts_with("#include ") {
            let imports = line["#include ".len() ..].split(',');
            for import in imports {
                process_imports(shader_dir, import, included, output);
            }
        } else if line.starts_with("#version ") || line.starts_with("#extension ") {
            // ignore
        } else {
            output.push_str(line);
            output.push('\n');
        }
    }
}

fn translate_shader(shader_key: &str, shader_dir: &str) {
    let mut imported = String::from("#define SWGL 1\n#define __VERSION__ 150\n");
    let _ = write!(imported, "#define WR_MAX_VERTEX_TEXTURE_WIDTH {}U\n",
                   webrender_build::MAX_VERTEX_TEXTURE_WIDTH);

    let (basename, features) =
        shader_key.split_at(shader_key.find(' ').unwrap_or(shader_key.len()));
    if !features.is_empty() {
        for feature in features.trim().split(',') {
            let _ = write!(imported, "#define WR_FEATURE_{}\n", feature);
        }
    }

    process_imports(shader_dir, basename, &mut HashSet::new(), &mut imported);

    let shader = shader_file(shader_key);

    let out_dir = std::env::var("OUT_DIR").unwrap();
    let imp_name = format!("{}/{}.c", out_dir, shader);
    std::fs::write(&imp_name, imported).unwrap();

    let mut build = cc::Build::new();
    build.no_default_flags(true);
    if build.get_compiler().is_like_msvc() {
        build.flag("/EP").flag("/clang:-undef");
    } else {
        build.flag("-xc").flag("-P").flag("-undef");
    }
    build.file(&imp_name);
    let vs = build.clone()
        .define("WR_VERTEX_SHADER", Some("1"))
        .expand();
    let fs = build.clone()
        .define("WR_FRAGMENT_SHADER", Some("1"))
        .expand();
    let vs_name = format!("{}/{}.vert", out_dir, shader);
    let fs_name = format!("{}/{}.frag", out_dir, shader);
    std::fs::write(&vs_name, vs).unwrap();
    std::fs::write(&fs_name, fs).unwrap();

    let args = vec![
        "glsl_to_cxx".to_string(),
        vs_name,
        fs_name,
    ];
    let result = glsl_to_cxx::translate(&mut args.into_iter());
    std::fs::write(format!("{}/{}.h", out_dir, shader), result).unwrap();
}

fn main() {
    let shader_dir = match std::env::var("MOZ_SRC") {
        Ok(dir) => dir + "/gfx/wr/webrender/res",
        Err(_) => std::env::var("CARGO_MANIFEST_DIR").unwrap() + "/../webrender/res",
    };

    let shader_flags =
        ShaderFeatureFlags::GL |
        ShaderFeatureFlags::DUAL_SOURCE_BLENDING |
        ShaderFeatureFlags::ADVANCED_BLEND_EQUATION |
        ShaderFeatureFlags::DEBUG;
    let mut shaders: Vec<String> = Vec::new();
    for (name, features) in get_shader_features(shader_flags) {
        shaders.extend(features.iter().map(|f| {
            if f.is_empty() { name.to_owned() } else { format!("{} {}", name, f) }
        }));
    }

    shaders.sort();

    for shader in &shaders {
        translate_shader(shader, &shader_dir);
    }

    write_load_shader(&shaders);

    println!("cargo:rerun-if-changed=src/blend.h");
    println!("cargo:rerun-if-changed=src/composite.h");
    println!("cargo:rerun-if-changed=src/gl_defs.h");
    println!("cargo:rerun-if-changed=src/glsl.h");
    println!("cargo:rerun-if-changed=src/program.h");
    println!("cargo:rerun-if-changed=src/rasterize.h");
    println!("cargo:rerun-if-changed=src/swgl_ext.h");
    println!("cargo:rerun-if-changed=src/texture.h");
    println!("cargo:rerun-if-changed=src/vector_type.h");
    println!("cargo:rerun-if-changed=src/gl.cc");
    let mut build = cc::Build::new();
    build.cpp(true);

    // SWGL relies heavily on inlining for performance so override -Oz with -O2
    if build.get_compiler().args().contains(&std::ffi::OsString::from("-Oz")) {
        build.flag("-O2");
    }

    build.file("src/gl.cc")
        .flag("-std=c++17")
        .flag("-UMOZILLA_CONFIG_H")
        .flag("-fno-exceptions")
        .flag("-fno-rtti")
        .flag("-fno-math-errno")
        .define("_GLIBCXX_USE_CXX11_ABI", Some("0"))
        .include(shader_dir)
        .include("src")
        .include(std::env::var("OUT_DIR").unwrap())
        .compile("gl_cc");
}
<|endoftext|>


<commit_before>use std::ascii::AsciiExt;

use std::borrow::Cow::Owned;
use itertools::Itertools;

use liquid;
use liquid::Token::{self, Identifier};
use liquid::lexer::Element::{self, Expression, Tag, Raw};

use syntect::parsing::{SyntaxDefinition, SyntaxSet};
use syntect::highlighting::{ThemeSet, Theme};
use syntect::html::{IncludeBackground, highlighted_snippet_for_string, styles_to_coloured_html,
                    start_coloured_html_snippet};
use syntect::easy::HighlightLines;

use pulldown_cmark as cmark;
use pulldown_cmark::Event::{self, Start, End, Text, Html};

use error;

struct Setup {
    syntax_set: SyntaxSet,
    theme_set: ThemeSet,
}

unsafe impl Send for Setup {}
unsafe impl Sync for Setup {}

lazy_static!{
    static ref SETUP: Setup = Setup {
        syntax_set: SyntaxSet::load_defaults_newlines(),
        theme_set: ThemeSet::load_defaults()
    };
}

pub fn has_syntax_theme(name: &str) -> error::Result<bool> {
    Ok(SETUP.theme_set.themes.contains_key(name))
}

pub fn list_syntax_themes<'a>() -> Vec<&'a String> {
    SETUP.theme_set.themes.keys().collect::<Vec<_>>()
}

pub fn list_syntaxes() -> Vec<String> {
    fn definition_to_string(sd: &SyntaxDefinition) -> String {
        let extensions = sd.file_extensions.iter().join(&", ".to_owned());
        format!("{} [{}]", sd.name, extensions)
    }

    let mut syntaxes = SETUP
        .syntax_set
        .syntaxes()
        .iter()
        .map(definition_to_string)
        .collect::<Vec<_>>();

    // sort alphabetically with insensitive ascii case
    syntaxes.sort_by_key(|a| a.to_ascii_lowercase());

    syntaxes
}

struct CodeBlock {
    lang: Option<String>,
    code: String,
    theme: Theme,
}

impl liquid::Renderable for CodeBlock {
    fn render(&self, _: &mut liquid::Context) -> Result<Option<String>, liquid::Error> {
        let syntax = match self.lang {
            Some(ref lang) => SETUP.syntax_set.find_syntax_by_token(lang),
            _ => None,
        }.unwrap_or_else(|| SETUP.syntax_set.find_syntax_plain_text());

        Ok(Some(highlighted_snippet_for_string(&self.code, syntax, &self.theme)))
    }
}

#[derive(Clone)]
pub struct CodeBlockParser {
    syntax_theme: String,
}

impl CodeBlockParser {
    pub fn new(syntax_theme: String) -> Self {
        Self { syntax_theme }
    }
}

impl liquid::ParseBlock for CodeBlockParser {
    fn parse(&self,
             _tag_name: &str,
             arguments: &[Token],
             tokens: &[Element],
             _options: &liquid::LiquidOptions)
             -> Result<Box<liquid::Renderable>, liquid::Error> {
        let content = tokens.iter().fold("".to_owned(), |a, b| {
            match *b {
                Expression(_, ref text) |
                Tag(_, ref text) |
                Raw(ref text) => text,
            }.to_owned() + &a
        });

        let lang = match arguments.iter().next() {
            Some(&Identifier(ref x)) => Some(x.clone()),
            _ => None,
        };

        Ok(Box::new(CodeBlock {
                        code: content,
                        lang: lang,
                        theme: SETUP.theme_set.themes[&self.syntax_theme].clone(),
                    }))
    }
}

pub struct DecoratedParser<'a> {
    h: Option<HighlightLines<'a>>,
    parser: cmark::Parser<'a>,
    theme: &'a Theme,
}

impl<'a> DecoratedParser<'a> {
    pub fn new(parser: cmark::Parser<'a>, theme: &'a Theme) -> Self {
        DecoratedParser {
            h: None,
            parser: parser,
            theme: theme,
        }
    }
}

impl<'a> Iterator for DecoratedParser<'a> {
    type Item = Event<'a>;

    fn next(&mut self) -> Option<Event<'a>> {
        match self.parser.next() {
            Some(item) => {
                if let Text(text) = item {
                    if let Some(ref mut h) = self.h {
                        let highlighted = &h.highlight(&text);
                        let html = styles_to_coloured_html(highlighted, IncludeBackground::Yes);
                        Some(Html(Owned(html)))
                    } else {
                        Some(Text(text))
                    }
                } else {
                    if let Start(cmark::Tag::CodeBlock(ref info)) = item {
                        // set local highlighter, if found
                        let cur_syntax =
                            info.clone()
                                .split(' ')
                                .next()
                                .and_then(|lang| SETUP.syntax_set.find_syntax_by_token(lang))
                                .unwrap_or_else(|| SETUP.syntax_set.find_syntax_plain_text());
                        self.h = Some(HighlightLines::new(cur_syntax, self.theme));
                        let snippet = start_coloured_html_snippet(self.theme);
                        return Some(Html(Owned(snippet)));
                    }
                    if let End(cmark::Tag::CodeBlock(_)) = item {
                        // reset highlighter
                        self.h = None;
                        // close the code block
                        return Some(Html(Owned("
".to_owned())));
                    }

                    Some(item)
                }
            }
            None => None,
        }
    }
}

pub fn decorate_markdown<'a>(parser: cmark::Parser<'a>, theme_name: &str) -> DecoratedParser<'a> {
    DecoratedParser::new(parser, &SETUP.theme_set.themes[theme_name])
}

#[cfg(test)]
mod test {

    use std::default::Default;
    use liquid::{self, Renderable, LiquidOptions, Context};

    use super::*;

    const CODE_BLOCK: &'static str = "mod test {
        fn hello(arg: int) -> bool {
            \
                                      true
        }
    }
    ";

    const CODEBLOCK_RENDERED: &'static str = "
\nmod test {\n        fn hello(arg: int) -> bool {\n            true\n        \
         }\n    \
         }\n    \
         \n
\n";

    const MARKDOWN_RENDERED: &'static str = "
\nmod test {\n        fn hello(arg: int) -> bool {\n            true\n        }\n    }\n    \n
";

    #[test]
    fn codeblock_renders_rust() {
        // Syntect isn't thread safe, for now run everything in the same test.
        {
            let mut options: LiquidOptions = Default::default();
            options.blocks.insert("codeblock".to_string(),
                                  Box::new(CodeBlockParser::new("base16-ocean.dark".to_owned())));
            let template = liquid::parse(&format!("{{% codeblock rust %}}{}{{% endcodeblock %}}",
                                                  CODE_BLOCK),
                                         options)
                .unwrap();
            let mut data = Context::new();
            let output = template.render(&mut data);
            assert_eq!(output.unwrap(), Some(CODEBLOCK_RENDERED.to_string()));
        }

        {
            let html = format!(
                "```rust
{}
```",
                CODE_BLOCK
            );

            let mut buf = String::new();
            let parser = cmark::Parser::new(&html);
            cmark::html::push_html(&mut buf, decorate_markdown(parser, "base16-ocean.dark"));
            assert_eq!(buf, MARKDOWN_RENDERED);
        }
    }
}
<commit_msg>chore: Quiet beta warning<commit_after>#[allow(unused_imports)]
use std::ascii::AsciiExt;

use std::borrow::Cow::Owned;
use itertools::Itertools;

use liquid;
use liquid::Token::{self, Identifier};
use liquid::lexer::Element::{self, Expression, Tag, Raw};

use syntect::parsing::{SyntaxDefinition, SyntaxSet};
use syntect::highlighting::{ThemeSet, Theme};
use syntect::html::{IncludeBackground, highlighted_snippet_for_string, styles_to_coloured_html,
                    start_coloured_html_snippet};
use syntect::easy::HighlightLines;

use pulldown_cmark as cmark;
use pulldown_cmark::Event::{self, Start, End, Text, Html};

use error;

struct Setup {
    syntax_set: SyntaxSet,
    theme_set: ThemeSet,
}

unsafe impl Send for Setup {}
unsafe impl Sync for Setup {}

lazy_static!{
    static ref SETUP: Setup = Setup {
        syntax_set: SyntaxSet::load_defaults_newlines(),
        theme_set: ThemeSet::load_defaults()
    };
}

pub fn has_syntax_theme(name: &str) -> error::Result<bool> {
    Ok(SETUP.theme_set.themes.contains_key(name))
}

pub fn list_syntax_themes<'a>() -> Vec<&'a String> {
    SETUP.theme_set.themes.keys().collect::<Vec<_>>()
}

pub fn list_syntaxes() -> Vec<String> {
    fn definition_to_string(sd: &SyntaxDefinition) -> String {
        let extensions = sd.file_extensions.iter().join(&", ".to_owned());
        format!("{} [{}]", sd.name, extensions)
    }

    let mut syntaxes = SETUP
        .syntax_set
        .syntaxes()
        .iter()
        .map(definition_to_string)
        .collect::<Vec<_>>();

    // sort alphabetically with insensitive ascii case
    syntaxes.sort_by_key(|a| a.to_ascii_lowercase());

    syntaxes
}

struct CodeBlock {
    lang: Option<String>,
    code: String,
    theme: Theme,
}

impl liquid::Renderable for CodeBlock {
    fn render(&self, _: &mut liquid::Context) -> Result<Option<String>, liquid::Error> {
        let syntax = match self.lang {
            Some(ref lang) => SETUP.syntax_set.find_syntax_by_token(lang),
            _ => None,
        }.unwrap_or_else(|| SETUP.syntax_set.find_syntax_plain_text());

        Ok(Some(highlighted_snippet_for_string(&self.code, syntax, &self.theme)))
    }
}

#[derive(Clone)]
pub struct CodeBlockParser {
    syntax_theme: String,
}

impl CodeBlockParser {
    pub fn new(syntax_theme: String) -> Self {
        Self { syntax_theme }
    }
}

impl liquid::ParseBlock for CodeBlockParser {
    fn parse(&self,
             _tag_name: &str,
             arguments: &[Token],
             tokens: &[Element],
             _options: &liquid::LiquidOptions)
             -> Result<Box<liquid::Renderable>, liquid::Error> {
        let content = tokens.iter().fold("".to_owned(), |a, b| {
            match *b {
                Expression(_, ref text) |
                Tag(_, ref text) |
                Raw(ref text) => text,
            }.to_owned() + &a
        });

        let lang = match arguments.iter().next() {
            Some(&Identifier(ref x)) => Some(x.clone()),
            _ => None,
        };

        Ok(Box::new(CodeBlock {
                        code: content,
                        lang: lang,
                        theme: SETUP.theme_set.themes[&self.syntax_theme].clone(),
                    }))
    }
}

pub struct DecoratedParser<'a> {
    h: Option<HighlightLines<'a>>,
    parser: cmark::Parser<'a>,
    theme: &'a Theme,
}

impl<'a> DecoratedParser<'a> {
    pub fn new(parser: cmark::Parser<'a>, theme: &'a Theme) -> Self {
        DecoratedParser {
            h: None,
            parser: parser,
            theme: theme,
        }
    }
}

impl<'a> Iterator for DecoratedParser<'a> {
    type Item = Event<'a>;

    fn next(&mut self) -> Option<Event<'a>> {
        match self.parser.next() {
            Some(item) => {
                if let Text(text) = item {
                    if let Some(ref mut h) = self.h {
                        let highlighted = &h.highlight(&text);
                        let html = styles_to_coloured_html(highlighted, IncludeBackground::Yes);
                        Some(Html(Owned(html)))
                    } else {
                        Some(Text(text))
                    }
                } else {
                    if let Start(cmark::Tag::CodeBlock(ref info)) = item {
                        // set local highlighter, if found
                        let cur_syntax =
                            info.clone()
                                .split(' ')
                                .next()
                                .and_then(|lang| SETUP.syntax_set.find_syntax_by_token(lang))
                                .unwrap_or_else(|| SETUP.syntax_set.find_syntax_plain_text());
                        self.h = Some(HighlightLines::new(cur_syntax, self.theme));
                        let snippet = start_coloured_html_snippet(self.theme);
                        return Some(Html(Owned(snippet)));
                    }
                    if let End(cmark::Tag::CodeBlock(_)) = item {
                        // reset highlighter
                        self.h = None;
                        // close the code block
                        return Some(Html(Owned("
".to_owned())));
                    }

                    Some(item)
                }
            }
            None => None,
        }
    }
}

pub fn decorate_markdown<'a>(parser: cmark::Parser<'a>, theme_name: &str) -> DecoratedParser<'a> {
    DecoratedParser::new(parser, &SETUP.theme_set.themes[theme_name])
}

#[cfg(test)]
mod test {

    use std::default::Default;
    use liquid::{self, Renderable, LiquidOptions, Context};

    use super::*;

    const CODE_BLOCK: &'static str = "mod test {
        fn hello(arg: int) -> bool {
            \
                                      true
        }
    }
    ";

    const CODEBLOCK_RENDERED: &'static str = "
\nmod test {\n        fn hello(arg: int) -> bool {\n            true\n        \
         }\n    \
         }\n    \
         \n
\n";

    const MARKDOWN_RENDERED: &'static str = "
\nmod test {\n        fn hello(arg: int) -> bool {\n            true\n        }\n    }\n    \n
";

    #[test]
    fn codeblock_renders_rust() {
        // Syntect isn't thread safe, for now run everything in the same test.
        {
            let mut options: LiquidOptions = Default::default();
            options.blocks.insert("codeblock".to_string(),
                                  Box::new(CodeBlockParser::new("base16-ocean.dark".to_owned())));
            let template = liquid::parse(&format!("{{% codeblock rust %}}{}{{% endcodeblock %}}",
                                                  CODE_BLOCK),
                                         options)
                .unwrap();
            let mut data = Context::new();
            let output = template.render(&mut data);
            assert_eq!(output.unwrap(), Some(CODEBLOCK_RENDERED.to_string()));
        }

        {
            let html = format!(
                "```rust
{}
```",
                CODE_BLOCK
            );

            let mut buf = String::new();
            let parser = cmark::Parser::new(&html);
            cmark::html::push_html(&mut buf, decorate_markdown(parser, "base16-ocean.dark"));
            assert_eq!(buf, MARKDOWN_RENDERED);
        }
    }
}
<|endoftext|>


<commit_before>//! Axis Aligned Bounding Boxes.

use nalgebra::{Point3, Vector3};
use std::f32;
use std::ops::Index;

/// AABB struct.
#[derive(Debug, Copy, Clone)]
pub struct AABB {
    // Minimum coordinates
    pub min: Point3<f32>,

    // Maximum coordinates
    pub max: Point3<f32>,
}

/// A trait implemented by things which can be bounded by an `AABB`.
pub trait Bounded {
    fn aabb(&self) -> AABB;
}

impl AABB {
    /// Creates a new `AABB` with the given bounds.
    pub fn new(min: Point3<f32>, max: Point3<f32>) -> AABB {
        AABB {
            min: min,
            max: max,
        }
    }

    /// Creates a new empty `AABB`.
    pub fn empty() -> AABB {
        AABB {
            min: Point3::new(f32::INFINITY, f32::INFINITY, f32::INFINITY),
            max: Point3::new(f32::NEG_INFINITY, f32::NEG_INFINITY, f32::NEG_INFINITY),
        }
    }

    /// Returns true if the `Point3` is inside the `AABB`.
    pub fn contains(&self, p: &Point3<f32>) -> bool {
        p.x >= self.min.x && p.x <= self.max.x && p.y >= self.min.y && p.y <= self.max.y &&
        p.z >= self.min.z && p.z <= self.max.z
    }

    /// Returns true if the `Point3` is approximately inside the `AABB`
    /// with respect to some `epsilon`.
    pub fn approx_contains_eps(&self, p: &Point3<f32>, epsilon: f32) -> bool {
        (p.x - self.min.x) > -epsilon && (p.x - self.max.x) < epsilon &&
        (p.y - self.min.y) > -epsilon && (p.y - self.max.y) < epsilon &&
        (p.z - self.min.z) > -epsilon && (p.z - self.max.z) < epsilon
    }

    /// Returns a new minimal `AABB` which contains both this `AABB` and `other`.
    pub fn union_aabb(&self, other: &AABB) -> AABB {
        AABB::new(Point3::new(self.min.x.min(other.min.x),
                              self.min.y.min(other.min.y),
                              self.min.z.min(other.min.z)),
                  Point3::new(self.max.x.max(other.max.x),
                              self.max.y.max(other.max.y),
                              self.max.z.max(other.max.z)))
    }

    /// Returns a new minimal `AABB` which contains both this `AABB` and `other`.
    pub fn union_point(&self, other: &Point3<f32>) -> AABB {
        AABB::new(Point3::new(self.min.x.min(other.x),
                              self.min.y.min(other.y),
                              self.min.z.min(other.z)),
                  Point3::new(self.max.x.max(other.x),
                              self.max.y.max(other.y),
                              self.max.z.max(other.z)))
    }

    /// Returns a new minimal `AABB` which contains both this `AABB` and `other`.
    pub fn union_bounded<T: Bounded>(&self, other: &T) -> AABB {
        self.union_aabb(&other.aabb())
    }

    /// Returns the size of this `AABB` in all three dimensions.
    pub fn size(&self) -> Vector3<f32> {
        self.max - self.min
    }

    /// Returns the center point of the `AABB`.
    pub fn center(&self) -> Point3<f32> {
        self.min + (self.size() / 2.0)
    }

    /// Returns the total surface area of this `AABB`.
    pub fn surface_area(&self) -> f32 {
        let size = self.size();
        2.0 * size.x * size.y + size.x * size.z + size.y * size.z
    }
}

/// Make `AABB`s indexable. `aabb[0]` gives a reference to the minimum bound.
/// All other indices return a reference to the maximum bound.
impl Index<usize> for AABB {
    type Output = Point3<f32>;

    fn index(&self, index: usize) -> &Point3<f32> {
        if index == 0 { &self.min } else { &self.max }
    }
}

/// Implementation of `Bounded` for single points.
impl Bounded for Point3<f32> {
    fn aabb(&self) -> AABB {
        AABB::new(*self, *self)
    }
}

#[cfg(test)]
mod tests {
    use aabb::AABB;
    use nalgebra::Point3;

    type TupleVec = (f32, f32, f32);

    fn tuple_to_point(tpl: &TupleVec) -> Point3<f32> {
        Point3::new(tpl.0, tpl.1, tpl.2)
    }

    /// Test whether an empty `AABB` does not contains anything.
    quickcheck!{
        fn test_empty_contains_nothing(tpl: TupleVec) -> bool {
            let p = tuple_to_point(&tpl);
            let aabb = AABB::empty();
            !aabb.contains(&p)
        }
    }

    /// Test whether an AABB always contains its center.
    quickcheck!{
        fn test_aabb_contains_center(a: TupleVec, b: TupleVec) -> bool {
            let p1 = tuple_to_point(&a);
            let p2 = tuple_to_point(&b);
            let aabb = AABB::empty().union_point(&p1).union_bounded(&p2);
            aabb.contains(&aabb.center())
        }
    }

    /// Test whether the union of two point-sets contains all the points.
    quickcheck!{
        fn test_join_two_aabbs(a: (TupleVec, TupleVec, TupleVec, TupleVec, TupleVec),
                               b: (TupleVec, TupleVec, TupleVec, TupleVec, TupleVec))
                               -> bool {
            let points = [a.0, a.1, a.2, a.3, a.4, b.0, b.1, b.2, b.3, b.4];
            let points = points.iter().map(tuple_to_point).collect::<Vec<Point3<f32>>>();
            let aabb1 =
                points.iter().take(5).fold(AABB::empty(), |aabb, point| aabb.union_point(&point));
            let aabb2 =
                points.iter().skip(5).fold(AABB::empty(), |aabb, point| aabb.union_point(&point));

            let aabb1_contains_init_five = points.iter()
                .take(5)
                .fold(true, |b, point| b && aabb1.contains(&point));
            let aabb2_contains_last_five = points.iter()
                .skip(5)
                .fold(true, |b, point| b && aabb2.contains(&point));

            let aabbu = aabb1.union_aabb(&aabb2);
            let aabbu_contains_all = points.iter()
                .fold(true, |b, point| b && aabbu.contains(&point));

            aabb1_contains_init_five && aabb2_contains_last_five && aabbu_contains_all
        }
    }
}
<commit_msg>Add comments, fixes and an AABB::volume function<commit_after>//! Axis Aligned Bounding Boxes.

use nalgebra::{Point3, Vector3};
use std::f32;
use std::ops::Index;

/// AABB struct.
#[derive(Debug, Copy, Clone)]
pub struct AABB {
    // Minimum coordinates
    pub min: Point3<f32>,

    // Maximum coordinates
    pub max: Point3<f32>,
}

/// A trait implemented by things which can be bounded by an `AABB`.
pub trait Bounded {
    fn aabb(&self) -> AABB;
}

impl AABB {
    /// Creates a new `AABB` with the given bounds.
    pub fn new(min: Point3<f32>, max: Point3<f32>) -> AABB {
        AABB {
            min: min,
            max: max,
        }
    }

    /// Creates a new empty `AABB`.
    pub fn empty() -> AABB {
        AABB {
            min: Point3::new(f32::INFINITY, f32::INFINITY, f32::INFINITY),
            max: Point3::new(f32::NEG_INFINITY, f32::NEG_INFINITY, f32::NEG_INFINITY),
        }
    }

    /// Returns true if the `Point3` is inside the `AABB`.
    pub fn contains(&self, p: &Point3<f32>) -> bool {
        p.x >= self.min.x && p.x <= self.max.x && p.y >= self.min.y && p.y <= self.max.y &&
        p.z >= self.min.z && p.z <= self.max.z
    }

    /// Returns true if the `Point3` is approximately inside the `AABB`
    /// with respect to some `epsilon`.
    pub fn approx_contains_eps(&self, p: &Point3<f32>, epsilon: f32) -> bool {
        (p.x - self.min.x) > -epsilon && (p.x - self.max.x) < epsilon &&
        (p.y - self.min.y) > -epsilon && (p.y - self.max.y) < epsilon &&
        (p.z - self.min.z) > -epsilon && (p.z - self.max.z) < epsilon
    }

    /// Returns a new minimal `AABB` which contains both this `AABB` and `other`.
    pub fn union_aabb(&self, other: &AABB) -> AABB {
        AABB::new(Point3::new(self.min.x.min(other.min.x),
                              self.min.y.min(other.min.y),
                              self.min.z.min(other.min.z)),
                  Point3::new(self.max.x.max(other.max.x),
                              self.max.y.max(other.max.y),
                              self.max.z.max(other.max.z)))
    }

    /// Returns a new minimal `AABB` which contains both this `AABB` and `other`.
    pub fn union_point(&self, other: &Point3<f32>) -> AABB {
        AABB::new(Point3::new(self.min.x.min(other.x),
                              self.min.y.min(other.y),
                              self.min.z.min(other.z)),
                  Point3::new(self.max.x.max(other.x),
                              self.max.y.max(other.y),
                              self.max.z.max(other.z)))
    }

    /// Returns a new minimal `AABB` which contains both this `AABB` and `other`.
    pub fn union_bounded<T: Bounded>(&self, other: &T) -> AABB {
        self.union_aabb(&other.aabb())
    }

    /// Returns the size of this `AABB` in all three dimensions.
    pub fn size(&self) -> Vector3<f32> {
        self.max - self.min
    }

    /// Returns the center point of the `AABB`.
    pub fn center(&self) -> Point3<f32> {
        self.min + (self.size() / 2.0)
    }

    /// Returns the total surface area of this `AABB`.
    pub fn surface_area(&self) -> f32 {
        let size = self.size();
        2.0 * (size.x * size.y + size.x * size.z + size.y * size.z)
    }

    /// Returns the volume of this `AABB`.
    pub fn volume(&self) -> f32 {
        let size = self.size();
        size.x * size.y * size.z
    }
}

/// Make `AABB`s indexable. `aabb[0]` gives a reference to the minimum bound.
/// All other indices return a reference to the maximum bound.
impl Index<usize> for AABB {
    type Output = Point3<f32>;

    fn index(&self, index: usize) -> &Point3<f32> {
        if index == 0 { &self.min } else { &self.max }
    }
}

/// Implementation of `Bounded` for single points.
impl Bounded for Point3<f32> {
    fn aabb(&self) -> AABB {
        AABB::new(*self, *self)
    }
}

#[cfg(test)]
mod tests {
    use aabb::AABB;
    use nalgebra::Point3;

    type TupleVec = (f32, f32, f32);

    fn tuple_to_point(tpl: &TupleVec) -> Point3<f32> {
        Point3::new(tpl.0, tpl.1, tpl.2)
    }

    /// Test whether an empty `AABB` does not contains anything.
    quickcheck!{
        fn test_empty_contains_nothing(tpl: TupleVec) -> bool {
            // Define a random Point
            let p = tuple_to_point(&tpl);

            // Create an empty AABB
            let aabb = AABB::empty();

            // It should not contain anything
            !aabb.contains(&p)
        }
    }

    /// Test whether an AABB always contains its center.
    quickcheck!{
        fn test_aabb_contains_center(a: TupleVec, b: TupleVec) -> bool {
            // Define two points which will be the corners of the `AABB`
            let p1 = tuple_to_point(&a);
            let p2 = tuple_to_point(&b);

            // Span the `AABB`
            let aabb = AABB::empty().union_point(&p1).union_bounded(&p2);

            // Its center should be inside the `AABB`
            aabb.contains(&aabb.center())
        }
    }

    /// Test whether the union of two point-sets contains all the points.
    quickcheck!{
        fn test_join_two_aabbs(a: (TupleVec, TupleVec, TupleVec, TupleVec, TupleVec),
                               b: (TupleVec, TupleVec, TupleVec, TupleVec, TupleVec))
                               -> bool {
            // Define an array of ten points
            let points = [a.0, a.1, a.2, a.3, a.4, b.0, b.1, b.2, b.3, b.4];

            // Convert these points to `Point3`
            let points = points.iter().map(tuple_to_point).collect::<Vec<Point3<f32>>>();

            // Create two `AABB`s. One spanned the first five points,
            // the other by the last five points
            let aabb1 =
                points.iter().take(5).fold(AABB::empty(), |aabb, point| aabb.union_point(&point));
            let aabb2 =
                points.iter().skip(5).fold(AABB::empty(), |aabb, point| aabb.union_point(&point));

            // The `AABB`s should contain the points by which they are spanned
            let aabb1_contains_init_five = points.iter()
                .take(5)
                .fold(true, |b, point| b && aabb1.contains(&point));
            let aabb2_contains_last_five = points.iter()
                .skip(5)
                .fold(true, |b, point| b && aabb2.contains(&point));

            // Build the union of the two `AABB`s
            let aabbu = aabb1.union_aabb(&aabb2);

            // The union should contain all points
            let aabbu_contains_all = points.iter()
                .fold(true, |b, point| b && aabbu.contains(&point));

            // Return the three properties
            aabb1_contains_init_five && aabb2_contains_last_five && aabbu_contains_all
        }
    }
}
<|endoftext|>