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

use std::f32;
use std::iter;
use std::ops::Deref;

use super::{Drawable, MeasureMode, BoundingBox, AbsoluteLayout, AbsoluteLayoutParams, Glyph, GlyphIndex, Text};
use ::platform::Context;
use ::paint::{Point, Canvas, GlyphConstructionDirection, MathRuler, GlyphAssembly, GlyphAssemblyPart};
use ::props::{Color, Directionality, MathVariant};
use ::elements::Element;

type SymbolReader<T> = fn(&T) -> &str;
type SymmetricReader<T> = fn(&T) -> bool;
type SizeReader<T> = fn(&T) -> f32;
type DirReader<T> = fn(&T) -> &Directionality;
type ColorReader<T> = fn(&T) -> &Color;
type VariantReader<T> = fn(&T) -> &MathVariant;

pub struct Symbol<'a, T: Element + 'a> {
    props: &'a T,
    symbol_reader: SymbolReader<T>,
    math_variant_reader: VariantReader<T>,
    symmetric_reader: SymmetricReader<T>,
    base_size_reader: SizeReader<T>,
    max_size_reader: SizeReader<T>,
    min_size_reader: SizeReader<T>,
    dir_reader: DirReader<T>,
    color_reader: ColorReader<T>,

    bounding_box: BoundingBox,
    layout: AbsoluteLayout<'a>,
}

impl<'a, T: Element + 'a> Drawable for Symbol<'a, T> {
    fn draw(&self, canvas: &Canvas, pen_pos: &Point) {
        self.layout.draw(canvas, pen_pos);
    }

    fn calculate(&mut self, context: &Context, width_mode: &MeasureMode, height_mode: &MeasureMode) {
        let base_size = (self.base_size_reader)(self.props);
        let symbol = (self.symbol_reader)(self.props);
        let ruler = context.platform().get_math_ruler(self.props, base_size);

        let chars: Vec<char> = symbol.chars().collect();

        if chars.len() > 1 {
            self.set_text(context);
            return;
        }

        let symbol = chars[0] as u32;

        if let MeasureMode::UpTo(height) = *height_mode {
            let stretch_dir = GlyphConstructionDirection::Vertical;
            let stretched_size = base_size
                .max((self.min_size_reader)(self.props))
                .min((self.max_size_reader)(self.props))
                .max(height);

            if self.try_stretch_symbol(context,symbol, ruler, stretched_size, &stretch_dir) {
                return;
            }
        }

        if let MeasureMode::UpTo(width) = *width_mode {
            let stretch_dir = GlyphConstructionDirection::Horizontal;
            let stretched_size = base_size
                .max((self.min_size_reader)(self.props))
                .min((self.max_size_reader)(self.props))
                .max(width);

            if self.try_stretch_symbol(context,symbol, ruler, stretched_size, &stretch_dir) {
                return;
            }
        }

        self.set_single_glyph(context,GlyphIndex::Char(symbol));
    }

    fn bounding_box(&self) -> &BoundingBox {
        &self.bounding_box
    }
}

impl<'a, T: Element + 'a> Symbol<'a, T> {
    pub fn new(props: &'a T, symbol_reader: SymbolReader<T>, math_variant_reader: VariantReader<T>,
               symmetric_reader: SymmetricReader<T>,
               base_size_reader: SizeReader<T>, max_size_reader: SizeReader<T>,
               min_size_reader: SizeReader<T>, dir_reader: DirReader<T>,
               color_reader: ColorReader<T>) -> Symbol<'a, T> {
        Symbol {
            props,
            symbol_reader,
            math_variant_reader,
            symmetric_reader,
            base_size_reader,
            max_size_reader,
            min_size_reader,
            dir_reader,
            color_reader,
            bounding_box: BoundingBox::default(),
            layout: AbsoluteLayout::new(),
        }
    }

    fn set_single_glyph(&mut self, context: &Context, glyph: GlyphIndex) {
        let glyph = Glyph::new(
            self.props,
            glyph,
            self.base_size_reader,
            self.color_reader,
            self.dir_reader
        );

        self.layout.clear();
        self.layout.add_child(Box::new(glyph),
                              AbsoluteLayoutParams::new(Point::new(0., 0.)));

        self.layout.calculate(context, &MeasureMode::Wrap, &MeasureMode::Wrap);

        self.bounding_box = self.layout.bounding_box().clone();
    }

    fn try_stretch_symbol(&mut self, context: &Context, symbol: u32, ruler: &MathRuler,
                          stretched_size: f32, stretch_dir: &GlyphConstructionDirection) -> bool {
        let variants = ruler.glyph_variants(symbol, stretch_dir);

        for variant in variants {
            if variant.advance() >= stretched_size {
                self.set_single_glyph(context, GlyphIndex::Index(variant.glyph_index()));
                return true;
            }
        }

        let assembly = ruler.glyph_assembly(symbol, stretch_dir);
        if assembly.parts().len() >= 2 {
            self.set_glyph_assembly(context, assembly, &stretch_dir, stretched_size, ruler);
            return true;
        }

        return false;
    }

    fn set_glyph_assembly(&mut self, context: &Context, assembly: GlyphAssembly,
                          stretch_dir: &GlyphConstructionDirection,
                          stretched_size: f32, ruler: &MathRuler) {
        let min_connector_overlap = ruler.minimum_connector_overlap(stretch_dir);

        let mut num_extenders = 0;
        let mut total_extender_advance = 0f32;

        let mut num_non_extenders = 0;
        let mut total_non_extender_advance = 0f32;

        let mut max_connector_overlap = f32::INFINITY;

        for (index, part) in assembly.parts().iter().enumerate() {
            if part.is_extender() {
                num_extenders += 1;
                total_extender_advance += part.full_advance();
            } else {
                num_non_extenders += 1;
                total_non_extender_advance += part.full_advance();
            }

            if index != 0 {
                max_connector_overlap = max_connector_overlap.min(part.start_connector_length());
            }

            max_connector_overlap = max_connector_overlap.min(part.full_advance());

            if index != assembly.parts().len()-1 {
                max_connector_overlap = max_connector_overlap.min(part.end_connector_length());
            }
        }

        let num_extenders = num_extenders as f32;
        let num_non_extenders = num_non_extenders as f32;

        // Number of times extenders needs to be repeated to get size >= stretched size
        let extender_multiplier = ((stretched_size-total_non_extender_advance-
            min_connector_overlap*(1.-num_non_extenders)) /
            (total_extender_advance-(min_connector_overlap*num_extenders))).ceil();

        // Overlap adjustment to minimize size - stretched_size
        let required_overlap = (total_non_extender_advance + (extender_multiplier * total_extender_advance)
            - stretched_size) /
            (num_non_extenders + (extender_multiplier * num_extenders) - 1.);

        // Never use overlap greater than maximum connector overlap
        let overlap = required_overlap.min(max_connector_overlap);

        #[cfg(debug_assertions)]
        {
            assert!(max_connector_overlap >= min_connector_overlap);

            let size_with_overlap_correction = extender_multiplier*total_extender_advance
                +total_non_extender_advance -
                (overlap*(num_non_extenders+(extender_multiplier*num_extenders)-1.));

            let size_without_overlap_correction = extender_multiplier*total_extender_advance
                +total_non_extender_advance -
                (min_connector_overlap*(num_non_extenders+(extender_multiplier*num_extenders)-1.));

            assert!(size_without_overlap_correction >= stretched_size);

            assert!(size_with_overlap_correction <= size_without_overlap_correction);
        }

        self.layout.clear();

        let mut pen_pos = 0f32;

        // Ordering of parts in GlyphAssembly for vertical construction is from bottom to top
        // and for horizontal construction is from left to right
        match *stretch_dir {
            GlyphConstructionDirection::Vertical => {
                for part in assembly.parts().iter().rev() {
                    pen_pos = self.add_glyph_part_to_layout(part, stretch_dir, overlap,
                                                            extender_multiplier as u32, pen_pos);
                }
            },
            GlyphConstructionDirection::Horizontal => {
                for part in assembly.parts().iter() {
                    pen_pos = self.add_glyph_part_to_layout(part, stretch_dir, overlap,
                                                            extender_multiplier as u32, pen_pos);
                }
            }
        }

        self.layout.calculate(context, &MeasureMode::Wrap, &MeasureMode::Wrap);
        self.bounding_box = self.layout.bounding_box().clone();
    }

    fn add_glyph_part_to_layout(&mut self, part: &GlyphAssemblyPart, stretch_dir: &GlyphConstructionDirection,
                                overlap: f32, multiplier: u32, pen_pos: f32) -> f32 {
        let num_iters = if part.is_extender() {
            multiplier
        } else {
            1
        };

        let mut pen_pos = pen_pos;

        for i in 0..num_iters {
            self.layout.add_child(
                Box::new(Glyph::new(
                    self.props,
                    GlyphIndex::Index(part.glyph_index()),
                    self.base_size_reader,
                    self.color_reader,
                    self.dir_reader)
                ),
                AbsoluteLayoutParams::new(match *stretch_dir {
                    GlyphConstructionDirection::Horizontal => Point::new(pen_pos, 0.),
                    GlyphConstructionDirection::Vertical => Point::new(0., pen_pos)
                })
            );

            pen_pos += part.full_advance()-overlap;
        }

        pen_pos
    }

    fn set_text(&mut self, context: &Context) {
        let text = Text::new(
            self.props,
            self.symbol_reader,
            self.base_size_reader,
            self.math_variant_reader,
            self.dir_reader,
            self.color_reader
        );

        self.layout.clear();
        self.layout.add_child(Box::new(text),
                              AbsoluteLayoutParams::new(Point::new(0., 0.)));

        self.layout.calculate(context, &MeasureMode::Wrap, &MeasureMode::Wrap);

        self.bounding_box = self.layout.bounding_box().clone();
    }

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

use std::f32;

use super::{Drawable, MeasureMode, BoundingBox, AbsoluteLayout, AbsoluteLayoutParams, Glyph, GlyphIndex, Text};
use ::platform::Context;
use ::paint::{Point, Canvas, GlyphConstructionDirection, MathRuler, GlyphAssembly, GlyphAssemblyPart};
use ::props::{Color, Directionality, MathVariant};
use ::elements::Element;

type SymbolReader<T> = fn(&T) -> &str;
type SymmetricReader<T> = fn(&T) -> bool;
type SizeReader<T> = fn(&T) -> f32;
type DirReader<T> = fn(&T) -> &Directionality;
type ColorReader<T> = fn(&T) -> &Color;
type VariantReader<T> = fn(&T) -> &MathVariant;

pub struct Symbol<'a, T: Element + 'a> {
    props: &'a T,
    symbol_reader: SymbolReader<T>,
    math_variant_reader: VariantReader<T>,
    symmetric_reader: SymmetricReader<T>,
    base_size_reader: SizeReader<T>,
    max_size_reader: SizeReader<T>,
    min_size_reader: SizeReader<T>,
    dir_reader: DirReader<T>,
    color_reader: ColorReader<T>,

    bounding_box: BoundingBox,
    layout: AbsoluteLayout<'a>,
}

impl<'a, T: Element + 'a> Drawable for Symbol<'a, T> {
    fn draw(&self, canvas: &Canvas, pen_pos: &Point) {
        self.layout.draw(canvas, pen_pos);
    }

    fn calculate(&mut self, context: &Context, width_mode: &MeasureMode, height_mode: &MeasureMode) {
        let base_size = (self.base_size_reader)(self.props);
        let symbol = (self.symbol_reader)(self.props);
        let ruler = context.platform().get_math_ruler(self.props, base_size);

        let chars: Vec<char> = symbol.chars().collect();

        if chars.len() > 1 {
            self.set_text(context);
            return;
        }

        let symbol = chars[0] as u32;

        if let MeasureMode::UpTo(height) = *height_mode {
            let stretch_dir = GlyphConstructionDirection::Vertical;
            let stretched_size = base_size
                .max((self.min_size_reader)(self.props))
                .min((self.max_size_reader)(self.props))
                .max(height);

            if self.try_stretch_symbol(context,symbol, ruler, stretched_size, &stretch_dir) {
                return;
            }
        }

        if let MeasureMode::UpTo(width) = *width_mode {
            let stretch_dir = GlyphConstructionDirection::Horizontal;
            let stretched_size = base_size
                .max((self.min_size_reader)(self.props))
                .min((self.max_size_reader)(self.props))
                .max(width);

            if self.try_stretch_symbol(context,symbol, ruler, stretched_size, &stretch_dir) {
                return;
            }
        }

        self.set_single_glyph(context,GlyphIndex::Char(symbol));
    }

    fn bounding_box(&self) -> &BoundingBox {
        &self.bounding_box
    }
}

impl<'a, T: Element + 'a> Symbol<'a, T> {
    pub fn new(props: &'a T, symbol_reader: SymbolReader<T>, math_variant_reader: VariantReader<T>,
               symmetric_reader: SymmetricReader<T>,
               base_size_reader: SizeReader<T>, max_size_reader: SizeReader<T>,
               min_size_reader: SizeReader<T>, dir_reader: DirReader<T>,
               color_reader: ColorReader<T>) -> Symbol<'a, T> {
        Symbol {
            props,
            symbol_reader,
            math_variant_reader,
            symmetric_reader,
            base_size_reader,
            max_size_reader,
            min_size_reader,
            dir_reader,
            color_reader,
            bounding_box: BoundingBox::default(),
            layout: AbsoluteLayout::new(),
        }
    }

    fn set_single_glyph(&mut self, context: &Context, glyph: GlyphIndex) {
        let glyph = Glyph::new(
            self.props,
            glyph,
            self.base_size_reader,
            self.color_reader,
            self.dir_reader
        );

        self.layout.clear();
        self.layout.add_child(Box::new(glyph),
                              AbsoluteLayoutParams::new(Point::new(0., 0.)));

        self.layout.calculate(context, &MeasureMode::Wrap, &MeasureMode::Wrap);

        self.bounding_box = self.layout.bounding_box().clone();
    }

    fn try_stretch_symbol(&mut self, context: &Context, symbol: u32, ruler: &MathRuler,
                          stretched_size: f32, stretch_dir: &GlyphConstructionDirection) -> bool {
        let variants = ruler.glyph_variants(symbol, stretch_dir);

        for variant in variants {
            if variant.advance() >= stretched_size {
                self.set_single_glyph(context, GlyphIndex::Index(variant.glyph_index()));
                return true;
            }
        }

        let assembly = ruler.glyph_assembly(symbol, stretch_dir);
        if assembly.parts().len() >= 2 {
            self.set_glyph_assembly(context, assembly, &stretch_dir, stretched_size, ruler);
            return true;
        }

        return false;
    }

    fn set_glyph_assembly(&mut self, context: &Context, assembly: GlyphAssembly,
                          stretch_dir: &GlyphConstructionDirection,
                          stretched_size: f32, ruler: &MathRuler) {
        let min_connector_overlap = ruler.minimum_connector_overlap(stretch_dir);

        let mut num_extenders = 0;
        let mut total_extender_advance = 0f32;

        let mut num_non_extenders = 0;
        let mut total_non_extender_advance = 0f32;

        let mut max_connector_overlap = f32::INFINITY;

        for (index, part) in assembly.parts().iter().enumerate() {
            if part.is_extender() {
                num_extenders += 1;
                total_extender_advance += part.full_advance();
            } else {
                num_non_extenders += 1;
                total_non_extender_advance += part.full_advance();
            }

            if index != 0 {
                max_connector_overlap = max_connector_overlap.min(part.start_connector_length());
            }

            max_connector_overlap = max_connector_overlap.min(part.full_advance());

            if index != assembly.parts().len()-1 {
                max_connector_overlap = max_connector_overlap.min(part.end_connector_length());
            }
        }

        let num_extenders = num_extenders as f32;
        let num_non_extenders = num_non_extenders as f32;

        // Number of times extenders needs to be repeated to get size >= stretched size
        let extender_multiplier = ((stretched_size-total_non_extender_advance-
            min_connector_overlap*(1.-num_non_extenders)) /
            (total_extender_advance-(min_connector_overlap*num_extenders))).ceil();

        // Overlap adjustment to minimize size - stretched_size
        let required_overlap = (total_non_extender_advance + (extender_multiplier * total_extender_advance)
            - stretched_size) /
            (num_non_extenders + (extender_multiplier * num_extenders) - 1.);

        // Never use overlap greater than maximum connector overlap
        let overlap = required_overlap.min(max_connector_overlap);

        #[cfg(debug_assertions)]
        {
            assert!(max_connector_overlap >= min_connector_overlap);

            let size_with_overlap_correction = extender_multiplier*total_extender_advance
                +total_non_extender_advance -
                (overlap*(num_non_extenders+(extender_multiplier*num_extenders)-1.));

            let size_without_overlap_correction = extender_multiplier*total_extender_advance
                +total_non_extender_advance -
                (min_connector_overlap*(num_non_extenders+(extender_multiplier*num_extenders)-1.));

            assert!(size_without_overlap_correction >= stretched_size);

            assert!(size_with_overlap_correction <= size_without_overlap_correction);
        }

        self.layout.clear();

        let mut pen_pos = 0f32;

        // Ordering of parts in GlyphAssembly for vertical construction is from bottom to top
        // and for horizontal construction is from left to right
        match *stretch_dir {
            GlyphConstructionDirection::Vertical => {
                for part in assembly.parts().iter().rev() {
                    pen_pos = self.add_glyph_part_to_layout(part, stretch_dir, overlap,
                                                            extender_multiplier as u32, pen_pos);
                }
            },
            GlyphConstructionDirection::Horizontal => {
                for part in assembly.parts().iter() {
                    pen_pos = self.add_glyph_part_to_layout(part, stretch_dir, overlap,
                                                            extender_multiplier as u32, pen_pos);
                }
            }
        }

        self.layout.calculate(context, &MeasureMode::Wrap, &MeasureMode::Wrap);
        self.bounding_box = self.layout.bounding_box().clone();
    }

    fn add_glyph_part_to_layout(&mut self, part: &GlyphAssemblyPart, stretch_dir: &GlyphConstructionDirection,
                                overlap: f32, multiplier: u32, pen_pos: f32) -> f32 {
        let num_iters = if part.is_extender() {
            multiplier
        } else {
            1
        };

        let mut pen_pos = pen_pos;

        for _ in 0..num_iters {
            self.layout.add_child(
                Box::new(Glyph::new(
                    self.props,
                    GlyphIndex::Index(part.glyph_index()),
                    self.base_size_reader,
                    self.color_reader,
                    self.dir_reader)
                ),
                AbsoluteLayoutParams::new(match *stretch_dir {
                    GlyphConstructionDirection::Horizontal => Point::new(pen_pos, 0.),
                    GlyphConstructionDirection::Vertical => Point::new(0., pen_pos)
                })
            );

            pen_pos += part.full_advance()-overlap;
        }

        pen_pos
    }

    fn set_text(&mut self, context: &Context) {
        let text = Text::new(
            self.props,
            self.symbol_reader,
            self.base_size_reader,
            self.math_variant_reader,
            self.dir_reader,
            self.color_reader
        );

        self.layout.clear();
        self.layout.add_child(Box::new(text),
                              AbsoluteLayoutParams::new(Point::new(0., 0.)));

        self.layout.calculate(context, &MeasureMode::Wrap, &MeasureMode::Wrap);

        self.bounding_box = self.layout.bounding_box().clone();
    }

}<|endoftext|>


<commit_before>//#![deny(warnings)]

extern crate orbclient;
extern crate orbtk;

use orbclient::WindowFlag;
use orbtk::{Action, Button, Menu, Point, Rect, Separator, TextBox, Window};
use orbtk::dialogs::FileDialog;
use orbtk::traits::{Click, Enter, Place, Resize, Text};

use std::{cmp, env};
use std::cell::RefCell;
use std::fs::File;
use std::io::{Read, Write};
use std::ops::DerefMut;
use std::path::{Path, PathBuf};
use std::rc::Rc;
use std::sync::Arc;

pub struct Editor {
    path_option: Option<PathBuf>,
    text_box: Arc<TextBox>,
    window: *mut Window,
}

impl Editor {
    pub fn new(path_option: Option<PathBuf>, width: u32, height: u32) -> Box<Window> {
        // DESIGN {
        let mut window =  Box::new(Window::new_flags(Rect::new(-1, -1, width, height), "Editor", &[WindowFlag::Resizable]));

        let text_box = TextBox::new();
        text_box.position(0, 16)
            .size(width, height - 16);
        window.add(&text_box);

        let menu = Menu::new("File");
        menu.position(0, 0).size(32, 16);

        let open_action = Action::new("Open");
        menu.add(&open_action);

        let reload_action = Action::new("Reload");
        menu.add(&reload_action);

        menu.add(&Separator::new());

        let save_action = Action::new("Save");
        menu.add(&save_action);

        let save_as_action = Action::new("Save As");
        menu.add(&save_as_action);

        menu.add(&Separator::new());

        let close_action = Action::new("Close");
        menu.add(&close_action);

        window.add(&menu);
        // } DESIGN

        // CODE {
        let editor_cell = Rc::new(RefCell::new(Editor {
            path_option: path_option,
            text_box: text_box.clone(),
            window: window.deref_mut() as *mut Window,
        }));

        {
            let mut editor = editor_cell.borrow_mut();
            if editor.path_option.is_some() {
                editor.load();
            }
        }

        {
            let editor_cell = editor_cell.clone();
            open_action.on_click(move |_action: &Action, _point: Point| {
                println!("Open");

                let mut dialog = FileDialog::new();
                dialog.title = "Open File".to_string();
                if let Some(ref path) = editor_cell.borrow().path_option {
                    if let Ok(canon) = path.canonicalize() {
                        if let Some(parent) = canon.parent() {
                            dialog.path = parent.to_owned();
                        }
                    }
                }

                if let Some(path) = dialog.exec() {
                    println!("Open {}", path.display());
                    editor_cell.borrow_mut().open(&path);
                }
            });
        }

        {
            let editor_cell = editor_cell.clone();
            reload_action.on_click(move |_action: &Action, _point: Point| {
                println!("Reload");
                editor_cell.borrow_mut().load();
            });
        }

        {
            let editor_cell = editor_cell.clone();
            save_action.on_click(move |_action: &Action, _point: Point| {
                println!("Save");
                editor_cell.borrow_mut().save();
            });
        }

        {
            let editor_cell = editor_cell.clone();
            save_as_action.on_click(move |_action: &Action, _point: Point| {
                println!("Save As");

                let mut window = {
                    let editor_dialog = editor_cell.clone();
                    editor_cell.borrow_mut().path_dialog("Save As", move |path| {
                        println!("Save As {}", path);
                        editor_dialog.borrow_mut().save_as(&path);
                    })
                };

                window.exec();
            });
        }

        {
            let editor_cell = editor_cell.clone();
            close_action.on_click(move |_action: &Action, _point: Point| {
                println!("Close");
                editor_cell.borrow_mut().close();
            });
        }

        {
            let editor_cell = editor_cell.clone();
            window.on_resize(move |_, width, height| {
                editor_cell.borrow().text_box.size(width, height - 16);
            });
        }
        // } CODE

        window
    }

    fn path_dialog<F: Fn(&str) + 'static>(&mut self, title: &str, func: F) -> Box<Window> {
        let func_rc = Rc::new(func);

        // DESIGN {
        let (p_x, p_y, p_w, p_h) = {
            let window = unsafe { &mut *self.window };
            (window.x(), window.y(), window.width(), window.height())
        };

        let w = 320;
        let h = 8 + 28 + 8 + 28 + 8;
        let x = p_x + (p_w as i32 - w as i32)/2;
        let y = p_y + (p_h as i32 - h as i32)/2;

        let mut window = Box::new(Window::new(Rect::new(x, y, w, h), title));

        let text_box = TextBox::new();
        text_box.position(8, 8)
            .size(w - 16, 28)
            .text_offset(6, 6)
            .grab_focus(true);
        window.add(&text_box);

        let cancel_button = Button::new();
        cancel_button.position(8, 8 + 28 + 8)
            .size((w - 16)/2 - 4, 28)
            .text_offset(6, 6)
            .text("Cancel");
        window.add(&cancel_button);

        let confirm_button = Button::new();
        confirm_button.position((w as i32)/2 + 4, 8 + 28 + 8)
            .size((w - 16)/2 - 4, 28)
            .text_offset(6, 6)
            .text(title);
        window.add(&confirm_button);
        // } DESIGN

        // CODE {
        if let Some(ref path) = self.path_option {
            if let Some(path_str) = path.to_str() {
                text_box.text.set(path_str.to_string());
            }
        }

        {
            let func_rc = func_rc.clone();
            let window_ptr = window.deref_mut() as *mut Window;
            text_box.on_enter(move |text_box: &TextBox| {
                let path = text_box.text.get();
                func_rc(&path);
                unsafe { (&mut *window_ptr).close(); }
            });
        }

        {
            let window_ptr = window.deref_mut() as *mut Window;
            cancel_button.on_click(move |_button: &Button, _point: Point| {
                unsafe { (&mut *window_ptr).close(); }
            });
        }

        {
            let func_rc = func_rc.clone();
            let window_ptr = window.deref_mut() as *mut Window;
            confirm_button.on_click(move |_button: &Button, _point: Point| {
                let path = text_box.text.get();
                func_rc(&path);
                unsafe { (&mut *window_ptr).close(); }
            });
        }
        // } CODE

        window
    }

    fn load(&mut self) {
        if let Some(ref path) = self.path_option {
            println!("Load {}", path.display());
            match File::open(path) {
                Ok(mut f) => {
                    let mut contents = String::new();
                    match f.read_to_string(&mut contents) {
                        Ok(_) => {
                            self.text_box.text.set(contents);
                        },
                        Err(e) => {
                            println!("Failed to read {}: {}", path.display(), e);
                        }
                    }
                },
                Err(e) => {
                    println!("Failed to open {}: {}", path.display(), e);
                }
            }
        } else {
            println!("Path not set");
        }
    }

    fn save(&mut self) {
        if let Some(ref path) = self.path_option {
            println!("Save {}", path.display());
            match File::create(path) {
                Ok(mut file) => {
                    let text = self.text_box.text.borrow();
                    match file.write(&text.as_bytes()) {
                        Ok(_) => match file.set_len(text.len() as u64) {
                            Ok(_) => println!("Successfully saved {}", path.display()),
                            Err(err) => println!("Failed to truncate {}: {}", path.display(), err)
                        },
                        Err(err) => println!("Failed to write {}: {}", path.display(), err)
                    }
                },
                Err(err) => println!("Failed to open {}: {}", path.display(), err)
            }
        } else {
            println!("Path not set");
        }
    }

    fn set_path<P: AsRef<Path>>(&mut self, path: P) {
        self.path_option = Some(path.as_ref().to_owned());
        let window = unsafe { &mut *self.window };
        window.set_title(&format!("{} - Editor", path.as_ref().display()));
    }

    fn open<P: AsRef<Path>>(&mut self, path: P) {
        self.set_path(path);
        self.load();
    }

    fn save_as<P: AsRef<Path>>(&mut self, path: P) {
        self.set_path(path);
        self.save();
    }

    fn close(&mut self) {
        let window = unsafe { &mut *self.window };
        window.close();
    }
}

fn main(){
    let path_option = env::args().nth(1).map(PathBuf::from);

    let (display_width, display_height) = orbclient::get_display_size().expect("viewer: failed to get display size");
    let (width, height) = (cmp::min(1024, display_width * 4/5), cmp::min(768, display_height * 4/5));

    Editor::new(path_option, width, height).exec();
}
<commit_msg>Adjust editor menu<commit_after>//#![deny(warnings)]

extern crate orbclient;
extern crate orbtk;

use orbclient::WindowFlag;
use orbtk::{Action, Button, Menu, Point, Rect, Separator, TextBox, Window};
use orbtk::dialogs::FileDialog;
use orbtk::traits::{Click, Enter, Place, Resize, Text};

use std::{cmp, env};
use std::cell::RefCell;
use std::fs::File;
use std::io::{Read, Write};
use std::ops::DerefMut;
use std::path::{Path, PathBuf};
use std::rc::Rc;
use std::sync::Arc;

pub struct Editor {
    path_option: Option<PathBuf>,
    text_box: Arc<TextBox>,
    window: *mut Window,
}

impl Editor {
    pub fn new(path_option: Option<PathBuf>, width: u32, height: u32) -> Box<Window> {
        // DESIGN {
        let mut window =  Box::new(Window::new_flags(Rect::new(-1, -1, width, height), "Editor", &[WindowFlag::Resizable]));

        let text_box = TextBox::new();
        text_box.position(0, 16)
            .size(width, height - 16);
        window.add(&text_box);

        let menu = Menu::new("File");
        menu.position(4, 0).size(32, 16);

        let open_action = Action::new("Open");
        menu.add(&open_action);

        let reload_action = Action::new("Reload");
        menu.add(&reload_action);

        menu.add(&Separator::new());

        let save_action = Action::new("Save");
        menu.add(&save_action);

        let save_as_action = Action::new("Save As");
        menu.add(&save_as_action);

        menu.add(&Separator::new());

        let close_action = Action::new("Close");
        menu.add(&close_action);

        window.add(&menu);
        // } DESIGN

        // CODE {
        let editor_cell = Rc::new(RefCell::new(Editor {
            path_option: path_option,
            text_box: text_box.clone(),
            window: window.deref_mut() as *mut Window,
        }));

        {
            let mut editor = editor_cell.borrow_mut();
            if editor.path_option.is_some() {
                editor.load();
            }
        }

        {
            let editor_cell = editor_cell.clone();
            open_action.on_click(move |_action: &Action, _point: Point| {
                println!("Open");

                let mut dialog = FileDialog::new();
                dialog.title = "Open File".to_string();
                if let Some(ref path) = editor_cell.borrow().path_option {
                    if let Ok(canon) = path.canonicalize() {
                        if let Some(parent) = canon.parent() {
                            dialog.path = parent.to_owned();
                        }
                    }
                }

                if let Some(path) = dialog.exec() {
                    println!("Open {}", path.display());
                    editor_cell.borrow_mut().open(&path);
                }
            });
        }

        {
            let editor_cell = editor_cell.clone();
            reload_action.on_click(move |_action: &Action, _point: Point| {
                println!("Reload");
                editor_cell.borrow_mut().load();
            });
        }

        {
            let editor_cell = editor_cell.clone();
            save_action.on_click(move |_action: &Action, _point: Point| {
                println!("Save");
                editor_cell.borrow_mut().save();
            });
        }

        {
            let editor_cell = editor_cell.clone();
            save_as_action.on_click(move |_action: &Action, _point: Point| {
                println!("Save As");

                let mut window = {
                    let editor_dialog = editor_cell.clone();
                    editor_cell.borrow_mut().path_dialog("Save As", move |path| {
                        println!("Save As {}", path);
                        editor_dialog.borrow_mut().save_as(&path);
                    })
                };

                window.exec();
            });
        }

        {
            let editor_cell = editor_cell.clone();
            close_action.on_click(move |_action: &Action, _point: Point| {
                println!("Close");
                editor_cell.borrow_mut().close();
            });
        }

        {
            let editor_cell = editor_cell.clone();
            window.on_resize(move |_, width, height| {
                editor_cell.borrow().text_box.size(width, height - 16);
            });
        }
        // } CODE

        window
    }

    fn path_dialog<F: Fn(&str) + 'static>(&mut self, title: &str, func: F) -> Box<Window> {
        let func_rc = Rc::new(func);

        // DESIGN {
        let (p_x, p_y, p_w, p_h) = {
            let window = unsafe { &mut *self.window };
            (window.x(), window.y(), window.width(), window.height())
        };

        let w = 320;
        let h = 8 + 28 + 8 + 28 + 8;
        let x = p_x + (p_w as i32 - w as i32)/2;
        let y = p_y + (p_h as i32 - h as i32)/2;

        let mut window = Box::new(Window::new(Rect::new(x, y, w, h), title));

        let text_box = TextBox::new();
        text_box.position(8, 8)
            .size(w - 16, 28)
            .text_offset(6, 6)
            .grab_focus(true);
        window.add(&text_box);

        let cancel_button = Button::new();
        cancel_button.position(8, 8 + 28 + 8)
            .size((w - 16)/2 - 4, 28)
            .text_offset(6, 6)
            .text("Cancel");
        window.add(&cancel_button);

        let confirm_button = Button::new();
        confirm_button.position((w as i32)/2 + 4, 8 + 28 + 8)
            .size((w - 16)/2 - 4, 28)
            .text_offset(6, 6)
            .text(title);
        window.add(&confirm_button);
        // } DESIGN

        // CODE {
        if let Some(ref path) = self.path_option {
            if let Some(path_str) = path.to_str() {
                text_box.text.set(path_str.to_string());
            }
        }

        {
            let func_rc = func_rc.clone();
            let window_ptr = window.deref_mut() as *mut Window;
            text_box.on_enter(move |text_box: &TextBox| {
                let path = text_box.text.get();
                func_rc(&path);
                unsafe { (&mut *window_ptr).close(); }
            });
        }

        {
            let window_ptr = window.deref_mut() as *mut Window;
            cancel_button.on_click(move |_button: &Button, _point: Point| {
                unsafe { (&mut *window_ptr).close(); }
            });
        }

        {
            let func_rc = func_rc.clone();
            let window_ptr = window.deref_mut() as *mut Window;
            confirm_button.on_click(move |_button: &Button, _point: Point| {
                let path = text_box.text.get();
                func_rc(&path);
                unsafe { (&mut *window_ptr).close(); }
            });
        }
        // } CODE

        window
    }

    fn load(&mut self) {
        if let Some(ref path) = self.path_option {
            println!("Load {}", path.display());
            match File::open(path) {
                Ok(mut f) => {
                    let mut contents = String::new();
                    match f.read_to_string(&mut contents) {
                        Ok(_) => {
                            self.text_box.text.set(contents);
                        },
                        Err(e) => {
                            println!("Failed to read {}: {}", path.display(), e);
                        }
                    }
                },
                Err(e) => {
                    println!("Failed to open {}: {}", path.display(), e);
                }
            }
        } else {
            println!("Path not set");
        }
    }

    fn save(&mut self) {
        if let Some(ref path) = self.path_option {
            println!("Save {}", path.display());
            match File::create(path) {
                Ok(mut file) => {
                    let text = self.text_box.text.borrow();
                    match file.write(&text.as_bytes()) {
                        Ok(_) => match file.set_len(text.len() as u64) {
                            Ok(_) => println!("Successfully saved {}", path.display()),
                            Err(err) => println!("Failed to truncate {}: {}", path.display(), err)
                        },
                        Err(err) => println!("Failed to write {}: {}", path.display(), err)
                    }
                },
                Err(err) => println!("Failed to open {}: {}", path.display(), err)
            }
        } else {
            println!("Path not set");
        }
    }

    fn set_path<P: AsRef<Path>>(&mut self, path: P) {
        self.path_option = Some(path.as_ref().to_owned());
        let window = unsafe { &mut *self.window };
        window.set_title(&format!("{} - Editor", path.as_ref().display()));
    }

    fn open<P: AsRef<Path>>(&mut self, path: P) {
        self.set_path(path);
        self.load();
    }

    fn save_as<P: AsRef<Path>>(&mut self, path: P) {
        self.set_path(path);
        self.save();
    }

    fn close(&mut self) {
        let window = unsafe { &mut *self.window };
        window.close();
    }
}

fn main(){
    let path_option = env::args().nth(1).map(PathBuf::from);

    let (display_width, display_height) = orbclient::get_display_size().expect("viewer: failed to get display size");
    let (width, height) = (cmp::min(1024, display_width * 4/5), cmp::min(768, display_height * 4/5));

    Editor::new(path_option, width, height).exec();
}
<|endoftext|>


<commit_before>// Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

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

extern crate capnp;

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

pub mod codegen;
pub mod codegen_types;
mod pointer_constants;

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

#[derive(Copy, Clone)]
pub enum RustEdition {
    Rust2015,
    Rust2018,
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

#[test]
fn compiler_command_with_output_path_no_out_dir() {
    let error = CompilerCommand::new().output_path("foo").run().unwrap_err().description;
    assert!(error.starts_with("Error while trying to execute `capnp compile`"));
}
<commit_msg>update capnpc version in doc comment<commit_after>// Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

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

extern crate capnp;

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

pub mod codegen;
pub mod codegen_types;
mod pointer_constants;

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

#[derive(Copy, Clone)]
pub enum RustEdition {
    Rust2015,
    Rust2018,
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


<commit_before>
//! Framebuffers are used to draw to off-screen render targets

use gl;
use std;
use gl::types::*;
use texture::TextureFormat;

/// Utility to specify the format of a framebuffer before building it.
pub struct FramebufferProperties {
    pub width: u32,
    pub height: u32,
    pub internal_format: TextureFormat,
}

impl FramebufferProperties {
    pub fn new(width: u32, height: u32) -> FramebufferProperties {
        FramebufferProperties {
            width: width,
            height: height,
            internal_format: TextureFormat::RGB_8
        }
    }

    /// Creates a new framebuffer with these properties
    pub fn build(&self) -> Result<Framebuffer, String> {
        Framebuffer::new(&self)
    }
}

pub struct Framebuffer {
    framebuffer: GLuint,
    texture: GLuint,
    pub width: u32,
    pub height: u32,
}

impl Framebuffer {
    fn new(properties: &FramebufferProperties) -> Result<Framebuffer, String> {
        let mut framebuffer: GLuint = 0;
        let mut texture: GLuint = 0;

        let mut error: Option<String> = None;

        unsafe {
            gl::GenFramebuffers(1, &mut framebuffer);
            gl::BindFramebuffer(gl::FRAMEBUFFER, framebuffer);

            gl::GenTextures(1, &mut texture);
            gl::BindTexture(gl::TEXTURE_2D, texture);
            gl::TexImage2D(
                gl::TEXTURE_2D,
                0, // Level
                properties.internal_format as GLint,
                properties.width as GLint, properties.height as GLint, 0, //Size and border
                gl::RGBA, gl::UNSIGNED_BYTE, std::ptr::null() // Data for texture
            );

            gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::NEAREST as GLint);
            gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::NEAREST as GLint);
            gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::NEAREST as GLint);
            gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_S, gl::REPEAT as GLint);
            gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_T, gl::REPEAT as GLint);

            gl::FramebufferTexture(gl::FRAMEBUFFER, gl::COLOR_ATTACHMENT0, texture, 0);
            gl::DrawBuffers(1, &gl::COLOR_ATTACHMENT0);

            let status = gl::CheckFramebufferStatus(gl::FRAMEBUFFER);
            if status != gl::FRAMEBUFFER_COMPLETE {
                gl::DeleteFramebuffers(1, &framebuffer);
                gl::DeleteTextures(1, &texture);
                error = Some(format!("Framebuffer error: {}", get_status_message(status)));
            }

            gl::BindFramebuffer(gl::FRAMEBUFFER, 0);
        }

        if let Some(error) = error {
            return Err(error);
        } else {
            return Ok(
                Framebuffer {
                    framebuffer: framebuffer,
                    texture: texture,
                    width: properties.width,
                    height: properties.height,
                }
            );
        }
    }

    /// Binds this framebuffer. Subsequent draw operations will modify this framebuffer
    /// rather than the backbuffer. Note that you probably want to modify the viewport
    /// to fit this framebuffers size.
    pub fn bind(&self) {
        unsafe {
            gl::BindFramebuffer(gl::FRAMEBUFFER, self.framebuffer);
        }
    }

    /// Binds framebuffer 0, resulting in draw operations drawing to the backbuffer.
    pub fn unbind(&self) {
        unsafe {
            gl::BindFramebuffer(gl::FRAMEBUFFER, 0);
        }
    }

    /// Moves the contents of this framebuffer to the given framebuffer, resolving multisampling
    /// if present. Note that this also unbinds this framebuffer
    pub fn blit_to_framebuffer(&self, other: &Framebuffer) {
        self.blit_indexed(other.framebuffer, other.width, other.height);
    }

    /// Moves the contents of this framebuffer to the backbuffer, resolving multisampling
    /// if present. Note that this also unbinds this framebuffer. This will only partially
    /// cover the backbuffer if this framebuffer is smaller than the backbuffer. To upscale
    /// a framebuffer while blitting, use [`blit_with_size`](struct.Framebuffer.html#method.blit_with_size).
    pub fn blit(&self) {
        self.blit_indexed(0, self.width, self.height);
    }

    /// Moves the contents of this framebuffer to the backbuffer, resolving multisampling
    /// if present. Note that this also unbinds this framebuffer. This allows setting
    /// the size to which this framebuffer should be scaled while blitting. This should
    /// be used if the framebuffer is larger or smaller than the backbuffer.
    pub fn blit_with_size(&self, width: u32, height: u32) {
        self.blit_indexed(0, width, height);
    }

    fn blit_indexed(&self, target: GLuint, dst_width: u32, dst_height: u32) {
        unsafe {
            gl::BindFramebuffer(gl::DRAW_FRAMEBUFFER, target);
            gl::BindFramebuffer(gl::READ_FRAMEBUFFER, self.framebuffer);
            gl::BlitFramebuffer(
                0, 0, self.width as i32, self.height as i32,
                0, 0, dst_width as i32, dst_height as i32,
                gl::COLOR_BUFFER_BIT,
                gl::NEAREST
            );
        }
        self.unbind();
    }
}

impl Drop for Framebuffer {
    fn drop(&mut self) {
        unsafe {
            gl::DeleteFramebuffers(1, &mut self.framebuffer);
            gl::DeleteTextures(1, &mut self.texture);
        }
    }
}

fn get_status_message(message: GLenum) -> String {
    String::from(match message {
        gl::FRAMEBUFFER_UNDEFINED                     => "GL_FRAMEBUFFER_UNDEFINED",
        gl::FRAMEBUFFER_INCOMPLETE_ATTACHMENT         => "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT",
        gl::FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT => "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT",
        gl::FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER        => "GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER",
        gl::FRAMEBUFFER_INCOMPLETE_READ_BUFFER        => "GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER",
        gl::FRAMEBUFFER_UNSUPPORTED                   => "GL_FRAMEBUFFER_UNSUPPORTED",
        gl::FRAMEBUFFER_INCOMPLETE_MULTISAMPLE        => "GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE",
        gl::FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS      => "GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS",
        _ => return format!("Unkown error ({})", message)
    })
}

<commit_msg>Added support for depth buffer in framebuffer<commit_after>
//! Framebuffers are used to draw to off-screen render targets

use gl;
use std;
use gl::types::*;
use texture::TextureFormat;

/// Utility to specify the format of a framebuffer before building it.
pub struct FramebufferProperties {
    pub width: u32,
    pub height: u32,
    pub internal_format: TextureFormat,
    pub depth_buffer: bool,
}

impl FramebufferProperties {
    pub fn new(width: u32, height: u32) -> FramebufferProperties {
        FramebufferProperties {
            width: width,
            height: height,
            internal_format: TextureFormat::RGB_8,
            depth_buffer: false,
        }
    }

    /// Creates a new framebuffer with these properties
    pub fn build(&self) -> Result<Framebuffer, String> {
        Framebuffer::new(&self)
    }
}

pub struct Framebuffer {
    framebuffer: GLuint,
    texture: GLuint,
    depth_buffer: Option<GLuint>,
    pub width: u32,
    pub height: u32
}

impl Framebuffer {
    fn new(properties: &FramebufferProperties) -> Result<Framebuffer, String> {
        let mut framebuffer: GLuint = 0;
        let mut texture: GLuint = 0;
        let mut depth_buffer: Option<GLuint> = None;

        let mut error: Option<String> = None;

        unsafe {
            gl::GenFramebuffers(1, &mut framebuffer);
            gl::BindFramebuffer(gl::FRAMEBUFFER, framebuffer);

            gl::GenTextures(1, &mut texture);
            gl::BindTexture(gl::TEXTURE_2D, texture);
            gl::TexImage2D(
                gl::TEXTURE_2D,
                0, // Level
                properties.internal_format as GLint,
                properties.width as GLint, properties.height as GLint, 0, //Size and border
                gl::RGBA, gl::UNSIGNED_BYTE, std::ptr::null() // Data for texture
            );

            gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::NEAREST as GLint);
            gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::NEAREST as GLint);
            gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::NEAREST as GLint);
            gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_S, gl::REPEAT as GLint);
            gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_T, gl::REPEAT as GLint);

            gl::FramebufferTexture(gl::FRAMEBUFFER, gl::COLOR_ATTACHMENT0, texture, 0);
            gl::DrawBuffers(1, &gl::COLOR_ATTACHMENT0);

            if properties.depth_buffer {
                let mut depth_buffer_handle = 0;
                gl::GenRenderbuffers(1, &mut depth_buffer_handle);
                gl::BindRenderbuffer(gl::RENDERBUFFER, depth_buffer_handle);
                gl::RenderbufferStorage(gl::RENDERBUFFER, gl::DEPTH_COMPONENT16,
                                        properties.width as GLint, properties.height as GLint);
                gl::FramebufferRenderbuffer(gl::FRAMEBUFFER, gl::DEPTH_ATTACHMENT, gl::RENDERBUFFER, depth_buffer_handle);
                depth_buffer = Some(depth_buffer_handle);
            }

            let status = gl::CheckFramebufferStatus(gl::FRAMEBUFFER);
            if status != gl::FRAMEBUFFER_COMPLETE {
                gl::DeleteFramebuffers(1, &framebuffer);
                gl::DeleteTextures(1, &texture);
                if let Some(depth_buffer) = depth_buffer {
                    gl::DeleteRenderbuffers(1, &depth_buffer);
                }
                error = Some(format!("Framebuffer error: {}", get_status_message(status)));
            }

            gl::BindFramebuffer(gl::FRAMEBUFFER, 0);
        }

        if let Some(error) = error {
            return Err(error);
        } else {
            return Ok(
                Framebuffer {
                    framebuffer: framebuffer,
                    texture: texture,
                    depth_buffer: depth_buffer,
                    width: properties.width,
                    height: properties.height,
                }
            );
        }
    }

    /// Binds this framebuffer. Subsequent draw operations will modify this framebuffer
    /// rather than the backbuffer. Note that you probably want to modify the viewport
    /// to fit this framebuffers size.
    pub fn bind(&self) {
        unsafe {
            gl::BindFramebuffer(gl::FRAMEBUFFER, self.framebuffer);
        }
    }

    /// Binds framebuffer 0, resulting in draw operations drawing to the backbuffer.
    pub fn unbind(&self) {
        unsafe {
            gl::BindFramebuffer(gl::FRAMEBUFFER, 0);
        }
    }

    /// Moves the contents of this framebuffer to the given framebuffer, resolving multisampling
    /// if present. Note that this also unbinds this framebuffer
    pub fn blit_to_framebuffer(&self, other: &Framebuffer) {
        self.blit_indexed(other.framebuffer, other.width, other.height);
    }

    /// Moves the contents of this framebuffer to the backbuffer, resolving multisampling
    /// if present. Note that this also unbinds this framebuffer. This will only partially
    /// cover the backbuffer if this framebuffer is smaller than the backbuffer. To upscale
    /// a framebuffer while blitting, use [`blit_with_size`](struct.Framebuffer.html#method.blit_with_size).
    pub fn blit(&self) {
        self.blit_indexed(0, self.width, self.height);
    }

    /// Moves the contents of this framebuffer to the backbuffer, resolving multisampling
    /// if present. Note that this also unbinds this framebuffer. This allows setting
    /// the size to which this framebuffer should be scaled while blitting. This should
    /// be used if the framebuffer is larger or smaller than the backbuffer.
    pub fn blit_with_size(&self, width: u32, height: u32) {
        self.blit_indexed(0, width, height);
    }

    fn blit_indexed(&self, target: GLuint, dst_width: u32, dst_height: u32) {
        unsafe {
            gl::BindFramebuffer(gl::DRAW_FRAMEBUFFER, target);
            gl::BindFramebuffer(gl::READ_FRAMEBUFFER, self.framebuffer);
            gl::BlitFramebuffer(
                0, 0, self.width as i32, self.height as i32,
                0, 0, dst_width as i32, dst_height as i32,
                gl::COLOR_BUFFER_BIT,
                gl::NEAREST
            );
        }
        self.unbind();
    }
}

impl Drop for Framebuffer {
    fn drop(&mut self) {
        unsafe {
            gl::DeleteFramebuffers(1, &self.framebuffer);
            gl::DeleteTextures(1, &self.texture);
            if let Some(depth_buffer) = self.depth_buffer {
                gl::DeleteRenderbuffers(1, &depth_buffer);
            }
        }
    }
}

fn get_status_message(message: GLenum) -> String {
    String::from(match message {
        gl::FRAMEBUFFER_UNDEFINED                     => "GL_FRAMEBUFFER_UNDEFINED",
        gl::FRAMEBUFFER_INCOMPLETE_ATTACHMENT         => "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT",
        gl::FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT => "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT",
        gl::FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER        => "GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER",
        gl::FRAMEBUFFER_INCOMPLETE_READ_BUFFER        => "GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER",
        gl::FRAMEBUFFER_UNSUPPORTED                   => "GL_FRAMEBUFFER_UNSUPPORTED",
        gl::FRAMEBUFFER_INCOMPLETE_MULTISAMPLE        => "GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE",
        gl::FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS      => "GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS",
        _ => return format!("Unkown error ({})", message)
    })
}

<|endoftext|>


<commit_before>use std::ptr::null;
use std::str::raw;
use libc::{c_int, c_char};
use sync::mutex::{StaticMutex, MUTEX_INIT};

#[link(name="gdal")]
extern {
    fn OGRRegisterAll();
    fn OGROpen(pszName: *c_char, bUpdate: c_int, pahDriverList: *()) -> *();
    fn OGR_DS_GetLayerCount(hDS: *()) -> c_int;
    fn OGR_DS_Destroy(hDataSource: *());
    fn OGR_DS_GetLayer(hDS: *(), iLayer: c_int) -> *();
    fn OGR_L_GetNextFeature(hLayer: *()) -> *();
    fn OGR_F_GetFieldIndex(hFeat: *(), pszName: *c_char) -> c_int;
    fn OGR_F_GetFieldAsString(hFeat: *(), iField: c_int) -> *c_char;
    fn OGR_F_Destroy(hFeat: *());
}

static mut LOCK: StaticMutex = MUTEX_INIT;
static mut registered_drivers: bool = false;

fn register_drivers() {
    unsafe {
        let _g = LOCK.lock();
        if ! registered_drivers {
            OGRRegisterAll();
            registered_drivers = true;
        }
    }
}

pub struct VectorDataset {
    c_dataset: *(),
}

impl VectorDataset {
    pub fn get_layer_count(&self) -> int {
        return unsafe { OGR_DS_GetLayerCount(self.c_dataset) } as int;
    }

    pub fn get_layer<'a>(&'a self, idx: int) -> Option<Layer<'a>> {
        let c_layer = unsafe { OGR_DS_GetLayer(self.c_dataset, idx as c_int) };
        return match c_layer.is_null() {
            true  => None,
            false => Some(Layer{vector_dataset: self, c_layer: c_layer}),
        };
    }
}

impl Drop for VectorDataset {
    fn drop(&mut self) {
        unsafe { OGR_DS_Destroy(self.c_dataset); }
    }
}

pub struct Layer<'a> {
    vector_dataset: &'a VectorDataset,
    c_layer: *(),
}

impl<'a> Layer<'a> {
    pub fn features<'a>(&'a self) -> FeatureIterator<'a> {
        return FeatureIterator{layer: self};
    }
}

pub struct FeatureIterator<'a> {
    layer: &'a Layer<'a>,
}

impl<'a> Iterator<Feature<'a>> for FeatureIterator<'a> {
    #[inline]
    fn next(&mut self) -> Option<Feature<'a>> {
        let c_feature = unsafe { OGR_L_GetNextFeature(self.layer.c_layer) };
        return match c_feature.is_null() {
            true  => None,
            false => Some(Feature{layer: self.layer, c_feature: c_feature}),
        };
    }
}

pub struct Feature<'a> {
    layer: &'a Layer<'a>,
    c_feature: *(),
}

impl<'a> Feature<'a> {
    pub fn get_field(&self, name: String) -> String {
        return name.with_c_str(|c_name| {
            unsafe {
                let field_id = OGR_F_GetFieldIndex(self.c_feature, c_name);
                let c_value = OGR_F_GetFieldAsString(self.c_feature, field_id);
                return raw::from_c_str(c_value);
            }
        });
    }
}

#[unsafe_destructor]
impl<'a> Drop for Feature<'a> {
    fn drop(&mut self) {
        unsafe { OGR_F_Destroy(self.c_feature); }
    }
}

pub fn open(path: &Path) -> Option<VectorDataset> {
    register_drivers();
    let filename = path.as_str().unwrap();
    let c_dataset = filename.with_c_str(|c_filename| {
        return unsafe { OGROpen(c_filename, 0, null()) };
    });
    return match c_dataset.is_null() {
        true  => None,
        false => Some(VectorDataset{c_dataset: c_dataset}),
    };
}

#[cfg(test)]
mod test {
    use std::os::getenv;
    use std::path::Path;
    use super::{Feature, open};

    fn fixture_path(name: &str) -> Path {
        let envvar = "RUST_GDAL_TEST_FIXTURES";
        let fixtures = match getenv(envvar) {
            Some(p) => Path::new(p),
            None => fail!("Environment variable {} not set", envvar)
        };
        let rv = fixtures.join(name);
        return rv;
    }

    #[test]
    fn test_layer_count() {
        let ds = open(&fixture_path("roads.geojson")).unwrap();
        assert_eq!(ds.get_layer_count(), 1);
    }

    #[test]
    fn test_iterate_features() {
        let ds = open(&fixture_path("roads.geojson")).unwrap();
        let layer = ds.get_layer(0).unwrap();
        let features: Vec<Feature> = layer.features().collect();
        assert_eq!(features.len(), 21);
    }

    #[test]
    fn test_get_field() {
        let ds = open(&fixture_path("roads.geojson")).unwrap();
        let layer = ds.get_layer(0).unwrap();
        let feature = layer.features().next().unwrap();
        assert_eq!(feature.get_field("highway".to_string()),
                   "footway".to_string());
        assert_eq!(
            layer.features()
                 .count(|f|
                    f.get_field("highway".to_string()) ==
                    "residential".to_string()),
            2);
    }
}
<commit_msg>export geometry to wkt<commit_after>use std::ptr::null;
use std::str::raw;
use libc::{c_int, c_char};
use sync::mutex::{StaticMutex, MUTEX_INIT};

#[link(name="gdal")]
extern {
    fn OGRRegisterAll();
    fn OGROpen(pszName: *c_char, bUpdate: c_int, pahDriverList: *()) -> *();
    fn OGR_DS_GetLayerCount(hDS: *()) -> c_int;
    fn OGR_DS_Destroy(hDataSource: *());
    fn OGR_DS_GetLayer(hDS: *(), iLayer: c_int) -> *();
    fn OGR_L_GetNextFeature(hLayer: *()) -> *();
    fn OGR_F_GetFieldIndex(hFeat: *(), pszName: *c_char) -> c_int;
    fn OGR_F_GetFieldAsString(hFeat: *(), iField: c_int) -> *c_char;
    fn OGR_F_GetGeometryRef(hFeat: *()) -> *();
    fn OGR_F_Destroy(hFeat: *());
    fn OGR_G_ExportToWkt(hGeom: *(), ppszSrcText: **c_char) -> c_int;
    fn OGRFree(ptr: *());
}

static mut LOCK: StaticMutex = MUTEX_INIT;
static mut registered_drivers: bool = false;

fn register_drivers() {
    unsafe {
        let _g = LOCK.lock();
        if ! registered_drivers {
            OGRRegisterAll();
            registered_drivers = true;
        }
    }
}

pub struct VectorDataset {
    c_dataset: *(),
}

impl VectorDataset {
    pub fn get_layer_count(&self) -> int {
        return unsafe { OGR_DS_GetLayerCount(self.c_dataset) } as int;
    }

    pub fn get_layer<'a>(&'a self, idx: int) -> Option<Layer<'a>> {
        let c_layer = unsafe { OGR_DS_GetLayer(self.c_dataset, idx as c_int) };
        return match c_layer.is_null() {
            true  => None,
            false => Some(Layer{vector_dataset: self, c_layer: c_layer}),
        };
    }
}

impl Drop for VectorDataset {
    fn drop(&mut self) {
        unsafe { OGR_DS_Destroy(self.c_dataset); }
    }
}

pub struct Layer<'a> {
    vector_dataset: &'a VectorDataset,
    c_layer: *(),
}

impl<'a> Layer<'a> {
    pub fn features<'a>(&'a self) -> FeatureIterator<'a> {
        return FeatureIterator{layer: self};
    }
}

pub struct FeatureIterator<'a> {
    layer: &'a Layer<'a>,
}

impl<'a> Iterator<Feature<'a>> for FeatureIterator<'a> {
    #[inline]
    fn next(&mut self) -> Option<Feature<'a>> {
        let c_feature = unsafe { OGR_L_GetNextFeature(self.layer.c_layer) };
        return match c_feature.is_null() {
            true  => None,
            false => Some(Feature{layer: self.layer, c_feature: c_feature}),
        };
    }
}

pub struct Feature<'a> {
    layer: &'a Layer<'a>,
    c_feature: *(),
}

impl<'a> Feature<'a> {
    pub fn get_field(&self, name: String) -> String {
        return name.with_c_str(|c_name| {
            unsafe {
                let field_id = OGR_F_GetFieldIndex(self.c_feature, c_name);
                let c_value = OGR_F_GetFieldAsString(self.c_feature, field_id);
                return raw::from_c_str(c_value);
            }
        });
    }

    pub fn get_wkt(&self) -> String {
        unsafe {
            let c_geom = OGR_F_GetGeometryRef(self.c_feature);
            let c_wkt: *c_char = null();
            OGR_G_ExportToWkt(c_geom, &c_wkt);
            let wkt = raw::from_c_str(c_wkt);
            OGRFree(c_wkt as *());
            return wkt;
        }
    }
}

#[unsafe_destructor]
impl<'a> Drop for Feature<'a> {
    fn drop(&mut self) {
        unsafe { OGR_F_Destroy(self.c_feature); }
    }
}

pub fn open(path: &Path) -> Option<VectorDataset> {
    register_drivers();
    let filename = path.as_str().unwrap();
    let c_dataset = filename.with_c_str(|c_filename| {
        return unsafe { OGROpen(c_filename, 0, null()) };
    });
    return match c_dataset.is_null() {
        true  => None,
        false => Some(VectorDataset{c_dataset: c_dataset}),
    };
}

#[cfg(test)]
mod test {
    use std::os::getenv;
    use std::path::Path;
    use super::{Feature, open};

    fn fixture_path(name: &str) -> Path {
        let envvar = "RUST_GDAL_TEST_FIXTURES";
        let fixtures = match getenv(envvar) {
            Some(p) => Path::new(p),
            None => fail!("Environment variable {} not set", envvar)
        };
        let rv = fixtures.join(name);
        return rv;
    }

    #[test]
    fn test_layer_count() {
        let ds = open(&fixture_path("roads.geojson")).unwrap();
        assert_eq!(ds.get_layer_count(), 1);
    }

    #[test]
    fn test_iterate_features() {
        let ds = open(&fixture_path("roads.geojson")).unwrap();
        let layer = ds.get_layer(0).unwrap();
        let features: Vec<Feature> = layer.features().collect();
        assert_eq!(features.len(), 21);
    }

    #[test]
    fn test_get_field() {
        let ds = open(&fixture_path("roads.geojson")).unwrap();
        let layer = ds.get_layer(0).unwrap();
        let feature = layer.features().next().unwrap();
        assert_eq!(feature.get_field("highway".to_string()),
                   "footway".to_string());
        assert_eq!(
            layer.features()
                 .count(|f|
                    f.get_field("highway".to_string()) ==
                    "residential".to_string()),
            2);
    }

    #[test]
    fn test_get_wkt() {
        let ds = open(&fixture_path("roads.geojson")).unwrap();
        let layer = ds.get_layer(0).unwrap();
        let feature = layer.features().next().unwrap();
        let wkt = feature.get_wkt();
        assert_eq!(wkt, "LINESTRING (26.1019276 44.4302748,26.1019382 44.4303191,26.1020002 44.4304202)".to_string());
    }
}
<|endoftext|>