<commit_before>// Module for rendering scene

extern crate rand;

use camera::Camera;
use model::{bvh, Renderable, Model};
use model::sphere::Sphere;
use ray::Ray;
use shader::material::Material;
use vec3::{Vec3};

use std::io::Write;
use std::thread;
use std::sync::Arc;
use rand::random;

const MIN_DISTANCE: f64 = 0.000001;
const MAX_DISTANCE: f64 = 1000.0;
const DEPTH_MAX: i32 = 50;

const COLOR_BLUE:    Vec3 = Vec3 { x: 0.5, y: 0.7, z: 1.0 };
const COLOR_WHITE:   Vec3 = Vec3 { x: 1.0, y: 1.0, z: 1.0 };
const COLOR_DEFAULT: Vec3 = Vec3 { x: 0.0, y: 0.0, z: 0.0 };

// Renders scene
//
// Return buffer of RGB triples as ints from top left to bottom right
// e.g. R G B R G B R G B ...
//
// Arguments:
//
// * `nx` - width of image
// * `ny` - height of image
// * `spp` - samples per pixel
pub fn render (
    scene: Box<Renderable + Sync>,
    camera: Arc<Camera>,
    nx: i32,
    ny: i32,
    spp: i32,
    nthreads: i32,
    debug: bool,
) -> Vec<i32>  {
    let mut threads = vec![];

    let shareable_scene: Arc<Box<Renderable>> = Arc::new(scene);

    // Each thread takes 1/nthreads of the image split into horizontal slices
    // rev because bottom of image first
    for nth in (0..nthreads).rev() {
        {
            let camera = camera.clone();
            let s = shareable_scene.clone();
            threads.push(thread::spawn(move || {
                // Get horizontal slice, final thread takes any extra due to int truncation
                let start = (ny/nthreads) * nth;
                let end = if nth == nthreads - 1 { ny } else { (ny/nthreads) * (nth+1) };
                let slice = end - start;

                let capacity = slice * nx * 3;
                let mut outbuf: Vec<i32> = Vec::with_capacity(capacity as usize);

                for j in (start..end).rev() {
                    for i in 0..nx {
                        let mut c = Vec3::new(0.0, 0.0, 0.0);

                        for _s in 0..spp {
                            c += sample(i,j,nx,ny,&(**s),&(*camera));
                        }
                        let c = c / (spp as f64);

                        // Output for PPM
                        // Gamma correct to 2: output color ^ (1/gamma) = x^(1/2) = sqrt
                        let c = c.map(&|x: f64| x.sqrt());
                        let c = c * 255.99;

                        // Save to buffer for image out
                        outbuf.push(c.x as i32);
                        outbuf.push(c.y as i32);
                        outbuf.push(c.z as i32);
                    }

                    // Write percentage progress
                    if debug && j % (slice / 10) == 0 {
                        writeln!(&mut ::std::io::stderr(), "{}: {}/{}", nth, slice-(j-start), slice).unwrap();
                    }
                }

                (outbuf, nth)
            }));
        }
    }

    // Join and create final buffer from results
    let mut outbuf: Vec<i32> = Vec::with_capacity((nx * ny * 3) as usize);

    for t in threads {
        let (mut res, nth) = t.join().unwrap(); // If thread fails, unrecoverable
        if debug {
            writeln!(&mut ::std::io::stderr(), "Thread {} done", nth);
        }
        outbuf.append(&mut res);
    }

    outbuf
}

fn sample(x: i32, y: i32, nx: i32, ny: i32, scene: &Renderable, camera: &Camera) -> Vec3 {
    // Get percent offset from bottom left corner
    let u = (x as f64 + random::<f64>()) / (nx as f64);
    let v = (y as f64 + random::<f64>()) / (ny as f64);

    // Make ray
    let r = camera.get_ray(u, v);

    // Get color
    color(&r, scene, 0)
}

fn color(r: &Ray, world: &Renderable, depth: i32) -> Vec3 {
    match world.hit(r, MIN_DISTANCE, MAX_DISTANCE) {
        Some((h, material)) => {
            let emitted = material.emitted(h.u, h.v, &h.p).unwrap_or(Vec3::new(0.0,0.0,0.0));
            if depth < DEPTH_MAX {
                match material.scatter(r, &h) {
                    Some((attentuation, scattered)) => {
                        emitted + attentuation * color(&scattered, world, depth+1)
                    }
                    // No scatter ray produced
                    None => emitted
                }
            } else {
                // Depth exceeded default color
                COLOR_DEFAULT
            }
        }
        None => {
            // Background

            // Sky
            // let unit_dir = r.dir.normalized();
            // let t = 0.5 * (unit_dir.y + 1.0);
            // COLOR_WHITE * (1.0-t) + COLOR_BLUE * (t)

            // Black
            COLOR_DEFAULT
        }
    }
}

pub fn random_scene() -> Box<Renderable + Sync> {
    let mut items: Vec<Box<bvh::BVHItem>> = Vec::new();

    // Ground
    items.push(Box::new(Model::new(
        Sphere {
            center: Vec3::new(0.0,-1000.0,0.0),
            radius: 1000.0
        },
        Material::lambertian_constant(random::<Vec3>()),
    )));

    // Big spheres
    items.push(Box::new(Model::new(
        Sphere {
            center: Vec3::new(-4.0,1.0,0.0),
            radius: 1.0,
        },
        Material::lambertian_constant(random::<Vec3>() * random::<Vec3>()),
    )));
    items.push(Box::new(Model::new(
        Sphere {
            center: Vec3::new(4.0, 1.0, 0.0),
            radius: 1.0,
        },
        Material::metal((random::<Vec3>() + 1.0) * 0.5, random::<f64>() * 0.3),
    )));
    items.push(Box::new(Model::new(
        Sphere {
            center: Vec3::new(0.0,1.0,0.0),
            radius: 1.0,
        },
        Material::dielectric(1.5),
    )));

    for a in -11..11 {
        for b in -11..11 {
            let choose_mat = random::<f64>();
            let center = Vec3::new(
                a as f64 + 0.9 * random::<f64>(),
                0.2,
                b as f64 + 0.9 * random::<f64>(),
            );
            if (center - Vec3::new(4.0,0.2,0.0)).length() > 0.9 {
                if choose_mat < 0.8 {
                    // diffuse
                    items.push(Box::new(Model::new(
                        Sphere {
                            center: center,
                            radius: 0.2,
                        },
                        Material::lambertian_constant(random::<Vec3>() * random::<Vec3>()),
                    )));
                } else if choose_mat < 0.95 {
                    // metal
                    items.push(Box::new(Model::new(
                        Sphere {
                            center: center,
                            radius: 0.2,
                        },
                        Material::metal(
                            (random::<Vec3>() + 1.0) * 0.5,
                            0.5 * random::<f64>(),
                        ),
                    )));

                } else {
                    // glass
                    items.push(Box::new(Model::new(
                        Sphere {
                            center: center,
                            radius: 0.2,
                        },
                        Material::dielectric(1.5),
                    )));

                }
            }
        }
    }

    Box::new(bvh::Node::new(items))
}
<commit_msg>Clamp output<commit_after>// Module for rendering scene

extern crate rand;

use camera::Camera;
use model::{bvh, Renderable, Model};
use model::sphere::Sphere;
use ray::Ray;
use shader::material::Material;
use vec3::{Vec3};

use std::io::Write;
use std::thread;
use std::sync::Arc;
use rand::random;

const MIN_DISTANCE: f64 = 0.000001;
const MAX_DISTANCE: f64 = 1000.0;
const DEPTH_MAX: i32 = 50;

#[allow(unused)]
const COLOR_BLUE:    Vec3 = Vec3 { x: 0.5, y: 0.7, z: 1.0 };
#[allow(unused)]
const COLOR_WHITE:   Vec3 = Vec3 { x: 1.0, y: 1.0, z: 1.0 };
#[allow(unused)]
const COLOR_DEFAULT: Vec3 = Vec3 { x: 0.0, y: 0.0, z: 0.0 };

// Renders scene
//
// Return buffer of RGB triples as ints from top left to bottom right
// e.g. R G B R G B R G B ...
//
// Arguments:
//
// * `nx` - width of image
// * `ny` - height of image
// * `spp` - samples per pixel
pub fn render (
    scene: Box<Renderable + Sync>,
    camera: Arc<Camera>,
    nx: i32,
    ny: i32,
    spp: i32,
    nthreads: i32,
    debug: bool,
) -> Vec<i32>  {
    let mut threads = vec![];

    let shareable_scene: Arc<Box<Renderable>> = Arc::new(scene);

    // Each thread takes 1/nthreads of the image split into horizontal slices
    // rev because bottom of image first
    for nth in (0..nthreads).rev() {
        {
            let camera = camera.clone();
            let s = shareable_scene.clone();
            threads.push(thread::spawn(move || {
                // Get horizontal slice, final thread takes any extra due to int truncation
                let start = (ny/nthreads) * nth;
                let end = if nth == nthreads - 1 { ny } else { (ny/nthreads) * (nth+1) };
                let slice = end - start;

                let capacity = slice * nx * 3;
                let mut outbuf: Vec<i32> = Vec::with_capacity(capacity as usize);

                for j in (start..end).rev() {
                    for i in 0..nx {
                        let mut c = Vec3::new(0.0, 0.0, 0.0);

                        for _s in 0..spp {
                            c += sample(i,j,nx,ny,&(**s),&(*camera));
                        }
                        let c = c / (spp as f64);

                        // Output for PPM
                        // Gamma correct to 2: output color ^ (1/gamma) = x^(1/2) = sqrt
                        let c = c.map(&|x: f64| x.sqrt());
                        let c = c * 255.99;
                        let c = c.map(&|x: f64| if x > 256.99 { 255.99 } else { x });

                        // Save to buffer for image out
                        outbuf.push(c.x as i32);
                        outbuf.push(c.y as i32);
                        outbuf.push(c.z as i32);
                    }

                    // Write percentage progress
                    if debug && j % (slice / 10) == 0 {
                        writeln!(&mut ::std::io::stderr(), "{}: {}/{}", nth, slice-(j-start), slice).unwrap();
                    }
                }

                (outbuf, nth)
            }));
        }
    }

    // Join and create final buffer from results
    let mut outbuf: Vec<i32> = Vec::with_capacity((nx * ny * 3) as usize);

    for t in threads {
        let (mut res, nth) = t.join().unwrap(); // If thread fails, unrecoverable
        if debug {
            writeln!(&mut ::std::io::stderr(), "Thread {} done", nth);
        }
        outbuf.append(&mut res);
    }

    outbuf
}

fn sample(x: i32, y: i32, nx: i32, ny: i32, scene: &Renderable, camera: &Camera) -> Vec3 {
    // Get percent offset from bottom left corner
    let u = (x as f64 + random::<f64>()) / (nx as f64);
    let v = (y as f64 + random::<f64>()) / (ny as f64);

    // Make ray
    let r = camera.get_ray(u, v);

    // Get color
    color(&r, scene, 0)
}

fn color(r: &Ray, world: &Renderable, depth: i32) -> Vec3 {
    match world.hit(r, MIN_DISTANCE, MAX_DISTANCE) {
        Some((h, material)) => {
            let emitted = material.emitted(h.u, h.v, &h.p).unwrap_or(Vec3::new(0.0,0.0,0.0));
            if depth < DEPTH_MAX {
                match material.scatter(r, &h) {
                    Some((attentuation, scattered)) => {
                        emitted + attentuation * color(&scattered, world, depth+1)
                    }
                    // No scatter ray produced
                    None => emitted
                }
            } else {
                // Depth exceeded default color
                COLOR_DEFAULT
            }
        }
        None => {
            // Background

            // Sky
            // let unit_dir = r.dir.normalized();
            // let t = 0.5 * (unit_dir.y + 1.0);
            // COLOR_WHITE * (1.0-t) + COLOR_BLUE * (t)

            // Black
            COLOR_DEFAULT
        }
    }
}

pub fn random_scene() -> Box<Renderable + Sync> {
    let mut items: Vec<Box<bvh::BVHItem>> = Vec::new();

    // Ground
    items.push(Box::new(Model::new(
        Sphere {
            center: Vec3::new(0.0,-1000.0,0.0),
            radius: 1000.0
        },
        Material::lambertian_constant(random::<Vec3>()),
    )));

    // Big spheres
    items.push(Box::new(Model::new(
        Sphere {
            center: Vec3::new(-4.0,1.0,0.0),
            radius: 1.0,
        },
        Material::lambertian_constant(random::<Vec3>() * random::<Vec3>()),
    )));
    items.push(Box::new(Model::new(
        Sphere {
            center: Vec3::new(4.0, 1.0, 0.0),
            radius: 1.0,
        },
        Material::metal((random::<Vec3>() + 1.0) * 0.5, random::<f64>() * 0.3),
    )));
    items.push(Box::new(Model::new(
        Sphere {
            center: Vec3::new(0.0,1.0,0.0),
            radius: 1.0,
        },
        Material::dielectric(1.5),
    )));

    for a in -11..11 {
        for b in -11..11 {
            let choose_mat = random::<f64>();
            let center = Vec3::new(
                a as f64 + 0.9 * random::<f64>(),
                0.2,
                b as f64 + 0.9 * random::<f64>(),
            );
            if (center - Vec3::new(4.0,0.2,0.0)).length() > 0.9 {
                if choose_mat < 0.8 {
                    // diffuse
                    items.push(Box::new(Model::new(
                        Sphere {
                            center: center,
                            radius: 0.2,
                        },
                        Material::lambertian_constant(random::<Vec3>() * random::<Vec3>()),
                    )));
                } else if choose_mat < 0.95 {
                    // metal
                    items.push(Box::new(Model::new(
                        Sphere {
                            center: center,
                            radius: 0.2,
                        },
                        Material::metal(
                            (random::<Vec3>() + 1.0) * 0.5,
                            0.5 * random::<f64>(),
                        ),
                    )));

                } else {
                    // glass
                    items.push(Box::new(Model::new(
                        Sphere {
                            center: center,
                            radius: 0.2,
                        },
                        Material::dielectric(1.5),
                    )));

                }
            }
        }
    }

    Box::new(bvh::Node::new(items))
}
<|endoftext|>


<commit_before>/*
Copyright 2021 Google LLC

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

     https://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

use colored::Colorize;
use std::cmp;
use rustc_hash::FxHashMap;

/// Struct for storing (partial) query matches.
/// We really don't want to keep track of tree-sitter AST lifetimes so
/// we do not store full nodes, but only their source range.
/// TODO: Improve this struct + benchmarking
#[derive(Debug, Eq, PartialEq)]
pub struct QueryResult {
    // for each captured node we store the offset ranges of its src location
    captures: Vec<CaptureResult>,
    // Mapping from Variables to index in `captures`
    pub vars: FxHashMap<String, usize>,
    // Range of the outermost node. This is badly named as it does not have to be a
    // function definition, but for final query results it normally is.
    function: std::ops::Range<usize>,
}

/// Stores the result (== source range) for a single capture.
/// We also store the corresponding query id and capture index
/// to make it possible to look up the result for a certain capture
/// index (see QueryResult::get_capture_result)
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct CaptureResult {
    pub range: std::ops::Range<usize>,
    pub query_id: usize,
    pub capture_idx: u32,
}

impl<'a, 'b> QueryResult {
    pub fn new(
        captures: Vec<CaptureResult>,
        vars: FxHashMap<String, usize>,
        function: std::ops::Range<usize>,
    ) -> QueryResult {
        QueryResult {
            captures,
            vars,
            function,
        }
    }

    pub fn start_offset(&self) -> usize {
        self.function.start
    }

    /// Returns a colored String representation of the result with `before` + `after`
    /// context lines around each captured node.
    pub fn display(&self, source: &'b str, before: usize, after: usize) -> String {
        let mut result = String::new();

        // Add two lines of the function header
        // TODO: We should just store the range of the header and always print it in full.
        let mut header_end = linebreak_index(source, self.function.start, 1, false);

        if self.captures.len() > 1 && self.captures[1].range.start > self.function.start {
            // Ensure we don't overlap with the range of the next node.
            header_end = cmp::min(header_end, self.captures[1].range.start - 1);
        }

        result += &source[self.function.start..header_end];

        let mut offset = header_end;

        let mut sorted = self.captures.clone();
        sorted.sort_by(|a, b| a.range.start.cmp(&b.range.start));

        // Before printing out the different nodes, we first filter out overlapping nodes.
        // If we matched on `(a + b)` and also captured `b` clean_ranges will not contain
        // the range for `b`.
        let mut clean_ranges: Vec<std::ops::Range<usize>> = Vec::with_capacity(self.captures.len());
        for r in sorted.into_iter().skip(1).map(|c| c.range) {
            if !clean_ranges.is_empty() && clean_ranges.last().unwrap().contains(&r.start) {
                continue;
            }
            clean_ranges.push(r.clone());
        }

        // Iterate over all remaining nodes and print them
        for (index, r) in clean_ranges.iter().enumerate() {
            if r.start <= offset {
                continue;
            }

            // Print lines before/after the match, based on -A / -B
            let start = linebreak_index(source, r.start, before, true);
            let mut end = linebreak_index(source, r.end, after, false);

            // Avoid overlapping with the next node
            if index < clean_ranges.len() - 1 && r.end < clean_ranges[index + 1].start {
                end = cmp::min(end, clean_ranges[index + 1].start - 1);
            }

            // Never go beyond the function boundary.
            end = cmp::min(end, self.function.end);

            if start <= offset {
                // we are not skipping anything
                result += &source[offset..r.start];
            } else {
                // indicate that some code is skipped
                result += "..";
                result += &source[start..r.start];
            }
            // Mark the node itself in red.
            result += &format!("{}", &source[r.start..r.end].red());
            result += &source[r.end..end];
            offset = end;
        }

        // Print function ending.
        if offset < self.function.end {
            let last_line = linebreak_index(source, self.function.end, 0, true);
            result += "..";
            result += &source[last_line..self.function.end];
        }

        result
    }

    /// Return the captured value for a variable.
    pub fn value(&self, var: &str, source: &'b str) -> Option<&'b str> {
        match self.vars.get(var) {
            None => None,
            Some(i) => Some(&source[self.captures[*i].range.clone()]),
        }
    }

    /// Try to merge two QueryResults from the same source file.
    /// The function returns None if the variable assignments for the two results differ.
    /// If `enforce_order` is set this can fail because the new ranges
    /// are not strictly after the current ranges.
    pub fn merge(
        &self,
        other: &QueryResult,
        source: &str,
        enforce_order: bool,
    ) -> Option<QueryResult> {
        let mut vars = self.vars.clone();

        let mut captures = self.captures.clone();

        if enforce_order {
            if other
                .captures
                .iter()
                .any(|r| self.captures.iter().any(|r2| r.range.start <= r2.range.end))
            {
                return None;
            }
        }

        captures.extend(other.captures.clone());

        for (k, v) in other.vars.iter() {
            match self.value(k, source) {
                None => {
                    vars.insert(k.clone(), v + self.captures.len());
                }
                Some(s) => {
                    if s != other.value(k, source).unwrap() {
                        return None;
                    }
                }
            }
        }

        Some(QueryResult::new(captures, vars, self.function.clone()))
    }

    /// Checks if two QueryResults from different source files have compatible variable assignments
    pub fn chainable(&self, source: &str, other: &QueryResult, other_source: &str) -> bool {
        !other.vars.iter().any(|(k, _)| {
            if let Some(value) = self.value(k, source) {
                value != other.value(k, other_source).unwrap()
            } else {
                false
            }
        })
    }

    /// Try to find the result for the capture `capture_idx` in query `query_id`
    pub fn get_capture_result(&self, query_id: usize, capture_idx: u32) -> Option<&CaptureResult> {
        self.captures
            .iter()
            .find(|c| c.capture_idx == capture_idx && c.query_id == query_id)
    }
}

// Try to merge sub_results into each result.
pub fn merge_results(
    results: &[QueryResult],
    sub_results: &[QueryResult],
    source: &str,
    enforce_order: bool,
) -> Vec<QueryResult> {
    results
        .iter()
        .flat_map(|r| {
            sub_results
                .iter()
                .filter_map(move |s| r.merge(s, source, enforce_order))
        })
        .collect()
}

// Returns the index of the nth newline before (if `backwards` is set ) or after `source[index]`
// This is used to display additional context around captured nodes. If not enough newlines
// exist the function will return 0 (for backwards) or source.len().
fn linebreak_index(source: &str, index: usize, count: usize, backwards: bool) -> usize {
    let length = source.len();

    let mut f;
    let mut b;

    let iter: &mut dyn Iterator<Item = (usize, char)> = if !backwards {
        f = source[index..length].char_indices();
        &mut f
    } else {
        b = source[..index].char_indices().rev();
        &mut b
    };

    let newline_index = iter.filter(|(_, c)| *c == '\n').nth(count);

    match newline_index {
        Some((i, _)) if !backwards => cmp::min(length, index + i + 1),
        Some((i, _)) => i,
        None if !backwards => length,
        None => 0,
    }
}

#[test]
fn test_linebreak_index() {
    let input = "aaa\nbbb\nccc\nd";
    let index = input.find('b').unwrap();

    assert_eq!(linebreak_index(&input, index, 1, true), 0);
    assert_eq!(
        linebreak_index(&input, index, 1, false),
        input.find('d').unwrap()
    );
    assert_eq!(linebreak_index(&input, index, 5, false), input.len());
    assert_eq!(linebreak_index(&input, index, 4, true), 0);
}
<commit_msg>result: make captures public<commit_after>/*
Copyright 2021 Google LLC

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

     https://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

use colored::Colorize;
use std::cmp;
use rustc_hash::FxHashMap;

/// Struct for storing (partial) query matches.
/// We really don't want to keep track of tree-sitter AST lifetimes so
/// we do not store full nodes, but only their source range.
/// TODO: Improve this struct + benchmarking
#[derive(Debug, Eq, PartialEq)]
pub struct QueryResult {
    // for each captured node we store the offset ranges of its src location
    pub captures: Vec<CaptureResult>,
    // Mapping from Variables to index in `captures`
    pub vars: FxHashMap<String, usize>,
    // Range of the outermost node. This is badly named as it does not have to be a
    // function definition, but for final query results it normally is.
    function: std::ops::Range<usize>,
}

/// Stores the result (== source range) for a single capture.
/// We also store the corresponding query id and capture index
/// to make it possible to look up the result for a certain capture
/// index (see QueryResult::get_capture_result)
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct CaptureResult {
    pub range: std::ops::Range<usize>,
    pub query_id: usize,
    pub capture_idx: u32,
}

impl<'a, 'b> QueryResult {
    pub fn new(
        captures: Vec<CaptureResult>,
        vars: FxHashMap<String, usize>,
        function: std::ops::Range<usize>,
    ) -> QueryResult {
        QueryResult {
            captures,
            vars,
            function,
        }
    }

    pub fn start_offset(&self) -> usize {
        self.function.start
    }

    /// Returns a colored String representation of the result with `before` + `after`
    /// context lines around each captured node.
    pub fn display(&self, source: &'b str, before: usize, after: usize) -> String {
        let mut result = String::new();

        // Add two lines of the function header
        // TODO: We should just store the range of the header and always print it in full.
        let mut header_end = linebreak_index(source, self.function.start, 1, false);

        if self.captures.len() > 1 && self.captures[1].range.start > self.function.start {
            // Ensure we don't overlap with the range of the next node.
            header_end = cmp::min(header_end, self.captures[1].range.start - 1);
        }

        result += &source[self.function.start..header_end];

        let mut offset = header_end;

        let mut sorted = self.captures.clone();
        sorted.sort_by(|a, b| a.range.start.cmp(&b.range.start));

        // Before printing out the different nodes, we first filter out overlapping nodes.
        // If we matched on `(a + b)` and also captured `b` clean_ranges will not contain
        // the range for `b`.
        let mut clean_ranges: Vec<std::ops::Range<usize>> = Vec::with_capacity(self.captures.len());
        for r in sorted.into_iter().skip(1).map(|c| c.range) {
            if !clean_ranges.is_empty() && clean_ranges.last().unwrap().contains(&r.start) {
                continue;
            }
            clean_ranges.push(r.clone());
        }

        // Iterate over all remaining nodes and print them
        for (index, r) in clean_ranges.iter().enumerate() {
            if r.start <= offset {
                continue;
            }

            // Print lines before/after the match, based on -A / -B
            let start = linebreak_index(source, r.start, before, true);
            let mut end = linebreak_index(source, r.end, after, false);

            // Avoid overlapping with the next node
            if index < clean_ranges.len() - 1 && r.end < clean_ranges[index + 1].start {
                end = cmp::min(end, clean_ranges[index + 1].start - 1);
            }

            // Never go beyond the function boundary.
            end = cmp::min(end, self.function.end);

            if start <= offset {
                // we are not skipping anything
                result += &source[offset..r.start];
            } else {
                // indicate that some code is skipped
                result += "..";
                result += &source[start..r.start];
            }
            // Mark the node itself in red.
            result += &format!("{}", &source[r.start..r.end].red());
            result += &source[r.end..end];
            offset = end;
        }

        // Print function ending.
        if offset < self.function.end {
            let last_line = linebreak_index(source, self.function.end, 0, true);
            result += "..";
            result += &source[last_line..self.function.end];
        }

        result
    }

    /// Return the captured value for a variable.
    pub fn value(&self, var: &str, source: &'b str) -> Option<&'b str> {
        match self.vars.get(var) {
            None => None,
            Some(i) => Some(&source[self.captures[*i].range.clone()]),
        }
    }

    /// Try to merge two QueryResults from the same source file.
    /// The function returns None if the variable assignments for the two results differ.
    /// If `enforce_order` is set this can fail because the new ranges
    /// are not strictly after the current ranges.
    pub fn merge(
        &self,
        other: &QueryResult,
        source: &str,
        enforce_order: bool,
    ) -> Option<QueryResult> {
        let mut vars = self.vars.clone();

        let mut captures = self.captures.clone();

        if enforce_order {
            if other
                .captures
                .iter()
                .any(|r| self.captures.iter().any(|r2| r.range.start <= r2.range.end))
            {
                return None;
            }
        }

        captures.extend(other.captures.clone());

        for (k, v) in other.vars.iter() {
            match self.value(k, source) {
                None => {
                    vars.insert(k.clone(), v + self.captures.len());
                }
                Some(s) => {
                    if s != other.value(k, source).unwrap() {
                        return None;
                    }
                }
            }
        }

        Some(QueryResult::new(captures, vars, self.function.clone()))
    }

    /// Checks if two QueryResults from different source files have compatible variable assignments
    pub fn chainable(&self, source: &str, other: &QueryResult, other_source: &str) -> bool {
        !other.vars.iter().any(|(k, _)| {
            if let Some(value) = self.value(k, source) {
                value != other.value(k, other_source).unwrap()
            } else {
                false
            }
        })
    }

    /// Try to find the result for the capture `capture_idx` in query `query_id`
    pub fn get_capture_result(&self, query_id: usize, capture_idx: u32) -> Option<&CaptureResult> {
        self.captures
            .iter()
            .find(|c| c.capture_idx == capture_idx && c.query_id == query_id)
    }
}

// Try to merge sub_results into each result.
pub fn merge_results(
    results: &[QueryResult],
    sub_results: &[QueryResult],
    source: &str,
    enforce_order: bool,
) -> Vec<QueryResult> {
    results
        .iter()
        .flat_map(|r| {
            sub_results
                .iter()
                .filter_map(move |s| r.merge(s, source, enforce_order))
        })
        .collect()
}

// Returns the index of the nth newline before (if `backwards` is set ) or after `source[index]`
// This is used to display additional context around captured nodes. If not enough newlines
// exist the function will return 0 (for backwards) or source.len().
fn linebreak_index(source: &str, index: usize, count: usize, backwards: bool) -> usize {
    let length = source.len();

    let mut f;
    let mut b;

    let iter: &mut dyn Iterator<Item = (usize, char)> = if !backwards {
        f = source[index..length].char_indices();
        &mut f
    } else {
        b = source[..index].char_indices().rev();
        &mut b
    };

    let newline_index = iter.filter(|(_, c)| *c == '\n').nth(count);

    match newline_index {
        Some((i, _)) if !backwards => cmp::min(length, index + i + 1),
        Some((i, _)) => i,
        None if !backwards => length,
        None => 0,
    }
}

#[test]
fn test_linebreak_index() {
    let input = "aaa\nbbb\nccc\nd";
    let index = input.find('b').unwrap();

    assert_eq!(linebreak_index(&input, index, 1, true), 0);
    assert_eq!(
        linebreak_index(&input, index, 1, false),
        input.find('d').unwrap()
    );
    assert_eq!(linebreak_index(&input, index, 5, false), input.len());
    assert_eq!(linebreak_index(&input, index, 4, true), 0);
}
<|endoftext|>


<commit_before>//! Serde integration support.

use std::fmt;
use std::marker::PhantomData;

use serde::*;
use serde::de::{Visitor, Error, Unexpected};

fn i64_to_u64<'d, V: Visitor<'d>, E: Error>(v: V, n: i64) -> Result<V::Value, E> {
	if n >= 0 {
		v.visit_u64(n as u64)
	} else {
		Err(E::invalid_value(Unexpected::Signed(n), &v))
	}
}

/// Ignore deserialization errors and revert to default.
pub fn ignore_errors<'d, T: Deserialize<'d> + Default, D: Deserializer<'d>>(d: D) -> Result<T, D::Error> {
	Ok(T::deserialize(d).ok().unwrap_or_default())
}

/// Deserialize a maybe-string ID into a u64.
pub fn deserialize_id<'d, D: Deserializer<'d>>(d: D) -> Result<u64, D::Error> {
	struct IdVisitor;
	impl<'d> Visitor<'d> for IdVisitor {
		type Value = u64;

		fn expecting(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
			write!(fmt, "a u64 or parseable string")
		}

		fn visit_i64<E: Error>(self, v: i64) -> Result<u64, E> {
			i64_to_u64(self, v)
		}

		fn visit_u64<E: Error>(self, v: u64) -> Result<u64, E> {
			Ok(v)
		}

		fn visit_str<E: Error>(self, v: &str) -> Result<u64, E> {
			v.parse::<u64>().map_err(|_| E::invalid_value(Unexpected::Str(v), &self))
		}
	}

	d.deserialize_u64(IdVisitor)
}

/// Deserialize a maybe-string discriminator into a u16.
/// Also enforces 0 <= N <= 9999.
#[allow(unused_comparisons)]
pub fn deserialize_discrim<'d, D: Deserializer<'d>>(d: D) -> Result<u16, D::Error> {
	macro_rules! check {
		($self:ident, $v:ident, $wrong:expr) => {
			if $v >= 0 && $v <= 9999 {
				Ok($v as u16)
			} else {
				Err(E::invalid_value($wrong, &$self))
			}
		}
	}

	struct DiscrimVisitor;
	impl<'d> Visitor<'d> for DiscrimVisitor {
		type Value = u16;

		fn expecting(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
			write!(fmt, "a u16 in [0, 9999] or parseable string")
		}

		fn visit_i64<E: Error>(self, v: i64) -> Result<u16, E> {
			check!(self, v, Unexpected::Signed(v))
		}

		fn visit_u64<E: Error>(self, v: u64) -> Result<u16, E> {
			check!(self, v, Unexpected::Unsigned(v))
		}

		fn visit_str<E: Error>(self, v: &str) -> Result<u16, E> {
			v.parse::<u16>()
				.map_err(|_| E::invalid_value(Unexpected::Str(v), &self))
				.and_then(|v| self.visit_u16(v))
		}
	}

	d.deserialize_u16(DiscrimVisitor)
}

/// Deserialize a single-field struct like a newtype struct.
macro_rules! serial_single_field {
	($typ:ident as $field:ident: $inner:path) => {
		impl ::serde::Serialize for $typ {
			fn serialize<S: ::serde::ser::Serializer>(&self, s: S) -> ::std::result::Result<S::Ok, S::Error> {
				self.$field.serialize(s)
			}
		}

		impl<'d> ::serde::Deserialize<'d> for $typ {
			fn deserialize<D: ::serde::de::Deserializer<'d>>(d: D) -> ::std::result::Result<$typ, D::Error> {
				<$inner as ::serde::de::Deserialize>::deserialize(d).map(|v| $typ { $field: v })
			}
		}
	}
}

/// Special support for the oddly complex `ReactionEmoji`.
pub mod reaction_emoji {
	use super::*;
	use model::{ReactionEmoji, EmojiId};

	#[derive(Serialize)]
	struct EmojiSer<'s> {
		name: &'s str,
		id: Option<EmojiId>
	}

	#[derive(Deserialize)]
	struct EmojiDe {
		name: String,
		id: Option<EmojiId>,
	}

	pub fn serialize<S: Serializer>(v: &ReactionEmoji, s: S) -> Result<S::Ok, S::Error> {
		(match *v {
			ReactionEmoji::Unicode(ref name) => EmojiSer { name: name, id: None },
			ReactionEmoji::Custom { ref name, id } => EmojiSer { name: name, id: Some(id) },
		}).serialize(s)
	}

	pub fn deserialize<'d, D: Deserializer<'d>>(d: D) -> Result<ReactionEmoji, D::Error> {
		Ok(match try!(EmojiDe::deserialize(d)) {
			EmojiDe { name, id: None } => ReactionEmoji::Unicode(name),
			EmojiDe { name, id: Some(id) } => ReactionEmoji::Custom { name: name, id: id },
		})
	}
}

/// Support for named enums.
pub mod named {
	use super::*;

	pub trait NamedEnum: Sized {
		fn name(&self) -> &'static str;
		fn from_name(name: &str) -> Option<Self>;
		fn typename() -> &'static str;
	}

	pub fn serialize<T: NamedEnum, S: Serializer>(v: &T, s: S) -> Result<S::Ok, S::Error> {
		v.name().serialize(s)
	}

	pub fn deserialize<'d, T: NamedEnum, D: Deserializer<'d>>(d: D) -> Result<T, D::Error> {
		struct NameVisitor<T>(PhantomData<T>);
		impl<'d, T: NamedEnum> Visitor<'d> for NameVisitor<T> {
			type Value = T;

			fn expecting(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
				write!(fmt, "a valid {} name", T::typename())
			}

			fn visit_str<E: Error>(self, v: &str) -> Result<T, E> {
				T::from_name(v).ok_or_else(|| E::invalid_value(Unexpected::Str(v), &self))
			}
		}

		d.deserialize_string(NameVisitor(PhantomData))
	}
}
macro_rules! serial_names {
	($typ:ident; $($entry:ident, $value:expr;)*) => {
		impl $typ {
			pub fn name(&self) -> &'static str {
				match *self {
					$($typ::$entry => $value,)*
				}
			}

			pub fn from_name(name: &str) -> Option<Self> {
				match name {
					$($value => Some($typ::$entry),)*
					_ => None,
				}
			}
		}

		impl ::serial::named::NamedEnum for $typ {
			fn name(&self) -> &'static str {
				self.name()
			}

			fn from_name(name: &str) -> Option<Self> {
				Self::from_name(name)
			}

			fn typename() -> &'static str {
				stringify!($typ)
			}
		}
	}
}

/// Support for numeric enums.
pub mod numeric {
	use super::*;

	pub trait NumericEnum: Sized {
		fn num(&self) -> u64;
		fn from_num(num: u64) -> Option<Self>;
		fn typename() -> &'static str;
	}

	pub fn serialize<T: NumericEnum, S: Serializer>(v: &T, s: S) -> Result<S::Ok, S::Error> {
		v.num().serialize(s)
	}

	pub fn deserialize<'d, T: NumericEnum, D: Deserializer<'d>>(d: D) -> Result<T, D::Error> {
		struct NumVisitor<T>(PhantomData<T>);
		impl<'d, T: NumericEnum> Visitor<'d> for NumVisitor<T> {
			type Value = T;

			fn expecting(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
				write!(fmt, "a valid {} number", T::typename())
			}

			fn visit_i64<E: Error>(self, v: i64) -> Result<T, E> {
				i64_to_u64(self, v)
			}

			fn visit_u64<E: Error>(self, v: u64) -> Result<T, E> {
				T::from_num(v).ok_or_else(|| E::invalid_value(Unexpected::Unsigned(v), &self))
			}
		}

		d.deserialize_string(NumVisitor(PhantomData))
	}
}
macro_rules! serial_numbers {
	($typ:ident; $($entry:ident, $value:expr;)*) => {
		impl $typ {
			pub fn num(&self) -> u64 {
				match *self {
					$($typ::$entry => $value,)*
				}
			}

			pub fn from_num(num: u64) -> Option<Self> {
				match num {
					$($value => Some($typ::$entry),)*
					_ => None,
				}
			}
		}
		impl ::serial::numeric::NumericEnum for $typ {
			fn num(&self) -> u64 {
				self.num()
			}

			fn from_num(num: u64) -> Option<Self> {
				Self::from_num(num)
			}

			fn typename() -> &'static str {
				stringify!($typ)
			}
		}
	}
}

/// Support for using "named" or "numeric" as the default ser/de impl.
macro_rules! serial_use_mapping {
	($typ:ident, $which:ident) => {
		impl ::serde::Serialize for $typ {
			#[inline]
			fn serialize<S: ::serde::ser::Serializer>(&self, s: S) -> ::std::result::Result<S::Ok, S::Error> {
				::serial::$which::serialize(self, s)
			}
		}

		impl<'d> ::serde::Deserialize<'d> for $typ {
			#[inline]
			fn deserialize<D: ::serde::de::Deserializer<'d>>(d: D) -> ::std::result::Result<$typ, D::Error> {
				::serial::$which::deserialize(d)
			}
		}
	}
}
<commit_msg>deserilization updates<commit_after>//! Serde integration support.

use std::fmt;
use std::marker::PhantomData;

use serde::*;
use serde::de::{Visitor, Error, Unexpected};

fn i64_to_u64<'d, V: Visitor<'d>, E: Error>(v: V, n: i64) -> Result<V::Value, E> {
	if n >= 0 {
		v.visit_u64(n as u64)
	} else {
		Err(E::invalid_value(Unexpected::Signed(n), &v))
	}
}

/// Ignore deserialization errors and revert to default.
pub fn ignore_errors<'d, T: Deserialize<'d> + Default, D: Deserializer<'d>>(d: D) -> Result<T, D::Error> {
	let v = Value::deserialize(d)?;
   	Ok(T::deserialize(v).ok().unwrap_or_default())
}

/// Deserialize a maybe-string ID into a u64.
pub fn deserialize_id<'d, D: Deserializer<'d>>(d: D) -> Result<u64, D::Error> {
	struct IdVisitor;
	impl<'d> Visitor<'d> for IdVisitor {
		type Value = u64;

		fn expecting(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
			write!(fmt, "a u64 or parseable string")
		}

		fn visit_i64<E: Error>(self, v: i64) -> Result<u64, E> {
			i64_to_u64(self, v)
		}

		fn visit_u64<E: Error>(self, v: u64) -> Result<u64, E> {
			Ok(v)
		}

		fn visit_str<E: Error>(self, v: &str) -> Result<u64, E> {
			v.parse::<u64>().map_err(|_| E::invalid_value(Unexpected::Str(v), &self))
		}
	}

	d.deserialize_any(IdVisitor)
}

/// Deserialize a maybe-string discriminator into a u16.
/// Also enforces 0 <= N <= 9999.
#[allow(unused_comparisons)]
pub fn deserialize_discrim<'d, D: Deserializer<'d>>(d: D) -> Result<u16, D::Error> {
	macro_rules! check {
		($self:ident, $v:ident, $wrong:expr) => {
			if $v >= 0 && $v <= 9999 {
				Ok($v as u16)
			} else {
				Err(E::invalid_value($wrong, &$self))
			}
		}
	}

	struct DiscrimVisitor;
	impl<'d> Visitor<'d> for DiscrimVisitor {
		type Value = u16;

		fn expecting(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
			write!(fmt, "a u16 in [0, 9999] or parseable string")
		}

		fn visit_i64<E: Error>(self, v: i64) -> Result<u16, E> {
			check!(self, v, Unexpected::Signed(v))
		}

		fn visit_u64<E: Error>(self, v: u64) -> Result<u16, E> {
			check!(self, v, Unexpected::Unsigned(v))
		}

		fn visit_str<E: Error>(self, v: &str) -> Result<u16, E> {
			v.parse::<u16>()
				.map_err(|_| E::invalid_value(Unexpected::Str(v), &self))
				.and_then(|v| self.visit_u16(v))
		}
	}

	d.deserialize_u16(DiscrimVisitor)
}

/// Deserialize a single-field struct like a newtype struct.
macro_rules! serial_single_field {
	($typ:ident as $field:ident: $inner:path) => {
		impl ::serde::Serialize for $typ {
			fn serialize<S: ::serde::ser::Serializer>(&self, s: S) -> ::std::result::Result<S::Ok, S::Error> {
				self.$field.serialize(s)
			}
		}

		impl<'d> ::serde::Deserialize<'d> for $typ {
			fn deserialize<D: ::serde::de::Deserializer<'d>>(d: D) -> ::std::result::Result<$typ, D::Error> {
				<$inner as ::serde::de::Deserialize>::deserialize(d).map(|v| $typ { $field: v })
			}
		}
	}
}

/// Special support for the oddly complex `ReactionEmoji`.
pub mod reaction_emoji {
	use super::*;
	use model::{ReactionEmoji, EmojiId};

	#[derive(Serialize)]
	struct EmojiSer<'s> {
		name: &'s str,
		id: Option<EmojiId>
	}

	#[derive(Deserialize)]
	struct EmojiDe {
		name: String,
		id: Option<EmojiId>,
	}

	pub fn serialize<S: Serializer>(v: &ReactionEmoji, s: S) -> Result<S::Ok, S::Error> {
		(match *v {
			ReactionEmoji::Unicode(ref name) => EmojiSer { name: name, id: None },
			ReactionEmoji::Custom { ref name, id } => EmojiSer { name: name, id: Some(id) },
		}).serialize(s)
	}

	pub fn deserialize<'d, D: Deserializer<'d>>(d: D) -> Result<ReactionEmoji, D::Error> {
		Ok(match try!(EmojiDe::deserialize(d)) {
			EmojiDe { name, id: None } => ReactionEmoji::Unicode(name),
			EmojiDe { name, id: Some(id) } => ReactionEmoji::Custom { name: name, id: id },
		})
	}
}

/// Support for named enums.
pub mod named {
	use super::*;

	pub trait NamedEnum: Sized {
		fn name(&self) -> &'static str;
		fn from_name(name: &str) -> Option<Self>;
		fn typename() -> &'static str;
	}

	pub fn serialize<T: NamedEnum, S: Serializer>(v: &T, s: S) -> Result<S::Ok, S::Error> {
		v.name().serialize(s)
	}

	pub fn deserialize<'d, T: NamedEnum, D: Deserializer<'d>>(d: D) -> Result<T, D::Error> {
		struct NameVisitor<T>(PhantomData<T>);
		impl<'d, T: NamedEnum> Visitor<'d> for NameVisitor<T> {
			type Value = T;

			fn expecting(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
				write!(fmt, "a valid {} name", T::typename())
			}

			fn visit_str<E: Error>(self, v: &str) -> Result<T, E> {
				T::from_name(v).ok_or_else(|| E::invalid_value(Unexpected::Str(v), &self))
			}
		}

		d.deserialize_string(NameVisitor(PhantomData))
	}
}
macro_rules! serial_names {
	($typ:ident; $($entry:ident, $value:expr;)*) => {
		impl $typ {
			pub fn name(&self) -> &'static str {
				match *self {
					$($typ::$entry => $value,)*
				}
			}

			pub fn from_name(name: &str) -> Option<Self> {
				match name {
					$($value => Some($typ::$entry),)*
					_ => None,
				}
			}
		}

		impl ::serial::named::NamedEnum for $typ {
			fn name(&self) -> &'static str {
				self.name()
			}

			fn from_name(name: &str) -> Option<Self> {
				Self::from_name(name)
			}

			fn typename() -> &'static str {
				stringify!($typ)
			}
		}
	}
}

/// Support for numeric enums.
pub mod numeric {
	use super::*;

	pub trait NumericEnum: Sized {
		fn num(&self) -> u64;
		fn from_num(num: u64) -> Option<Self>;
		fn typename() -> &'static str;
	}

	pub fn serialize<T: NumericEnum, S: Serializer>(v: &T, s: S) -> Result<S::Ok, S::Error> {
		v.num().serialize(s)
	}

	pub fn deserialize<'d, T: NumericEnum, D: Deserializer<'d>>(d: D) -> Result<T, D::Error> {
		struct NumVisitor<T>(PhantomData<T>);
		impl<'d, T: NumericEnum> Visitor<'d> for NumVisitor<T> {
			type Value = T;

			fn expecting(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
				write!(fmt, "a valid {} number", T::typename())
			}

			fn visit_i64<E: Error>(self, v: i64) -> Result<T, E> {
				i64_to_u64(self, v)
			}

			fn visit_u64<E: Error>(self, v: u64) -> Result<T, E> {
				T::from_num(v).ok_or_else(|| E::invalid_value(Unexpected::Unsigned(v), &self))
			}
		}

		d.deserialize_string(NumVisitor(PhantomData))
	}
}
macro_rules! serial_numbers {
	($typ:ident; $($entry:ident, $value:expr;)*) => {
		impl $typ {
			pub fn num(&self) -> u64 {
				match *self {
					$($typ::$entry => $value,)*
				}
			}

			pub fn from_num(num: u64) -> Option<Self> {
				match num {
					$($value => Some($typ::$entry),)*
					_ => None,
				}
			}
		}
		impl ::serial::numeric::NumericEnum for $typ {
			fn num(&self) -> u64 {
				self.num()
			}

			fn from_num(num: u64) -> Option<Self> {
				Self::from_num(num)
			}

			fn typename() -> &'static str {
				stringify!($typ)
			}
		}
	}
}

/// Support for using "named" or "numeric" as the default ser/de impl.
macro_rules! serial_use_mapping {
	($typ:ident, $which:ident) => {
		impl ::serde::Serialize for $typ {
			#[inline]
			fn serialize<S: ::serde::ser::Serializer>(&self, s: S) -> ::std::result::Result<S::Ok, S::Error> {
				::serial::$which::serialize(self, s)
			}
		}

		impl<'d> ::serde::Deserialize<'d> for $typ {
			#[inline]
			fn deserialize<D: ::serde::de::Deserializer<'d>>(d: D) -> ::std::result::Result<$typ, D::Error> {
				::serial::$which::deserialize(d)
			}
		}
	}
}
<|endoftext|>


<commit_before>use serde_json::{self, Value};

use std::convert::From;
use std::io;
use std::collections::HashMap;
use std::borrow::Cow;

use bytes::BytesMut;
use bytes::BufMut;

use futures::{future, Future, BoxFuture};

use tokio_proto::pipeline::ServerProto;
use tokio_service::Service;
use tokio_io::codec::{Encoder, Decoder};
use tokio_io::{AsyncRead, AsyncWrite};
use tokio_io::codec::Framed;

pub trait Interface {
    fn get_description(&self) -> &'static str;
    fn get_name(&self) -> &'static str;
    fn call(&self, Request) -> Result<Value, Error>;
}

#[derive(Serialize, Deserialize)]
pub struct Request {
    pub method: Cow<'static, str>,
    pub parameters: Option<Value>,
}

#[derive(Serialize, Deserialize)]
pub struct Reply {
    pub parameters: Option<Value>,
}

#[derive(Serialize, Deserialize, Default)]
pub struct Error {
    pub error: Cow<'static, str>,
    pub parameters: Option<Value>,
}

impl From<serde_json::Error> for Error {
    fn from(e: serde_json::Error) -> Self {
        Error {
            error: "UnknownError".into(),
            ..Default::default()
        }
    }
}

#[derive(Serialize, Deserialize)]
pub enum Response {
    Ok(Reply),
    Err(Error),
}

pub struct NulJsonCodec;

impl Decoder for NulJsonCodec {
    type Item = Request;
    type Error = io::Error;
    fn decode(&mut self, buf: &mut BytesMut) -> io::Result<Option<Request>> {
        if let Some(i) = buf.iter().position(|&b| b == 0) {
            // remove the serialized frame from the buffer.

            let line = buf.split_to(i);
            //println!("got {:?}", line);
            // Also remove the '0'
            buf.split_to(1);

            Ok(Some(serde_json::from_slice(&line)?))
        } else {
            Ok(None)
        }
    }
}

impl Encoder for NulJsonCodec {
    type Item = Response;
    type Error = io::Error;

    fn encode(&mut self, msg: Response, buf: &mut BytesMut) -> io::Result<()> {
        match msg {
            Response::Ok(val) => {
                println!("Response: {}", serde_json::to_string(&val).unwrap());
                buf.extend(serde_json::to_vec(&val)?)
            }
            Response::Err(val) => {
                println!("Response: {}", serde_json::to_string(&val).unwrap());
                buf.extend(serde_json::to_vec(&val)?)
            }

        }
        buf.put_u8(0);
        Ok(())
    }
}

pub struct Proto;

impl<T: AsyncRead + AsyncWrite + 'static> ServerProto<T> for Proto {
    // For this protocol style, `Request` matches the `Item` type of the codec's `Encoder`
    type Request = Request;

    // For this protocol style, `Response` matches the `Item` type of the codec's `Decoder`
    type Response = Response;

    // A bit of boilerplate to hook in the codec:
    type Transport = Framed<T, NulJsonCodec>;
    type BindTransport = Result<Self::Transport, io::Error>;
    fn bind_transport(&self, io: T) -> Self::BindTransport {
        Ok(io.framed(NulJsonCodec))
    }
}

#[derive(Deserialize)]
struct GetInterfaceArgs {
    interface: Cow<'static, str>,
}

#[derive(Serialize, Deserialize, Default)]
struct Property {
    key: Cow<'static, str>,
    value: Cow<'static, str>,
}

#[derive(Serialize, Deserialize, Default)]
struct ServiceInfo {
    vendor: Cow<'static, str>,
    product: Cow<'static, str>,
    version: Cow<'static, str>,
    url: Cow<'static, str>,
    interfaces: Vec<Cow<'static, str>>,
}

pub struct VarlinkService {
    info: ServiceInfo,
    ifaces: HashMap<Cow<'static, str>, Box<Interface>>,
}

impl VarlinkService {
    pub fn new(vendor: Cow<'static, str>,
               product: Cow<'static, str>,
               version: Cow<'static, str>,
               url: Cow<'static, str>,
               ifaces: Vec<Box<Interface>>)
               -> Self {
        let mut ifhashmap = HashMap::<Cow<'static, str>, Box<Interface>>::new();
        for i in ifaces {
            ifhashmap.insert(i.get_name().into(), i);
        }
        let mut ifnames: Vec<Cow<'static, str>> = Vec::new();
        ifnames.push("org.varlink.service".into());
        ifnames.extend(ifhashmap
                           .keys()
                           .map(|i| Cow::<'static, str>::from(i.clone())));
        VarlinkService {
            info: ServiceInfo {
                vendor: vendor,
                product: product,
                version: version,
                url: url,
                interfaces: ifnames,
                ..Default::default()
            },
            ifaces: ifhashmap,
        }
    }
}

impl Service for VarlinkService {
    // These types must match the corresponding protocol types:
    type Request = Request;
    type Response = Response;

    // For non-streaming protocols, service errors are always io::Error
    type Error = io::Error;

    // The future for computing the response; box it for simplicity.
    type Future = BoxFuture<Self::Response, Self::Error>;

    // Produce a future for computing a response from a request.
    fn call(&self, req: Self::Request) -> Self::Future {

        println!("Request: {}", serde_json::to_string(&req).unwrap());
        let n: usize = match req.method.rfind('.') {
            None => {
                return future::ok(Response::Err(Error {
                                                    error: "InterfaceNotFound".into(),
                                                    parameters: Some(json!({"interface": req.method})),
                                                    ..Default::default()
                                                })).boxed()
            }
            Some(x) => x,
        };
        let method: String = req.method.clone().into();
        let (iface, _) = method.split_at(n);

        match iface.as_ref() {
            "org.varlink.service" => {
                match self::Interface::call(self, req) {
                    Ok(val) => future::ok(Response::Ok(Reply { parameters: Some(val) })).boxed(),
                    Err(e) => future::ok(Response::Err(e)).boxed(),
                }
            }
            key => {
                if self.ifaces.contains_key(key) {
                    match self.ifaces[key].call(req) {
                        Ok(val) => {
                            future::ok(Response::Ok(Reply { parameters: Some(val) })).boxed()
                        }
                        Err(e) => future::ok(Response::Err(e)).boxed(),
                    }
                } else {
                    future::ok(Response::Err(Error {
                                                 error: "InterfaceNotFound".into(),
                                                 parameters: Some(json!({"interface": key})),
                                                 ..Default::default()
                                             })).boxed()
                }
            }

        }
    }
}

impl Interface for VarlinkService {
    fn get_description(&self) -> &'static str {
        r#"
# The Varlink Service Interface is provided by every varlink service. It
# describes the service and the interfaces it implements.
interface org.varlink.service

# Get a list of all the interfaces a service provides and information
# about the implementation.
method GetInfo() -> (
  vendor: string,
  product: string,
  version: string,
  url: string,
  interfaces: string[]
)

# Get the description of an interface that is implemented by this service.
method GetInterfaceDescription(interface: string) -> (description: string)

# The requested interface was not found.
error InterfaceNotFound (interface: string)

# The requested method was not found
error MethodNotFound (method: string)

# The interface defines the requested method, but the service does not
# implement it.
error MethodNotImplemented (method: string)

# One of the passed parameters is invalid.
error InvalidParameter (parameter: string)
	"#
    }

    fn get_name(&self) -> &'static str {
        "org.varlink.service"
    }

    fn call(&self, req: Request) -> Result<Value, Error> {
        match req.method.as_ref() {
            "org.varlink.service.GetInfo" => {
                return Ok(serde_json::to_value(&self.info)?);
            }
            "org.varlink.service.GetInterfaceDescription" => {
                if req.parameters == None {
                    return Err(Error {
                                   error: "InvalidParameter".into(),
                                   ..Default::default()
                               });
                }
                let args: GetInterfaceArgs = serde_json::from_value(req.parameters.unwrap())
                    .unwrap();
                match args.interface.as_ref() {
                    "org.varlink.service" => Ok(json!({"description": self.get_description()})),
                    key => {
                        if self.ifaces.contains_key(key) {
                            Ok(json!({"description": self.ifaces[key].get_description()}))
                        } else {
                            Err(Error {
                                    error: "InvalidParameter".into(),
                                    parameters: Some(json!({"parameter": "interface"})),
                                    ..Default::default()
                                })
                        }
                    }
                }
            }
            m => {
                let method: String = m.into();
                let n: usize = match method.rfind('.') {
                    None => 0,
                    Some(x) => x + 1,
                };
                let (_, method) = method.split_at(n);

                Err(Error {
                        error: "MethodNotFound".into(),
                        parameters: Some(json!({"method": method})),
                        ..Default::default()
                    })
            }
        }
    }
}
<commit_msg>Fix some smaller compilation issues with rust-1.22<commit_after>use serde_json::{self, Value};

use std::convert::From;
use std::io;
use std::collections::HashMap;
use std::borrow::Cow;

use bytes::BytesMut;
use bytes::BufMut;

use futures::{future, Future};

use tokio_proto::pipeline::ServerProto;
use tokio_service::Service;
use tokio_io::codec::{Encoder, Decoder};
use tokio_io::{AsyncRead, AsyncWrite};
use tokio_io::codec::Framed;

pub trait Interface {
    fn get_description(&self) -> &'static str;
    fn get_name(&self) -> &'static str;
    fn call(&self, Request) -> Result<Value, Error>;
}

#[derive(Serialize, Deserialize)]
pub struct Request {
    pub method: Cow<'static, str>,
    pub parameters: Option<Value>,
}

#[derive(Serialize, Deserialize)]
pub struct Reply {
    pub parameters: Option<Value>,
}

#[derive(Serialize, Deserialize, Default)]
pub struct Error {
    pub error: Cow<'static, str>,
    pub parameters: Option<Value>,
}

impl From<serde_json::Error> for Error {
    fn from(_e: serde_json::Error) -> Self {
        Error {
            error: "UnknownError".into(),
            ..Default::default()
        }
    }
}

#[derive(Serialize, Deserialize)]
pub enum Response {
    Ok(Reply),
    Err(Error),
}

pub struct NulJsonCodec;

impl Decoder for NulJsonCodec {
    type Item = Request;
    type Error = io::Error;
    fn decode(&mut self, buf: &mut BytesMut) -> io::Result<Option<Request>> {
        if let Some(i) = buf.iter().position(|&b| b == 0) {
            // remove the serialized frame from the buffer.

            let line = buf.split_to(i);
            //println!("got {:?}", line);
            // Also remove the '0'
            buf.split_to(1);

            Ok(Some(serde_json::from_slice(&line)?))
        } else {
            Ok(None)
        }
    }
}

impl Encoder for NulJsonCodec {
    type Item = Response;
    type Error = io::Error;

    fn encode(&mut self, msg: Response, buf: &mut BytesMut) -> io::Result<()> {
        match msg {
            Response::Ok(val) => {
                println!("Response: {}", serde_json::to_string(&val).unwrap());
                buf.extend(serde_json::to_vec(&val)?)
            }
            Response::Err(val) => {
                println!("Response: {}", serde_json::to_string(&val).unwrap());
                buf.extend(serde_json::to_vec(&val)?)
            }

        }
        buf.put_u8(0);
        Ok(())
    }
}

pub struct Proto;

impl<T: AsyncRead + AsyncWrite + 'static> ServerProto<T> for Proto {
    // For this protocol style, `Request` matches the `Item` type of the codec's `Encoder`
    type Request = Request;

    // For this protocol style, `Response` matches the `Item` type of the codec's `Decoder`
    type Response = Response;

    // A bit of boilerplate to hook in the codec:
    type Transport = Framed<T, NulJsonCodec>;
    type BindTransport = Result<Self::Transport, io::Error>;
    fn bind_transport(&self, io: T) -> Self::BindTransport {
        Ok(io.framed(NulJsonCodec))
    }
}

#[derive(Deserialize)]
struct GetInterfaceArgs {
    interface: Cow<'static, str>,
}

#[derive(Serialize, Deserialize, Default)]
struct Property {
    key: Cow<'static, str>,
    value: Cow<'static, str>,
}

#[derive(Serialize, Deserialize, Default)]
struct ServiceInfo {
    vendor: Cow<'static, str>,
    product: Cow<'static, str>,
    version: Cow<'static, str>,
    url: Cow<'static, str>,
    interfaces: Vec<Cow<'static, str>>,
}

pub struct VarlinkService {
    info: ServiceInfo,
    ifaces: HashMap<Cow<'static, str>, Box<Interface>>,
}

impl VarlinkService {
    pub fn new(vendor: Cow<'static, str>,
               product: Cow<'static, str>,
               version: Cow<'static, str>,
               url: Cow<'static, str>,
               ifaces: Vec<Box<Interface>>)
               -> Self {
        let mut ifhashmap = HashMap::<Cow<'static, str>, Box<Interface>>::new();
        for i in ifaces {
            ifhashmap.insert(i.get_name().into(), i);
        }
        let mut ifnames: Vec<Cow<'static, str>> = Vec::new();
        ifnames.push("org.varlink.service".into());
        ifnames.extend(ifhashmap
                           .keys()
                           .map(|i| Cow::<'static, str>::from(i.clone())));
        VarlinkService {
            info: ServiceInfo {
                vendor: vendor,
                product: product,
                version: version,
                url: url,
                interfaces: ifnames,
                ..Default::default()
            },
            ifaces: ifhashmap,
        }
    }
}

impl Service for VarlinkService {
    // These types must match the corresponding protocol types:
    type Request = Request;
    type Response = Response;

    // For non-streaming protocols, service errors are always io::Error
    type Error = io::Error;

    // The future for computing the response; box it for simplicity.
    type Future = Box<Future<Item=Self::Response, Error=Self::Error> + Send>;

    // Produce a future for computing a response from a request.
    fn call(&self, req: Self::Request) -> Self::Future {

        println!("Request: {}", serde_json::to_string(&req).unwrap());
        let n: usize = match req.method.rfind('.') {
            None => {
                return Box::new(future::ok(Response::Err(Error {
                                                    error: "InterfaceNotFound".into(),
                                                    parameters: Some(json!({"interface": req.method})),
                                                    ..Default::default()
                                                })))
            }
            Some(x) => x,
        };
        let method: String = req.method.clone().into();
        let (iface, _) = method.split_at(n);

        match iface.as_ref() {
            "org.varlink.service" => {
                match self::Interface::call(self, req) {
                    Ok(val) => Box::new(future::ok(Response::Ok(Reply { parameters: Some(val) }))),
                    Err(e) => Box::new(future::ok(Response::Err(e)))
                }
            }
            key => {
                if self.ifaces.contains_key(key) {
                    match self.ifaces[key].call(req) {
                        Ok(val) => {
                            Box::new(future::ok(Response::Ok(Reply { parameters: Some(val) })))
                        }
                        Err(e) => Box::new(future::ok(Response::Err(e))),
                    }
                } else {
                    Box::new(future::ok(Response::Err(Error {
                                                 error: "InterfaceNotFound".into(),
                                                 parameters: Some(json!({"interface": key})),
                                                 ..Default::default()
                                             })))
                }
            }

        }
    }
}

impl Interface for VarlinkService {
    fn get_description(&self) -> &'static str {
        r#"
# The Varlink Service Interface is provided by every varlink service. It
# describes the service and the interfaces it implements.
interface org.varlink.service

# Get a list of all the interfaces a service provides and information
# about the implementation.
method GetInfo() -> (
  vendor: string,
  product: string,
  version: string,
  url: string,
  interfaces: string[]
)

# Get the description of an interface that is implemented by this service.
method GetInterfaceDescription(interface: string) -> (description: string)

# The requested interface was not found.
error InterfaceNotFound (interface: string)

# The requested method was not found
error MethodNotFound (method: string)

# The interface defines the requested method, but the service does not
# implement it.
error MethodNotImplemented (method: string)

# One of the passed parameters is invalid.
error InvalidParameter (parameter: string)
	"#
    }

    fn get_name(&self) -> &'static str {
        "org.varlink.service"
    }

    fn call(&self, req: Request) -> Result<Value, Error> {
        match req.method.as_ref() {
            "org.varlink.service.GetInfo" => {
                return Ok(serde_json::to_value(&self.info)?);
            }
            "org.varlink.service.GetInterfaceDescription" => {
                if req.parameters == None {
                    return Err(Error {
                                   error: "InvalidParameter".into(),
                                   ..Default::default()
                               });
                }
                let args: GetInterfaceArgs = serde_json::from_value(req.parameters.unwrap())
                    .unwrap();
                match args.interface.as_ref() {
                    "org.varlink.service" => Ok(json!({"description": self.get_description()})),
                    key => {
                        if self.ifaces.contains_key(key) {
                            Ok(json!({"description": self.ifaces[key].get_description()}))
                        } else {
                            Err(Error {
                                    error: "InvalidParameter".into(),
                                    parameters: Some(json!({"parameter": "interface"})),
                                    ..Default::default()
                                })
                        }
                    }
                }
            }
            m => {
                let method: String = m.into();
                let n: usize = match method.rfind('.') {
                    None => 0,
                    Some(x) => x + 1,
                };
                let (_, method) = method.split_at(n);

                Err(Error {
                        error: "MethodNotFound".into(),
                        parameters: Some(json!({"method": method})),
                        ..Default::default()
                    })
            }
        }
    }
}
<|endoftext|>


<commit_before>use num::{ Float, ToPrimitive, FromPrimitive };
use callback::Callable;
use frame::Frameable;
use color::{ Color, Colorable };
use label::{ FontSize, Labelable };
use dimensions::Dimensions;
use label;
use mouse::Mouse;
use graphics::Graphics;
use graphics::character::CharacterCache;
use point::Point;
use rectangle;
use ui_context::{ UIID, UiContext };
use utils::{
    clamp,
    percentage,
    value_from_perc,
};
use widget::Widget;
use vecmath::vec2_add;

/// Represents the state of the Button widget.
#[derive(PartialEq, Clone, Copy)]
pub enum State {
    Normal,
    Highlighted,
    Clicked,
}

impl State {
    /// Return the associated Rectangle state.
    fn as_rectangle_state(&self) -> rectangle::State {
        match self {
            &State::Normal => rectangle::State::Normal,
            &State::Highlighted => rectangle::State::Highlighted,
            &State::Clicked => rectangle::State::Clicked,
        }
    }
}

widget_fns!(Slider, State, Widget::Slider(State::Normal));

/// Check the current state of the slider.
fn get_new_state(is_over: bool,
                 prev: State,
                 mouse: Mouse) -> State {
    use mouse::ButtonState::{Down, Up};
    use self::State::{Normal, Highlighted, Clicked};
    match (is_over, prev, mouse.left) {
        (true,  Normal,  Down) => Normal,
        (true,  _,       Down) => Clicked,
        (true,  _,       Up)   => Highlighted,
        (false, Clicked, Down) => Clicked,
        _ => Normal,
    }
}

/// A context on which the builder pattern can be implemented.
pub struct Slider<'a, T, F> {
    ui_id: UIID,
    value: T,
    min: T,
    max: T,
    pos: Point,
    dim: Dimensions,
    maybe_callback: Option<F>,
    maybe_color: Option<Color>,
    maybe_frame: Option<f64>,
    maybe_frame_color: Option<Color>,
    maybe_label: Option<&'a str>,
    maybe_label_color: Option<Color>,
    maybe_label_font_size: Option<u32>,
}

impl<'a, T, F> Slider<'a, T, F> {
    /// A button builder method to be implemented by the UiContext.
    pub fn new(ui_id: UIID, value: T, min: T, max: T) -> Slider<'a, T, F> {
        Slider {
            ui_id: ui_id,
            value: value,
            min: min,
            max: max,
            pos: [0.0, 0.0],
            dim: [192.0, 48.0],
            maybe_callback: None,
            maybe_color: None,
            maybe_frame: None,
            maybe_frame_color: None,
            maybe_label: None,
            maybe_label_color: None,
            maybe_label_font_size: None,
        }
    }
}

impl<'a, T, F> Colorable for Slider<'a, T, F> {
    fn color(mut self, color: Color) -> Self {
        self.maybe_color = Some(color);
        self
    }
}

impl<'a, T, F> Frameable for Slider<'a, T, F> {
    fn frame(mut self, width: f64) -> Self {
        self.maybe_frame = Some(width);
        self
    }
    fn frame_color(mut self, color: Color) -> Self {
        self.maybe_frame_color = Some(color);
        self
    }
}

impl<'a, T, F> Callable<F> for Slider<'a, T, F> {
    fn callback(mut self, cb: F) -> Self {
        self.maybe_callback = Some(cb);
        self
    }
}

impl<'a, T, F> Labelable<'a> for Slider<'a, T, F>
{
    fn label(mut self, text: &'a str) -> Self {
        self.maybe_label = Some(text);
        self
    }

    fn label_color(mut self, color: Color) -> Self {
        self.maybe_label_color = Some(color);
        self
    }

    fn label_font_size(mut self, size: FontSize) -> Self {
        self.maybe_label_font_size = Some(size);
        self
    }
}

/*
quack! {
    slider: Slider['a, T, F]
    get:
        fn () -> Size [] { Size(slider.dim) }
        fn () -> DefaultWidgetState [] {
            DefaultWidgetState(Widget::Slider(State::Normal))
        }
        fn () -> Id [] { Id(slider.ui_id) }
    set:
        fn (val: Position) [] { slider.pos = val.0 }
        fn (val: Size) [] { slider.dim = val.0 }
    action:
}
*/

impl<'a, T, F> ::draw::Drawable for Slider<'a, T, F>
    where
        T: Float + FromPrimitive + ToPrimitive,
        F: FnMut(T) + 'a
{

    fn draw<B, C>(&mut self, uic: &mut UiContext<C>, graphics: &mut B)
        where
            B: Graphics<Texture = <C as CharacterCache>::Texture>,
            C: CharacterCache
    {

        let state = *get_state(uic, self.ui_id);
        let mouse = uic.get_mouse_state();
        let is_over = rectangle::is_over(self.pos, mouse.pos, self.dim);
        let new_state = get_new_state(is_over, state, mouse);

        let frame_w = self.maybe_frame.unwrap_or(uic.theme.frame_width);
        let frame_w2 = frame_w * 2.0;
        let frame_color = self.maybe_frame_color.unwrap_or(uic.theme.frame_color);

        let is_horizontal = self.dim[0] > self.dim[1];
        let (new_value, pad_pos, pad_dim) = if is_horizontal {
            // Horizontal.
            let p = vec2_add(self.pos, [frame_w, frame_w]);
            let max_w = self.dim[0] - frame_w2;
            let w = match (is_over, state, new_state) {
                (true, State::Highlighted, State::Clicked) | (_, State::Clicked, State::Clicked)  =>
                     clamp(mouse.pos[0] - p[0], 0f64, max_w),
                _ => clamp(percentage(self.value, self.min, self.max) as f64 * max_w, 0f64, max_w),
            };
            let h = self.dim[1] - frame_w2;
            let new_value = value_from_perc((w / max_w) as f32, self.min, self.max);
            (new_value, p, [w, h])
        } else {
            // Vertical.
            let max_h = self.dim[1] - frame_w2;
            let corner = vec2_add(self.pos, [frame_w, frame_w]);
            let y_max = corner[1] + max_h;
            let (h, p) = match (is_over, state, new_state) {
                (true, State::Highlighted, State::Clicked) | (_, State::Clicked, State::Clicked) => {
                    let p = [corner[0], clamp(mouse.pos[1], corner[1], y_max)];
                    let h = clamp(max_h - (p[1] - corner[1]), 0.0, max_h);
                    (h, p)
                },
                _ => {
                    let h = clamp(percentage(self.value, self.min, self.max) as f64 * max_h, 0.0, max_h);
                    let p = [corner[0], corner[1] + max_h - h];
                    (h, p)
                },
            };
            let w = self.dim[0] - frame_w2;
            let new_value = value_from_perc((h / max_h) as f32, self.min, self.max);
            (new_value, p, [w, h])
        };

        // Callback.
        match self.maybe_callback {
            Some(ref mut callback) => {
                if self.value != new_value || match (state, new_state) {
                    (State::Highlighted, State::Clicked) | (State::Clicked, State::Highlighted) => true,
                    _ => false,
                } { (*callback)(new_value) }
            }, None => (),
        }

        // Draw.
        let rect_state = new_state.as_rectangle_state();
        let color = self.maybe_color.unwrap_or(uic.theme.shape_color);

        // Rectangle frame / backdrop.
        rectangle::draw(uic.win_w, uic.win_h, graphics, rect_state,
                        self.pos, self.dim, None, frame_color);
        // Slider rectangle.
        rectangle::draw(uic.win_w, uic.win_h, graphics, rect_state,
                        pad_pos, pad_dim, None, color);

        // If there's a label, draw it.
        if let Some(text) = self.maybe_label {
            let text_color = self.maybe_label_color.unwrap_or(uic.theme.label_color);
            let size = self.maybe_label_font_size.unwrap_or(uic.theme.font_size_medium);
            let is_horizontal = self.dim[0] > self.dim[1];
            let l_pos = if is_horizontal {
                let x = pad_pos[0] + (pad_dim[1] - size as f64) / 2.0;
                let y = pad_pos[1] + (pad_dim[1] - size as f64) / 2.0;
                [x, y]
            } else {
                let label_w = label::width(uic, size, &text);
                let x = pad_pos[0] + (pad_dim[0] - label_w) / 2.0;
                let y = pad_pos[1] + pad_dim[1] - pad_dim[0] - frame_w;
                [x, y]
            };
            // Draw the label.
            uic.draw_text(graphics, l_pos, size, text_color, &text);
        }

        set_state(uic, self.ui_id, Widget::Slider(new_state), self.pos, self.dim);

    }
}
<commit_msg>Implemented `Positionable` for `Slider`<commit_after>use num::{ Float, ToPrimitive, FromPrimitive };
use callback::Callable;
use frame::Frameable;
use color::{ Color, Colorable };
use label::{ FontSize, Labelable };
use dimensions::Dimensions;
use label;
use mouse::Mouse;
use graphics::Graphics;
use graphics::character::CharacterCache;
use point::Point;
use position::Positionable;
use rectangle;
use ui_context::{ UIID, UiContext };
use utils::{
    clamp,
    percentage,
    value_from_perc,
};
use widget::Widget;
use vecmath::vec2_add;

/// Represents the state of the Button widget.
#[derive(PartialEq, Clone, Copy)]
pub enum State {
    Normal,
    Highlighted,
    Clicked,
}

impl State {
    /// Return the associated Rectangle state.
    fn as_rectangle_state(&self) -> rectangle::State {
        match self {
            &State::Normal => rectangle::State::Normal,
            &State::Highlighted => rectangle::State::Highlighted,
            &State::Clicked => rectangle::State::Clicked,
        }
    }
}

widget_fns!(Slider, State, Widget::Slider(State::Normal));

/// Check the current state of the slider.
fn get_new_state(is_over: bool,
                 prev: State,
                 mouse: Mouse) -> State {
    use mouse::ButtonState::{Down, Up};
    use self::State::{Normal, Highlighted, Clicked};
    match (is_over, prev, mouse.left) {
        (true,  Normal,  Down) => Normal,
        (true,  _,       Down) => Clicked,
        (true,  _,       Up)   => Highlighted,
        (false, Clicked, Down) => Clicked,
        _ => Normal,
    }
}

/// A context on which the builder pattern can be implemented.
pub struct Slider<'a, T, F> {
    ui_id: UIID,
    value: T,
    min: T,
    max: T,
    pos: Point,
    dim: Dimensions,
    maybe_callback: Option<F>,
    maybe_color: Option<Color>,
    maybe_frame: Option<f64>,
    maybe_frame_color: Option<Color>,
    maybe_label: Option<&'a str>,
    maybe_label_color: Option<Color>,
    maybe_label_font_size: Option<u32>,
}

impl<'a, T, F> Slider<'a, T, F> {
    /// A button builder method to be implemented by the UiContext.
    pub fn new(ui_id: UIID, value: T, min: T, max: T) -> Slider<'a, T, F> {
        Slider {
            ui_id: ui_id,
            value: value,
            min: min,
            max: max,
            pos: [0.0, 0.0],
            dim: [192.0, 48.0],
            maybe_callback: None,
            maybe_color: None,
            maybe_frame: None,
            maybe_frame_color: None,
            maybe_label: None,
            maybe_label_color: None,
            maybe_label_font_size: None,
        }
    }
}

impl<'a, T, F> Colorable for Slider<'a, T, F> {
    fn color(mut self, color: Color) -> Self {
        self.maybe_color = Some(color);
        self
    }
}

impl<'a, T, F> Frameable for Slider<'a, T, F> {
    fn frame(mut self, width: f64) -> Self {
        self.maybe_frame = Some(width);
        self
    }
    fn frame_color(mut self, color: Color) -> Self {
        self.maybe_frame_color = Some(color);
        self
    }
}

impl<'a, T, F> Callable<F> for Slider<'a, T, F> {
    fn callback(mut self, cb: F) -> Self {
        self.maybe_callback = Some(cb);
        self
    }
}

impl<'a, T, F> Labelable<'a> for Slider<'a, T, F>
{
    fn label(mut self, text: &'a str) -> Self {
        self.maybe_label = Some(text);
        self
    }

    fn label_color(mut self, color: Color) -> Self {
        self.maybe_label_color = Some(color);
        self
    }

    fn label_font_size(mut self, size: FontSize) -> Self {
        self.maybe_label_font_size = Some(size);
        self
    }
}

impl<'a, T, F> Positionable for Slider<'a, T, F> {
    fn point(mut self, pos: Point) -> Self {
        self.pos = pos;
        self
    }
}

/*
quack! {
    slider: Slider['a, T, F]
    get:
        fn () -> Size [] { Size(slider.dim) }
        fn () -> DefaultWidgetState [] {
            DefaultWidgetState(Widget::Slider(State::Normal))
        }
        fn () -> Id [] { Id(slider.ui_id) }
    set:
        fn (val: Size) [] { slider.dim = val.0 }
    action:
}
*/

impl<'a, T, F> ::draw::Drawable for Slider<'a, T, F>
    where
        T: Float + FromPrimitive + ToPrimitive,
        F: FnMut(T) + 'a
{

    fn draw<B, C>(&mut self, uic: &mut UiContext<C>, graphics: &mut B)
        where
            B: Graphics<Texture = <C as CharacterCache>::Texture>,
            C: CharacterCache
    {

        let state = *get_state(uic, self.ui_id);
        let mouse = uic.get_mouse_state();
        let is_over = rectangle::is_over(self.pos, mouse.pos, self.dim);
        let new_state = get_new_state(is_over, state, mouse);

        let frame_w = self.maybe_frame.unwrap_or(uic.theme.frame_width);
        let frame_w2 = frame_w * 2.0;
        let frame_color = self.maybe_frame_color.unwrap_or(uic.theme.frame_color);

        let is_horizontal = self.dim[0] > self.dim[1];
        let (new_value, pad_pos, pad_dim) = if is_horizontal {
            // Horizontal.
            let p = vec2_add(self.pos, [frame_w, frame_w]);
            let max_w = self.dim[0] - frame_w2;
            let w = match (is_over, state, new_state) {
                (true, State::Highlighted, State::Clicked) | (_, State::Clicked, State::Clicked)  =>
                     clamp(mouse.pos[0] - p[0], 0f64, max_w),
                _ => clamp(percentage(self.value, self.min, self.max) as f64 * max_w, 0f64, max_w),
            };
            let h = self.dim[1] - frame_w2;
            let new_value = value_from_perc((w / max_w) as f32, self.min, self.max);
            (new_value, p, [w, h])
        } else {
            // Vertical.
            let max_h = self.dim[1] - frame_w2;
            let corner = vec2_add(self.pos, [frame_w, frame_w]);
            let y_max = corner[1] + max_h;
            let (h, p) = match (is_over, state, new_state) {
                (true, State::Highlighted, State::Clicked) | (_, State::Clicked, State::Clicked) => {
                    let p = [corner[0], clamp(mouse.pos[1], corner[1], y_max)];
                    let h = clamp(max_h - (p[1] - corner[1]), 0.0, max_h);
                    (h, p)
                },
                _ => {
                    let h = clamp(percentage(self.value, self.min, self.max) as f64 * max_h, 0.0, max_h);
                    let p = [corner[0], corner[1] + max_h - h];
                    (h, p)
                },
            };
            let w = self.dim[0] - frame_w2;
            let new_value = value_from_perc((h / max_h) as f32, self.min, self.max);
            (new_value, p, [w, h])
        };

        // Callback.
        match self.maybe_callback {
            Some(ref mut callback) => {
                if self.value != new_value || match (state, new_state) {
                    (State::Highlighted, State::Clicked) | (State::Clicked, State::Highlighted) => true,
                    _ => false,
                } { (*callback)(new_value) }
            }, None => (),
        }

        // Draw.
        let rect_state = new_state.as_rectangle_state();
        let color = self.maybe_color.unwrap_or(uic.theme.shape_color);

        // Rectangle frame / backdrop.
        rectangle::draw(uic.win_w, uic.win_h, graphics, rect_state,
                        self.pos, self.dim, None, frame_color);
        // Slider rectangle.
        rectangle::draw(uic.win_w, uic.win_h, graphics, rect_state,
                        pad_pos, pad_dim, None, color);

        // If there's a label, draw it.
        if let Some(text) = self.maybe_label {
            let text_color = self.maybe_label_color.unwrap_or(uic.theme.label_color);
            let size = self.maybe_label_font_size.unwrap_or(uic.theme.font_size_medium);
            let is_horizontal = self.dim[0] > self.dim[1];
            let l_pos = if is_horizontal {
                let x = pad_pos[0] + (pad_dim[1] - size as f64) / 2.0;
                let y = pad_pos[1] + (pad_dim[1] - size as f64) / 2.0;
                [x, y]
            } else {
                let label_w = label::width(uic, size, &text);
                let x = pad_pos[0] + (pad_dim[0] - label_w) / 2.0;
                let y = pad_pos[1] + pad_dim[1] - pad_dim[0] - frame_w;
                [x, y]
            };
            // Draw the label.
            uic.draw_text(graphics, l_pos, size, text_color, &text);
        }

        set_state(uic, self.ui_id, Widget::Slider(new_state), self.pos, self.dim);

    }
}
<|endoftext|>