<commit_before>use std::cmp::min;
use std::f64;

use markup5ever::{expanded_name, local_name, namespace_url, ns};
use nalgebra::{DMatrix, Dynamic, VecStorage};

use crate::drawing_ctx::DrawingCtx;
use crate::error::{AttributeResultExt, NodeError};
use crate::node::{NodeResult, NodeTrait, RsvgNode};
use crate::parsers;
use crate::property_bag::PropertyBag;
use crate::rect::IRect;
use crate::surface_utils::{
    shared_surface::{BlurDirection, Horizontal, SharedImageSurface, Vertical},
    EdgeMode,
};

use super::context::{FilterContext, FilterOutput, FilterResult};
use super::{FilterEffect, FilterError, PrimitiveWithInput};

/// The maximum gaussian blur kernel size.
///
/// The value of 500 is used in webkit.
const MAXIMUM_KERNEL_SIZE: usize = 500;

/// The `feGaussianBlur` filter primitive.
pub struct FeGaussianBlur {
    base: PrimitiveWithInput,
    std_deviation: (f64, f64),
}

impl Default for FeGaussianBlur {
    /// Constructs a new `GaussianBlur` with empty properties.
    #[inline]
    fn default() -> FeGaussianBlur {
        FeGaussianBlur {
            base: PrimitiveWithInput::new::<Self>(),
            std_deviation: (0.0, 0.0),
        }
    }
}

impl NodeTrait for FeGaussianBlur {
    impl_node_as_filter_effect!();

    fn set_atts(&mut self, parent: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> NodeResult {
        self.base.set_atts(parent, pbag)?;

        for (attr, value) in pbag.iter() {
            match attr.expanded() {
                expanded_name!(svg "stdDeviation") => {
                    self.std_deviation = parsers::number_optional_number(value)
                        .attribute(attr.clone())
                        .and_then(|(x, y)| {
                            if x >= 0.0 && y >= 0.0 {
                                Ok((x, y))
                            } else {
                                Err(NodeError::value_error(attr, "values can't be negative"))
                            }
                        })?
                }
                _ => (),
            }
        }

        Ok(())
    }
}

/// Computes a gaussian kernel line for the given standard deviation.
fn gaussian_kernel(std_deviation: f64) -> Vec<f64> {
    assert!(std_deviation > 0.0);

    // Make sure there aren't any infinities.
    let maximal_deviation = (MAXIMUM_KERNEL_SIZE / 2) as f64 / 3.0;

    // Values further away than std_deviation * 3 are too small to contribute anything meaningful.
    let radius = ((std_deviation.min(maximal_deviation) * 3.0) + 0.5) as usize;
    // Clamp the radius rather than diameter because `MAXIMUM_KERNEL_SIZE` might be even and we
    // want an odd-sized kernel.
    let radius = min(radius, (MAXIMUM_KERNEL_SIZE - 1) / 2);
    let diameter = radius * 2 + 1;

    let mut kernel = Vec::with_capacity(diameter);

    let gauss_point = |x: f64| (-x.powi(2) / (2.0 * std_deviation.powi(2))).exp();

    // Fill the matrix by doing numerical integration approximation from -2*std_dev to 2*std_dev,
    // sampling 50 points per pixel. We do the bottom half, mirror it to the top half, then compute
    // the center point. Otherwise asymmetric quantization errors will occur. The formula to
    // integrate is e^-(x^2/2s^2).
    for i in 0..diameter / 2 {
        let base_x = (diameter / 2 + 1 - i) as f64 - 0.5;

        let mut sum = 0.0;
        for j in 1..=50 {
            let r = base_x + 0.02 * f64::from(j);
            sum += gauss_point(r);
        }

        kernel.push(sum / 50.0);
    }

    // We'll compute the middle point later.
    kernel.push(0.0);

    // Mirror the bottom half to the top half.
    for i in 0..diameter / 2 {
        let x = kernel[diameter / 2 - 1 - i];
        kernel.push(x);
    }

    // Find center val -- calculate an odd number of quanta to make it symmetric, even if the
    // center point is weighted slightly higher than others.
    let mut sum = 0.0;
    for j in 0..=50 {
        let r = -0.5 + 0.02 * f64::from(j);
        sum += gauss_point(r);
    }
    kernel[diameter / 2] = sum / 51.0;

    // Normalize the distribution by scaling the total sum to 1.
    let sum = kernel.iter().sum::<f64>();
    kernel.iter_mut().for_each(|x| *x /= sum);

    kernel
}

/// Returns a size of the box blur kernel to approximate the gaussian blur.
fn box_blur_kernel_size(std_deviation: f64) -> usize {
    let d = (std_deviation * 3.0 * (2.0 * f64::consts::PI).sqrt() / 4.0 + 0.5).floor();
    let d = d.min(MAXIMUM_KERNEL_SIZE as f64);
    d as usize
}

/// Applies three box blurs to approximate the gaussian blur.
///
/// This is intended to be used in two steps, horizontal and vertical.
fn three_box_blurs<B: BlurDirection>(
    surface: &SharedImageSurface,
    bounds: IRect,
    std_deviation: f64,
) -> Result<SharedImageSurface, FilterError> {
    let d = box_blur_kernel_size(std_deviation);
    if d == 0 {
        return Ok(surface.clone());
    }

    let surface = if d % 2 == 1 {
        // Odd kernel sizes just get three successive box blurs.
        let mut surface = surface.clone();

        for _ in 0..3 {
            surface = surface.box_blur::<B>(bounds, d, d / 2)?;
        }

        surface
    } else {
        // Even kernel sizes have a more interesting scheme.
        let surface = surface.box_blur::<B>(bounds, d, d / 2)?;
        let surface = surface.box_blur::<B>(bounds, d, d / 2 - 1)?;

        let d = d + 1;
        surface.box_blur::<B>(bounds, d, d / 2)?
    };

    Ok(surface)
}

/// Applies the gaussian blur.
///
/// This is intended to be used in two steps, horizontal and vertical.
fn gaussian_blur(
    input_surface: &SharedImageSurface,
    bounds: IRect,
    std_deviation: f64,
    vertical: bool,
) -> Result<SharedImageSurface, FilterError> {
    let kernel = gaussian_kernel(std_deviation);
    let (rows, cols) = if vertical {
        (kernel.len(), 1)
    } else {
        (1, kernel.len())
    };
    let kernel = DMatrix::from_data(VecStorage::new(
        Dynamic::new(rows),
        Dynamic::new(cols),
        kernel,
    ));

    Ok(input_surface.convolve(
        bounds,
        ((cols / 2) as i32, (rows / 2) as i32),
        &kernel,
        EdgeMode::None,
    )?)
}

impl FilterEffect for FeGaussianBlur {
    fn render(
        &self,
        _node: &RsvgNode,
        ctx: &FilterContext,
        draw_ctx: &mut DrawingCtx,
    ) -> Result<FilterResult, FilterError> {
        let input = self.base.get_input(ctx, draw_ctx)?;
        let bounds = self
            .base
            .get_bounds(ctx)
            .add_input(&input)
            .into_irect(draw_ctx);

        let (std_x, std_y) = self.std_deviation;
        let (std_x, std_y) = ctx.paffine().transform_distance(std_x, std_y);

        // The deviation can become negative here due to the transform.
        let std_x = std_x.abs();
        let std_y = std_y.abs();

        // Performance TODO: gaussian blur is frequently used for shadows, operating on SourceAlpha
        // (so the image is alpha-only). We can use this to not waste time processing the other
        // channels.

        // Horizontal convolution.
        let horiz_result_surface = if std_x != 0.0 {
            // The spec says for deviation >= 2.0 three box blurs can be used as an optimization.
            if std_x >= 2.0 {
                three_box_blurs::<Horizontal>(input.surface(), bounds, std_x)?
            } else {
                gaussian_blur(input.surface(), bounds, std_x, false)?
            }
        } else {
            input.surface().clone()
        };

        // Vertical convolution.
        let output_surface = if std_y != 0.0 {
            // The spec says for deviation >= 2.0 three box blurs can be used as an optimization.
            if std_y >= 2.0 {
                three_box_blurs::<Vertical>(&horiz_result_surface, bounds, std_y)?
            } else {
                gaussian_blur(&horiz_result_surface, bounds, std_y, true)?
            }
        } else {
            horiz_result_surface
        };

        Ok(FilterResult {
            name: self.base.result.clone(),
            output: FilterOutput {
                surface: output_surface,
                bounds,
            },
        })
    }

    #[inline]
    fn is_affected_by_color_interpolation_filters(&self) -> bool {
        true
    }
}
<commit_msg>gaussian_blur.rs: Don't create a NodeError directly<commit_after>use std::cmp::min;
use std::f64;

use markup5ever::{expanded_name, local_name, namespace_url, ns};
use nalgebra::{DMatrix, Dynamic, VecStorage};

use crate::drawing_ctx::DrawingCtx;
use crate::error::*;
use crate::node::{NodeResult, NodeTrait, RsvgNode};
use crate::parsers;
use crate::property_bag::PropertyBag;
use crate::rect::IRect;
use crate::surface_utils::{
    shared_surface::{BlurDirection, Horizontal, SharedImageSurface, Vertical},
    EdgeMode,
};

use super::context::{FilterContext, FilterOutput, FilterResult};
use super::{FilterEffect, FilterError, PrimitiveWithInput};

/// The maximum gaussian blur kernel size.
///
/// The value of 500 is used in webkit.
const MAXIMUM_KERNEL_SIZE: usize = 500;

/// The `feGaussianBlur` filter primitive.
pub struct FeGaussianBlur {
    base: PrimitiveWithInput,
    std_deviation: (f64, f64),
}

impl Default for FeGaussianBlur {
    /// Constructs a new `GaussianBlur` with empty properties.
    #[inline]
    fn default() -> FeGaussianBlur {
        FeGaussianBlur {
            base: PrimitiveWithInput::new::<Self>(),
            std_deviation: (0.0, 0.0),
        }
    }
}

impl NodeTrait for FeGaussianBlur {
    impl_node_as_filter_effect!();

    fn set_atts(&mut self, parent: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> NodeResult {
        self.base.set_atts(parent, pbag)?;

        for (attr, value) in pbag.iter() {
            match attr.expanded() {
                expanded_name!(svg "stdDeviation") => {
                    self.std_deviation = parsers::number_optional_number(value)
                        .and_then(|(x, y)| {
                            if x >= 0.0 && y >= 0.0 {
                                Ok((x, y))
                            } else {
                                Err(ValueErrorKind::value_error("values can't be negative"))
                            }
                        }).attribute(attr)?
                }
                _ => (),
            }
        }

        Ok(())
    }
}

/// Computes a gaussian kernel line for the given standard deviation.
fn gaussian_kernel(std_deviation: f64) -> Vec<f64> {
    assert!(std_deviation > 0.0);

    // Make sure there aren't any infinities.
    let maximal_deviation = (MAXIMUM_KERNEL_SIZE / 2) as f64 / 3.0;

    // Values further away than std_deviation * 3 are too small to contribute anything meaningful.
    let radius = ((std_deviation.min(maximal_deviation) * 3.0) + 0.5) as usize;
    // Clamp the radius rather than diameter because `MAXIMUM_KERNEL_SIZE` might be even and we
    // want an odd-sized kernel.
    let radius = min(radius, (MAXIMUM_KERNEL_SIZE - 1) / 2);
    let diameter = radius * 2 + 1;

    let mut kernel = Vec::with_capacity(diameter);

    let gauss_point = |x: f64| (-x.powi(2) / (2.0 * std_deviation.powi(2))).exp();

    // Fill the matrix by doing numerical integration approximation from -2*std_dev to 2*std_dev,
    // sampling 50 points per pixel. We do the bottom half, mirror it to the top half, then compute
    // the center point. Otherwise asymmetric quantization errors will occur. The formula to
    // integrate is e^-(x^2/2s^2).
    for i in 0..diameter / 2 {
        let base_x = (diameter / 2 + 1 - i) as f64 - 0.5;

        let mut sum = 0.0;
        for j in 1..=50 {
            let r = base_x + 0.02 * f64::from(j);
            sum += gauss_point(r);
        }

        kernel.push(sum / 50.0);
    }

    // We'll compute the middle point later.
    kernel.push(0.0);

    // Mirror the bottom half to the top half.
    for i in 0..diameter / 2 {
        let x = kernel[diameter / 2 - 1 - i];
        kernel.push(x);
    }

    // Find center val -- calculate an odd number of quanta to make it symmetric, even if the
    // center point is weighted slightly higher than others.
    let mut sum = 0.0;
    for j in 0..=50 {
        let r = -0.5 + 0.02 * f64::from(j);
        sum += gauss_point(r);
    }
    kernel[diameter / 2] = sum / 51.0;

    // Normalize the distribution by scaling the total sum to 1.
    let sum = kernel.iter().sum::<f64>();
    kernel.iter_mut().for_each(|x| *x /= sum);

    kernel
}

/// Returns a size of the box blur kernel to approximate the gaussian blur.
fn box_blur_kernel_size(std_deviation: f64) -> usize {
    let d = (std_deviation * 3.0 * (2.0 * f64::consts::PI).sqrt() / 4.0 + 0.5).floor();
    let d = d.min(MAXIMUM_KERNEL_SIZE as f64);
    d as usize
}

/// Applies three box blurs to approximate the gaussian blur.
///
/// This is intended to be used in two steps, horizontal and vertical.
fn three_box_blurs<B: BlurDirection>(
    surface: &SharedImageSurface,
    bounds: IRect,
    std_deviation: f64,
) -> Result<SharedImageSurface, FilterError> {
    let d = box_blur_kernel_size(std_deviation);
    if d == 0 {
        return Ok(surface.clone());
    }

    let surface = if d % 2 == 1 {
        // Odd kernel sizes just get three successive box blurs.
        let mut surface = surface.clone();

        for _ in 0..3 {
            surface = surface.box_blur::<B>(bounds, d, d / 2)?;
        }

        surface
    } else {
        // Even kernel sizes have a more interesting scheme.
        let surface = surface.box_blur::<B>(bounds, d, d / 2)?;
        let surface = surface.box_blur::<B>(bounds, d, d / 2 - 1)?;

        let d = d + 1;
        surface.box_blur::<B>(bounds, d, d / 2)?
    };

    Ok(surface)
}

/// Applies the gaussian blur.
///
/// This is intended to be used in two steps, horizontal and vertical.
fn gaussian_blur(
    input_surface: &SharedImageSurface,
    bounds: IRect,
    std_deviation: f64,
    vertical: bool,
) -> Result<SharedImageSurface, FilterError> {
    let kernel = gaussian_kernel(std_deviation);
    let (rows, cols) = if vertical {
        (kernel.len(), 1)
    } else {
        (1, kernel.len())
    };
    let kernel = DMatrix::from_data(VecStorage::new(
        Dynamic::new(rows),
        Dynamic::new(cols),
        kernel,
    ));

    Ok(input_surface.convolve(
        bounds,
        ((cols / 2) as i32, (rows / 2) as i32),
        &kernel,
        EdgeMode::None,
    )?)
}

impl FilterEffect for FeGaussianBlur {
    fn render(
        &self,
        _node: &RsvgNode,
        ctx: &FilterContext,
        draw_ctx: &mut DrawingCtx,
    ) -> Result<FilterResult, FilterError> {
        let input = self.base.get_input(ctx, draw_ctx)?;
        let bounds = self
            .base
            .get_bounds(ctx)
            .add_input(&input)
            .into_irect(draw_ctx);

        let (std_x, std_y) = self.std_deviation;
        let (std_x, std_y) = ctx.paffine().transform_distance(std_x, std_y);

        // The deviation can become negative here due to the transform.
        let std_x = std_x.abs();
        let std_y = std_y.abs();

        // Performance TODO: gaussian blur is frequently used for shadows, operating on SourceAlpha
        // (so the image is alpha-only). We can use this to not waste time processing the other
        // channels.

        // Horizontal convolution.
        let horiz_result_surface = if std_x != 0.0 {
            // The spec says for deviation >= 2.0 three box blurs can be used as an optimization.
            if std_x >= 2.0 {
                three_box_blurs::<Horizontal>(input.surface(), bounds, std_x)?
            } else {
                gaussian_blur(input.surface(), bounds, std_x, false)?
            }
        } else {
            input.surface().clone()
        };

        // Vertical convolution.
        let output_surface = if std_y != 0.0 {
            // The spec says for deviation >= 2.0 three box blurs can be used as an optimization.
            if std_y >= 2.0 {
                three_box_blurs::<Vertical>(&horiz_result_surface, bounds, std_y)?
            } else {
                gaussian_blur(&horiz_result_surface, bounds, std_y, true)?
            }
        } else {
            horiz_result_surface
        };

        Ok(FilterResult {
            name: self.base.result.clone(),
            output: FilterOutput {
                surface: output_surface,
                bounds,
            },
        })
    }

    #[inline]
    fn is_affected_by_color_interpolation_filters(&self) -> bool {
        true
    }
}
<|endoftext|>


<commit_before>use std::mem::swap;

/// The buffer data structure, that Sodium is using.
///
/// This structure consists of two "subbuffers", which are just vectors over lines (defined by
/// Strings). The split is where the cursor currently is.
///
/// The second buffer is in reverse order to get the particular efficiency we want.
pub struct SplitBuffer {
    before: Vec<String>,
    after: Vec<String>,
}

impl SplitBuffer {
    /// Create a new empty buffer
    pub fn new() -> Self {
        SplitBuffer {
            before: vec![String::new()],
            after: Vec::new(),
        }
    }

    pub fn get_line(&self) -> &String {
        self.before.last().expect("Unexpected condition (the first part of the split buffer is empty)")
    }

    pub fn get_line_mut(&mut self) -> &mut String {
        self.before.last_mut().expect("Unexpected condition (the first part of the split buffer is empty)")
    }

    pub fn up(&mut self) -> bool {
        if self.before.len() == 1 {
            false
        } else {
            self.after.push(self.before.pop());
            true
        }
    }

    pub fn down(&mut self) -> bool {
        if self.after.len() == 1 {
            false
        } else {
            self.before.push(self.after.pop());
            true
        }
    }

    pub fn line(&self) -> usize {
        self.before.len()
    }

    pub fn get_nth_line(&self, n: usize) -> Option<&String> {
        if n < self.before.len() {
            Some(&self.before[n])
        } else if n < self.before.len() + self.after.len() {
            Some(&self.after[n - self.before.len()])
        } else {
            None
        }
    }

    pub fn get_nth_line_mut(&mut self, n: usize) -> Option<&mut String> {
        if n < self.before.len() {
            Some(&mut self.before[n])
        } else if n < self.before.len() + self.after.len() {
            Some(&mut self.after[n - self.before.len()])
        } else {
            None
        }
    }

    pub fn remove_line(&mut self, n: usize) {
        if n < self.before.len() {
            self.before.remove(n);
            Some(&mut self.before[n])
        } else if n < self.before.len() + self.after.len() {
            self.after.remove(n - self.before.len());
        }
    }

    pub fn from_lines(vec: Vec<String>) -> SplitBuffer {
        SplitBuffer {
            before: vec,
            after: Vec::new(),
        }
    }

    pub fn goto(&mut self, y: usize) {
        if y < self.line() {
            for _ in 1..self.line() - y {
                if !self.up() {
                    break;
                }
            }
        } else if y > self.line() {
            for _ in 1..y - self.line() {
                if !self.down() {
                    break;
                }
            }
        }
    }

    pub fn pop_line(&mut self) -> String {
        self.before.expect("Unexpected condition (Popped the last line)")
    }

    pub fn len(&self) -> usize {
        self.before.len() + self.after.len()
    }

    pub fn iter(&self) -> ::std::iter::Once<Vec<char>> {
        self.after.iter().chain(self.before.iter().reverse())
    }
}
<commit_msg>Add insert and index<commit_after>use std::mem::swap;
use std::ops::{Index, IndexMut};

/// The buffer data structure, that Sodium is using.
///
/// This structure consists of two "subbuffers", which are just vectors over lines (defined by
/// Strings). The split is where the cursor currently is.
///
/// The second buffer is in reverse order to get the particular efficiency we want.
pub struct SplitBuffer {
    before: Vec<String>,
    after: Vec<String>,
}

impl SplitBuffer {
    /// Create a new empty buffer
    pub fn new() -> Self {
        SplitBuffer {
            before: vec![String::new()],
            after: Vec::new(),
        }
    }

    pub fn cur_line(&self) -> &String {
        self.before.last().expect("Unexpected condition (the first part of the split buffer is empty)")
    }

    pub fn cur_line_mut(&mut self) -> &mut String {
        self.before.last_mut().expect("Unexpected condition (the first part of the split buffer is empty)")
    }

    pub fn up(&mut self) -> bool {
        if self.before.len() == 1 {
            false
        } else {
            self.after.push(self.before.pop());
            true
        }
    }

    pub fn down(&mut self) -> bool {
        if self.after.len() == 1 {
            false
        } else {
            self.before.push(self.after.pop());
            true
        }
    }

    pub fn y(&self) -> usize {
        self.before.len()
    }

    pub fn get_line(&self, n: usize) -> Option<&String> {
        if n < self.before.len() {
            Some(&self.before[n])
        } else if n < self.before.len() + self.after.len() {
            Some(&self.after[n - self.before.len()])
        } else {
            None
        }
    }

    pub fn get_line_mut(&mut self, n: usize) -> Option<&mut String> {
        if n < self.before.len() {
            Some(&mut self.before[n])
        } else if n < self.before.len() + self.after.len() {
            Some(&mut self.after[n - self.before.len()])
        } else {
            None
        }
    }

    pub fn remove_line(&mut self, n: usize) {
        if n < self.before.len() {
            self.before.remove(n);
        } else if n < self.before.len() + self.after.len() {
            self.after.remove(n - self.before.len());
        }
    }

    pub fn insert_line(&mut self, n: usize, line: String) {
        if n < self.before.len() {
            self.before.insert(n, line);
        } else if n < self.before.len() + self.after.len() {
            self.after.insert(n - self.before.len(), line);
        }
    }

    pub fn from_lines(vec: Vec<String>) -> SplitBuffer {
        SplitBuffer {
            before: vec,
            after: Vec::new(),
        }
    }

    pub fn goto(&mut self, y: usize) {
        if y < self.line() {
            for _ in 1..self.line() - y {
                if !self.up() {
                    break;
                }
            }
        } else if y > self.line() {
            for _ in 1..y - self.line() {
                if !self.down() {
                    break;
                }
            }
        }
    }

    pub fn pop_line(&mut self) -> String {
        self.before.expect("Unexpected condition (Popped the last line)")
    }

    pub fn len(&self) -> usize {
        self.before.len() + self.after.len()
    }

    pub fn lines(&self) -> ::std::iter::Once<Vec<char>> {
        self.after.iter().chain(self.before.iter().reverse())
    }
}

impl Index<usize> for SplitBuffer {
    type Output = String;

    fn index<'a>(&'a self, index: usize) -> &'a Foo {
        self.get_line(index).expect("Out of bound")
    }
}
impl IndexMut<usize> for SplitBuffer {
    type Output = String;

    fn index<'a>(&'a self, index: usize) -> &'a mut Foo {
        self.get_line(index).expect("Out of bound")
    }
}
<|endoftext|>


<commit_before>use std::path::PathBuf;
use std::io::stdin;
use std::fs::OpenOptions;
use std::result::Result as RResult;
use std::io::Read;
use std::ops::DerefMut;
use std::io::Write;
use std::io::stderr;
use std::process::exit;

use clap::ArgMatches;

use libimagrt::runtime::Runtime;
use libimagstore::store::Entry;
use libimagstore::store::EntryHeader;
use libimagstore::storeid::build_entry_path;
use libimagerror::trace::trace_error_exit;

use error::StoreError;
use error::StoreErrorKind;
use util::build_toml_header;

type Result<T> = RResult<T, StoreError>;

pub fn create(rt: &Runtime) {
    rt.cli()
        .subcommand_matches("create")
        .map(|scmd| {
            debug!("Found 'create' subcommand...");

            // unwrap is safe as value is required
            let path = scmd.value_of("path").or_else(|| scmd.value_of("id"));
            if path.is_none() {
                warn!("No ID / Path provided. Exiting now");
                write!(stderr(), "No ID / Path provided. Exiting now").ok();
                exit(1);
            }

            let path = build_entry_path(rt.store(), path.unwrap());
            if path.is_err() {
                trace_error_exit(&path.unwrap_err(), 1);
            }
            let path = path.unwrap();
            debug!("path = {:?}", path);

            if scmd.subcommand_matches("entry").is_some() {
                create_from_cli_spec(rt, scmd, &path)
                    .or_else(|_| create_from_source(rt, scmd, &path))
                    .or_else(|_| create_with_content_and_header(rt,
                                                                &path,
                                                                String::new(),
                                                                EntryHeader::new()))
            } else {
                create_with_content_and_header(rt, &path, String::new(), EntryHeader::new())
            }
            .unwrap_or_else(|e| debug!("Error building Entry: {:?}", e))
        });
}

fn create_from_cli_spec(rt: &Runtime, matches: &ArgMatches, path: &PathBuf) -> Result<()> {
    let content = matches.subcommand_matches("entry")
        .map_or_else(|| {
            debug!("Didn't find entry subcommand, getting raw content");
            matches.value_of("from-raw")
                .map_or_else(String::new, string_from_raw_src)
        }, |entry_subcommand| {
            debug!("Found entry subcommand, parsing content");
            entry_subcommand
                .value_of("content")
                .map_or_else(|| {
                    entry_subcommand.value_of("content-from")
                        .map_or_else(String::new, string_from_raw_src)
                }, String::from)
        });
    debug!("Got content with len = {}", content.len());

    let header = matches.subcommand_matches("entry")
        .map_or_else(EntryHeader::new,
            |entry_matches| build_toml_header(entry_matches, EntryHeader::new()));

    create_with_content_and_header(rt, path, content, header)
}

fn create_from_source(rt: &Runtime, matches: &ArgMatches, path: &PathBuf) -> Result<()> {
    let content = matches
        .value_of("from-raw")
        .ok_or(StoreError::new(StoreErrorKind::NoCommandlineCall, None))
        .map(string_from_raw_src);

    if content.is_err() {
        return content.map(|_| ());
    }
    let content = content.unwrap();
    debug!("Content with len = {}", content.len());

    Entry::from_str(path.clone(), &content[..])
        .and_then(|new_e| {
            let r = rt.store()
                .create(path.clone())
                .map(|mut old_e| {
                    *old_e.deref_mut() = new_e;
                });

            debug!("Entry build");
            r
        })
        .map_err(|serr| StoreError::new(StoreErrorKind::BackendError, Some(Box::new(serr))))
}

fn create_with_content_and_header(rt: &Runtime,
                                  path: &PathBuf,
                                  content: String,
                                  header: EntryHeader) -> Result<()>
{
    debug!("Creating entry with content");
    rt.store()
        .create(PathBuf::from(path))
        .map(|mut element| {
            {
                let mut e_content = element.get_content_mut();
                *e_content = content;
                debug!("New content set");
            }
            {
                let mut e_header  = element.get_header_mut();
                *e_header = header;
                debug!("New header set");
            }
        })
        .map_err(|e| StoreError::new(StoreErrorKind::BackendError, Some(Box::new(e))))
}

fn string_from_raw_src(raw_src: &str) -> String {
    let mut content = String::new();
    if raw_src == "-" {
        debug!("Reading entry from stdin");
        let res = stdin().read_to_string(&mut content);
        debug!("Read {:?} bytes", res);
    } else {
        debug!("Reading entry from file at {:?}", raw_src);
        let _ = OpenOptions::new()
            .read(true)
            .write(false)
            .create(false)
            .open(raw_src)
            .and_then(|mut f| f.read_to_string(&mut content));
    }
    content
}
<commit_msg>Replace unwrap() with match<commit_after>use std::path::PathBuf;
use std::io::stdin;
use std::fs::OpenOptions;
use std::result::Result as RResult;
use std::io::Read;
use std::ops::DerefMut;
use std::io::Write;
use std::io::stderr;
use std::process::exit;

use clap::ArgMatches;

use libimagrt::runtime::Runtime;
use libimagstore::store::Entry;
use libimagstore::store::EntryHeader;
use libimagstore::storeid::build_entry_path;
use libimagerror::trace::trace_error_exit;

use error::StoreError;
use error::StoreErrorKind;
use util::build_toml_header;

type Result<T> = RResult<T, StoreError>;

pub fn create(rt: &Runtime) {
    rt.cli()
        .subcommand_matches("create")
        .map(|scmd| {
            debug!("Found 'create' subcommand...");

            // unwrap is safe as value is required
            let path = scmd.value_of("path").or_else(|| scmd.value_of("id"));
            if path.is_none() {
                warn!("No ID / Path provided. Exiting now");
                write!(stderr(), "No ID / Path provided. Exiting now").ok();
                exit(1);
            }

            let path = match build_entry_path(rt.store(), path.unwrap()) {
                Err(e) => trace_error_exit(&e, 1),
                Ok(p) => p,
            };
            debug!("path = {:?}", path);

            if scmd.subcommand_matches("entry").is_some() {
                create_from_cli_spec(rt, scmd, &path)
                    .or_else(|_| create_from_source(rt, scmd, &path))
                    .or_else(|_| create_with_content_and_header(rt,
                                                                &path,
                                                                String::new(),
                                                                EntryHeader::new()))
            } else {
                create_with_content_and_header(rt, &path, String::new(), EntryHeader::new())
            }
            .unwrap_or_else(|e| debug!("Error building Entry: {:?}", e))
        });
}

fn create_from_cli_spec(rt: &Runtime, matches: &ArgMatches, path: &PathBuf) -> Result<()> {
    let content = matches.subcommand_matches("entry")
        .map_or_else(|| {
            debug!("Didn't find entry subcommand, getting raw content");
            matches.value_of("from-raw")
                .map_or_else(String::new, string_from_raw_src)
        }, |entry_subcommand| {
            debug!("Found entry subcommand, parsing content");
            entry_subcommand
                .value_of("content")
                .map_or_else(|| {
                    entry_subcommand.value_of("content-from")
                        .map_or_else(String::new, string_from_raw_src)
                }, String::from)
        });
    debug!("Got content with len = {}", content.len());

    let header = matches.subcommand_matches("entry")
        .map_or_else(EntryHeader::new,
            |entry_matches| build_toml_header(entry_matches, EntryHeader::new()));

    create_with_content_and_header(rt, path, content, header)
}

fn create_from_source(rt: &Runtime, matches: &ArgMatches, path: &PathBuf) -> Result<()> {
    let content = matches
        .value_of("from-raw")
        .ok_or(StoreError::new(StoreErrorKind::NoCommandlineCall, None))
        .map(string_from_raw_src);

    if content.is_err() {
        return content.map(|_| ());
    }
    let content = content.unwrap();
    debug!("Content with len = {}", content.len());

    Entry::from_str(path.clone(), &content[..])
        .and_then(|new_e| {
            let r = rt.store()
                .create(path.clone())
                .map(|mut old_e| {
                    *old_e.deref_mut() = new_e;
                });

            debug!("Entry build");
            r
        })
        .map_err(|serr| StoreError::new(StoreErrorKind::BackendError, Some(Box::new(serr))))
}

fn create_with_content_and_header(rt: &Runtime,
                                  path: &PathBuf,
                                  content: String,
                                  header: EntryHeader) -> Result<()>
{
    debug!("Creating entry with content");
    rt.store()
        .create(PathBuf::from(path))
        .map(|mut element| {
            {
                let mut e_content = element.get_content_mut();
                *e_content = content;
                debug!("New content set");
            }
            {
                let mut e_header  = element.get_header_mut();
                *e_header = header;
                debug!("New header set");
            }
        })
        .map_err(|e| StoreError::new(StoreErrorKind::BackendError, Some(Box::new(e))))
}

fn string_from_raw_src(raw_src: &str) -> String {
    let mut content = String::new();
    if raw_src == "-" {
        debug!("Reading entry from stdin");
        let res = stdin().read_to_string(&mut content);
        debug!("Read {:?} bytes", res);
    } else {
        debug!("Reading entry from file at {:?}", raw_src);
        let _ = OpenOptions::new()
            .read(true)
            .write(false)
            .create(false)
            .open(raw_src)
            .and_then(|mut f| f.read_to_string(&mut content));
    }
    content
}
<|endoftext|>


<commit_before>use std::env;

use conduit::Request;
use diesel::prelude::*;
use diesel::r2d2::{self, ConnectionManager, CustomizeConnection};
use parking_lot::{ReentrantMutex, ReentrantMutexGuard};
use std::ops::Deref;
use url::Url;

use crate::middleware::app::RequestApp;
use crate::util::CargoResult;
use crate::Env;

#[allow(missing_debug_implementations)]
pub enum DieselPool {
    Pool(r2d2::Pool<ConnectionManager<PgConnection>>),
    Test(ReentrantMutex<PgConnection>),
}

impl DieselPool {
    pub fn get(&self) -> CargoResult<DieselPooledConn> {
        match self {
            DieselPool::Pool(pool) => Ok(DieselPooledConn::Pool(pool.get()?)),
            DieselPool::Test(conn) => Ok(DieselPooledConn::Test(conn.lock())),
        }
    }

    fn test_conn(conn: PgConnection) -> Self {
        DieselPool::Test(ReentrantMutex::new(conn))
    }
}

#[allow(missing_debug_implementations)]
pub enum DieselPooledConn<'a> {
    Pool(r2d2::PooledConnection<ConnectionManager<PgConnection>>),
    Test(ReentrantMutexGuard<'a, PgConnection>),
}

impl Deref for DieselPooledConn<'_> {
    type Target = PgConnection;

    fn deref(&self) -> &Self::Target {
        match self {
            DieselPooledConn::Pool(conn) => conn.deref(),
            DieselPooledConn::Test(conn) => conn.deref(),
        }
    }
}

pub fn connect_now() -> ConnectionResult<PgConnection> {
    use diesel::Connection;
    let mut url = Url::parse(&crate::env("DATABASE_URL")).expect("Invalid database URL");
    if env::var("HEROKU").is_ok() && !url.query_pairs().any(|(k, _)| k == "sslmode") {
        url.query_pairs_mut().append_pair("sslmode", "require");
    }
    PgConnection::establish(&url.to_string())
}

pub fn diesel_pool(
    url: &str,
    env: Env,
    config: r2d2::Builder<ConnectionManager<PgConnection>>,
) -> DieselPool {
    let mut url = Url::parse(url).expect("Invalid database URL");
    if env::var("HEROKU").is_ok() && !url.query_pairs().any(|(k, _)| k == "sslmode") {
        url.query_pairs_mut().append_pair("sslmode", "require");
    }

    if env == Env::Test {
        let conn =
            PgConnection::establish(&url.into_string()).expect("failed to establish connection");
        DieselPool::test_conn(conn)
    } else {
        let manager = ConnectionManager::new(url.into_string());
        DieselPool::Pool(config.build(manager).unwrap())
    }
}

pub trait RequestTransaction {
    /// Return the lazily initialized postgres connection for this request.
    ///
    /// The connection will live for the lifetime of the request.
    // FIXME: This description does not match the implementation below.
    fn db_conn(&self) -> CargoResult<DieselPooledConn>;
}

impl<T: Request + ?Sized> RequestTransaction for T {
    fn db_conn(&self) -> CargoResult<DieselPooledConn> {
        self.app().diesel_database.get().map_err(Into::into)
    }
}

#[derive(Debug, Clone, Copy)]
pub struct SetStatementTimeout(pub u64);

impl CustomizeConnection<PgConnection, r2d2::Error> for SetStatementTimeout {
    fn on_acquire(&self, conn: &mut PgConnection) -> Result<(), r2d2::Error> {
        use diesel::sql_query;

        sql_query(format!("SET statement_timeout = {}", self.0 * 1000))
            .execute(conn)
            .map_err(r2d2::Error::QueryError)?;
        Ok(())
    }
}
<commit_msg>Add a state method to `Pool`<commit_after>use std::env;

use conduit::Request;
use diesel::prelude::*;
use diesel::r2d2::{self, ConnectionManager, CustomizeConnection};
use parking_lot::{ReentrantMutex, ReentrantMutexGuard};
use std::ops::Deref;
use url::Url;

use crate::middleware::app::RequestApp;
use crate::util::CargoResult;
use crate::Env;

#[allow(missing_debug_implementations)]
pub enum DieselPool {
    Pool(r2d2::Pool<ConnectionManager<PgConnection>>),
    Test(ReentrantMutex<PgConnection>),
}

impl DieselPool {
    pub fn get(&self) -> CargoResult<DieselPooledConn> {
        match self {
            DieselPool::Pool(pool) => Ok(DieselPooledConn::Pool(pool.get()?)),
            DieselPool::Test(conn) => Ok(DieselPooledConn::Test(conn.lock())),
        }
    }

    pub fn state(&self) -> r2d2::State {
        match self {
            DieselPool::Pool(pool) => pool.state(),
            DieselPool::Test(_) => panic!("Cannot get the state of a test pool"),
        }
    }

    fn test_conn(conn: PgConnection) -> Self {
        DieselPool::Test(ReentrantMutex::new(conn))
    }
}

#[allow(missing_debug_implementations)]
pub enum DieselPooledConn<'a> {
    Pool(r2d2::PooledConnection<ConnectionManager<PgConnection>>),
    Test(ReentrantMutexGuard<'a, PgConnection>),
}

impl Deref for DieselPooledConn<'_> {
    type Target = PgConnection;

    fn deref(&self) -> &Self::Target {
        match self {
            DieselPooledConn::Pool(conn) => conn.deref(),
            DieselPooledConn::Test(conn) => conn.deref(),
        }
    }
}

pub fn connect_now() -> ConnectionResult<PgConnection> {
    use diesel::Connection;
    let mut url = Url::parse(&crate::env("DATABASE_URL")).expect("Invalid database URL");
    if env::var("HEROKU").is_ok() && !url.query_pairs().any(|(k, _)| k == "sslmode") {
        url.query_pairs_mut().append_pair("sslmode", "require");
    }
    PgConnection::establish(&url.to_string())
}

pub fn diesel_pool(
    url: &str,
    env: Env,
    config: r2d2::Builder<ConnectionManager<PgConnection>>,
) -> DieselPool {
    let mut url = Url::parse(url).expect("Invalid database URL");
    if env::var("HEROKU").is_ok() && !url.query_pairs().any(|(k, _)| k == "sslmode") {
        url.query_pairs_mut().append_pair("sslmode", "require");
    }

    if env == Env::Test {
        let conn =
            PgConnection::establish(&url.into_string()).expect("failed to establish connection");
        DieselPool::test_conn(conn)
    } else {
        let manager = ConnectionManager::new(url.into_string());
        DieselPool::Pool(config.build(manager).unwrap())
    }
}

pub trait RequestTransaction {
    /// Return the lazily initialized postgres connection for this request.
    ///
    /// The connection will live for the lifetime of the request.
    // FIXME: This description does not match the implementation below.
    fn db_conn(&self) -> CargoResult<DieselPooledConn>;
}

impl<T: Request + ?Sized> RequestTransaction for T {
    fn db_conn(&self) -> CargoResult<DieselPooledConn> {
        self.app().diesel_database.get().map_err(Into::into)
    }
}

#[derive(Debug, Clone, Copy)]
pub struct SetStatementTimeout(pub u64);

impl CustomizeConnection<PgConnection, r2d2::Error> for SetStatementTimeout {
    fn on_acquire(&self, conn: &mut PgConnection) -> Result<(), r2d2::Error> {
        use diesel::sql_query;

        sql_query(format!("SET statement_timeout = {}", self.0 * 1000))
            .execute(conn)
            .map_err(r2d2::Error::QueryError)?;
        Ok(())
    }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::io;
use std::string::ToString;

use buffoon::{OutputStream, Serialize};
use serde_json::builder::ObjectBuilder;

use adapter::Adapter;
use block;
use entry::{self, Entry};
use error::{Error, Result};
use metadata::Metadata;
use object::{Class, Object};
use objecthash::{self, ObjectHash, ObjectHasher};
use path::{Path, PathBuf};
use proto::ToProto;
use timestamp::Timestamp;

#[cfg(test)]
extern crate tempdir;

#[derive(Debug, Eq, PartialEq, Copy, Clone)]
pub enum Type {
    Add,
}

impl ObjectHash for Type {
    #[inline]
    fn objecthash<H: ObjectHasher>(&self, hasher: &mut H) {
        self.to_string().objecthash(hasher);
    }
}

impl Serialize for Type {
    fn serialize<O: OutputStream>(&self, _: &mut O) -> io::Result<()> {
        unimplemented!();
    }

    fn serialize_nested<O: OutputStream>(&self, field: u32, out: &mut O) -> io::Result<()> {
        out.write_varint(field, *self as u32 + 1)
    }
}

impl ToString for Type {
    fn to_string(&self) -> String {
        match *self {
            Type::Add => "ADD".to_string(),
        }
    }
}

pub struct Op {
    pub optype: Type,
    pub path: PathBuf,
    pub object: Object,
}

impl Op {
    pub fn new(optype: Type, path: PathBuf, object: Object) -> Op {
        Op {
            optype: optype,
            path: path,
            object: object,
        }
    }

    pub fn apply<'a, 'b, A: Adapter<'a>>(&'b self,
                                         adapter: &A,
                                         txn: &mut A::W,
                                         state: &mut State<'b>,
                                         block_id: &block::Id,
                                         timestamp: Timestamp)
                                         -> Result<()> {
        match self.optype {
            Type::Add => self.add(adapter, txn, state, block_id, timestamp),
        }
    }

    pub fn build_json(&self, builder: ObjectBuilder) -> ObjectBuilder {
        builder.insert("optype", self.optype.to_string())
            .insert("path", self.path.as_path().to_string())
            .insert_object("objectclass", |b| self.object.build_json(b))
    }

    fn add<'a, 'b, A: Adapter<'a>>(&'b self,
                                   adapter: &A,
                                   txn: &mut A::W,
                                   state: &mut State<'b>,
                                   block_id: &block::Id,
                                   timestamp: Timestamp)
                                   -> Result<()> {
        let entry_id = state.get_next_entry_id();

        let parent_path = self.path.as_path().parent();
        let parent_id = match parent_path {
            Some(path) => {
                let parent_entry = try!(state.get_entry(adapter, txn, path));

                if !parent_entry.class.allows_child(&self.object) {
                    return Err(Error::ObjectNestingError);
                }

                parent_entry.id
            }
            None => {
                if self.object.class() != Class::Root {
                    return Err(Error::ObjectNestingError);
                }

                entry::Id::root()
            }
        };

        let name = try!(self.path.as_path().entry_name().ok_or(Error::PathInvalid));
        let metadata = Metadata::new(block_id.clone(), timestamp);
        let proto = try!(self.object.to_proto());
        let entry = Entry {
            id: entry_id,
            class: self.object.class(),
            data: &proto,
        };

        // NOTE: The underlying adapter must handle Error::EntryAlreadyExists
        try!(adapter.add_entry(txn, &entry, &name, parent_id, &metadata));

        let new_entry = StateEntry {
            id: entry_id,
            class: self.object.class(),
        };
        state.new_entries.insert(self.path.as_path(), new_entry);

        Ok(())
    }
}

impl Serialize for Op {
    fn serialize<O: OutputStream>(&self, out: &mut O) -> io::Result<()> {
        try!(out.write(1, &self.optype));
        try!(out.write(2, &self.path.as_path().to_string()));
        try!(out.write(3, &self.object));

        Ok(())
    }
}

impl ObjectHash for Op {
    #[inline]
    fn objecthash<H: ObjectHasher>(&self, hasher: &mut H) {
        objecthash_struct!(
            hasher,
            "optype" => self.optype,
            "path" => self.path,
            "object" => self.object
        )
    }
}

pub struct State<'a> {
    next_entry_id: entry::Id,
    new_entries: HashMap<&'a Path, StateEntry>,
}

impl<'a> State<'a> {
    pub fn new(next_entry_id: entry::Id) -> State<'a> {
        State {
            next_entry_id: next_entry_id,
            new_entries: HashMap::new(),
        }
    }

    fn get_next_entry_id(&mut self) -> entry::Id {
        let id = self.next_entry_id;
        self.next_entry_id = id.next();
        id
    }

    fn get_entry<'b, A: Adapter<'b>>(&self,
                                     adapter: &A,
                                     txn: &mut A::W,
                                     path: &Path)
                                     -> Result<StateEntry> {
        match self.new_entries.get(path) {
            Some(ref parent_entry) => Ok(*parent_entry.clone()),
            _ => {
                let id = try!(adapter.find_direntry(txn, path)).id;
                let class = try!(adapter.find_entry(txn, &id)).class;
                Ok(StateEntry {
                    id: id,
                    class: class,
                })
            }
        }
    }
}

#[derive(Debug, Eq, PartialEq, Copy, Clone)]
struct StateEntry {
    id: entry::Id,
    class: Class,
}

#[cfg(test)]
pub mod tests {
    use ring::rand;
    use super::tempdir::TempDir;

    use adapter::Adapter;
    use adapter::lmdb::LmdbAdapter;
    use block;
    use error::Error;
    use log;
    use object::Object;
    use object::domain::DomainEntry;
    use object::root::RootEntry;
    use path::PathBuf;
    use timestamp::Timestamp;

    use super::{Op, State, Type};

    fn test_adapter() -> LmdbAdapter {
        let dir = TempDir::new("ithos-test").unwrap();
        LmdbAdapter::create_database(&dir.path()).unwrap()
    }

    fn example_timestamp() -> Timestamp {
        Timestamp::at(1_231_006_505)
    }

    #[test]
    fn nesting_constraint_violation() {
        let adapter = test_adapter();
        let rng = rand::SystemRandom::new();
        let logid = log::Id::generate(&rng).unwrap();

        // Test nesting constraints on root entry
        {
            let mut txn = adapter.rw_transaction().unwrap();

            let example_block_id = block::Id::root();

            let op = Op::new(Type::Add,
                             PathBuf::from("/".to_string()),
                             Object::Domain(DomainEntry::new(None)));

            let mut state = State::new(adapter.next_free_entry_id(&txn).unwrap());

            let result = op.apply(&adapter,
                                  &mut txn,
                                  &mut state,
                                  &example_block_id,
                                  example_timestamp());

            assert_eq!(result, Err(Error::ObjectNestingError));
        }

        // Test nesting constraints on a non-root entry
        {
            let mut txn = adapter.rw_transaction().unwrap();

            let example_block_id = block::Id::root();

            let op1 = Op::new(Type::Add,
                              PathBuf::from("/".to_string()),
                              Object::Root(RootEntry::new(logid)));

            let op2 = Op::new(Type::Add,
                              PathBuf::from("/derp".to_string()),
                              Object::Root(RootEntry::new(logid)));

            let mut state = State::new(adapter.next_free_entry_id(&txn).unwrap());

            let result1 = op1.apply(&adapter,
                                    &mut txn,
                                    &mut state,
                                    &example_block_id,
                                    example_timestamp());

            assert!(result1.is_ok());

            let result2 = op2.apply(&adapter,
                                    &mut txn,
                                    &mut state,
                                    &example_block_id,
                                    example_timestamp());

            assert_eq!(result2, Err(Error::ObjectNestingError));
        }
    }
}
<commit_msg>Remove needless clone<commit_after>use std::collections::HashMap;
use std::io;
use std::string::ToString;

use buffoon::{OutputStream, Serialize};
use serde_json::builder::ObjectBuilder;

use adapter::Adapter;
use block;
use entry::{self, Entry};
use error::{Error, Result};
use metadata::Metadata;
use object::{Class, Object};
use objecthash::{self, ObjectHash, ObjectHasher};
use path::{Path, PathBuf};
use proto::ToProto;
use timestamp::Timestamp;

#[cfg(test)]
extern crate tempdir;

#[derive(Debug, Eq, PartialEq, Copy, Clone)]
pub enum Type {
    Add,
}

impl ObjectHash for Type {
    #[inline]
    fn objecthash<H: ObjectHasher>(&self, hasher: &mut H) {
        self.to_string().objecthash(hasher);
    }
}

impl Serialize for Type {
    fn serialize<O: OutputStream>(&self, _: &mut O) -> io::Result<()> {
        unimplemented!();
    }

    fn serialize_nested<O: OutputStream>(&self, field: u32, out: &mut O) -> io::Result<()> {
        out.write_varint(field, *self as u32 + 1)
    }
}

impl ToString for Type {
    fn to_string(&self) -> String {
        match *self {
            Type::Add => "ADD".to_string(),
        }
    }
}

pub struct Op {
    pub optype: Type,
    pub path: PathBuf,
    pub object: Object,
}

impl Op {
    pub fn new(optype: Type, path: PathBuf, object: Object) -> Op {
        Op {
            optype: optype,
            path: path,
            object: object,
        }
    }

    pub fn apply<'a, 'b, A: Adapter<'a>>(&'b self,
                                         adapter: &A,
                                         txn: &mut A::W,
                                         state: &mut State<'b>,
                                         block_id: &block::Id,
                                         timestamp: Timestamp)
                                         -> Result<()> {
        match self.optype {
            Type::Add => self.add(adapter, txn, state, block_id, timestamp),
        }
    }

    pub fn build_json(&self, builder: ObjectBuilder) -> ObjectBuilder {
        builder.insert("optype", self.optype.to_string())
            .insert("path", self.path.as_path().to_string())
            .insert_object("objectclass", |b| self.object.build_json(b))
    }

    fn add<'a, 'b, A: Adapter<'a>>(&'b self,
                                   adapter: &A,
                                   txn: &mut A::W,
                                   state: &mut State<'b>,
                                   block_id: &block::Id,
                                   timestamp: Timestamp)
                                   -> Result<()> {
        let entry_id = state.get_next_entry_id();

        let parent_path = self.path.as_path().parent();
        let parent_id = match parent_path {
            Some(path) => {
                let parent_entry = try!(state.get_entry(adapter, txn, path));

                if !parent_entry.class.allows_child(&self.object) {
                    return Err(Error::ObjectNestingError);
                }

                parent_entry.id
            }
            None => {
                if self.object.class() != Class::Root {
                    return Err(Error::ObjectNestingError);
                }

                entry::Id::root()
            }
        };

        let name = try!(self.path.as_path().entry_name().ok_or(Error::PathInvalid));
        let metadata = Metadata::new(block_id.clone(), timestamp);
        let proto = try!(self.object.to_proto());
        let entry = Entry {
            id: entry_id,
            class: self.object.class(),
            data: &proto,
        };

        // NOTE: The underlying adapter must handle Error::EntryAlreadyExists
        try!(adapter.add_entry(txn, &entry, &name, parent_id, &metadata));

        let new_entry = StateEntry {
            id: entry_id,
            class: self.object.class(),
        };
        state.new_entries.insert(self.path.as_path(), new_entry);

        Ok(())
    }
}

impl Serialize for Op {
    fn serialize<O: OutputStream>(&self, out: &mut O) -> io::Result<()> {
        try!(out.write(1, &self.optype));
        try!(out.write(2, &self.path.as_path().to_string()));
        try!(out.write(3, &self.object));

        Ok(())
    }
}

impl ObjectHash for Op {
    #[inline]
    fn objecthash<H: ObjectHasher>(&self, hasher: &mut H) {
        objecthash_struct!(
            hasher,
            "optype" => self.optype,
            "path" => self.path,
            "object" => self.object
        )
    }
}

pub struct State<'a> {
    next_entry_id: entry::Id,
    new_entries: HashMap<&'a Path, StateEntry>,
}

impl<'a> State<'a> {
    pub fn new(next_entry_id: entry::Id) -> State<'a> {
        State {
            next_entry_id: next_entry_id,
            new_entries: HashMap::new(),
        }
    }

    fn get_next_entry_id(&mut self) -> entry::Id {
        let id = self.next_entry_id;
        self.next_entry_id = id.next();
        id
    }

    fn get_entry<'b, A: Adapter<'b>>(&self,
                                     adapter: &A,
                                     txn: &mut A::W,
                                     path: &Path)
                                     -> Result<StateEntry> {
        match self.new_entries.get(path) {
            Some(parent_entry) => Ok(*parent_entry),
            _ => {
                let id = try!(adapter.find_direntry(txn, path)).id;
                let class = try!(adapter.find_entry(txn, &id)).class;
                Ok(StateEntry {
                    id: id,
                    class: class,
                })
            }
        }
    }
}

#[derive(Debug, Eq, PartialEq, Copy, Clone)]
struct StateEntry {
    id: entry::Id,
    class: Class,
}

#[cfg(test)]
pub mod tests {
    use ring::rand;
    use super::tempdir::TempDir;

    use adapter::Adapter;
    use adapter::lmdb::LmdbAdapter;
    use block;
    use error::Error;
    use log;
    use object::Object;
    use object::domain::DomainEntry;
    use object::root::RootEntry;
    use path::PathBuf;
    use timestamp::Timestamp;

    use super::{Op, State, Type};

    fn test_adapter() -> LmdbAdapter {
        let dir = TempDir::new("ithos-test").unwrap();
        LmdbAdapter::create_database(&dir.path()).unwrap()
    }

    fn example_timestamp() -> Timestamp {
        Timestamp::at(1_231_006_505)
    }

    #[test]
    fn nesting_constraint_violation() {
        let adapter = test_adapter();
        let rng = rand::SystemRandom::new();
        let logid = log::Id::generate(&rng).unwrap();

        // Test nesting constraints on root entry
        {
            let mut txn = adapter.rw_transaction().unwrap();

            let example_block_id = block::Id::root();

            let op = Op::new(Type::Add,
                             PathBuf::from("/".to_string()),
                             Object::Domain(DomainEntry::new(None)));

            let mut state = State::new(adapter.next_free_entry_id(&txn).unwrap());

            let result = op.apply(&adapter,
                                  &mut txn,
                                  &mut state,
                                  &example_block_id,
                                  example_timestamp());

            assert_eq!(result, Err(Error::ObjectNestingError));
        }

        // Test nesting constraints on a non-root entry
        {
            let mut txn = adapter.rw_transaction().unwrap();

            let example_block_id = block::Id::root();

            let op1 = Op::new(Type::Add,
                              PathBuf::from("/".to_string()),
                              Object::Root(RootEntry::new(logid)));

            let op2 = Op::new(Type::Add,
                              PathBuf::from("/derp".to_string()),
                              Object::Root(RootEntry::new(logid)));

            let mut state = State::new(adapter.next_free_entry_id(&txn).unwrap());

            let result1 = op1.apply(&adapter,
                                    &mut txn,
                                    &mut state,
                                    &example_block_id,
                                    example_timestamp());

            assert!(result1.is_ok());

            let result2 = op2.apply(&adapter,
                                    &mut txn,
                                    &mut state,
                                    &example_block_id,
                                    example_timestamp());

            assert_eq!(result2, Err(Error::ObjectNestingError));
        }
    }
}
<|endoftext|>


<commit_before>use std::io::Result;
use std::io::Error;
use std::io::ErrorKind::Other;
use std::collections::btree_map::BTreeMap;

use uuid::Uuid;
use time::now;
use nix;

use lv::{LV, Segment};
use pv::PV;
use parser::{LvmTextMap, Entry};
use pvlabel::MDA;
use lvmetad::vg_update_lvmetad;

#[derive(Debug, PartialEq, Clone)]
pub struct VG {
    pub name: String,
    pub id: String,
    pub seqno: u64,
    pub format: String,
    pub status: Vec<String>,
    pub flags: Vec<String>,
    pub extent_size: u64,
    pub max_lv: u64,
    pub max_pv: u64,
    pub metadata_copies: u64,
    pub pvs: BTreeMap<String, PV>,
    pub lvs: BTreeMap<String, LV>,
}

impl VG {
    pub fn extents_in_use(&self) -> u64 {
        self.lvs
            .values()
            .map(|x| x.used_extents())
            .sum()
    }

    pub fn extents_free(&self) -> u64 {
        self.extents() - self.extents_in_use()
    }

    pub fn extents(&self) -> u64 {
        self.pvs
            .values()
            .map(|x| x.pe_count)
            .sum()
    }

    pub fn new_linear_lv(&mut self, name: &str, extent_size: u64) -> Result<()> {
        if self.lvs.contains_key(name) {
            return Err(Error::new(Other, "LV already exists"));
        }

        let mut contig_area = None;
        for (pvname, areas) in self.free_areas() {
            for (start, len) in areas {
                if len >= extent_size {
                    contig_area = Some((pvname, start));
                    break;
                }
            }
        }

        // we don't support multiple segments yet
        let (pv_with_area, area_start) = match contig_area {
            None => return Err(Error::new(Other, "no contiguous area for new LV")),
            Some(x) => x,
        };

        let segment = Segment {
            name: "segment1".to_string(),
            start_extent: 0,
            extent_count: extent_size,
            ty: "striped".to_string(),
            stripes: vec![(pv_with_area, area_start)],
        };

        let lv = LV {
            name: name.to_string(),
            id: Uuid::new_v4().to_hyphenated_string(),
            status: vec!["READ".to_string(),
                         "WRITE".to_string(),
                         "VISIBLE".to_string()],
            flags: Vec::new(),
            creation_host: nix::sys::utsname::uname().nodename().to_string(),
            creation_time: now().to_timespec().sec,
            segments: vec![segment],
        };

        self.lvs.insert(name.to_string(), lv);

        let map = self.clone().into();

        // TODO: atomicity of updating pvs, metad, dm
        for pv in self.pvs.values() {
            if let Some(path) = pv.device.path() {
                let mut mda = MDA::new(&path).expect("could not open MDA");

                try!(mda.write_metadata(&map));
            }
        }

        try!(vg_update_lvmetad(&map));

        // tell lvmetad
        // poke dm and tell it about a new device
        // open champagne

        Ok(())
    }

    pub fn used_areas(&self) -> BTreeMap<String, BTreeMap<u64, u64>> {
        let mut used_map = BTreeMap::new();

        // pretty sure this is only correct for my system...
        for lv in self.lvs.values() {
            for seg in &lv.segments {
                for &(ref pvname, start) in &seg.stripes {
                    used_map.entry(pvname.to_string())
                        .or_insert(BTreeMap::new())
                        .insert(start as u64, seg.extent_count);
                }
            }
        }

        used_map
    }

    pub fn free_areas(&self) -> BTreeMap<String, BTreeMap<u64, u64>> {
        let mut free_map = BTreeMap::new();

        for (pvname, area_map) in &mut self.used_areas() {

            // Insert an entry to mark the end of the PV so the fold works
            // correctly
            let pv = self.pvs.get(pvname)
                .expect("area map name refers to nonexistent PV");
            area_map.insert(pv.pe_count, 0);

            area_map.iter()
                .fold(0, |prev_end, (start, len)| {
                    if prev_end < *start {
                        free_map.entry(pvname.clone())
                            .or_insert(BTreeMap::new())
                            .insert(prev_end, start-prev_end);
                    }
                    start + len
                });
        }

        free_map
    }
}

impl From<VG> for LvmTextMap {
    fn from(vg: VG) -> Self {
        let mut map = LvmTextMap::new();

        map.insert("id".to_string(), Entry::String(vg.id));
        map.insert("seqno".to_string(),
                   Entry::Number(vg.seqno as i64 + 1));
        map.insert("format".to_string(), Entry::String(vg.format));

        map.insert("max_pv".to_string(), Entry::Number(0));
        map.insert("max_lv".to_string(), Entry::Number(0));

        map.insert("status".to_string(),
                   Entry::List(
                       Box::new(
                           vg.status
                               .into_iter()
                               .map(|x| Entry::String(x))
                               .collect())));

        map.insert("flags".to_string(),
                   Entry::List(
                       Box::new(
                           vg.flags
                               .into_iter()
                               .map(|x| Entry::String(x))
                               .collect())));

        map.insert("extent_size".to_string(),
                   Entry::Number(vg.extent_size as i64));
        map.insert("metadata_copies".to_string(),
                   Entry::Number(vg.metadata_copies as i64));
        map.insert("physical_volumes".to_string(),
                   Entry::TextMap(
                       Box::new(
                           vg.pvs
                               .into_iter()
                               .map(|(k, v)|
                                    (k, Entry::TextMap(Box::new(v.into()))))
                               .collect())));

        map.insert("logical_volumes".to_string(),
                   Entry::TextMap(
                       Box::new(
                           vg.lvs
                               .into_iter()
                               .map(|(k, v)|
                                    (k, Entry::TextMap(Box::new(v.into()))))
                               .collect())));

        let mut outer_map = LvmTextMap::new();

        outer_map.insert(vg.name, Entry::TextMap(Box::new(map)));

        outer_map
    }
}
<commit_msg>Fix free_areas to handle PVs with no allocated extents<commit_after>use std::io::Result;
use std::io::Error;
use std::io::ErrorKind::Other;
use std::collections::btree_map::BTreeMap;

use uuid::Uuid;
use time::now;
use nix;

use lv::{LV, Segment};
use pv::PV;
use parser::{LvmTextMap, Entry};
use pvlabel::MDA;
use lvmetad::vg_update_lvmetad;

#[derive(Debug, PartialEq, Clone)]
pub struct VG {
    pub name: String,
    pub id: String,
    pub seqno: u64,
    pub format: String,
    pub status: Vec<String>,
    pub flags: Vec<String>,
    pub extent_size: u64,
    pub max_lv: u64,
    pub max_pv: u64,
    pub metadata_copies: u64,
    pub pvs: BTreeMap<String, PV>,
    pub lvs: BTreeMap<String, LV>,
}

impl VG {
    pub fn extents_in_use(&self) -> u64 {
        self.lvs
            .values()
            .map(|x| x.used_extents())
            .sum()
    }

    pub fn extents_free(&self) -> u64 {
        self.extents() - self.extents_in_use()
    }

    pub fn extents(&self) -> u64 {
        self.pvs
            .values()
            .map(|x| x.pe_count)
            .sum()
    }

    pub fn new_linear_lv(&mut self, name: &str, extent_size: u64) -> Result<()> {
        if self.lvs.contains_key(name) {
            return Err(Error::new(Other, "LV already exists"));
        }

        let mut contig_area = None;
        for (pvname, areas) in self.free_areas() {
            for (start, len) in areas {
                if len >= extent_size {
                    contig_area = Some((pvname, start));
                    break;
                }
            }
        }

        // we don't support multiple segments yet
        let (pv_with_area, area_start) = match contig_area {
            None => return Err(Error::new(Other, "no contiguous area for new LV")),
            Some(x) => x,
        };

        let segment = Segment {
            name: "segment1".to_string(),
            start_extent: 0,
            extent_count: extent_size,
            ty: "striped".to_string(),
            stripes: vec![(pv_with_area, area_start)],
        };

        let lv = LV {
            name: name.to_string(),
            id: Uuid::new_v4().to_hyphenated_string(),
            status: vec!["READ".to_string(),
                         "WRITE".to_string(),
                         "VISIBLE".to_string()],
            flags: Vec::new(),
            creation_host: nix::sys::utsname::uname().nodename().to_string(),
            creation_time: now().to_timespec().sec,
            segments: vec![segment],
        };

        self.lvs.insert(name.to_string(), lv);

        let map = self.clone().into();

        // TODO: atomicity of updating pvs, metad, dm
        for pv in self.pvs.values() {
            if let Some(path) = pv.device.path() {
                let mut mda = MDA::new(&path).expect("could not open MDA");

                try!(mda.write_metadata(&map));
            }
        }

        try!(vg_update_lvmetad(&map));

        // tell lvmetad
        // poke dm and tell it about a new device
        // open champagne

        Ok(())
    }

    pub fn used_areas(&self) -> BTreeMap<String, BTreeMap<u64, u64>> {
        let mut used_map = BTreeMap::new();

        // pretty sure this is only correct for my system...
        for lv in self.lvs.values() {
            for seg in &lv.segments {
                for &(ref pvname, start) in &seg.stripes {
                    used_map.entry(pvname.to_string())
                        .or_insert(BTreeMap::new())
                        .insert(start as u64, seg.extent_count);
                }
            }
        }

        used_map
    }

    pub fn free_areas(&self) -> BTreeMap<String, BTreeMap<u64, u64>> {
        let mut free_map = BTreeMap::new();

        for (pvname, area_map) in &mut self.used_areas() {

            // Insert an entry to mark the end of the PV so the fold works
            // correctly
            let pv = self.pvs.get(pvname)
                .expect("area map name refers to nonexistent PV");
            area_map.insert(pv.pe_count, 0);

            area_map.iter()
                .fold(0, |prev_end, (start, len)| {
                    if prev_end < *start {
                        free_map.entry(pvname.clone())
                            .or_insert(BTreeMap::new())
                            .insert(prev_end, start-prev_end);
                    }
                    start + len
                });
        }

        for (pvname, pv) in &self.pvs {
            if !free_map.contains_key(pvname) {
                let mut map = BTreeMap::new();
                map.insert(0, pv.pe_count);
                free_map.insert(pvname.clone(), map);
            }
        }

        free_map
    }
}

impl From<VG> for LvmTextMap {
    fn from(vg: VG) -> Self {
        let mut map = LvmTextMap::new();

        map.insert("id".to_string(), Entry::String(vg.id));
        map.insert("seqno".to_string(),
                   Entry::Number(vg.seqno as i64 + 1));
        map.insert("format".to_string(), Entry::String(vg.format));

        map.insert("max_pv".to_string(), Entry::Number(0));
        map.insert("max_lv".to_string(), Entry::Number(0));

        map.insert("status".to_string(),
                   Entry::List(
                       Box::new(
                           vg.status
                               .into_iter()
                               .map(|x| Entry::String(x))
                               .collect())));

        map.insert("flags".to_string(),
                   Entry::List(
                       Box::new(
                           vg.flags
                               .into_iter()
                               .map(|x| Entry::String(x))
                               .collect())));

        map.insert("extent_size".to_string(),
                   Entry::Number(vg.extent_size as i64));
        map.insert("metadata_copies".to_string(),
                   Entry::Number(vg.metadata_copies as i64));
        map.insert("physical_volumes".to_string(),
                   Entry::TextMap(
                       Box::new(
                           vg.pvs
                               .into_iter()
                               .map(|(k, v)|
                                    (k, Entry::TextMap(Box::new(v.into()))))
                               .collect())));

        map.insert("logical_volumes".to_string(),
                   Entry::TextMap(
                       Box::new(
                           vg.lvs
                               .into_iter()
                               .map(|(k, v)|
                                    (k, Entry::TextMap(Box::new(v.into()))))
                               .collect())));

        let mut outer_map = LvmTextMap::new();

        outer_map.insert(vg.name, Entry::TextMap(Box::new(map)));

        outer_map
    }
}
<|endoftext|>


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

#![allow(bare_trait_objects)] // Until https://github.com/brendanzab/gl-rs/pull/493

pub type ServoGl = std::rc::Rc<dyn servo::gl::Gl>;

#[cfg(any(target_os = "android", target_os = "windows"))]
#[allow(non_camel_case_types)]
pub mod egl {
    use servo::gl::GlesFns;
    use std::ffi::CString;
    use std::os::raw::c_void;

    pub type EGLNativeWindowType = *const libc::c_void;
    pub type khronos_utime_nanoseconds_t = khronos_uint64_t;
    pub type khronos_uint64_t = libc::uint64_t;
    pub type khronos_ssize_t = libc::c_long;
    pub type EGLint = libc::int32_t;
    pub type EGLContext = *const libc::c_void;
    pub type EGLNativeDisplayType = *const libc::c_void;
    pub type EGLNativePixmapType = *const libc::c_void;
    pub type NativeDisplayType = EGLNativeDisplayType;
    pub type NativePixmapType = EGLNativePixmapType;
    pub type NativeWindowType = EGLNativeWindowType;

    include!(concat!(env!("OUT_DIR"), "/egl_bindings.rs"));

    pub struct EGLInitResult {
        pub gl_wrapper: crate::gl_glue::ServoGl,
        pub gl_context: EGLContext,
        pub display: EGLNativeDisplayType,
    }

    #[cfg(target_os = "android")]
    pub fn init() -> Result<EGLInitResult, &'static str> {
        info!("Loading EGL...");
        unsafe {
            let egl = Egl;
            let display = egl.GetCurrentDisplay();
            egl.SwapInterval(display, 1);
            let egl = GlesFns::load_with(|addr| {
                let addr = CString::new(addr.as_bytes()).unwrap();
                let addr = addr.as_ptr();
                let egl = Egl;
                egl.GetProcAddress(addr) as *const c_void
            });
            info!("EGL loaded");
            Ok(EGLInitResult {
                gl_wrapper: egl,
                gl_context: Egl.GetCurrentContext(),
                display,
            })
        }
    }
}

#[cfg(target_os = "windows")]
pub mod gl {
    pub fn init() -> Result<crate::gl_glue::ServoGl, &'static str> {
        unimplemented!();
    }
}

#[cfg(target_os = "macos")]
pub mod gl {
    use core_foundation::base::TCFType;
    use core_foundation::bundle::{
        CFBundleGetBundleWithIdentifier, CFBundleGetFunctionPointerForName,
    };
    use core_foundation::string::CFString;
    use servo::gl::GlFns;
    use std::os::raw::c_void;
    use std::str;

    pub fn init() -> Result<crate::gl_glue::ServoGl, &'static str> {
        info!("Loading OpenGL...");
        let gl = unsafe {
            GlFns::load_with(|addr| {
                let symbol_name: CFString = str::FromStr::from_str(addr).unwrap();
                let framework_name: CFString = str::FromStr::from_str("com.apple.opengl").unwrap();
                let framework =
                    CFBundleGetBundleWithIdentifier(framework_name.as_concrete_TypeRef());
                let symbol =
                    CFBundleGetFunctionPointerForName(framework, symbol_name.as_concrete_TypeRef());
                symbol as *const c_void
            })
        };
        info!("OpenGL loaded");
        Ok(gl)
    }
}

#[cfg(any(
    target_os = "linux",
    target_os = "dragonfly",
    target_os = "freebsd",
    target_os = "openbsd"
))]
pub mod gl {
    use libloading::{Library, Symbol};
    use servo::gl::GlFns;
    use std::ffi::CString;
    use std::os::raw::c_void;

    pub fn init() -> Result<crate::gl_glue::ServoGl, &'static str> {
        info!("Loading OpenGL");

        pub mod glx {
            include!(concat!(env!("OUT_DIR"), "/glx_bindings.rs"));
        }

        let lib = match Library::new("libGL.so.1").or_else(|_| Library::new("libGL.so")) {
            Ok(lib) => lib,
            Err(_) => return Err("Can't find libGL.so, OpenGL isn't configured/installed"),
        };

        let glx = glx::Glx::load_with(|sym| unsafe {
            let symbol: Symbol<*const c_void> = lib.get(sym.as_bytes()).unwrap();
            *symbol.into_raw()
        });

        let gl = unsafe {
            GlFns::load_with(|addr| {
                let addr = CString::new(addr.as_bytes()).unwrap();
                glx.GetProcAddress(addr.as_ptr() as *const _) as *const _
            })
        };

        info!("OpenGL is loaded");

        Ok(gl)
    }
}
<commit_msg>Fix Windows build<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */

#![allow(bare_trait_objects)] // Until https://github.com/brendanzab/gl-rs/pull/493

pub type ServoGl = std::rc::Rc<dyn servo::gl::Gl>;

#[cfg(any(target_os = "android", target_os = "windows"))]
#[allow(non_camel_case_types)]
pub mod egl {
    use servo::gl::GlesFns;
    use std::ffi::CString;
    use std::os::raw::c_void;

    pub type EGLNativeWindowType = *const libc::c_void;
    pub type khronos_utime_nanoseconds_t = khronos_uint64_t;
    pub type khronos_uint64_t = libc::uint64_t;
    pub type khronos_ssize_t = libc::c_long;
    pub type EGLint = libc::int32_t;
    pub type EGLContext = *const libc::c_void;
    pub type EGLNativeDisplayType = *const libc::c_void;
    pub type EGLNativePixmapType = *const libc::c_void;
    pub type NativeDisplayType = EGLNativeDisplayType;
    pub type NativePixmapType = EGLNativePixmapType;
    pub type NativeWindowType = EGLNativeWindowType;

    include!(concat!(env!("OUT_DIR"), "/egl_bindings.rs"));

    pub struct EGLInitResult {
        pub gl_wrapper: crate::gl_glue::ServoGl,
        pub gl_context: EGLContext,
        pub display: EGLNativeDisplayType,
    }

    pub fn init() -> Result<EGLInitResult, &'static str> {
        info!("Loading EGL...");
        unsafe {
            let egl = Egl;
            let display = egl.GetCurrentDisplay();
            egl.SwapInterval(display, 1);
            let egl = GlesFns::load_with(|addr| {
                let addr = CString::new(addr.as_bytes()).unwrap();
                let addr = addr.as_ptr();
                let egl = Egl;
                egl.GetProcAddress(addr) as *const c_void
            });
            info!("EGL loaded");
            Ok(EGLInitResult {
                gl_wrapper: egl,
                gl_context: Egl.GetCurrentContext(),
                display,
            })
        }
    }
}

#[cfg(target_os = "windows")]
pub mod gl {
    pub fn init() -> Result<crate::gl_glue::ServoGl, &'static str> {
        unimplemented!();
    }
}

#[cfg(target_os = "macos")]
pub mod gl {
    use core_foundation::base::TCFType;
    use core_foundation::bundle::{
        CFBundleGetBundleWithIdentifier, CFBundleGetFunctionPointerForName,
    };
    use core_foundation::string::CFString;
    use servo::gl::GlFns;
    use std::os::raw::c_void;
    use std::str;

    pub fn init() -> Result<crate::gl_glue::ServoGl, &'static str> {
        info!("Loading OpenGL...");
        let gl = unsafe {
            GlFns::load_with(|addr| {
                let symbol_name: CFString = str::FromStr::from_str(addr).unwrap();
                let framework_name: CFString = str::FromStr::from_str("com.apple.opengl").unwrap();
                let framework =
                    CFBundleGetBundleWithIdentifier(framework_name.as_concrete_TypeRef());
                let symbol =
                    CFBundleGetFunctionPointerForName(framework, symbol_name.as_concrete_TypeRef());
                symbol as *const c_void
            })
        };
        info!("OpenGL loaded");
        Ok(gl)
    }
}

#[cfg(any(
    target_os = "linux",
    target_os = "dragonfly",
    target_os = "freebsd",
    target_os = "openbsd"
))]
pub mod gl {
    use libloading::{Library, Symbol};
    use servo::gl::GlFns;
    use std::ffi::CString;
    use std::os::raw::c_void;

    pub fn init() -> Result<crate::gl_glue::ServoGl, &'static str> {
        info!("Loading OpenGL");

        pub mod glx {
            include!(concat!(env!("OUT_DIR"), "/glx_bindings.rs"));
        }

        let lib = match Library::new("libGL.so.1").or_else(|_| Library::new("libGL.so")) {
            Ok(lib) => lib,
            Err(_) => return Err("Can't find libGL.so, OpenGL isn't configured/installed"),
        };

        let glx = glx::Glx::load_with(|sym| unsafe {
            let symbol: Symbol<*const c_void> = lib.get(sym.as_bytes()).unwrap();
            *symbol.into_raw()
        });

        let gl = unsafe {
            GlFns::load_with(|addr| {
                let addr = CString::new(addr.as_bytes()).unwrap();
                glx.GetProcAddress(addr.as_ptr() as *const _) as *const _
            })
        };

        info!("OpenGL is loaded");

        Ok(gl)
    }
}
<|endoftext|>


<commit_before>#![crate_name = "rustful_macros"]

#![crate_type = "dylib"]

#![doc(html_root_url = "http://ogeon.github.io/rustful/doc/")]

#![feature(plugin_registrar, quote, rustc_private, collections, path, core)]

//!This crate provides some helpful macros for rustful, including `insert_routes!` and `content_type!`.
//!
//!#`insert_routes!`
//!The `insert_routes!` macro generates routes from the provided handlers and routing tree and
//!adds them to the provided router. The router is then returned.
//!
//!This can be useful to lower the risk of typing errors, among other things.
//!
//!##Example 1
//!
//!```rust ignore
//!#![feature(plugin)]
//!#[plugin]
//!#[no_link]
//!extern crate rustful_macros;
//!
//!extern crate rustful;
//!
//!...
//!
//!let router = insert_routes!{
//!    TreeRouter::new(): {
//!        "/about" => Get: about_us,
//!        "/user/:user" => Get: show_user,
//!        "/product/:name" => Get: show_product,
//!        "/*" => Get: show_error,
//!        "/" => Get: show_welcome
//!    }
//!};
//!```
//!
//!##Example 2
//!
//!```rust ignore
//!#![feature(plugin)]
//!#[plugin]
//!#[no_link]
//!extern crate rustful_macros;
//!
//!extern crate rustful;
//!
//!...
//!
//!let mut router = TreeRouter::new();
//!insert_routes!{
//!    &mut router: {
//!        "/" => Get: show_home,
//!        "home" => Get: show_home,
//!        "user/:username" => {Get: show_user, Post: save_user},
//!        "product" => {
//!            Get: show_all_products,
//!
//!            "json" => Get: send_all_product_data
//!            ":id" => {
//!                Get: show_product,
//!                Post | Delete: edit_product,
//!
//!                "json" => Get: send_product_data
//!            }
//!        }
//!    }
//!};
//!```

extern crate syntax;
extern crate rustc;

use std::path::BytesContainer;

use syntax::{ast, codemap};
use syntax::ext::base::{
    ExtCtxt, MacResult, MacExpr,
    NormalTT, TTMacroExpander
};
use syntax::ext::build::AstBuilder;
use syntax::ext::quote::rt::ExtParseUtils;
use syntax::parse;
use syntax::parse::token;
use syntax::parse::parser;
use syntax::parse::parser::Parser;
use syntax::ptr::P;
//use syntax::print::pprust;

use rustc::plugin::Registry;

#[plugin_registrar]
#[doc(hidden)]
pub fn macro_registrar(reg: &mut Registry) {
    let expander = Box::new(expand_routes) as Box<TTMacroExpander>;
    reg.register_syntax_extension(token::intern("insert_routes"), NormalTT(expander, None));
}

fn expand_routes<'cx>(cx: &'cx mut ExtCtxt, sp: codemap::Span, tts: &[ast::TokenTree]) -> Box<MacResult + 'cx> {
    let insert_method = cx.ident_of("insert");
    let router_ident = cx.ident_of("router");
    let router_var = cx.expr_ident(sp, router_ident);
    let router_trait_path = cx.path_global(sp, vec![cx.ident_of("rustful"), cx.ident_of("Router")]);
    let router_trait_use = cx.item_use_simple(sp, ast::Inherited, router_trait_path);

    let mut parser = parse::new_parser_from_tts(
        cx.parse_sess(), cx.cfg(), tts.to_vec()
    );

    let mut calls = vec![cx.stmt_item(sp, router_trait_use), cx.stmt_let(sp, true, router_ident, parser.parse_expr())];
    parser.expect(&token::Colon);

    for (path, method, handler) in parse_routes(cx, &mut parser) {
        let path_expr = cx.parse_expr(format!("\"{}\"", path));
        let method_expr = cx.expr_path(method);
        calls.push(cx.stmt_expr(cx.expr_method_call(sp, router_var.clone(), insert_method, vec![method_expr, path_expr, handler])));
    }

    let block = cx.expr_block(cx.block_all(sp, calls, Some(router_var)));

    MacExpr::new(block)
}

fn parse_routes(cx: &mut ExtCtxt, parser: &mut Parser) -> Vec<(String, ast::Path, P<ast::Expr>)> {
    parse_subroutes("", cx, parser)
}

fn parse_subroutes(base: &str, cx: &mut ExtCtxt, parser: &mut Parser) -> Vec<(String, ast::Path, P<ast::Expr>)> {
    let mut routes = Vec::new();

    parser.eat(&token::OpenDelim(token::Brace));

    while !parser.eat(&token::Eof) {
        match parser.parse_optional_str() {
            Some((ref s, _, _)) => {
                if !parser.eat(&token::FatArrow) {
                    parser.expect(&token::FatArrow);
                    break;
                }

                let mut new_base = base.to_string();
                match s.container_as_str() {
                    Some(s) => {
                        new_base.push_str(s.trim_matches('/'));
                        new_base.push_str("/");
                    },
                    None => cx.span_err(parser.span, "invalid path")
                }

                if parser.eat(&token::Eof) {
                    cx.span_err(parser.span, "unexpected end of routing tree");
                }

                if parser.eat(&token::OpenDelim(token::Brace)) {
                    let subroutes = parse_subroutes(&*new_base, cx, parser);
                    routes.push_all(&*subroutes);

                    if parser.eat(&token::CloseDelim(token::Brace)) {
                        if !parser.eat(&token::Comma) {
                            break;
                        }
                    } else {
                        parser.expect_one_of(&[token::Comma, token::CloseDelim(token::Brace)], &[]);
                    }
                } else {
                    for (method, handler) in parse_handler(parser) {
                        routes.push((new_base.clone(), method, handler))
                    }

                    if !parser.eat(&token::Comma) {
                        break;
                    }
                }
            },
            None => {
                for (method, handler) in parse_handler(parser) {
                    routes.push((base.to_string(), method, handler))
                }

                if !parser.eat(&token::Comma) {
                    break;
                }
            }
        }
    }

    routes
}

fn parse_handler(parser: &mut Parser) -> Vec<(ast::Path, P<ast::Expr>)> {
    let mut methods = Vec::new();

    loop {
        methods.push(parser.parse_path(parser::NoTypesAllowed));

        if parser.eat(&token::Colon) {
            break;
        }

        if !parser.eat(&token::BinOp(token::Or)) {
            parser.expect_one_of(&[token::Colon, token::BinOp(token::Or)], &[]);
        }
    }

    let handler = parser.parse_expr();

    methods.into_iter().map(|m| (m, handler.clone())).collect()
}

/**
A macro for assigning content types.

It takes a main type, a sub type and a parameter list. Instead of this:

```
response.headers.content_type = Some(MediaType {
    type_: String::from_str("text"),
    subtype: String::from_str("html"),
    parameters: vec!((String::from_str("charset"), String::from_str("UTF-8")))
});
```

it can be written like this:

```
response.headers.content_type = content_type!("text", "html", "charset": "UTF-8");
```

The `"charset": "UTF-8"` part defines the parameter list for the content type.
It may contain more than one parameter, or be omitted:

```
response.headers.content_type = content_type!("application", "octet-stream", "type": "image/gif", "padding": "4");
```

```
response.headers.content_type = content_type!("image", "png");
```
**/
#[macro_export]
macro_rules! content_type {
    ($main_type:expr, $sub_type:expr) => ({
        ::rustful::mime::Mime (
            std::str::FromStr::from_str($main_type).unwrap(),
            std::str::FromStr::from_str($sub_type).unwrap(),
            Vec::new()
        )
    });

    ($main_type:expr, $sub_type:expr, $(($param:expr, $value:expr)),+) => ({
        ::rustful::mime::Mime (
            std::str::FromStr::from_str($main_type).unwrap(),
            std::str::FromStr::from_str($sub_type).unwrap(),
            vec!( $( (std::str::FromStr::from_str($param).unwrap(), std::str::FromStr::from_str($value).unwrap()) ),+ )
        )
    });
}

/**
A macro for callig `send` in response and aborting the handle function if it fails.

This macro will print an error to `stdout`.
**/
#[macro_export]
macro_rules! try_send {
    ($writer:expr, $content:expr) => (
        match $writer.send($content) {
            Ok(v) => v,
            Err(::rustful::ResponseError::IoError(e)) => {
                println!("IO error: {}", e);
                return;
            },
            Err(::rustful::ResponseError::PluginError(e)) => {
                println!("plugin error: {}", e);
                return;
            }
        }
    );

    ($writer:expr, $content:expr, $what:expr) => (
        match $writer.send($content) {
            Ok(v) => v,
            Err(::rustful::ResponseError::IoError(e)) => {
                println!("IO error while {}: {}", $what, e);
                return;
            },
            Err(::rustful::ResponseError::PluginError(e)) => {
                println!("plugin error while {}: {}", $what, e);
                return;
            }
        }
    )
}<commit_msg>path -> old_path<commit_after>#![crate_name = "rustful_macros"]

#![crate_type = "dylib"]

#![doc(html_root_url = "http://ogeon.github.io/rustful/doc/")]

#![feature(plugin_registrar, quote, rustc_private, collections, path, core)]

//!This crate provides some helpful macros for rustful, including `insert_routes!` and `content_type!`.
//!
//!#`insert_routes!`
//!The `insert_routes!` macro generates routes from the provided handlers and routing tree and
//!adds them to the provided router. The router is then returned.
//!
//!This can be useful to lower the risk of typing errors, among other things.
//!
//!##Example 1
//!
//!```rust ignore
//!#![feature(plugin)]
//!#[plugin]
//!#[no_link]
//!extern crate rustful_macros;
//!
//!extern crate rustful;
//!
//!...
//!
//!let router = insert_routes!{
//!    TreeRouter::new(): {
//!        "/about" => Get: about_us,
//!        "/user/:user" => Get: show_user,
//!        "/product/:name" => Get: show_product,
//!        "/*" => Get: show_error,
//!        "/" => Get: show_welcome
//!    }
//!};
//!```
//!
//!##Example 2
//!
//!```rust ignore
//!#![feature(plugin)]
//!#[plugin]
//!#[no_link]
//!extern crate rustful_macros;
//!
//!extern crate rustful;
//!
//!...
//!
//!let mut router = TreeRouter::new();
//!insert_routes!{
//!    &mut router: {
//!        "/" => Get: show_home,
//!        "home" => Get: show_home,
//!        "user/:username" => {Get: show_user, Post: save_user},
//!        "product" => {
//!            Get: show_all_products,
//!
//!            "json" => Get: send_all_product_data
//!            ":id" => {
//!                Get: show_product,
//!                Post | Delete: edit_product,
//!
//!                "json" => Get: send_product_data
//!            }
//!        }
//!    }
//!};
//!```

extern crate syntax;
extern crate rustc;

use std::old_path::BytesContainer;

use syntax::{ast, codemap};
use syntax::ext::base::{
    ExtCtxt, MacResult, MacExpr,
    NormalTT, TTMacroExpander
};
use syntax::ext::build::AstBuilder;
use syntax::ext::quote::rt::ExtParseUtils;
use syntax::parse;
use syntax::parse::token;
use syntax::parse::parser;
use syntax::parse::parser::Parser;
use syntax::ptr::P;
//use syntax::print::pprust;

use rustc::plugin::Registry;

#[plugin_registrar]
#[doc(hidden)]
pub fn macro_registrar(reg: &mut Registry) {
    let expander = Box::new(expand_routes) as Box<TTMacroExpander>;
    reg.register_syntax_extension(token::intern("insert_routes"), NormalTT(expander, None));
}

fn expand_routes<'cx>(cx: &'cx mut ExtCtxt, sp: codemap::Span, tts: &[ast::TokenTree]) -> Box<MacResult + 'cx> {
    let insert_method = cx.ident_of("insert");
    let router_ident = cx.ident_of("router");
    let router_var = cx.expr_ident(sp, router_ident);
    let router_trait_path = cx.path_global(sp, vec![cx.ident_of("rustful"), cx.ident_of("Router")]);
    let router_trait_use = cx.item_use_simple(sp, ast::Inherited, router_trait_path);

    let mut parser = parse::new_parser_from_tts(
        cx.parse_sess(), cx.cfg(), tts.to_vec()
    );

    let mut calls = vec![cx.stmt_item(sp, router_trait_use), cx.stmt_let(sp, true, router_ident, parser.parse_expr())];
    parser.expect(&token::Colon);

    for (path, method, handler) in parse_routes(cx, &mut parser) {
        let path_expr = cx.parse_expr(format!("\"{}\"", path));
        let method_expr = cx.expr_path(method);
        calls.push(cx.stmt_expr(cx.expr_method_call(sp, router_var.clone(), insert_method, vec![method_expr, path_expr, handler])));
    }

    let block = cx.expr_block(cx.block_all(sp, calls, Some(router_var)));

    MacExpr::new(block)
}

fn parse_routes(cx: &mut ExtCtxt, parser: &mut Parser) -> Vec<(String, ast::Path, P<ast::Expr>)> {
    parse_subroutes("", cx, parser)
}

fn parse_subroutes(base: &str, cx: &mut ExtCtxt, parser: &mut Parser) -> Vec<(String, ast::Path, P<ast::Expr>)> {
    let mut routes = Vec::new();

    parser.eat(&token::OpenDelim(token::Brace));

    while !parser.eat(&token::Eof) {
        match parser.parse_optional_str() {
            Some((ref s, _, _)) => {
                if !parser.eat(&token::FatArrow) {
                    parser.expect(&token::FatArrow);
                    break;
                }

                let mut new_base = base.to_string();
                match s.container_as_str() {
                    Some(s) => {
                        new_base.push_str(s.trim_matches('/'));
                        new_base.push_str("/");
                    },
                    None => cx.span_err(parser.span, "invalid path")
                }

                if parser.eat(&token::Eof) {
                    cx.span_err(parser.span, "unexpected end of routing tree");
                }

                if parser.eat(&token::OpenDelim(token::Brace)) {
                    let subroutes = parse_subroutes(&*new_base, cx, parser);
                    routes.push_all(&*subroutes);

                    if parser.eat(&token::CloseDelim(token::Brace)) {
                        if !parser.eat(&token::Comma) {
                            break;
                        }
                    } else {
                        parser.expect_one_of(&[token::Comma, token::CloseDelim(token::Brace)], &[]);
                    }
                } else {
                    for (method, handler) in parse_handler(parser) {
                        routes.push((new_base.clone(), method, handler))
                    }

                    if !parser.eat(&token::Comma) {
                        break;
                    }
                }
            },
            None => {
                for (method, handler) in parse_handler(parser) {
                    routes.push((base.to_string(), method, handler))
                }

                if !parser.eat(&token::Comma) {
                    break;
                }
            }
        }
    }

    routes
}

fn parse_handler(parser: &mut Parser) -> Vec<(ast::Path, P<ast::Expr>)> {
    let mut methods = Vec::new();

    loop {
        methods.push(parser.parse_path(parser::NoTypesAllowed));

        if parser.eat(&token::Colon) {
            break;
        }

        if !parser.eat(&token::BinOp(token::Or)) {
            parser.expect_one_of(&[token::Colon, token::BinOp(token::Or)], &[]);
        }
    }

    let handler = parser.parse_expr();

    methods.into_iter().map(|m| (m, handler.clone())).collect()
}

/**
A macro for assigning content types.

It takes a main type, a sub type and a parameter list. Instead of this:

```
response.headers.content_type = Some(MediaType {
    type_: String::from_str("text"),
    subtype: String::from_str("html"),
    parameters: vec!((String::from_str("charset"), String::from_str("UTF-8")))
});
```

it can be written like this:

```
response.headers.content_type = content_type!("text", "html", "charset": "UTF-8");
```

The `"charset": "UTF-8"` part defines the parameter list for the content type.
It may contain more than one parameter, or be omitted:

```
response.headers.content_type = content_type!("application", "octet-stream", "type": "image/gif", "padding": "4");
```

```
response.headers.content_type = content_type!("image", "png");
```
**/
#[macro_export]
macro_rules! content_type {
    ($main_type:expr, $sub_type:expr) => ({
        ::rustful::mime::Mime (
            std::str::FromStr::from_str($main_type).unwrap(),
            std::str::FromStr::from_str($sub_type).unwrap(),
            Vec::new()
        )
    });

    ($main_type:expr, $sub_type:expr, $(($param:expr, $value:expr)),+) => ({
        ::rustful::mime::Mime (
            std::str::FromStr::from_str($main_type).unwrap(),
            std::str::FromStr::from_str($sub_type).unwrap(),
            vec!( $( (std::str::FromStr::from_str($param).unwrap(), std::str::FromStr::from_str($value).unwrap()) ),+ )
        )
    });
}

/**
A macro for callig `send` in response and aborting the handle function if it fails.

This macro will print an error to `stdout`.
**/
#[macro_export]
macro_rules! try_send {
    ($writer:expr, $content:expr) => (
        match $writer.send($content) {
            Ok(v) => v,
            Err(::rustful::ResponseError::IoError(e)) => {
                println!("IO error: {}", e);
                return;
            },
            Err(::rustful::ResponseError::PluginError(e)) => {
                println!("plugin error: {}", e);
                return;
            }
        }
    );

    ($writer:expr, $content:expr, $what:expr) => (
        match $writer.send($content) {
            Ok(v) => v,
            Err(::rustful::ResponseError::IoError(e)) => {
                println!("IO error while {}: {}", $what, e);
                return;
            },
            Err(::rustful::ResponseError::PluginError(e)) => {
                println!("plugin error while {}: {}", $what, e);
                return;
            }
        }
    )
}<|endoftext|>