<commit_before>use geometry::bbox::BBox;
use geometry::prim::Prim;
use material::Material;
use raytracer::{Ray, Intersection};
use vec3::Vec3;

#[cfg(test)]
use material::materials::FlatMaterial;

#[allow(dead_code)]
pub struct Sphere {
    pub center: Vec3,
    pub radius: f64,
    pub material: Box<Material+Send+Share>
}

impl Prim for Sphere {
    fn intersects<'a>(&'a self, ray: &Ray, t_min: f64, t_max: f64) -> Option<Intersection<'a>> {
        let i = ray.origin - self.center;
        let a = 1.0;
        let b = 2.0 * ray.direction.dot(&i);
        let c = i.dot(&i) - self.radius * self.radius;
        let discriminant = b * b - 4.0 * a * c;

        if discriminant <= 0.0 {
            None
        } else {
            // Up to two intersections
            let disc_sqrt = discriminant.sqrt();
            let t1 = (-b + disc_sqrt) / 2.0 * a;
            let t2 = (-b - disc_sqrt) / 2.0 * a;

            if t1 >= t_min && t1 <= t_max ||
               t2 >= t_min && t2 <= t_max {
                // Valid intersection(s): get nearer intersection
                let t = if t1.abs() < t2.abs() { t1 } else { t2 };
                let intersection_point = ray.origin + ray.direction.scale(t);
                let n = (intersection_point - self.center).unit();

                let u = 0.5 + n.z.atan2(n.x) / ::std::f64::consts::PI_2;
                let v = 0.5 - n.y.asin() / ::std::f64::consts::PI;

                Some(Intersection {
                    n: n,
                    t: t,
                    u: u,
                    v: v,
                    position: intersection_point,
                    material: &self.material
                })
            } else {
                None
            }
        }
    }

    fn bounding(&self) -> Option<BBox> {
        Some(BBox {
            min: Vec3 {
                x: self.center.x - self.radius,
                y: self.center.y - self.radius,
                z: self.center.z - self.radius
            },
            max: Vec3 {
                x: self.center.x + self.radius,
                y: self.center.y + self.radius,
                z: self.center.z + self.radius
            }
        })
    }
}

#[test]
fn it_intersects_and_interpolates() {
    let sphere = Sphere {
        center: Vec3::zero(),
        radius: 1.0,
        material: box FlatMaterial {color: Vec3::one()}
    };

    // Tests actual intersection
    let intersecting_ray = Ray::new(Vec3 {x: 0.0, y: 0.0, z: -2.0}, Vec3 {x: 0.0, y: 0.0, z: 1.0});
    let intersection = sphere.intersects(&intersecting_ray, 0.0, 10.0).unwrap();
    assert_eq!(intersection.position.x, 0.0);
    assert_eq!(intersection.position.y, 0.0);
    assert_eq!(intersection.position.z, -1.0);
    assert_eq!(intersection.n.x, 0.0);
    assert_eq!(intersection.n.y, 0.0);
    assert_eq!(intersection.n.z, -1.0);

    // Ray off to the sides
    let mut non_intersecting_ray = Ray::new(Vec3 {x: 0.0, y: 0.0, z: -2.0}, Vec3 {x: 100.0, y: 100.0, z: 0.1});
    let mut non_intersection = sphere.intersects(&non_intersecting_ray, 0.0, 10.0);
    assert!(non_intersection.is_none());

    non_intersecting_ray = Ray::new(Vec3 {x: 0.0, y: 0.0, z: -2.0}, Vec3 {x: -100.0, y: -100.0, z: 0.1});
    non_intersection = sphere.intersects(&non_intersecting_ray, 0.0, 10.0);
    assert!(non_intersection.is_none());

    // Ray in opposite direction
    non_intersecting_ray = Ray::new(Vec3 {x: 0.0, y: 0.0, z: -2.0}, Vec3 {x: 0.0, y: 0.0, z: -1.0});
    non_intersection = sphere.intersects(&non_intersecting_ray, 0.0, 10.0);
    assert!(non_intersection.is_none());
}

#[test]
fn it_intersects_only_in_tmin_tmax() {
    let sphere = Sphere {
        center: Vec3::zero(),
        radius: 1.0,
        material: box FlatMaterial {color: Vec3::one()}
    };

    // Tests tmin
    let mut intersecting_ray = Ray::new(Vec3 {x: 0.0, y: 0.0, z: -2.0}, Vec3 {x: 0.0, y: 0.0, z: 1.0});
    let mut non_intersection = sphere.intersects(&intersecting_ray, 1000.0, 10000.0);
    assert!(non_intersection.is_none());

    // Tests tmax
    non_intersection = sphere.intersects(&intersecting_ray, 0.0, 0.0001);
    assert!(non_intersection.is_none());
}
<commit_msg>Minor Sphere test cleanup<commit_after>use geometry::bbox::BBox;
use geometry::prim::Prim;
use material::Material;
use raytracer::{Ray, Intersection};
use vec3::Vec3;

#[cfg(test)]
use material::materials::FlatMaterial;

#[allow(dead_code)]
pub struct Sphere {
    pub center: Vec3,
    pub radius: f64,
    pub material: Box<Material+Send+Share>
}

impl Prim for Sphere {
    fn intersects<'a>(&'a self, ray: &Ray, t_min: f64, t_max: f64) -> Option<Intersection<'a>> {
        let i = ray.origin - self.center;
        let a = 1.0;
        let b = 2.0 * ray.direction.dot(&i);
        let c = i.dot(&i) - self.radius * self.radius;
        let discriminant = b * b - 4.0 * a * c;

        if discriminant <= 0.0 {
            None
        } else {
            // Up to two intersections
            let disc_sqrt = discriminant.sqrt();
            let t1 = (-b + disc_sqrt) / 2.0 * a;
            let t2 = (-b - disc_sqrt) / 2.0 * a;

            if t1 >= t_min && t1 <= t_max ||
               t2 >= t_min && t2 <= t_max {
                // Valid intersection(s): get nearer intersection
                let t = if t1.abs() < t2.abs() { t1 } else { t2 };
                let intersection_point = ray.origin + ray.direction.scale(t);
                let n = (intersection_point - self.center).unit();

                let u = 0.5 + n.z.atan2(n.x) / ::std::f64::consts::PI_2;
                let v = 0.5 - n.y.asin() / ::std::f64::consts::PI;

                Some(Intersection {
                    n: n,
                    t: t,
                    u: u,
                    v: v,
                    position: intersection_point,
                    material: &self.material
                })
            } else {
                None
            }
        }
    }

    fn bounding(&self) -> Option<BBox> {
        Some(BBox {
            min: Vec3 {
                x: self.center.x - self.radius,
                y: self.center.y - self.radius,
                z: self.center.z - self.radius
            },
            max: Vec3 {
                x: self.center.x + self.radius,
                y: self.center.y + self.radius,
                z: self.center.z + self.radius
            }
        })
    }
}

#[test]
fn it_intersects() {
    let sphere = Sphere {
        center: Vec3::zero(),
        radius: 1.0,
        material: box FlatMaterial {color: Vec3::one()}
    };

    // Tests actual intersection
    let intersecting_ray = Ray::new(Vec3 {x: 0.0, y: 0.0, z: -2.0}, Vec3 {x: 0.0, y: 0.0, z: 1.0});
    let intersection = sphere.intersects(&intersecting_ray, 0.0, 10.0).unwrap();
    assert_eq!(intersection.position.x, 0.0);
    assert_eq!(intersection.position.y, 0.0);
    assert_eq!(intersection.position.z, -1.0);
    assert_eq!(intersection.n.x, 0.0);
    assert_eq!(intersection.n.y, 0.0);
    assert_eq!(intersection.n.z, -1.0);

    // Ray off to the sides
    let mut non_intersecting_ray = Ray::new(Vec3 {x: 0.0, y: 0.0, z: -2.0}, Vec3 {x: 100.0, y: 100.0, z: 0.1});
    let mut non_intersection = sphere.intersects(&non_intersecting_ray, 0.0, 10.0);
    assert!(non_intersection.is_none());

    non_intersecting_ray = Ray::new(Vec3 {x: 0.0, y: 0.0, z: -2.0}, Vec3 {x: -100.0, y: -100.0, z: 0.1});
    non_intersection = sphere.intersects(&non_intersecting_ray, 0.0, 10.0);
    assert!(non_intersection.is_none());

    // Ray in opposite direction
    non_intersecting_ray = Ray::new(Vec3 {x: 0.0, y: 0.0, z: -2.0}, Vec3 {x: 0.0, y: 0.0, z: -1.0});
    non_intersection = sphere.intersects(&non_intersecting_ray, 0.0, 10.0);
    assert!(non_intersection.is_none());
}

#[test]
fn it_intersects_only_in_tmin_tmax() {
    let sphere = Sphere {
        center: Vec3::zero(),
        radius: 1.0,
        material: box FlatMaterial {color: Vec3::one()}
    };

    // Tests tmin
    let intersecting_ray = Ray::new(Vec3 {x: 0.0, y: 0.0, z: -2.0}, Vec3 {x: 0.0, y: 0.0, z: 1.0});
    let mut non_intersection = sphere.intersects(&intersecting_ray, 1000.0, 10000.0);
    assert!(non_intersection.is_none());

    // Tests tmax
    non_intersection = sphere.intersects(&intersecting_ray, 0.0, 0.0001);
    assert!(non_intersection.is_none());
}
<|endoftext|>


<commit_before>use types::*;
use types::flags::*;
use types::generation::*;
use types::parsing::*;

use cookie_factory::GenError;

pub const NAME:          &'static str   = "{{protocol.name}}";
pub const MAJOR_VERSION: ShortShortUInt = {{protocol.major_version}};
pub const MINOR_VERSION: ShortShortUInt = {{protocol.minor_version}};
pub const REVISION:      ShortShortUInt = {{protocol.revision}};
pub const PORT:          LongUInt       = {{protocol.port}};
pub const COPYRIGHT:     &'static str   = r#"{{copyright}}"#;

{{#each protocol.constants as |constant| ~}}
pub const {{sanitize_name constant.name}}: {{constant.type}} = {{constant.value}};
{{/each ~}}

pub enum AMQPError {
    Soft(AMQPSoftError),
    Hard(AMQPHardError),
}

impl AMQPError {
    pub fn get_id(&self) -> ShortUInt {
        match *self {
            AMQPError::Soft(ref s) => s.get_id(),
            AMQPError::Hard(ref h) => h.get_id(),
        }
    }

    pub fn from_id(id: ShortUInt) -> Option<AMQPError> {
        AMQPSoftError::from_id(id).map(|e| AMQPError::Soft(e)).or_else(|| AMQPHardError::from_id(id).map(|e| AMQPError::Hard(e)))
    }
}

pub enum AMQPSoftError {
    {{#each protocol.soft_errors as |constant| ~}}
    {{camel constant.name}},
    {{/each ~}}
}

impl AMQPSoftError {
    pub fn get_id(&self) -> ShortUInt {
        match *self {
            {{#each protocol.soft_errors as |constant| ~}}
            AMQPSoftError::{{camel constant.name}} => {{constant.value}},
            {{/each ~}}
        }
    }

    pub fn from_id(id: ShortUInt) -> Option<AMQPSoftError> {
        match id {
            {{#each protocol.soft_errors as |constant| ~}}
            {{constant.value}} => Some(AMQPSoftError::{{camel constant.name}}),
            {{/each ~}}
            _                  => None,
        }
    }
}

pub enum AMQPHardError {
    {{#each protocol.hard_errors as |constant| ~}}
    {{camel constant.name}},
    {{/each ~}}
}

impl AMQPHardError {
    pub fn get_id(&self) -> ShortUInt {
        match *self {
            {{#each protocol.hard_errors as |constant| ~}}
            AMQPHardError::{{camel constant.name}} => {{constant.value}},
            {{/each ~}}
        }
    }

    pub fn from_id(id: ShortUInt) -> Option<AMQPHardError> {
        match id {
            {{#each protocol.hard_errors as |constant| ~}}
            {{constant.value}} => Some(AMQPHardError::{{camel constant.name}}),
            {{/each ~}}
            _                  => None,
        }
    }
}

{{#each protocol.classes as |class| ~}}
use self::{{snake class.name}}::parse_{{snake class.name}};
{{/each ~}}

named!(pub parse_class<AMQPClass>, switch!(parse_short_uint,
    {{#each protocol.classes as |class| ~}}
    {{class.id}} => map!(call!(parse_{{snake class.name}}), AMQPClass::{{camel class.name}}) {{#unless @last ~}}|{{/unless ~}}
    {{/each ~}}
));

pub fn gen_class<'a>(input: (&'a mut [u8], usize), class: &AMQPClass) -> Result<(&'a mut [u8], usize), GenError> {
    match *class {
        {{#each protocol.classes as |class| ~}}
        AMQPClass::{{camel class.name}}(ref {{snake class.name}}) => {{snake class.name}}::gen_{{snake class.name}}(input, {{snake class.name}}),
        {{/each ~}}
    }
}

#[derive(Clone, Debug, PartialEq)]
pub enum AMQPClass {
    {{#each protocol.classes as |class| ~}}
    {{camel class.name}}({{snake class.name}}::Method),
    {{/each ~}}
}

{{#each protocol.classes as |class|}}
pub mod {{snake class.name}} {
    use super::*;

    named!(pub parse_{{snake class.name}}<{{snake class.name}}::Method>, switch!(parse_short_uint,
        {{#each class.methods as |method| ~}}
        {{method.id}} => map!(call!(parse_{{snake method.name}}), Method::{{camel method.name}}) {{#unless @last ~}}|{{/unless ~}}
        {{/each ~}}
    ));

    pub fn gen_{{snake class.name}}<'a>(input: (&'a mut [u8], usize), method: &Method) -> Result<(&'a mut [u8], usize), GenError> {
        match *method {
            {{#each class.methods as |method| ~}}
            Method::{{camel method.name}}(ref {{snake method.name}}) => {
                do_gen!(input,
                    gen_short_uint(&{{class.id}}) >>
                    gen_{{snake method.name}}({{snake method.name}})
                )
            },
            {{/each ~}}
        }
    }

    #[derive(Clone, Debug, PartialEq)]
    pub enum Method {
        {{#each class.methods as |method| ~}}
        {{camel method.name}}({{camel method.name}}),
        {{/each ~}}
    }

    {{#each class.methods as |method|}}
    #[derive(Clone, Debug, PartialEq)]
    pub struct {{camel method.name}} {
        {{#each_argument method.arguments as |argument| ~}}
        {{#if argument_is_value ~}}
        pub {{snake argument.name}}: {{argument.type}},
        {{else}}
        {{#each_flag argument as |flag| ~}}
        pub {{snake flag.name}}: Boolean,
        {{/each_flag ~}}
        {{/if ~}}
        {{/each_argument ~}}
    }

    named!(pub parse_{{snake method.name}}<{{camel method.name}}>, do_parse!(
        {{#each_argument method.arguments as |argument| ~}}
        {{#if argument_is_value ~}}
        {{snake argument.name}}: parse_{{snake_type argument.type}} >>
        {{else}}
        /* FIXME: support multiple flags structs? */
        flags: apply!(parse_flags, &vec![
            {{#each_flag argument as |flag| ~}}
            "{{flag.name}}",
            {{/each_flag ~}}
        ]) >>
        {{/if ~}}
        {{/each_argument ~}}
        ({{camel method.name}} {
            {{#each_argument method.arguments as |argument| ~}}
            {{#if argument_is_value ~}}
            {{snake argument.name}}: {{snake argument.name}},
            {{else}}
            {{#each_flag argument as |flag| ~}}
            {{snake flag.name}}: flags.get_flag("{{snake flag.name}}").unwrap_or({{flag.default_value}}),
            {{/each_flag ~}}
            {{/if ~}}
            {{/each_argument ~}}
        })
    ));

    pub fn gen_{{snake method.name}}<'a>(input: (&'a mut [u8], usize), {{#if method.has_arguments ~}}method{{else}}_{{/if ~}}: &{{camel method.name}}) -> Result<(&'a mut [u8],usize), GenError> {
        {{#if method.has_flags ~}}
        /* FIXME: support multiple flags structs? */
        let mut flags = AMQPFlags::new();
        {{#each_argument method.arguments as |argument| ~}}
        {{#unless argument_is_value ~}}
        {{#each_flag argument as |flag| ~}}
        flags.add_flag("{{snake flag.name}}".to_string(), method.{{snake flag.name}});
        {{/each_flag ~}}
        {{/unless ~}}
        {{/each_argument ~}}
        {{/if ~}}
        do_gen!(input,
            gen_short_uint(&{{method.id}})
            {{#each_argument method.arguments as |argument| ~}}
            {{#if argument_is_value ~}}
            >> gen_{{snake_type argument.type}}(&method.{{snake argument.name}})
            {{else}}
            >> gen_flags(&flags)
            {{/if ~}}
            {{/each_argument ~}}
        )
    }
    {{/each ~}}
}
{{/each ~}}
<commit_msg>protocol: rename {,AMQP}Method<commit_after>use types::*;
use types::flags::*;
use types::generation::*;
use types::parsing::*;

use cookie_factory::GenError;

pub const NAME:          &'static str   = "{{protocol.name}}";
pub const MAJOR_VERSION: ShortShortUInt = {{protocol.major_version}};
pub const MINOR_VERSION: ShortShortUInt = {{protocol.minor_version}};
pub const REVISION:      ShortShortUInt = {{protocol.revision}};
pub const PORT:          LongUInt       = {{protocol.port}};
pub const COPYRIGHT:     &'static str   = r#"{{copyright}}"#;

{{#each protocol.constants as |constant| ~}}
pub const {{sanitize_name constant.name}}: {{constant.type}} = {{constant.value}};
{{/each ~}}

pub enum AMQPError {
    Soft(AMQPSoftError),
    Hard(AMQPHardError),
}

impl AMQPError {
    pub fn get_id(&self) -> ShortUInt {
        match *self {
            AMQPError::Soft(ref s) => s.get_id(),
            AMQPError::Hard(ref h) => h.get_id(),
        }
    }

    pub fn from_id(id: ShortUInt) -> Option<AMQPError> {
        AMQPSoftError::from_id(id).map(|e| AMQPError::Soft(e)).or_else(|| AMQPHardError::from_id(id).map(|e| AMQPError::Hard(e)))
    }
}

pub enum AMQPSoftError {
    {{#each protocol.soft_errors as |constant| ~}}
    {{camel constant.name}},
    {{/each ~}}
}

impl AMQPSoftError {
    pub fn get_id(&self) -> ShortUInt {
        match *self {
            {{#each protocol.soft_errors as |constant| ~}}
            AMQPSoftError::{{camel constant.name}} => {{constant.value}},
            {{/each ~}}
        }
    }

    pub fn from_id(id: ShortUInt) -> Option<AMQPSoftError> {
        match id {
            {{#each protocol.soft_errors as |constant| ~}}
            {{constant.value}} => Some(AMQPSoftError::{{camel constant.name}}),
            {{/each ~}}
            _                  => None,
        }
    }
}

pub enum AMQPHardError {
    {{#each protocol.hard_errors as |constant| ~}}
    {{camel constant.name}},
    {{/each ~}}
}

impl AMQPHardError {
    pub fn get_id(&self) -> ShortUInt {
        match *self {
            {{#each protocol.hard_errors as |constant| ~}}
            AMQPHardError::{{camel constant.name}} => {{constant.value}},
            {{/each ~}}
        }
    }

    pub fn from_id(id: ShortUInt) -> Option<AMQPHardError> {
        match id {
            {{#each protocol.hard_errors as |constant| ~}}
            {{constant.value}} => Some(AMQPHardError::{{camel constant.name}}),
            {{/each ~}}
            _                  => None,
        }
    }
}

{{#each protocol.classes as |class| ~}}
use self::{{snake class.name}}::parse_{{snake class.name}};
{{/each ~}}

named!(pub parse_class<AMQPClass>, switch!(parse_short_uint,
    {{#each protocol.classes as |class| ~}}
    {{class.id}} => map!(call!(parse_{{snake class.name}}), AMQPClass::{{camel class.name}}) {{#unless @last ~}}|{{/unless ~}}
    {{/each ~}}
));

pub fn gen_class<'a>(input: (&'a mut [u8], usize), class: &AMQPClass) -> Result<(&'a mut [u8], usize), GenError> {
    match *class {
        {{#each protocol.classes as |class| ~}}
        AMQPClass::{{camel class.name}}(ref {{snake class.name}}) => {{snake class.name}}::gen_{{snake class.name}}(input, {{snake class.name}}),
        {{/each ~}}
    }
}

#[derive(Clone, Debug, PartialEq)]
pub enum AMQPClass {
    {{#each protocol.classes as |class| ~}}
    {{camel class.name}}({{snake class.name}}::AMQPMethod),
    {{/each ~}}
}

{{#each protocol.classes as |class|}}
pub mod {{snake class.name}} {
    use super::*;

    named!(pub parse_{{snake class.name}}<{{snake class.name}}::AMQPMethod>, switch!(parse_short_uint,
        {{#each class.methods as |method| ~}}
        {{method.id}} => map!(call!(parse_{{snake method.name}}), AMQPMethod::{{camel method.name}}) {{#unless @last ~}}|{{/unless ~}}
        {{/each ~}}
    ));

    pub fn gen_{{snake class.name}}<'a>(input: (&'a mut [u8], usize), method: &AMQPMethod) -> Result<(&'a mut [u8], usize), GenError> {
        match *method {
            {{#each class.methods as |method| ~}}
            AMQPMethod::{{camel method.name}}(ref {{snake method.name}}) => {
                do_gen!(input,
                    gen_short_uint(&{{class.id}}) >>
                    gen_{{snake method.name}}({{snake method.name}})
                )
            },
            {{/each ~}}
        }
    }

    #[derive(Clone, Debug, PartialEq)]
    pub enum AMQPMethod {
        {{#each class.methods as |method| ~}}
        {{camel method.name}}({{camel method.name}}),
        {{/each ~}}
    }

    {{#each class.methods as |method|}}
    #[derive(Clone, Debug, PartialEq)]
    pub struct {{camel method.name}} {
        {{#each_argument method.arguments as |argument| ~}}
        {{#if argument_is_value ~}}
        pub {{snake argument.name}}: {{argument.type}},
        {{else}}
        {{#each_flag argument as |flag| ~}}
        pub {{snake flag.name}}: Boolean,
        {{/each_flag ~}}
        {{/if ~}}
        {{/each_argument ~}}
    }

    named!(pub parse_{{snake method.name}}<{{camel method.name}}>, do_parse!(
        {{#each_argument method.arguments as |argument| ~}}
        {{#if argument_is_value ~}}
        {{snake argument.name}}: parse_{{snake_type argument.type}} >>
        {{else}}
        /* FIXME: support multiple flags structs? */
        flags: apply!(parse_flags, &vec![
            {{#each_flag argument as |flag| ~}}
            "{{flag.name}}",
            {{/each_flag ~}}
        ]) >>
        {{/if ~}}
        {{/each_argument ~}}
        ({{camel method.name}} {
            {{#each_argument method.arguments as |argument| ~}}
            {{#if argument_is_value ~}}
            {{snake argument.name}}: {{snake argument.name}},
            {{else}}
            {{#each_flag argument as |flag| ~}}
            {{snake flag.name}}: flags.get_flag("{{snake flag.name}}").unwrap_or({{flag.default_value}}),
            {{/each_flag ~}}
            {{/if ~}}
            {{/each_argument ~}}
        })
    ));

    pub fn gen_{{snake method.name}}<'a>(input: (&'a mut [u8], usize), {{#if method.has_arguments ~}}method{{else}}_{{/if ~}}: &{{camel method.name}}) -> Result<(&'a mut [u8],usize), GenError> {
        {{#if method.has_flags ~}}
        /* FIXME: support multiple flags structs? */
        let mut flags = AMQPFlags::new();
        {{#each_argument method.arguments as |argument| ~}}
        {{#unless argument_is_value ~}}
        {{#each_flag argument as |flag| ~}}
        flags.add_flag("{{snake flag.name}}".to_string(), method.{{snake flag.name}});
        {{/each_flag ~}}
        {{/unless ~}}
        {{/each_argument ~}}
        {{/if ~}}
        do_gen!(input,
            gen_short_uint(&{{method.id}})
            {{#each_argument method.arguments as |argument| ~}}
            {{#if argument_is_value ~}}
            >> gen_{{snake_type argument.type}}(&method.{{snake argument.name}})
            {{else}}
            >> gen_flags(&flags)
            {{/if ~}}
            {{/each_argument ~}}
        )
    }
    {{/each ~}}
}
{{/each ~}}
<|endoftext|>


<commit_before>use std::path::PathBuf;

use serde::{Serialize, Deserialize};

use crate::{Color, Point, Speed, Event, Distance, Size};
use crate::renderer_server::{TurtleId, ExportError};
use crate::radians::Radians;

/// The different kinds of requests that can be sent from a client
///
/// Not all requests warrant a response. The response for each request is listed in its
/// documentation.
///
/// Each request is processed in order and will be held until all of its required resources are
/// available. For example, suppose a request needs access to a particular turtle in order to
/// perform an animation. If another request comes along to clear the image, that request will be
/// held until the animation is complete since it also requires access to that turtle. This
/// behaviour is important to ensure sequential consistency and avoid race conditions as requests
/// run concurrently.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ClientRequest {
    /// Sent initially and on creating any turtle in order to get a `TurtleId`
    ///
    /// Response: `ServerResponse::NewTurtle`
    CreateTurtle,

    /// Export the drawing in its current state to the given path using the given format
    ///
    /// Response: `ServerResponse::ExportComplete`
    Export(PathBuf, ExportFormat),

    /// Get the next event (if any are currently available)
    ///
    /// Response: `ServerResponse::Event`
    PollEvent,

    /// Get the given property of the drawing
    ///
    /// Response: `ServerResponse::DrawingProp`
    DrawingProp(DrawingProp),
    /// Set the given property of the drawing
    ///
    /// Response: N/A
    SetDrawingProp(DrawingPropValue),
    /// Resets the given property of the drawing
    ///
    /// Response: N/A
    ResetDrawingProp(DrawingProp),

    /// Get the given property of a turtle
    ///
    /// Response: `ServerResponse::TurtleProp`
    TurtleProp(TurtleId, TurtleProp),
    /// Set the given property of a turtle
    ///
    /// Response: N/A
    SetTurtleProp(TurtleId, TurtlePropValue),
    /// Resets the given property of a turtle
    ///
    /// Response: N/A
    ResetTurtleProp(TurtleId, TurtleProp),
    /// Resets all properties of a turtle
    ///
    /// Response: N/A
    ResetTurtle(TurtleId),

    /// Move a turtle forward by the given amount
    ///
    /// The turtle moves in the direction of its heading and is animated at its current speed. This
    /// may draw a line if the turtle's pen is down. It may also result in a change to the current
    /// fill if the turtle is currently filling a shape.
    ///
    /// The response to this request provides no additional information, but is necessary to ensure
    /// that animations that should be sequenced, can be sequenced correctly. Without a signal for
    /// when the animation is complete, there would be no way to know when to start the next one.
    ///
    /// Response: `ServerResponse::AnimationComplete`
    MoveForward(TurtleId, Distance),
    /// Move a turtle to the given position
    ///
    /// The turtle movement is animated at its current speed. This may draw a line if the turtle's
    /// pen is down. It may also result in a change to the current fill if the turtle is currently
    /// filling a shape.
    ///
    /// The response to this request provides no additional information, but is necessary to ensure
    /// that animations that should be sequenced, can be sequenced correctly. Without a signal for
    /// when the animation is complete, there would be no way to know when to start the next one.
    ///
    /// Response: `ServerResponse::AnimationComplete`
    MoveTo(TurtleId, Point),
    /// Rotate a turtle in place by the given angle in the given direction
    ///
    /// The turtle rotates with an animation at its current speed. It will not draw any line while
    /// rotating since it has not moved.
    ///
    /// The response to this request provides no additional information, but is necessary to ensure
    /// that animations that should be sequenced, can be sequenced correctly. Without a signal for
    /// when the animation is complete, there would be no way to know when to start the next one.
    ///
    /// Response: `ServerResponse::AnimationComplete`
    RotateInPlace(TurtleId, Radians, RotationDirection),

    /// Creates a fill polygon from a turtle's current position
    ///
    /// The color of the fill will always be consistent with the fill color property of the turtle.
    /// That is, if the color changes in the middle of filling, the entire filled area will change
    /// color. It does not matter what the fill color was when this request was first sent.
    ///
    /// If the turtle was already filling when this request was sent, this request is ignored.
    ///
    /// Response: N/A
    BeginFill(TurtleId),
    /// Completes a fill polygon at a turtle's current position
    ///
    /// No further points will be added to the polygon and it will remain the color that it was at
    /// the time that this request was sent.
    ///
    /// If the turtle was not filling when this request was sent, this request is ignored.
    ///
    /// Response: N/A
    EndFill(TurtleId),

    /// Clears all drawings created by a turtle
    ///
    /// If no ID is provided, all drawings from all turtles will be cleared.
    ///
    /// The cleared turtles will not move. None of their positions, headings, pens, or other
    /// properties will change. If a turtle was current filling, it will stop filling and its
    /// fill polygon will also be cleared. No properties on the drawing will change, including the
    /// background image, title, etc.
    ///
    /// Response: N/A
    Clear(Option<TurtleId>),
}

#[derive(Debug, Serialize, Deserialize)]
pub enum ServerResponse {
    /// The ID of a newly created turtle, guaranteed to be unique
    NewTurtle(TurtleId),

    /// The result of the export, possibly an error if something went wrong
    ExportComplete(Result<(), ExportError>),

    /// The next event, if any
    ///
    /// A response of `None` only indicates that no events are *currently* available, NOT that no
    /// events will *ever* be available.
    Event(Option<Event>),

    /// The value of the given property of the drawing
    DrawingProp(DrawingPropValue),

    /// The value of the given property of a turtle
    TurtleProp(TurtleId, TurtlePropValue),

    /// An animation was completed for a given turtle
    AnimationComplete(TurtleId),
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum RotationDirection {
    Clockwise,
    Counterclockwise,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum ExportFormat {
    Svg,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum DrawingProp {
    Title,
    Background,
    Center,
    Size,
    Width,
    Height,
    IsMaximized,
    IsFullscreen,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum DrawingPropValue {
    Title(String),
    Background(Color),
    Center(Point),
    Size(Size),
    Width(u32),
    Height(u32),
    IsMaximized(bool),
    IsFullscreen(bool),
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum TurtleProp {
    Pen(PenProp),
    FillColor,
    IsFilling,
    Position,
    PositionX,
    PositionY,
    Heading,
    Speed,
    IsVisible,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum TurtlePropValue {
    Pen(PenPropValue),
    FillColor(Color),
    /// NOTE: Instead of using this with `SetTurtleProp`, use `BeginFill` and `EndFill` instead.
    IsFilling(bool),
    /// NOTE: Instead of using this with `SetTurtleProp`, use `MoveTo` instead.
    Position(Point),
    /// NOTE: Instead of using this with `SetTurtleProp`, use `MoveTo` instead.
    PositionX(f64),
    /// NOTE: Instead of using this with `SetTurtleProp`, use `MoveTo` instead.
    PositionY(f64),
    /// NOTE: Instead of using this with `SetTurtleProp`, use `RotateInPlace` instead.
    Heading(Radians),
    Speed(Speed),
    IsVisible(bool),
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum PenProp {
    IsEnabled,
    Thickness,
    Color,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum PenPropValue {
    IsEnabled(bool),
    Thickness(f64),
    Color(Color),
}
<commit_msg>Clarifying CreateTurtle request description<commit_after>use std::path::PathBuf;

use serde::{Serialize, Deserialize};

use crate::{Color, Point, Speed, Event, Distance, Size};
use crate::renderer_server::{TurtleId, ExportError};
use crate::radians::Radians;

/// The different kinds of requests that can be sent from a client
///
/// Not all requests warrant a response. The response for each request is listed in its
/// documentation.
///
/// Each request is processed in order and will be held until all of its required resources are
/// available. For example, suppose a request needs access to a particular turtle in order to
/// perform an animation. If another request comes along to clear the image, that request will be
/// held until the animation is complete since it also requires access to that turtle. This
/// behaviour is important to ensure sequential consistency and avoid race conditions as requests
/// run concurrently.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ClientRequest {
    /// Creates a new turtle and provides the `TurtleId` that can be used to control it
    ///
    /// Response: `ServerResponse::NewTurtle`
    CreateTurtle,

    /// Export the drawing in its current state to the given path using the given format
    ///
    /// Response: `ServerResponse::ExportComplete`
    Export(PathBuf, ExportFormat),

    /// Get the next event (if any are currently available)
    ///
    /// Response: `ServerResponse::Event`
    PollEvent,

    /// Get the given property of the drawing
    ///
    /// Response: `ServerResponse::DrawingProp`
    DrawingProp(DrawingProp),
    /// Set the given property of the drawing
    ///
    /// Response: N/A
    SetDrawingProp(DrawingPropValue),
    /// Resets the given property of the drawing
    ///
    /// Response: N/A
    ResetDrawingProp(DrawingProp),

    /// Get the given property of a turtle
    ///
    /// Response: `ServerResponse::TurtleProp`
    TurtleProp(TurtleId, TurtleProp),
    /// Set the given property of a turtle
    ///
    /// Response: N/A
    SetTurtleProp(TurtleId, TurtlePropValue),
    /// Resets the given property of a turtle
    ///
    /// Response: N/A
    ResetTurtleProp(TurtleId, TurtleProp),
    /// Resets all properties of a turtle
    ///
    /// Response: N/A
    ResetTurtle(TurtleId),

    /// Move a turtle forward by the given amount
    ///
    /// The turtle moves in the direction of its heading and is animated at its current speed. This
    /// may draw a line if the turtle's pen is down. It may also result in a change to the current
    /// fill if the turtle is currently filling a shape.
    ///
    /// The response to this request provides no additional information, but is necessary to ensure
    /// that animations that should be sequenced, can be sequenced correctly. Without a signal for
    /// when the animation is complete, there would be no way to know when to start the next one.
    ///
    /// Response: `ServerResponse::AnimationComplete`
    MoveForward(TurtleId, Distance),
    /// Move a turtle to the given position
    ///
    /// The turtle movement is animated at its current speed. This may draw a line if the turtle's
    /// pen is down. It may also result in a change to the current fill if the turtle is currently
    /// filling a shape.
    ///
    /// The response to this request provides no additional information, but is necessary to ensure
    /// that animations that should be sequenced, can be sequenced correctly. Without a signal for
    /// when the animation is complete, there would be no way to know when to start the next one.
    ///
    /// Response: `ServerResponse::AnimationComplete`
    MoveTo(TurtleId, Point),
    /// Rotate a turtle in place by the given angle in the given direction
    ///
    /// The turtle rotates with an animation at its current speed. It will not draw any line while
    /// rotating since it has not moved.
    ///
    /// The response to this request provides no additional information, but is necessary to ensure
    /// that animations that should be sequenced, can be sequenced correctly. Without a signal for
    /// when the animation is complete, there would be no way to know when to start the next one.
    ///
    /// Response: `ServerResponse::AnimationComplete`
    RotateInPlace(TurtleId, Radians, RotationDirection),

    /// Creates a fill polygon from a turtle's current position
    ///
    /// The color of the fill will always be consistent with the fill color property of the turtle.
    /// That is, if the color changes in the middle of filling, the entire filled area will change
    /// color. It does not matter what the fill color was when this request was first sent.
    ///
    /// If the turtle was already filling when this request was sent, this request is ignored.
    ///
    /// Response: N/A
    BeginFill(TurtleId),
    /// Completes a fill polygon at a turtle's current position
    ///
    /// No further points will be added to the polygon and it will remain the color that it was at
    /// the time that this request was sent.
    ///
    /// If the turtle was not filling when this request was sent, this request is ignored.
    ///
    /// Response: N/A
    EndFill(TurtleId),

    /// Clears all drawings created by a turtle
    ///
    /// If no ID is provided, all drawings from all turtles will be cleared.
    ///
    /// The cleared turtles will not move. None of their positions, headings, pens, or other
    /// properties will change. If a turtle was current filling, it will stop filling and its
    /// fill polygon will also be cleared. No properties on the drawing will change, including the
    /// background image, title, etc.
    ///
    /// Response: N/A
    Clear(Option<TurtleId>),
}

#[derive(Debug, Serialize, Deserialize)]
pub enum ServerResponse {
    /// The ID of a newly created turtle, guaranteed to be unique
    NewTurtle(TurtleId),

    /// The result of the export, possibly an error if something went wrong
    ExportComplete(Result<(), ExportError>),

    /// The next event, if any
    ///
    /// A response of `None` only indicates that no events are *currently* available, NOT that no
    /// events will *ever* be available.
    Event(Option<Event>),

    /// The value of the given property of the drawing
    DrawingProp(DrawingPropValue),

    /// The value of the given property of a turtle
    TurtleProp(TurtleId, TurtlePropValue),

    /// An animation was completed for a given turtle
    AnimationComplete(TurtleId),
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum RotationDirection {
    Clockwise,
    Counterclockwise,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum ExportFormat {
    Svg,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum DrawingProp {
    Title,
    Background,
    Center,
    Size,
    Width,
    Height,
    IsMaximized,
    IsFullscreen,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum DrawingPropValue {
    Title(String),
    Background(Color),
    Center(Point),
    Size(Size),
    Width(u32),
    Height(u32),
    IsMaximized(bool),
    IsFullscreen(bool),
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum TurtleProp {
    Pen(PenProp),
    FillColor,
    IsFilling,
    Position,
    PositionX,
    PositionY,
    Heading,
    Speed,
    IsVisible,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum TurtlePropValue {
    Pen(PenPropValue),
    FillColor(Color),
    /// NOTE: Instead of using this with `SetTurtleProp`, use `BeginFill` and `EndFill` instead.
    IsFilling(bool),
    /// NOTE: Instead of using this with `SetTurtleProp`, use `MoveTo` instead.
    Position(Point),
    /// NOTE: Instead of using this with `SetTurtleProp`, use `MoveTo` instead.
    PositionX(f64),
    /// NOTE: Instead of using this with `SetTurtleProp`, use `MoveTo` instead.
    PositionY(f64),
    /// NOTE: Instead of using this with `SetTurtleProp`, use `RotateInPlace` instead.
    Heading(Radians),
    Speed(Speed),
    IsVisible(bool),
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum PenProp {
    IsEnabled,
    Thickness,
    Color,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum PenPropValue {
    IsEnabled(bool),
    Thickness(f64),
    Color(Color),
}
<|endoftext|>


<commit_before>/*!
A lower level API for packed multiple substring search, principally for a small
number of patterns.

This sub-module provides vectorized routines for quickly finding matches of a
small number of patterns. In general, users of this crate shouldn't need to
interface with this module directory, as the primary
[`AhoCorasick`](../struct.AhoCorasick.html)
searcher will use these routines automatically as a prefilter when applicable.
However, in some cases, callers may want to bypass the Aho-Corasick machinery
entirely and use this vectorized searcher directly.

# Overview

The primary types in this sub-module are:

* [`Searcher`](struct.Searcher.html) executes the actual search algorithm to
  report matches in a haystack.
* [`Builder`](struct.Builder.html) accumulates patterns incrementally and can
  construct a `Searcher`.
* [`Config`](struct.Config.html) permits tuning the searcher, and itself will
  produce a `Builder` (which can then be used to build a `Searcher`).
  Currently, the only tuneable knob are the match semantics, but this may be
  expanded in the future.

# Examples

This example shows how to create a searcher from an iterator of patterns.
By default, leftmost-first match semantics are used. (See the top-level
[`MatchKind`](../enum.MatchKind.html) type for more details about match
semantics, which apply similarly to packed substring search.)

```
use aho_corasick::packed::{MatchKind, Searcher};

# fn example() -> Option<()> {
let searcher = Searcher::new(["foobar", "foo"].iter().cloned())?;
let matches: Vec<usize> = searcher
    .find_iter("foobar")
    .map(|mat| mat.pattern())
    .collect();
assert_eq!(vec![0], matches);
# Some(()) }
# if cfg!(target_arch = "x86_64") {
#     example().unwrap()
# } else {
#     assert!(example().is_none());
# }
```

This example shows how to use [`Config`](struct.Config.html) to change the
match semantics to leftmost-longest:

```
use aho_corasick::packed::{Config, MatchKind};

# fn example() -> Option<()> {
let searcher = Config::new()
    .match_kind(MatchKind::LeftmostLongest)
    .builder()
    .add("foo")
    .add("foobar")
    .build()?;
let matches: Vec<usize> = searcher
    .find_iter("foobar")
    .map(|mat| mat.pattern())
    .collect();
assert_eq!(vec![1], matches);
# Some(()) }
# if cfg!(target_arch = "x86_64") {
#     example().unwrap()
# } else {
#     assert!(example().is_none());
# }
```

# Packed substring searching

Packed substring searching refers to the use of SIMD (Single Instruction,
Multiple Data) to accelerate the detection of matches in a haystack. Unlike
conventional algorithms, such as Aho-Corasick, SIMD algorithms for substring
search tend to do better with a small number of patterns, where as Aho-Corasick
generally maintains reasonably consistent performance regardless of the number
of patterns you give it. Because of this, the vectorized searcher in this
sub-module cannot be used as a general purpose searcher, since building the
searcher may fail. However, in exchange, when searching for a small number of
patterns, searching can be quite a bit faster than Aho-Corasick (sometimes by
an order of magnitude).

The key take away here is that constructing a searcher from a list of patterns
is a fallible operation. While the precise conditions under which building a
searcher can fail is specifically an implementation detail, here are some
common reasons:

* Too many patterns were given. Typically, the limit is on the order of 100 or
  so, but this limit may fluctuate based on available CPU features.
* The available packed algorithms require CPU features that aren't available.
  For example, currently, this crate only provides packed algorithms for
  `x86_64`. Therefore, constructing a packed searcher on any other target
  (e.g., ARM) will always fail.
* Zero patterns were given, or one of the patterns given was empty. Packed
  searchers require at least one pattern and that all patterns are non-empty.
* Something else about the nature of the patterns (typically based on
  heuristics) suggests that a packed searcher would perform very poorly, so
  no searcher is built.
*/

pub use crate::packed::api::{Builder, Config, FindIter, MatchKind, Searcher};

mod api;
mod pattern;
mod rabinkarp;
mod teddy;
#[cfg(test)]
mod tests;
#[cfg(target_arch = "x86_64")]
mod vector;
<commit_msg>doc: fix wording<commit_after>/*!
A lower level API for packed multiple substring search, principally for a small
number of patterns.

This sub-module provides vectorized routines for quickly finding matches of a
small number of patterns. In general, users of this crate shouldn't need to
interface with this module directly, as the primary
[`AhoCorasick`](../struct.AhoCorasick.html)
searcher will use these routines automatically as a prefilter when applicable.
However, in some cases, callers may want to bypass the Aho-Corasick machinery
entirely and use this vectorized searcher directly.

# Overview

The primary types in this sub-module are:

* [`Searcher`](struct.Searcher.html) executes the actual search algorithm to
  report matches in a haystack.
* [`Builder`](struct.Builder.html) accumulates patterns incrementally and can
  construct a `Searcher`.
* [`Config`](struct.Config.html) permits tuning the searcher, and itself will
  produce a `Builder` (which can then be used to build a `Searcher`).
  Currently, the only tuneable knob are the match semantics, but this may be
  expanded in the future.

# Examples

This example shows how to create a searcher from an iterator of patterns.
By default, leftmost-first match semantics are used. (See the top-level
[`MatchKind`](../enum.MatchKind.html) type for more details about match
semantics, which apply similarly to packed substring search.)

```
use aho_corasick::packed::{MatchKind, Searcher};

# fn example() -> Option<()> {
let searcher = Searcher::new(["foobar", "foo"].iter().cloned())?;
let matches: Vec<usize> = searcher
    .find_iter("foobar")
    .map(|mat| mat.pattern())
    .collect();
assert_eq!(vec![0], matches);
# Some(()) }
# if cfg!(target_arch = "x86_64") {
#     example().unwrap()
# } else {
#     assert!(example().is_none());
# }
```

This example shows how to use [`Config`](struct.Config.html) to change the
match semantics to leftmost-longest:

```
use aho_corasick::packed::{Config, MatchKind};

# fn example() -> Option<()> {
let searcher = Config::new()
    .match_kind(MatchKind::LeftmostLongest)
    .builder()
    .add("foo")
    .add("foobar")
    .build()?;
let matches: Vec<usize> = searcher
    .find_iter("foobar")
    .map(|mat| mat.pattern())
    .collect();
assert_eq!(vec![1], matches);
# Some(()) }
# if cfg!(target_arch = "x86_64") {
#     example().unwrap()
# } else {
#     assert!(example().is_none());
# }
```

# Packed substring searching

Packed substring searching refers to the use of SIMD (Single Instruction,
Multiple Data) to accelerate the detection of matches in a haystack. Unlike
conventional algorithms, such as Aho-Corasick, SIMD algorithms for substring
search tend to do better with a small number of patterns, where as Aho-Corasick
generally maintains reasonably consistent performance regardless of the number
of patterns you give it. Because of this, the vectorized searcher in this
sub-module cannot be used as a general purpose searcher, since building the
searcher may fail. However, in exchange, when searching for a small number of
patterns, searching can be quite a bit faster than Aho-Corasick (sometimes by
an order of magnitude).

The key take away here is that constructing a searcher from a list of patterns
is a fallible operation. While the precise conditions under which building a
searcher can fail is specifically an implementation detail, here are some
common reasons:

* Too many patterns were given. Typically, the limit is on the order of 100 or
  so, but this limit may fluctuate based on available CPU features.
* The available packed algorithms require CPU features that aren't available.
  For example, currently, this crate only provides packed algorithms for
  `x86_64`. Therefore, constructing a packed searcher on any other target
  (e.g., ARM) will always fail.
* Zero patterns were given, or one of the patterns given was empty. Packed
  searchers require at least one pattern and that all patterns are non-empty.
* Something else about the nature of the patterns (typically based on
  heuristics) suggests that a packed searcher would perform very poorly, so
  no searcher is built.
*/

pub use crate::packed::api::{Builder, Config, FindIter, MatchKind, Searcher};

mod api;
mod pattern;
mod rabinkarp;
mod teddy;
#[cfg(test)]
mod tests;
#[cfg(target_arch = "x86_64")]
mod vector;
<|endoftext|>


<commit_before>// Copyright (c) 2013-2014 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.

//! # Cap'n Proto Schema Compiler Plugin Library
//!
//! This library allows you to do
//! [Cap'n Proto code generation](https://capnproto.org/otherlang.html#how-to-write-compiler-plugins)
//! within a Cargo build. You still need the `capnp` binary (implemented in C++).
//! (If you use a package manager, try looking for a package called
//! `capnproto`.)
//!
//! In your Cargo.toml:
//!
//! ```ignore
//! [package]
//! build = "build.rs"
//!
//! [build-dependencies]
//! capnpc = "0.12"
//! ```
//!
//! In your build.rs:
//!
//! ```ignore
//! extern crate capnpc;
//!
//! fn main() {
//!     capnpc::CompilerCommand::new()
//!         .src_prefix("schema")
//!         .file("schema/foo.capnp")
//!         .file("schema/bar.capnp")
//!         .run().expect("schema compiler command");
//! }
//! ```
//!
//! This will be equivalent to executing the shell command
//!
//! ```ignore
//!   capnp compile -orust:$OUT_DIR --src-prefix=schema schema/foo.capnp schema/bar.capnp
//! ```
//!

extern crate capnp;

/// Code generated from
/// [schema.capnp](https://github.com/capnproto/capnproto/blob/master/c%2B%2B/src/capnp/schema.capnp).
pub mod schema_capnp;

pub mod codegen;
pub mod codegen_types;
mod pointer_constants;

use std::path::{Path, PathBuf};

fn run_command(mut command: ::std::process::Command, path: &PathBuf) -> ::capnp::Result<()> {
    let mut p = command.spawn()?;
    crate::codegen::generate_code(p.stdout.take().unwrap(), path.as_path())?;
    let exit_status = p.wait()?;
    if !exit_status.success() {
        Err(::capnp::Error::failed(format!(
            "Non-success exit status: {}",
            exit_status
        )))
    } else {
        Ok(())
    }
}

/// A builder object for schema compiler commands.
pub struct CompilerCommand {
    files: Vec<PathBuf>,
    src_prefixes: Vec<PathBuf>,
    import_paths: Vec<PathBuf>,
    no_standard_import: bool,
    executable_path: Option<PathBuf>,
    output_path: Option<PathBuf>,
}

impl CompilerCommand {
    /// Creates a new, empty command.
    pub fn new() -> CompilerCommand {
        CompilerCommand {
            files: Vec::new(),
            src_prefixes: Vec::new(),
            import_paths: Vec::new(),
            no_standard_import: false,
            executable_path: None,
            output_path: None,
        }
    }

    /// Adds a file to be compiled.
    pub fn file<P>(&mut self, path: P) -> &mut CompilerCommand
    where
        P: AsRef<Path>,
    {
        self.files.push(path.as_ref().to_path_buf());
        self
    }

    /// Adds a --src-prefix flag. For all files specified for compilation that start
    /// with `prefix`, removes the prefix when computing output filenames.
    pub fn src_prefix<P>(&mut self, prefix: P) -> &mut CompilerCommand
    where
        P: AsRef<Path>,
    {
        self.src_prefixes.push(prefix.as_ref().to_path_buf());
        self
    }

    /// Adds an --import_path flag. Adds `dir` to the list of directories searched
    /// for absolute imports.
    pub fn import_path<P>(&mut self, dir: P) -> &mut CompilerCommand
    where
        P: AsRef<Path>,
    {
        self.import_paths.push(dir.as_ref().to_path_buf());
        self
    }

    /// Adds the --no-standard-import flag, indicating that the default import paths of
    /// /usr/include and /usr/local/include should not bet included.
    pub fn no_standard_import(&mut self) -> &mut CompilerCommand {
        assert!(!self.no_standard_import, "no_standard_import() must only be called once");

        self.no_standard_import = true;
        self
    }

    /// Sets the output directory of generated code. Default is OUT_DIR
    pub fn output_path<P>(&mut self, path: P) -> &mut CompilerCommand
    where
        P: AsRef<Path>,
    {
        assert!(self.executable_path.is_none(), "output_path() must only be called once");

        self.output_path = Some(path.as_ref().to_path_buf());
        self
    }

    /// Specify the executable which is used for the 'capnp' tool. When this method is not called, the command looks for a name 'capnp'
    /// on the system (e.g. in working directory or in PATH environment variable).
    pub fn capnp_executable<P>(&mut self, path: P) -> &mut CompilerCommand
    where
        P: AsRef<Path>
    {
        assert!(self.executable_path.is_none(), "capnp_executable() must only be called once");

        self.executable_path = Some(path.as_ref().to_path_buf());
        self
    }

    /// Runs the command.
    /// Returns an error if `OUT_DIR` or a custom output directory was not set, or if `capnp compile` fails.
    pub fn run(&mut self) -> ::capnp::Result<()> {
        let mut command = if let Some(executable) = &self.executable_path {
            ::std::process::Command::new(executable)
        } else {
            ::std::process::Command::new("capnp")
        };

        command.arg("compile").arg("-o").arg("-");

        if self.no_standard_import {
            command.arg("--no-standard-import");
        }

        for import_path in &self.import_paths {
            command.arg(&format!("--import-path={}", import_path.display()));
        }

        for src_prefix in &self.src_prefixes {
            command.arg(&format!("--src-prefix={}", src_prefix.display()));
        }

        for file in &self.files {
            command.arg(&format!("{}", file.display()));
        }

        let output_path = if let Some(output_path) = &self.output_path {
            output_path.clone()
        } else {
            // Try `OUT_DIR` by default
            PathBuf::from(::std::env::var("OUT_DIR").map_err(|error| {
                ::capnp::Error::failed(format!(
                    "Could not access `OUT_DIR` environment variable: {}. \
                     You might need to set it up or instead create you own output \
                     structure using `CompilerCommand::output_path`",
                    error
                ))
            })?)
        };

        command.stdout(::std::process::Stdio::piped());
        command.stderr(::std::process::Stdio::inherit());

        run_command(command, &output_path).map_err(|error| {
            ::capnp::Error::failed(format!(
                "Error while trying to execute `capnp compile`: {}.  \
                 Please verify that version 0.5.2 or higher of the capnp executable \
                 is installed on your system. See https://capnproto.org/install.html",
                error
            ))
        })
    }
}

#[test]
fn compiler_command_new_no_out_dir() {
    let error = CompilerCommand::new().run().unwrap_err().description;
    assert!(error.starts_with("Could not access `OUT_DIR` environment variable"));
}

#[test]
fn compiler_command_with_output_path_no_out_dir() {
    let error = CompilerCommand::new().output_path("foo").run().unwrap_err().description;
    assert!(error.starts_with("Error while trying to execute `capnp compile`"));
}
<commit_msg>remove assert! statements in CompilerCommand<commit_after>// Copyright (c) 2013-2014 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.

//! # Cap'n Proto Schema Compiler Plugin Library
//!
//! This library allows you to do
//! [Cap'n Proto code generation](https://capnproto.org/otherlang.html#how-to-write-compiler-plugins)
//! within a Cargo build. You still need the `capnp` binary (implemented in C++).
//! (If you use a package manager, try looking for a package called
//! `capnproto`.)
//!
//! In your Cargo.toml:
//!
//! ```ignore
//! [package]
//! build = "build.rs"
//!
//! [build-dependencies]
//! capnpc = "0.12"
//! ```
//!
//! In your build.rs:
//!
//! ```ignore
//! extern crate capnpc;
//!
//! fn main() {
//!     capnpc::CompilerCommand::new()
//!         .src_prefix("schema")
//!         .file("schema/foo.capnp")
//!         .file("schema/bar.capnp")
//!         .run().expect("schema compiler command");
//! }
//! ```
//!
//! This will be equivalent to executing the shell command
//!
//! ```ignore
//!   capnp compile -orust:$OUT_DIR --src-prefix=schema schema/foo.capnp schema/bar.capnp
//! ```
//!

extern crate capnp;

/// Code generated from
/// [schema.capnp](https://github.com/capnproto/capnproto/blob/master/c%2B%2B/src/capnp/schema.capnp).
pub mod schema_capnp;

pub mod codegen;
pub mod codegen_types;
mod pointer_constants;

use std::path::{Path, PathBuf};

fn run_command(mut command: ::std::process::Command, path: &PathBuf) -> ::capnp::Result<()> {
    let mut p = command.spawn()?;
    crate::codegen::generate_code(p.stdout.take().unwrap(), path.as_path())?;
    let exit_status = p.wait()?;
    if !exit_status.success() {
        Err(::capnp::Error::failed(format!(
            "Non-success exit status: {}",
            exit_status
        )))
    } else {
        Ok(())
    }
}

/// A builder object for schema compiler commands.
pub struct CompilerCommand {
    files: Vec<PathBuf>,
    src_prefixes: Vec<PathBuf>,
    import_paths: Vec<PathBuf>,
    no_standard_import: bool,
    executable_path: Option<PathBuf>,
    output_path: Option<PathBuf>,
}

impl CompilerCommand {
    /// Creates a new, empty command.
    pub fn new() -> CompilerCommand {
        CompilerCommand {
            files: Vec::new(),
            src_prefixes: Vec::new(),
            import_paths: Vec::new(),
            no_standard_import: false,
            executable_path: None,
            output_path: None,
        }
    }

    /// Adds a file to be compiled.
    pub fn file<P>(&mut self, path: P) -> &mut CompilerCommand
    where
        P: AsRef<Path>,
    {
        self.files.push(path.as_ref().to_path_buf());
        self
    }

    /// Adds a --src-prefix flag. For all files specified for compilation that start
    /// with `prefix`, removes the prefix when computing output filenames.
    pub fn src_prefix<P>(&mut self, prefix: P) -> &mut CompilerCommand
    where
        P: AsRef<Path>,
    {
        self.src_prefixes.push(prefix.as_ref().to_path_buf());
        self
    }

    /// Adds an --import_path flag. Adds `dir` to the list of directories searched
    /// for absolute imports.
    pub fn import_path<P>(&mut self, dir: P) -> &mut CompilerCommand
    where
        P: AsRef<Path>,
    {
        self.import_paths.push(dir.as_ref().to_path_buf());
        self
    }

    /// Adds the --no-standard-import flag, indicating that the default import paths of
    /// /usr/include and /usr/local/include should not bet included.
    pub fn no_standard_import(&mut self) -> &mut CompilerCommand {
        self.no_standard_import = true;
        self
    }

    /// Sets the output directory of generated code. Default is OUT_DIR
    pub fn output_path<P>(&mut self, path: P) -> &mut CompilerCommand
    where
        P: AsRef<Path>,
    {
        self.output_path = Some(path.as_ref().to_path_buf());
        self
    }

    /// Specify the executable which is used for the 'capnp' tool. When this method is not called, the command looks for a name 'capnp'
    /// on the system (e.g. in working directory or in PATH environment variable).
    pub fn capnp_executable<P>(&mut self, path: P) -> &mut CompilerCommand
    where
        P: AsRef<Path>
    {
        self.executable_path = Some(path.as_ref().to_path_buf());
        self
    }

    /// Runs the command.
    /// Returns an error if `OUT_DIR` or a custom output directory was not set, or if `capnp compile` fails.
    pub fn run(&mut self) -> ::capnp::Result<()> {
        let mut command = if let Some(executable) = &self.executable_path {
            ::std::process::Command::new(executable)
        } else {
            ::std::process::Command::new("capnp")
        };

        command.arg("compile").arg("-o").arg("-");

        if self.no_standard_import {
            command.arg("--no-standard-import");
        }

        for import_path in &self.import_paths {
            command.arg(&format!("--import-path={}", import_path.display()));
        }

        for src_prefix in &self.src_prefixes {
            command.arg(&format!("--src-prefix={}", src_prefix.display()));
        }

        for file in &self.files {
            command.arg(&format!("{}", file.display()));
        }

        let output_path = if let Some(output_path) = &self.output_path {
            output_path.clone()
        } else {
            // Try `OUT_DIR` by default
            PathBuf::from(::std::env::var("OUT_DIR").map_err(|error| {
                ::capnp::Error::failed(format!(
                    "Could not access `OUT_DIR` environment variable: {}. \
                     You might need to set it up or instead create you own output \
                     structure using `CompilerCommand::output_path`",
                    error
                ))
            })?)
        };

        command.stdout(::std::process::Stdio::piped());
        command.stderr(::std::process::Stdio::inherit());

        run_command(command, &output_path).map_err(|error| {
            ::capnp::Error::failed(format!(
                "Error while trying to execute `capnp compile`: {}.  \
                 Please verify that version 0.5.2 or higher of the capnp executable \
                 is installed on your system. See https://capnproto.org/install.html",
                error
            ))
        })
    }
}

#[test]
fn compiler_command_new_no_out_dir() {
    let error = CompilerCommand::new().run().unwrap_err().description;
    assert!(error.starts_with("Could not access `OUT_DIR` environment variable"));
}

#[test]
fn compiler_command_with_output_path_no_out_dir() {
    let error = CompilerCommand::new().output_path("foo").run().unwrap_err().description;
    assert!(error.starts_with("Error while trying to execute `capnp compile`"));
}
<|endoftext|>


<commit_before>// Copyright (c) 2016 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

/// Builds a `RenderPass` object whose template parameter is of undeterminate type.
#[macro_export]
macro_rules! single_pass_renderpass {
    (
        $device:expr,
        attachments: { $($a:tt)* },
        pass: {
            color: [$($color_atch:ident),*],
            depth_stencil: {$($depth_atch:ident)*}$(,)*
            $(resolve: [$($resolve_atch:ident),*])*$(,)*
        }
    ) => (
        ordered_passes_renderpass!(
            $device,
            attachments: { $($a)* },
            passes: [
                {
                    color: [$($color_atch),*],
                    depth_stencil: {$($depth_atch)*},
                    input: [],
                    resolve: [$($($resolve_atch),*)*]
                }
            ]
        )
    )
}

/// Builds a `RenderPass` object whose template parameter is of undeterminate type.
#[macro_export]
macro_rules! ordered_passes_renderpass {
    (
        $device:expr,
        attachments: {
            $(
                $atch_name:ident: {
                    load: $load:ident,
                    store: $store:ident,
                    format: $format:expr,
                    samples: $samples:expr,
                    $(initial_layout: $init_layout:expr,)*
                    $(final_layout: $final_layout:expr,)*
                }
            ),*
        },
        passes: [
            $(
                {
                    color: [$($color_atch:ident),*],
                    depth_stencil: {$($depth_atch:ident)*},
                    input: [$($input_atch:ident),*]$(,)*
                    $(resolve: [$($resolve_atch:ident),*])*$(,)*
                }
            ),*
        ]
    ) => ({
        use $crate::framebuffer::RenderPassDesc;

        mod scope {
            #![allow(non_camel_case_types)]
            #![allow(non_snake_case)]

            use $crate::format::ClearValue;
            use $crate::format::Format;
            use $crate::framebuffer::RenderPassDesc;
            use $crate::framebuffer::RenderPassDescClearValues;
            use $crate::framebuffer::LayoutAttachmentDescription;
            use $crate::framebuffer::LayoutPassDescription;
            use $crate::framebuffer::LayoutPassDependencyDescription;
            use $crate::image::ImageLayout;
            use $crate::sync::AccessFlagBits;
            use $crate::sync::PipelineStages;

            pub struct CustomRenderPassDesc {
                $(
                    pub $atch_name: (Format, u32),
                )*
            }

            #[allow(unsafe_code)]
            unsafe impl RenderPassDesc for CustomRenderPassDesc {
                #[inline]
                fn num_attachments(&self) -> usize {
                    num_attachments()
                }

                #[inline]
                fn attachment_desc(&self, id: usize) -> Option<LayoutAttachmentDescription> {
                    attachment(self, id)
                }

                #[inline]
                fn num_subpasses(&self) -> usize {
                    num_subpasses()
                }

                #[inline]
                fn subpass_desc(&self, id: usize) -> Option<LayoutPassDescription> {
                    subpass(id)
                }

                #[inline]
                fn num_dependencies(&self) -> usize {
                    num_dependencies()
                }

                #[inline]
                fn dependency_desc(&self, id: usize) -> Option<LayoutPassDependencyDescription> {
                    dependency(id)
                }
            }

            unsafe impl RenderPassDescClearValues<Vec<ClearValue>> for CustomRenderPassDesc {
                fn convert_clear_values(&self, values: Vec<ClearValue>) -> Box<Iterator<Item = ClearValue>> {
                    // FIXME: safety checks
                    Box::new(values.into_iter())
                }
            }

            #[inline]
            fn num_attachments() -> usize {
                #![allow(unused_assignments)]
                #![allow(unused_mut)]
                #![allow(unused_variables)]
                let mut num = 0;
                $(let $atch_name = num; num += 1;)*
                num
            }

            #[inline]
            fn attachment(desc: &CustomRenderPassDesc, id: usize) -> Option<LayoutAttachmentDescription> {
                #![allow(unused_assignments)]
                #![allow(unused_mut)]

                let mut num = 0;

                $({
                    if id == num {
                        let (initial_layout, final_layout) = attachment_layouts(num);

                        return Some($crate::framebuffer::LayoutAttachmentDescription {
                            format: desc.$atch_name.0,
                            samples: desc.$atch_name.1,
                            load: $crate::framebuffer::LoadOp::$load,
                            store: $crate::framebuffer::StoreOp::$store,
                            stencil_load: $crate::framebuffer::LoadOp::$load,
                            stencil_store: $crate::framebuffer::StoreOp::$store,
                            initial_layout: initial_layout,
                            final_layout: final_layout,
                        });
                    }

                    num += 1;
                })*

                None
            }

            #[inline]
            fn num_subpasses() -> usize {
                #![allow(unused_assignments)]
                #![allow(unused_mut)]
                #![allow(unused_variables)]
                let mut num = 0;
                $($(let $color_atch = num;)* num += 1;)*
                num
            }

            #[inline]
            fn subpass(id: usize) -> Option<LayoutPassDescription> {
                #![allow(unused_assignments)]
                #![allow(unused_mut)]
                #![allow(unused_variables)]

                let mut attachment_num = 0;
                $(
                    let $atch_name = attachment_num;
                    attachment_num += 1;
                )*

                let mut cur_pass_num = 0;

                $({
                    if id == cur_pass_num {
                        let mut depth = None;
                        $(
                            depth = Some(($depth_atch, ImageLayout::DepthStencilAttachmentOptimal));
                        )*

                        let mut desc = LayoutPassDescription {
                            color_attachments: vec![
                                $(
                                    ($color_atch, ImageLayout::ColorAttachmentOptimal)
                                ),*
                            ],
                            depth_stencil: depth,
                            input_attachments: vec![
                                $(
                                    ($input_atch, ImageLayout::ShaderReadOnlyOptimal)
                                ),*
                            ],
                            resolve_attachments: vec![
                                $($(
                                    ($resolve_atch, ImageLayout::TransferDstOptimal)
                                ),*)*
                            ],
                            preserve_attachments: (0 .. attachment_num).filter(|&a| {
                                $(if a == $color_atch { return false; })*
                                $(if a == $depth_atch { return false; })*
                                $(if a == $input_atch { return false; })*
                                $($(if a == $resolve_atch { return false; })*)*
                                true
                            }).collect()
                        };

                        assert!(desc.resolve_attachments.is_empty() ||
                                desc.resolve_attachments.len() == desc.color_attachments.len());
                        return Some(desc);
                    }

                    cur_pass_num += 1;
                })*

                None
            }

            #[inline]
            fn num_dependencies() -> usize {
                num_subpasses().saturating_sub(1)
            }

            #[inline]
            fn dependency(id: usize) -> Option<LayoutPassDependencyDescription> {
                let num_passes = num_subpasses();

                if id + 1 >= num_passes {
                    return None;
                }

                Some(LayoutPassDependencyDescription {
                    source_subpass: id,
                    destination_subpass: id + 1,
                    source_stages: PipelineStages { all_graphics: true, .. PipelineStages::none() },         // TODO: correct values
                    destination_stages: PipelineStages { all_graphics: true, .. PipelineStages::none() },         // TODO: correct values
                    source_access: AccessFlagBits::all(),         // TODO: correct values
                    destination_access: AccessFlagBits::all(),         // TODO: correct values
                    by_region: true,            // TODO: correct values
                })
            }

            /// Returns the initial and final layout of an attachment, given its num.
            ///
            /// The value always correspond to the first and last usages of an attachment.
            fn attachment_layouts(num: usize) -> (ImageLayout, ImageLayout) {
                #![allow(unused_assignments)]
                #![allow(unused_mut)]
                #![allow(unused_variables)]

                let mut attachment_num = 0;
                $(
                    let $atch_name = attachment_num;
                    attachment_num += 1;
                )*

                let mut initial_layout = None;
                let mut final_layout = None;

                $({
                    $(
                        if $depth_atch == num {
                            if initial_layout.is_none() {
                                initial_layout = Some(ImageLayout::DepthStencilAttachmentOptimal);
                            }
                            final_layout = Some(ImageLayout::DepthStencilAttachmentOptimal);
                        }
                    )*

                    $(
                        if $color_atch == num {
                            if initial_layout.is_none() {
                                initial_layout = Some(ImageLayout::ColorAttachmentOptimal);
                            }
                            final_layout = Some(ImageLayout::ColorAttachmentOptimal);
                        }
                    )*

                    $($(
                        if $resolve_atch == num {
                            if initial_layout.is_none() {
                                initial_layout = Some(ImageLayout::TransferDstOptimal);
                            }
                            final_layout = Some(ImageLayout::TransferDstOptimal);
                        }
                    )*)*

                    $(
                        if $input_atch == num {
                            if initial_layout.is_none() {
                                initial_layout = Some(ImageLayout::ShaderReadOnlyOptimal);
                            }
                            final_layout = Some(ImageLayout::ShaderReadOnlyOptimal);
                        }
                    )*
                })*

                $(if $atch_name == num {
                    // If the clear OP is Clear or DontCare, default to the Undefined layout.
                    if initial_layout == Some(ImageLayout::DepthStencilAttachmentOptimal) ||
                        initial_layout == Some(ImageLayout::ColorAttachmentOptimal) ||
                        initial_layout == Some(ImageLayout::TransferDstOptimal)
                    {
                        if $crate::framebuffer::LoadOp::$load == $crate::framebuffer::LoadOp::Clear ||
                            $crate::framebuffer::LoadOp::$load == $crate::framebuffer::LoadOp::DontCare
                        {
                            initial_layout = Some(ImageLayout::Undefined);
                        }
                    }

                    $(initial_layout = Some($init_layout);)*
                    $(final_layout = Some($final_layout);)*
                })*

                (initial_layout.unwrap(), final_layout.unwrap())
            }
        }

        scope::CustomRenderPassDesc {
            $(
                $atch_name: ($format, $samples),
            )*
        }.build_render_pass($device)
    });
}

#[cfg(test)]
mod tests {
    use format::Format;

    #[test]
    fn single_pass_resolve() {
        let (device, _) = gfx_dev_and_queue!();
        let _ = single_pass_renderpass!(device.clone(),
            attachments: {
                a: {
                    load: Clear,
                    store: DontCare,
                    format: Format::R8G8B8A8Unorm,
                    samples: 4,
                },
                b: {
                    load: DontCare,
                    store: Store,
                    format: Format::R8G8B8A8Unorm,
                    samples: 1,
                }
            },
            pass: {
                color: [a],
                depth_stencil: {},
                resolve: [b],
            }
        ).unwrap();
    }
}
<commit_msg>missing initial_layout/final_layout panic gives a useful message (#906)<commit_after>// Copyright (c) 2016 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

/// Builds a `RenderPass` object whose template parameter is of undeterminate type.
#[macro_export]
macro_rules! single_pass_renderpass {
    (
        $device:expr,
        attachments: { $($a:tt)* },
        pass: {
            color: [$($color_atch:ident),*],
            depth_stencil: {$($depth_atch:ident)*}$(,)*
            $(resolve: [$($resolve_atch:ident),*])*$(,)*
        }
    ) => (
        ordered_passes_renderpass!(
            $device,
            attachments: { $($a)* },
            passes: [
                {
                    color: [$($color_atch),*],
                    depth_stencil: {$($depth_atch)*},
                    input: [],
                    resolve: [$($($resolve_atch),*)*]
                }
            ]
        )
    )
}

/// Builds a `RenderPass` object whose template parameter is of undeterminate type.
#[macro_export]
macro_rules! ordered_passes_renderpass {
    (
        $device:expr,
        attachments: {
            $(
                $atch_name:ident: {
                    load: $load:ident,
                    store: $store:ident,
                    format: $format:expr,
                    samples: $samples:expr,
                    $(initial_layout: $init_layout:expr,)*
                    $(final_layout: $final_layout:expr,)*
                }
            ),*
        },
        passes: [
            $(
                {
                    color: [$($color_atch:ident),*],
                    depth_stencil: {$($depth_atch:ident)*},
                    input: [$($input_atch:ident),*]$(,)*
                    $(resolve: [$($resolve_atch:ident),*])*$(,)*
                }
            ),*
        ]
    ) => ({
        use $crate::framebuffer::RenderPassDesc;

        mod scope {
            #![allow(non_camel_case_types)]
            #![allow(non_snake_case)]

            use $crate::format::ClearValue;
            use $crate::format::Format;
            use $crate::framebuffer::RenderPassDesc;
            use $crate::framebuffer::RenderPassDescClearValues;
            use $crate::framebuffer::LayoutAttachmentDescription;
            use $crate::framebuffer::LayoutPassDescription;
            use $crate::framebuffer::LayoutPassDependencyDescription;
            use $crate::image::ImageLayout;
            use $crate::sync::AccessFlagBits;
            use $crate::sync::PipelineStages;

            pub struct CustomRenderPassDesc {
                $(
                    pub $atch_name: (Format, u32),
                )*
            }

            #[allow(unsafe_code)]
            unsafe impl RenderPassDesc for CustomRenderPassDesc {
                #[inline]
                fn num_attachments(&self) -> usize {
                    num_attachments()
                }

                #[inline]
                fn attachment_desc(&self, id: usize) -> Option<LayoutAttachmentDescription> {
                    attachment(self, id)
                }

                #[inline]
                fn num_subpasses(&self) -> usize {
                    num_subpasses()
                }

                #[inline]
                fn subpass_desc(&self, id: usize) -> Option<LayoutPassDescription> {
                    subpass(id)
                }

                #[inline]
                fn num_dependencies(&self) -> usize {
                    num_dependencies()
                }

                #[inline]
                fn dependency_desc(&self, id: usize) -> Option<LayoutPassDependencyDescription> {
                    dependency(id)
                }
            }

            unsafe impl RenderPassDescClearValues<Vec<ClearValue>> for CustomRenderPassDesc {
                fn convert_clear_values(&self, values: Vec<ClearValue>) -> Box<Iterator<Item = ClearValue>> {
                    // FIXME: safety checks
                    Box::new(values.into_iter())
                }
            }

            #[inline]
            fn num_attachments() -> usize {
                #![allow(unused_assignments)]
                #![allow(unused_mut)]
                #![allow(unused_variables)]
                let mut num = 0;
                $(let $atch_name = num; num += 1;)*
                num
            }

            #[inline]
            fn attachment(desc: &CustomRenderPassDesc, id: usize) -> Option<LayoutAttachmentDescription> {
                #![allow(unused_assignments)]
                #![allow(unused_mut)]

                let mut num = 0;

                $({
                    if id == num {
                        let (initial_layout, final_layout) = attachment_layouts(num);

                        return Some($crate::framebuffer::LayoutAttachmentDescription {
                            format: desc.$atch_name.0,
                            samples: desc.$atch_name.1,
                            load: $crate::framebuffer::LoadOp::$load,
                            store: $crate::framebuffer::StoreOp::$store,
                            stencil_load: $crate::framebuffer::LoadOp::$load,
                            stencil_store: $crate::framebuffer::StoreOp::$store,
                            initial_layout: initial_layout,
                            final_layout: final_layout,
                        });
                    }

                    num += 1;
                })*

                None
            }

            #[inline]
            fn num_subpasses() -> usize {
                #![allow(unused_assignments)]
                #![allow(unused_mut)]
                #![allow(unused_variables)]
                let mut num = 0;
                $($(let $color_atch = num;)* num += 1;)*
                num
            }

            #[inline]
            fn subpass(id: usize) -> Option<LayoutPassDescription> {
                #![allow(unused_assignments)]
                #![allow(unused_mut)]
                #![allow(unused_variables)]

                let mut attachment_num = 0;
                $(
                    let $atch_name = attachment_num;
                    attachment_num += 1;
                )*

                let mut cur_pass_num = 0;

                $({
                    if id == cur_pass_num {
                        let mut depth = None;
                        $(
                            depth = Some(($depth_atch, ImageLayout::DepthStencilAttachmentOptimal));
                        )*

                        let mut desc = LayoutPassDescription {
                            color_attachments: vec![
                                $(
                                    ($color_atch, ImageLayout::ColorAttachmentOptimal)
                                ),*
                            ],
                            depth_stencil: depth,
                            input_attachments: vec![
                                $(
                                    ($input_atch, ImageLayout::ShaderReadOnlyOptimal)
                                ),*
                            ],
                            resolve_attachments: vec![
                                $($(
                                    ($resolve_atch, ImageLayout::TransferDstOptimal)
                                ),*)*
                            ],
                            preserve_attachments: (0 .. attachment_num).filter(|&a| {
                                $(if a == $color_atch { return false; })*
                                $(if a == $depth_atch { return false; })*
                                $(if a == $input_atch { return false; })*
                                $($(if a == $resolve_atch { return false; })*)*
                                true
                            }).collect()
                        };

                        assert!(desc.resolve_attachments.is_empty() ||
                                desc.resolve_attachments.len() == desc.color_attachments.len());
                        return Some(desc);
                    }

                    cur_pass_num += 1;
                })*

                None
            }

            #[inline]
            fn num_dependencies() -> usize {
                num_subpasses().saturating_sub(1)
            }

            #[inline]
            fn dependency(id: usize) -> Option<LayoutPassDependencyDescription> {
                let num_passes = num_subpasses();

                if id + 1 >= num_passes {
                    return None;
                }

                Some(LayoutPassDependencyDescription {
                    source_subpass: id,
                    destination_subpass: id + 1,
                    source_stages: PipelineStages { all_graphics: true, .. PipelineStages::none() },         // TODO: correct values
                    destination_stages: PipelineStages { all_graphics: true, .. PipelineStages::none() },         // TODO: correct values
                    source_access: AccessFlagBits::all(),         // TODO: correct values
                    destination_access: AccessFlagBits::all(),         // TODO: correct values
                    by_region: true,            // TODO: correct values
                })
            }

            /// Returns the initial and final layout of an attachment, given its num.
            ///
            /// The value always correspond to the first and last usages of an attachment.
            fn attachment_layouts(num: usize) -> (ImageLayout, ImageLayout) {
                #![allow(unused_assignments)]
                #![allow(unused_mut)]
                #![allow(unused_variables)]

                let mut attachment_num = 0;
                $(
                    let $atch_name = attachment_num;
                    attachment_num += 1;
                )*

                let mut initial_layout = None;
                let mut final_layout = None;

                $({
                    $(
                        if $depth_atch == num {
                            if initial_layout.is_none() {
                                initial_layout = Some(ImageLayout::DepthStencilAttachmentOptimal);
                            }
                            final_layout = Some(ImageLayout::DepthStencilAttachmentOptimal);
                        }
                    )*

                    $(
                        if $color_atch == num {
                            if initial_layout.is_none() {
                                initial_layout = Some(ImageLayout::ColorAttachmentOptimal);
                            }
                            final_layout = Some(ImageLayout::ColorAttachmentOptimal);
                        }
                    )*

                    $($(
                        if $resolve_atch == num {
                            if initial_layout.is_none() {
                                initial_layout = Some(ImageLayout::TransferDstOptimal);
                            }
                            final_layout = Some(ImageLayout::TransferDstOptimal);
                        }
                    )*)*

                    $(
                        if $input_atch == num {
                            if initial_layout.is_none() {
                                initial_layout = Some(ImageLayout::ShaderReadOnlyOptimal);
                            }
                            final_layout = Some(ImageLayout::ShaderReadOnlyOptimal);
                        }
                    )*
                })*

                $(if $atch_name == num {
                    // If the clear OP is Clear or DontCare, default to the Undefined layout.
                    if initial_layout == Some(ImageLayout::DepthStencilAttachmentOptimal) ||
                        initial_layout == Some(ImageLayout::ColorAttachmentOptimal) ||
                        initial_layout == Some(ImageLayout::TransferDstOptimal)
                    {
                        if $crate::framebuffer::LoadOp::$load == $crate::framebuffer::LoadOp::Clear ||
                            $crate::framebuffer::LoadOp::$load == $crate::framebuffer::LoadOp::DontCare
                        {
                            initial_layout = Some(ImageLayout::Undefined);
                        }
                    }

                    $(initial_layout = Some($init_layout);)*
                    $(final_layout = Some($final_layout);)*
                })*
                (
                    initial_layout.expect(format!("Attachment {} is missing initial_layout, this is normally \
                        automatically determined but you can manually specify it for an individual \
                        attachment in the single_pass_renderpass! macro", attachment_num).as_ref()),
                    final_layout.expect(format!("Attachment {} is missing final_layout, this is normally \
                        automatically determined but you can manually specify it for an individual \
                        attachment in the single_pass_renderpass! macro", attachment_num).as_ref())
                )
            }
        }

        scope::CustomRenderPassDesc {
            $(
                $atch_name: ($format, $samples),
            )*
        }.build_render_pass($device)
    });
}

#[cfg(test)]
mod tests {
    use format::Format;

    #[test]
    fn single_pass_resolve() {
        let (device, _) = gfx_dev_and_queue!();
        let _ = single_pass_renderpass!(device.clone(),
            attachments: {
                a: {
                    load: Clear,
                    store: DontCare,
                    format: Format::R8G8B8A8Unorm,
                    samples: 4,
                },
                b: {
                    load: DontCare,
                    store: Store,
                    format: Format::R8G8B8A8Unorm,
                    samples: 1,
                }
            },
            pass: {
                color: [a],
                depth_stencil: {},
                resolve: [b],
            }
        ).unwrap();
    }
}
<|endoftext|>