<commit_before>#![allow(unstable)]
#![feature(box_syntax)]

use std::io::{BufferedWriter, File};
use std::ops::{Add, Sub, Mul};
use std::num::Float;
use std::default::Default;
use std::rand::random;

#[derive(Show, Copy, Clone, Default)]
struct Vector {
    x: f64,
    y: f64,
    z: f64
}

#[derive(Show, Copy, Clone, Default)]
struct Ray {
    o: Vector,
    d: Vector
}

#[derive(Show, Clone, Default)]
struct Sphere {
    radius: f64,
    position: Vector,
    emission: Vector,
    color: Vector,
}

#[derive(Show, Default)]
struct Camera {
    eye: Ray, // origin and direction of cam
    // Field of view:
    right: Vector, // right vector
    up: Vector, // up vector
}

trait Shape {
    fn intersect(self, r: Ray) -> f64;
}

trait ShapeRef {
    fn color(self, r: &Ray, t: f64) -> Vector;
}

impl Shape for Sphere {
    fn intersect(self, r: Ray) -> f64 {
        // Solve t^2*d.d + 2*t*(o-p).d + (o-p).(o-p)-R^2 = 0
        let eps = 1e-4;
        let op = &self.position - &r.o;
        let b = op.dot(&r.d);
        let mut det = b * b - op.dot(&op) + self.radius * self.radius;

        if det < 0.0 {
            return 0.0;
        } else {
            det = det.sqrt();
        }

        if (b - det) > eps {
            return b-det;
        }

        if (b + det) > eps {
            return b+det;
        }

        return 0.0;
    }
}

impl<'a> Add for &'a Vector {
    type Output = Vector;

    fn add(self, other: &'a Vector) -> Vector {
        Vector {x: self.x + other.x, y: self.y + other.y, z: self.z + other.z}
    }
}

impl<'a> Sub for &'a Vector {
    type Output = Vector;

    fn sub(self, other: &'a Vector) -> Vector {
        Vector {x: self.x - other.x, y: self.y - other.y, z: self.z - other.z}
    }
}

impl<'a> Mul for &'a Vector {
    type Output = Vector;

    fn mul(self, other: &'a Vector) -> Vector {
        Vector {x: self.x * other.x, y: self.y * other.y, z: self.z * other.z}
    }
}

trait VectorOps {
    fn smul(self, rhs: f64) -> Vector;
    fn norm(self) -> Vector;
    fn cross(self, rhs: Vector) -> Vector;
    fn dot(&self, rhs: &Vector) -> f64;
}

impl VectorOps for Vector {

    fn smul(self, other: f64) -> Vector {
        Vector {x: self.x * other, y: self.y * other, z: self.z * other}
    }

    fn norm(self) -> Vector {
        let normalize = 1.0 / (self.x * self.x + self.y * self.y + self.z * self.z).sqrt() ;
        self.smul( normalize )
    }

    fn cross(self, b: Vector) -> Vector {
        Vector{x: self.y * b.z - self.z * b.y, y: self.z * b.x - self.x * b.z, z: self.x * b.y - self.y * b.x}
    }

    fn dot(&self, other: &Vector) -> f64 {
        (*self).x * (*other).x + (*self).y * (*other).y + (*self).z * (*other).z
    }
}

fn clamp(x: f64) -> f64
{
    if x < 0.0 {
        return 0.0;
    }
    if x > 1.0 {
        return 1.0;
    }

    x
}

fn to_int(x: f64) -> i64
{
    (clamp(x).powf(1.0 / 2.2) * 255.0 + 0.5) as i64
}

fn intersect(r: Ray, t: &mut f64, id: &mut usize) -> bool
{
    let inf = 10e20f64;
    *t = inf;
    for (i, sphere) in SPHERES.iter().enumerate() {
        let d: f64 = sphere.clone().intersect(r.clone());
        if d != 0.0 && d < *t {
            *t = d;
            *id = i;
        }

    }
    return *t < inf;

}

fn get_ray(cam: &Camera, a: usize, b: usize) -> Ray {

    let w = cam.eye.d.norm().smul(-1.0);
    let u = cam.up.cross(w).norm();
    let v = w.cross(u);

    let u0 = -1.0;
    let v0 = -1.0;
    let u1 = 1.0;
    let v1 = 1.0;
    let d = 2.0;

    let across = u.smul(u1-u0);
    let up = v.smul(v1-v0);
    let an = (a as f64) / HEIGHT as f64;
    let bn = (b as f64) / WIDTH as f64;

    let corner = &(&(&cam.eye.o + &u.smul(u0)) + &v.smul(v0)) - &w.smul(d);
    let target = &( &corner + &across.smul(an)) + &up.smul(bn);
    Ray{o: cam.eye.o, d: (&target-&cam.eye.o).norm()}
}

fn get_light(ray: Ray, depth: usize) -> Vector{

    if depth > 5 {
        return Default::default();
    }

    let mut t: f64 = 0.0;
    let mut id: usize = 0;
    if intersect(ray, &mut t, &mut id) {
        let r1: f64 = 2.0 * std::rand::random() * PI;
        let r2: f64 = std::rand::random();
        let r2s: f64 = r2.sqrt();

        let x: Vector = &ray.o + &ray.d.smul(t); // Hitpoint
        let n: Vector = (&x - &SPHERES[id].position).norm();
        let nl = if n.dot(&ray.d) < 0.0 { n } else { n.smul(-1.0) };

        let w = nl;
        let u = if w.x > 0.1 { Vector{x: 0.0, y: 1.0, z: 0.0} } else { Vector{x: 1.0, y: 0.0, z: 0.0 } }.norm();
        let v = w.cross(u);

        let d = &(&u.smul(r1.cos()*r2s)  + &v.smul(r1.sin()*r2s)) + &w.smul((1.0-r2).sqrt());

        return &SPHERES[id].emission + &(&SPHERES[id].color * &get_light(Ray{o: x, d: d}, depth+1));
    }

    return Default::default();

}

static SPHERES: [Sphere; 9] = [
    Sphere{radius:1e5 as f64,  position: Vector{ x: (1e5 + 1.0) as f64, y: 40.8 as f64, z: 81.6}, emission: Vector{x: 0.0, y: 0.0, z: 0.0 }, color: Vector{x: 0.75,y: 0.25,z: 0.25}}, // Left
    Sphere{radius:1e5 as f64,  position: Vector{ x:  -1e5 as f64 + 99.0,y: 40.8 as f64, z: 81.6}, emission: Vector{x: 0.0, y: 0.0, z: 0.0 }, color: Vector{x: 0.25,y: 0.25,z: 0.75}}, // Rght
    Sphere{radius:1e5 as f64,  position: Vector{ x: 50 as f64, y: 40.8 as f64, z: 1e5 as f64}, emission: Vector{x: 0.0, y: 0.0, z: 0.0 }, color: Vector{x: 0.75,y: 0.75,z: 0.75}}, // Back
    Sphere{radius:1e5 as f64,  position: Vector{ x: 50 as f64, y: 40.8 as f64, z: -1e5+600 as f64}, emission: Vector{x: 0.0, y: 0.0, z: 0.0 }, color: Vector{x: 1.0, y: 1.0, z: 1.0 }}, // Frnt
    Sphere{radius:1e5 as f64,  position: Vector{ x: 50 as f64, y:  1e5 as f64, z: 81.6}, emission: Vector{x: 0.0, y: 0.0, z: 0.0 }, color: Vector{x: 0.75,y: 0.75,z: 0.75}}, // Botm
    Sphere{radius:1e5 as f64,  position: Vector{ x: 50 as f64, y: -1e5+81.6 as f64,z: 81.6}, emission: Vector{x: 0.0, y: 0.0, z: 0.0 }, color: Vector{x: 0.75,y: 0.75,z: 0.75}}, // Top
    Sphere{radius:16.5, position: Vector{ x: 27.0, y: 16.5 as f64, z: 47.0}, emission: Vector{x: 0.0, y: 0.0, z: 0.0 }, color: Vector{x: 0.999, y: 0.999, z: 0.999}}, // Mirr
    Sphere{radius:16.5, position: Vector{ x: 73.0, y: 16.5 as f64, z: 78.0}, emission: Vector{x: 0.0, y: 0.0, z: 0.0 }, color: Vector{x: 0.999, y: 0.999, z: 0.999}}, // Glas
    Sphere{radius:600 as f64,  position: Vector{ x: 50 as f64, y: 681.6-0.27 as f64, z: 81.6}, emission: Vector{x: 12.0, y: 12.0, z: 12.0}, color: Vector{x: 1.0, y: 1.0, z: 1.0}}, //Lite
];

const WIDTH: usize = 1024;
const HEIGHT: usize = 768;
//static NTHREADS: usize = 4;
static PI: f64 = 3.14159265358979323846264338327950288_f64;

fn main() {
    let mut cam: Camera = Default::default();
    cam.eye.o = Vector {x: 50.0, y: 52.0, z: 295.6};
    cam.eye.d = Vector {x: 0.0, y: -0.042612, z: -1.0};
    cam.up = Vector{x: 1.0, y: 0.0, z: 0.0};
    let samples = 500;
    let mut output = box [[Vector{x: 0.0, y: 0.0, z: 0.0}; WIDTH]; HEIGHT];

    for i in range(0, HEIGHT) {
        print!("\rRaytracing... ({:.0}%)", 100.0 - ((WIDTH*(HEIGHT-i)*samples) as f64) / ((WIDTH*HEIGHT*samples) as  f64) * 100.0);
        for j in range(0, WIDTH) {
            let mut r: Vector = Default::default();

            for _ in range(0, samples) {
                let ray: Ray = get_ray(&cam, i, j);
                r = &r + &get_light(ray, 0).smul(1.0/samples as f64);
            }
            output[i][j] = Vector{ x: clamp(r.x), y: clamp(r.y), z: clamp(r.z) };
        }
    }

    println!("\nWriting Image...");
    let file = File::create(&Path::new("image.ppm"));
    let mut writer = BufferedWriter::new(file);

    writer.write(format!("P3\n{} {}\n{}\n", WIDTH, HEIGHT, 255).as_bytes()).ok();
    for i in range(0, HEIGHT) {
        for j in range(0, WIDTH) {
            writer.write(format!("{} {} {} ", to_int(output[i][j].x), to_int(output[i][j].y), to_int(output[i][j].z)).as_bytes()).ok();
        }
    }
}<commit_msg>Draft revision.<commit_after>#![allow(unstable)]
#![feature(box_syntax)]

use std::io::{BufferedWriter, File};
use std::ops::{Add, Sub, Mul};
use std::num::Float;
use std::default::Default;
use std::rand::random;
use std::sync::TaskPool;
use std::sync::mpsc::channel;
use std::sync::mpsc::{Sender, Receiver};

#[derive(Show, Copy, Clone, Default)]
struct Vector {
    x: f64,
    y: f64,
    z: f64
}

#[derive(Show, Copy, Clone, Default)]
struct Ray {
    o: Vector,
    d: Vector
}

#[derive(Show, Clone, Default)]
struct Sphere {
    radius: f64,
    position: Vector,
    emission: Vector,
    color: Vector,
}

#[derive(Show, Default, Clone)]
struct Camera {
    eye: Ray, // origin and direction of cam
    // Field of view:
    right: Vector, // right vector
    up: Vector, // up vector
}

trait Shape {
    fn intersect(self, r: Ray) -> f64;
}

trait ShapeRef {
    fn color(self, r: &Ray, t: f64) -> Vector;
}

impl Shape for Sphere {
    fn intersect(self, r: Ray) -> f64 {
        // Solve t^2*d.d + 2*t*(o-p).d + (o-p).(o-p)-R^2 = 0
        let eps = 1e-4;
        let op = &self.position - &r.o;
        let b = op.dot(&r.d);
        let mut det = b * b - op.dot(&op) + self.radius * self.radius;

        if det < 0.0 {
            return 0.0;
        } else {
            det = det.sqrt();
        }

        if (b - det) > eps {
            return b-det;
        }

        if (b + det) > eps {
            return b+det;
        }

        return 0.0;
    }
}

impl<'a> Add for &'a Vector {
    type Output = Vector;

    fn add(self, other: &'a Vector) -> Vector {
        Vector {x: self.x + other.x, y: self.y + other.y, z: self.z + other.z}
    }
}

impl<'a> Sub for &'a Vector {
    type Output = Vector;

    fn sub(self, other: &'a Vector) -> Vector {
        Vector {x: self.x - other.x, y: self.y - other.y, z: self.z - other.z}
    }
}

impl<'a> Mul for &'a Vector {
    type Output = Vector;

    fn mul(self, other: &'a Vector) -> Vector {
        Vector {x: self.x * other.x, y: self.y * other.y, z: self.z * other.z}
    }
}

trait VectorOps {
    fn smul(self, rhs: f64) -> Vector;
    fn norm(self) -> Vector;
    fn cross(self, rhs: Vector) -> Vector;
    fn dot(&self, rhs: &Vector) -> f64;
}

impl VectorOps for Vector {

    fn smul(self, other: f64) -> Vector {
        Vector {x: self.x * other, y: self.y * other, z: self.z * other}
    }

    fn norm(self) -> Vector {
        let normalize = 1.0 / (self.x * self.x + self.y * self.y + self.z * self.z).sqrt() ;
        self.smul( normalize )
    }

    fn cross(self, b: Vector) -> Vector {
        Vector{x: self.y * b.z - self.z * b.y, y: self.z * b.x - self.x * b.z, z: self.x * b.y - self.y * b.x}
    }

    fn dot(&self, other: &Vector) -> f64 {
        (*self).x * (*other).x + (*self).y * (*other).y + (*self).z * (*other).z
    }
}

fn clamp(x: f64) -> f64
{
    if x < 0.0 {
        return 0.0;
    }
    if x > 1.0 {
        return 1.0;
    }

    x
}

fn to_int(x: f64) -> i64
{
    (clamp(x).powf(1.0 / 2.2) * 255.0 + 0.5) as i64
}

fn intersect(r: Ray, t: &mut f64, id: &mut usize) -> bool
{
    let inf = 10e20f64;
    *t = inf;
    for (i, sphere) in SPHERES.iter().enumerate() {
        let d: f64 = sphere.clone().intersect(r.clone());
        if d != 0.0 && d < *t {
            *t = d;
            *id = i;
        }

    }
    return *t < inf;

}

fn get_ray(cam: &Camera, a: usize, b: usize) -> Ray {

    let w = cam.eye.d.norm().smul(-1.0);
    let u = cam.up.cross(w).norm();
    let v = w.cross(u);

    let u0 = -1.0;
    let v0 = -1.0;
    let u1 = 1.0;
    let v1 = 1.0;
    let d = 2.0;

    let across = u.smul(u1-u0);
    let up = v.smul(v1-v0);
    let an = (a as f64) / HEIGHT as f64;
    let bn = (b as f64) / WIDTH as f64;

    let corner = &(&(&cam.eye.o + &u.smul(u0)) + &v.smul(v0)) - &w.smul(d);
    let target = &( &corner + &across.smul(an)) + &up.smul(bn);
    Ray{o: cam.eye.o, d: (&target-&cam.eye.o).norm()}
}

fn get_light(ray: Ray, depth: usize) -> Vector{
    let mut t: f64 = 0.0;
    let mut id: usize = 0;
    if intersect(ray, &mut t, &mut id) {
        if depth > 5 {
            return SPHERES[id].emission;
        }

        let r1: f64 = 2.0 * PI* std::rand::random();
        let r2: f64 = std::rand::random();
        let r2s: f64 = r2.sqrt();

        // Hitpoint
        let x: Vector = &ray.o + &ray.d.smul(t);
        let n: Vector = (&x - &SPHERES[id].position).norm();
        let nl = if n.dot(&ray.d) < 0.0 { n } else { n.smul(-1.0) };

        let w = nl;
        let u = if w.x.abs() > 0.1 { Vector{x: 0.0, y: 1.0, z: 0.0} } else { Vector{x: 1.0, y: 0.0, z: 0.0 } }.cross(w).norm();
        let v = w.cross(u);

        let d = (&(&u.smul( r1.cos()*r2s )  + &v.smul(r1.sin()*r2s)) + &w.smul((1.0-r2).sqrt())).norm();
        return &SPHERES[id].emission + &(&SPHERES[id].color * &get_light(Ray{o: x, d: d}, depth+1));
    }

    return Default::default();
}

static SPHERES: [Sphere; 9] = [
    Sphere{radius:1e5 as f64,  position: Vector{ x: (1e5 + 1.0) as f64, y: 40.8 as f64, z: 81.6}, emission: Vector{x: 0.0, y: 0.0, z: 0.0 }, color: Vector{x: 0.75,y: 0.25,z: 0.25}}, // Left
    Sphere{radius:1e5 as f64,  position: Vector{ x:  -1e5 as f64 + 99.0,y: 40.8 as f64, z: 81.6}, emission: Vector{x: 0.0, y: 0.0, z: 0.0 }, color: Vector{x: 0.25,y: 0.25,z: 0.75}}, // Rght
    Sphere{radius:1e5 as f64,  position: Vector{ x: 50 as f64, y: 40.8 as f64, z: 1e5 as f64}, emission: Vector{x: 0.0, y: 0.0, z: 0.0 }, color: Vector{x: 0.75,y: 0.75,z: 0.75}}, // Back
    Sphere{radius:1e5 as f64,  position: Vector{ x: 50 as f64, y: 40.8 as f64, z: -1e5+600 as f64}, emission: Vector{x: 0.0, y: 0.0, z: 0.0 }, color: Vector{x: 1.0, y: 1.0, z: 1.0 }}, // Frnt
    Sphere{radius:1e5 as f64,  position: Vector{ x: 50 as f64, y:  1e5 as f64, z: 81.6}, emission: Vector{x: 0.0, y: 0.0, z: 0.0 }, color: Vector{x: 0.75,y: 0.75,z: 0.75}}, // Botm
    Sphere{radius:1e5 as f64,  position: Vector{ x: 50 as f64, y: -1e5+81.6 as f64,z: 81.6}, emission: Vector{x: 0.0, y: 0.0, z: 0.0 }, color: Vector{x: 0.75,y: 0.75,z: 0.75}}, // Top
    Sphere{radius:16.5, position: Vector{ x: 27.0, y: 16.5 as f64, z: 47.0}, emission: Vector{x: 0.0, y: 0.0, z: 0.0 }, color: Vector{x: 0.999, y: 0.999, z: 0.999}}, // Mirr
    Sphere{radius:16.5, position: Vector{ x: 73.0, y: 16.5 as f64, z: 78.0}, emission: Vector{x: 0.0, y: 0.0, z: 0.0 }, color: Vector{x: 0.999, y: 0.999, z: 0.999}}, // Glas
    Sphere{radius:600 as f64,  position: Vector{ x: 50 as f64, y: 681.6-0.27 as f64, z: 81.6}, emission: Vector{x: 12.0, y: 12.0, z: 12.0}, color: Vector{x: 1.0, y: 1.0, z: 1.0}}, //Lite
];
/*
static SPHERES: [Sphere; 2] = [
    Sphere{ radius: 1.41,  position: Vector{ x:0.0, y: 0.0, z: -1.0}, emission: Vector{x: 0.0, y: 0.0, z: 0.0}, color: Vector{x: 0.25, y: 0.50, z: 0.75} },
    Sphere{ radius: 40.0,  position: Vector{ x:0.0, y: 9.0, z: 100.0}, emission: Vector{x: 12.0, y: 12.0, z: 12.0}, color: Vector{x: 1.0, y: 1.0, z: 1.0} },
];*/

const WIDTH: usize = 1024;
const HEIGHT: usize = 768;
static NTHREADS: usize = 4;
static PI: f64 = 3.14159265358979323846264338327950288_f64;

fn main() {

    let mut cam: Camera = Default::default();
    cam.eye.o = Vector {x: 50.0, y: 52.0, z: 295.6};
    cam.eye.d = Vector {x: 0.0, y: -0.042612, z: -1.0};
    cam.up = Vector{x: 1.0, y: 0.0, z: 0.0};
    /*let mut cam: Camera = Default::default();
    cam.eye.o = Vector {x: 0.0, y: 0.0, z: 4.0};
    cam.eye.d = Vector {x: 0.0, y: 0.0, z: -2.0};
    cam.up = Vector{x: 0.0, y: 1.0, z: 0.0};*/

    let pool = TaskPool::new(NTHREADS);
    let (tx, rx):  (Sender<(usize, usize, Vector)>, Receiver<(usize, usize, Vector)>) = channel();

    let samples: usize = 50;
    let mut output =  box [[Vector{x: 0.0, y: 0.0, z: 0.0}; WIDTH]; HEIGHT];

    for i in range(0, HEIGHT) {
        for j in range(0, WIDTH) {
            let tx = tx.clone();
            let cam = cam.clone();
            pool.execute(move|| {
                let mut r: Vector = Default::default();
                for _ in range(0, samples) {
                    let ray: Ray = get_ray(&cam, i, j);
                    r = &r + &get_light(ray, 0).smul(1.0/samples as f64);
                }
                tx.send((i, j, Vector{ x: clamp(r.x), y: clamp(r.y), z: clamp(r.z) })).unwrap();
            });
        }
    }

    for p in range(0,WIDTH*HEIGHT-1) {
        print!("\rRaytracing... ({:.0}%)", (p as f64) / ((WIDTH*HEIGHT) as f64) * 100.0);
        let (i, j, color) = rx.recv().unwrap();
        output[i][j] = color;
    }
    println!("\nWriting Image...");
    let file = File::create(&Path::new("image.ppm"));
    let mut writer = BufferedWriter::new(file);

    writer.write(format!("P3\n{} {}\n{}\n", WIDTH, HEIGHT, 255).as_bytes()).ok();
    for i in range(0, HEIGHT) {
        for j in range(0, WIDTH) {
            writer.write(format!("{} {} {} ", to_int(output[i][j].x), to_int(output[i][j].y), to_int(output[i][j].z)).as_bytes()).ok();
        }
    }
}<|endoftext|>


<commit_before>use prometheus::{Registry, Gauge, Encoder, TextEncoder};

use super::*;

pub struct PrometheusRenderer {
    pub buffer: Vec<u8>,
    registry: prometheus::Registry,
    pub encoder: prometheus::TextEncoder,
    count: prometheus::Gauge,
    max: prometheus::Gauge,
    min: prometheus::Gauge,
    avg: prometheus::Gauge,
    median: prometheus::Gauge,
    percentile90: prometheus::Gauge,
    percentile99: prometheus::Gauge,
    client_error_4xx_rate: prometheus::Gauge,
    server_error_5xx_rate: prometheus::Gauge,
}

impl PrometheusRenderer {
    pub fn new() -> PrometheusRenderer {
        fn make_and_register_gauge(
            gauge_name: &str,
            registry: &prometheus::Registry,
        ) -> prometheus::Gauge {
            let gauge = prometheus::Gauge::new(
                String::from(gauge_name),
                format!("The {} of responses.", gauge_name),
            ).expect("Failed to create Prometheus gauge.");

            registry.register(Box::new(gauge.clone())).expect(
                "Failed to register Prometheus gauge.",
            );
            gauge
        }

        let registry = prometheus::Registry::new();

        PrometheusRenderer {
            buffer: Vec::new(),
            encoder: prometheus::TextEncoder::new(),
            count: make_and_register_gauge("request_count", &registry),
            max: make_and_register_gauge("request_time_max", &registry),
            min: make_and_register_gauge("request_time_min", &registry),
            avg: make_and_register_gauge("request_time_avg", &registry),
            median: make_and_register_gauge("request_time_median", &registry),
            percentile90: make_and_register_gauge("request_time_percentile90", &registry),
            percentile99: make_and_register_gauge("request_time_percentile99", &registry),
            client_error_4xx_rate: make_and_register_gauge(
                "request_error_client_error_4xx_rate",
                &registry,
            ),
            server_error_5xx_rate: make_and_register_gauge(
                "request_error_server_error_5xx_rate",
                &registry,
            ),
            registry,
        }
    }
}

impl Renderer for PrometheusRenderer {
    fn render(&mut self, result: result::RequestLogAnalyzerResult) {
        self.count.set(result.count as f64);

        match result.timing {
            Some(timing) => {
                self.max.set(timing.max as f64);
                self.min.set(timing.min as f64);
                self.avg.set(timing.avg as f64);
                self.median.set(timing.median as f64);
                self.percentile90.set(timing.percentile90 as f64);
                self.percentile99.set(timing.percentile99 as f64);
            }
            None => {
                warn!("No matching log lines in file.");
            }
        }

        match result.error {
            Some(error) => {
                self.client_error_4xx_rate.set(
                    f64::from(error.client_error_4xx),
                );
                self.server_error_5xx_rate.set(
                    f64::from(error.server_error_5xx),
                );
            }
            None => {
                warn!("No matching log lines in file.");
            }
        }
        let metric_familys = self.registry.gather();

        self.encoder
            .encode(&metric_familys, &mut self.buffer)
            .expect("Failed to encode Prometheus metrics.");
    }
}

#[cfg(test)]
mod tests {
    use std::str;
    use super::*;
    use crate::analyzer;

    #[test]
    fn test_render_1() {
        let result = result::RequestLogAnalyzerResult {
            count: 3,
            timing: Some(analyzer::TimingResult {
                max: 100,
                min: 1,
                avg: 37,
                median: 10,
                percentile90: 90,
                percentile99: 99,
                count: 3,
            }),
            error: Some(analyzer::aggregated_error_rates::ErrorRatesResult {
                client_error_4xx: 0.1,
                server_error_5xx: 0.2,
            }),
        };

        let mut renderer = PrometheusRenderer::new();
        renderer.render(result);

        let buffer_text = str::from_utf8(&renderer.buffer).unwrap();
        assert!(buffer_text.contains("request_count 3"));
        assert!(buffer_text.contains("request_time_max 100"));
        assert!(buffer_text.contains("request_time_min 1"));
        assert!(buffer_text.contains("request_time_avg 37"));
        assert!(buffer_text.contains("request_time_median 10"));
        assert!(buffer_text.contains("request_time_percentile90 90"));
        assert!(buffer_text.contains("request_time_percentile99 99"));
        assert!(buffer_text.contains(
            "request_error_client_error_4xx_rate 0.1",
        ));
        assert!(buffer_text.contains(
            "request_error_server_error_5xx_rate 0.2",
        ));
    }

    #[test]
    fn test_render_2() {
        let result = result::RequestLogAnalyzerResult {
            count: 300,
            timing: Some(analyzer::TimingResult {
                max: 1000,
                min: 10,
                avg: 42,
                median: 75,
                percentile90: 900,
                percentile99: 990,
                count: 300,
            }),
            error: None,
        };

        let mut renderer = PrometheusRenderer::new();
        renderer.render(result);

        let buffer_text = str::from_utf8(&renderer.buffer).unwrap();
        assert!(buffer_text.contains("request_count 300"));
        assert!(buffer_text.contains("request_time_max 1000"));
        assert!(buffer_text.contains("request_time_min 10"));
        assert!(buffer_text.contains("request_time_avg 42"));
        assert!(buffer_text.contains("request_time_median 75"));
        assert!(buffer_text.contains("request_time_percentile90 900"));
        assert!(buffer_text.contains("request_time_percentile99 990"));
    }

    #[test]
    fn test_render_no_lines() {
        let result = result::RequestLogAnalyzerResult {
            count: 0,
            timing: None,
            error: None,
        };

        let mut renderer = PrometheusRenderer::new();
        renderer.render(result);

        let buffer_text = str::from_utf8(&renderer.buffer).unwrap();

        assert!(buffer_text.contains("request_count 0"));
        assert!(buffer_text.contains("request_time_max 0"));
        assert!(buffer_text.contains("request_time_min 0"));
        assert!(buffer_text.contains("request_time_avg 0"));
        assert!(buffer_text.contains("request_time_median 0"));
        assert!(buffer_text.contains("request_time_percentile90 0"));
        assert!(buffer_text.contains("request_time_percentile99 0"));
    }
}
<commit_msg>Fix ambiguous import under 2018 rules<commit_after>use ::prometheus::{Registry, Gauge, Encoder, TextEncoder};

use super::*;

pub struct PrometheusRenderer {
    pub buffer: Vec<u8>,
    registry: prometheus::Registry,
    pub encoder: prometheus::TextEncoder,
    count: prometheus::Gauge,
    max: prometheus::Gauge,
    min: prometheus::Gauge,
    avg: prometheus::Gauge,
    median: prometheus::Gauge,
    percentile90: prometheus::Gauge,
    percentile99: prometheus::Gauge,
    client_error_4xx_rate: prometheus::Gauge,
    server_error_5xx_rate: prometheus::Gauge,
}

impl PrometheusRenderer {
    pub fn new() -> PrometheusRenderer {
        fn make_and_register_gauge(
            gauge_name: &str,
            registry: &prometheus::Registry,
        ) -> prometheus::Gauge {
            let gauge = prometheus::Gauge::new(
                String::from(gauge_name),
                format!("The {} of responses.", gauge_name),
            ).expect("Failed to create Prometheus gauge.");

            registry.register(Box::new(gauge.clone())).expect(
                "Failed to register Prometheus gauge.",
            );
            gauge
        }

        let registry = prometheus::Registry::new();

        PrometheusRenderer {
            buffer: Vec::new(),
            encoder: prometheus::TextEncoder::new(),
            count: make_and_register_gauge("request_count", &registry),
            max: make_and_register_gauge("request_time_max", &registry),
            min: make_and_register_gauge("request_time_min", &registry),
            avg: make_and_register_gauge("request_time_avg", &registry),
            median: make_and_register_gauge("request_time_median", &registry),
            percentile90: make_and_register_gauge("request_time_percentile90", &registry),
            percentile99: make_and_register_gauge("request_time_percentile99", &registry),
            client_error_4xx_rate: make_and_register_gauge(
                "request_error_client_error_4xx_rate",
                &registry,
            ),
            server_error_5xx_rate: make_and_register_gauge(
                "request_error_server_error_5xx_rate",
                &registry,
            ),
            registry,
        }
    }
}

impl Renderer for PrometheusRenderer {
    fn render(&mut self, result: result::RequestLogAnalyzerResult) {
        self.count.set(result.count as f64);

        match result.timing {
            Some(timing) => {
                self.max.set(timing.max as f64);
                self.min.set(timing.min as f64);
                self.avg.set(timing.avg as f64);
                self.median.set(timing.median as f64);
                self.percentile90.set(timing.percentile90 as f64);
                self.percentile99.set(timing.percentile99 as f64);
            }
            None => {
                warn!("No matching log lines in file.");
            }
        }

        match result.error {
            Some(error) => {
                self.client_error_4xx_rate.set(
                    f64::from(error.client_error_4xx),
                );
                self.server_error_5xx_rate.set(
                    f64::from(error.server_error_5xx),
                );
            }
            None => {
                warn!("No matching log lines in file.");
            }
        }
        let metric_familys = self.registry.gather();

        self.encoder
            .encode(&metric_familys, &mut self.buffer)
            .expect("Failed to encode Prometheus metrics.");
    }
}

#[cfg(test)]
mod tests {
    use std::str;
    use super::*;
    use crate::analyzer;

    #[test]
    fn test_render_1() {
        let result = result::RequestLogAnalyzerResult {
            count: 3,
            timing: Some(analyzer::TimingResult {
                max: 100,
                min: 1,
                avg: 37,
                median: 10,
                percentile90: 90,
                percentile99: 99,
                count: 3,
            }),
            error: Some(analyzer::aggregated_error_rates::ErrorRatesResult {
                client_error_4xx: 0.1,
                server_error_5xx: 0.2,
            }),
        };

        let mut renderer = PrometheusRenderer::new();
        renderer.render(result);

        let buffer_text = str::from_utf8(&renderer.buffer).unwrap();
        assert!(buffer_text.contains("request_count 3"));
        assert!(buffer_text.contains("request_time_max 100"));
        assert!(buffer_text.contains("request_time_min 1"));
        assert!(buffer_text.contains("request_time_avg 37"));
        assert!(buffer_text.contains("request_time_median 10"));
        assert!(buffer_text.contains("request_time_percentile90 90"));
        assert!(buffer_text.contains("request_time_percentile99 99"));
        assert!(buffer_text.contains(
            "request_error_client_error_4xx_rate 0.1",
        ));
        assert!(buffer_text.contains(
            "request_error_server_error_5xx_rate 0.2",
        ));
    }

    #[test]
    fn test_render_2() {
        let result = result::RequestLogAnalyzerResult {
            count: 300,
            timing: Some(analyzer::TimingResult {
                max: 1000,
                min: 10,
                avg: 42,
                median: 75,
                percentile90: 900,
                percentile99: 990,
                count: 300,
            }),
            error: None,
        };

        let mut renderer = PrometheusRenderer::new();
        renderer.render(result);

        let buffer_text = str::from_utf8(&renderer.buffer).unwrap();
        assert!(buffer_text.contains("request_count 300"));
        assert!(buffer_text.contains("request_time_max 1000"));
        assert!(buffer_text.contains("request_time_min 10"));
        assert!(buffer_text.contains("request_time_avg 42"));
        assert!(buffer_text.contains("request_time_median 75"));
        assert!(buffer_text.contains("request_time_percentile90 900"));
        assert!(buffer_text.contains("request_time_percentile99 990"));
    }

    #[test]
    fn test_render_no_lines() {
        let result = result::RequestLogAnalyzerResult {
            count: 0,
            timing: None,
            error: None,
        };

        let mut renderer = PrometheusRenderer::new();
        renderer.render(result);

        let buffer_text = str::from_utf8(&renderer.buffer).unwrap();

        assert!(buffer_text.contains("request_count 0"));
        assert!(buffer_text.contains("request_time_max 0"));
        assert!(buffer_text.contains("request_time_min 0"));
        assert!(buffer_text.contains("request_time_avg 0"));
        assert!(buffer_text.contains("request_time_median 0"));
        assert!(buffer_text.contains("request_time_percentile90 0"));
        assert!(buffer_text.contains("request_time_percentile99 0"));
    }
}
<|endoftext|>


<commit_before>use std::cell::Cell;
use std::sync::atomic::{AtomicUsize, Ordering};
use super::internal::*;
use super::*;
use super::len::*;

// The consumer for find_first/find_last has fake indexes representing the lower
// and upper bounds of the "range" of data it consumes. This range does not
// correspond to indexes from the consumed iterator but rather indicate the
// consumer's position relative to other consumers. The purpose is to allow a
// consumer to know it should stop consuming items when another consumer finds a
// better match.

// An indexed consumer could specialize to use the real indexes instead, but we
// don't implement that for now. The only downside of the current approach is
// that in some cases, iterators very close to each other will have the same
// range and therefore not be able to stop processing if one of them finds a
// better match than the others.

#[derive(Copy, Clone)]
enum MatchPosition {
    Leftmost,
    Rightmost,
}

// Returns true if pos1 is a better match than pos2 according to MatchPosition
fn better_position(pos1: usize, pos2: usize, mp: MatchPosition) -> bool {
    match mp {
        MatchPosition::Leftmost => pos1 < pos2,
        MatchPosition::Rightmost => pos1 > pos2,
    }
}

pub fn find_first<PAR_ITER, FIND_OP>(pi: PAR_ITER, find_op: FIND_OP) -> Option<PAR_ITER::Item>
    where PAR_ITER: ParallelIterator,
          FIND_OP: Fn(&PAR_ITER::Item) -> bool + Sync
{
    let best_found = AtomicUsize::new(usize::max_value());
    let consumer = FindConsumer::new(&find_op, MatchPosition::Leftmost, &best_found);
    pi.drive_unindexed(consumer)
}

pub fn find_last<PAR_ITER, FIND_OP>(pi: PAR_ITER, find_op: FIND_OP) -> Option<PAR_ITER::Item>
    where PAR_ITER: ParallelIterator,
          FIND_OP: Fn(&PAR_ITER::Item) -> bool + Sync
{
    let best_found = AtomicUsize::new(0);
    let consumer = FindConsumer::new(&find_op, MatchPosition::Rightmost, &best_found);
    pi.drive_unindexed(consumer)
}

struct FindConsumer<'f, FIND_OP: 'f> {
    find_op: &'f FIND_OP,
    lower_bound: Cell<usize>,
    upper_bound: usize,
    match_position: MatchPosition,
    best_found: &'f AtomicUsize,
}

impl<'f, FIND_OP> FindConsumer<'f, FIND_OP> {
    fn new(find_op: &'f FIND_OP,
           match_position: MatchPosition,
           best_found: &'f AtomicUsize) -> Self {
        FindConsumer {
            find_op: find_op,
            lower_bound: Cell::new(0),
            upper_bound: usize::max_value(),
            match_position: match_position,
            best_found: best_found,
        }
    }

    fn current_index(&self) -> usize {
        match self.match_position {
            MatchPosition::Leftmost => self.lower_bound.get(),
            MatchPosition::Rightmost => self.upper_bound
        }
    }
}

impl<'f, ITEM, FIND_OP> Consumer<ITEM> for FindConsumer<'f, FIND_OP>
    where ITEM: Send,
          FIND_OP: Fn(&ITEM) -> bool + Sync
{
    type Folder = FindFolder<'f, ITEM, FIND_OP>;
    type Reducer = FindReducer;
    type Result = Option<ITEM>;

    fn cost(&mut self, cost: f64) -> f64 {
        cost * FUNC_ADJUSTMENT
    }

    fn split_at(self, _index: usize) -> (Self, Self, Self::Reducer) {
        let dir = self.match_position;
        (self.split_off(),
         self,
         FindReducer { match_position: dir })
    }

    fn into_folder(self) -> Self::Folder {
        FindFolder {
            find_op: self.find_op,
            boundary: self.current_index(),
            match_position: self.match_position,
            best_found: self.best_found,
            item: None,
        }
    }

    fn full(&self) -> bool {
        // can stop consuming if the best found index so far is *strictly*
        // better than anything this consumer will find
        better_position(self.best_found.load(Ordering::Relaxed),
                        self.current_index(),
                        self.match_position)
    }
}

impl<'f, ITEM, FIND_OP> UnindexedConsumer<ITEM> for FindConsumer<'f, FIND_OP>
    where ITEM: Send,
          FIND_OP: Fn(&ITEM) -> bool + Sync
{
    fn split_off(&self) -> Self {
        // Upper bound for one consumer will be lower bound for the other. This
        // overlap is okay, because only one of the bounds will be used for
        // comparing against best_found; the other is kept only to be able to
        // divide the range in half
        let old_lower_bound = self.lower_bound.get();
        let median = old_lower_bound + ((self.upper_bound - old_lower_bound) / 2);
        self.lower_bound.set(median);

        FindConsumer {
            find_op: self.find_op,
            lower_bound: Cell::new(old_lower_bound),
            upper_bound: median,
            match_position: self.match_position,
            best_found: self.best_found,
        }
    }

    fn to_reducer(&self) -> Self::Reducer {
        FindReducer { match_position: self.match_position }
    }
}

struct FindFolder<'f, ITEM, FIND_OP: 'f> {
    find_op: &'f FIND_OP,
    boundary: usize,
    match_position: MatchPosition,
    best_found: &'f AtomicUsize,
    item: Option<ITEM>,
}

impl<'f, FIND_OP: 'f + Fn(&ITEM) -> bool, ITEM> Folder<ITEM> for FindFolder<'f, ITEM, FIND_OP> {
    type Result = Option<ITEM>;

    fn consume(mut self, item: ITEM) -> Self {
        if (self.find_op)(&item) {
            // This may sometimes set best_found to a worse index than it was
            // before, depending on timing. This means more consumers will
            // continue to run than necessary, but the reducer will still ensure
            // the correct value is returned.
            self.best_found.swap(self.boundary, Ordering::Relaxed);
            self.item = Some(item);
        }
        self
    }

    fn complete(self) -> Self::Result {
        self.item
    }

    fn full(&self) -> bool {
        better_position(self.best_found.load(Ordering::Relaxed),
                        self.boundary,
                        self.match_position)
    }
}

struct FindReducer {
    match_position: MatchPosition
}

impl<ITEM> Reducer<Option<ITEM>> for FindReducer {
    fn reduce(self, left: Option<ITEM>, right: Option<ITEM>) -> Option<ITEM> {
        match self.match_position {
            MatchPosition::Leftmost => left.or(right),
            MatchPosition::Rightmost => right.or(left)
        }
    }
}
<commit_msg>Loop to set best_found for find_first/last<commit_after>use std::cell::Cell;
use std::sync::atomic::{AtomicUsize, Ordering};
use super::internal::*;
use super::*;
use super::len::*;

// The consumer for find_first/find_last has fake indexes representing the lower
// and upper bounds of the "range" of data it consumes. This range does not
// correspond to indexes from the consumed iterator but rather indicate the
// consumer's position relative to other consumers. The purpose is to allow a
// consumer to know it should stop consuming items when another consumer finds a
// better match.

// An indexed consumer could specialize to use the real indexes instead, but we
// don't implement that for now. The only downside of the current approach is
// that in some cases, iterators very close to each other will have the same
// range and therefore not be able to stop processing if one of them finds a
// better match than the others.

#[derive(Copy, Clone)]
enum MatchPosition {
    Leftmost,
    Rightmost,
}

// Returns true if pos1 is a better match than pos2 according to MatchPosition
fn better_position(pos1: usize, pos2: usize, mp: MatchPosition) -> bool {
    match mp {
        MatchPosition::Leftmost => pos1 < pos2,
        MatchPosition::Rightmost => pos1 > pos2,
    }
}

pub fn find_first<PAR_ITER, FIND_OP>(pi: PAR_ITER, find_op: FIND_OP) -> Option<PAR_ITER::Item>
    where PAR_ITER: ParallelIterator,
          FIND_OP: Fn(&PAR_ITER::Item) -> bool + Sync
{
    let best_found = AtomicUsize::new(usize::max_value());
    let consumer = FindConsumer::new(&find_op, MatchPosition::Leftmost, &best_found);
    pi.drive_unindexed(consumer)
}

pub fn find_last<PAR_ITER, FIND_OP>(pi: PAR_ITER, find_op: FIND_OP) -> Option<PAR_ITER::Item>
    where PAR_ITER: ParallelIterator,
          FIND_OP: Fn(&PAR_ITER::Item) -> bool + Sync
{
    let best_found = AtomicUsize::new(0);
    let consumer = FindConsumer::new(&find_op, MatchPosition::Rightmost, &best_found);
    pi.drive_unindexed(consumer)
}

struct FindConsumer<'f, FIND_OP: 'f> {
    find_op: &'f FIND_OP,
    lower_bound: Cell<usize>,
    upper_bound: usize,
    match_position: MatchPosition,
    best_found: &'f AtomicUsize,
}

impl<'f, FIND_OP> FindConsumer<'f, FIND_OP> {
    fn new(find_op: &'f FIND_OP,
           match_position: MatchPosition,
           best_found: &'f AtomicUsize) -> Self {
        FindConsumer {
            find_op: find_op,
            lower_bound: Cell::new(0),
            upper_bound: usize::max_value(),
            match_position: match_position,
            best_found: best_found,
        }
    }

    fn current_index(&self) -> usize {
        match self.match_position {
            MatchPosition::Leftmost => self.lower_bound.get(),
            MatchPosition::Rightmost => self.upper_bound
        }
    }
}

impl<'f, ITEM, FIND_OP> Consumer<ITEM> for FindConsumer<'f, FIND_OP>
    where ITEM: Send,
          FIND_OP: Fn(&ITEM) -> bool + Sync
{
    type Folder = FindFolder<'f, ITEM, FIND_OP>;
    type Reducer = FindReducer;
    type Result = Option<ITEM>;

    fn cost(&mut self, cost: f64) -> f64 {
        cost * FUNC_ADJUSTMENT
    }

    fn split_at(self, _index: usize) -> (Self, Self, Self::Reducer) {
        let dir = self.match_position;
        (self.split_off(),
         self,
         FindReducer { match_position: dir })
    }

    fn into_folder(self) -> Self::Folder {
        FindFolder {
            find_op: self.find_op,
            boundary: self.current_index(),
            match_position: self.match_position,
            best_found: self.best_found,
            item: None,
        }
    }

    fn full(&self) -> bool {
        // can stop consuming if the best found index so far is *strictly*
        // better than anything this consumer will find
        better_position(self.best_found.load(Ordering::Relaxed),
                        self.current_index(),
                        self.match_position)
    }
}

impl<'f, ITEM, FIND_OP> UnindexedConsumer<ITEM> for FindConsumer<'f, FIND_OP>
    where ITEM: Send,
          FIND_OP: Fn(&ITEM) -> bool + Sync
{
    fn split_off(&self) -> Self {
        // Upper bound for one consumer will be lower bound for the other. This
        // overlap is okay, because only one of the bounds will be used for
        // comparing against best_found; the other is kept only to be able to
        // divide the range in half
        let old_lower_bound = self.lower_bound.get();
        let median = old_lower_bound + ((self.upper_bound - old_lower_bound) / 2);
        self.lower_bound.set(median);

        FindConsumer {
            find_op: self.find_op,
            lower_bound: Cell::new(old_lower_bound),
            upper_bound: median,
            match_position: self.match_position,
            best_found: self.best_found,
        }
    }

    fn to_reducer(&self) -> Self::Reducer {
        FindReducer { match_position: self.match_position }
    }
}

struct FindFolder<'f, ITEM, FIND_OP: 'f> {
    find_op: &'f FIND_OP,
    boundary: usize,
    match_position: MatchPosition,
    best_found: &'f AtomicUsize,
    item: Option<ITEM>,
}

impl<'f, FIND_OP: 'f + Fn(&ITEM) -> bool, ITEM> Folder<ITEM> for FindFolder<'f, ITEM, FIND_OP> {
    type Result = Option<ITEM>;

    fn consume(mut self, item: ITEM) -> Self {
        if (self.find_op)(&item) {
            // Continuously try to set best_found until we succeed or we
            // discover a better match was already found.
            let mut current = self.best_found.load(Ordering::Relaxed);
            let mut exchange_result = Result::Err(0);
            while better_position(self.boundary, current, self.match_position) &&
                  exchange_result.is_err() {
                exchange_result = self.best_found.compare_exchange_weak(current,
                                                                        self.boundary,
                                                                        Ordering::Relaxed,
                                                                        Ordering::Relaxed);
                if exchange_result.is_err() {
                    current = self.best_found.load(Ordering::Relaxed);
                }
            }

            if exchange_result.is_ok() {
                self.item = Some(item);
            }
        }
        self
    }

    fn complete(self) -> Self::Result {
        self.item
    }

    fn full(&self) -> bool {
        better_position(self.best_found.load(Ordering::Relaxed),
                        self.boundary,
                        self.match_position)
    }
}

struct FindReducer {
    match_position: MatchPosition
}

impl<ITEM> Reducer<Option<ITEM>> for FindReducer {
    fn reduce(self, left: Option<ITEM>, right: Option<ITEM>) -> Option<ITEM> {
        match self.match_position {
            MatchPosition::Leftmost => left.or(right),
            MatchPosition::Rightmost => right.or(left)
        }
    }
}
<|endoftext|>


<commit_before>use std::io::Read;
use std::convert::From;
use std::str;
use hyper::client;
use hyper::header::{ContentLength, ContentType};
use hyper::mime::{Mime, TopLevel, SubLevel};
use hyper::status::StatusCode;
use serde::Deserialize;
use serde_json;

pub type HyperResponse = client::Response;

#[derive(Debug)]
pub struct Response {
    content: Vec<u8>,
    hr: HyperResponse,
}

impl From<HyperResponse> for Response {
    fn from(mut raw: HyperResponse) -> Self {
        let mut content = match raw.headers.get::<ContentLength>() {
            Some(&ContentLength(length)) => Vec::with_capacity(length as usize),
            None => Vec::new(),
        };

        if raw.read_to_end(&mut content).is_err() {
            content = Vec::new()
        }

        Response {
            content: content,
            hr: raw,
        }
    }
}

impl<'a> Response {
    pub fn url(&self) -> String {
        self.hr.url.serialize()
    }

    pub fn status_code(&self) -> StatusCode {
        self.hr.status
    }

    pub fn reason(&self) -> String {
        self.hr.status.canonical_reason().unwrap_or("UNAVAILABLE").to_owned()
    }

    pub fn ok(&self) -> bool {
        self.hr.status == StatusCode::Ok
    }

    pub fn text(&'a self) -> Option<&'a str> {
        str::from_utf8(&self.content).ok()
    }

    pub fn json(&self) -> Option<String> {
        if self.is_json() {
            self.text().map(|x| x.to_owned())
        } else {
            None
        }
    }

    pub fn from_json<T>(&self) -> Option<T> where T: Deserialize {
        self.text().and_then(|t| serde_json::from_str::<T>(t).ok())
    }

    pub fn is_json(&self) -> bool {
        match self.hr.headers.get::<ContentType>() {
            Some(&ContentType(Mime(TopLevel::Application, SubLevel::Json, _))) => true,
            _ => false,
        }
    }
}
<commit_msg>Change Response::url() return type<commit_after>use std::io::Read;
use std::convert::From;
use std::str;
use hyper::client;
use hyper::header::{ContentLength, ContentType};
use hyper::mime::{Mime, TopLevel, SubLevel};
use hyper::status::StatusCode;
use serde::Deserialize;
use serde_json;

pub type HyperResponse = client::Response;

#[derive(Debug)]
pub struct Response {
    content: Vec<u8>,
    hr: HyperResponse,
}

impl From<HyperResponse> for Response {
    fn from(mut raw: HyperResponse) -> Self {
        let mut content = match raw.headers.get::<ContentLength>() {
            Some(&ContentLength(length)) => Vec::with_capacity(length as usize),
            None => Vec::new(),
        };

        if raw.read_to_end(&mut content).is_err() {
            content = Vec::new()
        }

        Response {
            content: content,
            hr: raw,
        }
    }
}

impl<'a> Response {
    pub fn url(&self) -> &str {
        self.hr.url.as_str()
    }

    pub fn status_code(&self) -> StatusCode {
        self.hr.status
    }

    pub fn reason(&self) -> String {
        self.hr.status.canonical_reason().unwrap_or("UNAVAILABLE").to_owned()
    }

    pub fn ok(&self) -> bool {
        self.hr.status == StatusCode::Ok
    }

    pub fn text(&'a self) -> Option<&'a str> {
        str::from_utf8(&self.content).ok()
    }

    pub fn json(&self) -> Option<String> {
        if self.is_json() {
            self.text().map(|x| x.to_owned())
        } else {
            None
        }
    }

    pub fn from_json<T>(&self) -> Option<T> where T: Deserialize {
        self.text().and_then(|t| serde_json::from_str::<T>(t).ok())
    }

    pub fn is_json(&self) -> bool {
        match self.hr.headers.get::<ContentType>() {
            Some(&ContentType(Mime(TopLevel::Application, SubLevel::Json, _))) => true,
            _ => false,
        }
    }
}
<|endoftext|>


<commit_before>//! Types for the *m.room.guest_access* event.
<commit_msg>Add GuestAccessEvent.<commit_after>//! Types for the *m.room.guest_access* event.

use core::{Event, EventType, RoomEvent, StateEvent};

/// Controls whether guest users are allowed to join rooms.
///
/// This event controls whether guest users are allowed to join rooms. If this event is absent,
/// servers should act as if it is present and has the value `GuestAccess::Forbidden`.
pub struct GuestAccessEvent<'a, 'b> {
    content: GuestAccessEventContent<'a>,
    event_id: &'a str,
    prev_content: Option<GuestAccessEventContent<'b>>,
    room_id: &'a str,
    user_id: &'a str,
}

impl<'a, 'b> Event<'a, GuestAccessEventContent<'a>> for GuestAccessEvent<'a, 'b> {
    fn content(&'a self) -> &'a GuestAccessEventContent<'a> {
        &self.content
    }

    fn event_type(&self) -> EventType {
        EventType::RoomGuestAccess
    }
}

impl<'a, 'b> RoomEvent<'a, GuestAccessEventContent<'a>> for GuestAccessEvent<'a, 'b> {
    fn event_id(&'a self) -> &'a str {
        &self.event_id
    }

    fn room_id(&'a self) -> &'a str {
        &self.room_id
    }

    fn user_id(&'a self) -> &'a str {
        &self.user_id
    }
}

impl<'a, 'b> StateEvent<'a, 'b, GuestAccessEventContent<'a>> for GuestAccessEvent<'a, 'b> {
    fn prev_content(&'a self) -> Option<&'b GuestAccessEventContent> {
        match self.prev_content {
            Some(ref prev_content) => Some(prev_content),
            None => None,
        }
    }

    fn state_key(&self) -> &'a str {
        ""
    }
}

/// The payload of a `GuestAccessEvent`.
pub struct GuestAccessEventContent<'a> {
    guest_access: &'a GuestAccess,
}

/// A policy for guest user access to a room.
pub enum GuestAccess {
    /// Guests are allowed to join the room.
    CanJoin,
    /// Guests are not allowed to join the room.
    Forbidden,
}
<|endoftext|>


<commit_before>use super::*;
use super::super::packets::{self, Encodable, Decodable};
use super::super::status_translator;
use crc::crc32;
use byteorder::{self, BigEndian, ByteOrder};
use std::collections;
use std::net;
use std::thread;
use std::io;
use std::sync::mpsc;
use mio;
use mio::udp;

const SOCKET_TOKEN: mio::Token = mio::Token(0);

pub enum MioHandlerCommand {
    DoCall(Box<fn(&mut MioHandler)>),
}

/*This exists so that we can lend these buffers around too, not just the socket.
If we implement just for the socket, then everything has to figure out its own buffer.*/
pub struct MioSocketState<'a> {
    socket: &'a udp::UdpSocket,
    incoming_packet_buffer: [u8; 1000],
    outgoing_packet_buffer: [u8; 1000],
}

pub struct MioHandler<'a> {
    socket_state: MioSocketState<'a>,
    connections: collections::HashMap<net::SocketAddr, Connection>,
    next_connection_id: u64,
    status_translator: status_translator::StatusTranslator,
}

impl<'a> MioHandler<'a> {
    pub fn new(socket: &'a udp::UdpSocket)->MioHandler<'a> {
        MioHandler {
            socket_state: MioSocketState {
                socket: socket,
                incoming_packet_buffer: [0u8; 1000],
                outgoing_packet_buffer: [0u8; 1000],
            },
            connections: collections::HashMap::new(),
            next_connection_id: 0,
            status_translator: status_translator::StatusTranslator::new(true, packets::PROTOCOL_VERSION, &[""; 0]),
        }
    }

    fn got_packet(&mut self, size: usize, address: net::SocketAddr) {
        if size == 0 {return;}
        let maybe_packet = {
            let slice = &self.socket_state.incoming_packet_buffer[0..size];
            let computed_checksum = crc32::checksum_castagnoli(&slice[4..]);
            let expected_checksum = BigEndian::read_u32(&slice[..4]);
            if computed_checksum != expected_checksum {Err(packets::PacketDecodingError::Invalid)}
            else {packets::decode_packet(&slice[4..])}
        };
        if let Err(_) = maybe_packet {return;}
        let packet = maybe_packet.unwrap();
        if let Some(ref mut conn) = self.connections.get_mut(&address) {
            if conn.handle_incoming_packet(&packet, &mut self.socket_state) {return;}
        }
        match packet {
            packets ::Packet::Connect => {
                if let Some(_) = self.connections.get(&address) {return;}
                let id = self.next_connection_id;
                self.next_connection_id += 1;
                let conn = Connection::new(address, id);
                self.connections.insert(address, conn);
                self.socket_state.send(&packets::Packet::Connected(id), address);
            },
            packets::Packet::StatusRequest(ref req) => {
                self.socket_state.send(&packets::Packet::StatusResponse(self.status_translator.translate(req)), address);
            },
            _ => {}
        }
    }
}

impl<'a> PacketSender for MioSocketState<'a> {
    fn send(&mut self, packet: &packets::Packet, address: net::SocketAddr)->bool {
        if let Ok(size) = packets::encode_packet(packet, &mut self.outgoing_packet_buffer[4..]) {
            let checksum = crc32::checksum_castagnoli(&self.outgoing_packet_buffer[4..size]);
            BigEndian::write_u32(&mut self.outgoing_packet_buffer[..4], checksum);
            if let Ok(Some(sent_bytes)) = self.socket.send_to(&self.outgoing_packet_buffer[..4+size], &address) {
                if sent_bytes == 4+size {return true;}
                else {return false;}
            }
            else {return false;}
        }
        else {return false;};
    }
}

impl<'a> mio::Handler for MioHandler<'a> {
    type Timeout = ();
    type Message = MioHandlerCommand;

    fn ready(&mut self, event_loop: &mut mio::EventLoop<Self>, token: mio::Token, events: mio::EventSet) {
        //We only have one socket, so can avoid the match on the token.
        if events.is_error() {
            //We need to do something sensible here, probably a callback with whatever state we can get.
        }
        if events.is_readable() {
            let result = self.socket_state.socket.recv_from(&mut self.socket_state.incoming_packet_buffer);
            if let Ok(Some((size, address))) = result {
                self.got_packet(size, address);
            }
        }
    }
}

fn mio_server_thread(address: net::SocketAddr, notify_created: mpsc::Sender<Result<mio::Sender<MioHandlerCommand>, io::Error>>) {
    let maybe_socket = match address {
        net::SocketAddr::V4(_) => udp::UdpSocket::v4(),
        net::SocketAddr::V6(_) => udp::UdpSocket::v6()
    };
    if let Err(what) = maybe_socket {
        notify_created.send(Err(what)).unwrap();
        return;
    }
    let socket = maybe_socket.unwrap();
    if let  Err(what) = socket.bind(&address) {
        notify_created.send(Err(what)).unwrap();
        return;
    }
    let maybe_loop  = mio::EventLoop::new();
    if let Err(what) = maybe_loop {
        notify_created.send(Err(what)).unwrap();
        return;
    }
    let mut event_loop = maybe_loop.unwrap();
    let mut handler = MioHandler::new(&socket);
    if let Err(what)  = event_loop.register(&socket, SOCKET_TOKEN, mio::EventSet::all(), mio::PollOpt::all()) {
        notify_created.send(Err(what)).unwrap();
        return;
    }
    let sender = event_loop.channel();
    notify_created.send(Ok(sender));
    event_loop.run(&mut handler);
}

pub struct MioServer {
    thread: thread::JoinHandle<()>,
    sender: mio::Sender<MioHandlerCommand>,
}

impl MioServer {
    fn new(address: net::SocketAddr)->Result<MioServer, io::Error> {
        let (sender, receiver) = mpsc::channel();
        let join_handle = thread::spawn(move || mio_server_thread(address, sender));
        let message_sender = try!(receiver.recv().unwrap());
        Ok(MioServer {
            thread: join_handle,
            sender: message_sender,
        })
    }
}
<commit_msg>Ids start from 1, not zero.<commit_after>use super::*;
use super::super::packets::{self, Encodable, Decodable};
use super::super::status_translator;
use crc::crc32;
use byteorder::{self, BigEndian, ByteOrder};
use std::collections;
use std::net;
use std::thread;
use std::io;
use std::sync::mpsc;
use mio;
use mio::udp;

const SOCKET_TOKEN: mio::Token = mio::Token(0);

pub enum MioHandlerCommand {
    DoCall(Box<fn(&mut MioHandler)>),
}

/*This exists so that we can lend these buffers around too, not just the socket.
If we implement just for the socket, then everything has to figure out its own buffer.*/
pub struct MioSocketState<'a> {
    socket: &'a udp::UdpSocket,
    incoming_packet_buffer: [u8; 1000],
    outgoing_packet_buffer: [u8; 1000],
}

pub struct MioHandler<'a> {
    socket_state: MioSocketState<'a>,
    connections: collections::HashMap<net::SocketAddr, Connection>,
    next_connection_id: u64,
    status_translator: status_translator::StatusTranslator,
}

impl<'a> MioHandler<'a> {
    pub fn new(socket: &'a udp::UdpSocket)->MioHandler<'a> {
        MioHandler {
            socket_state: MioSocketState {
                socket: socket,
                incoming_packet_buffer: [0u8; 1000],
                outgoing_packet_buffer: [0u8; 1000],
            },
            connections: collections::HashMap::new(),
            next_connection_id: 1,
            status_translator: status_translator::StatusTranslator::new(true, packets::PROTOCOL_VERSION, &[""; 0]),
        }
    }

    fn got_packet(&mut self, size: usize, address: net::SocketAddr) {
        if size == 0 {return;}
        let maybe_packet = {
            let slice = &self.socket_state.incoming_packet_buffer[0..size];
            let computed_checksum = crc32::checksum_castagnoli(&slice[4..]);
            let expected_checksum = BigEndian::read_u32(&slice[..4]);
            if computed_checksum != expected_checksum {Err(packets::PacketDecodingError::Invalid)}
            else {packets::decode_packet(&slice[4..])}
        };
        if let Err(_) = maybe_packet {return;}
        let packet = maybe_packet.unwrap();
        if let Some(ref mut conn) = self.connections.get_mut(&address) {
            if conn.handle_incoming_packet(&packet, &mut self.socket_state) {return;}
        }
        match packet {
            packets ::Packet::Connect => {
                if let Some(_) = self.connections.get(&address) {return;}
                let id = self.next_connection_id;
                self.next_connection_id += 1;
                let conn = Connection::new(address, id);
                self.connections.insert(address, conn);
                self.socket_state.send(&packets::Packet::Connected(id), address);
            },
            packets::Packet::StatusRequest(ref req) => {
                self.socket_state.send(&packets::Packet::StatusResponse(self.status_translator.translate(req)), address);
            },
            _ => {}
        }
    }
}

impl<'a> PacketSender for MioSocketState<'a> {
    fn send(&mut self, packet: &packets::Packet, address: net::SocketAddr)->bool {
        if let Ok(size) = packets::encode_packet(packet, &mut self.outgoing_packet_buffer[4..]) {
            let checksum = crc32::checksum_castagnoli(&self.outgoing_packet_buffer[4..size]);
            BigEndian::write_u32(&mut self.outgoing_packet_buffer[..4], checksum);
            if let Ok(Some(sent_bytes)) = self.socket.send_to(&self.outgoing_packet_buffer[..4+size], &address) {
                if sent_bytes == 4+size {return true;}
                else {return false;}
            }
            else {return false;}
        }
        else {return false;};
    }
}

impl<'a> mio::Handler for MioHandler<'a> {
    type Timeout = ();
    type Message = MioHandlerCommand;

    fn ready(&mut self, event_loop: &mut mio::EventLoop<Self>, token: mio::Token, events: mio::EventSet) {
        //We only have one socket, so can avoid the match on the token.
        if events.is_error() {
            //We need to do something sensible here, probably a callback with whatever state we can get.
        }
        if events.is_readable() {
            let result = self.socket_state.socket.recv_from(&mut self.socket_state.incoming_packet_buffer);
            if let Ok(Some((size, address))) = result {
                self.got_packet(size, address);
            }
        }
    }
}

fn mio_server_thread(address: net::SocketAddr, notify_created: mpsc::Sender<Result<mio::Sender<MioHandlerCommand>, io::Error>>) {
    let maybe_socket = match address {
        net::SocketAddr::V4(_) => udp::UdpSocket::v4(),
        net::SocketAddr::V6(_) => udp::UdpSocket::v6()
    };
    if let Err(what) = maybe_socket {
        notify_created.send(Err(what)).unwrap();
        return;
    }
    let socket = maybe_socket.unwrap();
    if let  Err(what) = socket.bind(&address) {
        notify_created.send(Err(what)).unwrap();
        return;
    }
    let maybe_loop  = mio::EventLoop::new();
    if let Err(what) = maybe_loop {
        notify_created.send(Err(what)).unwrap();
        return;
    }
    let mut event_loop = maybe_loop.unwrap();
    let mut handler = MioHandler::new(&socket);
    if let Err(what)  = event_loop.register(&socket, SOCKET_TOKEN, mio::EventSet::all(), mio::PollOpt::all()) {
        notify_created.send(Err(what)).unwrap();
        return;
    }
    let sender = event_loop.channel();
    notify_created.send(Ok(sender));
    event_loop.run(&mut handler);
}

pub struct MioServer {
    thread: thread::JoinHandle<()>,
    sender: mio::Sender<MioHandlerCommand>,
}

impl MioServer {
    fn new(address: net::SocketAddr)->Result<MioServer, io::Error> {
        let (sender, receiver) = mpsc::channel();
        let join_handle = thread::spawn(move || mio_server_thread(address, sender));
        let message_sender = try!(receiver.recv().unwrap());
        Ok(MioServer {
            thread: join_handle,
            sender: message_sender,
        })
    }
}
<|endoftext|>


<commit_before>//! Functions for setting configuration and executing the generator.

use cpp_to_rust_generator::common::errors::Result;
use cpp_to_rust_generator::common::{log, toml};
use cpp_to_rust_generator::common::file_utils::{PathBufWithAdded, repo_crate_local_path};
use cpp_to_rust_generator::config::{Config, CacheUsage, DebugLoggingConfig, exec};
use cpp_to_rust_generator::cpp_data::CppVisibility;
use cpp_to_rust_generator::common::cpp_build_config::{CppBuildConfigData, CppLibraryType};
use cpp_to_rust_generator::common::target;
use qt_generator_common::{get_installation_data, lib_folder_name, lib_dependencies};
use std::path::PathBuf;
use versions;

use doc_parser::DocParser;
use fix_header_names::fix_header_names;
use cpp_to_rust_generator::cpp_method::CppMethod;
use cpp_to_rust_generator::cpp_data::CppTypeKind;
use cpp_to_rust_generator::config::{CrateProperties, is_completed};
use doc_decoder::DocData;
use lib_configs;

/// Options passed to `exec_all`,
/// as in `cpp_to_rust_generator::config::Config`.
pub struct ExecConfig {
  pub write_dependencies_local_paths: bool,
  pub cache_usage: CacheUsage,
  pub write_cache: bool,
  pub debug_logging_config: DebugLoggingConfig,
  pub quiet_mode: bool,
}

/// Executes generator for `libs` with given configuration.
pub fn exec_all(libs: Vec<String>,
                cache_dir: PathBuf,
                output_dir: PathBuf,
                config: ExecConfig)
                -> Result<()> {
  if config.quiet_mode {
    let mut logger = log::default_logger();

    logger.set_category_settings(log::Status,
                                 log::LoggerSettings {
                                   file_path: None,
                                   write_to_stderr: false,
                                 });
  }

  let crate_templates_path =
    PathBuf::from(env!("CARGO_MANIFEST_DIR")).with_added("crate_templates");
  let final_libs = if libs.iter().any(|x| x == "all") {
    vec!["core".to_string(),
         "gui".to_string(),
         "widgets".to_string(),
         "3d_core".to_string(),
         "3d_render".to_string(),
         "3d_input".to_string(),
         "3d_logic".to_string(),
         "3d_extras".to_string(),
         "ui_tools".to_string()]
  } else {
    libs
  };
  let mut configs: Vec<Config> = Vec::new();
  for sublib_name in final_libs {
    let lib_cache_dir = cache_dir.with_added(format!("qt_{}", sublib_name));
    let lib_crate_templates_path = crate_templates_path.with_added(&sublib_name);
    let lib_output_dir = output_dir.with_added(format!("qt_{}", sublib_name));

    let mut dependency_paths = Vec::new();
    for dep in lib_dependencies(&sublib_name)? {
      let path = cache_dir.with_added(format!("qt_{}", dep));
      if !configs.iter().any(|c| c.cache_dir_path() == &path) && !is_completed(&path) {
        return Err(format!("\"{}\" depends on \"{}\" but processing \
          in \"{}\" directory is not completed.",
                           sublib_name,
                           dep,
                           path.display())
                       .into());
      }
      dependency_paths.push(path);
    }
    if is_completed(&lib_cache_dir) && config.cache_usage.can_skip_all() {
      log::status("No processing! cpp_to_rust uses previous results.");
      log::status("Run with -C0 to force full processing.");
      continue;
    }
    configs.push(make_config(&sublib_name,
                             lib_cache_dir,
                             lib_output_dir,
                             lib_crate_templates_path,
                             dependency_paths,
                             &config)?);
  }
  exec(configs.into_iter())?;
  Ok(())
}

/// Executes the generator for a single Qt module with given configuration.
fn make_config(sublib_name: &str,
               cache_dir: PathBuf,
               output_dir: PathBuf,
               crate_templates_path: PathBuf,
               dependency_paths: Vec<PathBuf>,
               exec_config: &ExecConfig)
               -> Result<Config> {
  log::status(format!("Preparing generator config for library: {}", sublib_name));
  let crate_name = format!("qt_{}", sublib_name);
  let mut crate_properties = CrateProperties::new(crate_name.clone(),
                                                  versions::QT_OUTPUT_CRATES_VERSION);
  let mut custom_fields = toml::Table::new();
  let mut package_data = toml::Table::new();
  package_data.insert("authors".to_string(),
                      toml::Value::Array(vec![toml::Value::String("Pavel Strakhov <ri@idzaaus.org>"
                                                                    .to_string())]));
  let description = format!("Bindings for {} C++ library (generated automatically with cpp_to_rust project)",
                            lib_folder_name(sublib_name));
  package_data.insert("description".to_string(), toml::Value::String(description));
  let doc_url = format!("https://rust-qt.github.io/rustdoc/qt/{}", &crate_name);
  package_data.insert("documentation".to_string(), toml::Value::String(doc_url));
  package_data.insert("repository".to_string(),
                      toml::Value::String("https://github.com/rust-qt/cpp_to_rust".to_string()));
  package_data.insert("license".to_string(),
                      toml::Value::String("MIT".to_string()));

  custom_fields.insert("package".to_string(), toml::Value::Table(package_data));
  crate_properties.set_custom_fields(custom_fields);
  crate_properties.remove_default_build_dependencies();
  let qt_build_tools_path = if exec_config.write_dependencies_local_paths {
    Some(repo_crate_local_path("qt_generator/qt_build_tools")?)
  } else {
    None
  };
  crate_properties.add_build_dependency("qt_build_tools",
                                        versions::QT_BUILD_TOOLS_VERSION,
                                        qt_build_tools_path);
  let mut config = Config::new(&output_dir, &cache_dir, crate_properties);
  let installation_data = get_installation_data(sublib_name)?;
  config.add_include_path(&installation_data.root_include_path);
  config.add_include_path(&installation_data.lib_include_path);
  for dep in lib_dependencies(&sublib_name)? {
    let dep_data = get_installation_data(dep)?;
    config.add_include_path(&dep_data.lib_include_path);
  }
  config.add_target_include_path(&installation_data.lib_include_path);
  config.set_cache_usage(exec_config.cache_usage.clone());
  config.set_write_dependencies_local_paths(exec_config.write_dependencies_local_paths);
  config.set_write_cache(exec_config.write_cache);
  config.set_quiet_mode(exec_config.quiet_mode);
  config.set_debug_logging_config(exec_config.debug_logging_config.clone());
  config.set_cpp_lib_version(installation_data.qt_version.as_str());
  if exec_config.write_dependencies_local_paths {
    log::status("Output Cargo.toml file will contain local paths of used dependencies \
               (use --no-local-paths to disable).");
  } else {
    log::status("Local paths will not be written to the output crate. Make sure all dependencies \
               are published before trying to compile the crate.");
  }
  // TODO: does parsing work on MacOS without adding "-F"?

  config.add_include_directive(&lib_folder_name(sublib_name));
  let lib_include_path = installation_data.lib_include_path.clone();
  config.add_cpp_data_filter(move |cpp_data| fix_header_names(cpp_data, &lib_include_path));
  config.add_cpp_parser_arguments(vec!["-fPIC", "-fcxx-exceptions"]);
  {
    let mut data = CppBuildConfigData::new();
    data.add_compiler_flag("-std=gnu++11");
    config
      .cpp_build_config_mut()
      .add(target::Condition::Env(target::Env::Msvc).negate(), data);
  }
  {
    let mut data = CppBuildConfigData::new();
    data.add_compiler_flag("-fPIC");
    // msvc and mingw don't need this
    config
      .cpp_build_config_mut()
      .add(target::Condition::OS(target::OS::Windows).negate(), data);
  }
  {
    let mut data = CppBuildConfigData::new();
    data.set_library_type(CppLibraryType::Shared);
    config
      .cpp_build_config_mut()
      .add(target::Condition::Env(target::Env::Msvc), data);
  }

  if target::current_env() == target::Env::Msvc {
    config.add_cpp_parser_argument("-std=c++14");
  } else {
    config.add_cpp_parser_argument("-std=gnu++11");
  }
  config.add_cpp_parser_blocked_name("qt_check_for_QGADGET_macro");
  let sublib_name_clone = sublib_name.to_string();
  let docs_path = installation_data.docs_path.clone();

  config.add_cpp_data_filter(move |cpp_data| {
    match DocData::new(&sublib_name_clone, &docs_path) {
      Ok(doc_data) => {
        let mut parser = DocParser::new(doc_data);
        find_methods_docs(&mut cpp_data.methods, &mut parser)?;
        for type1 in &mut cpp_data.types {
          match parser.doc_for_type(&type1.name) {
            Ok(doc) => {
              // log::debug(format!("Found doc for type: {}", type1.name));
              type1.doc = Some(doc.0);
              if let CppTypeKind::Enum { ref mut values } = type1.kind {
                let enum_namespace = if let Some(index) = type1.name.rfind("::") {
                  type1.name[0..index + 2].to_string()
                } else {
                  String::new()
                };
                for value in values {
                  if let Some(r) = doc.1.iter().find(|x| x.name == value.name) {
                    value.doc = Some(r.html.clone());

                    // let full_name = format!("{}::{}", enum_namespace, &value.name);
                    // println!("full name: {}", full_name);
                    parser.mark_enum_variant_used(&format!("{}{}", enum_namespace, &value.name));

                  } else {
                    let type_name = &type1.name;
                    log::llog(log::DebugQtDoc, || {
                      format!("Not found doc for enum variant: {}::{}",
                              type_name,
                              &value.name)
                    });
                  }
                }
              }
            }
            Err(err) => {
              log::llog(log::DebugQtDoc,
                        || format!("Not found doc for type: {}: {}", type1.name, err));
            }
          }
        }
        parser.report_unused_anchors();
      }
      Err(err) => {
        log::error(format!("Failed to get Qt documentation: {}", err));
        err.discard_expected();
      }
    }
    Ok(())
  });

  config.set_crate_template_path(crate_templates_path);
  match sublib_name {
    "core" => lib_configs::core(&mut config)?,
    "gui" => lib_configs::gui(&mut config)?,
    "widgets" => lib_configs::widgets(&mut config)?,
    "3d_core" => lib_configs::core_3d(&mut config)?,
    "3d_render" => lib_configs::render_3d(&mut config)?,
    "3d_input" => lib_configs::input_3d(&mut config)?,
    "3d_logic" => lib_configs::logic_3d(&mut config)?,
    "3d_extras" => lib_configs::extras_3d(&mut config)?,
    "ui_tools" => {}
    _ => return Err(format!("Unknown lib name: {}", sublib_name).into()),
  }

  config.set_dependency_cache_paths(dependency_paths);
  Ok(config)
}

/// Adds documentation from `data` to `cpp_methods`.
fn find_methods_docs(cpp_methods: &mut [CppMethod], data: &mut DocParser) -> Result<()> {
  for cpp_method in cpp_methods {
    if let Some(ref info) = cpp_method.class_membership {
      if info.visibility == CppVisibility::Private {
        continue;
      }
    }
    if let Some(ref declaration_code) = cpp_method.declaration_code {
      match data.doc_for_method(&cpp_method.doc_id(),
                                declaration_code,
                                &cpp_method.short_text()) {
        Ok(doc) => cpp_method.doc = Some(doc),
        Err(msg) => {
          if cpp_method.class_membership.is_some() &&
             (&cpp_method.name == "tr" || &cpp_method.name == "trUtf8" ||
              &cpp_method.name == "metaObject") {
            // no error message
          } else {
            log::llog(log::DebugQtDoc, || {
              format!("Failed to get documentation for method: {}: {}",
                      &cpp_method.short_text(),
                      msg)
            });
          }
        }
      }
    }
  }
  Ok(())
}
<commit_msg>change all libs order<commit_after>//! Functions for setting configuration and executing the generator.

use cpp_to_rust_generator::common::errors::Result;
use cpp_to_rust_generator::common::{log, toml};
use cpp_to_rust_generator::common::file_utils::{PathBufWithAdded, repo_crate_local_path};
use cpp_to_rust_generator::config::{Config, CacheUsage, DebugLoggingConfig, exec};
use cpp_to_rust_generator::cpp_data::CppVisibility;
use cpp_to_rust_generator::common::cpp_build_config::{CppBuildConfigData, CppLibraryType};
use cpp_to_rust_generator::common::target;
use qt_generator_common::{get_installation_data, lib_folder_name, lib_dependencies};
use std::path::PathBuf;
use versions;

use doc_parser::DocParser;
use fix_header_names::fix_header_names;
use cpp_to_rust_generator::cpp_method::CppMethod;
use cpp_to_rust_generator::cpp_data::CppTypeKind;
use cpp_to_rust_generator::config::{CrateProperties, is_completed};
use doc_decoder::DocData;
use lib_configs;

/// Options passed to `exec_all`,
/// as in `cpp_to_rust_generator::config::Config`.
pub struct ExecConfig {
  pub write_dependencies_local_paths: bool,
  pub cache_usage: CacheUsage,
  pub write_cache: bool,
  pub debug_logging_config: DebugLoggingConfig,
  pub quiet_mode: bool,
}

/// Executes generator for `libs` with given configuration.
pub fn exec_all(libs: Vec<String>,
                cache_dir: PathBuf,
                output_dir: PathBuf,
                config: ExecConfig)
                -> Result<()> {
  if config.quiet_mode {
    let mut logger = log::default_logger();

    logger.set_category_settings(log::Status,
                                 log::LoggerSettings {
                                   file_path: None,
                                   write_to_stderr: false,
                                 });
  }

  let crate_templates_path =
    PathBuf::from(env!("CARGO_MANIFEST_DIR")).with_added("crate_templates");
  let final_libs = if libs.iter().any(|x| x == "all") {
    vec!["core".to_string(),
         "gui".to_string(),
         "widgets".to_string(),
         "ui_tools".to_string(),
         "3d_core".to_string(),
         "3d_render".to_string(),
         "3d_input".to_string(),
         "3d_logic".to_string(),
         "3d_extras".to_string()]
  } else {
    libs
  };
  let mut configs: Vec<Config> = Vec::new();
  for sublib_name in final_libs {
    let lib_cache_dir = cache_dir.with_added(format!("qt_{}", sublib_name));
    let lib_crate_templates_path = crate_templates_path.with_added(&sublib_name);
    let lib_output_dir = output_dir.with_added(format!("qt_{}", sublib_name));

    let mut dependency_paths = Vec::new();
    for dep in lib_dependencies(&sublib_name)? {
      let path = cache_dir.with_added(format!("qt_{}", dep));
      if !configs.iter().any(|c| c.cache_dir_path() == &path) && !is_completed(&path) {
        return Err(format!("\"{}\" depends on \"{}\" but processing \
          in \"{}\" directory is not completed.",
                           sublib_name,
                           dep,
                           path.display())
                       .into());
      }
      dependency_paths.push(path);
    }
    if is_completed(&lib_cache_dir) && config.cache_usage.can_skip_all() {
      log::status("No processing! cpp_to_rust uses previous results.");
      log::status("Run with -C0 to force full processing.");
      continue;
    }
    configs.push(make_config(&sublib_name,
                             lib_cache_dir,
                             lib_output_dir,
                             lib_crate_templates_path,
                             dependency_paths,
                             &config)?);
  }
  exec(configs.into_iter())?;
  Ok(())
}

/// Executes the generator for a single Qt module with given configuration.
fn make_config(sublib_name: &str,
               cache_dir: PathBuf,
               output_dir: PathBuf,
               crate_templates_path: PathBuf,
               dependency_paths: Vec<PathBuf>,
               exec_config: &ExecConfig)
               -> Result<Config> {
  log::status(format!("Preparing generator config for library: {}", sublib_name));
  let crate_name = format!("qt_{}", sublib_name);
  let mut crate_properties = CrateProperties::new(crate_name.clone(),
                                                  versions::QT_OUTPUT_CRATES_VERSION);
  let mut custom_fields = toml::Table::new();
  let mut package_data = toml::Table::new();
  package_data.insert("authors".to_string(),
                      toml::Value::Array(vec![toml::Value::String("Pavel Strakhov <ri@idzaaus.org>"
                                                                    .to_string())]));
  let description = format!("Bindings for {} C++ library (generated automatically with cpp_to_rust project)",
                            lib_folder_name(sublib_name));
  package_data.insert("description".to_string(), toml::Value::String(description));
  let doc_url = format!("https://rust-qt.github.io/rustdoc/qt/{}", &crate_name);
  package_data.insert("documentation".to_string(), toml::Value::String(doc_url));
  package_data.insert("repository".to_string(),
                      toml::Value::String("https://github.com/rust-qt/cpp_to_rust".to_string()));
  package_data.insert("license".to_string(),
                      toml::Value::String("MIT".to_string()));

  custom_fields.insert("package".to_string(), toml::Value::Table(package_data));
  crate_properties.set_custom_fields(custom_fields);
  crate_properties.remove_default_build_dependencies();
  let qt_build_tools_path = if exec_config.write_dependencies_local_paths {
    Some(repo_crate_local_path("qt_generator/qt_build_tools")?)
  } else {
    None
  };
  crate_properties.add_build_dependency("qt_build_tools",
                                        versions::QT_BUILD_TOOLS_VERSION,
                                        qt_build_tools_path);
  let mut config = Config::new(&output_dir, &cache_dir, crate_properties);
  let installation_data = get_installation_data(sublib_name)?;
  config.add_include_path(&installation_data.root_include_path);
  config.add_include_path(&installation_data.lib_include_path);
  for dep in lib_dependencies(&sublib_name)? {
    let dep_data = get_installation_data(dep)?;
    config.add_include_path(&dep_data.lib_include_path);
  }
  config.add_target_include_path(&installation_data.lib_include_path);
  config.set_cache_usage(exec_config.cache_usage.clone());
  config.set_write_dependencies_local_paths(exec_config.write_dependencies_local_paths);
  config.set_write_cache(exec_config.write_cache);
  config.set_quiet_mode(exec_config.quiet_mode);
  config.set_debug_logging_config(exec_config.debug_logging_config.clone());
  config.set_cpp_lib_version(installation_data.qt_version.as_str());
  if exec_config.write_dependencies_local_paths {
    log::status("Output Cargo.toml file will contain local paths of used dependencies \
               (use --no-local-paths to disable).");
  } else {
    log::status("Local paths will not be written to the output crate. Make sure all dependencies \
               are published before trying to compile the crate.");
  }
  // TODO: does parsing work on MacOS without adding "-F"?

  config.add_include_directive(&lib_folder_name(sublib_name));
  let lib_include_path = installation_data.lib_include_path.clone();
  config.add_cpp_data_filter(move |cpp_data| fix_header_names(cpp_data, &lib_include_path));
  config.add_cpp_parser_arguments(vec!["-fPIC", "-fcxx-exceptions"]);
  {
    let mut data = CppBuildConfigData::new();
    data.add_compiler_flag("-std=gnu++11");
    config
      .cpp_build_config_mut()
      .add(target::Condition::Env(target::Env::Msvc).negate(), data);
  }
  {
    let mut data = CppBuildConfigData::new();
    data.add_compiler_flag("-fPIC");
    // msvc and mingw don't need this
    config
      .cpp_build_config_mut()
      .add(target::Condition::OS(target::OS::Windows).negate(), data);
  }
  {
    let mut data = CppBuildConfigData::new();
    data.set_library_type(CppLibraryType::Shared);
    config
      .cpp_build_config_mut()
      .add(target::Condition::Env(target::Env::Msvc), data);
  }

  if target::current_env() == target::Env::Msvc {
    config.add_cpp_parser_argument("-std=c++14");
  } else {
    config.add_cpp_parser_argument("-std=gnu++11");
  }
  config.add_cpp_parser_blocked_name("qt_check_for_QGADGET_macro");
  let sublib_name_clone = sublib_name.to_string();
  let docs_path = installation_data.docs_path.clone();

  config.add_cpp_data_filter(move |cpp_data| {
    match DocData::new(&sublib_name_clone, &docs_path) {
      Ok(doc_data) => {
        let mut parser = DocParser::new(doc_data);
        find_methods_docs(&mut cpp_data.methods, &mut parser)?;
        for type1 in &mut cpp_data.types {
          match parser.doc_for_type(&type1.name) {
            Ok(doc) => {
              // log::debug(format!("Found doc for type: {}", type1.name));
              type1.doc = Some(doc.0);
              if let CppTypeKind::Enum { ref mut values } = type1.kind {
                let enum_namespace = if let Some(index) = type1.name.rfind("::") {
                  type1.name[0..index + 2].to_string()
                } else {
                  String::new()
                };
                for value in values {
                  if let Some(r) = doc.1.iter().find(|x| x.name == value.name) {
                    value.doc = Some(r.html.clone());

                    // let full_name = format!("{}::{}", enum_namespace, &value.name);
                    // println!("full name: {}", full_name);
                    parser.mark_enum_variant_used(&format!("{}{}", enum_namespace, &value.name));

                  } else {
                    let type_name = &type1.name;
                    log::llog(log::DebugQtDoc, || {
                      format!("Not found doc for enum variant: {}::{}",
                              type_name,
                              &value.name)
                    });
                  }
                }
              }
            }
            Err(err) => {
              log::llog(log::DebugQtDoc,
                        || format!("Not found doc for type: {}: {}", type1.name, err));
            }
          }
        }
        parser.report_unused_anchors();
      }
      Err(err) => {
        log::error(format!("Failed to get Qt documentation: {}", err));
        err.discard_expected();
      }
    }
    Ok(())
  });

  config.set_crate_template_path(crate_templates_path);
  match sublib_name {
    "core" => lib_configs::core(&mut config)?,
    "gui" => lib_configs::gui(&mut config)?,
    "widgets" => lib_configs::widgets(&mut config)?,
    "3d_core" => lib_configs::core_3d(&mut config)?,
    "3d_render" => lib_configs::render_3d(&mut config)?,
    "3d_input" => lib_configs::input_3d(&mut config)?,
    "3d_logic" => lib_configs::logic_3d(&mut config)?,
    "3d_extras" => lib_configs::extras_3d(&mut config)?,
    "ui_tools" => {}
    _ => return Err(format!("Unknown lib name: {}", sublib_name).into()),
  }

  config.set_dependency_cache_paths(dependency_paths);
  Ok(config)
}

/// Adds documentation from `data` to `cpp_methods`.
fn find_methods_docs(cpp_methods: &mut [CppMethod], data: &mut DocParser) -> Result<()> {
  for cpp_method in cpp_methods {
    if let Some(ref info) = cpp_method.class_membership {
      if info.visibility == CppVisibility::Private {
        continue;
      }
    }
    if let Some(ref declaration_code) = cpp_method.declaration_code {
      match data.doc_for_method(&cpp_method.doc_id(),
                                declaration_code,
                                &cpp_method.short_text()) {
        Ok(doc) => cpp_method.doc = Some(doc),
        Err(msg) => {
          if cpp_method.class_membership.is_some() &&
             (&cpp_method.name == "tr" || &cpp_method.name == "trUtf8" ||
              &cpp_method.name == "metaObject") {
            // no error message
          } else {
            log::llog(log::DebugQtDoc, || {
              format!("Failed to get documentation for method: {}: {}",
                      &cpp_method.short_text(),
                      msg)
            });
          }
        }
      }
    }
  }
  Ok(())
}
<|endoftext|>


<commit_before>//!

#[cfg(not(windows))]
use libc::*;

#[cfg(windows)]
use kernel32::*

#[cfg(windows)]
use winapi::*;

use std::ffi::CString;

enum FileHandle {
    Posix(c_int),
    Win32(HANDLE),
}

pub enum CreateMode {
    CreateOnly,
    OpenOrCreate,
    OpenOnly,
}

pub enum AccessMode {
    ReadOnly,
    ReadWrite,
}

pub enum Permissions {
    User,
    Group,
    Others
}

pub struct Handle {
    shm_fd: FileHandle,
    name: CString,
    access_mode: AccessMode,
}

impl Handle {
    #[cfg(not(windows))]
    pub fn new(name: &str, create_mode: CreateMode, access_mode: AccessMode, permissions: Permissions) -> Result<Handle, String> {
        let cmode = match create_mode {
            CreateMode::CreateOnly => O_CREAT | O_EXCL,
            CreateMode::OpenOrCreate => O_CREAT,
            CreateMode::OpenOnly => 0,
        };
        let amode = match access_mode {
            AccessMode::ReadOnly => O_RDONLY,
            AccessMode::ReadWrite => O_RDWR,
        };
        let perm = match permissions {
            Permissions::User => S_IRWXU,
            Permissions::Group => S_IRWXG,
            Permissions::Others => S_IRWXO,
        };
        let cstr = match CString::new(name) {
            Err(_) => return Err(format!("Unable to convert to CString: {}", name)),
            Ok(val) => val,
        };
        let fd = unsafe { shm_open(cstr.as_ptr(), cmode | amode as c_int, perm as c_uint) };
        match fd {
            -1 => Err(format!("Unable to open/create shared memory object: {}", name)),
            _ => Ok(Handle {shm_fd: FileHandle::Posix(fd), name: cstr, access_mode: access_mode}),
        }
    }

    #[cfg(not(windows))]
    pub fn remove(name: &str) -> bool {
        let cstr = match CString::new(name) {
            Err(_) => return false,
            Ok(val) => val,
        };
        match unsafe { shm_unlink(cstr.as_ptr()) } {
            -1 => false,
            _ => true,
        }
    }

    #[cfg(windows)]
    pub fn new(name: &str, create_mode: CreateMode, access_mode: AccessMode, permissions: Permissions) -> Result<Handle, String> {
        let cmode = match create_mode {
            CreateMode::CreateOnly => CREATE_NEW,
            CreateMode::OpenOrCreate => OPEN_ALWAYS,
            CreateMode::OpenOnly => OPEN_EXISTING,
        };
        let amode = match access_mode {
            AccessMode::ReadOnly => GENERIC_READ,
            AccessMode::ReadWrite => GENERIC_READ | GENERIC_WRITE,
        };
        let perm = match permissions {
	    // TODO
            _ => NULL,
        };
        let cstr = match CString::new(name) {
            Err(_) => return Err(format!("Unable to convert to CString: {}", name)),
            Ok(val) => val,
        };
        let fd = unsafe { CreateFileA(cstr.as_ptr(), amode, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, perm, cmode, 0, NULL) };
        match fd {
	    INVALID_HANDLE_VALUE => Err(format!("Unable to open/create shared memory object: {}", name)),
            _ =>  Ok(Handle {shm_fd: FileHandle::Win32(fd), name: cstr, access_mode: access_mode}),

        }
    }

    #[cfg(windows)]
    pub fn remove(name: &str) -> bool {
        let cstr = match CString::new(name) {
            Err(_) => return false,
            Ok(val) => val,
        };
        match unsafe { DeleteFile(cstr.as_ptr()) } {
            0 => true,
            _ => false,
        }
    }

    pub fn name(&self) -> String {
        self.name.to_string_lossy().into_owned()
    }

    pub fn access_mode(&self) -> &AccessMode {
        &self.access_mode
    }
}

#[cfg(not(windows))]
impl Drop for Handle {
    fn drop(&mut self) {
        unsafe {
	    match self.shm_fd {
                FileHandle::Posix(fd) => close(fd),
	        _ => {}
	    }
        };
    }
}

#[cfg(windows)]
impl Drop for Handle {
    fn drop(&mut self) {
        unsafe {
	    match self.shm_fd {
                FileHandle::Win32(fd) => CloseHandle(fd),
	        _ => {}
	    }
        };
    }
}

#[cfg(test)]
mod tests {
    #[test]
    fn breath_test_handle() {
        use super::*;
        let name = "handleBreathTest";
        {
            let handle = Handle::new(name, CreateMode::CreateOnly, AccessMode::ReadWrite, Permissions::User).unwrap();
            assert_eq!(handle.name(), name);
            match handle.access_mode() {
                &AccessMode::ReadWrite => {},
                _ => assert!(false, "wrong access mode"),
            };
            assert!(handle.native_handle() >= 0);
        }
        let removed = Handle::remove(name);
        assert!(removed);
    }
}
<commit_msg>win compilation passed<commit_after>//!

#[cfg(not(windows))]
use libc::*;

#[cfg(windows)]
use kernel32::*;

#[cfg(windows)]
use winapi::*;

use std::ffi::CString;
use std::ptr;

enum FileHandle {
    Posix(c_int),
    Win32(HANDLE),
}

pub enum CreateMode {
    CreateOnly,
    OpenOrCreate,
    OpenOnly,
}

pub enum AccessMode {
    ReadOnly,
    ReadWrite,
}

pub enum Permissions {
    User,
    Group,
    Others
}

pub struct Handle {
    shm_fd: FileHandle,
    name: CString,
    access_mode: AccessMode,
}

impl Handle {
    #[cfg(not(windows))]
    pub fn new(name: &str, create_mode: CreateMode, access_mode: AccessMode, permissions: Permissions) -> Result<Handle, String> {
        let cmode = match create_mode {
            CreateMode::CreateOnly => O_CREAT | O_EXCL,
            CreateMode::OpenOrCreate => O_CREAT,
            CreateMode::OpenOnly => 0,
        };
        let amode = match access_mode {
            AccessMode::ReadOnly => O_RDONLY,
            AccessMode::ReadWrite => O_RDWR,
        };
        let perm = match permissions {
            Permissions::User => S_IRWXU,
            Permissions::Group => S_IRWXG,
            Permissions::Others => S_IRWXO,
        };
        let cstr = match CString::new(name) {
            Err(_) => return Err(format!("Unable to convert to CString: {}", name)),
            Ok(val) => val,
        };
        let fd = unsafe { shm_open(cstr.as_ptr(), cmode | amode as c_int, perm as c_uint) };
        match fd {
            -1 => Err(format!("Unable to open/create shared memory object: {}", name)),
            _ => Ok(Handle {shm_fd: FileHandle::Posix(fd), name: cstr, access_mode: access_mode}),
        }
    }

    #[cfg(not(windows))]
    pub fn remove(name: &str) -> bool {
        let cstr = match CString::new(name) {
            Err(_) => return false,
            Ok(val) => val,
        };
        match unsafe { shm_unlink(cstr.as_ptr()) } {
            -1 => false,
            _ => true,
        }
    }

    #[cfg(windows)]
    pub fn new(name: &str, create_mode: CreateMode, access_mode: AccessMode, permissions: Permissions) -> Result<Handle, String> {
        let cmode = match create_mode {
            CreateMode::CreateOnly => CREATE_NEW,
            CreateMode::OpenOrCreate => OPEN_ALWAYS,
            CreateMode::OpenOnly => OPEN_EXISTING,
        };
        let amode = match access_mode {
            AccessMode::ReadOnly => GENERIC_READ,
            AccessMode::ReadWrite => GENERIC_READ | GENERIC_WRITE,
        };
        let perm = match permissions {
	    // TODO
            _ => ptr::null_mut(),
        };
        let cstr = match CString::new(name) {
            Err(_) => return Err(format!("Unable to convert to CString: {}", name)),
            Ok(val) => val,
        };
        let fd = unsafe {
	    CreateFileA(cstr.as_ptr(),
	                amode,
	                FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
	                perm as *mut SECURITY_ATTRIBUTES,
	                cmode,
	                0,
	                ptr::null_mut() as *mut c_void)
	};
        if fd == INVALID_HANDLE_VALUE {
	    Err(format!("Unable to open/create shared memory object: {}", name))
	} else {
            Ok(Handle {shm_fd: FileHandle::Win32(fd), name: cstr, access_mode: access_mode})
	}
    }

    #[cfg(windows)]
    pub fn remove(name: &str) -> bool {
        let cstr = match CString::new(name) {
            Err(_) => return false,
            Ok(val) => val,
        };
        match unsafe { DeleteFileA(cstr.as_ptr()) } {
            0 => true,
            _ => false,
        }
    }

    pub fn name(&self) -> String {
        self.name.to_string_lossy().into_owned()
    }

    pub fn access_mode(&self) -> &AccessMode {
        &self.access_mode
    }
}

#[cfg(not(windows))]
impl Drop for Handle {
    fn drop(&mut self) {
        unsafe {
	    match self.shm_fd {
                FileHandle::Posix(fd) => close(fd),
	        _ => 0
	    }
        };
    }
}

#[cfg(windows)]
impl Drop for Handle {
    fn drop(&mut self) {
        unsafe {
	    match self.shm_fd {
                FileHandle::Win32(fd) => CloseHandle(fd),
	        _ => 0
	    }
        };
    }
}

#[cfg(test)]
mod tests {
    #[test]
    fn breath_test_handle() {
        use super::*;
        let name = "handleBreathTest";
        {
            let handle = Handle::new(name, CreateMode::CreateOnly, AccessMode::ReadWrite, Permissions::User).unwrap();
            assert_eq!(handle.name(), name);
            match handle.access_mode() {
                &AccessMode::ReadWrite => {},
                _ => assert!(false, "wrong access mode"),
            };
            assert!(handle.native_handle() >= 0);
        }
        let removed = Handle::remove(name);
        assert!(removed);
    }
}
<|endoftext|>