<commit_before>use super::super::{Y, X, Coord, Grid};
use grid_loop::{Edge, GridLoop, GridLoopField};
use super::*;

#[derive(Clone)]
pub struct Field<'a> {
    grid_loop: GridLoop,
    clue: Grid<Clue>,
    dic: &'a Dictionary,
}
impl<'a> Field<'a> {
    pub fn new(clue: &Grid<Clue>, dic: &'a Dictionary) -> Field<'a> {
        let grid_loop = GridLoop::new(clue.height(), clue.width());

        Field {
            grid_loop: grid_loop,
            clue: clue.clone(),
            dic: dic,
        }
    }
    pub fn height(&self) -> i32 {
        self.clue.height()
    }
    pub fn width(&self) -> i32 {
        self.clue.width()
    }
    pub fn inconsistent(&self) -> bool {
        self.grid_loop.inconsistent()
    }
    pub fn set_inconsistent(&mut self) {
        self.grid_loop.set_inconsistent()
    }
    pub fn fully_solved(&self) -> bool {
        self.grid_loop.fully_solved()
    }
    pub fn check_all_cell(&mut self) {
        let height = self.height();
        let width = self.width();
        let mut handle = GridLoop::get_handle(self);
        for y in 0..height {
            for x in 0..width {
                let clue = handle.get_clue((Y(y), X(x)));
                if clue != NO_CLUE {
                    handle.inspect_technique((Y(y * 2 + 1), X(x * 2 + 1)));
                    GridLoop::check(&mut *handle, (Y(y * 2 + 1), X(x * 2 + 1)));
                }
            }
        }
    }
    pub fn get_clue(&self, cd: Coord) -> Clue {
        self.clue[cd]
    }
    pub fn add_clue(&mut self, cd: Coord, clue: Clue) {
        if self.clue[cd] != NO_CLUE {
            if self.clue[cd] != clue {
                self.grid_loop.set_inconsistent();
            }
        } else {
            self.clue[cd] = clue;

            let mut handle = GridLoop::get_handle(self);
            let (Y(y), X(x)) = cd;
            handle.inspect_technique((Y(y * 2 + 1), X(x * 2 + 1)));
            GridLoop::check(&mut *handle, (Y(y * 2 + 1), X(x * 2 + 1)));
        }
    }
    pub fn get_edge(&self, cd: Coord) -> Edge {
        self.grid_loop.get_edge(cd)
    }
    pub fn get_edge_safe(&self, cd: Coord) -> Edge {
        self.grid_loop.get_edge_safe(cd)
    }

    fn inspect_technique(&mut self, (Y(y), X(x)): Coord) {
        let neighbor = [
            (Y(1), X(0)),
            (Y(0), X(1)),
            (Y(-1), X(0)),
            (Y(0), X(-1)),
        ];

        if y % 2 == 1 && x % 2 == 1 {
            let clue = self.clue[(Y(y / 2), X(x / 2))];
            if clue == Clue(0) {
                for d in 0..4 {
                    let (Y(dy), X(dx)) = neighbor[d];
                    GridLoop::decide_edge(self, (Y(y + dy), X(x + dx)), Edge::Blank);
                }
            }
            if clue == Clue(3) {
                // adjacent 3
                for d in 0..4 {
                    let (Y(dy), X(dx)) = neighbor[d];
                    let cell2 = (Y(y / 2 + dy), X(x / 2 + dx));
                    if self.clue.is_valid_coord(cell2) && self.clue[cell2] == Clue(3) {
                        // Deriberately ignoring the possible small loop encircling the two 3's
                        GridLoop::decide_edge(self, (Y(y - dy), X(x - dx)), Edge::Line);
                        GridLoop::decide_edge(self, (Y(y + dy), X(x + dx)), Edge::Line);
                        GridLoop::decide_edge(self, (Y(y + 3 * dy), X(x + 3 * dx)), Edge::Line);
                        GridLoop::decide_edge(self, (Y(y + dy + 2 * dx), X(x + dx + 2 * dy)), Edge::Blank);
                        GridLoop::decide_edge(self, (Y(y + dy - 2 * dx), X(x + dx - 2 * dy)), Edge::Blank);
                    }
                }

                // diagonal 3
                for d in 0..4 {
                    let (Y(dy1), X(dx1)) = neighbor[d];
                    let (Y(dy2), X(dx2)) = neighbor[(d + 1) % 4];
                    let cell2 = (Y(y / 2 + dy1 + dy2), X(x / 2 + dx1 + dx2));
                    if self.clue.is_valid_coord(cell2) && self.clue[cell2] == Clue(3) {
                        GridLoop::decide_edge(self, (Y(y - dy1), X(x - dx1)), Edge::Line);
                        GridLoop::decide_edge(self, (Y(y - dy2), X(x - dx2)), Edge::Line);
                        GridLoop::decide_edge(self, (Y(y + 2 * dy1 + 3 * dy2), X(x + 2 * dx1 + 3 * dx2)), Edge::Line);
                        GridLoop::decide_edge(self, (Y(y + 3 * dy1 + 2 * dy2), X(x + 3 * dx1 + 2 * dx2)), Edge::Line);
                    }
                }
            }
        }
    }
}
impl<'a> GridLoopField for Field<'a> {
    fn grid_loop(&mut self) -> &mut GridLoop {
        &mut self.grid_loop
    }
    fn check_neighborhood(&mut self, (Y(y), X(x)): Coord) {
        if y % 2 == 1 {
            GridLoop::check(self, (Y(y - 1), X(x)));
            GridLoop::check(self, (Y(y + 1), X(x)));

            GridLoop::check(self, (Y(y), X(x - 1)));
            GridLoop::check(self, (Y(y), X(x + 1)));
            GridLoop::check(self, (Y(y - 2), X(x - 1)));
            GridLoop::check(self, (Y(y - 2), X(x + 1)));
            GridLoop::check(self, (Y(y + 2), X(x - 1)));
            GridLoop::check(self, (Y(y + 2), X(x + 1)));
        } else {
            GridLoop::check(self, (Y(y), X(x - 1)));
            GridLoop::check(self, (Y(y), X(x + 1)));

            GridLoop::check(self, (Y(y - 1), X(x)));
            GridLoop::check(self, (Y(y + 1), X(x)));
            GridLoop::check(self, (Y(y - 1), X(x - 2)));
            GridLoop::check(self, (Y(y + 1), X(x - 2)));
            GridLoop::check(self, (Y(y - 1), X(x + 2)));
            GridLoop::check(self, (Y(y + 1), X(x + 2)));
        }
    }
    fn inspect(&mut self, (Y(y), X(x)): Coord) {
        if y % 2 == 1 && x % 2 == 1 {
            let clue = self.clue[(Y(y / 2), X(x / 2))];
            if clue == NO_CLUE || clue == Clue(0) { return; }

            let mut neighbors_code = 0;
            let mut pow3 = 1;
            for i in 0..DICTIONARY_NEIGHBOR_SIZE {
                let (Y(dy), X(dx)) = DICTIONARY_EDGE_OFFSET[i];
                neighbors_code += pow3 * match self.grid_loop.get_edge_safe((Y(y + dy), X(x + dx))) {
                    Edge::Undecided => 0,
                    Edge::Line => 1,
                    Edge::Blank => 2,
                };
                pow3 *= 3;
            }

            let res = self.dic.consult_raw(clue, neighbors_code);
            if res == DICTIONARY_INCONSISTENT {
                self.grid_loop.set_inconsistent();
                return;
            }
            for i in 0..DICTIONARY_NEIGHBOR_SIZE {
                let e = (res >> (2 * i)) & 3;
                if e == 1 {
                    let (Y(dy), X(dx)) = DICTIONARY_EDGE_OFFSET[i];
                    GridLoop::decide_edge(self, (Y(y + dy), X(x + dx)), Edge::Line);
                } else if e == 2 {
                    let (Y(dy), X(dx)) = DICTIONARY_EDGE_OFFSET[i];
                    GridLoop::decide_edge(self, (Y(y + dy), X(x + dx)), Edge::Blank);
                }
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use common;

    fn run_problem_test(dic: &Dictionary, input: &[&str], fully_solved: bool) {
        let height = (input.len() / 2) as i32;
        let width = (input[0].len() / 2) as i32;

        let mut clue = Grid::new(height, width, NO_CLUE);
        for y in 0..height {
            let mut row_iter = input[(y * 2 + 1) as usize].chars();

            for x in 0..width {
                row_iter.next();
                let c = row_iter.next().unwrap();
                if '0' <= c && c <= '3' {
                    clue[(Y(y), X(x))] = Clue(((c as u8) - ('0' as u8)) as i32);
                }
            }
        }

        let mut field = Field::new(&clue, dic);
        field.check_all_cell();

        assert_eq!(field.inconsistent(), false);
        assert_eq!(field.fully_solved(), fully_solved);

        for y in 0..(input.len() as i32) {
            let mut row_iter = input[y as usize].chars();

            for x in 0..(input[0].len() as i32) {
                let ch = row_iter.next().unwrap();

                if !field.grid_loop().is_edge((Y(y), X(x))) {
                    continue;
                }

                let expected_edge = match ch {
                    '|' | '-' => Edge::Line,
                    'x' => Edge::Blank,
                    _ => Edge::Undecided,
                };

                assert_eq!(field.get_edge((Y(y), X(x))), expected_edge, "Comparing at y={}, x={}", y, x);
            }
        }
    }

    #[test]
    fn test_problem() {
        let dic = Dictionary::complete();

        run_problem_test(&dic, &[
            "+x+-+-+ +",
            "x | x    ",
            "+x+-+x+ +",
            "x0x3|    ",
            "+x+-+x+ +",
            "x | x    ",
            "+x+-+-+ +",
        ], false);
        run_problem_test(&dic, &[
            "+x+-+x+x+",
            "x |3| x x",
            "+x+x+-+-+",
            "x | x x3|",
            "+x+-+x+-+",
            "x0x2| | x",
            "+x+x+-+x+",
        ], true);
        run_problem_test(&dic, &[
            "+-+-+-+-+",
            "|3x x x |",
            "+-+ +-+x+",
            "x     | |",
            "+x+ +x+-+",
            "x x x0x1x",
            "+x+x+x+x+",
        ], false);
        run_problem_test(&dic, &[
            "+ +-+-+x+",
            " 2  x2| x",
            "+ +x+x+-+",
            "| x x0x |",
            "+ + +x+ +",
            "         ",
            "+ + + + +",
        ], false);
        run_problem_test(&dic, &[
            "+ +-+ +x+",
            "   3   1x",
            "+x+-+x+ +",
            "   3  |  ",
            "+ +-+ + +",
            "         ",
            "+ + + + +",
        ], false);
        run_problem_test(&dic, &[
            "+-+-+ + +",
            "|2x      ",
            "+x+-+ + +",
            "| |3     ",
            "+ + + + +",
            "     3| x",
            "+ + +-+x+",
        ], false);
    }
}
<commit_msg>Slitherlink: improve Field::inspect<commit_after>use super::super::{Y, X, Coord, Grid};
use grid_loop::{Edge, GridLoop, GridLoopField};
use super::*;

#[derive(Clone)]
pub struct Field<'a> {
    grid_loop: GridLoop,
    clue: Grid<Clue>,
    dic: &'a Dictionary,
}
impl<'a> Field<'a> {
    pub fn new(clue: &Grid<Clue>, dic: &'a Dictionary) -> Field<'a> {
        let grid_loop = GridLoop::new(clue.height(), clue.width());

        Field {
            grid_loop: grid_loop,
            clue: clue.clone(),
            dic: dic,
        }
    }
    pub fn height(&self) -> i32 {
        self.clue.height()
    }
    pub fn width(&self) -> i32 {
        self.clue.width()
    }
    pub fn inconsistent(&self) -> bool {
        self.grid_loop.inconsistent()
    }
    pub fn set_inconsistent(&mut self) {
        self.grid_loop.set_inconsistent()
    }
    pub fn fully_solved(&self) -> bool {
        self.grid_loop.fully_solved()
    }
    pub fn check_all_cell(&mut self) {
        let height = self.height();
        let width = self.width();
        let mut handle = GridLoop::get_handle(self);
        for y in 0..height {
            for x in 0..width {
                let clue = handle.get_clue((Y(y), X(x)));
                if clue != NO_CLUE {
                    handle.inspect_technique((Y(y * 2 + 1), X(x * 2 + 1)));
                    GridLoop::check(&mut *handle, (Y(y * 2 + 1), X(x * 2 + 1)));
                }
            }
        }
    }
    pub fn get_clue(&self, cd: Coord) -> Clue {
        self.clue[cd]
    }
    pub fn add_clue(&mut self, cd: Coord, clue: Clue) {
        if self.clue[cd] != NO_CLUE {
            if self.clue[cd] != clue {
                self.grid_loop.set_inconsistent();
            }
        } else {
            self.clue[cd] = clue;

            let mut handle = GridLoop::get_handle(self);
            let (Y(y), X(x)) = cd;
            handle.inspect_technique((Y(y * 2 + 1), X(x * 2 + 1)));
            GridLoop::check(&mut *handle, (Y(y * 2 + 1), X(x * 2 + 1)));
        }
    }
    pub fn get_edge(&self, cd: Coord) -> Edge {
        self.grid_loop.get_edge(cd)
    }
    pub fn get_edge_safe(&self, cd: Coord) -> Edge {
        self.grid_loop.get_edge_safe(cd)
    }

    fn inspect_technique(&mut self, (Y(y), X(x)): Coord) {
        let neighbor = [
            (Y(1), X(0)),
            (Y(0), X(1)),
            (Y(-1), X(0)),
            (Y(0), X(-1)),
        ];

        if y % 2 == 1 && x % 2 == 1 {
            let clue = self.clue[(Y(y / 2), X(x / 2))];
            if clue == Clue(0) {
                for d in 0..4 {
                    let (Y(dy), X(dx)) = neighbor[d];
                    GridLoop::decide_edge(self, (Y(y + dy), X(x + dx)), Edge::Blank);
                }
            }
            if clue == Clue(3) {
                // adjacent 3
                for d in 0..4 {
                    let (Y(dy), X(dx)) = neighbor[d];
                    let cell2 = (Y(y / 2 + dy), X(x / 2 + dx));
                    if self.clue.is_valid_coord(cell2) && self.clue[cell2] == Clue(3) {
                        // Deriberately ignoring the possible small loop encircling the two 3's
                        GridLoop::decide_edge(self, (Y(y - dy), X(x - dx)), Edge::Line);
                        GridLoop::decide_edge(self, (Y(y + dy), X(x + dx)), Edge::Line);
                        GridLoop::decide_edge(self, (Y(y + 3 * dy), X(x + 3 * dx)), Edge::Line);
                        GridLoop::decide_edge(self, (Y(y + dy + 2 * dx), X(x + dx + 2 * dy)), Edge::Blank);
                        GridLoop::decide_edge(self, (Y(y + dy - 2 * dx), X(x + dx - 2 * dy)), Edge::Blank);
                    }
                }

                // diagonal 3
                for d in 0..4 {
                    let (Y(dy1), X(dx1)) = neighbor[d];
                    let (Y(dy2), X(dx2)) = neighbor[(d + 1) % 4];
                    let cell2 = (Y(y / 2 + dy1 + dy2), X(x / 2 + dx1 + dx2));
                    if self.clue.is_valid_coord(cell2) && self.clue[cell2] == Clue(3) {
                        GridLoop::decide_edge(self, (Y(y - dy1), X(x - dx1)), Edge::Line);
                        GridLoop::decide_edge(self, (Y(y - dy2), X(x - dx2)), Edge::Line);
                        GridLoop::decide_edge(self, (Y(y + 2 * dy1 + 3 * dy2), X(x + 2 * dx1 + 3 * dx2)), Edge::Line);
                        GridLoop::decide_edge(self, (Y(y + 3 * dy1 + 2 * dy2), X(x + 3 * dx1 + 2 * dx2)), Edge::Line);
                    }
                }
            }
        }
    }
}
impl<'a> GridLoopField for Field<'a> {
    fn grid_loop(&mut self) -> &mut GridLoop {
        &mut self.grid_loop
    }
    fn check_neighborhood(&mut self, (Y(y), X(x)): Coord) {
        if y % 2 == 1 {
            GridLoop::check(self, (Y(y - 1), X(x)));
            GridLoop::check(self, (Y(y + 1), X(x)));

            GridLoop::check(self, (Y(y), X(x - 1)));
            GridLoop::check(self, (Y(y), X(x + 1)));
            GridLoop::check(self, (Y(y - 2), X(x - 1)));
            GridLoop::check(self, (Y(y - 2), X(x + 1)));
            GridLoop::check(self, (Y(y + 2), X(x - 1)));
            GridLoop::check(self, (Y(y + 2), X(x + 1)));
        } else {
            GridLoop::check(self, (Y(y), X(x - 1)));
            GridLoop::check(self, (Y(y), X(x + 1)));

            GridLoop::check(self, (Y(y - 1), X(x)));
            GridLoop::check(self, (Y(y + 1), X(x)));
            GridLoop::check(self, (Y(y - 1), X(x - 2)));
            GridLoop::check(self, (Y(y + 1), X(x - 2)));
            GridLoop::check(self, (Y(y - 1), X(x + 2)));
            GridLoop::check(self, (Y(y + 1), X(x + 2)));
        }
    }
    fn inspect(&mut self, (Y(y), X(x)): Coord) {
        if y % 2 == 1 && x % 2 == 1 {
            let clue = self.clue[(Y(y / 2), X(x / 2))];
            if clue == NO_CLUE || clue == Clue(0) { return; }

            let mut neighbors_code = 0;
            let mut pow3 = 1;
            for i in 0..DICTIONARY_NEIGHBOR_SIZE {
                let (Y(dy), X(dx)) = DICTIONARY_EDGE_OFFSET[i];
                neighbors_code += pow3 * match self.grid_loop.get_edge_safe((Y(y + dy), X(x + dx))) {
                    Edge::Undecided => 0,
                    Edge::Line => 1,
                    Edge::Blank => 2,
                };
                pow3 *= 3;
            }

            let res = self.dic.consult_raw(clue, neighbors_code);
            if res == DICTIONARY_INCONSISTENT {
                self.grid_loop.set_inconsistent();
                return;
            }
            let mut res = res;
            while res != 0 {
                let ix = res.trailing_zeros();
                let i = ix / 2;
                let (Y(dy), X(dx)) = DICTIONARY_EDGE_OFFSET[i as usize];
                GridLoop::decide_edge(self, (Y(y + dy), X(x + dx)), if ix % 2 == 0 { Edge::Line } else { Edge::Blank });
                res ^= 1u32 << ix;
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use common;

    fn run_problem_test(dic: &Dictionary, input: &[&str], fully_solved: bool) {
        let height = (input.len() / 2) as i32;
        let width = (input[0].len() / 2) as i32;

        let mut clue = Grid::new(height, width, NO_CLUE);
        for y in 0..height {
            let mut row_iter = input[(y * 2 + 1) as usize].chars();

            for x in 0..width {
                row_iter.next();
                let c = row_iter.next().unwrap();
                if '0' <= c && c <= '3' {
                    clue[(Y(y), X(x))] = Clue(((c as u8) - ('0' as u8)) as i32);
                }
            }
        }

        let mut field = Field::new(&clue, dic);
        field.check_all_cell();

        assert_eq!(field.inconsistent(), false);
        assert_eq!(field.fully_solved(), fully_solved);

        for y in 0..(input.len() as i32) {
            let mut row_iter = input[y as usize].chars();

            for x in 0..(input[0].len() as i32) {
                let ch = row_iter.next().unwrap();

                if !field.grid_loop().is_edge((Y(y), X(x))) {
                    continue;
                }

                let expected_edge = match ch {
                    '|' | '-' => Edge::Line,
                    'x' => Edge::Blank,
                    _ => Edge::Undecided,
                };

                assert_eq!(field.get_edge((Y(y), X(x))), expected_edge, "Comparing at y={}, x={}", y, x);
            }
        }
    }

    #[test]
    fn test_problem() {
        let dic = Dictionary::complete();

        run_problem_test(&dic, &[
            "+x+-+-+ +",
            "x | x    ",
            "+x+-+x+ +",
            "x0x3|    ",
            "+x+-+x+ +",
            "x | x    ",
            "+x+-+-+ +",
        ], false);
        run_problem_test(&dic, &[
            "+x+-+x+x+",
            "x |3| x x",
            "+x+x+-+-+",
            "x | x x3|",
            "+x+-+x+-+",
            "x0x2| | x",
            "+x+x+-+x+",
        ], true);
        run_problem_test(&dic, &[
            "+-+-+-+-+",
            "|3x x x |",
            "+-+ +-+x+",
            "x     | |",
            "+x+ +x+-+",
            "x x x0x1x",
            "+x+x+x+x+",
        ], false);
        run_problem_test(&dic, &[
            "+ +-+-+x+",
            " 2  x2| x",
            "+ +x+x+-+",
            "| x x0x |",
            "+ + +x+ +",
            "         ",
            "+ + + + +",
        ], false);
        run_problem_test(&dic, &[
            "+ +-+ +x+",
            "   3   1x",
            "+x+-+x+ +",
            "   3  |  ",
            "+ +-+ + +",
            "         ",
            "+ + + + +",
        ], false);
        run_problem_test(&dic, &[
            "+-+-+ + +",
            "|2x      ",
            "+x+-+ + +",
            "| |3     ",
            "+ + + + +",
            "     3| x",
            "+ + +-+x+",
        ], false);
    }
}
<|endoftext|>


<commit_before>use std::collections::BTreeMap;

use super::ActionType;

mod deser;
mod builder;

pub use self::builder::MessageActionBuilder;

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct MessageAction {
    uuid: String,
    name: Option<String>,
    values: BTreeMap<String, String>
}

impl MessageAction {
    pub fn uuid(&self) -> &String {
        &self.uuid
    }
    pub fn name(&self) -> Option<&String> {
        self.name.as_ref()
    }
}

impl From<MessageAction> for super::ActionType {
    fn from(action: MessageAction) -> super::ActionType {
        super::ActionType::Message(action)
    }
}
<commit_msg>MessageAction: add values() method<commit_after>use std::collections::BTreeMap;

use super::ActionType;

mod deser;
mod builder;

pub use self::builder::MessageActionBuilder;

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct MessageAction {
    uuid: String,
    name: Option<String>,
    values: BTreeMap<String, String>
}

impl MessageAction {
    pub fn uuid(&self) -> &String {
        &self.uuid
    }
    pub fn name(&self) -> Option<&String> {
        self.name.as_ref()
    }
    pub fn values(&self) -> &BTreeMap<String, String> {
        &self.values
    }
}

impl From<MessageAction> for super::ActionType {
    fn from(action: MessageAction) -> super::ActionType {
        super::ActionType::Message(action)
    }
}
<|endoftext|>


<commit_before>use super::prelude::*;

/// Returns the JSON representation of the current deployed commit sha.
///
/// The sha is contained within the `HEROKU_SLUG_COMMIT` environment variable.
/// If `HEROKU_SLUG_COMMIT` is not set, returns `"unknown"`.
pub fn show_deployed_sha(req: &mut dyn RequestExt) -> EndpointResult {
    let deployed_sha =
        dotenv::var("HEROKU_SLUG_COMMIT").unwrap_or_else(|_| String::from("unknown"));

    Ok(req.json(&json!({
        "deployed_sha": &deployed_sha[..],
        "commit": &deployed_sha[..],
    })))
}
<commit_msg>site_metadata: Expose `read_only` field<commit_after>use super::prelude::*;

/// Returns the JSON representation of the current deployed commit sha.
///
/// The sha is contained within the `HEROKU_SLUG_COMMIT` environment variable.
/// If `HEROKU_SLUG_COMMIT` is not set, returns `"unknown"`.
pub fn show_deployed_sha(req: &mut dyn RequestExt) -> EndpointResult {
    let config = &req.app().config;
    let read_only = config.db.are_all_read_only();

    let deployed_sha =
        dotenv::var("HEROKU_SLUG_COMMIT").unwrap_or_else(|_| String::from("unknown"));

    Ok(req.json(&json!({
        "deployed_sha": &deployed_sha[..],
        "commit": &deployed_sha[..],
        "read_only": read_only,
    })))
}
<|endoftext|>


<commit_before>mod number;
mod identifier;
mod call;
mod byte_literal;

pub use self::call::call;

use parser::Expression;
use memory::MemoryBlock;

use operations::OperationsResult;
use operations::scope::{TypeId, ScopeStack, ArraySize};

use self::identifier::{store_identifier, store_identifier_array};
use self::number::store_number;
use self::byte_literal::store_byte_literal;
use self::call::call_with_exprs;

/// Generates operations for evaluating the given expression
/// and storing its result in the given target memory block
/// NOTE: Assumes that the target memory block is **zero**
/// so that it can be mutated
pub fn into_operations(
    scope: &mut ScopeStack,
    expr: Expression,
    target_type: TypeId,
    target: MemoryBlock,
) -> OperationsResult {
    match expr {
        Expression::Identifier(name) => store_identifier(scope, name, target_type, target),
        Expression::Number(value) => store_number(scope, value, target_type, target),
        Expression::Call {method, args} => call_with_exprs(scope, *method, args, target_type, target),
        _ => unimplemented!(),
    }
}

/// Generates operations for evaluating the given expression
/// and storing its result in the given target memory block
/// that represents an array
/// NOTE: Assumes that the target memory block is **zero**
/// so that it can be mutated
pub fn into_operations_array(
    scope: &mut ScopeStack,
    expr: Expression,
    item_type: TypeId,
    size: ArraySize,
    target: MemoryBlock,
) -> OperationsResult {
    match expr {
        Expression::ByteLiteral(bytes) => store_byte_literal(scope, bytes, item_type, size, target),
        Expression::Identifier(name) => store_identifier_array(scope, name, item_type, size, target),
        _ => unimplemented!(),
    }
}
<commit_msg>Some simple errors that could obviously be returned in the expression code<commit_after>mod number;
mod identifier;
mod call;
mod byte_literal;

pub use self::call::call;

use parser::Expression;
use memory::MemoryBlock;

use operations::{Error, OperationsResult};
use operations::item_type::ItemType;
use operations::scope::{TypeId, ScopeStack, ArraySize};

use self::identifier::{store_identifier, store_identifier_array};
use self::number::store_number;
use self::byte_literal::store_byte_literal;
use self::call::call_with_exprs;

/// Generates operations for evaluating the given expression
/// and storing its result in the given target memory block
/// NOTE: Assumes that the target memory block is **zero**
/// so that it can be mutated
pub fn into_operations(
    scope: &mut ScopeStack,
    expr: Expression,
    target_type: TypeId,
    target: MemoryBlock,
) -> OperationsResult {
    match expr {
        Expression::Identifier(name) => store_identifier(scope, name, target_type, target),
        Expression::Number(value) => store_number(scope, value, target_type, target),
        Expression::Call {method, args} => call_with_exprs(scope, *method, args, target_type, target),
        Expression::ByteLiteral(bytes) => Err(Error::MismatchedTypes {
            expected: scope.get_type(target_type).clone(),
            found: ItemType::Array {
                item: Some(scope.primitives().u8()),
                size: Some(bytes.len()),
            },
        }),
        _ => unimplemented!(),
    }
}

/// Generates operations for evaluating the given expression
/// and storing its result in the given target memory block
/// that represents an array
/// NOTE: Assumes that the target memory block is **zero**
/// so that it can be mutated
pub fn into_operations_array(
    scope: &mut ScopeStack,
    expr: Expression,
    item_type: TypeId,
    size: ArraySize,
    target: MemoryBlock,
) -> OperationsResult {
    match expr {
        Expression::ByteLiteral(bytes) => store_byte_literal(scope, bytes, item_type, size, target),
        Expression::Identifier(name) => store_identifier_array(scope, name, item_type, size, target),
        Expression::Number(value) => Err(Error::MismatchedTypes {
            expected: ItemType::Array {
                item: Some(item_type),
                size: Some(size),
            },
            found: scope.get_type(scope.primitives().u8()).clone(),
        }),
        _ => unimplemented!(),
    }
}
<|endoftext|>


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

// spell-checker:ignore (ToDO) RFILE refsize rfilename fsize tsize

#[macro_use]
extern crate uucore;

use clap::{crate_version, App, Arg};
use std::convert::TryFrom;
use std::fs::{metadata, OpenOptions};
use std::io::ErrorKind;
use std::path::Path;
use uucore::display::Quotable;
use uucore::parse_size::{parse_size, ParseSizeError};

#[derive(Debug, Eq, PartialEq)]
enum TruncateMode {
    Absolute(usize),
    Extend(usize),
    Reduce(usize),
    AtMost(usize),
    AtLeast(usize),
    RoundDown(usize),
    RoundUp(usize),
}

impl TruncateMode {
    /// Compute a target size in bytes for this truncate mode.
    ///
    /// `fsize` is the size of the reference file, in bytes.
    ///
    /// # Examples
    ///
    /// ```rust,ignore
    /// let mode = TruncateMode::Extend(5);
    /// let fsize = 10;
    /// assert_eq!(mode.to_size(fsize), 15);
    /// ```
    fn to_size(&self, fsize: usize) -> usize {
        match self {
            TruncateMode::Absolute(size) => *size,
            TruncateMode::Extend(size) => fsize + size,
            TruncateMode::Reduce(size) => fsize - size,
            TruncateMode::AtMost(size) => fsize.min(*size),
            TruncateMode::AtLeast(size) => fsize.max(*size),
            TruncateMode::RoundDown(size) => fsize - fsize % size,
            TruncateMode::RoundUp(size) => fsize + fsize % size,
        }
    }
}

static ABOUT: &str = "Shrink or extend the size of each file to the specified size.";

pub mod options {
    pub static IO_BLOCKS: &str = "io-blocks";
    pub static NO_CREATE: &str = "no-create";
    pub static REFERENCE: &str = "reference";
    pub static SIZE: &str = "size";
    pub static ARG_FILES: &str = "files";
}

fn usage() -> String {
    format!("{0} [OPTION]... [FILE]...", uucore::execution_phrase())
}

fn get_long_usage() -> String {
    String::from(
        "
    SIZE is an integer with an optional prefix and optional unit.
    The available units (K, M, G, T, P, E, Z, and Y) use the following format:
        'KB' =>           1000 (kilobytes)
        'K'  =>           1024 (kibibytes)
        'MB' =>      1000*1000 (megabytes)
        'M'  =>      1024*1024 (mebibytes)
        'GB' => 1000*1000*1000 (gigabytes)
        'G'  => 1024*1024*1024 (gibibytes)
    SIZE may also be prefixed by one of the following to adjust the size of each
    file based on its current size:
        '+'  => extend by
        '-'  => reduce by
        '<'  => at most
        '>'  => at least
        '/'  => round down to multiple of
        '%'  => round up to multiple of",
    )
}

pub fn uumain(args: impl uucore::Args) -> i32 {
    let usage = usage();
    let long_usage = get_long_usage();

    let matches = uu_app()
        .usage(&usage[..])
        .after_help(&long_usage[..])
        .get_matches_from(args);

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

    if files.is_empty() {
        show_error!("Missing an argument");
        return 1;
    } else {
        let io_blocks = matches.is_present(options::IO_BLOCKS);
        let no_create = matches.is_present(options::NO_CREATE);
        let reference = matches.value_of(options::REFERENCE).map(String::from);
        let size = matches.value_of(options::SIZE).map(String::from);
        if let Err(e) = truncate(no_create, io_blocks, reference, size, files) {
            match e.kind() {
                ErrorKind::NotFound => {
                    // TODO Improve error-handling so that the error
                    // returned by `truncate()` provides the necessary
                    // parameter for formatting the error message.
                    let reference = matches.value_of(options::REFERENCE).map(String::from);
                    crash!(
                        1,
                        "cannot stat {}: No such file or directory",
                        reference.as_deref().unwrap_or("").quote()
                    ); // TODO: fix '--no-create' see test_reference and test_truncate_bytes_size
                }
                _ => crash!(1, "{}", e.to_string()),
            }
        }
    }

    0
}

pub fn uu_app() -> App<'static, 'static> {
    App::new(uucore::util_name())
        .version(crate_version!())
        .about(ABOUT)
        .arg(
            Arg::with_name(options::IO_BLOCKS)
            .short("o")
            .long(options::IO_BLOCKS)
            .help("treat SIZE as the number of I/O blocks of the file rather than bytes (NOT IMPLEMENTED)")
        )
        .arg(
            Arg::with_name(options::NO_CREATE)
            .short("c")
            .long(options::NO_CREATE)
            .help("do not create files that do not exist")
        )
        .arg(
            Arg::with_name(options::REFERENCE)
            .short("r")
            .long(options::REFERENCE)
            .required_unless(options::SIZE)
            .help("base the size of each file on the size of RFILE")
            .value_name("RFILE")
        )
        .arg(
            Arg::with_name(options::SIZE)
            .short("s")
            .long(options::SIZE)
            .required_unless(options::REFERENCE)
            .help("set or adjust the size of each file according to SIZE, which is in bytes unless --io-blocks is specified")
            .value_name("SIZE")
        )
        .arg(Arg::with_name(options::ARG_FILES)
             .value_name("FILE")
             .multiple(true)
             .takes_value(true)
             .required(true)
             .min_values(1))
}

/// Truncate the named file to the specified size.
///
/// If `create` is true, then the file will be created if it does not
/// already exist. If `size` is larger than the number of bytes in the
/// file, then the file will be padded with zeros. If `size` is smaller
/// than the number of bytes in the file, then the file will be
/// truncated and any bytes beyond `size` will be lost.
///
/// # Errors
///
/// If the file could not be opened, or there was a problem setting the
/// size of the file.
fn file_truncate(filename: &str, create: bool, size: usize) -> std::io::Result<()> {
    let path = Path::new(filename);
    let f = OpenOptions::new().write(true).create(create).open(path)?;
    f.set_len(u64::try_from(size).unwrap())
}

/// Truncate files to a size relative to a given file.
///
/// `rfilename` is the name of the reference file.
///
/// `size_string` gives the size relative to the reference file to which
/// to set the target files. For example, "+3K" means "set each file to
/// be three kilobytes larger than the size of the reference file".
///
/// If `create` is true, then each file will be created if it does not
/// already exist.
///
/// # Errors
///
/// If the any file could not be opened, or there was a problem setting
/// the size of at least one file.
fn truncate_reference_and_size(
    rfilename: &str,
    size_string: &str,
    filenames: Vec<String>,
    create: bool,
) -> std::io::Result<()> {
    let mode = match parse_mode_and_size(size_string) {
        Ok(m) => match m {
            TruncateMode::Absolute(_) => {
                crash!(1, "you must specify a relative '--size' with '--reference'")
            }
            _ => m,
        },
        Err(e) => crash!(1, "Invalid number: {}", e.to_string()),
    };
    let fsize = usize::try_from(metadata(rfilename)?.len()).unwrap();
    let tsize = mode.to_size(fsize);
    for filename in &filenames {
        file_truncate(filename, create, tsize)?;
    }
    Ok(())
}

/// Truncate files to match the size of a given reference file.
///
/// `rfilename` is the name of the reference file.
///
/// If `create` is true, then each file will be created if it does not
/// already exist.
///
/// # Errors
///
/// If the any file could not be opened, or there was a problem setting
/// the size of at least one file.
fn truncate_reference_file_only(
    rfilename: &str,
    filenames: Vec<String>,
    create: bool,
) -> std::io::Result<()> {
    let tsize = usize::try_from(metadata(rfilename)?.len()).unwrap();
    for filename in &filenames {
        file_truncate(filename, create, tsize)?;
    }
    Ok(())
}

/// Truncate files to a specified size.
///
/// `size_string` gives either an absolute size or a relative size. A
/// relative size adjusts the size of each file relative to its current
/// size. For example, "3K" means "set each file to be three kilobytes"
/// whereas "+3K" means "set each file to be three kilobytes larger than
/// its current size".
///
/// If `create` is true, then each file will be created if it does not
/// already exist.
///
/// # Errors
///
/// If the any file could not be opened, or there was a problem setting
/// the size of at least one file.
fn truncate_size_only(
    size_string: &str,
    filenames: Vec<String>,
    create: bool,
) -> std::io::Result<()> {
    let mode = match parse_mode_and_size(size_string) {
        Ok(m) => m,
        Err(e) => crash!(1, "Invalid number: {}", e.to_string()),
    };
    for filename in &filenames {
        let fsize = usize::try_from(metadata(filename)?.len()).unwrap();
        let tsize = mode.to_size(fsize);
        file_truncate(filename, create, tsize)?;
    }
    Ok(())
}

fn truncate(
    no_create: bool,
    _: bool,
    reference: Option<String>,
    size: Option<String>,
    filenames: Vec<String>,
) -> std::io::Result<()> {
    let create = !no_create;
    // There are four possibilities
    // - reference file given and size given,
    // - reference file given but no size given,
    // - no reference file given but size given,
    // - no reference file given and no size given,
    match (reference, size) {
        (Some(rfilename), Some(size_string)) => {
            truncate_reference_and_size(&rfilename, &size_string, filenames, create)
        }
        (Some(rfilename), None) => truncate_reference_file_only(&rfilename, filenames, create),
        (None, Some(size_string)) => truncate_size_only(&size_string, filenames, create),
        (None, None) => crash!(1, "you must specify either --reference or --size"), // this case cannot happen anymore because it's handled by clap
    }
}

/// Decide whether a character is one of the size modifiers, like '+' or '<'.
fn is_modifier(c: char) -> bool {
    c == '+' || c == '-' || c == '<' || c == '>' || c == '/' || c == '%'
}

/// Parse a size string with optional modifier symbol as its first character.
///
/// A size string is as described in [`parse_size`]. The first character
/// of `size_string` might be a modifier symbol, like `'+'` or
/// `'<'`. The first element of the pair returned by this function
/// indicates which modifier symbol was present, or
/// [`TruncateMode::Absolute`] if none.
///
/// # Panics
///
/// If `size_string` is empty, or if no number could be parsed from the
/// given string (for example, if the string were `"abc"`).
///
/// # Examples
///
/// ```rust,ignore
/// assert_eq!(parse_mode_and_size("+123"), (TruncateMode::Extend, 123));
/// ```
fn parse_mode_and_size(size_string: &str) -> Result<TruncateMode, ParseSizeError> {
    // Trim any whitespace.
    let mut size_string = size_string.trim();

    // Get the modifier character from the size string, if any. For
    // example, if the argument is "+123", then the modifier is '+'.
    if let Some(c) = size_string.chars().next() {
        if is_modifier(c) {
            size_string = &size_string[1..];
        }
        parse_size(size_string).map(match c {
            '+' => TruncateMode::Extend,
            '-' => TruncateMode::Reduce,
            '<' => TruncateMode::AtMost,
            '>' => TruncateMode::AtLeast,
            '/' => TruncateMode::RoundDown,
            '%' => TruncateMode::RoundUp,
            _ => TruncateMode::Absolute,
        })
    } else {
        Err(ParseSizeError::ParseFailure(size_string.to_string()))
    }
}

#[cfg(test)]
mod tests {
    use crate::parse_mode_and_size;
    use crate::TruncateMode;

    #[test]
    fn test_parse_mode_and_size() {
        assert_eq!(parse_mode_and_size("10"), Ok(TruncateMode::Absolute(10)));
        assert_eq!(parse_mode_and_size("+10"), Ok(TruncateMode::Extend(10)));
        assert_eq!(parse_mode_and_size("-10"), Ok(TruncateMode::Reduce(10)));
        assert_eq!(parse_mode_and_size("<10"), Ok(TruncateMode::AtMost(10)));
        assert_eq!(parse_mode_and_size(">10"), Ok(TruncateMode::AtLeast(10)));
        assert_eq!(parse_mode_and_size("/10"), Ok(TruncateMode::RoundDown(10)));
        assert_eq!(parse_mode_and_size("%10"), Ok(TruncateMode::RoundUp(10)));
    }
}
<commit_msg>truncate: return UResult from uumain() function<commit_after>//  * This file is part of the uutils coreutils package.
//  *
//  * (c) Alex Lyon <arcterus@mail.com>
//  *
//  * For the full copyright and license information, please view the LICENSE
//  * file that was distributed with this source code.

// spell-checker:ignore (ToDO) RFILE refsize rfilename fsize tsize

#[macro_use]
extern crate uucore;

use clap::{crate_version, App, Arg};
use std::convert::TryFrom;
use std::fs::{metadata, OpenOptions};
use std::io::ErrorKind;
use std::path::Path;
use uucore::display::Quotable;
use uucore::error::{UIoError, UResult, USimpleError, UUsageError};
use uucore::parse_size::{parse_size, ParseSizeError};

#[derive(Debug, Eq, PartialEq)]
enum TruncateMode {
    Absolute(usize),
    Extend(usize),
    Reduce(usize),
    AtMost(usize),
    AtLeast(usize),
    RoundDown(usize),
    RoundUp(usize),
}

impl TruncateMode {
    /// Compute a target size in bytes for this truncate mode.
    ///
    /// `fsize` is the size of the reference file, in bytes.
    ///
    /// # Examples
    ///
    /// ```rust,ignore
    /// let mode = TruncateMode::Extend(5);
    /// let fsize = 10;
    /// assert_eq!(mode.to_size(fsize), 15);
    /// ```
    fn to_size(&self, fsize: usize) -> usize {
        match self {
            TruncateMode::Absolute(size) => *size,
            TruncateMode::Extend(size) => fsize + size,
            TruncateMode::Reduce(size) => fsize - size,
            TruncateMode::AtMost(size) => fsize.min(*size),
            TruncateMode::AtLeast(size) => fsize.max(*size),
            TruncateMode::RoundDown(size) => fsize - fsize % size,
            TruncateMode::RoundUp(size) => fsize + fsize % size,
        }
    }
}

static ABOUT: &str = "Shrink or extend the size of each file to the specified size.";

pub mod options {
    pub static IO_BLOCKS: &str = "io-blocks";
    pub static NO_CREATE: &str = "no-create";
    pub static REFERENCE: &str = "reference";
    pub static SIZE: &str = "size";
    pub static ARG_FILES: &str = "files";
}

fn usage() -> String {
    format!("{0} [OPTION]... [FILE]...", uucore::execution_phrase())
}

fn get_long_usage() -> String {
    String::from(
        "
    SIZE is an integer with an optional prefix and optional unit.
    The available units (K, M, G, T, P, E, Z, and Y) use the following format:
        'KB' =>           1000 (kilobytes)
        'K'  =>           1024 (kibibytes)
        'MB' =>      1000*1000 (megabytes)
        'M'  =>      1024*1024 (mebibytes)
        'GB' => 1000*1000*1000 (gigabytes)
        'G'  => 1024*1024*1024 (gibibytes)
    SIZE may also be prefixed by one of the following to adjust the size of each
    file based on its current size:
        '+'  => extend by
        '-'  => reduce by
        '<'  => at most
        '>'  => at least
        '/'  => round down to multiple of
        '%'  => round up to multiple of",
    )
}

#[uucore_procs::gen_uumain]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
    let usage = usage();
    let long_usage = get_long_usage();

    let matches = uu_app()
        .usage(&usage[..])
        .after_help(&long_usage[..])
        .get_matches_from(args);

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

    if files.is_empty() {
        return Err(UUsageError::new(1, "missing file operand"));
    } else {
        let io_blocks = matches.is_present(options::IO_BLOCKS);
        let no_create = matches.is_present(options::NO_CREATE);
        let reference = matches.value_of(options::REFERENCE).map(String::from);
        let size = matches.value_of(options::SIZE).map(String::from);
        truncate(no_create, io_blocks, reference, size, files).map_err(|e| {
            match e.kind() {
                ErrorKind::NotFound => {
                    // TODO Improve error-handling so that the error
                    // returned by `truncate()` provides the necessary
                    // parameter for formatting the error message.
                    let reference = matches.value_of(options::REFERENCE).map(String::from);
                    USimpleError::new(
                        1,
                        format!(
                            "cannot stat {}: No such file or directory",
                            reference.as_deref().unwrap_or("").quote()
                        ),
                    ) // TODO: fix '--no-create' see test_reference and test_truncate_bytes_size
                }
                _ => uio_error!(e, ""),
            }
        })
    }
}

pub fn uu_app() -> App<'static, 'static> {
    App::new(uucore::util_name())
        .version(crate_version!())
        .about(ABOUT)
        .arg(
            Arg::with_name(options::IO_BLOCKS)
            .short("o")
            .long(options::IO_BLOCKS)
            .help("treat SIZE as the number of I/O blocks of the file rather than bytes (NOT IMPLEMENTED)")
        )
        .arg(
            Arg::with_name(options::NO_CREATE)
            .short("c")
            .long(options::NO_CREATE)
            .help("do not create files that do not exist")
        )
        .arg(
            Arg::with_name(options::REFERENCE)
            .short("r")
            .long(options::REFERENCE)
            .required_unless(options::SIZE)
            .help("base the size of each file on the size of RFILE")
            .value_name("RFILE")
        )
        .arg(
            Arg::with_name(options::SIZE)
            .short("s")
            .long(options::SIZE)
            .required_unless(options::REFERENCE)
            .help("set or adjust the size of each file according to SIZE, which is in bytes unless --io-blocks is specified")
            .value_name("SIZE")
        )
        .arg(Arg::with_name(options::ARG_FILES)
             .value_name("FILE")
             .multiple(true)
             .takes_value(true)
             .required(true)
             .min_values(1))
}

/// Truncate the named file to the specified size.
///
/// If `create` is true, then the file will be created if it does not
/// already exist. If `size` is larger than the number of bytes in the
/// file, then the file will be padded with zeros. If `size` is smaller
/// than the number of bytes in the file, then the file will be
/// truncated and any bytes beyond `size` will be lost.
///
/// # Errors
///
/// If the file could not be opened, or there was a problem setting the
/// size of the file.
fn file_truncate(filename: &str, create: bool, size: usize) -> std::io::Result<()> {
    let path = Path::new(filename);
    let f = OpenOptions::new().write(true).create(create).open(path)?;
    f.set_len(u64::try_from(size).unwrap())
}

/// Truncate files to a size relative to a given file.
///
/// `rfilename` is the name of the reference file.
///
/// `size_string` gives the size relative to the reference file to which
/// to set the target files. For example, "+3K" means "set each file to
/// be three kilobytes larger than the size of the reference file".
///
/// If `create` is true, then each file will be created if it does not
/// already exist.
///
/// # Errors
///
/// If the any file could not be opened, or there was a problem setting
/// the size of at least one file.
fn truncate_reference_and_size(
    rfilename: &str,
    size_string: &str,
    filenames: Vec<String>,
    create: bool,
) -> std::io::Result<()> {
    let mode = match parse_mode_and_size(size_string) {
        Ok(m) => match m {
            TruncateMode::Absolute(_) => {
                crash!(1, "you must specify a relative '--size' with '--reference'")
            }
            _ => m,
        },
        Err(e) => crash!(1, "Invalid number: {}", e.to_string()),
    };
    let fsize = usize::try_from(metadata(rfilename)?.len()).unwrap();
    let tsize = mode.to_size(fsize);
    for filename in &filenames {
        file_truncate(filename, create, tsize)?;
    }
    Ok(())
}

/// Truncate files to match the size of a given reference file.
///
/// `rfilename` is the name of the reference file.
///
/// If `create` is true, then each file will be created if it does not
/// already exist.
///
/// # Errors
///
/// If the any file could not be opened, or there was a problem setting
/// the size of at least one file.
fn truncate_reference_file_only(
    rfilename: &str,
    filenames: Vec<String>,
    create: bool,
) -> std::io::Result<()> {
    let tsize = usize::try_from(metadata(rfilename)?.len()).unwrap();
    for filename in &filenames {
        file_truncate(filename, create, tsize)?;
    }
    Ok(())
}

/// Truncate files to a specified size.
///
/// `size_string` gives either an absolute size or a relative size. A
/// relative size adjusts the size of each file relative to its current
/// size. For example, "3K" means "set each file to be three kilobytes"
/// whereas "+3K" means "set each file to be three kilobytes larger than
/// its current size".
///
/// If `create` is true, then each file will be created if it does not
/// already exist.
///
/// # Errors
///
/// If the any file could not be opened, or there was a problem setting
/// the size of at least one file.
fn truncate_size_only(
    size_string: &str,
    filenames: Vec<String>,
    create: bool,
) -> std::io::Result<()> {
    let mode = match parse_mode_and_size(size_string) {
        Ok(m) => m,
        Err(e) => crash!(1, "Invalid number: {}", e.to_string()),
    };
    for filename in &filenames {
        let fsize = usize::try_from(metadata(filename)?.len()).unwrap();
        let tsize = mode.to_size(fsize);
        file_truncate(filename, create, tsize)?;
    }
    Ok(())
}

fn truncate(
    no_create: bool,
    _: bool,
    reference: Option<String>,
    size: Option<String>,
    filenames: Vec<String>,
) -> std::io::Result<()> {
    let create = !no_create;
    // There are four possibilities
    // - reference file given and size given,
    // - reference file given but no size given,
    // - no reference file given but size given,
    // - no reference file given and no size given,
    match (reference, size) {
        (Some(rfilename), Some(size_string)) => {
            truncate_reference_and_size(&rfilename, &size_string, filenames, create)
        }
        (Some(rfilename), None) => truncate_reference_file_only(&rfilename, filenames, create),
        (None, Some(size_string)) => truncate_size_only(&size_string, filenames, create),
        (None, None) => unreachable!(), // this case cannot happen anymore because it's handled by clap
    }
}

/// Decide whether a character is one of the size modifiers, like '+' or '<'.
fn is_modifier(c: char) -> bool {
    c == '+' || c == '-' || c == '<' || c == '>' || c == '/' || c == '%'
}

/// Parse a size string with optional modifier symbol as its first character.
///
/// A size string is as described in [`parse_size`]. The first character
/// of `size_string` might be a modifier symbol, like `'+'` or
/// `'<'`. The first element of the pair returned by this function
/// indicates which modifier symbol was present, or
/// [`TruncateMode::Absolute`] if none.
///
/// # Panics
///
/// If `size_string` is empty, or if no number could be parsed from the
/// given string (for example, if the string were `"abc"`).
///
/// # Examples
///
/// ```rust,ignore
/// assert_eq!(parse_mode_and_size("+123"), (TruncateMode::Extend, 123));
/// ```
fn parse_mode_and_size(size_string: &str) -> Result<TruncateMode, ParseSizeError> {
    // Trim any whitespace.
    let mut size_string = size_string.trim();

    // Get the modifier character from the size string, if any. For
    // example, if the argument is "+123", then the modifier is '+'.
    if let Some(c) = size_string.chars().next() {
        if is_modifier(c) {
            size_string = &size_string[1..];
        }
        parse_size(size_string).map(match c {
            '+' => TruncateMode::Extend,
            '-' => TruncateMode::Reduce,
            '<' => TruncateMode::AtMost,
            '>' => TruncateMode::AtLeast,
            '/' => TruncateMode::RoundDown,
            '%' => TruncateMode::RoundUp,
            _ => TruncateMode::Absolute,
        })
    } else {
        Err(ParseSizeError::ParseFailure(size_string.to_string()))
    }
}

#[cfg(test)]
mod tests {
    use crate::parse_mode_and_size;
    use crate::TruncateMode;

    #[test]
    fn test_parse_mode_and_size() {
        assert_eq!(parse_mode_and_size("10"), Ok(TruncateMode::Absolute(10)));
        assert_eq!(parse_mode_and_size("+10"), Ok(TruncateMode::Extend(10)));
        assert_eq!(parse_mode_and_size("-10"), Ok(TruncateMode::Reduce(10)));
        assert_eq!(parse_mode_and_size("<10"), Ok(TruncateMode::AtMost(10)));
        assert_eq!(parse_mode_and_size(">10"), Ok(TruncateMode::AtLeast(10)));
        assert_eq!(parse_mode_and_size("/10"), Ok(TruncateMode::RoundDown(10)));
        assert_eq!(parse_mode_and_size("%10"), Ok(TruncateMode::RoundUp(10)));
    }
}
<|endoftext|>


<commit_before>use libc::{c_char, c_int, c_long};
use object::PyObject;

#[cfg_attr(windows, link(name="pythonXY"))] extern "C" {
    pub fn PyImport_GetMagicNumber() -> c_long;
    pub fn PyImport_GetMagicTag() -> *const c_char;
    pub fn PyImport_ExecCodeModule(name: *const c_char,
                                   co: *mut PyObject) -> *mut PyObject;
    pub fn PyImport_ExecCodeModuleEx(name: *const c_char,
                                     co: *mut PyObject,
                                     pathname: *const c_char)
     -> *mut PyObject;
    pub fn PyImport_ExecCodeModuleWithPathnames(name: *const c_char,
                                                co: *mut PyObject,
                                                pathname:
                                                    *const c_char,
                                                cpathname:
                                                    *const c_char)
     -> *mut PyObject;
    pub fn PyImport_ExecCodeModuleObject(name: *mut PyObject,
                                         co: *mut PyObject,
                                         pathname: *mut PyObject,
                                         cpathname: *mut PyObject)
     -> *mut PyObject;
    pub fn PyImport_GetModuleDict() -> *mut PyObject;
    #[cfg(Py_3_7)]
    pub fn PyImport_GetModule(name: *mut PyObject) -> *mut PyObject;
    pub fn PyImport_AddModuleObject(name: *mut PyObject) -> *mut PyObject;
    pub fn PyImport_AddModule(name: *const c_char) -> *mut PyObject;
    pub fn PyImport_ImportModule(name: *const c_char)
     -> *mut PyObject;
    pub fn PyImport_ImportModuleNoBlock(name: *const c_char)
     -> *mut PyObject;
    pub fn PyImport_ImportModuleLevel(name: *const c_char,
                                      globals: *mut PyObject,
                                      locals: *mut PyObject,
                                      fromlist: *mut PyObject,
                                      level: c_int) -> *mut PyObject;
    pub fn PyImport_ImportModuleLevelObject(name: *mut PyObject,
                                            globals: *mut PyObject,
                                            locals: *mut PyObject,
                                            fromlist: *mut PyObject,
                                            level: c_int)
     -> *mut PyObject;
}

#[inline]
pub unsafe fn PyImport_ImportModuleEx(name: *const c_char,
                                      globals: *mut PyObject,
                                      locals: *mut PyObject,
                                      fromlist: *mut PyObject)
  -> *mut PyObject {
    PyImport_ImportModuleLevel(name, globals, locals, fromlist, 0)
}

#[cfg_attr(windows, link(name="pythonXY"))] extern "C" {
    pub fn PyImport_GetImporter(path: *mut PyObject) -> *mut PyObject;
    pub fn PyImport_Import(name: *mut PyObject) -> *mut PyObject;
    pub fn PyImport_ReloadModule(m: *mut PyObject) -> *mut PyObject;
    pub fn PyImport_Cleanup() -> ();
    pub fn PyImport_ImportFrozenModuleObject(name: *mut PyObject)
     -> c_int;
    pub fn PyImport_ImportFrozenModule(name: *const c_char)
     -> c_int;

    // pub static mut PyNullImporter_Type: PyTypeObject; -- does not actually exist in shared library

    pub fn PyImport_AppendInittab(name: *const c_char,
                                  initfunc: Option<extern "C" fn() -> *mut PyObject>)
     -> c_int;
}

<commit_msg>Expose _frozen and PyImport_FrozenModules<commit_after>use libc::{c_char, c_int, c_long, c_uchar};
use object::PyObject;

#[cfg_attr(windows, link(name="pythonXY"))] extern "C" {
    pub fn PyImport_GetMagicNumber() -> c_long;
    pub fn PyImport_GetMagicTag() -> *const c_char;
    pub fn PyImport_ExecCodeModule(name: *const c_char,
                                   co: *mut PyObject) -> *mut PyObject;
    pub fn PyImport_ExecCodeModuleEx(name: *const c_char,
                                     co: *mut PyObject,
                                     pathname: *const c_char)
     -> *mut PyObject;
    pub fn PyImport_ExecCodeModuleWithPathnames(name: *const c_char,
                                                co: *mut PyObject,
                                                pathname:
                                                    *const c_char,
                                                cpathname:
                                                    *const c_char)
     -> *mut PyObject;
    pub fn PyImport_ExecCodeModuleObject(name: *mut PyObject,
                                         co: *mut PyObject,
                                         pathname: *mut PyObject,
                                         cpathname: *mut PyObject)
     -> *mut PyObject;
    pub fn PyImport_GetModuleDict() -> *mut PyObject;
    #[cfg(Py_3_7)]
    pub fn PyImport_GetModule(name: *mut PyObject) -> *mut PyObject;
    pub fn PyImport_AddModuleObject(name: *mut PyObject) -> *mut PyObject;
    pub fn PyImport_AddModule(name: *const c_char) -> *mut PyObject;
    pub fn PyImport_ImportModule(name: *const c_char)
     -> *mut PyObject;
    pub fn PyImport_ImportModuleNoBlock(name: *const c_char)
     -> *mut PyObject;
    pub fn PyImport_ImportModuleLevel(name: *const c_char,
                                      globals: *mut PyObject,
                                      locals: *mut PyObject,
                                      fromlist: *mut PyObject,
                                      level: c_int) -> *mut PyObject;
    pub fn PyImport_ImportModuleLevelObject(name: *mut PyObject,
                                            globals: *mut PyObject,
                                            locals: *mut PyObject,
                                            fromlist: *mut PyObject,
                                            level: c_int)
     -> *mut PyObject;
}

#[inline]
pub unsafe fn PyImport_ImportModuleEx(name: *const c_char,
                                      globals: *mut PyObject,
                                      locals: *mut PyObject,
                                      fromlist: *mut PyObject)
  -> *mut PyObject {
    PyImport_ImportModuleLevel(name, globals, locals, fromlist, 0)
}

#[cfg_attr(windows, link(name="pythonXY"))] extern "C" {
    pub fn PyImport_GetImporter(path: *mut PyObject) -> *mut PyObject;
    pub fn PyImport_Import(name: *mut PyObject) -> *mut PyObject;
    pub fn PyImport_ReloadModule(m: *mut PyObject) -> *mut PyObject;
    pub fn PyImport_Cleanup() -> ();
    pub fn PyImport_ImportFrozenModuleObject(name: *mut PyObject)
     -> c_int;
    pub fn PyImport_ImportFrozenModule(name: *const c_char)
     -> c_int;

    // pub static mut PyNullImporter_Type: PyTypeObject; -- does not actually exist in shared library

    pub fn PyImport_AppendInittab(name: *const c_char,
                                  initfunc: Option<extern "C" fn() -> *mut PyObject>)
     -> c_int;
}

#[repr(C)]
#[derive(Copy, Clone)]
#[cfg(not(Py_LIMITED_API))]
pub struct _frozen {
    pub name: *const c_char,
    pub code: *const c_uchar,
    pub size: c_int,
}

#[cfg(not(Py_LIMITED_API))]
#[cfg_attr(windows, link(name="pythonXY"))]
extern "C" {
    pub static mut PyImport_FrozenModules: *const _frozen;
}
<|endoftext|>


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

use crate::buffer::Buffer;
use crate::layout::Rect;
use crate::style::Style;
use crate::symbols::bar;
use crate::widgets::{Block, Widget};

/// Widget to render a sparkline over one or more lines.
///
/// # Examples
///
/// ```
/// # use tui::widgets::{Block, Borders, Sparkline};
/// # use tui::style::{Style, Color};
/// # fn main() {
/// Sparkline::default()
///     .block(Block::default().title("Sparkline").borders(Borders::ALL))
///     .data(&[0, 2, 3, 4, 1, 4, 10])
///     .max(5)
///     .style(Style::default().fg(Color::Red).bg(Color::White));
/// # }
/// ```
pub struct Sparkline<'a> {
    /// A block to wrap the widget in
    block: Option<Block<'a>>,
    /// Widget style
    style: Style,
    /// A slice of the data to display
    data: &'a [u64],
    /// The maximum value to take to compute the maximum bar height (if nothing is specified, the
    /// widget uses the max of the dataset)
    max: Option<u64>,
}

impl<'a> Default for Sparkline<'a> {
    fn default() -> Sparkline<'a> {
        Sparkline {
            block: None,
            style: Default::default(),
            data: &[],
            max: None,
        }
    }
}

impl<'a> Sparkline<'a> {
    pub fn block(mut self, block: Block<'a>) -> Sparkline<'a> {
        self.block = Some(block);
        self
    }

    pub fn style(mut self, style: Style) -> Sparkline<'a> {
        self.style = style;
        self
    }

    pub fn data(mut self, data: &'a [u64]) -> Sparkline<'a> {
        self.data = data;
        self
    }

    pub fn max(mut self, max: u64) -> Sparkline<'a> {
        self.max = Some(max);
        self
    }
}

impl<'a> Widget for Sparkline<'a> {
    fn draw(&mut self, area: Rect, buf: &mut Buffer) {
        let spark_area = match self.block {
            Some(ref mut b) => {
                b.draw(area, buf);
                b.inner(area)
            }
            None => area,
        };

        if spark_area.height < 1 {
            return;
        }

        let max = match self.max {
            Some(v) => v,
            None => *self.data.iter().max().unwrap_or(&1u64),
        };
        let max_index = min(spark_area.width as usize, self.data.len());
        let mut data = self
            .data
            .iter()
            .take(max_index)
            .map(|e| e * u64::from(spark_area.height) * 8 / max)
            .collect::<Vec<u64>>();
        for j in (0..spark_area.height).rev() {
            for (i, d) in data.iter_mut().enumerate() {
                let symbol = match *d {
                    0 => " ",
                    1 => bar::ONE_EIGHTH,
                    2 => bar::ONE_QUATER,
                    3 => bar::THREE_EIGHTHS,
                    4 => bar::HALF,
                    5 => bar::FIVE_EIGHTHS,
                    6 => bar::THREE_QUATERS,
                    7 => bar::SEVEN_EIGHTHS,
                    _ => bar::FULL,
                };
                buf.get_mut(spark_area.left() + i as u16, spark_area.top() + j)
                    .set_symbol(symbol)
                    .set_fg(self.style.fg)
                    .set_bg(self.style.bg);

                if *d > 8 {
                    *d -= 8;
                } else {
                    *d = 0;
                }
            }
        }
    }
}
<commit_msg>Fix sparkline panic when max is zero<commit_after>use std::cmp::min;

use crate::buffer::Buffer;
use crate::layout::Rect;
use crate::style::Style;
use crate::symbols::bar;
use crate::widgets::{Block, Widget};

/// Widget to render a sparkline over one or more lines.
///
/// # Examples
///
/// ```
/// # use tui::widgets::{Block, Borders, Sparkline};
/// # use tui::style::{Style, Color};
/// # fn main() {
/// Sparkline::default()
///     .block(Block::default().title("Sparkline").borders(Borders::ALL))
///     .data(&[0, 2, 3, 4, 1, 4, 10])
///     .max(5)
///     .style(Style::default().fg(Color::Red).bg(Color::White));
/// # }
/// ```
pub struct Sparkline<'a> {
    /// A block to wrap the widget in
    block: Option<Block<'a>>,
    /// Widget style
    style: Style,
    /// A slice of the data to display
    data: &'a [u64],
    /// The maximum value to take to compute the maximum bar height (if nothing is specified, the
    /// widget uses the max of the dataset)
    max: Option<u64>,
}

impl<'a> Default for Sparkline<'a> {
    fn default() -> Sparkline<'a> {
        Sparkline {
            block: None,
            style: Default::default(),
            data: &[],
            max: None,
        }
    }
}

impl<'a> Sparkline<'a> {
    pub fn block(mut self, block: Block<'a>) -> Sparkline<'a> {
        self.block = Some(block);
        self
    }

    pub fn style(mut self, style: Style) -> Sparkline<'a> {
        self.style = style;
        self
    }

    pub fn data(mut self, data: &'a [u64]) -> Sparkline<'a> {
        self.data = data;
        self
    }

    pub fn max(mut self, max: u64) -> Sparkline<'a> {
        self.max = Some(max);
        self
    }
}

impl<'a> Widget for Sparkline<'a> {
    fn draw(&mut self, area: Rect, buf: &mut Buffer) {
        let spark_area = match self.block {
            Some(ref mut b) => {
                b.draw(area, buf);
                b.inner(area)
            }
            None => area,
        };

        if spark_area.height < 1 {
            return;
        }

        let max = match self.max {
            Some(v) => v,
            None => *self.data.iter().max().unwrap_or(&1u64),
        };
        let max_index = min(spark_area.width as usize, self.data.len());
        let mut data = self
            .data
            .iter()
            .take(max_index)
            .map(|e| {
                if max != 0 {
                    e * u64::from(spark_area.height) * 8 / max
                } else {
                    0
                }
            })
            .collect::<Vec<u64>>();
        for j in (0..spark_area.height).rev() {
            for (i, d) in data.iter_mut().enumerate() {
                let symbol = match *d {
                    0 => " ",
                    1 => bar::ONE_EIGHTH,
                    2 => bar::ONE_QUATER,
                    3 => bar::THREE_EIGHTHS,
                    4 => bar::HALF,
                    5 => bar::FIVE_EIGHTHS,
                    6 => bar::THREE_QUATERS,
                    7 => bar::SEVEN_EIGHTHS,
                    _ => bar::FULL,
                };
                buf.get_mut(spark_area.left() + i as u16, spark_area.top() + j)
                    .set_symbol(symbol)
                    .set_fg(self.style.fg)
                    .set_bg(self.style.bg);

                if *d > 8 {
                    *d -= 8;
                } else {
                    *d = 0;
                }
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn it_does_not_panic_if_max_is_zero() {
        let mut widget = Sparkline::default().data(&[0, 0, 0]);
        let area = Rect::new(0, 0, 3, 1);
        let mut buffer = Buffer::empty(area);
        widget.draw(area, &mut buffer);
    }

    #[test]
    fn it_does_not_panic_if_max_is_set_to_zero() {
        let mut widget = Sparkline::default().data(&[0, 1, 2]).max(0);
        let area = Rect::new(0, 0, 3, 1);
        let mut buffer = Buffer::empty(area);
        widget.draw(area, &mut buffer);
    }
}
<|endoftext|>


<commit_before>use std::time::SystemTime;

use anyhow::Context;
use aws_sigv4::{
    http_request::{self, SignableRequest, SigningSettings},
    SigningParams,
};
use reqwest::blocking::Client;
use retry::delay::{jitter, Exponential};
use retry::OperationResult;

#[derive(Clone)]
pub struct CloudFront {
    distribution_id: String,
    access_key: String,
    secret_key: String,
}

impl CloudFront {
    pub fn from_environment() -> Option<Self> {
        let distribution_id = dotenv::var("CLOUDFRONT_DISTRIBUTION").ok()?;
        let access_key = dotenv::var("AWS_ACCESS_KEY").expect("missing AWS_ACCESS_KEY");
        let secret_key = dotenv::var("AWS_SECRET_KEY").expect("missing AWS_SECRET_KEY");
        Some(Self {
            distribution_id,
            access_key,
            secret_key,
        })
    }

    /// Invalidate a file on CloudFront
    ///
    /// `path` is the path to the file to invalidate, such as `config.json`, or `re/ge/regex`
    pub fn invalidate(&self, client: &Client, path: &str) -> anyhow::Result<()> {
        let path = path.trim_start_matches('/');
        let url = format!(
            "https://cloudfront.amazonaws.com/2020-05-31/distribution/{}/invalidation",
            self.distribution_id
        );

        let backoff = Exponential::from_millis(1000).map(jitter).take(2);
        retry::retry_with_index(backoff, |attempt| {
            if attempt > 1 {
                debug!(?attempt, "Retrying CloudFront invalidation…")
            }

            let now = chrono::offset::Utc::now().timestamp_micros();
            let body = format!(
                r#"
<?xml version="1.0" encoding="UTF-8"?>
<InvalidationBatch xmlns="http://cloudfront.amazonaws.com/doc/2020-05-31/">
    <CallerReference>{now}</CallerReference>
    <Paths>
        <Items>
            <Path>/{path}</Path>
        </Items>
        <Quantity>1</Quantity>
    </Paths>
</InvalidationBatch>
"#
            );

            let request = match http::Request::post(&url)
                .body(&body)
                .context("Failed to construct HTTP request")
            {
                Ok(request) => request,
                Err(error) => return OperationResult::Err(error),
            };

            let request = SignableRequest::from(&request);
            let params = SigningParams::builder()
                .access_key(&self.access_key)
                .secret_key(&self.secret_key)
                .region("us-east-1") // cloudfront is a regionless service, use the default region for signing.
                .service_name("cloudfront")
                .settings(SigningSettings::default())
                .time(SystemTime::now())
                .build()
                .unwrap(); // all required fields are set

            let (mut signature_headers, _) =
                http_request::sign(request, &params).unwrap().into_parts();

            let response = match client
                .post(&url)
                .headers(signature_headers.take_headers().unwrap_or_default())
                .body(body)
                .send()
                .context("Failed to send invalidation request")
            {
                Ok(response) => response,
                Err(error) => return OperationResult::Retry(error),
            };

            let status = response.status();

            let result = match response.error_for_status_ref() {
                Ok(_) => Ok(()),
                Err(error) => {
                    let headers = format!("{:?}", response.headers());
                    let body = response
                        .text()
                        .unwrap_or_else(|_| "Failed to decode response payload".to_string());

                    Err(error).context(format!("{headers}; body: {body}"))
                }
            };

            match result {
                Ok(_) => OperationResult::Ok(()),
                Err(error) if status.is_server_error() => OperationResult::Retry(error),
                Err(error) => OperationResult::Err(error),
            }
        })
        .map_err(|error| error.error)
    }
}
<commit_msg>cloudfront: Improve logging (#5489)<commit_after>use std::time::SystemTime;

use anyhow::Context;
use aws_sigv4::{
    http_request::{self, SignableRequest, SigningSettings},
    SigningParams,
};
use reqwest::blocking::Client;
use retry::delay::{jitter, Exponential};
use retry::OperationResult;

#[derive(Clone)]
pub struct CloudFront {
    distribution_id: String,
    access_key: String,
    secret_key: String,
}

impl CloudFront {
    pub fn from_environment() -> Option<Self> {
        let distribution_id = dotenv::var("CLOUDFRONT_DISTRIBUTION").ok()?;
        let access_key = dotenv::var("AWS_ACCESS_KEY").expect("missing AWS_ACCESS_KEY");
        let secret_key = dotenv::var("AWS_SECRET_KEY").expect("missing AWS_SECRET_KEY");
        Some(Self {
            distribution_id,
            access_key,
            secret_key,
        })
    }

    /// Invalidate a file on CloudFront
    ///
    /// `path` is the path to the file to invalidate, such as `config.json`, or `re/ge/regex`
    #[instrument(skip(self, client))]
    pub fn invalidate(&self, client: &Client, path: &str) -> anyhow::Result<()> {
        let path = path.trim_start_matches('/');
        let url = format!(
            "https://cloudfront.amazonaws.com/2020-05-31/distribution/{}/invalidation",
            self.distribution_id
        );
        trace!(?url);

        let backoff = Exponential::from_millis(1000).map(jitter).take(2);
        retry::retry_with_index(backoff, |attempt| {
            let now = chrono::offset::Utc::now().timestamp_micros();
            let body = format!(
                r#"
<?xml version="1.0" encoding="UTF-8"?>
<InvalidationBatch xmlns="http://cloudfront.amazonaws.com/doc/2020-05-31/">
    <CallerReference>{now}</CallerReference>
    <Paths>
        <Items>
            <Path>/{path}</Path>
        </Items>
        <Quantity>1</Quantity>
    </Paths>
</InvalidationBatch>
"#
            );
            trace!(?body);

            let request = match http::Request::post(&url)
                .body(&body)
                .context("Failed to construct HTTP request")
            {
                Ok(request) => request,
                Err(error) => return OperationResult::Err(error),
            };

            trace!("Signing invalidation request");
            let request = SignableRequest::from(&request);
            let params = SigningParams::builder()
                .access_key(&self.access_key)
                .secret_key(&self.secret_key)
                .region("us-east-1") // cloudfront is a regionless service, use the default region for signing.
                .service_name("cloudfront")
                .settings(SigningSettings::default())
                .time(SystemTime::now())
                .build()
                .unwrap(); // all required fields are set

            let (mut signature_headers, _) =
                http_request::sign(request, &params).unwrap().into_parts();

            debug!(?attempt, "Sending invalidation request");
            let response = match client
                .post(&url)
                .headers(signature_headers.take_headers().unwrap_or_default())
                .body(body)
                .send()
                .context("Failed to send invalidation request")
            {
                Ok(response) => response,
                Err(error) => return OperationResult::Retry(error),
            };

            let status = response.status();

            let result = match response.error_for_status_ref() {
                Ok(_) => {
                    debug!(?status, "Invalidation request successful");
                    Ok(())
                }
                Err(error) => {
                    let headers = response.headers().clone();
                    let body = response.text();
                    debug!(?status, ?headers, ?body, "Invalidation request failed");

                    Err(error).with_context(|| format!("Failed to invalidate {path}"))
                }
            };

            match result {
                Ok(_) => OperationResult::Ok(()),
                Err(error) if status.is_server_error() => OperationResult::Retry(error),
                Err(error) => OperationResult::Err(error),
            }
        })
        .map_err(|error| error.error)
    }
}
<|endoftext|>


<commit_before>use std::cell::RefCell;

use cairo::{self, ImageSurface};
use nalgebra::{Matrix3, Matrix4x5, Matrix5, Vector5};

use attributes::Attribute;
use drawing_ctx::DrawingCtx;
use error::NodeError;
use handle::RsvgHandle;
use node::{NodeResult, NodeTrait, RsvgNode};
use parsers::{self, ListLength, NumberListError, ParseError};
use property_bag::PropertyBag;
use surface_utils::{
    iterators::Pixels,
    shared_surface::SharedImageSurface,
    ImageSurfaceDataExt,
    Pixel,
};
use util::clamp;

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

/// Color matrix operation types.
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
enum OperationType {
    Matrix,
    Saturate,
    HueRotate,
    LuminanceToAlpha,
}

/// The `feColorMatrix` filter primitive.
pub struct ColorMatrix {
    base: PrimitiveWithInput,
    matrix: RefCell<Matrix5<f64>>,
}

impl ColorMatrix {
    /// Constructs a new `ColorMatrix` with empty properties.
    #[inline]
    pub fn new() -> ColorMatrix {
        ColorMatrix {
            base: PrimitiveWithInput::new::<Self>(),
            matrix: RefCell::new(Matrix5::identity()),
        }
    }
}

impl NodeTrait for ColorMatrix {
    fn set_atts(
        &self,
        node: &RsvgNode,
        handle: *const RsvgHandle,
        pbag: &PropertyBag<'_>,
    ) -> NodeResult {
        self.base.set_atts(node, handle, pbag)?;

        // First, determine the operation type.
        let mut operation_type = OperationType::Matrix;
        for (attr, value) in pbag.iter().filter(|(attr, _)| *attr == Attribute::Type) {
            operation_type = OperationType::parse(attr, value)?;
        }

        // Now read the matrix correspondingly.
        // LuminanceToAlpha doesn't accept any matrix.
        if operation_type == OperationType::LuminanceToAlpha {
            #[cfg_attr(rustfmt, rustfmt_skip)]
            self.matrix.replace(
                Matrix5::new(
                    0.0,    0.0,    0.0,    0.0, 0.0,
                    0.0,    0.0,    0.0,    0.0, 0.0,
                    0.0,    0.0,    0.0,    0.0, 0.0,
                    0.2125, 0.7154, 0.0721, 0.0, 0.0,
                    0.0,    0.0,    0.0,    0.0, 1.0,
                ),
            );
        } else {
            for (attr, value) in pbag
                .iter()
                .filter(|(attr, _)| *attr == Attribute::Values)
            {
                let new_matrix = match operation_type {
                    OperationType::LuminanceToAlpha => unreachable!(),
                    OperationType::Matrix => {
                        let matrix = Matrix4x5::from_row_slice(
                            &parsers::number_list_from_str(value, ListLength::Exact(20)).map_err(
                                |err| {
                                    NodeError::parse_error(
                                        attr,
                                        match err {
                                            NumberListError::IncorrectNumberOfElements => {
                                                ParseError::new(
                                                    "incorrect number of elements: expected 20",
                                                )
                                            }
                                            NumberListError::Parse(err) => err,
                                        },
                                    )
                                },
                            )?,
                        );

                        let mut matrix = matrix.fixed_resize(0.0);
                        matrix[(4, 4)] = 1.0;
                        matrix
                    }
                    OperationType::Saturate => {
                        let s = parsers::number(value)
                            .map_err(|err| NodeError::attribute_error(attr, err))?;
                        if s < 0.0 || s > 1.0 {
                            return Err(NodeError::value_error(attr, "expected value from 0 to 1"));
                        }

                        #[cfg_attr(rustfmt, rustfmt_skip)]
                        Matrix5::new(
                            0.213 + 0.787 * s, 0.715 - 0.715 * s, 0.072 - 0.072 * s, 0.0, 0.0,
                            0.213 - 0.213 * s, 0.715 + 0.285 * s, 0.072 - 0.072 * s, 0.0, 0.0,
                            0.213 - 0.213 * s, 0.715 - 0.715 * s, 0.072 + 0.928 * s, 0.0, 0.0,
                            0.0,               0.0,               0.0,               1.0, 0.0,
                            0.0,               0.0,               0.0,               0.0, 1.0,
                        )
                    }
                    OperationType::HueRotate => {
                        let degrees = parsers::number(value)
                            .map_err(|err| NodeError::attribute_error(attr, err))?;

                        let (sin, cos) = degrees.to_radians().sin_cos();

                        #[cfg_attr(rustfmt, rustfmt_skip)]
                        let a = Matrix3::new(
                            0.213, 0.715, 0.072,
                            0.213, 0.715, 0.072,
                            0.213, 0.715, 0.072,
                        );

                        #[cfg_attr(rustfmt, rustfmt_skip)]
                        let b = Matrix3::new(
                             0.787, -0.715, -0.072,
                            -0.213,  0.285, -0.072,
                            -0.213, -0.715,  0.928,
                        );

                        #[cfg_attr(rustfmt, rustfmt_skip)]
                        let c = Matrix3::new(
                            -0.213, -0.715,  0.928,
                             0.143,  0.140, -0.283,
                            -0.787,  0.715,  0.072,
                        );

                        let top_left = a + b * cos + c * sin;

                        let mut matrix = top_left.fixed_resize(0.0);
                        matrix[(3, 3)] = 1.0;
                        matrix[(4, 4)] = 1.0;
                        matrix
                    }
                };

                self.matrix.replace(new_matrix);
            }
        }

        Ok(())
    }
}

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

        let matrix = &*self.matrix.borrow();

        let mut output_surface = ImageSurface::create(
            cairo::Format::ARgb32,
            ctx.source_graphic().width(),
            ctx.source_graphic().height(),
        )?;

        let output_stride = output_surface.get_stride() as usize;
        {
            let mut output_data = output_surface.get_data().unwrap();

            for (x, y, pixel) in Pixels::new(input.surface(), bounds) {
                let alpha = f64::from(pixel.a) / 255f64;

                let pixel_vec = if alpha == 0.0 {
                    Vector5::new(0.0, 0.0, 0.0, 0.0, 1.0)
                } else {
                    Vector5::new(
                        f64::from(pixel.r) / 255f64 / alpha,
                        f64::from(pixel.g) / 255f64 / alpha,
                        f64::from(pixel.b) / 255f64 / alpha,
                        alpha,
                        1.0,
                    )
                };
                let mut new_pixel_vec = Vector5::zeros();
                matrix.mul_to(&pixel_vec, &mut new_pixel_vec);

                let new_alpha = clamp(new_pixel_vec[3], 0.0, 1.0);

                let premultiply = |x: f64| ((clamp(x, 0.0, 1.0) * new_alpha * 255f64) + 0.5) as u8;

                let output_pixel = Pixel {
                    r: premultiply(new_pixel_vec[0]),
                    g: premultiply(new_pixel_vec[1]),
                    b: premultiply(new_pixel_vec[2]),
                    a: ((new_alpha * 255f64) + 0.5) as u8,
                };

                output_data.set_pixel(output_stride, output_pixel, x, y);
            }
        }

        Ok(FilterResult {
            name: self.base.result.borrow().clone(),
            output: FilterOutput {
                surface: SharedImageSurface::new(output_surface, input.surface().surface_type())?,
                bounds,
            },
        })
    }

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

impl OperationType {
    fn parse(attr: Attribute, s: &str) -> Result<Self, NodeError> {
        match s {
            "matrix" => Ok(OperationType::Matrix),
            "saturate" => Ok(OperationType::Saturate),
            "hueRotate" => Ok(OperationType::HueRotate),
            "luminanceToAlpha" => Ok(OperationType::LuminanceToAlpha),
            _ => Err(NodeError::parse_error(
                attr,
                ParseError::new("invalid value"),
            )),
        }
    }
}
<commit_msg>rustfmt<commit_after>use std::cell::RefCell;

use cairo::{self, ImageSurface};
use nalgebra::{Matrix3, Matrix4x5, Matrix5, Vector5};

use attributes::Attribute;
use drawing_ctx::DrawingCtx;
use error::NodeError;
use handle::RsvgHandle;
use node::{NodeResult, NodeTrait, RsvgNode};
use parsers::{self, ListLength, NumberListError, ParseError};
use property_bag::PropertyBag;
use surface_utils::{
    iterators::Pixels,
    shared_surface::SharedImageSurface,
    ImageSurfaceDataExt,
    Pixel,
};
use util::clamp;

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

/// Color matrix operation types.
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
enum OperationType {
    Matrix,
    Saturate,
    HueRotate,
    LuminanceToAlpha,
}

/// The `feColorMatrix` filter primitive.
pub struct ColorMatrix {
    base: PrimitiveWithInput,
    matrix: RefCell<Matrix5<f64>>,
}

impl ColorMatrix {
    /// Constructs a new `ColorMatrix` with empty properties.
    #[inline]
    pub fn new() -> ColorMatrix {
        ColorMatrix {
            base: PrimitiveWithInput::new::<Self>(),
            matrix: RefCell::new(Matrix5::identity()),
        }
    }
}

impl NodeTrait for ColorMatrix {
    fn set_atts(
        &self,
        node: &RsvgNode,
        handle: *const RsvgHandle,
        pbag: &PropertyBag<'_>,
    ) -> NodeResult {
        self.base.set_atts(node, handle, pbag)?;

        // First, determine the operation type.
        let mut operation_type = OperationType::Matrix;
        for (attr, value) in pbag.iter().filter(|(attr, _)| *attr == Attribute::Type) {
            operation_type = OperationType::parse(attr, value)?;
        }

        // Now read the matrix correspondingly.
        // LuminanceToAlpha doesn't accept any matrix.
        if operation_type == OperationType::LuminanceToAlpha {
            #[cfg_attr(rustfmt, rustfmt_skip)]
            self.matrix.replace(
                Matrix5::new(
                    0.0,    0.0,    0.0,    0.0, 0.0,
                    0.0,    0.0,    0.0,    0.0, 0.0,
                    0.0,    0.0,    0.0,    0.0, 0.0,
                    0.2125, 0.7154, 0.0721, 0.0, 0.0,
                    0.0,    0.0,    0.0,    0.0, 1.0,
                ),
            );
        } else {
            for (attr, value) in pbag.iter().filter(|(attr, _)| *attr == Attribute::Values) {
                let new_matrix = match operation_type {
                    OperationType::LuminanceToAlpha => unreachable!(),
                    OperationType::Matrix => {
                        let matrix = Matrix4x5::from_row_slice(
                            &parsers::number_list_from_str(value, ListLength::Exact(20)).map_err(
                                |err| {
                                    NodeError::parse_error(
                                        attr,
                                        match err {
                                            NumberListError::IncorrectNumberOfElements => {
                                                ParseError::new(
                                                    "incorrect number of elements: expected 20",
                                                )
                                            }
                                            NumberListError::Parse(err) => err,
                                        },
                                    )
                                },
                            )?,
                        );

                        let mut matrix = matrix.fixed_resize(0.0);
                        matrix[(4, 4)] = 1.0;
                        matrix
                    }
                    OperationType::Saturate => {
                        let s = parsers::number(value)
                            .map_err(|err| NodeError::attribute_error(attr, err))?;
                        if s < 0.0 || s > 1.0 {
                            return Err(NodeError::value_error(attr, "expected value from 0 to 1"));
                        }

                        #[cfg_attr(rustfmt, rustfmt_skip)]
                        Matrix5::new(
                            0.213 + 0.787 * s, 0.715 - 0.715 * s, 0.072 - 0.072 * s, 0.0, 0.0,
                            0.213 - 0.213 * s, 0.715 + 0.285 * s, 0.072 - 0.072 * s, 0.0, 0.0,
                            0.213 - 0.213 * s, 0.715 - 0.715 * s, 0.072 + 0.928 * s, 0.0, 0.0,
                            0.0,               0.0,               0.0,               1.0, 0.0,
                            0.0,               0.0,               0.0,               0.0, 1.0,
                        )
                    }
                    OperationType::HueRotate => {
                        let degrees = parsers::number(value)
                            .map_err(|err| NodeError::attribute_error(attr, err))?;

                        let (sin, cos) = degrees.to_radians().sin_cos();

                        #[cfg_attr(rustfmt, rustfmt_skip)]
                        let a = Matrix3::new(
                            0.213, 0.715, 0.072,
                            0.213, 0.715, 0.072,
                            0.213, 0.715, 0.072,
                        );

                        #[cfg_attr(rustfmt, rustfmt_skip)]
                        let b = Matrix3::new(
                             0.787, -0.715, -0.072,
                            -0.213,  0.285, -0.072,
                            -0.213, -0.715,  0.928,
                        );

                        #[cfg_attr(rustfmt, rustfmt_skip)]
                        let c = Matrix3::new(
                            -0.213, -0.715,  0.928,
                             0.143,  0.140, -0.283,
                            -0.787,  0.715,  0.072,
                        );

                        let top_left = a + b * cos + c * sin;

                        let mut matrix = top_left.fixed_resize(0.0);
                        matrix[(3, 3)] = 1.0;
                        matrix[(4, 4)] = 1.0;
                        matrix
                    }
                };

                self.matrix.replace(new_matrix);
            }
        }

        Ok(())
    }
}

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

        let matrix = &*self.matrix.borrow();

        let mut output_surface = ImageSurface::create(
            cairo::Format::ARgb32,
            ctx.source_graphic().width(),
            ctx.source_graphic().height(),
        )?;

        let output_stride = output_surface.get_stride() as usize;
        {
            let mut output_data = output_surface.get_data().unwrap();

            for (x, y, pixel) in Pixels::new(input.surface(), bounds) {
                let alpha = f64::from(pixel.a) / 255f64;

                let pixel_vec = if alpha == 0.0 {
                    Vector5::new(0.0, 0.0, 0.0, 0.0, 1.0)
                } else {
                    Vector5::new(
                        f64::from(pixel.r) / 255f64 / alpha,
                        f64::from(pixel.g) / 255f64 / alpha,
                        f64::from(pixel.b) / 255f64 / alpha,
                        alpha,
                        1.0,
                    )
                };
                let mut new_pixel_vec = Vector5::zeros();
                matrix.mul_to(&pixel_vec, &mut new_pixel_vec);

                let new_alpha = clamp(new_pixel_vec[3], 0.0, 1.0);

                let premultiply = |x: f64| ((clamp(x, 0.0, 1.0) * new_alpha * 255f64) + 0.5) as u8;

                let output_pixel = Pixel {
                    r: premultiply(new_pixel_vec[0]),
                    g: premultiply(new_pixel_vec[1]),
                    b: premultiply(new_pixel_vec[2]),
                    a: ((new_alpha * 255f64) + 0.5) as u8,
                };

                output_data.set_pixel(output_stride, output_pixel, x, y);
            }
        }

        Ok(FilterResult {
            name: self.base.result.borrow().clone(),
            output: FilterOutput {
                surface: SharedImageSurface::new(output_surface, input.surface().surface_type())?,
                bounds,
            },
        })
    }

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

impl OperationType {
    fn parse(attr: Attribute, s: &str) -> Result<Self, NodeError> {
        match s {
            "matrix" => Ok(OperationType::Matrix),
            "saturate" => Ok(OperationType::Saturate),
            "hueRotate" => Ok(OperationType::HueRotate),
            "luminanceToAlpha" => Ok(OperationType::LuminanceToAlpha),
            _ => Err(NodeError::parse_error(
                attr,
                ParseError::new("invalid value"),
            )),
        }
    }
}
<|endoftext|>


<commit_before>// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you 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.

//! Query optimizer traits

use crate::error::Result;
use crate::logical_plan::LogicalPlan;

/// An optimizer rules performs a transformation on a logical plan to produce an optimized
/// logical plan.
pub trait OptimizerRule {
    /// Perform optimizations on the plan
    fn optimize(&self, plan: &LogicalPlan) -> Result<LogicalPlan>;
    /// Produce a human readable name for this optimizer rule
    fn name(&self) -> &str;
}
<commit_msg>ARROW-11692: [Rust][DataFusion] Improve OptimizerRule comments<commit_after>// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you 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.

//! Query optimizer traits

use crate::error::Result;
use crate::logical_plan::LogicalPlan;

/// `OptimizerRule` transforms one ['LogicalPlan'] into another which
/// computes the same results, but in a potentially more efficient
/// way.
pub trait OptimizerRule {
    /// Rewrite `plan` to an optimized form
    fn optimize(&self, plan: &LogicalPlan) -> Result<LogicalPlan>;

    /// A human readable name for this optimizer rule
    fn name(&self) -> &str;
}
<|endoftext|>