<commit_before>// Copyright 2014 Nick Fitzgerald
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Implementation of primitive procedures.

use environment::{ActivationPtr, Environment};
use heap::{Heap, Rooted};
use value::{RootedValue, SchemeResult, Value};

/// The function signature for primitives.
pub type PrimitiveFunction = fn(&mut Heap, Vec<RootedValue>) -> SchemeResult;

fn cons(heap: &mut Heap, args: Vec<RootedValue>) -> SchemeResult {
    if let [ref car, ref cdr] = args.as_slice() {
        Ok(Value::new_pair(heap, car, cdr))
    } else {
        Err("Bad arguments".to_string())
    }
}

fn car(heap: &mut Heap, args: Vec<RootedValue>) -> SchemeResult {
    if let [ref arg] = args.as_slice() {
        arg.car(heap).ok_or(
            format!("Cannot take car of non-cons: {}", **arg))
    } else {
        Err("Bad arguments".to_string())
    }
}

fn cdr(heap: &mut Heap, args: Vec<RootedValue>) -> SchemeResult {
    if let [ref arg] = args.as_slice() {
        arg.cdr(heap).ok_or(
            format!("Cannot take cdr of non-cons: {}", **arg))
    } else {
        Err("Bad arguments".to_string())
    }
}

fn list(heap: &mut Heap, args: Vec<RootedValue>) -> SchemeResult {
    use value;
    return Ok(value::list(heap, args.as_slice()));
}

fn print(heap: &mut Heap, args: Vec<RootedValue>) -> SchemeResult {
    for val in args.iter() {
        println!("{}", **val);
    }
    Ok(heap.unspecified_symbol())
}

fn null_question(heap: &mut Heap, args: Vec<RootedValue>) -> SchemeResult {
    if let [ref arg] = args.as_slice() {
        Ok(Rooted::new(heap, Value::new_boolean(**arg == Value::EmptyList)))
    } else {
        Err("Bad arguments".to_string())
    }
}

fn pair_question(heap: &mut Heap, args: Vec<RootedValue>) -> SchemeResult {
    if let [ref arg] = args.as_slice() {
        Ok(Rooted::new(heap, Value::new_boolean(match **arg {
            Value::Pair(_) => true,
            _              => false,
        })))
    } else {
        Err("Bad arguments".to_string())
    }
}

fn atom_question(heap: &mut Heap, args: Vec<RootedValue>) -> SchemeResult {
    if let [ref arg] = args.as_slice() {
        Ok(Rooted::new(heap, Value::new_boolean(match **arg {
            Value::Pair(_) => false,
            _              => true,
        })))
    } else {
        Err("Bad arguments".to_string())
    }
}

fn eq_question(heap: &mut Heap, args: Vec<RootedValue>) -> SchemeResult {
    if let [ref a, ref b] = args.as_slice() {
        Ok(Rooted::new(heap, Value::new_boolean(*a == *b)))
    } else {
        Err("Bad arguments".to_string())
    }
}

fn add(heap: &mut Heap, args: Vec<RootedValue>) -> SchemeResult {
    if let [ref a, ref b] = args.as_slice() {
        let first = try!(a.to_integer().ok_or(
            "Cannot use + with non-numbers".to_string()));
        let second = try!(b.to_integer().ok_or(
                     "Cannot use + with non-numbers".to_string()));
        Ok(Rooted::new(heap, Value::new_integer(first + second)))
    } else {
        Err("Bad arguments".to_string())
    }
}

fn subtract(heap: &mut Heap, args: Vec<RootedValue>) -> SchemeResult {
    if let [ref a, ref b] = args.as_slice() {
        let first = try!(a.to_integer().ok_or(
            "Cannot use - with non-numbers".to_string()));
        let second = try!(b.to_integer().ok_or(
                     "Cannot use - with non-numbers".to_string()));
        Ok(Rooted::new(heap, Value::new_integer(first - second)))
    } else {
        Err("Bad arguments".to_string())
    }
}

fn divide(heap: &mut Heap, args: Vec<RootedValue>) -> SchemeResult {
    if let [ref a, ref b] = args.as_slice() {
        let first = try!(a.to_integer().ok_or(
            "Cannot use / with non-numbers".to_string()));
        let second = try!(b.to_integer().ok_or(
                     "Cannot use / with non-numbers".to_string()));
        if second == 0 {
            return Err("Divide by zero".to_string());
        }
        Ok(Rooted::new(heap, Value::new_integer(first / second)))
    } else {
        Err("Bad arguments".to_string())
    }
}

fn multiply(heap: &mut Heap, args: Vec<RootedValue>) -> SchemeResult {
    if let [ref a, ref b] = args.as_slice() {
        let first = try!(a.to_integer().ok_or(
            "Cannot use * with non-numbers".to_string()));
        let second = try!(b.to_integer().ok_or(
                     "Cannot use * with non-numbers".to_string()));
        Ok(Rooted::new(heap, Value::new_integer(first * second)))
    } else {
        Err("Bad arguments".to_string())
    }
}

fn define_primitive(env: &mut Environment,
                    act: &mut ActivationPtr,
                    name: &'static str,
                    function: PrimitiveFunction) {
    let (i, j) = env.define(name.to_string());
    assert!(i == 0, "All primitives should be defined on the global activation");
    act.define(j, Value::new_primitive(name, function));
}

pub fn define_primitives(env: &mut Environment, act: &mut ActivationPtr) {
    define_primitive(env, act, "cons", cons);
    define_primitive(env, act, "car", car);
    define_primitive(env, act, "cdr", cdr);
    define_primitive(env, act, "list", list);

    define_primitive(env, act, "print", print);

    define_primitive(env, act, "null?", null_question);
    define_primitive(env, act, "pair?", pair_question);
    define_primitive(env, act, "atom?", atom_question);
    define_primitive(env, act, "eq?", eq_question);

    define_primitive(env, act, "+", add);
    define_primitive(env, act, "-", subtract);
    define_primitive(env, act, "/", divide);
    define_primitive(env, act, "*", multiply);
}

// TESTS -----------------------------------------------------------------------

#[test]
fn test_primitives_cons() {
    use eval::evaluate_file;

    let heap = &mut Heap::new();
    let result = evaluate_file(heap, "./tests/test_primitives_cons.scm")
        .ok()
        .expect("Should be able to eval a file.");
    let pair = result.to_pair(heap)
        .expect("Result should be a pair");
    assert_eq!(*pair.car(heap), Value::new_integer(1));
    assert_eq!(*pair.cdr(heap), Value::new_integer(2));
}

#[test]
fn test_primitives_car() {
    use eval::evaluate_file;

    let heap = &mut Heap::new();
    let result = evaluate_file(heap, "./tests/test_primitives_car.scm")
        .ok()
        .expect("Should be able to eval a file.");
    assert_eq!(*result, Value::new_integer(1));
}

#[test]
fn test_primitives_cdr() {
    use eval::evaluate_file;

    let heap = &mut Heap::new();
    let result = evaluate_file(heap, "./tests/test_primitives_cdr.scm")
        .ok()
        .expect("Should be able to eval a file.");
    assert_eq!(*result, Value::new_integer(2));
}

#[test]
fn test_primitives_list() {
    use eval::evaluate_file;

    let heap = &mut Heap::new();
    let result = evaluate_file(heap, "./tests/test_primitives_list.scm")
        .ok()
        .expect("Should be able to eval a file.");
    let pair = result.to_pair(heap)
        .expect("Result should be a pair");
    assert_eq!(*pair.car(heap),
               Value::new_integer(1));
    assert_eq!(*pair.cadr(heap).ok().expect("pair.cadr"),
               Value::new_integer(2));
    assert_eq!(*pair.caddr(heap).ok().expect("pair.caddr"),
               Value::new_integer(3));
    assert_eq!(*pair.cdddr(heap).ok().expect("pair.cdddr"),
               Value::EmptyList);
}

#[test]
fn test_primitives_null() {
    use eval::evaluate_file;

    let heap = &mut Heap::new();
    let result = evaluate_file(heap, "./tests/test_primitives_null.scm")
        .ok()
        .expect("Should be able to eval a file.");
    let pair = result.to_pair(heap)
        .expect("Result should be a pair");
    assert_eq!(*pair.car(heap), Value::new_boolean(true));
    assert_eq!(*pair.cdr(heap), Value::new_boolean(false));
}

#[test]
fn test_primitives_arithmetic() {
    use eval::evaluate_file;

    let heap = &mut Heap::new();
    let result = evaluate_file(heap, "./tests/test_primitives_arithmetic.scm")
        .ok()
        .expect("Should be able to eval a file.");
    assert_eq!(*result, Value::new_integer(42));
}

#[test]
fn test_primitives_pair() {
    use eval::evaluate_file;

    let heap = &mut Heap::new();
    let result = evaluate_file(heap, "./tests/test_primitives_pair.scm")
        .ok()
        .expect("Should be able to eval a file.");
    let pair = result.to_pair(heap)
        .expect("Result should be a pair");
    assert_eq!(*pair.car(heap), Value::new_boolean(true));
    assert_eq!(*pair.cdr(heap), Value::new_boolean(false));
}

#[test]
fn test_primitives_atom() {
    use eval::evaluate_file;

    let heap = &mut Heap::new();
    let result = evaluate_file(heap, "./tests/test_primitives_atom.scm")
        .ok()
        .expect("Should be able to eval a file.");
    let pair = result.to_pair(heap)
        .expect("Result should be a pair");
    assert_eq!(*pair.car(heap), Value::new_boolean(true));
    assert_eq!(*pair.cdr(heap), Value::new_boolean(false));
}

#[test]
fn test_primitives_eq() {
    use eval::evaluate_file;

    let heap = &mut Heap::new();
    let result = evaluate_file(heap, "./tests/test_primitives_eq.scm")
        .ok()
        .expect("Should be able to eval a file.");
    let pair = result.to_pair(heap)
        .expect("Result should be a pair");
    assert_eq!(*pair.car(heap), Value::new_boolean(true));
    assert_eq!(*pair.cdr(heap), Value::new_boolean(false));
}
<commit_msg>Make the primitives module have a tests submodule<commit_after>// Copyright 2014 Nick Fitzgerald
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Implementation of primitive procedures.

use environment::{ActivationPtr, Environment};
use heap::{Heap, Rooted};
use value::{RootedValue, SchemeResult, Value};

/// The function signature for primitives.
pub type PrimitiveFunction = fn(&mut Heap, Vec<RootedValue>) -> SchemeResult;

fn cons(heap: &mut Heap, args: Vec<RootedValue>) -> SchemeResult {
    if let [ref car, ref cdr] = args.as_slice() {
        Ok(Value::new_pair(heap, car, cdr))
    } else {
        Err("Bad arguments".to_string())
    }
}

fn car(heap: &mut Heap, args: Vec<RootedValue>) -> SchemeResult {
    if let [ref arg] = args.as_slice() {
        arg.car(heap).ok_or(
            format!("Cannot take car of non-cons: {}", **arg))
    } else {
        Err("Bad arguments".to_string())
    }
}

fn cdr(heap: &mut Heap, args: Vec<RootedValue>) -> SchemeResult {
    if let [ref arg] = args.as_slice() {
        arg.cdr(heap).ok_or(
            format!("Cannot take cdr of non-cons: {}", **arg))
    } else {
        Err("Bad arguments".to_string())
    }
}

fn list(heap: &mut Heap, args: Vec<RootedValue>) -> SchemeResult {
    use value;
    return Ok(value::list(heap, args.as_slice()));
}

fn print(heap: &mut Heap, args: Vec<RootedValue>) -> SchemeResult {
    for val in args.iter() {
        println!("{}", **val);
    }
    Ok(heap.unspecified_symbol())
}

fn null_question(heap: &mut Heap, args: Vec<RootedValue>) -> SchemeResult {
    if let [ref arg] = args.as_slice() {
        Ok(Rooted::new(heap, Value::new_boolean(**arg == Value::EmptyList)))
    } else {
        Err("Bad arguments".to_string())
    }
}

fn pair_question(heap: &mut Heap, args: Vec<RootedValue>) -> SchemeResult {
    if let [ref arg] = args.as_slice() {
        Ok(Rooted::new(heap, Value::new_boolean(match **arg {
            Value::Pair(_) => true,
            _              => false,
        })))
    } else {
        Err("Bad arguments".to_string())
    }
}

fn atom_question(heap: &mut Heap, args: Vec<RootedValue>) -> SchemeResult {
    if let [ref arg] = args.as_slice() {
        Ok(Rooted::new(heap, Value::new_boolean(match **arg {
            Value::Pair(_) => false,
            _              => true,
        })))
    } else {
        Err("Bad arguments".to_string())
    }
}

fn eq_question(heap: &mut Heap, args: Vec<RootedValue>) -> SchemeResult {
    if let [ref a, ref b] = args.as_slice() {
        Ok(Rooted::new(heap, Value::new_boolean(*a == *b)))
    } else {
        Err("Bad arguments".to_string())
    }
}

fn add(heap: &mut Heap, args: Vec<RootedValue>) -> SchemeResult {
    if let [ref a, ref b] = args.as_slice() {
        let first = try!(a.to_integer().ok_or(
            "Cannot use + with non-numbers".to_string()));
        let second = try!(b.to_integer().ok_or(
                     "Cannot use + with non-numbers".to_string()));
        Ok(Rooted::new(heap, Value::new_integer(first + second)))
    } else {
        Err("Bad arguments".to_string())
    }
}

fn subtract(heap: &mut Heap, args: Vec<RootedValue>) -> SchemeResult {
    if let [ref a, ref b] = args.as_slice() {
        let first = try!(a.to_integer().ok_or(
            "Cannot use - with non-numbers".to_string()));
        let second = try!(b.to_integer().ok_or(
                     "Cannot use - with non-numbers".to_string()));
        Ok(Rooted::new(heap, Value::new_integer(first - second)))
    } else {
        Err("Bad arguments".to_string())
    }
}

fn divide(heap: &mut Heap, args: Vec<RootedValue>) -> SchemeResult {
    if let [ref a, ref b] = args.as_slice() {
        let first = try!(a.to_integer().ok_or(
            "Cannot use / with non-numbers".to_string()));
        let second = try!(b.to_integer().ok_or(
                     "Cannot use / with non-numbers".to_string()));
        if second == 0 {
            return Err("Divide by zero".to_string());
        }
        Ok(Rooted::new(heap, Value::new_integer(first / second)))
    } else {
        Err("Bad arguments".to_string())
    }
}

fn multiply(heap: &mut Heap, args: Vec<RootedValue>) -> SchemeResult {
    if let [ref a, ref b] = args.as_slice() {
        let first = try!(a.to_integer().ok_or(
            "Cannot use * with non-numbers".to_string()));
        let second = try!(b.to_integer().ok_or(
                     "Cannot use * with non-numbers".to_string()));
        Ok(Rooted::new(heap, Value::new_integer(first * second)))
    } else {
        Err("Bad arguments".to_string())
    }
}

fn define_primitive(env: &mut Environment,
                    act: &mut ActivationPtr,
                    name: &'static str,
                    function: PrimitiveFunction) {
    let (i, j) = env.define(name.to_string());
    assert!(i == 0, "All primitives should be defined on the global activation");
    act.define(j, Value::new_primitive(name, function));
}

pub fn define_primitives(env: &mut Environment, act: &mut ActivationPtr) {
    define_primitive(env, act, "cons", cons);
    define_primitive(env, act, "car", car);
    define_primitive(env, act, "cdr", cdr);
    define_primitive(env, act, "list", list);

    define_primitive(env, act, "print", print);

    define_primitive(env, act, "null?", null_question);
    define_primitive(env, act, "pair?", pair_question);
    define_primitive(env, act, "atom?", atom_question);
    define_primitive(env, act, "eq?", eq_question);

    define_primitive(env, act, "+", add);
    define_primitive(env, act, "-", subtract);
    define_primitive(env, act, "/", divide);
    define_primitive(env, act, "*", multiply);
}

// TESTS -----------------------------------------------------------------------

#[cfg(test)]
mod tests {
    use heap::{Heap};
    use value::{Value};

    #[test]
    fn test_primitives_cons() {
        use eval::evaluate_file;

        let heap = &mut Heap::new();
        let result = evaluate_file(heap, "./tests/test_primitives_cons.scm")
            .ok()
            .expect("Should be able to eval a file.");
        let pair = result.to_pair(heap)
            .expect("Result should be a pair");
        assert_eq!(*pair.car(heap), Value::new_integer(1));
        assert_eq!(*pair.cdr(heap), Value::new_integer(2));
    }

    #[test]
    fn test_primitives_car() {
        use eval::evaluate_file;

        let heap = &mut Heap::new();
        let result = evaluate_file(heap, "./tests/test_primitives_car.scm")
            .ok()
            .expect("Should be able to eval a file.");
        assert_eq!(*result, Value::new_integer(1));
    }

    #[test]
    fn test_primitives_cdr() {
        use eval::evaluate_file;

        let heap = &mut Heap::new();
        let result = evaluate_file(heap, "./tests/test_primitives_cdr.scm")
            .ok()
            .expect("Should be able to eval a file.");
        assert_eq!(*result, Value::new_integer(2));
    }

    #[test]
    fn test_primitives_list() {
        use eval::evaluate_file;

        let heap = &mut Heap::new();
        let result = evaluate_file(heap, "./tests/test_primitives_list.scm")
            .ok()
            .expect("Should be able to eval a file.");
        let pair = result.to_pair(heap)
            .expect("Result should be a pair");
        assert_eq!(*pair.car(heap),
                   Value::new_integer(1));
        assert_eq!(*pair.cadr(heap).ok().expect("pair.cadr"),
                   Value::new_integer(2));
        assert_eq!(*pair.caddr(heap).ok().expect("pair.caddr"),
                   Value::new_integer(3));
        assert_eq!(*pair.cdddr(heap).ok().expect("pair.cdddr"),
                   Value::EmptyList);
    }

    #[test]
    fn test_primitives_null() {
        use eval::evaluate_file;

        let heap = &mut Heap::new();
        let result = evaluate_file(heap, "./tests/test_primitives_null.scm")
            .ok()
            .expect("Should be able to eval a file.");
        let pair = result.to_pair(heap)
            .expect("Result should be a pair");
        assert_eq!(*pair.car(heap), Value::new_boolean(true));
        assert_eq!(*pair.cdr(heap), Value::new_boolean(false));
    }

    #[test]
    fn test_primitives_arithmetic() {
        use eval::evaluate_file;

        let heap = &mut Heap::new();
        let result = evaluate_file(heap, "./tests/test_primitives_arithmetic.scm")
            .ok()
            .expect("Should be able to eval a file.");
        assert_eq!(*result, Value::new_integer(42));
    }

    #[test]
    fn test_primitives_pair() {
        use eval::evaluate_file;

        let heap = &mut Heap::new();
        let result = evaluate_file(heap, "./tests/test_primitives_pair.scm")
            .ok()
            .expect("Should be able to eval a file.");
        let pair = result.to_pair(heap)
            .expect("Result should be a pair");
        assert_eq!(*pair.car(heap), Value::new_boolean(true));
        assert_eq!(*pair.cdr(heap), Value::new_boolean(false));
    }

    #[test]
    fn test_primitives_atom() {
        use eval::evaluate_file;

        let heap = &mut Heap::new();
        let result = evaluate_file(heap, "./tests/test_primitives_atom.scm")
            .ok()
            .expect("Should be able to eval a file.");
        let pair = result.to_pair(heap)
            .expect("Result should be a pair");
        assert_eq!(*pair.car(heap), Value::new_boolean(true));
        assert_eq!(*pair.cdr(heap), Value::new_boolean(false));
    }

    #[test]
    fn test_primitives_eq() {
        use eval::evaluate_file;

        let heap = &mut Heap::new();
        let result = evaluate_file(heap, "./tests/test_primitives_eq.scm")
            .ok()
            .expect("Should be able to eval a file.");
        let pair = result.to_pair(heap)
            .expect("Result should be a pair");
        assert_eq!(*pair.car(heap), Value::new_boolean(true));
        assert_eq!(*pair.cdr(heap), Value::new_boolean(false));
    }
}
<|endoftext|>


<commit_before>// Copyright 2014 The Gfx-rs Developers.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#![crate_name = "render"]
#![comment = "A platform independent renderer for gfx-rs."]
#![license = "ASL2"]
#![crate_type = "lib"]

#![feature(macro_rules, phase)]

#[phase(plugin, link)] extern crate log;
extern crate comm;
extern crate device;

use std::fmt::Show;
use std::vec::MoveItems;

use backend = device::dev;
use device::shade::{CreateShaderError, ProgramMeta, Vertex, Fragment, UniformValue, ShaderSource};
use device::target::{ClearData, TargetColor, TargetDepth, TargetStencil};
use envir::BindableStorage;
use resource::Pending;

pub type BufferHandle = uint;
pub type SurfaceHandle = backend::Surface;
pub type TextureHandle = backend::Texture;
pub type SamplerHandle = uint;
pub type ShaderHandle = uint;
pub type ProgramHandle = uint;
pub type EnvirHandle = uint;

pub mod envir;
pub mod mesh;
pub mod rast;
pub mod resource;
pub mod target;

pub type Token = uint;
pub type RequestChannel = Sender<device::Request<Token>>;
pub type ReceiveChannel = Receiver<device::Reply<Token>>;

/// Graphics state
struct State {
    frame: target::Frame,
}

#[deriving(Clone, Show)]
pub enum DeviceError {
    ErrorNewBuffer(BufferHandle),
    ErrorNewArrayBuffer,
    ErrorNewShader(ShaderHandle, CreateShaderError),
    ErrorNewProgram(ProgramHandle),
    ErrorNewFrameBuffer,
}

#[deriving(Show)]
pub enum DrawError<'a> {
    ErrorMesh(MeshError),
    ErrorProgram,
    ErrorEnvironment(envir::OptimizeError<'a>),
}

#[deriving(Show)]
pub enum MeshError {
    ErrorAttributeMissing,
    ErrorAttributeType,
}

pub struct Renderer {
    device_tx: RequestChannel,
    device_rx: ReceiveChannel,
    swap_ack: Receiver<device::Ack>,
    should_finish: comm::ShouldClose,
    device_errors: Vec<DeviceError>,
    /// the default FBO for drawing
    default_frame_buffer: backend::FrameBuffer,
    /// cached meta-data for meshes and programs
    resource: resource::Cache,
    environments: Vec<envir::Storage>,
    /// current state
    state: State,
}

/// Resource-oriented private methods
impl Renderer {
    /// Make sure the resource is loaded. Optimally, we'd like this method to return
    /// the resource reference, but the borrow checker doesn't like the match over `Future`
    /// inside the body.
    fn demand(channel: &ReceiveChannel, resource: &mut resource::Cache,
            errors: &mut Vec<DeviceError>, fn_ready: |&resource::Cache| -> bool) {
        while !fn_ready(resource) {
            let reply = channel.recv();
            match resource.process(reply) {
                Ok(_) => (),
                Err(e) => errors.push(e),
            }
        }
    }

    /// Get a guaranteed copy of a specific resource accessed by the function.
    fn get_any<R: Copy, E: Show>(&mut self, fun: <'a>|&'a resource::Cache| -> &'a resource::Future<R, E>) -> R {
        Renderer::demand(&self.device_rx, &mut self.resource, &mut self.device_errors, |res| !fun(res).is_pending());
        *fun(&self.resource).unwrap()
    }

    fn get_buffer(&mut self, handle: BufferHandle) -> backend::Buffer {
        self.get_any(|res| res.buffers.get(handle))
    }

    fn get_common_array_buffer(&mut self) -> backend::ArrayBuffer {
        self.get_any(|res| res.array_buffers.get(0))
    }

    fn get_shader(&mut self, handle: ShaderHandle) -> backend::Shader {
        self.get_any(|res| res.shaders.get(handle))
    }

    fn get_common_frame_buffer(&mut self) -> backend::FrameBuffer {
        self.get_any(|res| res.frame_buffers.get(0))
    }
}

/// Graphics-oriented methods
impl Renderer {
    pub fn new(device_tx: RequestChannel, device_rx: ReceiveChannel,
            swap_rx: Receiver<device::Ack>, should_finish: comm::ShouldClose) -> Renderer {
        // Request the creation of the common array buffer and frame buffer
        let mut res = resource::Cache::new();
        res.array_buffers.push(Pending);
        res.frame_buffers.push(Pending);
        device_tx.send(device::Call(0, device::CreateArrayBuffer));
        device_tx.send(device::Call(0, device::CreateFrameBuffer));
        // Return
        Renderer {
            device_tx: device_tx,
            device_rx: device_rx,
            swap_ack: swap_rx,
            should_finish: should_finish,
            device_errors: Vec::new(),
            default_frame_buffer: 0,
            resource: res,
            environments: Vec::new(),
            state: State {
                frame: target::Frame::new(),
            },
        }
    }

    /// Ask the device to create something for us
    fn call(&self, token: Token, msg: device::CallRequest) {
        self.device_tx.send(device::Call(token, msg));
    }

    /// Ask the device to do something for us
    fn cast(&self, msg: device::CastRequest) {
        self.device_tx.send(device::Cast(msg));
    }

    pub fn should_finish(&self) -> bool {
        self.should_finish.check()
    }

    pub fn iter_errors(&mut self) -> MoveItems<DeviceError> {
        let errors = self.device_errors.clone();
        self.device_errors.clear();
        errors.move_iter()
    }

    pub fn clear(&mut self, data: ClearData, frame: target::Frame) {
        self.bind_frame(&frame);
        self.cast(device::Clear(data));
    }

    pub fn draw<'a>(&'a mut self, mesh: &mesh::Mesh, slice: mesh::Slice, frame: target::Frame,
            program_handle: ProgramHandle, env_handle: EnvirHandle, state: rast::DrawState)
            -> Result<(), DrawError<'a>> {
        // demand resources. This section needs the mutable self, so we are unable to do this
        // after we get a reference to ether the `Environment` or the `ProgramMeta`
        self.prebind_mesh(mesh);
        Renderer::demand(&self.device_rx, &mut self.resource, &mut self.device_errors,
            |res| !res.programs.get(program_handle).is_pending());
        // bind state
        self.cast(device::SetPrimitiveState(state.primitive));
        self.cast(device::SetDepthStencilState(state.depth, state.stencil,
            state.primitive.get_cull_mode()));
        self.cast(device::SetBlendState(state.blend));
        // bind array buffer
        let vao = self.get_common_array_buffer();
        self.cast(device::BindArrayBuffer(vao));
        // bind output frame
        self.bind_frame(&frame);
        // bind shaders
        let env = self.environments.get(env_handle);
        // prebind the environment (unable to make it a method of self...)
        for handle in env.iter_buffers() {
            Renderer::demand(&self.device_rx, &mut self.resource, &mut self.device_errors,
                |res| !res.buffers.get(handle).is_pending());
        }
        let program = match *self.resource.programs.get(program_handle) {
            resource::Pending => fail!("Program is not loaded yet"),
            resource::Loaded(ref p) => p,
            resource::Failed(_) => return Err(ErrorProgram),
        };
        match env.optimize(program) {
            Ok(ref cut) => self.bind_environment(env, cut, program),
            Err(err) => return Err(ErrorEnvironment(err)),
        }
        // bind vertex attributes
        match self.bind_mesh(mesh, program) {
            Ok(_) => (),
            Err(e) => return Err(ErrorMesh(e)),
        }
        // draw
        match slice {
            mesh::VertexSlice(start, end) => {
                self.cast(device::Draw(start, end));
            },
            mesh::IndexSlice(buf, start, end) => {
                self.cast(device::BindIndex(buf));
                self.cast(device::DrawIndexed(start, end));
            },
        }
        Ok(())
    }

    pub fn end_frame(&self) {
        self.device_tx.send(device::SwapBuffers);
        self.swap_ack.recv();  //wait for acknowlegement
    }

    pub fn create_program(&mut self, vs_src: ShaderSource, fs_src: ShaderSource) -> ProgramHandle {
        let id = self.resource.shaders.len();
        self.resource.shaders.push(Pending);
        self.resource.shaders.push(Pending);
        self.call(id + 0, device::CreateShader(Vertex, vs_src));
        self.call(id + 1, device::CreateShader(Fragment, fs_src));
        let h_vs = self.get_shader(id + 0);
        let h_fs = self.get_shader(id + 1);
        let token = self.resource.programs.len();
        self.call(token, device::CreateProgram(vec![h_vs, h_fs]));
        self.resource.programs.push(Pending);
        token
    }

    pub fn create_buffer<T: Send>(&mut self, data: Option<Vec<T>>) -> BufferHandle {
        let token = self.resource.buffers.len();
        let blob = data.map(|v| (box v) as Box<device::Blob + Send>);
        self.call(token, device::CreateBuffer(blob));
        self.resource.buffers.push(Pending);
        token
    }

    pub fn create_environment(&mut self, storage: envir::Storage) -> EnvirHandle {
        let handle = self.environments.len();
        self.environments.push(storage);
        handle
    }

    pub fn set_env_block(&mut self, handle: EnvirHandle, var: envir::BlockVar, buf: BufferHandle) {
        self.environments.get_mut(handle).set_block(var, buf);
    }

    pub fn set_env_uniform(&mut self, handle: EnvirHandle, var: envir::UniformVar, value: UniformValue) {
        self.environments.get_mut(handle).set_uniform(var, value);
    }

    pub fn set_env_texture(&mut self, handle: EnvirHandle, var: envir::TextureVar, texture: TextureHandle, sampler: SamplerHandle) {
        self.environments.get_mut(handle).set_texture(var, texture, sampler);
    }

    pub fn update_buffer_vec<T: Send>(&mut self, handle: BufferHandle, data: Vec<T>) {
        let buf = self.get_buffer(handle);
        self.cast(device::UpdateBuffer(buf, (box data) as Box<device::Blob + Send>));
    }

    pub fn update_buffer_struct<T: device::Blob+Send>(&mut self, handle: BufferHandle, data: T) {
        let buf = self.get_buffer(handle);
        self.cast(device::UpdateBuffer(buf, (box data) as Box<device::Blob + Send>));
    }

    fn bind_frame(&mut self, frame: &target::Frame) {
        if frame.is_default() {
            // binding the default FBO, not touching our common one
            self.cast(device::BindFrameBuffer(self.default_frame_buffer));
        } else {
            let fbo = self.get_common_frame_buffer();
            self.cast(device::BindFrameBuffer(fbo));
            for (i, (cur, new)) in self.state.frame.colors.iter().zip(frame.colors.iter()).enumerate() {
                if *cur != *new {
                    self.cast(device::BindTarget(TargetColor(i as u8), *new));
                }
            }
            if self.state.frame.depth != frame.depth {
                self.cast(device::BindTarget(TargetDepth, frame.depth));
            }
            if self.state.frame.stencil != frame.stencil {
                self.cast(device::BindTarget(TargetStencil, frame.stencil));
            }
            self.state.frame = *frame;
        }
    }

    /// Make sure all the mesh buffers are successfully created/loaded
    fn prebind_mesh(&mut self, mesh: &mesh::Mesh) {
        for at in mesh.attributes.iter() {
            self.get_buffer(at.buffer);
        }
    }

    fn bind_mesh(&self, mesh: &mesh::Mesh, prog: &ProgramMeta) -> Result<(),MeshError> {
        for sat in prog.attributes.iter() {
            match mesh.attributes.iter().find(|a| a.name.as_slice() == sat.name.as_slice()) {
                Some(vat) => match vat.elem_type.is_compatible(sat.base_type) {
                    Ok(_) => self.cast(device::BindAttribute(
                        sat.location as device::AttributeSlot,
                        *self.resource.buffers.get(vat.buffer).unwrap(),
                        vat.elem_count, vat.elem_type, vat.stride, vat.offset)),
                    Err(_) => return Err(ErrorAttributeType)
                },
                None => return Err(ErrorAttributeMissing)
            }
        }
        Ok(())
    }

    fn bind_environment(&self, storage: &envir::Storage, shortcut: &envir::Shortcut, program: &ProgramMeta) {
        debug_assert!(storage.is_fit(shortcut, program));
        self.cast(device::BindProgram(program.name));

        for (i, (&k, block_var)) in shortcut.blocks.iter().zip(program.blocks.iter()).enumerate() {
            let handle = storage.get_block(k);
            let block = *self.resource.buffers.get(handle).unwrap();
            block_var.active_slot.set(i as u8);
            self.cast(device::BindUniformBlock(program.name, i as u8, i as device::UniformBufferSlot, block));
        }

        for (&k, uniform_var) in shortcut.uniforms.iter().zip(program.uniforms.iter()) {
            let value = storage.get_uniform(k);
            uniform_var.active_value.set(value);
            self.cast(device::BindUniform(uniform_var.location, value));
        }

        for (_i, (&_k, _texture)) in shortcut.textures.iter().zip(program.textures.iter()).enumerate() {
            unimplemented!()
        }
    }
}
<commit_msg>Dispatcher implemented as a part of Renderer<commit_after>// Copyright 2014 The Gfx-rs Developers.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#![crate_name = "render"]
#![comment = "A platform independent renderer for gfx-rs."]
#![license = "ASL2"]
#![crate_type = "lib"]

#![feature(macro_rules, phase)]

#[phase(plugin, link)] extern crate log;
extern crate comm;
extern crate device;

use std::fmt::Show;
use std::vec::MoveItems;

use backend = device::dev;
use device::shade::{CreateShaderError, ProgramMeta, Vertex, Fragment, UniformValue, ShaderSource};
use device::target::{ClearData, TargetColor, TargetDepth, TargetStencil};
use envir::BindableStorage;
use resource::Pending;

pub type BufferHandle = uint;
pub type SurfaceHandle = backend::Surface;
pub type TextureHandle = backend::Texture;
pub type SamplerHandle = uint;
pub type ShaderHandle = uint;
pub type ProgramHandle = uint;
pub type EnvirHandle = uint;

pub mod envir;
pub mod mesh;
pub mod rast;
pub mod resource;
pub mod target;

pub type Token = uint;

/// Graphics state
struct State {
    frame: target::Frame,
}

#[deriving(Clone, Show)]
pub enum DeviceError {
    ErrorNewBuffer(BufferHandle),
    ErrorNewArrayBuffer,
    ErrorNewShader(ShaderHandle, CreateShaderError),
    ErrorNewProgram(ProgramHandle),
    ErrorNewFrameBuffer,
}

#[deriving(Show)]
pub enum DrawError<'a> {
    ErrorMesh(MeshError),
    ErrorProgram,
    ErrorEnvironment(envir::OptimizeError<'a>),
}

#[deriving(Show)]
pub enum MeshError {
    ErrorAttributeMissing,
    ErrorAttributeType,
}

struct Dispatcher {
    /// Channel to receive device messages
    channel: Receiver<device::Reply<Token>>,
    /// Asynchronous device error queue
    pub errors: Vec<DeviceError>,
    /// cached meta-data for meshes and programs
    pub resource: resource::Cache,
}

impl Dispatcher {
    /// Make sure the resource is loaded. Optimally, we'd like this method to return
    /// the resource reference, but the borrow checker doesn't like the match over `Future`
    /// inside the body.
    fn demand(&mut self, fn_ready: |&resource::Cache| -> bool) {
        while !fn_ready(&self.resource) {
            let reply = self.channel.recv();
            match self.resource.process(reply) {
                Ok(_) => (),
                Err(e) => self.errors.push(e),
            }
        }
    }

    /// Get a guaranteed copy of a specific resource accessed by the function.
    fn get_any<R: Copy, E: Show>(&mut self, fun: <'a>|&'a resource::Cache| -> &'a resource::Future<R, E>) -> R {
        self.demand(|res| !fun(res).is_pending());
        *fun(&self.resource).unwrap()
    }

    fn get_buffer(&mut self, handle: BufferHandle) -> backend::Buffer {
        self.get_any(|res| res.buffers.get(handle))
    }

    fn get_common_array_buffer(&mut self) -> backend::ArrayBuffer {
        self.get_any(|res| res.array_buffers.get(0))
    }

    fn get_shader(&mut self, handle: ShaderHandle) -> backend::Shader {
        self.get_any(|res| res.shaders.get(handle))
    }

    fn get_common_frame_buffer(&mut self) -> backend::FrameBuffer {
        self.get_any(|res| res.frame_buffers.get(0))
    }
}

pub struct Renderer {
    dispatcher: Dispatcher,
    device_tx: Sender<device::Request<Token>>,
    swap_ack: Receiver<device::Ack>,
    should_finish: comm::ShouldClose,
    /// the default FBO for drawing
    default_frame_buffer: backend::FrameBuffer,
    environments: Vec<envir::Storage>,
    /// current state
    state: State,
}

impl Renderer {
    pub fn new(device_tx: Sender<device::Request<Token>>, device_rx: Receiver<device::Reply<Token>>,
            swap_rx: Receiver<device::Ack>, should_finish: comm::ShouldClose) -> Renderer {
        // Request the creation of the common array buffer and frame buffer
        let mut res = resource::Cache::new();
        res.array_buffers.push(Pending);
        res.frame_buffers.push(Pending);
        device_tx.send(device::Call(0, device::CreateArrayBuffer));
        device_tx.send(device::Call(0, device::CreateFrameBuffer));
        // Return
        Renderer {
            dispatcher: Dispatcher {
                channel: device_rx,
                errors: Vec::new(),
                resource: res,
            },
            device_tx: device_tx,
            swap_ack: swap_rx,
            should_finish: should_finish,
            default_frame_buffer: 0,
            environments: Vec::new(),
            state: State {
                frame: target::Frame::new(),
            },
        }
    }

    /// Ask the device to do something for us
    fn cast(&self, msg: device::CastRequest) {
        self.device_tx.send(device::Cast(msg));
    }

    pub fn should_finish(&self) -> bool {
        self.should_finish.check()
    }

    pub fn iter_errors(&mut self) -> MoveItems<DeviceError> {
        let errors = self.dispatcher.errors.clone();
        self.dispatcher.errors.clear();
        errors.move_iter()
    }

    pub fn clear(&mut self, data: ClearData, frame: target::Frame) {
        self.bind_frame(&frame);
        self.cast(device::Clear(data));
    }

    pub fn draw<'a>(&'a mut self, mesh: &mesh::Mesh, slice: mesh::Slice, frame: target::Frame,
            program_handle: ProgramHandle, env_handle: EnvirHandle, state: rast::DrawState)
            -> Result<(), DrawError<'a>> {
        // demand resources. This section needs the mutable self, so we are unable to do this
        // after we get a reference to ether the `Environment` or the `ProgramMeta`
        self.prebind_mesh(mesh);
        self.dispatcher.demand(|res| !res.programs.get(program_handle).is_pending());
        // bind state
        self.cast(device::SetPrimitiveState(state.primitive));
        self.cast(device::SetDepthStencilState(state.depth, state.stencil,
            state.primitive.get_cull_mode()));
        self.cast(device::SetBlendState(state.blend));
        // bind array buffer
        let vao = self.dispatcher.get_common_array_buffer();
        self.cast(device::BindArrayBuffer(vao));
        // bind output frame
        self.bind_frame(&frame);
        // bind shaders
        let env = self.environments.get(env_handle);
        // prebind the environment (unable to make it a method of self...)
        for handle in env.iter_buffers() {
            self.dispatcher.demand(|res| !res.buffers.get(handle).is_pending());
        }
        let program = match *self.dispatcher.resource.programs.get(program_handle) {
            resource::Pending => fail!("Program is not loaded yet"),
            resource::Loaded(ref p) => p,
            resource::Failed(_) => return Err(ErrorProgram),
        };
        match env.optimize(program) {
            Ok(ref cut) => self.bind_environment(env, cut, program),
            Err(err) => return Err(ErrorEnvironment(err)),
        }
        // bind vertex attributes
        match self.bind_mesh(mesh, program) {
            Ok(_) => (),
            Err(e) => return Err(ErrorMesh(e)),
        }
        // draw
        match slice {
            mesh::VertexSlice(start, end) => {
                self.cast(device::Draw(start, end));
            },
            mesh::IndexSlice(buf, start, end) => {
                self.cast(device::BindIndex(buf));
                self.cast(device::DrawIndexed(start, end));
            },
        }
        Ok(())
    }

    pub fn end_frame(&self) {
        self.device_tx.send(device::SwapBuffers);
        self.swap_ack.recv();  //wait for acknowlegement
    }

    pub fn create_program(&mut self, vs_src: ShaderSource, fs_src: ShaderSource) -> ProgramHandle {
        let ds = &mut self.dispatcher;
        let id = ds.resource.shaders.len();
        ds.resource.shaders.push(Pending);
        ds.resource.shaders.push(Pending);
        self.device_tx.send(device::Call(id + 0, device::CreateShader(Vertex, vs_src)));
        self.device_tx.send(device::Call(id + 1, device::CreateShader(Fragment, fs_src)));
        let h_vs = ds.get_shader(id + 0);
        let h_fs = ds.get_shader(id + 1);
        let token = ds.resource.programs.len();
        self.device_tx.send(device::Call(token, device::CreateProgram(vec![h_vs, h_fs])));
        ds.resource.programs.push(Pending);
        token
    }

    pub fn create_buffer<T: Send>(&mut self, data: Option<Vec<T>>) -> BufferHandle {
        let bufs = &mut self.dispatcher.resource.buffers;
        let token = bufs.len();
        let blob = data.map(|v| (box v) as Box<device::Blob + Send>);
        self.device_tx.send(device::Call(token, device::CreateBuffer(blob)));
        bufs.push(Pending);
        token
    }

    pub fn create_environment(&mut self, storage: envir::Storage) -> EnvirHandle {
        let handle = self.environments.len();
        self.environments.push(storage);
        handle
    }

    pub fn set_env_block(&mut self, handle: EnvirHandle, var: envir::BlockVar, buf: BufferHandle) {
        self.environments.get_mut(handle).set_block(var, buf);
    }

    pub fn set_env_uniform(&mut self, handle: EnvirHandle, var: envir::UniformVar, value: UniformValue) {
        self.environments.get_mut(handle).set_uniform(var, value);
    }

    pub fn set_env_texture(&mut self, handle: EnvirHandle, var: envir::TextureVar, texture: TextureHandle, sampler: SamplerHandle) {
        self.environments.get_mut(handle).set_texture(var, texture, sampler);
    }

    pub fn update_buffer_vec<T: Send>(&mut self, handle: BufferHandle, data: Vec<T>) {
        let buf = self.dispatcher.get_buffer(handle);
        self.cast(device::UpdateBuffer(buf, (box data) as Box<device::Blob + Send>));
    }

    pub fn update_buffer_struct<T: device::Blob+Send>(&mut self, handle: BufferHandle, data: T) {
        let buf = self.dispatcher.get_buffer(handle);
        self.cast(device::UpdateBuffer(buf, (box data) as Box<device::Blob + Send>));
    }

    fn bind_frame(&mut self, frame: &target::Frame) {
        if frame.is_default() {
            // binding the default FBO, not touching our common one
            self.cast(device::BindFrameBuffer(self.default_frame_buffer));
        } else {
            let fbo = self.dispatcher.get_common_frame_buffer();
            self.cast(device::BindFrameBuffer(fbo));
            for (i, (cur, new)) in self.state.frame.colors.iter().zip(frame.colors.iter()).enumerate() {
                if *cur != *new {
                    self.cast(device::BindTarget(TargetColor(i as u8), *new));
                }
            }
            if self.state.frame.depth != frame.depth {
                self.cast(device::BindTarget(TargetDepth, frame.depth));
            }
            if self.state.frame.stencil != frame.stencil {
                self.cast(device::BindTarget(TargetStencil, frame.stencil));
            }
            self.state.frame = *frame;
        }
    }

    /// Make sure all the mesh buffers are successfully created/loaded
    fn prebind_mesh(&mut self, mesh: &mesh::Mesh) {
        for at in mesh.attributes.iter() {
            self.dispatcher.get_buffer(at.buffer);
        }
    }

    fn bind_mesh(&self, mesh: &mesh::Mesh, prog: &ProgramMeta) -> Result<(),MeshError> {
        for sat in prog.attributes.iter() {
            match mesh.attributes.iter().find(|a| a.name.as_slice() == sat.name.as_slice()) {
                Some(vat) => match vat.elem_type.is_compatible(sat.base_type) {
                    Ok(_) => self.cast(device::BindAttribute(
                        sat.location as device::AttributeSlot,
                        *self.dispatcher.resource.buffers.get(vat.buffer).unwrap(),
                        vat.elem_count, vat.elem_type, vat.stride, vat.offset)),
                    Err(_) => return Err(ErrorAttributeType)
                },
                None => return Err(ErrorAttributeMissing)
            }
        }
        Ok(())
    }

    fn bind_environment(&self, storage: &envir::Storage, shortcut: &envir::Shortcut, program: &ProgramMeta) {
        debug_assert!(storage.is_fit(shortcut, program));
        self.cast(device::BindProgram(program.name));

        for (i, (&k, block_var)) in shortcut.blocks.iter().zip(program.blocks.iter()).enumerate() {
            let handle = storage.get_block(k);
            let block = *self.dispatcher.resource.buffers.get(handle).unwrap();
            block_var.active_slot.set(i as u8);
            self.cast(device::BindUniformBlock(program.name, i as u8, i as device::UniformBufferSlot, block));
        }

        for (&k, uniform_var) in shortcut.uniforms.iter().zip(program.uniforms.iter()) {
            let value = storage.get_uniform(k);
            uniform_var.active_value.set(value);
            self.cast(device::BindUniform(uniform_var.location, value));
        }

        for (_i, (&_k, _texture)) in shortcut.textures.iter().zip(program.textures.iter()).enumerate() {
            unimplemented!()
        }
    }
}
<|endoftext|>


<commit_before>use std::path::{Path, PathBuf};
use url::Url;
use error::GhqError;
use vcs::VCS;

#[allow(dead_code)]
#[cfg_attr(rustfmt, rustfmt_skip)]
const KNOWN_HOSTS: &'static [(&'static str, usize)] = &[
    ("github.com", 2)
  , ("gist.github.com", 1)
  , ("bitbucket.org", 2)
  , ("gitlab.com", 2)
];

#[derive(Debug)]
pub struct Repository {
  url: Option<Url>,
  vcs: VCS,
  host: String,
  path: String,
}

impl Repository {
  pub fn from_local(path: &Path) -> Result<Repository, GhqError> {
    // TODO use detected VCS
    let _ = VCS::detect(path);

    let path = path.to_string_lossy().into_owned();
    let splitted: Vec<_> = path.splitn(3, '/').collect();
    if splitted.len() < 3 {
      return Err("").map_err(Into::into);
    }

    let host = splitted[0].to_owned();
    let path = Vec::from(&splitted[1..]).join("/");

    Ok(Repository {
      url: None,
      vcs: VCS::Git,
      host: host,
      path: path,
    })
  }

  pub fn from_remote(s: &str) -> Result<Repository, GhqError> {
    if let Ok(url) = Url::parse(s) {
      let host = url.host_str().ok_or("cannot retrieve host information")?.to_owned();
      let path = url.path().trim_left_matches("/").trim_right_matches(".git").to_owned();
      // TODO: check if given URL is valid

      Ok(Repository {
        url: Some(url),
        vcs: VCS::Git,
        host: host,
        path: path,
      })

    } else {
      let path: Vec<_> = s.split("/").collect();
      let (host, path) = match path.len() {
        0 => Err("unsupported pattern to resolve remote URL")?,
        1 => ("github.com".to_owned(), vec![path[0], path[0]]),
        2 => ("github.com".to_owned(), vec![path[0], path[1]]),
        _ => (path[0].to_owned(), Vec::from(&path[1..])),
      };

      let url = Url::parse(&format!("{}://{}/{}.git",
                                    "https",
                                    host,
                                    path.iter().take(2).cloned().collect::<Vec<_>>().join("/")))?;

      Ok(Repository {
        url: Some(url),
        vcs: VCS::Git,
        host: host,
        path: path.join("/"),
      })
    }
  }

  pub fn local_path(&self, root: &str) -> PathBuf {
    Path::new(root).join(&self.host).join(&self.path)
  }

  pub fn clone_into(&self, root: &str) -> Result<(), GhqError> {
    if let Some(ref url) = self.url {
      let dest = Path::new(root).join(&self.host).join(&self.path);

      if dest.exists() {
        println!("The target has already existed: {}", dest.display());
        return Ok(());
      }

      println!("clone '{}' into '{}'", url.as_str(), dest.display());
      self.vcs.clone_repository(url, dest.as_path())?;
    }

    Ok(())
  }
}

#[cfg(test)]
mod test_from_remote {
  use super::Repository;

  macro_rules! def_test {
    ($name:ident, $s:expr, $url:expr, $host:expr, $path:expr) => {
      #[test]
      fn $name() {
        let repo = Repository::from_remote($s).unwrap();
        assert_eq!(repo.url.unwrap().as_str(), $url);
        assert_eq!(repo.host, $host);
        assert_eq!(repo.path, $path);
      }
    }
  }

  def_test!(user_project,
            "hoge/fuga",
            "https://github.com/hoge/fuga.git",
            "github.com",
            "hoge/fuga");

  def_test!(domain_user_project,
            "github.com/hoge/fuga",
            "https://github.com/hoge/fuga.git",
            "github.com",
            "hoge/fuga");

  def_test!(only_project_name,
            "fuga",
            "https://github.com/fuga/fuga.git",
            "github.com",
            "fuga/fuga");

  def_test!(repository_url,
            "https://gitlab.com/funga-/pecopeco.git",
            "https://gitlab.com/funga-/pecopeco.git",
            "gitlab.com",
            "funga-/pecopeco");

  def_test!(long_path,
            "github.com/hoge/fuga/foo/a/b/c",
            "https://github.com/hoge/fuga.git",
            "github.com",
            "hoge/fuga/foo/a/b/c");
}
<commit_msg>add test case<commit_after>use std::path::{Path, PathBuf};
use url::Url;
use error::GhqError;
use vcs::VCS;

#[allow(dead_code)]
#[cfg_attr(rustfmt, rustfmt_skip)]
const KNOWN_HOSTS: &'static [(&'static str, usize)] = &[
    ("github.com", 2)
  , ("gist.github.com", 1)
  , ("bitbucket.org", 2)
  , ("gitlab.com", 2)
];

#[derive(Debug)]
pub struct Repository {
  url: Option<Url>,
  vcs: VCS,
  host: String,
  path: String,
}

impl Repository {
  pub fn from_local<P: AsRef<Path>>(path: P) -> Result<Repository, GhqError> {
    // TODO use detected VCS
    let _ = VCS::detect(path.as_ref());

    let path = path.as_ref().to_string_lossy().into_owned();
    let splitted: Vec<_> = path.splitn(2, '/').collect();
    if splitted.len() < 2 {
      return Err("").map_err(Into::into);
    }

    let host = splitted[0].to_owned();
    let path = Vec::from(&splitted[1..]).join("/");

    Ok(Repository {
      url: None,
      vcs: VCS::Git,
      host: host,
      path: path,
    })
  }

  pub fn from_remote(s: &str) -> Result<Repository, GhqError> {
    if let Ok(url) = Url::parse(s) {
      let host = url.host_str().ok_or("cannot retrieve host information")?.to_owned();
      let path = url.path().trim_left_matches("/").trim_right_matches(".git").to_owned();
      // TODO: check if given URL is valid

      Ok(Repository {
        url: Some(url),
        vcs: VCS::Git,
        host: host,
        path: path,
      })

    } else {
      let path: Vec<_> = s.split("/").collect();
      let (host, path) = match path.len() {
        0 => Err("unsupported pattern to resolve remote URL")?,
        1 => ("github.com".to_owned(), vec![path[0], path[0]]),
        2 => ("github.com".to_owned(), vec![path[0], path[1]]),
        _ => (path[0].to_owned(), Vec::from(&path[1..])),
      };

      let url = Url::parse(&format!("{}://{}/{}.git",
                                    "https",
                                    host,
                                    path.iter().take(2).cloned().collect::<Vec<_>>().join("/")))?;

      Ok(Repository {
        url: Some(url),
        vcs: VCS::Git,
        host: host,
        path: path.join("/"),
      })
    }
  }

  pub fn local_path(&self, root: &str) -> PathBuf {
    Path::new(root).join(&self.host).join(&self.path)
  }

  pub fn clone_into(&self, root: &str) -> Result<(), GhqError> {
    if let Some(ref url) = self.url {
      let dest = Path::new(root).join(&self.host).join(&self.path);

      if dest.exists() {
        println!("The target has already existed: {}", dest.display());
        return Ok(());
      }

      println!("clone '{}' into '{}'", url.as_str(), dest.display());
      self.vcs.clone_repository(url, dest.as_path())?;
    }

    Ok(())
  }
}

#[cfg(test)]
mod test_from_local {
  use super::Repository;

  #[test]
  fn case1() {
    let repo = Repository::from_local("github.com/hoge/fuga").unwrap();
    assert!(repo.url.is_none());
    assert_eq!(repo.host, "github.com");
    assert_eq!(repo.path, "hoge/fuga");
  }

  #[test]
  fn case2() {
    let repo = Repository::from_local("gist.github.com/0bacdbefa19f").unwrap();
    assert!(repo.url.is_none());
    assert_eq!(repo.host, "gist.github.com");
    assert_eq!(repo.path, "0bacdbefa19f");
  }
}

#[cfg(test)]
mod test_from_remote {
  use super::Repository;

  macro_rules! def_test {
    ($name:ident, $s:expr, $url:expr, $host:expr, $path:expr) => {
      #[test]
      fn $name() {
        let repo = Repository::from_remote($s).unwrap();
        assert_eq!(repo.url.unwrap().as_str(), $url);
        assert_eq!(repo.host, $host);
        assert_eq!(repo.path, $path);
      }
    }
  }

  def_test!(user_project,
            "hoge/fuga",
            "https://github.com/hoge/fuga.git",
            "github.com",
            "hoge/fuga");

  def_test!(domain_user_project,
            "github.com/hoge/fuga",
            "https://github.com/hoge/fuga.git",
            "github.com",
            "hoge/fuga");

  def_test!(only_project_name,
            "fuga",
            "https://github.com/fuga/fuga.git",
            "github.com",
            "fuga/fuga");

  def_test!(repository_url,
            "https://gitlab.com/funga-/pecopeco.git",
            "https://gitlab.com/funga-/pecopeco.git",
            "gitlab.com",
            "funga-/pecopeco");

  def_test!(long_path,
            "github.com/hoge/fuga/foo/a/b/c",
            "https://github.com/hoge/fuga.git",
            "github.com",
            "hoge/fuga/foo/a/b/c");
}
<|endoftext|>


<commit_before>use std::ptr;

use get_error;
use SdlResult;
use surface::SurfaceRef;
use video;

use sys::mouse as ll;

#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
#[repr(u32)]
pub enum SystemCursor {
    Arrow = ll::SDL_SYSTEM_CURSOR_ARROW,
    IBeam = ll::SDL_SYSTEM_CURSOR_IBEAM,
    Wait = ll::SDL_SYSTEM_CURSOR_WAIT,
    Crosshair = ll::SDL_SYSTEM_CURSOR_CROSSHAIR,
    WaitArrow = ll::SDL_SYSTEM_CURSOR_WAITARROW,
    SizeNWSE = ll::SDL_SYSTEM_CURSOR_SIZENWSE,
    SizeNESW = ll::SDL_SYSTEM_CURSOR_SIZENESW,
    SizeWE = ll::SDL_SYSTEM_CURSOR_SIZEWE,
    SizeNS = ll::SDL_SYSTEM_CURSOR_SIZENS,
    SizeAll = ll::SDL_SYSTEM_CURSOR_SIZEALL,
    No = ll::SDL_SYSTEM_CURSOR_NO,
    Hand = ll::SDL_SYSTEM_CURSOR_HAND,
}

pub struct Cursor {
    raw: *mut ll::SDL_Cursor
}

impl Drop for Cursor {
    #[inline]
    fn drop(&mut self) {
        unsafe { ll::SDL_FreeCursor(self.raw) };
    }
}

impl Cursor {
    pub fn new(data: &[u8], mask: &[u8], width: i32, height: i32, hot_x: i32, hot_y: i32) -> SdlResult<Cursor> {
        unsafe {
            let raw = ll::SDL_CreateCursor(data.as_ptr(),
                                           mask.as_ptr(),
                                           width as i32, height as i32,
                                           hot_x as i32, hot_y as i32);

            if raw == ptr::null_mut() {
                Err(get_error())
            } else {
                Ok(Cursor{ raw: raw })
            }
        }
    }

    // TODO: figure out how to pass Surface in here correctly
    pub fn from_surface<S: AsRef<SurfaceRef>>(surface: S, hot_x: i32, hot_y: i32) -> SdlResult<Cursor> {
        unsafe {
            let raw = ll::SDL_CreateColorCursor(surface.as_ref().raw(), hot_x, hot_y);

            if raw == ptr::null_mut() {
                Err(get_error())
            } else {
                Ok(Cursor{ raw: raw })
            }
        }
    }

    pub fn from_system(cursor: SystemCursor) -> SdlResult<Cursor> {
        unsafe {
            let raw = ll::SDL_CreateSystemCursor(cursor as u32);

            if raw == ptr::null_mut() {
                Err(get_error())
            } else {
                Ok(Cursor{ raw: raw })
            }
        }
    }

    pub fn set(&self) {
        unsafe { ll::SDL_SetCursor(self.raw); }
    }
}

#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub enum Mouse {
    Left,
    Middle,
    Right,
    X1,
    X2,
    Unknown(u8)
}

impl Mouse {
    #[inline]
    pub fn from_ll(button: u8) -> Mouse {
        match button {
            1 => Mouse::Left,
            2 => Mouse::Middle,
            3 => Mouse::Right,
            4 => Mouse::X1,
            5 => Mouse::X2,
            _ => Mouse::Unknown(button)
        }
    }
}

#[derive(Copy, Clone, Eq, PartialEq, Hash)]
pub struct MouseState {
    flags: u32
}

impl MouseState {
    /// Tests if a mouse button was pressed.
    pub fn button(&self, button: Mouse) -> bool {
        match button {
            Mouse::Left => self.left(),
            Mouse::Middle => self.middle(),
            Mouse::Right => self.right(),
            Mouse::X1 => self.x1(),
            Mouse::X2 => self.x2(),
            Mouse::Unknown(x) => {
                assert!(x <= 32);
                let mask = 1 << ((x as u32) - 1);
                (self.flags & mask) != 0
            }
        }
    }

    /// Tests if the left mouse button was pressed.
    pub fn left(&self) -> bool { (self.flags & ll::SDL_BUTTON_LMASK) != 0 }

    /// Tests if the middle mouse button was pressed.
    pub fn middle(&self) -> bool { (self.flags & ll::SDL_BUTTON_MMASK) != 0 }

    /// Tests if the right mouse button was pressed.
    pub fn right(&self) -> bool { (self.flags & ll::SDL_BUTTON_RMASK) != 0 }

    /// Tests if the X1 mouse button was pressed.
    pub fn x1(&self) -> bool { (self.flags & ll::SDL_BUTTON_X1MASK) != 0 }

    /// Tests if the X2 mouse button was pressed.
    pub fn x2(&self) -> bool { (self.flags & ll::SDL_BUTTON_X2MASK) != 0 }

    pub fn from_flags(flags: u32) -> MouseState {
        MouseState { flags: flags }
    }
}

impl ::Sdl {
    #[inline]
    pub fn mouse(&self) -> MouseUtil {
        MouseUtil {
            _sdldrop: self.sdldrop()
        }
    }
}

/// Mouse utility functions. Access with `Sdl::mouse()`.
///
/// ```no_run
/// let sdl_context = sdl2::init().unwrap();
///
/// // Hide the cursor
/// sdl_context.mouse().show_cursor(false);
/// ```
pub struct MouseUtil {
    _sdldrop: ::std::rc::Rc<::SdlDrop>
}

impl MouseUtil {
    /// Gets the id of the window which currently has mouse focus.
    pub fn focused_window_id(&self) -> Option<u32> {
        let raw = unsafe { ll::SDL_GetMouseFocus() };
        if raw == ptr::null_mut() {
            None
        } else {
            let id = unsafe { ::sys::video::SDL_GetWindowID(raw) };
            Some(id)
        }
    }

    pub fn mouse_state(&self) -> (MouseState, i32, i32) {
        let mut x = 0;
        let mut y = 0;
        unsafe {
            let raw = ll::SDL_GetMouseState(&mut x, &mut y);
            return (MouseState::from_flags(raw), x as i32, y as i32);
        }
    }

    pub fn relative_mouse_state(&self) -> (MouseState, i32, i32) {
        let mut x = 0;
        let mut y = 0;
        unsafe {
            let raw = ll::SDL_GetRelativeMouseState(&mut x, &mut y);
            return (MouseState::from_flags(raw), x as i32, y as i32);
        }
    }

    pub fn warp_mouse_in_window(&self, window: &video::Window, x: i32, y: i32) {
        unsafe { ll::SDL_WarpMouseInWindow(window.raw(), x, y); }
    }

    pub fn set_relative_mouse_mode(&self, on: bool) {
        unsafe { ll::SDL_SetRelativeMouseMode(on as i32); }
    }

    pub fn relative_mouse_mode(&self) -> bool {
        unsafe { ll::SDL_GetRelativeMouseMode() == 1 }
    }

    pub fn is_cursor_showing(&self) -> bool {
        unsafe { ll::SDL_ShowCursor(ll::SDL_QUERY) == 1 }
    }

    pub fn show_cursor(&self, show: bool) {
        unsafe { ll::SDL_ShowCursor(show as i32); }
    }
}
<commit_msg>Change parameter for `warp_mouse_in_window` from `Window` to `WindowRef`<commit_after>use std::ptr;

use get_error;
use SdlResult;
use surface::SurfaceRef;
use video;

use sys::mouse as ll;

#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
#[repr(u32)]
pub enum SystemCursor {
    Arrow = ll::SDL_SYSTEM_CURSOR_ARROW,
    IBeam = ll::SDL_SYSTEM_CURSOR_IBEAM,
    Wait = ll::SDL_SYSTEM_CURSOR_WAIT,
    Crosshair = ll::SDL_SYSTEM_CURSOR_CROSSHAIR,
    WaitArrow = ll::SDL_SYSTEM_CURSOR_WAITARROW,
    SizeNWSE = ll::SDL_SYSTEM_CURSOR_SIZENWSE,
    SizeNESW = ll::SDL_SYSTEM_CURSOR_SIZENESW,
    SizeWE = ll::SDL_SYSTEM_CURSOR_SIZEWE,
    SizeNS = ll::SDL_SYSTEM_CURSOR_SIZENS,
    SizeAll = ll::SDL_SYSTEM_CURSOR_SIZEALL,
    No = ll::SDL_SYSTEM_CURSOR_NO,
    Hand = ll::SDL_SYSTEM_CURSOR_HAND,
}

pub struct Cursor {
    raw: *mut ll::SDL_Cursor
}

impl Drop for Cursor {
    #[inline]
    fn drop(&mut self) {
        unsafe { ll::SDL_FreeCursor(self.raw) };
    }
}

impl Cursor {
    pub fn new(data: &[u8], mask: &[u8], width: i32, height: i32, hot_x: i32, hot_y: i32) -> SdlResult<Cursor> {
        unsafe {
            let raw = ll::SDL_CreateCursor(data.as_ptr(),
                                           mask.as_ptr(),
                                           width as i32, height as i32,
                                           hot_x as i32, hot_y as i32);

            if raw == ptr::null_mut() {
                Err(get_error())
            } else {
                Ok(Cursor{ raw: raw })
            }
        }
    }

    // TODO: figure out how to pass Surface in here correctly
    pub fn from_surface<S: AsRef<SurfaceRef>>(surface: S, hot_x: i32, hot_y: i32) -> SdlResult<Cursor> {
        unsafe {
            let raw = ll::SDL_CreateColorCursor(surface.as_ref().raw(), hot_x, hot_y);

            if raw == ptr::null_mut() {
                Err(get_error())
            } else {
                Ok(Cursor{ raw: raw })
            }
        }
    }

    pub fn from_system(cursor: SystemCursor) -> SdlResult<Cursor> {
        unsafe {
            let raw = ll::SDL_CreateSystemCursor(cursor as u32);

            if raw == ptr::null_mut() {
                Err(get_error())
            } else {
                Ok(Cursor{ raw: raw })
            }
        }
    }

    pub fn set(&self) {
        unsafe { ll::SDL_SetCursor(self.raw); }
    }
}

#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub enum Mouse {
    Left,
    Middle,
    Right,
    X1,
    X2,
    Unknown(u8)
}

impl Mouse {
    #[inline]
    pub fn from_ll(button: u8) -> Mouse {
        match button {
            1 => Mouse::Left,
            2 => Mouse::Middle,
            3 => Mouse::Right,
            4 => Mouse::X1,
            5 => Mouse::X2,
            _ => Mouse::Unknown(button)
        }
    }
}

#[derive(Copy, Clone, Eq, PartialEq, Hash)]
pub struct MouseState {
    flags: u32
}

impl MouseState {
    /// Tests if a mouse button was pressed.
    pub fn button(&self, button: Mouse) -> bool {
        match button {
            Mouse::Left => self.left(),
            Mouse::Middle => self.middle(),
            Mouse::Right => self.right(),
            Mouse::X1 => self.x1(),
            Mouse::X2 => self.x2(),
            Mouse::Unknown(x) => {
                assert!(x <= 32);
                let mask = 1 << ((x as u32) - 1);
                (self.flags & mask) != 0
            }
        }
    }

    /// Tests if the left mouse button was pressed.
    pub fn left(&self) -> bool { (self.flags & ll::SDL_BUTTON_LMASK) != 0 }

    /// Tests if the middle mouse button was pressed.
    pub fn middle(&self) -> bool { (self.flags & ll::SDL_BUTTON_MMASK) != 0 }

    /// Tests if the right mouse button was pressed.
    pub fn right(&self) -> bool { (self.flags & ll::SDL_BUTTON_RMASK) != 0 }

    /// Tests if the X1 mouse button was pressed.
    pub fn x1(&self) -> bool { (self.flags & ll::SDL_BUTTON_X1MASK) != 0 }

    /// Tests if the X2 mouse button was pressed.
    pub fn x2(&self) -> bool { (self.flags & ll::SDL_BUTTON_X2MASK) != 0 }

    pub fn from_flags(flags: u32) -> MouseState {
        MouseState { flags: flags }
    }
}

impl ::Sdl {
    #[inline]
    pub fn mouse(&self) -> MouseUtil {
        MouseUtil {
            _sdldrop: self.sdldrop()
        }
    }
}

/// Mouse utility functions. Access with `Sdl::mouse()`.
///
/// ```no_run
/// let sdl_context = sdl2::init().unwrap();
///
/// // Hide the cursor
/// sdl_context.mouse().show_cursor(false);
/// ```
pub struct MouseUtil {
    _sdldrop: ::std::rc::Rc<::SdlDrop>
}

impl MouseUtil {
    /// Gets the id of the window which currently has mouse focus.
    pub fn focused_window_id(&self) -> Option<u32> {
        let raw = unsafe { ll::SDL_GetMouseFocus() };
        if raw == ptr::null_mut() {
            None
        } else {
            let id = unsafe { ::sys::video::SDL_GetWindowID(raw) };
            Some(id)
        }
    }

    pub fn mouse_state(&self) -> (MouseState, i32, i32) {
        let mut x = 0;
        let mut y = 0;
        unsafe {
            let raw = ll::SDL_GetMouseState(&mut x, &mut y);
            return (MouseState::from_flags(raw), x as i32, y as i32);
        }
    }

    pub fn relative_mouse_state(&self) -> (MouseState, i32, i32) {
        let mut x = 0;
        let mut y = 0;
        unsafe {
            let raw = ll::SDL_GetRelativeMouseState(&mut x, &mut y);
            return (MouseState::from_flags(raw), x as i32, y as i32);
        }
    }

    pub fn warp_mouse_in_window(&self, window: &video::WindowRef, x: i32, y: i32) {
        unsafe { ll::SDL_WarpMouseInWindow(window.raw(), x, y); }
    }

    pub fn set_relative_mouse_mode(&self, on: bool) {
        unsafe { ll::SDL_SetRelativeMouseMode(on as i32); }
    }

    pub fn relative_mouse_mode(&self) -> bool {
        unsafe { ll::SDL_GetRelativeMouseMode() == 1 }
    }

    pub fn is_cursor_showing(&self) -> bool {
        unsafe { ll::SDL_ShowCursor(ll::SDL_QUERY) == 1 }
    }

    pub fn show_cursor(&self, show: bool) {
        unsafe { ll::SDL_ShowCursor(show as i32); }
    }
}
<|endoftext|>


<commit_before>mod middleware;

use crate::env_optional;
pub use middleware::CustomSentryMiddleware as SentryMiddleware;
use sentry::{ClientInitGuard, ClientOptions, IntoDsn, TransactionContext};
use std::sync::Arc;

/// Initializes the Sentry SDK from the environment variables.
///
/// If `SENTRY_DSN_API` is not set then Sentry will not be initialized,
/// otherwise it is required to be a valid DSN string. `SENTRY_ENV_API` must
/// be set if a DSN is provided.
///
/// `HEROKU_SLUG_COMMIT`, if present, will be used as the `release` property
/// on all events.
pub fn init() -> ClientInitGuard {
    let dsn = dotenv::var("SENTRY_DSN_API")
        .ok()
        .into_dsn()
        .expect("SENTRY_DSN_API is not a valid Sentry DSN value");

    let environment = dsn.as_ref().map(|_| {
        dotenv::var("SENTRY_ENV_API")
            .expect("SENTRY_ENV_API must be set when using SENTRY_DSN_API")
            .into()
    });

    let release = dotenv::var("HEROKU_SLUG_COMMIT").ok().map(Into::into);

    let traces_sample_rate = env_optional("SENTRY_TRACES_SAMPLE_RATE").unwrap_or(0.0);

    let traces_sampler = move |_ctx: &TransactionContext| -> f32 { traces_sample_rate };

    let opts = ClientOptions {
        auto_session_tracking: true,
        dsn,
        environment,
        release,
        session_mode: sentry::SessionMode::Request,
        traces_sampler: Some(Arc::new(traces_sampler)),
        ..Default::default()
    };

    sentry::init(opts)
}
<commit_msg>sentry: Reduce trace sample rate for download endpoint<commit_after>mod middleware;

use crate::env_optional;
pub use middleware::CustomSentryMiddleware as SentryMiddleware;
use sentry::{ClientInitGuard, ClientOptions, IntoDsn, TransactionContext};
use std::sync::Arc;

/// Initializes the Sentry SDK from the environment variables.
///
/// If `SENTRY_DSN_API` is not set then Sentry will not be initialized,
/// otherwise it is required to be a valid DSN string. `SENTRY_ENV_API` must
/// be set if a DSN is provided.
///
/// `HEROKU_SLUG_COMMIT`, if present, will be used as the `release` property
/// on all events.
pub fn init() -> ClientInitGuard {
    let dsn = dotenv::var("SENTRY_DSN_API")
        .ok()
        .into_dsn()
        .expect("SENTRY_DSN_API is not a valid Sentry DSN value");

    let environment = dsn.as_ref().map(|_| {
        dotenv::var("SENTRY_ENV_API")
            .expect("SENTRY_ENV_API must be set when using SENTRY_DSN_API")
            .into()
    });

    let release = dotenv::var("HEROKU_SLUG_COMMIT").ok().map(Into::into);

    let traces_sample_rate = env_optional("SENTRY_TRACES_SAMPLE_RATE").unwrap_or(0.0);

    let traces_sampler = move |ctx: &TransactionContext| -> f32 {
        let is_download_endpoint =
            ctx.name().starts_with("/api/v1/crates/") && ctx.name().ends_with("/download");

        if is_download_endpoint {
            // Reduce the sample rate for the download endpoint, since we have significantly
            // more traffic on that endpoint compared to the rest
            traces_sample_rate / 10.
        } else {
            traces_sample_rate
        }
    };

    let opts = ClientOptions {
        auto_session_tracking: true,
        dsn,
        environment,
        release,
        session_mode: sentry::SessionMode::Request,
        traces_sampler: Some(Arc::new(traces_sampler)),
        ..Default::default()
    };

    sentry::init(opts)
}
<|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/. */

use std::fmt;
use std::mem;
use std::num::Float;

use text_writer::{mod, TextWriter};

use super::{Token, NumericValue, PercentageValue};

pub trait ToCss for Sized? {
    /// Serialize `self` in CSS syntax, writing to `dest`.
    fn to_css<W>(&self, dest: &mut W) -> text_writer::Result where W: TextWriter;

    /// Serialize `self` in CSS syntax and return a string.
    ///
    /// (This is a convenience wrapper for `to_css` and probably should not be overridden.)
    #[inline]
    fn to_css_string(&self) -> String {
        let mut s = String::new();
        self.to_css(&mut s).unwrap();
        s
    }

    /// Serialize `self` in CSS syntax and return a result compatible with `std::fmt::Show`.
    ///
    /// Typical usage is, for a `Foo` that implements `ToCss`:
    ///
    /// ```{rust,ignore}
    /// use std::fmt;
    /// impl fmt::Show for Foo {
    ///     #[inline] fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.fmt_to_css(f) }
    /// }
    /// ```
    ///
    /// (This is a convenience wrapper for `to_css` and probably should not be overridden.)
    #[inline]
    fn fmt_to_css<W>(&self, dest: &mut W) -> fmt::Result where W: TextWriter {
        self.to_css(dest).map_err(|_| fmt::Error)
    }
}

#[inline]
fn write_numeric<W>(value: NumericValue, dest: &mut W) -> text_writer::Result
where W: TextWriter {
    // `value.value >= 0` is true for negative 0.
    if value.signed && value.value.is_positive() {
        try!(dest.write_str("+"));
    }

    if value.value == 0.0 && value.value.is_negative() {
        // Negative zero. Work around #20596.
        try!(dest.write_str("-0"))
    } else {
        try!(write!(dest, "{}", value.value))
    }

    if value.int_value.is_none() && value.value.fract() == 0. {
        try!(dest.write_str(".0"));
    }
    Ok(())
}

impl<'a> ToCss for Token<'a> {
    fn to_css<W>(&self, dest: &mut W) -> text_writer::Result where W: TextWriter {
        match *self {
            Token::Ident(ref value) => try!(serialize_identifier(value.as_slice(), dest)),
            Token::AtKeyword(ref value) => {
                try!(dest.write_char('@'));
                try!(serialize_identifier(value.as_slice(), dest));
            },
            Token::Hash(ref value) => {
                try!(dest.write_char('#'));
                for c in value.as_slice().chars() {
                    try!(serialize_char(c, dest, /* is_identifier_start = */ false));
                }
            },
            Token::IDHash(ref value) => {
                try!(dest.write_char('#'));
                try!(serialize_identifier(value.as_slice(), dest));
            }
            Token::QuotedString(ref value) => try!(serialize_string(value.as_slice(), dest)),
            Token::Url(ref value) => {
                try!(dest.write_str("url("));
                try!(serialize_string(value.as_slice(), dest));
                try!(dest.write_char(')'));
            },
            Token::Delim(value) => try!(dest.write_char(value)),

            Token::Number(value) => try!(write_numeric(value, dest)),
            Token::Percentage(PercentageValue { unit_value, int_value, signed }) => {
                let value = NumericValue {
                    value: unit_value * 100.,
                    int_value: int_value,
                    signed: signed,
                };
                try!(write_numeric(value, dest));
                try!(dest.write_char('%'));
            },
            Token::Dimension(value, ref unit) => {
                try!(write_numeric(value, dest));
                // Disambiguate with scientific notation.
                let unit = unit.as_slice();
                if unit == "e" || unit == "E" || unit.starts_with("e-") || unit.starts_with("E-") {
                    try!(dest.write_str("\\65 "));
                    for c in unit.slice_from(1).chars() {
                        try!(serialize_char(c, dest, /* is_identifier_start = */ false));
                    }
                } else {
                    try!(serialize_identifier(unit, dest));
                }
            },

            Token::UnicodeRange(start, end) => {
                try!(dest.write_str(format!("U+{:X}", start).as_slice()));
                if end != start {
                    try!(dest.write_str(format!("-{:X}", end).as_slice()));
                }
            }

            Token::WhiteSpace(content) => try!(dest.write_str(content)),
            Token::Comment(content) => try!(write!(dest, "/*{}*/", content)),
            Token::Colon => try!(dest.write_char(':')),
            Token::Semicolon => try!(dest.write_char(';')),
            Token::Comma => try!(dest.write_char(',')),
            Token::IncludeMatch => try!(dest.write_str("~=")),
            Token::DashMatch => try!(dest.write_str("|=")),
            Token::PrefixMatch => try!(dest.write_str("^=")),
            Token::SuffixMatch => try!(dest.write_str("$=")),
            Token::SubstringMatch => try!(dest.write_str("*=")),
            Token::Column => try!(dest.write_str("||")),
            Token::CDO => try!(dest.write_str("<!--")),
            Token::CDC => try!(dest.write_str("-->")),

            Token::Function(ref name) => {
                try!(serialize_identifier(name.as_slice(), dest));
                try!(dest.write_char('('));
            },
            Token::ParenthesisBlock => try!(dest.write_char('(')),
            Token::SquareBracketBlock => try!(dest.write_char('[')),
            Token::CurlyBracketBlock => try!(dest.write_char('{')),

            Token::BadUrl => try!(dest.write_str("url(<bad url>)")),
            Token::BadString => try!(dest.write_str("\"<bad string>\n")),
            Token::CloseParenthesis => try!(dest.write_char(')')),
            Token::CloseSquareBracket => try!(dest.write_char(']')),
            Token::CloseCurlyBracket => try!(dest.write_char('}')),
        }
        Ok(())
    }
}

pub fn serialize_identifier<W>(value: &str, dest: &mut W) -> text_writer::Result
where W:TextWriter {
    // TODO: avoid decoding/re-encoding UTF-8?
    let mut iter = value.chars();
    let mut c = iter.next().unwrap();
    if c == '-' {
        c = match iter.next() {
            None => return dest.write_str("\\-"),
            Some(c) => { try!(dest.write_char('-')); c },
        }
    };
    try!(serialize_char(c, dest, /* is_identifier_start = */ true));
    for c in iter {
        try!(serialize_char(c, dest, /* is_identifier_start = */ false));
    }
    Ok(())
}

#[inline]
fn serialize_char<W>(c: char, dest: &mut W, is_identifier_start: bool) -> text_writer::Result
where W: TextWriter {
    match c {
        '0'...'9' if is_identifier_start => try!(dest.write_str(format!("\\3{} ", c).as_slice())),
        '-' if is_identifier_start => try!(dest.write_str("\\-")),
        '0'...'9' | 'A'...'Z' | 'a'...'z' | '_' | '-' => try!(dest.write_char(c)),
        _ if c > '\x7F' => try!(dest.write_char(c)),
        '\n' => try!(dest.write_str("\\A ")),
        '\r' => try!(dest.write_str("\\D ")),
        '\x0C' => try!(dest.write_str("\\C ")),
        _ => { try!(dest.write_char('\\')); try!(dest.write_char(c)) },
    };
    Ok(())
}

pub fn serialize_string<W>(value: &str, dest: &mut W) -> text_writer::Result
where W: TextWriter {
    try!(dest.write_char('"'));
    try!(CssStringWriter::new(dest).write_str(value));
    try!(dest.write_char('"'));
    Ok(())
}

/// A `TextWriter` adaptor that escapes text for writing as a CSS string.
/// Quotes are not included.
///
/// Typical usage:
///
/// ```{rust,ignore}
/// fn write_foo<W>(foo: &Foo, dest: &mut W) -> text_writer::Result where W: TextWriter {
///     try!(dest.write_char('"'));
///     {
///         let mut string_dest = CssStringWriter::new(dest);
///         // Write into string_dest...
///     }
///     try!(dest.write_char('"'));
///     Ok(())
/// }
/// ```
pub struct CssStringWriter<'a, W: 'a> {
    inner: &'a mut W,
}

impl<'a, W> CssStringWriter<'a, W> where W: TextWriter {
    pub fn new(inner: &'a mut W) -> CssStringWriter<'a, W> {
        CssStringWriter { inner: inner }
    }
}

impl<'a, W> TextWriter for CssStringWriter<'a, W> where W: TextWriter {
    fn write_str(&mut self, s: &str) -> text_writer::Result {
        // TODO: avoid decoding/re-encoding UTF-8?
        for c in s.chars() {
            try!(self.write_char(c))
        }
        Ok(())
    }

    fn write_char(&mut self, c: char) -> text_writer::Result {
        match c {
            '"' => self.inner.write_str("\\\""),
            '\\' => self.inner.write_str("\\\\"),
            '\n' => self.inner.write_str("\\A "),
            '\r' => self.inner.write_str("\\D "),
            '\x0C' => self.inner.write_str("\\C "),
            _ => self.inner.write_char(c),
        }
    }
}

impl<'a> ToCss for [Token<'a>] {
    fn to_css<W>(&self, dest: &mut W) -> text_writer::Result where W: TextWriter {
        let mut writer = TokenWriter::new(dest);
        for token in self.iter() {
            try!(writer.write(token))
        }
        Ok(())
    }
}

pub struct TokenWriter<'i, 'a, W: 'a> {
    dest: &'a mut W,
    previous_token: Option<Token<'i>>,
}

impl<'i, 'a, W> TokenWriter<'i, 'a, W> where W: TextWriter {
    pub fn new<'a>(dest: &'a mut W) -> TokenWriter<'i, 'a, W> {
        TokenWriter {
            dest: dest,
            previous_token: None,
        }
    }

    pub fn write(&mut self, token: &Token<'i>) -> text_writer::Result {
        use Token::*;
        let previous = &mem::replace(&mut self.previous_token, Some((*token).clone()))
                        // A "not special" token:
                        .unwrap_or(Colon);
        let (a, b) = (previous, token);
        if (
            matches!(*a, Ident(..) | AtKeyword(..) | Hash(..) | IDHash(..) |
                         Dimension(..) | Delim('#') | Delim('-') | Number(..)) &&
            matches!(*b, Ident(..) | Function(..) | Url(..) | BadUrl(..) |
                         Number(..) | Percentage(..) | Dimension(..) | UnicodeRange(..))
        ) || (
            matches!(*a, Ident(..)) &&
            matches!(*b, ParenthesisBlock(..))
        ) || (
            matches!(*a, Ident(..) | AtKeyword(..) | Hash(..) | IDHash(..) | Dimension(..)) &&
            matches!(*b, Delim('-') | CDC)
        ) || (
            matches!(*a, Delim('#') | Delim('-') | Number(..) | Delim('@')) &&
            matches!(*b, Ident(..) | Function(..) | Url(..) | BadUrl(..))
        ) || (
            matches!(*a, Delim('@')) &&
            matches!(*b, Ident(..) | Function(..) | Url(..) | BadUrl(..) |
                         UnicodeRange(..) | Delim('-'))
        ) || (
            matches!(*a, UnicodeRange(..) | Delim('.') | Delim('+')) &&
            matches!(*b, Number(..) | Percentage(..) | Dimension(..))
        ) || (
            matches!(*a, UnicodeRange(..)) &&
            matches!(*b, Ident(..) | Function(..) | Delim('?'))
        ) || matches!((a, b), (&Delim(a), &Delim(b)) if matches!((a, b),
            ('#', '-') |
            ('$', '=') |
            ('*', '=') |
            ('^', '=') |
            ('~', '=') |
            ('|', '=') |
            ('|', '|') |
            ('/', '*')
        )) {
            try!(self.dest.write_str("/**/"));
        }
        // Skip whitespace when '\n' was previously written at the previous iteration.
        if !matches!((previous, token), (&Delim('\\'), &WhiteSpace(_))) {
            try!(token.to_css(self.dest));
        }
        if token == &Delim('\\') {
            try!(self.dest.write_char('\n'));
        }
        Ok(())
    }
}
<commit_msg>Serialize <unicode-range> with question marks when possible.<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/. */

use std::fmt;
use std::mem;
use std::cmp;
use std::num::{Float, Int};

use text_writer::{mod, TextWriter};

use super::{Token, NumericValue, PercentageValue};

pub trait ToCss for Sized? {
    /// Serialize `self` in CSS syntax, writing to `dest`.
    fn to_css<W>(&self, dest: &mut W) -> text_writer::Result where W: TextWriter;

    /// Serialize `self` in CSS syntax and return a string.
    ///
    /// (This is a convenience wrapper for `to_css` and probably should not be overridden.)
    #[inline]
    fn to_css_string(&self) -> String {
        let mut s = String::new();
        self.to_css(&mut s).unwrap();
        s
    }

    /// Serialize `self` in CSS syntax and return a result compatible with `std::fmt::Show`.
    ///
    /// Typical usage is, for a `Foo` that implements `ToCss`:
    ///
    /// ```{rust,ignore}
    /// use std::fmt;
    /// impl fmt::Show for Foo {
    ///     #[inline] fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.fmt_to_css(f) }
    /// }
    /// ```
    ///
    /// (This is a convenience wrapper for `to_css` and probably should not be overridden.)
    #[inline]
    fn fmt_to_css<W>(&self, dest: &mut W) -> fmt::Result where W: TextWriter {
        self.to_css(dest).map_err(|_| fmt::Error)
    }
}

#[inline]
fn write_numeric<W>(value: NumericValue, dest: &mut W) -> text_writer::Result
where W: TextWriter {
    // `value.value >= 0` is true for negative 0.
    if value.signed && value.value.is_positive() {
        try!(dest.write_str("+"));
    }

    if value.value == 0.0 && value.value.is_negative() {
        // Negative zero. Work around #20596.
        try!(dest.write_str("-0"))
    } else {
        try!(write!(dest, "{}", value.value))
    }

    if value.int_value.is_none() && value.value.fract() == 0. {
        try!(dest.write_str(".0"));
    }
    Ok(())
}

impl<'a> ToCss for Token<'a> {
    fn to_css<W>(&self, dest: &mut W) -> text_writer::Result where W: TextWriter {
        match *self {
            Token::Ident(ref value) => try!(serialize_identifier(value.as_slice(), dest)),
            Token::AtKeyword(ref value) => {
                try!(dest.write_char('@'));
                try!(serialize_identifier(value.as_slice(), dest));
            },
            Token::Hash(ref value) => {
                try!(dest.write_char('#'));
                for c in value.as_slice().chars() {
                    try!(serialize_char(c, dest, /* is_identifier_start = */ false));
                }
            },
            Token::IDHash(ref value) => {
                try!(dest.write_char('#'));
                try!(serialize_identifier(value.as_slice(), dest));
            }
            Token::QuotedString(ref value) => try!(serialize_string(value.as_slice(), dest)),
            Token::Url(ref value) => {
                try!(dest.write_str("url("));
                try!(serialize_string(value.as_slice(), dest));
                try!(dest.write_char(')'));
            },
            Token::Delim(value) => try!(dest.write_char(value)),

            Token::Number(value) => try!(write_numeric(value, dest)),
            Token::Percentage(PercentageValue { unit_value, int_value, signed }) => {
                let value = NumericValue {
                    value: unit_value * 100.,
                    int_value: int_value,
                    signed: signed,
                };
                try!(write_numeric(value, dest));
                try!(dest.write_char('%'));
            },
            Token::Dimension(value, ref unit) => {
                try!(write_numeric(value, dest));
                // Disambiguate with scientific notation.
                let unit = unit.as_slice();
                if unit == "e" || unit == "E" || unit.starts_with("e-") || unit.starts_with("E-") {
                    try!(dest.write_str("\\65 "));
                    for c in unit.slice_from(1).chars() {
                        try!(serialize_char(c, dest, /* is_identifier_start = */ false));
                    }
                } else {
                    try!(serialize_identifier(unit, dest));
                }
            },

            Token::UnicodeRange(start, end) => {
                try!(dest.write_str("U+"));
                let bits = cmp::min(start.trailing_zeros(), (!end).trailing_zeros());
                if bits >= 4 && start >> bits == end >> bits {
                    let question_marks = bits / 4;
                    let common = start >> question_marks * 4;
                    if common != 0 {
                        try!(write!(dest, "{:X}", common));
                    }
                    for _ in range(0, question_marks) {
                        try!(dest.write_str("?"));
                    }
                } else {
                    try!(write!(dest, "{:X}", start));
                    if end != start {
                        try!(write!(dest, "-{:X}", end));
                    }
                }
            }

            Token::WhiteSpace(content) => try!(dest.write_str(content)),
            Token::Comment(content) => try!(write!(dest, "/*{}*/", content)),
            Token::Colon => try!(dest.write_char(':')),
            Token::Semicolon => try!(dest.write_char(';')),
            Token::Comma => try!(dest.write_char(',')),
            Token::IncludeMatch => try!(dest.write_str("~=")),
            Token::DashMatch => try!(dest.write_str("|=")),
            Token::PrefixMatch => try!(dest.write_str("^=")),
            Token::SuffixMatch => try!(dest.write_str("$=")),
            Token::SubstringMatch => try!(dest.write_str("*=")),
            Token::Column => try!(dest.write_str("||")),
            Token::CDO => try!(dest.write_str("<!--")),
            Token::CDC => try!(dest.write_str("-->")),

            Token::Function(ref name) => {
                try!(serialize_identifier(name.as_slice(), dest));
                try!(dest.write_char('('));
            },
            Token::ParenthesisBlock => try!(dest.write_char('(')),
            Token::SquareBracketBlock => try!(dest.write_char('[')),
            Token::CurlyBracketBlock => try!(dest.write_char('{')),

            Token::BadUrl => try!(dest.write_str("url(<bad url>)")),
            Token::BadString => try!(dest.write_str("\"<bad string>\n")),
            Token::CloseParenthesis => try!(dest.write_char(')')),
            Token::CloseSquareBracket => try!(dest.write_char(']')),
            Token::CloseCurlyBracket => try!(dest.write_char('}')),
        }
        Ok(())
    }
}

pub fn serialize_identifier<W>(value: &str, dest: &mut W) -> text_writer::Result
where W:TextWriter {
    // TODO: avoid decoding/re-encoding UTF-8?
    let mut iter = value.chars();
    let mut c = iter.next().unwrap();
    if c == '-' {
        c = match iter.next() {
            None => return dest.write_str("\\-"),
            Some(c) => { try!(dest.write_char('-')); c },
        }
    };
    try!(serialize_char(c, dest, /* is_identifier_start = */ true));
    for c in iter {
        try!(serialize_char(c, dest, /* is_identifier_start = */ false));
    }
    Ok(())
}

#[inline]
fn serialize_char<W>(c: char, dest: &mut W, is_identifier_start: bool) -> text_writer::Result
where W: TextWriter {
    match c {
        '0'...'9' if is_identifier_start => try!(dest.write_str(format!("\\3{} ", c).as_slice())),
        '-' if is_identifier_start => try!(dest.write_str("\\-")),
        '0'...'9' | 'A'...'Z' | 'a'...'z' | '_' | '-' => try!(dest.write_char(c)),
        _ if c > '\x7F' => try!(dest.write_char(c)),
        '\n' => try!(dest.write_str("\\A ")),
        '\r' => try!(dest.write_str("\\D ")),
        '\x0C' => try!(dest.write_str("\\C ")),
        _ => { try!(dest.write_char('\\')); try!(dest.write_char(c)) },
    };
    Ok(())
}

pub fn serialize_string<W>(value: &str, dest: &mut W) -> text_writer::Result
where W: TextWriter {
    try!(dest.write_char('"'));
    try!(CssStringWriter::new(dest).write_str(value));
    try!(dest.write_char('"'));
    Ok(())
}

/// A `TextWriter` adaptor that escapes text for writing as a CSS string.
/// Quotes are not included.
///
/// Typical usage:
///
/// ```{rust,ignore}
/// fn write_foo<W>(foo: &Foo, dest: &mut W) -> text_writer::Result where W: TextWriter {
///     try!(dest.write_char('"'));
///     {
///         let mut string_dest = CssStringWriter::new(dest);
///         // Write into string_dest...
///     }
///     try!(dest.write_char('"'));
///     Ok(())
/// }
/// ```
pub struct CssStringWriter<'a, W: 'a> {
    inner: &'a mut W,
}

impl<'a, W> CssStringWriter<'a, W> where W: TextWriter {
    pub fn new(inner: &'a mut W) -> CssStringWriter<'a, W> {
        CssStringWriter { inner: inner }
    }
}

impl<'a, W> TextWriter for CssStringWriter<'a, W> where W: TextWriter {
    fn write_str(&mut self, s: &str) -> text_writer::Result {
        // TODO: avoid decoding/re-encoding UTF-8?
        for c in s.chars() {
            try!(self.write_char(c))
        }
        Ok(())
    }

    fn write_char(&mut self, c: char) -> text_writer::Result {
        match c {
            '"' => self.inner.write_str("\\\""),
            '\\' => self.inner.write_str("\\\\"),
            '\n' => self.inner.write_str("\\A "),
            '\r' => self.inner.write_str("\\D "),
            '\x0C' => self.inner.write_str("\\C "),
            _ => self.inner.write_char(c),
        }
    }
}

impl<'a> ToCss for [Token<'a>] {
    fn to_css<W>(&self, dest: &mut W) -> text_writer::Result where W: TextWriter {
        let mut writer = TokenWriter::new(dest);
        for token in self.iter() {
            try!(writer.write(token))
        }
        Ok(())
    }
}

pub struct TokenWriter<'i, 'a, W: 'a> {
    dest: &'a mut W,
    previous_token: Option<Token<'i>>,
}

impl<'i, 'a, W> TokenWriter<'i, 'a, W> where W: TextWriter {
    pub fn new<'a>(dest: &'a mut W) -> TokenWriter<'i, 'a, W> {
        TokenWriter {
            dest: dest,
            previous_token: None,
        }
    }

    pub fn write(&mut self, token: &Token<'i>) -> text_writer::Result {
        use Token::*;
        let previous = &mem::replace(&mut self.previous_token, Some((*token).clone()))
                        // A "not special" token:
                        .unwrap_or(Colon);
        let (a, b) = (previous, token);
        if (
            matches!(*a, Ident(..) | AtKeyword(..) | Hash(..) | IDHash(..) |
                         Dimension(..) | Delim('#') | Delim('-') | Number(..)) &&
            matches!(*b, Ident(..) | Function(..) | Url(..) | BadUrl(..) |
                         Number(..) | Percentage(..) | Dimension(..) | UnicodeRange(..))
        ) || (
            matches!(*a, Ident(..)) &&
            matches!(*b, ParenthesisBlock(..))
        ) || (
            matches!(*a, Ident(..) | AtKeyword(..) | Hash(..) | IDHash(..) | Dimension(..)) &&
            matches!(*b, Delim('-') | CDC)
        ) || (
            matches!(*a, Delim('#') | Delim('-') | Number(..) | Delim('@')) &&
            matches!(*b, Ident(..) | Function(..) | Url(..) | BadUrl(..))
        ) || (
            matches!(*a, Delim('@')) &&
            matches!(*b, Ident(..) | Function(..) | Url(..) | BadUrl(..) |
                         UnicodeRange(..) | Delim('-'))
        ) || (
            matches!(*a, UnicodeRange(..) | Delim('.') | Delim('+')) &&
            matches!(*b, Number(..) | Percentage(..) | Dimension(..))
        ) || (
            matches!(*a, UnicodeRange(..)) &&
            matches!(*b, Ident(..) | Function(..) | Delim('?'))
        ) || matches!((a, b), (&Delim(a), &Delim(b)) if matches!((a, b),
            ('#', '-') |
            ('$', '=') |
            ('*', '=') |
            ('^', '=') |
            ('~', '=') |
            ('|', '=') |
            ('|', '|') |
            ('/', '*')
        )) {
            try!(self.dest.write_str("/**/"));
        }
        // Skip whitespace when '\n' was previously written at the previous iteration.
        if !matches!((previous, token), (&Delim('\\'), &WhiteSpace(_))) {
            try!(token.to_css(self.dest));
        }
        if token == &Delim('\\') {
            try!(self.dest.write_char('\n'));
        }
        Ok(())
    }
}
<|endoftext|>