<commit_before>use std::io::Read;
use std::io::Write;
use std::path::Path;
use std::fs::File;

use tomson::Toml;
use handlebars::Handlebars;
use rustc_serialize::json::Json;

fn generate_code(mut toml: String) -> String {
    // turn the toml string into json for compatibility with handlebars
    let mut json = Toml::as_json(&mut toml).unwrap();

    for (id, field) in json.as_object_mut().unwrap().get_mut("field").unwrap().as_object_mut().unwrap().iter_mut() {
        let field_obj = field.as_object_mut().unwrap();
        let field_type = field_obj.get("type").unwrap().as_string().unwrap().to_string();
        let field_name = id.clone();
        let component_name = if let Some(component_json) = field_obj.get("component") {
            component_json.as_string().unwrap().to_string()
        } else {
            id.clone()
        };

        let component_has_type = if let Some(component_has_type_json) = field_obj.get("component_has_type") {
            component_has_type_json.as_boolean().unwrap()
        } else {
            false
        };

        field_obj.insert("struct_field_name".to_string(), Json::String(field_name.clone()));
        field_obj.insert("getter_name".to_string(), Json::String(id.clone()));
        field_obj.insert("component_name".to_string(), Json::String(component_name.clone()));
        field_obj.insert(field_type.clone(), Json::Boolean(true));

        if component_has_type {
            field_obj.insert("component_has_type".to_string(), Json::Boolean(true));
        }

        match field_type.as_ref() {
            "sum_f64" => {
                field_obj.insert("struct_field_type".to_string(), Json::String("f64".to_string()));
                field_obj.insert("struct_field_cons".to_string(), Json::String("0.0".to_string()));
                field_obj.insert("getter_type".to_string(), Json::String("f64".to_string()));
                field_obj.insert("getter_expr".to_string(), Json::String(format!("self.{}", field_name)));
                field_obj.insert("remove_entity".to_string(), Json::String(format!("if let Some(v) = entity.{}() {{ cell.{} -= v; }}",
                                                                                component_name, field_name)));
                field_obj.insert("insert_entity".to_string(), Json::String(format!("if let Some(v) = entity.{}() {{ cell.{} += v; }}",
                                                                                component_name, field_name)));
            }
            "count_bool" => {
                field_obj.insert("struct_field_type".to_string(), Json::String("usize".to_string()));
                field_obj.insert("struct_field_cons".to_string(), Json::String("0".to_string()));
                field_obj.insert("getter_type".to_string(), Json::String("bool".to_string()));
                field_obj.insert("getter_expr".to_string(), Json::String(format!("self.{} != 0", field_name)));
                field_obj.insert("remove_entity".to_string(), Json::String(format!("if entity.contains_{}() {{ cell.{} -= 1; }}",
                                                                                component_name, field_name)));
                field_obj.insert("insert_entity".to_string(), Json::String(format!("if entity.contains_{}() {{ cell.{} += 1; }}",
                                                                                component_name, field_name)));
            }
            "any" => {
                field_obj.insert("struct_field_type".to_string(), Json::String("AnySet<EntityId>".to_string()));
                field_obj.insert("struct_field_cons".to_string(), Json::String("AnySet::new()".to_string()));
                field_obj.insert("getter_type".to_string(), Json::String("bool".to_string()));
                field_obj.insert("getter_expr".to_string(), Json::String(format!("!self.{}.is_empty()", field_name)));
                field_obj.insert("any_name".to_string(), Json::String(format!("any_{}", field_name)));
                field_obj.insert("any_type".to_string(), Json::String("Option<EntityId>".to_string()));
                field_obj.insert("any_expr".to_string(), Json::String(format!("self.{}.any()", field_name)));
                field_obj.insert("remove_entity".to_string(), Json::String(format!("if entity.contains_{}() {{ cell.{}.remove(entity.id()); }}",
                                                                                component_name, field_name)));
                field_obj.insert("insert_entity".to_string(), Json::String(format!("if entity.contains_{}() {{ cell.{}.insert(entity.id()); }}",
                                                                                component_name, field_name)));
                field_obj.insert("is_set_type".to_string(), Json::Boolean(true));
            }
            "set" => {
                field_obj.insert("struct_field_type".to_string(), Json::String("EntitySet".to_string()));
                field_obj.insert("struct_field_cons".to_string(), Json::String("EntitySet::new()".to_string()));
                field_obj.insert("getter_type".to_string(), Json::String("bool".to_string()));
                field_obj.insert("getter_expr".to_string(), Json::String(format!("!self.{}.is_empty()", field_name)));
                field_obj.insert("iter_name".to_string(), Json::String(format!("{}_iter", field_name)));
                field_obj.insert("iter_type".to_string(), Json::String("EntitySetIter".to_string()));
                field_obj.insert("iter_expr".to_string(), Json::String(format!("self.{}.iter()", field_name)));
                field_obj.insert("remove_entity".to_string(), Json::String(format!("if entity.contains_{}() {{ cell.{}.remove(entity.id()); }}",
                                                                                component_name, field_name)));
                field_obj.insert("insert_entity".to_string(), Json::String(format!("if entity.contains_{}() {{ cell.{}.insert(entity.id()); }}",
                                                                                component_name, field_name)));
                field_obj.insert("is_set_type".to_string(), Json::Boolean(true));
            }
            other => panic!("unknown field type {}", other),
        }
    }

    let mut handlebars = Handlebars::new();

    // prevent xml escaping
    handlebars.register_escape_fn(|input| input.to_string());
    handlebars.template_render(TEMPLATE, &json).unwrap()

}

fn read_file_to_string<P: AsRef<Path>>(path: P) -> String {
    let mut file = File::open(path).unwrap();
    let mut string = String::new();
    file.read_to_string(&mut string).unwrap();

    string
}

pub fn generate_spatial_hash<P: AsRef<Path>, Q: AsRef<Path>>(in_path: P, out_path: Q) {
    let string = read_file_to_string(in_path);

    let output_string = generate_code(string);

    let mut outfile = File::create(out_path).unwrap();
    write!(outfile, "{}", output_string).unwrap();
}

const TEMPLATE: &'static str = r#"// Automatically generated. Do not edit.
#![allow(unused_imports)]
use ecs::*;
use coord::Coord;
use grid::{Grid, StaticGrid, DefaultGrid, IterGrid, CoordIterGrid};
use util::AnySet;

pub type SpatialHashCellIter<'a> = <StaticGrid<SpatialHashCell> as IterGrid<'a>>::Iter;
pub type SpatialHashCoordIter = <StaticGrid<SpatialHashCell> as CoordIterGrid>::CoordIter;

pub struct SpatialHashCell {

{{#each field}}
    {{ struct_field_name }}: {{ struct_field_type }},
{{/each}}

    // set of entities currently in this cell
    entities: EntitySet,

    // action on which this cell was last updated
    last_updated: u64,
}

impl SpatialHashCell {
    pub fn new() -> Self {
        SpatialHashCell {
{{#each field}}
            {{ struct_field_name }}: {{ struct_field_cons }},
{{/each}}

            entities: EntitySet::new(),
            last_updated: 0,
        }
    }

    pub fn last_updated(&self) -> u64 {
        self.last_updated
    }

    pub fn entity_ids(&self) -> &EntitySet {
        &self.entities
    }

    pub fn entity_id_iter(&self) -> EntitySetIter {
        self.entities.iter()
    }

{{#each field}}
    pub fn {{ getter_name }}(&self) -> {{ getter_type }} {
        {{ getter_expr }}
    }
    {{#if iter_name}}
    pub fn {{ iter_name }}(&self) -> {{ iter_type }} {
        {{ iter_expr }}
    }
    {{/if}}
    {{#if any_name}}
    pub fn {{ any_name }}(&self) -> {{ any_type }} {
        {{ any_expr }}
    }
    {{/if}}
{{/each}}
}

impl Default for SpatialHashCell {
    fn default() -> Self {
        Self::new()
    }
}

pub struct SpatialHashTable {
    grid: StaticGrid<SpatialHashCell>,
    empty: SpatialHashCell,
}

impl SpatialHashTable {
    pub fn new(width: usize, height: usize) -> Self {
        SpatialHashTable {
            grid: StaticGrid::new_default(width, height),
            empty: SpatialHashCell::new(),
        }
    }

    pub fn cell_iter(&self) -> SpatialHashCellIter {
        self.grid.iter()
    }

    pub fn coord_iter(&self) -> SpatialHashCoordIter {
        self.grid.coord_iter()
    }

    pub fn get(&self, coord: Coord) -> &SpatialHashCell {
        self.grid.get(coord).unwrap_or(&self.empty)
    }

    pub fn limits_min(&self) -> Coord {
        self.grid.limits_min()
    }

    pub fn limits_max(&self) -> Coord {
        self.grid.limits_max()
    }

    pub fn width(&self) -> usize {
        self.grid.width()
    }

    pub fn height(&self) -> usize {
        self.grid.height()
    }

    fn get_mut(&mut self, coord: Coord) -> &mut SpatialHashCell {
        unsafe { self.grid.get_unchecked_mut(coord) }
    }

    fn change_entity_position(&mut self, entity: EntityRef, current_position: Coord, new_position: Coord, action_id: u64) {
        self.remove_entity_position(entity, current_position, action_id);
        self.add_entity_position(entity, new_position, action_id);
    }

    fn remove_entity_position(&mut self, entity: EntityRef, position: Coord, action_id: u64) {
        let mut cell = self.get_mut(position);

{{#each field}}
        {{ remove_entity }}
{{/each}}

        cell.entities.remove(entity.id());
        cell.last_updated = action_id;
    }

    fn add_entity_position(&mut self, entity: EntityRef, position: Coord, action_id: u64) {
        let mut cell = self.get_mut(position);

{{#each field}}
        {{ insert_entity }}
{{/each}}

        cell.entities.insert(entity.id());
        cell.last_updated = action_id;
    }

    pub fn update(&mut self, ecs: &EcsCtx, action: &EcsAction, action_id: u64) {

        for (entity_id, new_position) in action.position_positive_iter(ecs) {
            let entity = ecs.entity(entity_id);
            // Add and remove tracked components based on the current data stored about the
            // entity, ignoring any component changes in the current action. These will be
            // applied later.
            if let Some(current_position) = entity.position() {
                // the entity is changing position
                self.change_entity_position(entity, current_position, *new_position, action_id);
            } else {
                // the entity is gaining a position
                self.add_entity_position(entity, *new_position, action_id);
            }
        }

        for entity_id in action.position_negative_iter(ecs) {
            let entity = ecs.entity(entity_id);
            if let Some(position) = entity.position() {
                self.remove_entity_position(entity, position, action_id);
            }
        }

{{#each field}}
        self.update_{{ struct_field_name }}(ecs, action, action_id);
{{/each}}
    }

{{#each field}}
    fn update_{{ struct_field_name }}(&mut self, ecs: &EcsCtx, action: &EcsAction, action_id: u64) {
    {{#if count_bool}}
        for entity_id in action.{{ component_name }}_positive_iter(ecs) {
            let entity = ecs.post_action_entity(entity_id, action);
            if let Some(position) = entity.position() {
                if !entity.current_contains_{{ component_name }}() {
                    let cell = self.get_mut(position);
                    cell.{{ struct_field_name }} += 1;
                    cell.last_updated = action_id;
                }
            }
        }

        for entity_id in action.{{ component_name }}_negative_iter(ecs) {
            let entity = ecs.entity(entity_id);
            if let Some(position) = entity.position() {
                if entity.contains_{{ component_name }}() {
                    let cell = self.get_mut(position);
                    cell.{{ struct_field_name }} -= 1;
                    cell.last_updated = action_id;
                }
            }
        }
    {{/if}}
    {{#if sum_f64}}
        for (entity_id, new) in action.{{ component_name }}_positive_iter(ecs) {
            let entity = ecs.post_action_entity(entity_id, action);
            if let Some(position) = entity.position() {
                let current = entity.current_{{ component_name }}().unwrap_or(0.0);
                let increase = new - current;
                let cell = self.get_mut(position);
                cell.{{ struct_field_name }} += increase;
                cell.last_updated = action_id;
            }
        }
        for entity_id in action.{{ component_name }}_negative_iter(ecs) {
            let entity = ecs.entity(entity_id);
            if let Some(position) = entity.position() {
                if let Some(value) = entity.{{ component_name }}() {
                    let cell = self.get_mut(position);
                    cell.{{ struct_field_name }} -= value;
                    cell.last_updated = action_id;
                }
            }
        }
    {{/if}}
    {{#if is_set_type}}
        {{#if component_has_type}}
        for (entity_id, _) in action.{{ component_name }}_positive_iter(ecs) {
        {{else}}
        for entity_id in action.{{ component_name }}_positive_iter(ecs) {
        {{/if}}
            let entity = ecs.post_action_entity(entity_id, action);
            if let Some(position) = entity.position() {
        {{#if component_has_type}}
                if entity.current_{{ component_name }}().is_none() {
        {{else}}
                if !entity.current_contains_{{ component_name }}() {
        {{/if}}
                    let cell = self.get_mut(position);
                    cell.{{ struct_field_name }}.insert(entity_id);
                    cell.last_updated = action_id;
                }
            }
        }
        for entity_id in action.{{ component_name }}_negative_iter(ecs) {
            let entity = ecs.entity(entity_id);
            if let Some(position) = entity.position() {
                if entity.contains_{{ component_name }}() {
                    let cell = self.get_mut(position);
                    cell.{{ struct_field_name }}.remove(entity_id);
                    cell.last_updated = action_id;
                }
            }
        }
    {{/if}}
    }
{{/each}}
}
"#;

<commit_msg>Fix bug in removing entities from spatial hash<commit_after>use std::io::Read;
use std::io::Write;
use std::path::Path;
use std::fs::File;

use tomson::Toml;
use handlebars::Handlebars;
use rustc_serialize::json::Json;

fn generate_code(mut toml: String) -> String {
    // turn the toml string into json for compatibility with handlebars
    let mut json = Toml::as_json(&mut toml).unwrap();

    for (id, field) in json.as_object_mut().unwrap().get_mut("field").unwrap().as_object_mut().unwrap().iter_mut() {
        let field_obj = field.as_object_mut().unwrap();
        let field_type = field_obj.get("type").unwrap().as_string().unwrap().to_string();
        let field_name = id.clone();
        let component_name = if let Some(component_json) = field_obj.get("component") {
            component_json.as_string().unwrap().to_string()
        } else {
            id.clone()
        };

        let component_has_type = if let Some(component_has_type_json) = field_obj.get("component_has_type") {
            component_has_type_json.as_boolean().unwrap()
        } else {
            false
        };

        field_obj.insert("struct_field_name".to_string(), Json::String(field_name.clone()));
        field_obj.insert("getter_name".to_string(), Json::String(id.clone()));
        field_obj.insert("component_name".to_string(), Json::String(component_name.clone()));
        field_obj.insert(field_type.clone(), Json::Boolean(true));

        if component_has_type {
            field_obj.insert("component_has_type".to_string(), Json::Boolean(true));
        }

        match field_type.as_ref() {
            "sum_f64" => {
                field_obj.insert("struct_field_type".to_string(), Json::String("f64".to_string()));
                field_obj.insert("struct_field_cons".to_string(), Json::String("0.0".to_string()));
                field_obj.insert("getter_type".to_string(), Json::String("f64".to_string()));
                field_obj.insert("getter_expr".to_string(), Json::String(format!("self.{}", field_name)));
                field_obj.insert("remove_entity".to_string(), Json::String(format!("if let Some(v) = entity.{}() {{ cell.{} -= v; }}",
                                                                                component_name, field_name)));
                field_obj.insert("insert_entity".to_string(), Json::String(format!("if let Some(v) = entity.{}() {{ cell.{} += v; }}",
                                                                                component_name, field_name)));
            }
            "count_bool" => {
                field_obj.insert("struct_field_type".to_string(), Json::String("usize".to_string()));
                field_obj.insert("struct_field_cons".to_string(), Json::String("0".to_string()));
                field_obj.insert("getter_type".to_string(), Json::String("bool".to_string()));
                field_obj.insert("getter_expr".to_string(), Json::String(format!("self.{} != 0", field_name)));
                field_obj.insert("remove_entity".to_string(), Json::String(format!("if entity.contains_{}() {{ cell.{} -= 1; }}",
                                                                                component_name, field_name)));
                field_obj.insert("insert_entity".to_string(), Json::String(format!("if entity.contains_{}() {{ cell.{} += 1; }}",
                                                                                component_name, field_name)));
            }
            "any" => {
                field_obj.insert("struct_field_type".to_string(), Json::String("AnySet<EntityId>".to_string()));
                field_obj.insert("struct_field_cons".to_string(), Json::String("AnySet::new()".to_string()));
                field_obj.insert("getter_type".to_string(), Json::String("bool".to_string()));
                field_obj.insert("getter_expr".to_string(), Json::String(format!("!self.{}.is_empty()", field_name)));
                field_obj.insert("any_name".to_string(), Json::String(format!("any_{}", field_name)));
                field_obj.insert("any_type".to_string(), Json::String("Option<EntityId>".to_string()));
                field_obj.insert("any_expr".to_string(), Json::String(format!("self.{}.any()", field_name)));
                field_obj.insert("remove_entity".to_string(), Json::String(format!("if entity.contains_{}() {{ cell.{}.remove(entity.id()); }}",
                                                                                component_name, field_name)));
                field_obj.insert("insert_entity".to_string(), Json::String(format!("if entity.contains_{}() {{ cell.{}.insert(entity.id()); }}",
                                                                                component_name, field_name)));
                field_obj.insert("is_set_type".to_string(), Json::Boolean(true));
            }
            "set" => {
                field_obj.insert("struct_field_type".to_string(), Json::String("EntitySet".to_string()));
                field_obj.insert("struct_field_cons".to_string(), Json::String("EntitySet::new()".to_string()));
                field_obj.insert("getter_type".to_string(), Json::String("bool".to_string()));
                field_obj.insert("getter_expr".to_string(), Json::String(format!("!self.{}.is_empty()", field_name)));
                field_obj.insert("iter_name".to_string(), Json::String(format!("{}_iter", field_name)));
                field_obj.insert("iter_type".to_string(), Json::String("EntitySetIter".to_string()));
                field_obj.insert("iter_expr".to_string(), Json::String(format!("self.{}.iter()", field_name)));
                field_obj.insert("remove_entity".to_string(), Json::String(format!("if entity.contains_{}() {{ cell.{}.remove(entity.id()); }}",
                                                                                component_name, field_name)));
                field_obj.insert("insert_entity".to_string(), Json::String(format!("if entity.contains_{}() {{ cell.{}.insert(entity.id()); }}",
                                                                                component_name, field_name)));
                field_obj.insert("is_set_type".to_string(), Json::Boolean(true));
            }
            other => panic!("unknown field type {}", other),
        }
    }

    let mut handlebars = Handlebars::new();

    // prevent xml escaping
    handlebars.register_escape_fn(|input| input.to_string());
    handlebars.template_render(TEMPLATE, &json).unwrap()

}

fn read_file_to_string<P: AsRef<Path>>(path: P) -> String {
    let mut file = File::open(path).unwrap();
    let mut string = String::new();
    file.read_to_string(&mut string).unwrap();

    string
}

pub fn generate_spatial_hash<P: AsRef<Path>, Q: AsRef<Path>>(in_path: P, out_path: Q) {
    let string = read_file_to_string(in_path);

    let output_string = generate_code(string);

    let mut outfile = File::create(out_path).unwrap();
    write!(outfile, "{}", output_string).unwrap();
}

const TEMPLATE: &'static str = r#"// Automatically generated. Do not edit.
#![allow(unused_imports)]
use ecs::*;
use coord::Coord;
use grid::{Grid, StaticGrid, DefaultGrid, IterGrid, CoordIterGrid};
use util::AnySet;

pub type SpatialHashCellIter<'a> = <StaticGrid<SpatialHashCell> as IterGrid<'a>>::Iter;
pub type SpatialHashCoordIter = <StaticGrid<SpatialHashCell> as CoordIterGrid>::CoordIter;

pub struct SpatialHashCell {

{{#each field}}
    {{ struct_field_name }}: {{ struct_field_type }},
{{/each}}

    // set of entities currently in this cell
    entities: EntitySet,

    // action on which this cell was last updated
    last_updated: u64,
}

impl SpatialHashCell {
    pub fn new() -> Self {
        SpatialHashCell {
{{#each field}}
            {{ struct_field_name }}: {{ struct_field_cons }},
{{/each}}

            entities: EntitySet::new(),
            last_updated: 0,
        }
    }

    pub fn last_updated(&self) -> u64 {
        self.last_updated
    }

    pub fn entity_ids(&self) -> &EntitySet {
        &self.entities
    }

    pub fn entity_id_iter(&self) -> EntitySetIter {
        self.entities.iter()
    }

{{#each field}}
    pub fn {{ getter_name }}(&self) -> {{ getter_type }} {
        {{ getter_expr }}
    }
    {{#if iter_name}}
    pub fn {{ iter_name }}(&self) -> {{ iter_type }} {
        {{ iter_expr }}
    }
    {{/if}}
    {{#if any_name}}
    pub fn {{ any_name }}(&self) -> {{ any_type }} {
        {{ any_expr }}
    }
    {{/if}}
{{/each}}
}

impl Default for SpatialHashCell {
    fn default() -> Self {
        Self::new()
    }
}

pub struct SpatialHashTable {
    grid: StaticGrid<SpatialHashCell>,
    empty: SpatialHashCell,
}

impl SpatialHashTable {
    pub fn new(width: usize, height: usize) -> Self {
        SpatialHashTable {
            grid: StaticGrid::new_default(width, height),
            empty: SpatialHashCell::new(),
        }
    }

    pub fn cell_iter(&self) -> SpatialHashCellIter {
        self.grid.iter()
    }

    pub fn coord_iter(&self) -> SpatialHashCoordIter {
        self.grid.coord_iter()
    }

    pub fn get(&self, coord: Coord) -> &SpatialHashCell {
        self.grid.get(coord).unwrap_or(&self.empty)
    }

    pub fn limits_min(&self) -> Coord {
        self.grid.limits_min()
    }

    pub fn limits_max(&self) -> Coord {
        self.grid.limits_max()
    }

    pub fn width(&self) -> usize {
        self.grid.width()
    }

    pub fn height(&self) -> usize {
        self.grid.height()
    }

    fn get_mut(&mut self, coord: Coord) -> &mut SpatialHashCell {
        unsafe { self.grid.get_unchecked_mut(coord) }
    }

    fn change_entity_position(&mut self, entity: EntityRef, current_position: Coord, new_position: Coord, action_id: u64) {
        self.remove_entity_position(entity, current_position, action_id);
        self.add_entity_position(entity, new_position, action_id);
    }

    fn remove_entity_position(&mut self, entity: EntityRef, position: Coord, action_id: u64) {
        let mut cell = self.get_mut(position);

{{#each field}}
        {{ remove_entity }}
{{/each}}

        cell.entities.remove(entity.id());
        cell.last_updated = action_id;
    }

    fn add_entity_position(&mut self, entity: EntityRef, position: Coord, action_id: u64) {
        let mut cell = self.get_mut(position);

{{#each field}}
        {{ insert_entity }}
{{/each}}

        cell.entities.insert(entity.id());
        cell.last_updated = action_id;
    }

    pub fn update(&mut self, ecs: &EcsCtx, action: &EcsAction, action_id: u64) {

        for (entity_id, new_position) in action.position_positive_iter(ecs) {
            let entity = ecs.entity(entity_id);
            // Add and remove tracked components based on the current data stored about the
            // entity, ignoring any component changes in the current action. These will be
            // applied later.
            if let Some(current_position) = entity.position() {
                // the entity is changing position
                self.change_entity_position(entity, current_position, *new_position, action_id);
            } else {
                // the entity is gaining a position
                self.add_entity_position(entity, *new_position, action_id);
            }
        }

        for entity_id in action.position_negative_iter(ecs) {
            let entity = ecs.entity(entity_id);
            if let Some(position) = entity.position() {
                self.remove_entity_position(entity, position, action_id);
            }
        }

{{#each field}}
        self.update_{{ struct_field_name }}(ecs, action, action_id);
{{/each}}
    }

{{#each field}}
    fn update_{{ struct_field_name }}(&mut self, ecs: &EcsCtx, action: &EcsAction, action_id: u64) {
    {{#if count_bool}}
        for entity_id in action.{{ component_name }}_positive_iter(ecs) {
            let entity = ecs.post_action_entity(entity_id, action);
            if let Some(position) = entity.position() {
                if !entity.current_contains_{{ component_name }}() {
                    let cell = self.get_mut(position);
                    cell.{{ struct_field_name }} += 1;
                    cell.last_updated = action_id;
                }
            }
        }

        for entity_id in action.{{ component_name }}_negative_iter(ecs) {
            let entity = ecs.post_action_entity(entity_id, action);
            if let Some(position) = entity.position() {
                if entity.contains_{{ component_name }}() {
                    let cell = self.get_mut(position);
                    cell.{{ struct_field_name }} -= 1;
                    cell.last_updated = action_id;
                }
            }
        }
    {{/if}}
    {{#if sum_f64}}
        for (entity_id, new) in action.{{ component_name }}_positive_iter(ecs) {
            let entity = ecs.post_action_entity(entity_id, action);
            if let Some(position) = entity.position() {
                let current = entity.current_{{ component_name }}().unwrap_or(0.0);
                let increase = new - current;
                let cell = self.get_mut(position);
                cell.{{ struct_field_name }} += increase;
                cell.last_updated = action_id;
            }
        }
        for entity_id in action.{{ component_name }}_negative_iter(ecs) {
            let entity = ecs.post_action_entity(entity_id, action);
            if let Some(position) = entity.position() {
                if let Some(value) = entity.{{ component_name }}() {
                    let cell = self.get_mut(position);
                    cell.{{ struct_field_name }} -= value;
                    cell.last_updated = action_id;
                }
            }
        }
    {{/if}}
    {{#if is_set_type}}
        {{#if component_has_type}}
        for (entity_id, _) in action.{{ component_name }}_positive_iter(ecs) {
        {{else}}
        for entity_id in action.{{ component_name }}_positive_iter(ecs) {
        {{/if}}
            let entity = ecs.post_action_entity(entity_id, action);
            if let Some(position) = entity.position() {
        {{#if component_has_type}}
                if entity.current_{{ component_name }}().is_none() {
        {{else}}
                if !entity.current_contains_{{ component_name }}() {
        {{/if}}
                    let cell = self.get_mut(position);
                    cell.{{ struct_field_name }}.insert(entity_id);
                    cell.last_updated = action_id;
                }
            }
        }
        for entity_id in action.{{ component_name }}_negative_iter(ecs) {
            let entity = ecs.post_action_entity(entity_id, action);
            if let Some(position) = entity.position() {
        {{#if component_has_type}}
                if entity.current_{{ component_name }}().is_some() {
        {{else}}
                if entity.current_contains_{{ component_name }}() {
        {{/if}}
                    let cell = self.get_mut(position);
                    cell.{{ struct_field_name }}.remove(entity_id);
                    cell.last_updated = action_id;
                }
            }
        }
    {{/if}}
    }
{{/each}}
}
"#;

<|endoftext|>


<commit_before>//! This crate helps to create a Fractalide agent
//!
//! It provides the macro `agent` which takes the high level view of the agent, and creates the code for the scheduler.
//!
//! It also declare the Trait Agent and the shared methods needed by every agent and the scheduler.

extern crate capnp;
use ports::Ports;
use result::Result;

/// Provide the generic functions of agents
///
/// These three functions are used by the scheduler
pub trait Agent {
    /// Return true if there is at least one input port
    fn is_input_ports(&self) -> bool;
    /// Connect output port
    fn connect(&mut self, port: &str, sender: IPSender) -> Result<()>;
    /// Connect array output port
    fn connect_array(&mut self, port: &str, element: String, sender: IPSender) -> Result<()>;
    /// Add input element
    fn add_inarr_element(&mut self, port: &str, element: String, recv: IPReceiver) -> Result<()>;
    /// Run the method of the component, his personal logic
    fn run(&mut self) -> Result<Signal>;
}

/// The agent macro.
///
/// It helps to define a agent, by defining the input and output ports, if there is an option or an acc port, ...
///
/// `edges()` and `portal()` are optional.
///
/// Example :
///
/// ```rust,ignore
/// agent! {
///    display, edges(generic_text)
///    inputs(input: any),
///    inputs_array(),
///    outputs(output: any),
///    outputs_array(),
///    option(generic_text),
///    acc(), portal()
///    fn run(&mut self) -> Result<()> {
///        // Receive an IP
///        let ip = try!(self.ports.recv("input"));
///
///        // Received an IP from the option port (a generic_text)
///        let opt = self.recv_opt();
///
///        // Get the capn'p reader
///        let reader: generic_text::Reader = try!(opt.read_schema());
///        // Print the option
///        println!("{}", try!(reader.get_text()));
///
///        // Send the received IP outside, but don't care about the success (drop on fail)
///        let _ = self.ports.send("output", ip);
///
///        Ok(())
///    }
/// }
/// ```
#[macro_export]
macro_rules! agent {
    (
       $name:ident, $( edges( $( $edge:ident ),* ) )*
        inputs($( $input_field_name:ident: $input_edge_name:ident),* ),
        inputs_array($( $input_array_name:ident: $input_edge_array:ident),* ),
        outputs($( $output_field_name:ident: $output_edge_name:ident),* ),
        outputs_array($($output_array_name:ident: $output_edge_array:ident),* ),
        option($($option_edge: ident),*),
        acc($($acc_edge: ident),*), $( portal($portal_type:ty => $portal_value:expr))*
        fn run(&mut $arg:ident) -> Result<()> $fun:block
    )
        =>
    {
        use rustfbp::agent::Agent;

        use rustfbp::result;
        use rustfbp::result::Result;
        use rustfbp::ports::IPSender;
        use rustfbp::scheduler::CompMsg;
        use std::error::Error;

        use std::sync::mpsc::Sender;

        use rustfbp::ports::{IP, Ports};

        #[allow(unused_imports)]
        use std::collections::HashMap;

        use capnp::serialize;
        use capnp::message;

        use std::io::{Read, Write};

        mod edge_capnp {
                include!("edge_capnp.rs");
        }

        $( $(
            use edge_capnp::$edge;
        )* )*

        impl $name {
            pub fn recv_option(&mut self) -> IP {
                self.try_recv_option();
                if self.option_ip.is_none() {
                    self.option_ip = self.ports.recv("option").ok();
                }
                match self.option_ip {
                    Some(ref ip) => ip.clone(),
                    None => unreachable!(),
                }
            }

            pub fn try_recv_option(&mut self) -> Option<IP> {
                loop {
                    match self.ports.try_recv("option") {
                        Err(_) => { break; },
                        Ok(ip) => { self.option_ip = Some(ip); }
                    };
                }
                self.option_ip.as_ref().map(|ip|{ ip.clone() })
            }
        }

        // simple and array
        impl Agent for $name {

            fn get_ports(&mut self) -> &mut Ports {
                &mut self.ports
            }

            fn is_input_ports(&self) -> bool {
                $(
                    if true || stringify!($input_field_name) == "" { return true; }
                )*
                $(
                    if true || stringify!($input_array_name) == "" { return true; }
                )*
                false
            }

            fn run(&mut $arg) -> Result<()> $fun

        }

        /* Global agent */

        #[allow(dead_code)]
        #[allow(non_camel_case_types)]
        pub struct $name {
            name: String,
            pub ports: Ports,
            pub option_ip: Option<IP>,
            $(
            pub portal: $portal_type ,
            )*
        }

        #[allow(dead_code)]
        pub fn new(name: String, sched: Sender<CompMsg>) -> Result<(Box<Agent + Send>, HashMap<String, IPSender>)> {
            let (ports, senders) = try!(Ports::new(name.clone(), sched,
                                   vec!["option".into(), "acc".into(), $( stringify!($input_field_name).to_string() ),*],
                                   vec![$( stringify!($input_array_name).to_string() ),*],
                                   vec!["acc".into(), $( stringify!($output_field_name).to_string() ),*],
                                   vec![$( stringify!($output_array_name).to_string() ),*],));

            // Put it together
            let comp = $name{
                name: name,
                ports: ports,
                option_ip: None,
                $(
                    portal: $portal_value,
                )*
            };
            Ok((Box::new(comp) as Box<Agent + Send>, senders))
        }

        #[no_mangle]
        pub extern fn create_agent(name: String, sched: Sender<CompMsg>) -> Result<(Box<Agent + Send>, HashMap<String, IPSender>)> {
            new(name, sched)
        }

        #[no_mangle]
        pub extern fn get_schema_input(port: &str) -> Result<String> {
            match port {
                $(
                    stringify!($input_field_name)=> Ok(stringify!($input_edge_name).into()),
                )*
                _ => { Err(result::Error::PortNotFound("unknown".into(), port.into())) }
            }
        }

        #[no_mangle]
        pub extern fn get_schema_input_array(port: &str) -> Result<String> {
            match port {
                $(
                    stringify!($input_array_name) => Ok(stringify!($input_edge_array).into()),
                )*
                _ => { Err(result::Error::PortNotFound("unknown".into(), port.into())) }
            }
        }

        #[no_mangle]
        pub extern fn get_schema_output(port: &str) -> Result<String> {
            match port {
                $(
                    stringify!($output_field_name)=> Ok(stringify!($output_edge_name).into()),
                )*
                _ => { Err(result::Error::PortNotFound("unknown".into(), port.into())) }
            }
        }

        #[no_mangle]
        pub extern fn get_schema_output_array(port: &str) -> Result<String> {
            match port {
                $(
                    stringify!($output_array_name) => Ok(stringify!($output_edge_array).into()),
                )*
                _ => { Err(result::Error::PortNotFound("unknown".into(), port.into())) }
            }
        }
    }
}
<commit_msg>Agent: update comment<commit_after>//! This crate helps to create a Fractalide component
//!
//! It provides the macro `component` which takes the high level view of the component, and creates the code for the scheduler.
//!
//! It also declare the Trait Agent and the shared methods needed by every component and the scheduler.

extern crate capnp;
use ports::Ports;
use result::Result;

/// Provide the generic functions of agents
///
/// These three functions are used by the scheduler
pub trait Agent {
    /// Return true if there is at least one input port
    fn is_input_ports(&self) -> bool;
    /// Connect output port
    fn connect(&mut self, port: &str, sender: IPSender) -> Result<()>;
    /// Connect array output port
    fn connect_array(&mut self, port: &str, element: String, sender: IPSender) -> Result<()>;
    /// Add input element
    fn add_inarr_element(&mut self, port: &str, element: String, recv: IPReceiver) -> Result<()>;
    /// Run the method of the component, his personal logic
    fn run(&mut self) -> Result<Signal>;
}

/// The agent macro.
///
/// It helps to define a agent, by defining the input and output ports, if there is an option or an acc port, ...
///
/// Example :
///
/// ```rust,ignore
/// agent! {
///    inputs(input: any),
///    outputs(output: any),
///    option(generic_text),
///    fn run(&mut self) -> Result<Signal> {
///        // Receive an IP
///        let ip = try!(self.input.input.recv());
///
///        // Received an IP from the option port (a generic_text)
///        let opt = self.recv_opt();
///
///        // Get the capn'p reader
///        let reader: generic_text::Reader = try!(opt.read_schema());
///        // Print the option
///        println!("{}", try!(reader.get_text()));
///
///        // Send the received IP outside, but don't care about the success (drop on fail)
///        let _ = self.output.output.send(ip);
///
///        Ok(End)
///    }
/// }
/// ```
#[macro_export]
macro_rules! agent {
    (
       $name:ident, $( edges( $( $edge:ident ),* ) )*
        inputs($( $input_field_name:ident: $input_edge_name:ident),* ),
        inputs_array($( $input_array_name:ident: $input_edge_array:ident),* ),
        outputs($( $output_field_name:ident: $output_edge_name:ident),* ),
        outputs_array($($output_array_name:ident: $output_edge_array:ident),* ),
        option($($option_edge: ident),*),
        acc($($acc_edge: ident),*), $( portal($portal_type:ty => $portal_value:expr))*
        fn run(&mut $arg:ident) -> Result<()> $fun:block
    )
        =>
    {
        use rustfbp::agent::Agent;

        use rustfbp::result;
        use rustfbp::result::Result;
        use rustfbp::ports::IPSender;
        use rustfbp::scheduler::CompMsg;
        use std::error::Error;

        use std::sync::mpsc::Sender;

        use rustfbp::ports::{IP, Ports};

        #[allow(unused_imports)]
        use std::collections::HashMap;

        use capnp::serialize;
        use capnp::message;

        use std::io::{Read, Write};

        mod edge_capnp {
                include!("edge_capnp.rs");
        }

        $( $(
            use edge_capnp::$edge;
        )* )*

        impl $name {
            pub fn recv_option(&mut self) -> IP {
                self.try_recv_option();
                if self.option_ip.is_none() {
                    self.option_ip = self.ports.recv("option").ok();
                }
                match self.option_ip {
                    Some(ref ip) => ip.clone(),
                    None => unreachable!(),
                }
            }

            pub fn try_recv_option(&mut self) -> Option<IP> {
                loop {
                    match self.ports.try_recv("option") {
                        Err(_) => { break; },
                        Ok(ip) => { self.option_ip = Some(ip); }
                    };
                }
                self.option_ip.as_ref().map(|ip|{ ip.clone() })
            }
        }

        // simple and array
        impl Agent for $name {

            fn get_ports(&mut self) -> &mut Ports {
                &mut self.ports
            }

            fn is_input_ports(&self) -> bool {
                $(
                    if true || stringify!($input_field_name) == "" { return true; }
                )*
                $(
                    if true || stringify!($input_array_name) == "" { return true; }
                )*
                false
            }

            fn run(&mut $arg) -> Result<()> $fun

        }

        /* Global agent */

        #[allow(dead_code)]
        #[allow(non_camel_case_types)]
        pub struct $name {
            name: String,
            pub ports: Ports,
            pub option_ip: Option<IP>,
            $(
            pub portal: $portal_type ,
            )*
        }

        #[allow(dead_code)]
        pub fn new(name: String, sched: Sender<CompMsg>) -> Result<(Box<Agent + Send>, HashMap<String, IPSender>)> {
            let (ports, senders) = try!(Ports::new(name.clone(), sched,
                                   vec!["option".into(), "acc".into(), $( stringify!($input_field_name).to_string() ),*],
                                   vec![$( stringify!($input_array_name).to_string() ),*],
                                   vec!["acc".into(), $( stringify!($output_field_name).to_string() ),*],
                                   vec![$( stringify!($output_array_name).to_string() ),*],));

            // Put it together
            let comp = $name{
                name: name,
                ports: ports,
                option_ip: None,
                $(
                    portal: $portal_value,
                )*
            };
            Ok((Box::new(comp) as Box<Agent + Send>, senders))
        }

        #[no_mangle]
        pub extern fn create_agent(name: String, sched: Sender<CompMsg>) -> Result<(Box<Agent + Send>, HashMap<String, IPSender>)> {
            new(name, sched)
        }

        #[no_mangle]
        pub extern fn get_schema_input(port: &str) -> Result<String> {
            match port {
                $(
                    stringify!($input_field_name)=> Ok(stringify!($input_edge_name).into()),
                )*
                _ => { Err(result::Error::PortNotFound("unknown".into(), port.into())) }
            }
        }

        #[no_mangle]
        pub extern fn get_schema_input_array(port: &str) -> Result<String> {
            match port {
                $(
                    stringify!($input_array_name) => Ok(stringify!($input_edge_array).into()),
                )*
                _ => { Err(result::Error::PortNotFound("unknown".into(), port.into())) }
            }
        }

        #[no_mangle]
        pub extern fn get_schema_output(port: &str) -> Result<String> {
            match port {
                $(
                    stringify!($output_field_name)=> Ok(stringify!($output_edge_name).into()),
                )*
                _ => { Err(result::Error::PortNotFound("unknown".into(), port.into())) }
            }
        }

        #[no_mangle]
        pub extern fn get_schema_output_array(port: &str) -> Result<String> {
            match port {
                $(
                    stringify!($output_array_name) => Ok(stringify!($output_edge_array).into()),
                )*
                _ => { Err(result::Error::PortNotFound("unknown".into(), port.into())) }
            }
        }
    }
}
<|endoftext|>


<commit_before>pub mod cli;
pub mod crypto;
pub mod error;
pub mod parse;

use std::fmt;

use base64;

static START_DELIMITER: &'static str = "---BEGIN CRYPT---";
static END_HEADER_DELIMITER: &'static str = "---END CRYPT HEADER---";
static END_DELIMITER: &'static str = "---END CRYPT---";

static START_DELIMITER_WHOLE_REGEX: &'static str = r"(?i)^-*BEGIN[\s\W\p{Punct}]*CRYPT-*$";
static START_DELIMITER_CONTAINS_REGEX: &'static str = r"(?i)-*BEGIN[\s\W\p{Punct}]*CRYPT-*";
static END_HEADER_WHOLE_REGEX: &'static str = r"(?i)^-*END.*CRYPT[\s\W\p{Punct}]*HEADER-*$";
static END_HEADER_CONTAINS_REGEX: &'static str = r"(?i)-*END.*CRYPT[\s\W\p{Punct}]*HEADER-*";
static END_DELIMITER_WHOLE_REGEX: &'static str = r"(?i)^-*END[\s\W\p{Punct}]*CRYPT-*$";
static END_DELIMITER_CONTAINS_REGEX: &'static str = r"(?i)^-*END[\s\W\p{Punct}]*CRYPT-*$";

const BASE64_CONFIG: base64::Config = base64::STANDARD_NO_PAD;

#[derive(Debug, Clone, PartialEq, Eq)]
enum Block {
    Plaintext(String),
    Crypt(CryptBlock),
}

#[derive(Debug)]
pub struct CryptFile {
    blocks: Vec<Block>,
}

impl fmt::Display for CryptFile {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.blocks
            .iter()
            .map(|block| match block {
                Block::Plaintext(text) => write!(f, "{}", text),
                Block::Crypt(crypt_block) => {
                    if crypt_block.has_algorithm_and_nonce() {
                        write!(
                            f,
                            "{}\n{};{}\n{}\n{}\n{}\n",
                            START_DELIMITER,
                            crypt_block.algorithm.as_ref().expect("missing algorithm"),
                            crypt_block.nonce.as_ref().expect("missing nonce"),
                            END_HEADER_DELIMITER,
                            crypt_block.ciphertext,
                            END_DELIMITER
                        )
                    } else {
                        write!(
                            f,
                            "{}\n{}\n{}\n",
                            START_DELIMITER, crypt_block.ciphertext, END_DELIMITER
                        )
                    }
                }
            })
            .collect()
    }
}

#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub struct CryptBlock {
    algorithm: Option<String>,
    nonce: Option<String>,
    ciphertext: String,
}

impl CryptBlock {
    fn is_encrypted(&self) -> bool {
        self.has_algorithm_and_nonce()
            && base64::decode_config(&self.ciphertext, BASE64_CONFIG).is_ok()
    }
    fn has_algorithm_and_nonce(&self) -> bool {
        self.algorithm.is_some() && self.nonce.is_some()
    }

    fn new_unencrypted(txt: &str) -> Self {
        CryptBlock {
            ciphertext: txt.to_string(),
            ..Default::default()
        }
    }
}
<commit_msg>text-crypt: prepare for migration to new ascii armor format<commit_after>pub mod cli;
pub mod crypto;
pub mod error;
pub mod parse;

use std::fmt;

use base64;

static START_DELIMITER: &'static str = "---BEGIN CRYPT---";
static END_HEADER_DELIMITER: &'static str = "---END CRYPT HEADER---";
static END_DELIMITER: &'static str = "---END CRYPT---";

static START_DELIMITER_WHOLE_REGEX: &'static str = r"(?i)^-*BEGIN[\s\W\p{Punct}]*CRYPT-*$";
static START_DELIMITER_CONTAINS_REGEX: &'static str = r"(?i)-*BEGIN[\s\W\p{Punct}]*CRYPT-*";
static END_HEADER_WHOLE_REGEX: &'static str = r"(?i)^-*END.*CRYPT[\s\W\p{Punct}]*HEADER-*$";
static END_HEADER_CONTAINS_REGEX: &'static str = r"(?i)-*END.*CRYPT[\s\W\p{Punct}]*HEADER-*";
static END_DELIMITER_WHOLE_REGEX: &'static str = r"(?i)^-*END[\s\W\p{Punct}]*CRYPT-*$";
static END_DELIMITER_CONTAINS_REGEX: &'static str = r"(?i)^-*END[\s\W\p{Punct}]*CRYPT-*$";

const BASE64_CONFIG: base64::Config = base64::STANDARD_NO_PAD;

#[derive(Debug, Clone, PartialEq, Eq)]
enum Block {
    Plaintext(String),
    Crypt(CryptBlock),
}

#[derive(Debug)]
pub struct CryptFile {
    blocks: Vec<Block>,
}

impl fmt::Display for CryptFile {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.blocks
            .iter()
            .map(|block| match block {
                Block::Plaintext(text) => write!(f, "{}", text),
                Block::Crypt(crypt_block) => {
                    if crypt_block.has_algorithm_and_nonce() {
                        write!(
                            f,
                            "{}\n{};{}\n{}\n{}\n{}\n",
                            START_DELIMITER,
                            crypt_block.algorithm.as_ref().expect("missing algorithm"),
                            crypt_block.nonce.as_ref().expect("missing nonce"),
                            END_HEADER_DELIMITER,
                            crypt_block.ciphertext,
                            END_DELIMITER
                        )
                    } else {
                        write!(f, "BEGIN CRYPT\n{}\nEND CRYPT\n", crypt_block.ciphertext)
                    }
                }
            })
            .collect()
    }
}

#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub struct CryptBlock {
    algorithm: Option<String>,
    nonce: Option<String>,
    ciphertext: String,
}

impl CryptBlock {
    fn is_encrypted(&self) -> bool {
        self.has_algorithm_and_nonce()
            && base64::decode_config(&self.ciphertext, BASE64_CONFIG).is_ok()
    }
    fn has_algorithm_and_nonce(&self) -> bool {
        self.algorithm.is_some() && self.nonce.is_some()
    }

    fn new_unencrypted(txt: &str) -> Self {
        CryptBlock {
            ciphertext: txt.to_string(),
            ..Default::default()
        }
    }
}
<|endoftext|>


<commit_before>use std::env;
use std::path::{Path, PathBuf};

pub(crate) enum TargetDir {
    Path(PathBuf),
    Unknown,
}

pub(crate) fn find_target_dir(out_dir: &Path) -> TargetDir {
    if let Some(target_dir) = env::var_os("CARGO_TARGET_DIR") {
        let target_dir = PathBuf::from(target_dir);
        if target_dir.is_absolute() {
            return TargetDir::Path(target_dir);
        } else {
            return TargetDir::Unknown;
        };
    }

    // fs::canonicalize on Windows produces UNC paths which cl.exe is unable to
    // handle in includes.
    // https://github.com/rust-lang/rust/issues/42869
    // https://github.com/alexcrichton/cc-rs/issues/169
    let mut also_try_canonical = cfg!(not(windows));

    let mut dir = out_dir.to_owned();
    loop {
        if dir.join(".rustc_info.json").exists() || dir.join("CACHEDIR.TAG").exists() {
            return TargetDir::Path(dir);
        }
        if dir.pop() {
            continue;
        }
        if also_try_canonical {
            if let Ok(canonical_dir) = out_dir.canonicalize() {
                dir = canonical_dir;
                also_try_canonical = false;
                continue;
            }
        }
        return TargetDir::Unknown;
    }
}
<commit_msg>Add a target/../Cargo.toml condition for finding target dir<commit_after>use std::env;
use std::ffi::OsStr;
use std::path::{Path, PathBuf};

pub(crate) enum TargetDir {
    Path(PathBuf),
    Unknown,
}

pub(crate) fn find_target_dir(out_dir: &Path) -> TargetDir {
    if let Some(target_dir) = env::var_os("CARGO_TARGET_DIR") {
        let target_dir = PathBuf::from(target_dir);
        if target_dir.is_absolute() {
            return TargetDir::Path(target_dir);
        } else {
            return TargetDir::Unknown;
        };
    }

    // fs::canonicalize on Windows produces UNC paths which cl.exe is unable to
    // handle in includes.
    // https://github.com/rust-lang/rust/issues/42869
    // https://github.com/alexcrichton/cc-rs/issues/169
    let mut also_try_canonical = cfg!(not(windows));

    let mut dir = out_dir.to_owned();
    loop {
        if dir.join(".rustc_info.json").exists()
            || dir.join("CACHEDIR.TAG").exists()
            || dir.file_name() == Some(OsStr::new("target"))
                && dir
                    .parent()
                    .map_or(false, |parent| parent.join("Cargo.toml").exists())
        {
            return TargetDir::Path(dir);
        }
        if dir.pop() {
            continue;
        }
        if also_try_canonical {
            if let Ok(canonical_dir) = out_dir.canonicalize() {
                dir = canonical_dir;
                also_try_canonical = false;
                continue;
            }
        }
        return TargetDir::Unknown;
    }
}
<|endoftext|>


<commit_before>pub mod animation;
pub mod animation_data;

pub use self::animation_data::AnimationData;
pub use self::animation::Animation;

use master_smasher::shape::Circle;

use moho::errors as moho_errors;

use std::slice::Iter;

use glm;

pub trait Rectifiable {
    fn rectify(&self) -> glm::IVec4;
}

impl Rectifiable for Circle {
    fn rectify(&self) -> glm::IVec4 {
        glm::to_ivec4(glm::dvec4(self.center.x - self.radius,
                                 self.center.y - self.radius,
                                 self.radius * 2.,
                                 self.radius * 2.))
    }
}

pub trait TryIterator {
    type Item;
    fn try<F>(self, action: F) -> moho_errors::Result<()>
        where F: FnMut(&Self::Item) -> moho_errors::Result<()>;
}

impl<'a, T> TryIterator for Iter<'a, T> {
    type Item = T;
    fn try<F>(self, action: F) -> moho_errors::Result<()>
        where F: FnMut(&T) -> moho_errors::Result<()>
    {
        self.map(action)
            .take_while(Result::is_ok)
            .last()
            .unwrap_or(Ok(()))
    }
}
<commit_msg>make error from tryiterator generic<commit_after>pub mod animation;
pub mod animation_data;

pub use self::animation_data::AnimationData;
pub use self::animation::Animation;

use master_smasher::shape::Circle;

use std::slice::Iter;

use glm;

pub trait Rectifiable {
    fn rectify(&self) -> glm::IVec4;
}

impl Rectifiable for Circle {
    fn rectify(&self) -> glm::IVec4 {
        glm::to_ivec4(glm::dvec4(self.center.x - self.radius,
                                 self.center.y - self.radius,
                                 self.radius * 2.,
                                 self.radius * 2.))
    }
}

pub trait TryIterator {
    type Item;
    fn try<F, E>(self, action: F) -> Result<(), E> where F: FnMut(&Self::Item) -> Result<(), E>;
}

impl<'a, T> TryIterator for Iter<'a, T> {
    type Item = T;
    fn try<F, E>(self, action: F) -> Result<(), E>
        where F: FnMut(&T) -> Result<(), E>
    {
        self.map(action)
            .take_while(Result::is_ok)
            .last()
            .unwrap_or(Ok(()))
    }
}
<|endoftext|>


<commit_before>//  * This file is part of the uutils coreutils package.
//  *
//  * (c) Ben Eills <ben@beneills.com>
//  *
//  * For the full copyright and license information, please view the LICENSE file
//  * that was distributed with this source code.

// spell-checker:ignore (ToDO) rwxr sourcepath targetpath

extern crate clap;
extern crate libc;

mod mode;

#[macro_use]
extern crate uucore;

use clap::{App, Arg, ArgMatches};
use std::fs;
use std::path::{Path, PathBuf};
use std::result::Result;

const DEFAULT_MODE: u32 = 755;

#[allow(dead_code)]
pub struct Behavior {
    main_function: MainFunction,
    specified_mode: Option<u32>,
    suffix: String,
    verbose: bool,
}

#[derive(Clone, Eq, PartialEq)]
pub enum MainFunction {
    /// Create directories
    Directory,
    /// Install files to locations (primary functionality)
    Standard,
}

impl Behavior {
    /// Determine the mode for chmod after copy.
    pub fn mode(&self) -> u32 {
        match self.specified_mode {
            Some(x) => x,
            None => DEFAULT_MODE,
        }
    }
}

static ABOUT: &str = "Copy SOURCE to DEST or multiple SOURCE(s) to the existing
 DIRECTORY, while setting permission modes and owner/group";
static VERSION: &str = env!("CARGO_PKG_VERSION");

static OPT_COMPARE: &str = "compare";
static OPT_BACKUP: &str = "backup";
static OPT_BACKUP_2: &str = "backup2";
static OPT_DIRECTORY: &str = "directory";
static OPT_IGNORED: &str = "ignored";
static OPT_CREATED: &str = "created";
static OPT_GROUP: &str = "group";
static OPT_MODE: &str = "mode";
static OPT_OWNER: &str = "owner";
static OPT_PRESERVE_TIMESTAMPS: &str = "preserve-timestamps";
static OPT_STRIP: &str = "strip";
static OPT_STRIP_PROGRAM: &str = "strip-program";
static OPT_SUFFIX: &str = "suffix";
static OPT_TARGET_DIRECTORY: &str = "target-directory";
static OPT_NO_TARGET_DIRECTORY: &str = "no-target-directory";
static OPT_VERBOSE: &str = "verbose";
static OPT_PRESERVE_CONTEXT: &str = "preserve-context";
static OPT_CONTEXT: &str = "context";

static ARG_FILES: &str = "files";

fn get_usage() -> String {
    format!("{0} [OPTION]... [FILE]...", executable!())
}

/// Main install utility function, called from main.rs.
///
/// Returns a program return code.
///
pub fn uumain(args: impl uucore::Args) -> i32 {
    let usage = get_usage();

    let matches = App::new(executable!())
        .version(VERSION)
        .about(ABOUT)
        .usage(&usage[..])
        .arg(
                Arg::with_name(OPT_BACKUP)
                .long(OPT_BACKUP)
                .help("(unimplemented) make a backup of each existing destination file")
                .value_name("CONTROL")
        )
    // TODO implement flag
        .arg(
            Arg::with_name(OPT_BACKUP_2)
            .short("b")
            .help("(unimplemented) like --backup but does not accept an argument")
        )
        .arg(
            Arg::with_name(OPT_IGNORED)
            .short("c")
            .help("ignored")
        )
        .arg(
            // TODO implement flag
            Arg::with_name(OPT_COMPARE)
            .short("C")
            .long(OPT_COMPARE)
            .help("(unimplemented) compare each pair of source and destination files, and in some cases, do not modify the destination at all")
        )
        .arg(
            Arg::with_name(OPT_DIRECTORY)
                .short("d")
                .long(OPT_DIRECTORY)
                .help("treat all arguments as directory names. create all components of the specified directories")
        )

        .arg(
            // TODO implement flag
            Arg::with_name(OPT_CREATED)
                .short("D")
                .help("(unimplemented) create all leading components of DEST except the last, then copy SOURCE to DEST")
        )
        .arg(
            // TODO implement flag
            Arg::with_name(OPT_GROUP)
                .short("g")
                .long(OPT_GROUP)
                .help("(unimplemented) set group ownership, instead of process's current group")
                .value_name("GROUP")
        )
        .arg(
            Arg::with_name(OPT_MODE)
                .short("m")
                .long(OPT_MODE)
                .help("set permission mode (as in chmod), instead of rwxr-xr-x")
                .value_name("MODE")
        )
        .arg(
            // TODO implement flag
            Arg::with_name(OPT_OWNER)
                .short("o")
                .long(OPT_OWNER)
                .help("(unimplemented) set ownership (super-user only)")
                .value_name("OWNER")
        )
        .arg(
            // TODO implement flag
            Arg::with_name(OPT_PRESERVE_TIMESTAMPS)
                .short("p")
                .long(OPT_PRESERVE_TIMESTAMPS)
                .help("(unimplemented) apply access/modification times of SOURCE files to corresponding destination files")
        )
        .arg(
            // TODO implement flag
            Arg::with_name(OPT_STRIP)
            .short("s")
            .long(OPT_STRIP)
            .help("(unimplemented) strip symbol tables")
        )
        .arg(
            // TODO implement flag
            Arg::with_name(OPT_STRIP_PROGRAM)
                .long(OPT_STRIP_PROGRAM)
                .help("(unimplemented) program used to strip binaries")
                .value_name("PROGRAM")
        )
        .arg(
            // TODO implement flag
            Arg::with_name(OPT_SUFFIX)
                .short("S")
                .long(OPT_SUFFIX)
                .help("(unimplemented) override the usual backup suffix")
                .value_name("SUFFIX")
        )
        .arg(
            // TODO implement flag
            Arg::with_name(OPT_TARGET_DIRECTORY)
                .short("t")
                .long(OPT_TARGET_DIRECTORY)
                .help("(unimplemented) move all SOURCE arguments into DIRECTORY")
                .value_name("DIRECTORY")
        )
        .arg(
            // TODO implement flag
            Arg::with_name(OPT_NO_TARGET_DIRECTORY)
                .short("T")
                .long(OPT_NO_TARGET_DIRECTORY)
                .help("(unimplemented) treat DEST as a normal file")

        )
        .arg(
            Arg::with_name(OPT_VERBOSE)
            .short("v")
            .long(OPT_VERBOSE)
            .help("explain what is being done")
        )
        .arg(
            // TODO implement flag
            Arg::with_name(OPT_PRESERVE_CONTEXT)
                .short("P")
                .long(OPT_PRESERVE_CONTEXT)
                .help("(unimplemented) preserve security context")
        )
        .arg(
            // TODO implement flag
            Arg::with_name(OPT_CONTEXT)
                .short("Z")
                .long(OPT_CONTEXT)
                .help("(unimplemented) set security context of files and directories")
                .value_name("CONTEXT")
        )
        .arg(Arg::with_name(ARG_FILES).multiple(true).takes_value(true))
        .get_matches_from(args);

    let mut paths: Vec<String> = matches
        .values_of(ARG_FILES)
        .map(|v| v.map(ToString::to_string).collect())
        .unwrap_or_default();

    if let Err(s) = check_unimplemented(&matches) {
        show_error!("Unimplemented feature: {}", s);
        return 2;
    }

    let behavior = match behavior(&matches) {
        Ok(x) => x,
        Err(ret) => {
            return ret;
        }
    };

    match behavior.main_function {
        MainFunction::Directory => directory(paths, behavior),
        MainFunction::Standard => standard(paths, behavior),
    }
}

/// Check for unimplemented command line arguments.
///
/// Either return the degenerate Ok value, or an Err with string.
///
/// # Errors
///
/// Error datum is a string of the unimplemented argument.
///
///
fn check_unimplemented<'a>(matches: &ArgMatches) -> Result<(), &'a str> {
    if matches.is_present(OPT_BACKUP) {
        Err("--backup")
    } else if matches.is_present(OPT_BACKUP_2) {
        Err("-b")
    } else if matches.is_present(OPT_COMPARE) {
        Err("--compare, -C")
    } else if matches.is_present(OPT_CREATED) {
        Err("-D")
    } else if matches.is_present(OPT_GROUP) {
        Err("--group, -g")
    } else if matches.is_present(OPT_OWNER) {
        Err("--owner, -o")
    } else if matches.is_present(OPT_PRESERVE_TIMESTAMPS) {
        Err("--preserve-timestamps, -p")
    } else if matches.is_present(OPT_STRIP) {
        Err("--strip, -s")
    } else if matches.is_present(OPT_STRIP_PROGRAM) {
        Err("--strip-program")
    } else if matches.is_present(OPT_SUFFIX) {
        Err("--suffix, -S")
    } else if matches.is_present(OPT_TARGET_DIRECTORY) {
        Err("--target-directory, -t")
    } else if matches.is_present(OPT_NO_TARGET_DIRECTORY) {
        Err("--no-target-directory, -T")
    } else if matches.is_present(OPT_PRESERVE_CONTEXT) {
        Err("--preserve-context, -P")
    } else if matches.is_present(OPT_CONTEXT) {
        Err("--context, -Z")
    } else {
        Ok(())
    }
}

/// Determine behavior, given command line arguments.
///
/// If successful, returns a filled-out Behavior struct.
///
/// # Errors
///
/// In event of failure, returns an integer intended as a program return code.
///
fn behavior(matches: &ArgMatches) -> Result<Behavior, i32> {
    let main_function = if matches.is_present("directory") {
        MainFunction::Directory
    } else {
        MainFunction::Standard
    };

    let considering_dir: bool = MainFunction::Directory == main_function;

    let specified_mode: Option<u32> = if matches.is_present(OPT_MODE) {
        match matches.value_of(OPT_MODE) {
            Some(x) => match mode::parse(&x[..], considering_dir) {
                Ok(y) => Some(y),
                Err(err) => {
                    show_error!("Invalid mode string: {}", err);
                    return Err(1);
                }
            },
            None => {
                show_error!(
                    "option '--mode' requires an argument\n \
                     Try '{} --help' for more information.",
                    executable!()
                );
                return Err(1);
            }
        }
    } else {
        None
    };

    let backup_suffix = if matches.is_present(OPT_SUFFIX) {
        match matches.value_of(OPT_SUFFIX) {
            Some(x) => x,
            None => {
                show_error!(
                    "option '--suffix' requires an argument\n\
                     Try '{} --help' for more information.",
                    executable!()
                );
                return Err(1);
            }
        }
    } else {
        "~"
    };

    Ok(Behavior {
        main_function,
        specified_mode,
        suffix: backup_suffix.to_string(),
        verbose: matches.is_present("v"),
    })
}

/// Creates directories.
///
/// GNU man pages describe this functionality as creating 'all components of
/// the specified directories'.
///
/// Returns an integer intended as a program return code.
///
fn directory(paths: Vec<String>, b: Behavior) -> i32 {
    if paths.is_empty() {
        println!("{} with -d requires at least one argument.", executable!());
        1
    } else {
        let mut all_successful = true;

        for directory in paths.iter() {
            let path = Path::new(directory);

            if path.exists() {
                show_info!("cannot create directory '{}': File exists", path.display());
                all_successful = false;
            }

            if let Err(e) = fs::create_dir(directory) {
                show_info!("{}: {}", path.display(), e.to_string());
                all_successful = false;
            }

            if mode::chmod(&path, b.mode()).is_err() {
                all_successful = false;
            }

            if b.verbose {
                show_info!("created directory '{}'", path.display());
            }
        }
        if all_successful {
            0
        } else {
            1
        }
    }
}

/// Test if the path is a a new file path that can be
/// created immediately
fn is_new_file_path(path: &Path) -> bool {
    path.is_file() || !path.exists() && path.parent().map(Path::is_dir).unwrap_or(true)
}

/// Perform an install, given a list of paths and behavior.
///
/// Returns an integer intended as a program return code.
///
fn standard(paths: Vec<String>, b: Behavior) -> i32 {
    if paths.len() < 2 {
        println!("{} requires at least 2 arguments.", executable!());
        1
    } else {
        let sources = &paths[0..paths.len() - 1]
            .iter()
            .map(PathBuf::from)
            .collect::<Vec<_>>();
        let target = Path::new(paths.last().unwrap());

        if (target.is_file() || is_new_file_path(target)) && sources.len() == 1 {
            copy_file_to_file(&sources[0], &target.to_path_buf(), &b)
        } else {
            copy_files_into_dir(sources, &target.to_path_buf(), &b)
        }
    }
}

/// Copy some files into a directory.
///
/// Prints verbose information and error messages.
/// Returns an integer intended as a program return code.
///
/// # Parameters
///
/// _files_ must all exist as non-directories.
/// _target_dir_ must be a directory.
///
fn copy_files_into_dir(files: &[PathBuf], target_dir: &PathBuf, b: &Behavior) -> i32 {
    if !target_dir.is_dir() {
        show_error!("target ‘{}’ is not a directory", target_dir.display());
        return 1;
    }

    let mut all_successful = true;
    for sourcepath in files.iter() {
        let targetpath = match sourcepath.as_os_str().to_str() {
            Some(name) => target_dir.join(name),
            None => {
                show_error!(
                    "cannot stat ‘{}’: No such file or directory",
                    sourcepath.display()
                );

                all_successful = false;
                continue;
            }
        };

        if copy(sourcepath, &targetpath, b).is_err() {
            all_successful = false;
        }
    }
    if all_successful {
        0
    } else {
        1
    }
}

/// Copy a file to another file.
///
/// Prints verbose information and error messages.
/// Returns an integer intended as a program return code.
///
/// # Parameters
///
/// _file_ must exist as a non-directory.
/// _target_ must be a non-directory
///
fn copy_file_to_file(file: &PathBuf, target: &PathBuf, b: &Behavior) -> i32 {
    if copy(file, &target, b).is_err() {
        1
    } else {
        0
    }
}

/// Copy one file to a new location, changing metadata.
///
/// # Parameters
///
/// _from_ must exist as a non-directory.
/// _to_ must be a non-existent file, whose parent directory exists.
///
/// # Errors
///
/// If the copy system call fails, we print a verbose error and return an empty error value.
///
fn copy(from: &PathBuf, to: &PathBuf, b: &Behavior) -> Result<(), ()> {
    let io_result = fs::copy(from, to);

    if let Err(err) = io_result {
        show_error!(
            "install: cannot install ‘{}’ to ‘{}’: {}",
            from.display(),
            to.display(),
            err
        );
        return Err(());
    }

    if mode::chmod(&to, b.mode()).is_err() {
        return Err(());
    }

    if b.verbose {
        show_info!("'{}' -> '{}'", from.display(), to.display());
    }

    Ok(())
}
<commit_msg>change the position of the TODO<commit_after>//  * This file is part of the uutils coreutils package.
//  *
//  * (c) Ben Eills <ben@beneills.com>
//  *
//  * For the full copyright and license information, please view the LICENSE file
//  * that was distributed with this source code.

// spell-checker:ignore (ToDO) rwxr sourcepath targetpath

extern crate clap;
extern crate libc;

mod mode;

#[macro_use]
extern crate uucore;

use clap::{App, Arg, ArgMatches};
use std::fs;
use std::path::{Path, PathBuf};
use std::result::Result;

const DEFAULT_MODE: u32 = 755;

#[allow(dead_code)]
pub struct Behavior {
    main_function: MainFunction,
    specified_mode: Option<u32>,
    suffix: String,
    verbose: bool,
}

#[derive(Clone, Eq, PartialEq)]
pub enum MainFunction {
    /// Create directories
    Directory,
    /// Install files to locations (primary functionality)
    Standard,
}

impl Behavior {
    /// Determine the mode for chmod after copy.
    pub fn mode(&self) -> u32 {
        match self.specified_mode {
            Some(x) => x,
            None => DEFAULT_MODE,
        }
    }
}

static ABOUT: &str = "Copy SOURCE to DEST or multiple SOURCE(s) to the existing
 DIRECTORY, while setting permission modes and owner/group";
static VERSION: &str = env!("CARGO_PKG_VERSION");

static OPT_COMPARE: &str = "compare";
static OPT_BACKUP: &str = "backup";
static OPT_BACKUP_2: &str = "backup2";
static OPT_DIRECTORY: &str = "directory";
static OPT_IGNORED: &str = "ignored";
static OPT_CREATED: &str = "created";
static OPT_GROUP: &str = "group";
static OPT_MODE: &str = "mode";
static OPT_OWNER: &str = "owner";
static OPT_PRESERVE_TIMESTAMPS: &str = "preserve-timestamps";
static OPT_STRIP: &str = "strip";
static OPT_STRIP_PROGRAM: &str = "strip-program";
static OPT_SUFFIX: &str = "suffix";
static OPT_TARGET_DIRECTORY: &str = "target-directory";
static OPT_NO_TARGET_DIRECTORY: &str = "no-target-directory";
static OPT_VERBOSE: &str = "verbose";
static OPT_PRESERVE_CONTEXT: &str = "preserve-context";
static OPT_CONTEXT: &str = "context";

static ARG_FILES: &str = "files";

fn get_usage() -> String {
    format!("{0} [OPTION]... [FILE]...", executable!())
}

/// Main install utility function, called from main.rs.
///
/// Returns a program return code.
///
pub fn uumain(args: impl uucore::Args) -> i32 {
    let usage = get_usage();

    let matches = App::new(executable!())
        .version(VERSION)
        .about(ABOUT)
        .usage(&usage[..])
        .arg(
                Arg::with_name(OPT_BACKUP)
                .long(OPT_BACKUP)
                .help("(unimplemented) make a backup of each existing destination file")
                .value_name("CONTROL")
        )
        .arg(
            // TODO implement flag
            Arg::with_name(OPT_BACKUP_2)
            .short("b")
            .help("(unimplemented) like --backup but does not accept an argument")
        )
        .arg(
            Arg::with_name(OPT_IGNORED)
            .short("c")
            .help("ignored")
        )
        .arg(
            // TODO implement flag
            Arg::with_name(OPT_COMPARE)
            .short("C")
            .long(OPT_COMPARE)
            .help("(unimplemented) compare each pair of source and destination files, and in some cases, do not modify the destination at all")
        )
        .arg(
            Arg::with_name(OPT_DIRECTORY)
                .short("d")
                .long(OPT_DIRECTORY)
                .help("treat all arguments as directory names. create all components of the specified directories")
        )

        .arg(
            // TODO implement flag
            Arg::with_name(OPT_CREATED)
                .short("D")
                .help("(unimplemented) create all leading components of DEST except the last, then copy SOURCE to DEST")
        )
        .arg(
            // TODO implement flag
            Arg::with_name(OPT_GROUP)
                .short("g")
                .long(OPT_GROUP)
                .help("(unimplemented) set group ownership, instead of process's current group")
                .value_name("GROUP")
        )
        .arg(
            Arg::with_name(OPT_MODE)
                .short("m")
                .long(OPT_MODE)
                .help("set permission mode (as in chmod), instead of rwxr-xr-x")
                .value_name("MODE")
        )
        .arg(
            // TODO implement flag
            Arg::with_name(OPT_OWNER)
                .short("o")
                .long(OPT_OWNER)
                .help("(unimplemented) set ownership (super-user only)")
                .value_name("OWNER")
        )
        .arg(
            // TODO implement flag
            Arg::with_name(OPT_PRESERVE_TIMESTAMPS)
                .short("p")
                .long(OPT_PRESERVE_TIMESTAMPS)
                .help("(unimplemented) apply access/modification times of SOURCE files to corresponding destination files")
        )
        .arg(
            // TODO implement flag
            Arg::with_name(OPT_STRIP)
            .short("s")
            .long(OPT_STRIP)
            .help("(unimplemented) strip symbol tables")
        )
        .arg(
            // TODO implement flag
            Arg::with_name(OPT_STRIP_PROGRAM)
                .long(OPT_STRIP_PROGRAM)
                .help("(unimplemented) program used to strip binaries")
                .value_name("PROGRAM")
        )
        .arg(
            // TODO implement flag
            Arg::with_name(OPT_SUFFIX)
                .short("S")
                .long(OPT_SUFFIX)
                .help("(unimplemented) override the usual backup suffix")
                .value_name("SUFFIX")
        )
        .arg(
            // TODO implement flag
            Arg::with_name(OPT_TARGET_DIRECTORY)
                .short("t")
                .long(OPT_TARGET_DIRECTORY)
                .help("(unimplemented) move all SOURCE arguments into DIRECTORY")
                .value_name("DIRECTORY")
        )
        .arg(
            // TODO implement flag
            Arg::with_name(OPT_NO_TARGET_DIRECTORY)
                .short("T")
                .long(OPT_NO_TARGET_DIRECTORY)
                .help("(unimplemented) treat DEST as a normal file")

        )
        .arg(
            Arg::with_name(OPT_VERBOSE)
            .short("v")
            .long(OPT_VERBOSE)
            .help("explain what is being done")
        )
        .arg(
            // TODO implement flag
            Arg::with_name(OPT_PRESERVE_CONTEXT)
                .short("P")
                .long(OPT_PRESERVE_CONTEXT)
                .help("(unimplemented) preserve security context")
        )
        .arg(
            // TODO implement flag
            Arg::with_name(OPT_CONTEXT)
                .short("Z")
                .long(OPT_CONTEXT)
                .help("(unimplemented) set security context of files and directories")
                .value_name("CONTEXT")
        )
        .arg(Arg::with_name(ARG_FILES).multiple(true).takes_value(true))
        .get_matches_from(args);

    let mut paths: Vec<String> = matches
        .values_of(ARG_FILES)
        .map(|v| v.map(ToString::to_string).collect())
        .unwrap_or_default();

    if let Err(s) = check_unimplemented(&matches) {
        show_error!("Unimplemented feature: {}", s);
        return 2;
    }

    let behavior = match behavior(&matches) {
        Ok(x) => x,
        Err(ret) => {
            return ret;
        }
    };

    match behavior.main_function {
        MainFunction::Directory => directory(paths, behavior),
        MainFunction::Standard => standard(paths, behavior),
    }
}

/// Check for unimplemented command line arguments.
///
/// Either return the degenerate Ok value, or an Err with string.
///
/// # Errors
///
/// Error datum is a string of the unimplemented argument.
///
///
fn check_unimplemented<'a>(matches: &ArgMatches) -> Result<(), &'a str> {
    if matches.is_present(OPT_BACKUP) {
        Err("--backup")
    } else if matches.is_present(OPT_BACKUP_2) {
        Err("-b")
    } else if matches.is_present(OPT_COMPARE) {
        Err("--compare, -C")
    } else if matches.is_present(OPT_CREATED) {
        Err("-D")
    } else if matches.is_present(OPT_GROUP) {
        Err("--group, -g")
    } else if matches.is_present(OPT_OWNER) {
        Err("--owner, -o")
    } else if matches.is_present(OPT_PRESERVE_TIMESTAMPS) {
        Err("--preserve-timestamps, -p")
    } else if matches.is_present(OPT_STRIP) {
        Err("--strip, -s")
    } else if matches.is_present(OPT_STRIP_PROGRAM) {
        Err("--strip-program")
    } else if matches.is_present(OPT_SUFFIX) {
        Err("--suffix, -S")
    } else if matches.is_present(OPT_TARGET_DIRECTORY) {
        Err("--target-directory, -t")
    } else if matches.is_present(OPT_NO_TARGET_DIRECTORY) {
        Err("--no-target-directory, -T")
    } else if matches.is_present(OPT_PRESERVE_CONTEXT) {
        Err("--preserve-context, -P")
    } else if matches.is_present(OPT_CONTEXT) {
        Err("--context, -Z")
    } else {
        Ok(())
    }
}

/// Determine behavior, given command line arguments.
///
/// If successful, returns a filled-out Behavior struct.
///
/// # Errors
///
/// In event of failure, returns an integer intended as a program return code.
///
fn behavior(matches: &ArgMatches) -> Result<Behavior, i32> {
    let main_function = if matches.is_present("directory") {
        MainFunction::Directory
    } else {
        MainFunction::Standard
    };

    let considering_dir: bool = MainFunction::Directory == main_function;

    let specified_mode: Option<u32> = if matches.is_present(OPT_MODE) {
        match matches.value_of(OPT_MODE) {
            Some(x) => match mode::parse(&x[..], considering_dir) {
                Ok(y) => Some(y),
                Err(err) => {
                    show_error!("Invalid mode string: {}", err);
                    return Err(1);
                }
            },
            None => {
                show_error!(
                    "option '--mode' requires an argument\n \
                     Try '{} --help' for more information.",
                    executable!()
                );
                return Err(1);
            }
        }
    } else {
        None
    };

    let backup_suffix = if matches.is_present(OPT_SUFFIX) {
        match matches.value_of(OPT_SUFFIX) {
            Some(x) => x,
            None => {
                show_error!(
                    "option '--suffix' requires an argument\n\
                     Try '{} --help' for more information.",
                    executable!()
                );
                return Err(1);
            }
        }
    } else {
        "~"
    };

    Ok(Behavior {
        main_function,
        specified_mode,
        suffix: backup_suffix.to_string(),
        verbose: matches.is_present("v"),
    })
}

/// Creates directories.
///
/// GNU man pages describe this functionality as creating 'all components of
/// the specified directories'.
///
/// Returns an integer intended as a program return code.
///
fn directory(paths: Vec<String>, b: Behavior) -> i32 {
    if paths.is_empty() {
        println!("{} with -d requires at least one argument.", executable!());
        1
    } else {
        let mut all_successful = true;

        for directory in paths.iter() {
            let path = Path::new(directory);

            if path.exists() {
                show_info!("cannot create directory '{}': File exists", path.display());
                all_successful = false;
            }

            if let Err(e) = fs::create_dir(directory) {
                show_info!("{}: {}", path.display(), e.to_string());
                all_successful = false;
            }

            if mode::chmod(&path, b.mode()).is_err() {
                all_successful = false;
            }

            if b.verbose {
                show_info!("created directory '{}'", path.display());
            }
        }
        if all_successful {
            0
        } else {
            1
        }
    }
}

/// Test if the path is a a new file path that can be
/// created immediately
fn is_new_file_path(path: &Path) -> bool {
    path.is_file() || !path.exists() && path.parent().map(Path::is_dir).unwrap_or(true)
}

/// Perform an install, given a list of paths and behavior.
///
/// Returns an integer intended as a program return code.
///
fn standard(paths: Vec<String>, b: Behavior) -> i32 {
    if paths.len() < 2 {
        println!("{} requires at least 2 arguments.", executable!());
        1
    } else {
        let sources = &paths[0..paths.len() - 1]
            .iter()
            .map(PathBuf::from)
            .collect::<Vec<_>>();
        let target = Path::new(paths.last().unwrap());

        if (target.is_file() || is_new_file_path(target)) && sources.len() == 1 {
            copy_file_to_file(&sources[0], &target.to_path_buf(), &b)
        } else {
            copy_files_into_dir(sources, &target.to_path_buf(), &b)
        }
    }
}

/// Copy some files into a directory.
///
/// Prints verbose information and error messages.
/// Returns an integer intended as a program return code.
///
/// # Parameters
///
/// _files_ must all exist as non-directories.
/// _target_dir_ must be a directory.
///
fn copy_files_into_dir(files: &[PathBuf], target_dir: &PathBuf, b: &Behavior) -> i32 {
    if !target_dir.is_dir() {
        show_error!("target ‘{}’ is not a directory", target_dir.display());
        return 1;
    }

    let mut all_successful = true;
    for sourcepath in files.iter() {
        let targetpath = match sourcepath.as_os_str().to_str() {
            Some(name) => target_dir.join(name),
            None => {
                show_error!(
                    "cannot stat ‘{}’: No such file or directory",
                    sourcepath.display()
                );

                all_successful = false;
                continue;
            }
        };

        if copy(sourcepath, &targetpath, b).is_err() {
            all_successful = false;
        }
    }
    if all_successful {
        0
    } else {
        1
    }
}

/// Copy a file to another file.
///
/// Prints verbose information and error messages.
/// Returns an integer intended as a program return code.
///
/// # Parameters
///
/// _file_ must exist as a non-directory.
/// _target_ must be a non-directory
///
fn copy_file_to_file(file: &PathBuf, target: &PathBuf, b: &Behavior) -> i32 {
    if copy(file, &target, b).is_err() {
        1
    } else {
        0
    }
}

/// Copy one file to a new location, changing metadata.
///
/// # Parameters
///
/// _from_ must exist as a non-directory.
/// _to_ must be a non-existent file, whose parent directory exists.
///
/// # Errors
///
/// If the copy system call fails, we print a verbose error and return an empty error value.
///
fn copy(from: &PathBuf, to: &PathBuf, b: &Behavior) -> Result<(), ()> {
    let io_result = fs::copy(from, to);

    if let Err(err) = io_result {
        show_error!(
            "install: cannot install ‘{}’ to ‘{}’: {}",
            from.display(),
            to.display(),
            err
        );
        return Err(());
    }

    if mode::chmod(&to, b.mode()).is_err() {
        return Err(());
    }

    if b.verbose {
        show_info!("'{}' -> '{}'", from.display(), to.display());
    }

    Ok(())
}
<|endoftext|>