<commit_before>extern crate num;

use ast::AST;
use environment::{IEnvironment, Environment};
use std::collections::HashMap;
use helpers::ast_to_operator;
use self::num::traits::Num;

pub struct Evaluator<'a> {
    env: &'a mut IEnvironment,
}

impl<'a> Evaluator<'a> {
    pub fn new(env: &'a mut IEnvironment) -> Self {
        Evaluator { env: env }
    }

    fn evaluate_arithmetic(&mut self,
                           operator: &str,
                           child1: AST,
                           child2: AST)
                           -> Result<AST, String> {
        let result1 = self.evaluate(child1);
        let result2 = self.evaluate(child2);

        match result1 {
            Ok(AST::Number(val)) => {
                let mut param1: i32 = val;

                match result2 {
                    Ok(AST::Decimal(param2)) =>
                        Ok(AST::Decimal(apply_arithmetic_operator(operator, param1 as f64, param2))),
                    Ok(AST::Number(param2)) =>
                        Ok(AST::Number(apply_arithmetic_operator(operator, param1, param2))),
                    _ => Err("Right hand result is not a numeric value".to_string()),
                }
            }
            Ok(AST::Decimal(val)) => {
                let mut param1: f64 = val;

                match result2 {
                    Ok(AST::Number(param2)) =>
                        Ok(AST::Decimal(apply_arithmetic_operator(operator, param1, param2 as f64))),
                    Ok(AST::Decimal(param2)) =>
                        Ok(AST::Decimal(apply_arithmetic_operator(operator, param1, param2))),
                    _ => Err("Right hand result is not a numeric value".to_string()),
                }
            }
            _ => Err("Left hand result is not a numeric value".to_string()),
        }
    }

    fn evaluate_set_variable(&mut self,
                             operator: &str,
                             child1: AST,
                             child2: AST)
                             -> Result<AST, String> {
        let result = self.evaluate(child2);

        match child1 {
            AST::Variable(name) => {
                match result {
                    Ok(value) => {
                        match operator {
                            ":=" => self.env.set(name, value.clone()),
                            "=" => self.env.assign(name, value.clone()),
                            _ =>
                                return Err("Variable setting operator not implemented".to_string()),
                        }
                        Ok(value)
                    }
                    err @ Err(_) => err,
                }
            }
            _ => Err("Left hand result must be a variable".to_string()),
        }
    }

    fn evaluate_equality(&mut self,
                         operator: &str,
                         child1: AST,
                         child2: AST)
                         -> Result<AST, String> {
        let result1 = self.evaluate(child1).unwrap_or(AST::None);
        let result2 = self.evaluate(child2).unwrap_or(AST::None);

        match operator {
            "==" => {
                if result1.eq(&result2) {
                    Ok(AST::Number(1))
                } else {
                    Ok(AST::Number(0))
                }
            }
            "!=" => {
                if result1.eq(&result2) {
                    Ok(AST::Number(1))
                } else {
                    Ok(AST::Number(0))
                }
            }
            _ => Err("Equality operator not implemented".to_string()),
        }
    }

    /// evaluate evaluates the given AST and returns an AST
    /// of the result
    pub fn evaluate(&mut self, ast: AST) -> Result<AST, String> {
        let op = ast_to_operator(&ast);
        match ast {
            AST::Variable(name) => match self.env.get(name) {
                Some(val) => Ok(val.clone()),
                None => Err("Variable is not in environment".to_string()),
            },

            ast @ AST::Number(_) | ast @ AST::String(_) | ast @ AST::Decimal(_) => Ok(ast),

            AST::Assign(box child1, box child2) |
            AST::Declare(box child1, box child2) =>
                self.evaluate_set_variable(op.as_str(), child1, child2),

            AST::Equal(box child1, box child2) |
            AST::NotEqual(box child1, box child2) =>
                self.evaluate_equality(op.as_str(), child1, child2),

            AST::Plus(box child1, box child2) |
            AST::Minus(box child1, box child2) |
            AST::Times(box child1, box child2) |
            AST::Divide(box child1, box child2) |
            AST::Modulo(box child1, box child2) =>
                self.evaluate_arithmetic(op.as_str(), child1, child2),
            _ => Err("Operation is not implemented yet".to_string()),
        }
    }
}

fn apply_arithmetic_operator<T: Num>(operator: &str, a: T, b: T) -> T {
    match operator {
        "+" => a + b,
        "-" => a - b,
        "*" => a * b,
        "/" => a / b,
        "%" => a % b,
        _ => a + b,
    }
}

#[test]
fn test_arith_ast() {
    // Test that the result for ast:
    //      *
    //    /   \
    //   5     +
    //       /   \
    //      -     6
    //    /   \
    //   3     2
    // is "35"

    let mut env = Environment::new();
    let mut evaluator = Evaluator::new(&mut env);

    let ast = AST::Times(box AST::Number(5),
                         box AST::Plus(box AST::Minus(box AST::Number(3), box AST::Number(2)),
                                       box AST::Number(6)));

    let result = evaluator.evaluate(ast);

    assert_eq!(result, Ok(AST::Number(35)));
}

#[test]
fn test_variable_ast() {
    // Test that the result for ast:
    //      *
    //    /   \
    //   a     +
    //       /   \
    //      -     d
    //    /   \
    //   b     c
    // is "35" when a is 5, b is 3, c is 2, and d is 6

    let mut env = Environment::new();
    env.set("a".to_string(), AST::Number(5));
    env.set("b".to_string(), AST::Number(3));
    env.set("c".to_string(), AST::Number(2));
    env.set("d".to_string(), AST::Number(6));

    let mut evaluator = Evaluator::new(&mut env);

    let ast = AST::Times(box AST::Variable("a".to_string()),
                         box AST::Plus(box AST::Minus(box AST::Variable("b".to_string()),
                                                      box AST::Variable("c".to_string())),
                                       box AST::Variable("d".to_string())));
    let result = evaluator.evaluate(ast);

    assert_eq!(result, Ok(AST::Number(35)));
}

#[test]
fn test_float_ast() {
    // Test that the result for ast:
    //      *
    //    /   \
    //   a     +
    //       /   \
    //      -     c
    //    /   \
    //   1.5   b
    // is "27.5" when a is 5, b is 2, and c is 6

    let mut env = Environment::new();
    env.set("a".to_string(), AST::Number(5));
    env.set("b".to_string(), AST::Number(2));
    env.set("c".to_string(), AST::Number(6));

    let mut evaluator = Evaluator::new(&mut env);

    let ast = AST::Times(box AST::Variable("a".to_string()),
                         box AST::Plus(box AST::Minus(box AST::Decimal(1.5),
                                                      box AST::Variable("b".to_string())),
                                       box AST::Variable("c".to_string())));
    let result = evaluator.evaluate(ast);

    assert_eq!(result, Ok(AST::Decimal(27.5)));
}

#[test]
fn test_declare_assign() {
    // Test that evaluating ast:
    //     :=
    //    /  \
    //   x    *
    //       / \
    //      10  +
    //         / \
    //        2  3
    // results in x being bound to 50 in the current scope
    // then after pushing a new scope and evaluating ast:
    //    =
    //   / \
    //  x   +
    //     / \
    //    1   2
    // results in x being set to 3 in the original scope

    let mut env = Environment::new();
    let mut evaluator = Evaluator::new(&mut env);
    let ast = AST::Declare(box AST::Variable("x".to_string()),
                           box AST::Times(box AST::Number(10),
                                          box AST::Plus(box AST::Number(2), box AST::Number(3))));
    let result = evaluator.evaluate(ast);
    assert_eq!(result, Ok(AST::Number(50)));
    assert_eq!(evaluator.env.get("x".to_string()), Some(&AST::Number(50)));

    evaluator.env.push();

    let ast = AST::Assign(box AST::Variable("x".to_string()),
                          box AST::Plus(box AST::Number(1), box AST::Number(2)));

    let result = evaluator.evaluate(ast);
    assert_eq!(result, Ok(AST::Number(3)));

    evaluator.env.pop();
    assert_eq!(evaluator.env.get("x".to_string()), Some(&AST::Number(3)));
}

#[test]
fn test_equality() {
    let mut env = Environment::new();
    let mut evaluator = Evaluator::new(&mut env);

    // Test number equality

    let ast = AST::Equal(box AST::Number(5), box AST::Number(5));
    let result = evaluator.evaluate(ast);
    assert_eq!(result, Ok(AST::Number(1)));

    let ast = AST::Equal(box AST::Number(5), box AST::Number(4));
    let result = evaluator.evaluate(ast);
    assert_eq!(result, Ok(AST::Number(0)));

    // Test decimal equality

    let ast = AST::Equal(box AST::Decimal(2.56), box AST::Decimal(2.56));
    let result = evaluator.evaluate(ast);
    assert_eq!(result, Ok(AST::Number(1)));

    let ast = AST::Equal(box AST::Decimal(2.56), box AST::Decimal(2.55));
    let result = evaluator.evaluate(ast);
    assert_eq!(result, Ok(AST::Number(0)));

    // Test string equality

    let ast = AST::Equal(box AST::String("Hello".to_string()), box AST::String("Hello".to_string()));
    let result = evaluator.evaluate(ast);
    assert_eq!(result, Ok(AST::Number(1)));

    let ast = AST::Equal(box AST::String("Hello".to_string()), box AST::String("hello".to_string()));
    let result = evaluator.evaluate(ast);
    assert_eq!(result, Ok(AST::Number(0)));
}
<commit_msg>Added and and or operator evaluation<commit_after>extern crate num;

use ast::AST;
use environment::{IEnvironment, Environment};
use std::collections::HashMap;
use helpers::ast_to_operator;
use self::num::traits::Num;

pub struct Evaluator<'a> {
    env: &'a mut IEnvironment,
}

impl<'a> Evaluator<'a> {
    pub fn new(env: &'a mut IEnvironment) -> Self {
        Evaluator { env: env }
    }

    fn evaluate_arithmetic(&mut self,
                           operator: &str,
                           child1: AST,
                           child2: AST)
                           -> Result<AST, String> {
        let result1 = self.evaluate(child1);
        let result2 = self.evaluate(child2);

        match result1 {
            Ok(AST::Number(val)) => {
                let mut param1: i32 = val;

                match result2 {
                    Ok(AST::Decimal(param2)) =>
                        Ok(AST::Decimal(apply_arithmetic_operator(operator, param1 as f64, param2))),
                    Ok(AST::Number(param2)) =>
                        Ok(AST::Number(apply_arithmetic_operator(operator, param1, param2))),
                    _ => Err("Right hand result is not a numeric value".to_string()),
                }
            }
            Ok(AST::Decimal(val)) => {
                let mut param1: f64 = val;

                match result2 {
                    Ok(AST::Number(param2)) =>
                        Ok(AST::Decimal(apply_arithmetic_operator(operator, param1, param2 as f64))),
                    Ok(AST::Decimal(param2)) =>
                        Ok(AST::Decimal(apply_arithmetic_operator(operator, param1, param2))),
                    _ => Err("Right hand result is not a numeric value".to_string()),
                }
            }
            _ => Err("Left hand result is not a numeric value".to_string()),
        }
    }

    fn evaluate_set_variable(&mut self,
                             operator: &str,
                             child1: AST,
                             child2: AST)
                             -> Result<AST, String> {
        let result = self.evaluate(child2).unwrap_or(AST::None);

        match child1 {
            AST::Variable(name) => {
                if result == AST::None {
                    return Ok(result);
                }

                if operator == ":=" {
                    self.env.set(name, result.clone());
                } else if operator == "=" {
                    self.env.assign(name, result.clone());
                } else {
                    return Err("Variable setting operator not implemented".to_string());
                }

                Ok(result)
            }
            _ => Err("Left hand result must be a variable".to_string()),
        }
    }

    fn evaluate_equality(&mut self,
                         operator: &str,
                         child1: AST,
                         child2: AST)
                         -> Result<AST, String> {
        let result1 = self.evaluate(child1).unwrap_or(AST::None);
        let result2 = self.evaluate(child2).unwrap_or(AST::None);

        match operator {
            "==" => {
                if result1.eq(&result2) {
                    Ok(AST::Number(1))
                } else {
                    Ok(AST::Number(0))
                }
            }
            "!=" => {
                if result1.eq(&result2) {
                    Ok(AST::Number(1))
                } else {
                    Ok(AST::Number(0))
                }
            }
            _ => Err("Equality operator not implemented".to_string()),
        }
    }

    fn evaluate_boolean(&mut self,
                        operator: &str,
                        child1: AST,
                        child2: AST)
                        -> Result<AST, String> {
        let result1 = self.evaluate(child1).unwrap_or(AST::None);

        match result1 {
            AST::Number(val1) => {
                if val1 == 0 && operator == "&&" || val1 > 0 && operator == "||" {
                    return Ok(AST::Number(val1));
                }
                let result2 = self.evaluate(child2).unwrap_or(AST::None);

                match result2 {
                    AST::Number(val2) => Ok(AST::Number(val2)),
                    _ => Err("Right hand result must be a number".to_string()),
                }
            }
            _ => Err("Left hand result must be a number".to_string()),
        }
    }

    /// evaluate evaluates the given AST and returns an AST
    /// of the result
    pub fn evaluate(&mut self, ast: AST) -> Result<AST, String> {
        let op = ast_to_operator(&ast);
        match ast {
            AST::Variable(name) => match self.env.get(name) {
                Some(val) => Ok(val.clone()),
                None => Err("Variable is not in environment".to_string()),
            },

            ast @ AST::Number(_) | ast @ AST::String(_) | ast @ AST::Decimal(_) => Ok(ast),

            AST::Assign(box child1, box child2) |
            AST::Declare(box child1, box child2) =>
                self.evaluate_set_variable(op.as_str(), child1, child2),

            AST::Equal(box child1, box child2) |
            AST::NotEqual(box child1, box child2) =>
                self.evaluate_equality(op.as_str(), child1, child2),

            AST::And(box child1, box child2) |
            AST::Or(box child1, box child2) => self.evaluate_boolean(op.as_str(), child1, child2),

            AST::Plus(box child1, box child2) |
            AST::Minus(box child1, box child2) |
            AST::Times(box child1, box child2) |
            AST::Divide(box child1, box child2) |
            AST::Modulo(box child1, box child2) =>
                self.evaluate_arithmetic(op.as_str(), child1, child2),

            _ => Err("Operation is not implemented yet".to_string()),
        }
    }
}

fn apply_arithmetic_operator<T: Num>(operator: &str, a: T, b: T) -> T {
    match operator {
        "+" => a + b,
        "-" => a - b,
        "*" => a * b,
        "/" => a / b,
        "%" => a % b,
        _ => a + b,
    }
}

#[test]
fn test_arith_ast() {
    // Test that the result for ast:
    //      *
    //    /   \
    //   5     +
    //       /   \
    //      -     6
    //    /   \
    //   3     2
    // is "35"

    let mut env = Environment::new();
    let mut evaluator = Evaluator::new(&mut env);

    let ast = AST::Times(box AST::Number(5),
                         box AST::Plus(box AST::Minus(box AST::Number(3), box AST::Number(2)),
                                       box AST::Number(6)));

    let result = evaluator.evaluate(ast);

    assert_eq!(result, Ok(AST::Number(35)));
}

#[test]
fn test_variable_ast() {
    // Test that the result for ast:
    //      *
    //    /   \
    //   a     +
    //       /   \
    //      -     d
    //    /   \
    //   b     c
    // is "35" when a is 5, b is 3, c is 2, and d is 6

    let mut env = Environment::new();
    env.set("a".to_string(), AST::Number(5));
    env.set("b".to_string(), AST::Number(3));
    env.set("c".to_string(), AST::Number(2));
    env.set("d".to_string(), AST::Number(6));

    let mut evaluator = Evaluator::new(&mut env);

    let ast = AST::Times(box AST::Variable("a".to_string()),
                         box AST::Plus(box AST::Minus(box AST::Variable("b".to_string()),
                                                      box AST::Variable("c".to_string())),
                                       box AST::Variable("d".to_string())));
    let result = evaluator.evaluate(ast);

    assert_eq!(result, Ok(AST::Number(35)));
}

#[test]
fn test_float_ast() {
    // Test that the result for ast:
    //      *
    //    /   \
    //   a     +
    //       /   \
    //      -     c
    //    /   \
    //   1.5   b
    // is "27.5" when a is 5, b is 2, and c is 6

    let mut env = Environment::new();
    env.set("a".to_string(), AST::Number(5));
    env.set("b".to_string(), AST::Number(2));
    env.set("c".to_string(), AST::Number(6));

    let mut evaluator = Evaluator::new(&mut env);

    let ast = AST::Times(box AST::Variable("a".to_string()),
                         box AST::Plus(box AST::Minus(box AST::Decimal(1.5),
                                                      box AST::Variable("b".to_string())),
                                       box AST::Variable("c".to_string())));
    let result = evaluator.evaluate(ast);

    assert_eq!(result, Ok(AST::Decimal(27.5)));
}

#[test]
fn test_declare_assign() {
    // Test that evaluating ast:
    //     :=
    //    /  \
    //   x    *
    //       / \
    //      10  +
    //         / \
    //        2  3
    // results in x being bound to 50 in the current scope
    // then after pushing a new scope and evaluating ast:
    //    =
    //   / \
    //  x   +
    //     / \
    //    1   2
    // results in x being set to 3 in the original scope

    let mut env = Environment::new();
    let mut evaluator = Evaluator::new(&mut env);
    let ast = AST::Declare(box AST::Variable("x".to_string()),
                           box AST::Times(box AST::Number(10),
                                          box AST::Plus(box AST::Number(2), box AST::Number(3))));
    let result = evaluator.evaluate(ast);
    assert_eq!(result, Ok(AST::Number(50)));
    assert_eq!(evaluator.env.get("x".to_string()), Some(&AST::Number(50)));

    evaluator.env.push();

    let ast = AST::Assign(box AST::Variable("x".to_string()),
                          box AST::Plus(box AST::Number(1), box AST::Number(2)));

    let result = evaluator.evaluate(ast);
    assert_eq!(result, Ok(AST::Number(3)));

    evaluator.env.pop();
    assert_eq!(evaluator.env.get("x".to_string()), Some(&AST::Number(3)));
}

#[test]
fn test_equality() {
    let mut env = Environment::new();
    let mut evaluator = Evaluator::new(&mut env);

    // Test number equality

    let ast = AST::Equal(box AST::Number(5), box AST::Number(5));
    let result = evaluator.evaluate(ast);
    assert_eq!(result, Ok(AST::Number(1)));

    let ast = AST::Equal(box AST::Number(5), box AST::Number(4));
    let result = evaluator.evaluate(ast);
    assert_eq!(result, Ok(AST::Number(0)));

    // Test decimal equality

    let ast = AST::Equal(box AST::Decimal(2.56), box AST::Decimal(2.56));
    let result = evaluator.evaluate(ast);
    assert_eq!(result, Ok(AST::Number(1)));

    let ast = AST::Equal(box AST::Decimal(2.56), box AST::Decimal(2.55));
    let result = evaluator.evaluate(ast);
    assert_eq!(result, Ok(AST::Number(0)));

    // Test string equality

    let ast = AST::Equal(box AST::String("Hello".to_string()),
                         box AST::String("Hello".to_string()));
    let result = evaluator.evaluate(ast);
    assert_eq!(result, Ok(AST::Number(1)));

    let ast = AST::Equal(box AST::String("Hello".to_string()),
                         box AST::String("hello".to_string()));
    let result = evaluator.evaluate(ast);
    assert_eq!(result, Ok(AST::Number(0)));
}

#[test]
fn test_boolean_operators() {
    let mut env = Environment::new();
    let mut evaluator = Evaluator::new(&mut env);

    // Test and operator

    let ast = AST::And(box AST::Number(1), box AST::Number(0));
    let result = evaluator.evaluate(ast);
    assert_eq!(result, Ok(AST::Number(0)));

    let ast = AST::And(box AST::Number(0), box AST::Number(5));
    let result = evaluator.evaluate(ast);
    assert_eq!(result, Ok(AST::Number(0)));

    let ast = AST::And(box AST::Number(3), box AST::Number(5));
    let result = evaluator.evaluate(ast);
    assert_eq!(result, Ok(AST::Number(5)));

    // Test or operator

    let ast = AST::Or(box AST::Number(5), box AST::Number(1));
    let result = evaluator.evaluate(ast);
    assert_eq!(result, Ok(AST::Number(5)));

    let ast = AST::Or(box AST::Number(0), box AST::Number(3));
    let result = evaluator.evaluate(ast);
    assert_eq!(result, Ok(AST::Number(3)));

    let ast = AST::Or(box AST::Number(0), box AST::Number(0));
    let result = evaluator.evaluate(ast);
    assert_eq!(result, Ok(AST::Number(0)));
}
<|endoftext|>


<commit_before>//! Exceptions
//!
//! All the exceptions prefixed with an underscore (`_`) can overridden by the top crate:
//!
//! ```
//! //! Override the hard fault exception
//!
//! #![feature(asm)]
//! #![no_main]
//! #![no_std]
//!
//! #[macro_use]
//! extern crate cortex_m;
//! extern crate f3;
//!
//! use core::ptr;
//!
//! #[export_name = "main"]
//! pub fn main() -> ! {
//!     unsafe {
//!         // Trigger a `hard_fault` exception (using UNSAFE code, of course!) and ...
//!         ptr::write_volatile(0x0000_0000 as *mut u32, 0xdead_beef);
//!     }
//!
//!     loop {}
//! }
//!
//! #[export_name = "_hard_fault"]  // <-- Important! Note the underscore.
//! pub extern "C" fn my_hard_fault_handler() {
//!     unsafe {
//!         // .. you should reach THIS breakpoint!
//!         bkpt!();
//!     }
//!
//!     loop {}
//! }
//! ```

use cortex_m::{self, Handler, StackFrame};
use r0;

/// Kind of exception
#[derive(Debug)]
pub enum Exception {
    /// i.e. currently not servicing an exception
    ThreadMode,
    /// Non-maskable interrupt.
    Nmi,
    /// All class of fault.
    HardFault,
    /// Memory management.
    MemoryManagementFault,
    /// Pre-fetch fault, memory access fault.
    BusFault,
    /// Undefined instruction or illegal state.
    UsageFault,
    /// System service call via SWI instruction
    SVCall,
    /// Pendable request for system service
    PendSV,
    /// System tick timer
    Systick,
    /// An interrupt
    Interrupt(u8),
    // Unreachable variant
    #[doc(hidden)]
    Reserved,
}

impl Exception {
    /// Returns the exception that's currently being serviced
    pub fn current() -> Exception {
        match cortex_m::peripheral::scb().icsr.read() as u8 {
            0 => Exception::ThreadMode,
            2 => Exception::Nmi,
            3 => Exception::HardFault,
            4 => Exception::MemoryManagementFault,
            5 => Exception::BusFault,
            6 => Exception::UsageFault,
            11 => Exception::SVCall,
            14 => Exception::PendSV,
            15 => Exception::Systick,
            n if n >= 16 => Exception::Interrupt(n - 16),
            _ => Exception::Reserved,

        }
    }
}

#[cfg(target_arch = "arm")]
#[doc(hidden)]
#[export_name = "_default_exception_handler"]
#[linkage = "weak"]
#[naked]
pub unsafe extern "C" fn _default_handler() {
    use core::intrinsics;

    // NOTE need asm!, #[naked] and unreachable() to avoid modifying the stack pointer (MSP)
    asm!("mrs r0, MSP
          ldr r1, [r0, #20]
          b _default_exception_handler_impl" :::: "volatile");
    intrinsics::unreachable();
}

#[cfg(not(target_arch = "arm"))]
pub extern "C" fn _default_handler() {}

#[doc(hidden)]
#[export_name = "_default_exception_handler_impl"]
pub unsafe extern "C" fn default_handler(sf: &StackFrame) -> ! {
    iprintln!("EXCEPTION {:?} @ PC=0x{:08x}", Exception::current(), sf.pc);

    bkpt!();

    loop {}
}

/// List of all the exceptions minus the reset handler as allocated in the vector table.
///
/// `None` indicates that the spot is RESERVED.
#[link_section = ".text.exceptions"]
#[no_mangle]
pub static EXCEPTIONS: [Option<Handler>; 14] = [Some(_nmi),
                                                Some(_hard_fault),
                                                Some(_memmanage_fault),
                                                Some(_bus_fault),
                                                Some(_usage_fault),
                                                None,
                                                None,
                                                None,
                                                None,
                                                Some(_svcall),
                                                None,
                                                None,
                                                Some(_pendsv),
                                                Some(_systick)];

extern "C" {
    /// Non-maskable interrupt.
    pub fn _nmi();
    /// All class of fault.
    pub fn _hard_fault();
    /// Memory management.
    pub fn _memmanage_fault();
    /// Pre-fetch fault, memory access fault.
    pub fn _bus_fault();
    /// Undefined instruction or illegal state.
    pub fn _usage_fault();
    /// System service call via SWI instruction
    pub fn _svcall();
    /// Pendable request for system service
    pub fn _pendsv();
    /// System tick timer
    pub fn _systick();
}

/// Reset handler
#[export_name = "_reset"]
pub unsafe extern "C" fn reset() -> ! {
    extern "C" {
        static _ebss: u32;
        static _edata: u32;
        static _sidata: u32;
        static mut _sbss: u32;
        static mut _sdata: u32;

    }

    r0::zero_bss(&mut _sbss, &_ebss);
    r0::init_data(&mut _sdata, &_edata, &_sidata);

    ::init();

    ::main();

    // safety net in case `main` returns
    panic!("returned from main!")
}
<commit_msg>remove duplicate example about overriding an exception<commit_after>//! Exceptions
//!
//! All the exceptions prefixed with an underscore (`_`) can overridden by the top crate. Check out
//! [this example](../examples/override_exception/index.html).

use cortex_m::{self, Handler, StackFrame};
use r0;

/// Kind of exception
#[derive(Debug)]
pub enum Exception {
    /// i.e. currently not servicing an exception
    ThreadMode,
    /// Non-maskable interrupt.
    Nmi,
    /// All class of fault.
    HardFault,
    /// Memory management.
    MemoryManagementFault,
    /// Pre-fetch fault, memory access fault.
    BusFault,
    /// Undefined instruction or illegal state.
    UsageFault,
    /// System service call via SWI instruction
    SVCall,
    /// Pendable request for system service
    PendSV,
    /// System tick timer
    Systick,
    /// An interrupt
    Interrupt(u8),
    // Unreachable variant
    #[doc(hidden)]
    Reserved,
}

impl Exception {
    /// Returns the exception that's currently being serviced
    pub fn current() -> Exception {
        match cortex_m::peripheral::scb().icsr.read() as u8 {
            0 => Exception::ThreadMode,
            2 => Exception::Nmi,
            3 => Exception::HardFault,
            4 => Exception::MemoryManagementFault,
            5 => Exception::BusFault,
            6 => Exception::UsageFault,
            11 => Exception::SVCall,
            14 => Exception::PendSV,
            15 => Exception::Systick,
            n if n >= 16 => Exception::Interrupt(n - 16),
            _ => Exception::Reserved,

        }
    }
}

#[cfg(target_arch = "arm")]
#[doc(hidden)]
#[export_name = "_default_exception_handler"]
#[linkage = "weak"]
#[naked]
pub unsafe extern "C" fn _default_handler() {
    use core::intrinsics;

    // NOTE need asm!, #[naked] and unreachable() to avoid modifying the stack pointer (MSP)
    asm!("mrs r0, MSP
          ldr r1, [r0, #20]
          b _default_exception_handler_impl" :::: "volatile");
    intrinsics::unreachable();
}

#[cfg(not(target_arch = "arm"))]
pub extern "C" fn _default_handler() {}

#[doc(hidden)]
#[export_name = "_default_exception_handler_impl"]
pub unsafe extern "C" fn default_handler(sf: &StackFrame) -> ! {
    iprintln!("EXCEPTION {:?} @ PC=0x{:08x}", Exception::current(), sf.pc);

    bkpt!();

    loop {}
}

/// List of all the exceptions minus the reset handler as allocated in the vector table.
///
/// `None` indicates that the spot is RESERVED.
#[link_section = ".text.exceptions"]
#[no_mangle]
pub static EXCEPTIONS: [Option<Handler>; 14] = [Some(_nmi),
                                                Some(_hard_fault),
                                                Some(_memmanage_fault),
                                                Some(_bus_fault),
                                                Some(_usage_fault),
                                                None,
                                                None,
                                                None,
                                                None,
                                                Some(_svcall),
                                                None,
                                                None,
                                                Some(_pendsv),
                                                Some(_systick)];

extern "C" {
    /// Non-maskable interrupt.
    pub fn _nmi();
    /// All class of fault.
    pub fn _hard_fault();
    /// Memory management.
    pub fn _memmanage_fault();
    /// Pre-fetch fault, memory access fault.
    pub fn _bus_fault();
    /// Undefined instruction or illegal state.
    pub fn _usage_fault();
    /// System service call via SWI instruction
    pub fn _svcall();
    /// Pendable request for system service
    pub fn _pendsv();
    /// System tick timer
    pub fn _systick();
}

/// Reset handler
#[export_name = "_reset"]
pub unsafe extern "C" fn reset() -> ! {
    extern "C" {
        static _ebss: u32;
        static _edata: u32;
        static _sidata: u32;
        static mut _sbss: u32;
        static mut _sdata: u32;

    }

    r0::zero_bss(&mut _sbss, &_ebss);
    r0::init_data(&mut _sdata, &_edata, &_sidata);

    ::init();

    ::main();

    // safety net in case `main` returns
    panic!("returned from main!")
}
<|endoftext|>


<commit_before>use crate::{
    app::{AppContext, AppContextPointer},
    errors::SondeError,
};
use gdk::Event;
use gtk::{self, prelude::*, Button, Notebook, Paned, Widget, Window};
use std::rc::Rc;

mod menu_callbacks;

const TABS: [(&str, &str); 8] = [
    ("skew_t", "Skew T"),
    ("hodograph_area", "Hodograph"),
    ("fire_plume_container", "Fire Plume"),
    ("text_area_container", "Text"),
    ("control_area", "Controls"),
    ("profiles_area_container", "Profiles"),
    ("indexes_scrolled_window", "Indexes"),
    ("provider_data_text_container", "Provider Data"),
];

pub fn set_up_main_window(ac: &AppContextPointer) -> Result<(), SondeError> {
    connect_header_bar(ac)?;
    configure_main_window(ac)?;

    Ok(())
}

macro_rules! set_up_button {
    ($ac:ident, $id:expr, $win:ident, $fn:expr) => {
        let button: Button = $ac.fetch_widget($id)?;
        let ac1 = Rc::clone($ac);
        let win1 = $win.clone();
        button.connect_clicked(move |_| {
            $fn(&ac1, &win1);
        });
    };
}

fn connect_header_bar(ac: &AppContextPointer) -> Result<(), SondeError> {
    let win: Window = ac.fetch_widget("main_window")?;

    set_up_button!(
        ac,
        "open-button",
        win,
        menu_callbacks::open_toolbar_callback
    );
    set_up_button!(
        ac,
        "save-image-button",
        win,
        menu_callbacks::save_image_callback
    );

    set_up_button!(ac, "go-first-button", win, |ac: &Rc<AppContext>, _win| ac
        .display_first());
    set_up_button!(
        ac,
        "go-previous-button",
        win,
        |ac: &Rc<AppContext>, _win| ac.display_previous()
    );
    set_up_button!(ac, "go-next-button", win, |ac: &Rc<AppContext>, _win| ac
        .display_next());
    set_up_button!(ac, "go-last-button", win, |ac: &Rc<AppContext>, _win| ac
        .display_last());

    set_up_button!(ac, "zoom-in-button", win, |ac: &Rc<AppContext>, _win| ac
        .zoom_in());
    set_up_button!(ac, "zoom-out-button", win, |ac: &Rc<AppContext>, _win| ac
        .zoom_out());

    set_up_button!(ac, "quit-button", win, |ac, win| {
        update_window_config_and_exit(win, ac)
    });

    Ok(())
}

fn configure_main_window(ac: &AppContextPointer) -> Result<(), SondeError> {
    let window: Window = ac.fetch_widget("main_window")?;

    layout_tabs_window(&window, ac)?;

    let ac1 = Rc::clone(ac);
    window.connect_delete_event(move |win, ev| on_delete(win, ev, &ac1));

    window.show_all();

    Ok(())
}

fn update_window_config_and_exit(win: &Window, ac: &AppContext) {
    let mut config = ac.config.borrow_mut();

    // Save the window dimensions
    let (width, height) = win.get_size();
    config.window_width = width;
    config.window_height = height;

    // Save the Paned view slider position.
    if let Ok(pane) = ac.fetch_widget::<Paned>("main_pane_view") {
        let pos = (pane.get_position() as f32) / (width as f32);
        config.pane_position = pos;
    }

    // Save the tabs, which notebook they are in, their order, and which ones were selected.
    if let (Ok(lnb), Ok(rnb)) = (
        ac.fetch_widget::<Notebook>("left_notebook"),
        ac.fetch_widget::<Notebook>("right_notebook"),
    ) {
        let tabs: Vec<Widget> = TABS
            .iter()
            // If there is an error here, it will ALWAYS fail. So go ahead and unwrap.
            .map(|&widget_id| {
                ac.fetch_widget::<Widget>(widget_id.0)
                    .expect("Error loading widget!")
            })
            .collect();

        let save_tabs = |cfg_tabs: &mut Vec<String>, nb: &Notebook| {
            cfg_tabs.clear();
            for child in nb.get_children() {
                for (idx, tab) in tabs.iter().enumerate() {
                    if child == *tab {
                        cfg_tabs.push(TABS[idx].0.to_owned());
                    }
                }
            }
        };

        save_tabs(&mut config.left_tabs, &lnb);
        save_tabs(&mut config.right_tabs, &rnb);

        config.left_page_selected = lnb.get_property_page();
        config.right_page_selected = rnb.get_property_page();
    }

    gtk::main_quit();
}

fn on_delete(win: &Window, _ev: &Event, ac: &AppContext) -> Inhibit {
    update_window_config_and_exit(win, ac);
    Inhibit(false)
}

fn layout_tabs_window(win: &Window, ac: &AppContext) -> Result<(), SondeError> {
    let cfg = ac.config.borrow();

    let pane: Paned = ac.fetch_widget("main_pane_view")?;

    let (width, height, pane_position) =
        { (cfg.window_width, cfg.window_height, cfg.pane_position) };

    if width > 0 || height > 0 {
        win.resize(width, height);
    }

    if pane_position > 0.0 {
        let (width, _) = win.get_size();
        let pos = (width as f32 * pane_position).round() as i32;

        debug_assert!(pos < width);
        pane.set_position(pos);
    }

    if !(cfg.left_tabs.is_empty() && cfg.right_tabs.is_empty()) {
        if let (Ok(lnb), Ok(rnb)) = (
            ac.fetch_widget::<Notebook>("left_notebook"),
            ac.fetch_widget::<Notebook>("right_notebook"),
        ) {
            let restore_tabs = |cfg_tabs: &Vec<String>, tgt_nb: &Notebook, other_nb: &Notebook| {
                for tab_id in cfg_tabs {
                    TABS.iter().position(|&s| s.0 == tab_id).and_then(|idx| {
                        ac.fetch_widget::<Widget>(TABS[idx].0)
                            .ok()
                            .and_then(|widget| {
                                let tgt_children = tgt_nb.get_children();
                                let other_children = other_nb.get_children();

                                if tgt_children.contains(&widget) {
                                    tgt_nb.remove(&widget);
                                } else if other_children.contains(&widget) {
                                    other_nb.remove(&widget);
                                }

                                tgt_nb.add(&widget);
                                tgt_nb.set_tab_label_text(&widget, TABS[idx].1);
                                tgt_nb.set_tab_detachable(&widget, true);
                                tgt_nb.set_tab_reorderable(&widget, true);

                                Some(())
                            })
                    });
                }
            };

            restore_tabs(&cfg.left_tabs, &lnb, &rnb);
            restore_tabs(&cfg.right_tabs, &rnb, &lnb);

            lnb.set_property_page(cfg.left_page_selected);
            rnb.set_property_page(cfg.right_page_selected);
        }
    }

    Ok(())
}
<commit_msg>Formatting.<commit_after>use crate::{
    app::{AppContext, AppContextPointer},
    errors::SondeError,
};
use gdk::Event;
use gtk::{self, prelude::*, Button, Notebook, Paned, Widget, Window};
use std::rc::Rc;

mod menu_callbacks;

const TABS: [(&str, &str); 8] = [
    ("skew_t", "Skew T"),
    ("hodograph_area", "Hodograph"),
    ("fire_plume_container", "Fire Plume"),
    ("text_area_container", "Text"),
    ("control_area", "Controls"),
    ("profiles_area_container", "Profiles"),
    ("indexes_scrolled_window", "Indexes"),
    ("provider_data_text_container", "Provider Data"),
];

pub fn set_up_main_window(ac: &AppContextPointer) -> Result<(), SondeError> {
    connect_header_bar(ac)?;
    configure_main_window(ac)?;

    Ok(())
}

macro_rules! set_up_button {
    ($ac:ident, $id:expr, $win:ident, $fn:expr) => {
        let button: Button = $ac.fetch_widget($id)?;
        let ac1 = Rc::clone($ac);
        let win1 = $win.clone();
        button.connect_clicked(move |_| {
            $fn(&ac1, &win1);
        });
    };
    ($ac:ident, $id:expr, $fn:expr) => {
        let button: Button = $ac.fetch_widget($id)?;
        let ac1: Rc<AppContext> = Rc::clone($ac);
        button.connect_clicked(move |_| {
            $fn(&ac1);
        });
    };
}

fn connect_header_bar(ac: &AppContextPointer) -> Result<(), SondeError> {
    use menu_callbacks::{open_toolbar_callback, save_image_callback};

    let win: Window = ac.fetch_widget("main_window")?;

    set_up_button!(ac, "open-button", win, open_toolbar_callback);
    set_up_button!(ac, "save-image-button", win, save_image_callback);

    set_up_button!(ac, "go-first-button", |ac: &Rc<AppContext>| ac.display_first());
    set_up_button!(ac, "go-previous-button", |ac: &Rc<AppContext>| ac.display_previous());
    set_up_button!(ac, "go-next-button", |ac: &Rc<AppContext>| ac.display_next());
    set_up_button!(ac, "go-last-button", |ac: &Rc<AppContext>| ac.display_last());

    set_up_button!(ac, "zoom-in-button", |ac: &Rc<AppContext>| ac.zoom_in());
    set_up_button!(ac, "zoom-out-button", |ac: &Rc<AppContext>| ac.zoom_out());

    set_up_button!(ac, "quit-button", win, update_window_config_and_exit);

    Ok(())
}

fn configure_main_window(ac: &AppContextPointer) -> Result<(), SondeError> {
    let window: Window = ac.fetch_widget("main_window")?;

    layout_tabs_window(&window, ac)?;

    let ac1 = Rc::clone(ac);
    window.connect_delete_event(move |win, ev| on_delete(win, ev, &ac1));

    window.show_all();

    Ok(())
}

fn update_window_config_and_exit(ac: &AppContext, win: &Window) {
    let mut config = ac.config.borrow_mut();

    // Save the window dimensions
    let (width, height) = win.get_size();
    config.window_width = width;
    config.window_height = height;

    // Save the Paned view slider position.
    if let Ok(pane) = ac.fetch_widget::<Paned>("main_pane_view") {
        let pos = (pane.get_position() as f32) / (width as f32);
        config.pane_position = pos;
    }

    // Save the tabs, which notebook they are in, their order, and which ones were selected.
    if let (Ok(lnb), Ok(rnb)) = (
        ac.fetch_widget::<Notebook>("left_notebook"),
        ac.fetch_widget::<Notebook>("right_notebook"),
    ) {
        let tabs: Vec<Widget> = TABS
            .iter()
            // If there is an error here, it will ALWAYS fail. So go ahead and unwrap.
            .map(|&widget_id| {
                ac.fetch_widget::<Widget>(widget_id.0)
                    .expect("Error loading widget!")
            })
            .collect();

        let save_tabs = |cfg_tabs: &mut Vec<String>, nb: &Notebook| {
            cfg_tabs.clear();
            for child in nb.get_children() {
                for (idx, tab) in tabs.iter().enumerate() {
                    if child == *tab {
                        cfg_tabs.push(TABS[idx].0.to_owned());
                    }
                }
            }
        };

        save_tabs(&mut config.left_tabs, &lnb);
        save_tabs(&mut config.right_tabs, &rnb);

        config.left_page_selected = lnb.get_property_page();
        config.right_page_selected = rnb.get_property_page();
    }

    gtk::main_quit();
}

fn on_delete(win: &Window, _ev: &Event, ac: &AppContext) -> Inhibit {
    update_window_config_and_exit(ac, win);
    Inhibit(false)
}

fn layout_tabs_window(win: &Window, ac: &AppContext) -> Result<(), SondeError> {
    let cfg = ac.config.borrow();

    let pane: Paned = ac.fetch_widget("main_pane_view")?;

    let (width, height, pane_position) =
        { (cfg.window_width, cfg.window_height, cfg.pane_position) };

    if width > 0 || height > 0 {
        win.resize(width, height);
    }

    if pane_position > 0.0 {
        let (width, _) = win.get_size();
        let pos = (width as f32 * pane_position).round() as i32;

        debug_assert!(pos < width);
        pane.set_position(pos);
    }

    if !(cfg.left_tabs.is_empty() && cfg.right_tabs.is_empty()) {
        if let (Ok(lnb), Ok(rnb)) = (
            ac.fetch_widget::<Notebook>("left_notebook"),
            ac.fetch_widget::<Notebook>("right_notebook"),
        ) {
            let restore_tabs = |cfg_tabs: &Vec<String>, tgt_nb: &Notebook, other_nb: &Notebook| {
                for tab_id in cfg_tabs {
                    TABS.iter().position(|&s| s.0 == tab_id).and_then(|idx| {
                        ac.fetch_widget::<Widget>(TABS[idx].0)
                            .ok()
                            .and_then(|widget| {
                                let tgt_children = tgt_nb.get_children();
                                let other_children = other_nb.get_children();

                                if tgt_children.contains(&widget) {
                                    tgt_nb.remove(&widget);
                                } else if other_children.contains(&widget) {
                                    other_nb.remove(&widget);
                                }

                                tgt_nb.add(&widget);
                                tgt_nb.set_tab_label_text(&widget, TABS[idx].1);
                                tgt_nb.set_tab_detachable(&widget, true);
                                tgt_nb.set_tab_reorderable(&widget, true);

                                Some(())
                            })
                    });
                }
            };

            restore_tabs(&cfg.left_tabs, &lnb, &rnb);
            restore_tabs(&cfg.right_tabs, &rnb, &lnb);

            lnb.set_property_page(cfg.left_page_selected);
            rnb.set_property_page(cfg.right_page_selected);
        }
    }

    Ok(())
}
<|endoftext|>


<commit_before>use alloc::borrow::Cow;
use alloc::vec::Vec;
use core::fmt::{Debug, LowerHex};

use crate::error;
use scroll::ctx::TryFromCtx;
use scroll::{Pread, Pwrite, SizeWith};

use crate::pe::data_directories;
use crate::pe::options;
use crate::pe::section_table;
use crate::pe::utils;

use log::{debug, warn};

pub const IMPORT_BY_ORDINAL_32: u32 = 0x8000_0000;
pub const IMPORT_BY_ORDINAL_64: u64 = 0x8000_0000_0000_0000;
pub const IMPORT_RVA_MASK_32: u32 = 0x7fff_ffff;
pub const IMPORT_RVA_MASK_64: u64 = 0x0000_0000_7fff_ffff;

pub trait Bitfield<'a>:
    Into<u64>
    + PartialEq
    + Eq
    + LowerHex
    + Debug
    + TryFromCtx<'a, scroll::Endian, Error = scroll::Error>
{
    fn is_ordinal(&self) -> bool;
    fn to_ordinal(&self) -> u16;
    fn to_rva(&self) -> u32;
    fn size_of() -> usize;
    fn is_zero(&self) -> bool;
}

impl<'a> Bitfield<'a> for u64 {
    fn is_ordinal(&self) -> bool {
        self & IMPORT_BY_ORDINAL_64 == IMPORT_BY_ORDINAL_64
    }
    fn to_ordinal(&self) -> u16 {
        (0xffff & self) as u16
    }
    fn to_rva(&self) -> u32 {
        (self & IMPORT_RVA_MASK_64) as u32
    }
    fn size_of() -> usize {
        8
    }
    fn is_zero(&self) -> bool {
        *self == 0
    }
}

impl<'a> Bitfield<'a> for u32 {
    fn is_ordinal(&self) -> bool {
        self & IMPORT_BY_ORDINAL_32 == IMPORT_BY_ORDINAL_32
    }
    fn to_ordinal(&self) -> u16 {
        (0xffff & self) as u16
    }
    fn to_rva(&self) -> u32 {
        (self & IMPORT_RVA_MASK_32) as u32
    }
    fn size_of() -> usize {
        4
    }
    fn is_zero(&self) -> bool {
        *self == 0
    }
}

#[derive(Debug, Clone)]
pub struct HintNameTableEntry<'a> {
    pub hint: u16,
    pub name: &'a str,
}

impl<'a> HintNameTableEntry<'a> {
    fn parse(bytes: &'a [u8], mut offset: usize) -> error::Result<Self> {
        let offset = &mut offset;
        let hint = bytes.gread_with(offset, scroll::LE)?;
        let name = bytes.pread::<&'a str>(*offset)?;
        Ok(HintNameTableEntry { hint, name })
    }
}

#[derive(Debug, Clone)]
pub enum SyntheticImportLookupTableEntry<'a> {
    OrdinalNumber(u16),
    HintNameTableRVA((u32, HintNameTableEntry<'a>)), // [u8; 31] bitfield :/
}

pub type ImportLookupTable<'a> = Vec<SyntheticImportLookupTableEntry<'a>>;

impl<'a> SyntheticImportLookupTableEntry<'a> {
    pub fn parse<T: Bitfield<'a>>(
        bytes: &'a [u8],
        offset: usize,
        sections: &[section_table::SectionTable],
        file_alignment: u32,
    ) -> error::Result<ImportLookupTable<'a>> {
        Self::parse_with_opts::<T>(
            bytes,
            offset,
            sections,
            file_alignment,
            &options::ParseOptions::default(),
        )
    }

    pub fn parse_with_opts<T: Bitfield<'a>>(
        bytes: &'a [u8],
        mut offset: usize,
        sections: &[section_table::SectionTable],
        file_alignment: u32,
        opts: &options::ParseOptions,
    ) -> error::Result<ImportLookupTable<'a>> {
        let le = scroll::LE;
        let offset = &mut offset;
        let mut table = Vec::new();
        loop {
            let bitfield: T = bytes.gread_with(offset, le)?;
            if bitfield.is_zero() {
                debug!("imports done");
                break;
            } else {
                let entry = {
                    debug!("bitfield {:#x}", bitfield);
                    use self::SyntheticImportLookupTableEntry::*;
                    if bitfield.is_ordinal() {
                        let ordinal = bitfield.to_ordinal();
                        debug!("importing by ordinal {:#x}", ordinal);
                        OrdinalNumber(ordinal)
                    } else {
                        let rva = bitfield.to_rva();
                        let hentry = {
                            debug!("searching for RVA {:#x}", rva);
                            if let Some(offset) =
                                utils::find_offset(rva as usize, sections, file_alignment, opts)
                            {
                                debug!("offset {:#x}", offset);
                                HintNameTableEntry::parse(bytes, offset)?
                            } else {
                                warn!("Entry {} has bad RVA: {:#x}", table.len(), rva);
                                continue;
                            }
                        };
                        HintNameTableRVA((rva, hentry))
                    }
                };
                table.push(entry);
            }
        }
        Ok(table)
    }
}

// get until entry is 0
pub type ImportAddressTable = Vec<u64>;

#[repr(C)]
#[derive(Debug, Pread, Pwrite, SizeWith)]
pub struct ImportDirectoryEntry {
    pub import_lookup_table_rva: u32,
    pub time_date_stamp: u32,
    pub forwarder_chain: u32,
    pub name_rva: u32,
    pub import_address_table_rva: u32,
}

pub const SIZEOF_IMPORT_DIRECTORY_ENTRY: usize = 20;

impl ImportDirectoryEntry {
    pub fn is_null(&self) -> bool {
        (self.import_lookup_table_rva == 0)
            && (self.time_date_stamp == 0)
            && (self.forwarder_chain == 0)
            && (self.name_rva == 0)
            && (self.import_address_table_rva == 0)
    }
}

#[derive(Debug)]
pub struct SyntheticImportDirectoryEntry<'a> {
    pub import_directory_entry: ImportDirectoryEntry,
    /// Computed
    pub name: &'a str,
    /// The import lookup table is a vector of either ordinals, or RVAs + import names
    pub import_lookup_table: Option<ImportLookupTable<'a>>,
    /// Computed
    pub import_address_table: ImportAddressTable,
}

impl<'a> SyntheticImportDirectoryEntry<'a> {
    pub fn parse<T: Bitfield<'a>>(
        bytes: &'a [u8],
        import_directory_entry: ImportDirectoryEntry,
        sections: &[section_table::SectionTable],
        file_alignment: u32,
    ) -> error::Result<SyntheticImportDirectoryEntry<'a>> {
        Self::parse_with_opts::<T>(
            bytes,
            import_directory_entry,
            sections,
            file_alignment,
            &options::ParseOptions::default(),
        )
    }

    pub fn parse_with_opts<T: Bitfield<'a>>(
        bytes: &'a [u8],
        import_directory_entry: ImportDirectoryEntry,
        sections: &[section_table::SectionTable],
        file_alignment: u32,
        opts: &options::ParseOptions,
    ) -> error::Result<SyntheticImportDirectoryEntry<'a>> {
        const LE: scroll::Endian = scroll::LE;
        let name_rva = import_directory_entry.name_rva;
        let name = utils::try_name(bytes, name_rva as usize, sections, file_alignment, opts)?;
        let import_lookup_table = {
            let import_lookup_table_rva = import_directory_entry.import_lookup_table_rva;
            let import_address_table_rva = import_directory_entry.import_address_table_rva;
            if let Some(import_lookup_table_offset) = utils::find_offset(
                import_lookup_table_rva as usize,
                sections,
                file_alignment,
                opts,
            ) {
                debug!("Synthesizing lookup table imports for {} lib, with import lookup table rva: {:#x}", name, import_lookup_table_rva);
                let import_lookup_table = SyntheticImportLookupTableEntry::parse::<T>(
                    bytes,
                    import_lookup_table_offset,
                    sections,
                    file_alignment,
                )?;
                debug!(
                    "Successfully synthesized import lookup table entry from lookup table: {:#?}",
                    import_lookup_table
                );
                Some(import_lookup_table)
            } else if let Some(import_address_table_offset) = utils::find_offset(
                import_address_table_rva as usize,
                sections,
                file_alignment,
                opts,
            ) {
                debug!("Synthesizing lookup table imports for {} lib, with import address table rva: {:#x}", name, import_lookup_table_rva);
                let import_address_table = SyntheticImportLookupTableEntry::parse::<T>(
                    bytes,
                    import_address_table_offset,
                    sections,
                    file_alignment,
                )?;
                debug!(
                    "Successfully synthesized import lookup table entry from IAT: {:#?}",
                    import_address_table
                );
                Some(import_address_table)
            } else {
                None
            }
        };

        let import_address_table_offset = &mut utils::find_offset(
            import_directory_entry.import_address_table_rva as usize,
            sections,
            file_alignment,
            opts,
        )
        .ok_or_else(|| {
            error::Error::Malformed(format!(
                "Cannot map import_address_table_rva {:#x} into offset for {}",
                import_directory_entry.import_address_table_rva, name
            ))
        })?;
        let mut import_address_table = Vec::new();
        loop {
            let import_address = bytes
                .gread_with::<T>(import_address_table_offset, LE)?
                .into();
            if import_address == 0 {
                break;
            } else {
                import_address_table.push(import_address);
            }
        }
        Ok(SyntheticImportDirectoryEntry {
            import_directory_entry,
            name,
            import_lookup_table,
            import_address_table,
        })
    }
}

#[derive(Debug)]
/// Contains a list of synthesized import data for this binary, e.g., which symbols from which libraries it is importing from
pub struct ImportData<'a> {
    pub import_data: Vec<SyntheticImportDirectoryEntry<'a>>,
}

impl<'a> ImportData<'a> {
    pub fn parse<T: Bitfield<'a>>(
        bytes: &'a [u8],
        dd: data_directories::DataDirectory,
        sections: &[section_table::SectionTable],
        file_alignment: u32,
    ) -> error::Result<ImportData<'a>> {
        Self::parse_with_opts::<T>(
            bytes,
            dd,
            sections,
            file_alignment,
            &options::ParseOptions::default(),
        )
    }

    pub fn parse_with_opts<T: Bitfield<'a>>(
        bytes: &'a [u8],
        dd: data_directories::DataDirectory,
        sections: &[section_table::SectionTable],
        file_alignment: u32,
        opts: &options::ParseOptions,
    ) -> error::Result<ImportData<'a>> {
        let import_directory_table_rva = dd.virtual_address as usize;
        debug!(
            "import_directory_table_rva {:#x}",
            import_directory_table_rva
        );
        let offset =
            &mut utils::find_offset(import_directory_table_rva, sections, file_alignment, opts)
                .ok_or_else(|| {
                    error::Error::Malformed(format!(
                "Cannot create ImportData; cannot map import_directory_table_rva {:#x} into offset",
                import_directory_table_rva
            ))
                })?;
        debug!("import data offset {:#x}", offset);
        let mut import_data = Vec::new();
        loop {
            let import_directory_entry: ImportDirectoryEntry =
                bytes.gread_with(offset, scroll::LE)?;
            debug!("{:#?}", import_directory_entry);
            if import_directory_entry.is_null() {
                break;
            } else {
                let entry = SyntheticImportDirectoryEntry::parse_with_opts::<T>(
                    bytes,
                    import_directory_entry,
                    sections,
                    file_alignment,
                    opts,
                )?;
                debug!("entry {:#?}", entry);
                import_data.push(entry);
            }
        }
        debug!("finished ImportData");
        Ok(ImportData { import_data })
    }
}

#[derive(Debug)]
/// A synthesized symbol import, the name is pre-indexed, and the binary offset is computed, as well as which dll it belongs to
pub struct Import<'a> {
    pub name: Cow<'a, str>,
    pub dll: &'a str,
    pub ordinal: u16,
    pub offset: usize,
    pub rva: usize,
    pub size: usize,
}

impl<'a> Import<'a> {
    pub fn parse<T: Bitfield<'a>>(
        _bytes: &'a [u8],
        import_data: &ImportData<'a>,
        _sections: &[section_table::SectionTable],
    ) -> error::Result<Vec<Import<'a>>> {
        let mut imports = Vec::new();
        for data in &import_data.import_data {
            if let Some(ref import_lookup_table) = data.import_lookup_table {
                let dll = data.name;
                let import_base = data.import_directory_entry.import_address_table_rva as usize;
                debug!("Getting imports from {}", &dll);
                for (i, entry) in import_lookup_table.iter().enumerate() {
                    let offset = import_base + (i * T::size_of());
                    use self::SyntheticImportLookupTableEntry::*;
                    let (rva, name, ordinal) = match *entry {
                        HintNameTableRVA((rva, ref hint_entry)) => {
                            // if hint_entry.name = "" && hint_entry.hint = 0 {
                            //     println!("<PE.Import> warning hint/name table rva from {} without hint {:#x}", dll, rva);
                            // }
                            (rva, Cow::Borrowed(hint_entry.name), hint_entry.hint)
                        }
                        OrdinalNumber(ordinal) => {
                            let name = format!("ORDINAL {}", ordinal);
                            (0x0, Cow::Owned(name), ordinal)
                        }
                    };
                    let import = Import {
                        name,
                        ordinal,
                        dll,
                        size: T::size_of(),
                        offset,
                        rva: rva as usize,
                    };
                    imports.push(import);
                }
            }
        }
        Ok(imports)
    }
}
<commit_msg>pe:import:SyntheticImportDirectoryEntry use parse_with_opts (fixes #267)<commit_after>use alloc::borrow::Cow;
use alloc::vec::Vec;
use core::fmt::{Debug, LowerHex};

use crate::error;
use scroll::ctx::TryFromCtx;
use scroll::{Pread, Pwrite, SizeWith};

use crate::pe::data_directories;
use crate::pe::options;
use crate::pe::section_table;
use crate::pe::utils;

use log::{debug, warn};

pub const IMPORT_BY_ORDINAL_32: u32 = 0x8000_0000;
pub const IMPORT_BY_ORDINAL_64: u64 = 0x8000_0000_0000_0000;
pub const IMPORT_RVA_MASK_32: u32 = 0x7fff_ffff;
pub const IMPORT_RVA_MASK_64: u64 = 0x0000_0000_7fff_ffff;

pub trait Bitfield<'a>:
    Into<u64>
    + PartialEq
    + Eq
    + LowerHex
    + Debug
    + TryFromCtx<'a, scroll::Endian, Error = scroll::Error>
{
    fn is_ordinal(&self) -> bool;
    fn to_ordinal(&self) -> u16;
    fn to_rva(&self) -> u32;
    fn size_of() -> usize;
    fn is_zero(&self) -> bool;
}

impl<'a> Bitfield<'a> for u64 {
    fn is_ordinal(&self) -> bool {
        self & IMPORT_BY_ORDINAL_64 == IMPORT_BY_ORDINAL_64
    }
    fn to_ordinal(&self) -> u16 {
        (0xffff & self) as u16
    }
    fn to_rva(&self) -> u32 {
        (self & IMPORT_RVA_MASK_64) as u32
    }
    fn size_of() -> usize {
        8
    }
    fn is_zero(&self) -> bool {
        *self == 0
    }
}

impl<'a> Bitfield<'a> for u32 {
    fn is_ordinal(&self) -> bool {
        self & IMPORT_BY_ORDINAL_32 == IMPORT_BY_ORDINAL_32
    }
    fn to_ordinal(&self) -> u16 {
        (0xffff & self) as u16
    }
    fn to_rva(&self) -> u32 {
        (self & IMPORT_RVA_MASK_32) as u32
    }
    fn size_of() -> usize {
        4
    }
    fn is_zero(&self) -> bool {
        *self == 0
    }
}

#[derive(Debug, Clone)]
pub struct HintNameTableEntry<'a> {
    pub hint: u16,
    pub name: &'a str,
}

impl<'a> HintNameTableEntry<'a> {
    fn parse(bytes: &'a [u8], mut offset: usize) -> error::Result<Self> {
        let offset = &mut offset;
        let hint = bytes.gread_with(offset, scroll::LE)?;
        let name = bytes.pread::<&'a str>(*offset)?;
        Ok(HintNameTableEntry { hint, name })
    }
}

#[derive(Debug, Clone)]
pub enum SyntheticImportLookupTableEntry<'a> {
    OrdinalNumber(u16),
    HintNameTableRVA((u32, HintNameTableEntry<'a>)), // [u8; 31] bitfield :/
}

pub type ImportLookupTable<'a> = Vec<SyntheticImportLookupTableEntry<'a>>;

impl<'a> SyntheticImportLookupTableEntry<'a> {
    pub fn parse<T: Bitfield<'a>>(
        bytes: &'a [u8],
        offset: usize,
        sections: &[section_table::SectionTable],
        file_alignment: u32,
    ) -> error::Result<ImportLookupTable<'a>> {
        Self::parse_with_opts::<T>(
            bytes,
            offset,
            sections,
            file_alignment,
            &options::ParseOptions::default(),
        )
    }

    pub fn parse_with_opts<T: Bitfield<'a>>(
        bytes: &'a [u8],
        mut offset: usize,
        sections: &[section_table::SectionTable],
        file_alignment: u32,
        opts: &options::ParseOptions,
    ) -> error::Result<ImportLookupTable<'a>> {
        let le = scroll::LE;
        let offset = &mut offset;
        let mut table = Vec::new();
        loop {
            let bitfield: T = bytes.gread_with(offset, le)?;
            if bitfield.is_zero() {
                debug!("imports done");
                break;
            } else {
                let entry = {
                    debug!("bitfield {:#x}", bitfield);
                    use self::SyntheticImportLookupTableEntry::*;
                    if bitfield.is_ordinal() {
                        let ordinal = bitfield.to_ordinal();
                        debug!("importing by ordinal {:#x}", ordinal);
                        OrdinalNumber(ordinal)
                    } else {
                        let rva = bitfield.to_rva();
                        let hentry = {
                            debug!("searching for RVA {:#x}", rva);
                            if let Some(offset) =
                                utils::find_offset(rva as usize, sections, file_alignment, opts)
                            {
                                debug!("offset {:#x}", offset);
                                HintNameTableEntry::parse(bytes, offset)?
                            } else {
                                warn!("Entry {} has bad RVA: {:#x}", table.len(), rva);
                                continue;
                            }
                        };
                        HintNameTableRVA((rva, hentry))
                    }
                };
                table.push(entry);
            }
        }
        Ok(table)
    }
}

// get until entry is 0
pub type ImportAddressTable = Vec<u64>;

#[repr(C)]
#[derive(Debug, Pread, Pwrite, SizeWith)]
pub struct ImportDirectoryEntry {
    pub import_lookup_table_rva: u32,
    pub time_date_stamp: u32,
    pub forwarder_chain: u32,
    pub name_rva: u32,
    pub import_address_table_rva: u32,
}

pub const SIZEOF_IMPORT_DIRECTORY_ENTRY: usize = 20;

impl ImportDirectoryEntry {
    pub fn is_null(&self) -> bool {
        (self.import_lookup_table_rva == 0)
            && (self.time_date_stamp == 0)
            && (self.forwarder_chain == 0)
            && (self.name_rva == 0)
            && (self.import_address_table_rva == 0)
    }
}

#[derive(Debug)]
pub struct SyntheticImportDirectoryEntry<'a> {
    pub import_directory_entry: ImportDirectoryEntry,
    /// Computed
    pub name: &'a str,
    /// The import lookup table is a vector of either ordinals, or RVAs + import names
    pub import_lookup_table: Option<ImportLookupTable<'a>>,
    /// Computed
    pub import_address_table: ImportAddressTable,
}

impl<'a> SyntheticImportDirectoryEntry<'a> {
    pub fn parse<T: Bitfield<'a>>(
        bytes: &'a [u8],
        import_directory_entry: ImportDirectoryEntry,
        sections: &[section_table::SectionTable],
        file_alignment: u32,
    ) -> error::Result<SyntheticImportDirectoryEntry<'a>> {
        Self::parse_with_opts::<T>(
            bytes,
            import_directory_entry,
            sections,
            file_alignment,
            &options::ParseOptions::default(),
        )
    }

    pub fn parse_with_opts<T: Bitfield<'a>>(
        bytes: &'a [u8],
        import_directory_entry: ImportDirectoryEntry,
        sections: &[section_table::SectionTable],
        file_alignment: u32,
        opts: &options::ParseOptions,
    ) -> error::Result<SyntheticImportDirectoryEntry<'a>> {
        const LE: scroll::Endian = scroll::LE;
        let name_rva = import_directory_entry.name_rva;
        let name = utils::try_name(bytes, name_rva as usize, sections, file_alignment, opts)?;
        let import_lookup_table = {
            let import_lookup_table_rva = import_directory_entry.import_lookup_table_rva;
            let import_address_table_rva = import_directory_entry.import_address_table_rva;
            if let Some(import_lookup_table_offset) = utils::find_offset(
                import_lookup_table_rva as usize,
                sections,
                file_alignment,
                opts,
            ) {
                debug!("Synthesizing lookup table imports for {} lib, with import lookup table rva: {:#x}", name, import_lookup_table_rva);
                let import_lookup_table = SyntheticImportLookupTableEntry::parse_with_opts::<T>(
                    bytes,
                    import_lookup_table_offset,
                    sections,
                    file_alignment,
                    opts,
                )?;
                debug!(
                    "Successfully synthesized import lookup table entry from lookup table: {:#?}",
                    import_lookup_table
                );
                Some(import_lookup_table)
            } else if let Some(import_address_table_offset) = utils::find_offset(
                import_address_table_rva as usize,
                sections,
                file_alignment,
                opts,
            ) {
                debug!("Synthesizing lookup table imports for {} lib, with import address table rva: {:#x}", name, import_lookup_table_rva);
                let import_address_table = SyntheticImportLookupTableEntry::parse_with_opts::<T>(
                    bytes,
                    import_address_table_offset,
                    sections,
                    file_alignment,
                    opts,
                )?;
                debug!(
                    "Successfully synthesized import lookup table entry from IAT: {:#?}",
                    import_address_table
                );
                Some(import_address_table)
            } else {
                None
            }
        };

        let import_address_table_offset = &mut utils::find_offset(
            import_directory_entry.import_address_table_rva as usize,
            sections,
            file_alignment,
            opts,
        )
        .ok_or_else(|| {
            error::Error::Malformed(format!(
                "Cannot map import_address_table_rva {:#x} into offset for {}",
                import_directory_entry.import_address_table_rva, name
            ))
        })?;
        let mut import_address_table = Vec::new();
        loop {
            let import_address = bytes
                .gread_with::<T>(import_address_table_offset, LE)?
                .into();
            if import_address == 0 {
                break;
            } else {
                import_address_table.push(import_address);
            }
        }
        Ok(SyntheticImportDirectoryEntry {
            import_directory_entry,
            name,
            import_lookup_table,
            import_address_table,
        })
    }
}

#[derive(Debug)]
/// Contains a list of synthesized import data for this binary, e.g., which symbols from which libraries it is importing from
pub struct ImportData<'a> {
    pub import_data: Vec<SyntheticImportDirectoryEntry<'a>>,
}

impl<'a> ImportData<'a> {
    pub fn parse<T: Bitfield<'a>>(
        bytes: &'a [u8],
        dd: data_directories::DataDirectory,
        sections: &[section_table::SectionTable],
        file_alignment: u32,
    ) -> error::Result<ImportData<'a>> {
        Self::parse_with_opts::<T>(
            bytes,
            dd,
            sections,
            file_alignment,
            &options::ParseOptions::default(),
        )
    }

    pub fn parse_with_opts<T: Bitfield<'a>>(
        bytes: &'a [u8],
        dd: data_directories::DataDirectory,
        sections: &[section_table::SectionTable],
        file_alignment: u32,
        opts: &options::ParseOptions,
    ) -> error::Result<ImportData<'a>> {
        let import_directory_table_rva = dd.virtual_address as usize;
        debug!(
            "import_directory_table_rva {:#x}",
            import_directory_table_rva
        );
        let offset =
            &mut utils::find_offset(import_directory_table_rva, sections, file_alignment, opts)
                .ok_or_else(|| {
                    error::Error::Malformed(format!(
                "Cannot create ImportData; cannot map import_directory_table_rva {:#x} into offset",
                import_directory_table_rva
            ))
                })?;
        debug!("import data offset {:#x}", offset);
        let mut import_data = Vec::new();
        loop {
            let import_directory_entry: ImportDirectoryEntry =
                bytes.gread_with(offset, scroll::LE)?;
            debug!("{:#?}", import_directory_entry);
            if import_directory_entry.is_null() {
                break;
            } else {
                let entry = SyntheticImportDirectoryEntry::parse_with_opts::<T>(
                    bytes,
                    import_directory_entry,
                    sections,
                    file_alignment,
                    opts,
                )?;
                debug!("entry {:#?}", entry);
                import_data.push(entry);
            }
        }
        debug!("finished ImportData");
        Ok(ImportData { import_data })
    }
}

#[derive(Debug)]
/// A synthesized symbol import, the name is pre-indexed, and the binary offset is computed, as well as which dll it belongs to
pub struct Import<'a> {
    pub name: Cow<'a, str>,
    pub dll: &'a str,
    pub ordinal: u16,
    pub offset: usize,
    pub rva: usize,
    pub size: usize,
}

impl<'a> Import<'a> {
    pub fn parse<T: Bitfield<'a>>(
        _bytes: &'a [u8],
        import_data: &ImportData<'a>,
        _sections: &[section_table::SectionTable],
    ) -> error::Result<Vec<Import<'a>>> {
        let mut imports = Vec::new();
        for data in &import_data.import_data {
            if let Some(ref import_lookup_table) = data.import_lookup_table {
                let dll = data.name;
                let import_base = data.import_directory_entry.import_address_table_rva as usize;
                debug!("Getting imports from {}", &dll);
                for (i, entry) in import_lookup_table.iter().enumerate() {
                    let offset = import_base + (i * T::size_of());
                    use self::SyntheticImportLookupTableEntry::*;
                    let (rva, name, ordinal) = match *entry {
                        HintNameTableRVA((rva, ref hint_entry)) => {
                            // if hint_entry.name = "" && hint_entry.hint = 0 {
                            //     println!("<PE.Import> warning hint/name table rva from {} without hint {:#x}", dll, rva);
                            // }
                            (rva, Cow::Borrowed(hint_entry.name), hint_entry.hint)
                        }
                        OrdinalNumber(ordinal) => {
                            let name = format!("ORDINAL {}", ordinal);
                            (0x0, Cow::Owned(name), ordinal)
                        }
                    };
                    let import = Import {
                        name,
                        ordinal,
                        dll,
                        size: T::size_of(),
                        offset,
                        rva: rva as usize,
                    };
                    imports.push(import);
                }
            }
        }
        Ok(imports)
    }
}
<|endoftext|>


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

use rustc_serialize::json::{Json, ToJson, Object};
use {Point, MultiPoint, LineString, MultiLineString, Polygon, MultiPolygon, GeometryCollection, GeoJsonResult, GeoJsonError};

/// Geometry
#[derive(RustcEncodable, Clone, Debug)]
pub enum Geometry {
    Point(Point),
    MultiPoint(MultiPoint),
    LineString(LineString),
    MultiLineString(MultiLineString),
    Polygon(Polygon),
    MultiPolygon(MultiPolygon),
    GeometryCollection(GeometryCollection),
}

impl ToJson for Geometry {
    fn to_json(&self) -> Json {
        match *self {
            Geometry::Point(ref geom) => geom.to_json(),
            Geometry::MultiPoint(ref geom) => geom.to_json(),
            Geometry::LineString(ref geom) => geom.to_json(),
            Geometry::MultiLineString(ref geom) => geom.to_json(),
            Geometry::Polygon(ref geom) => geom.to_json(),
            Geometry::MultiPolygon(ref geom) => geom.to_json(),
            Geometry::GeometryCollection(ref geom) => geom.to_json(),
        }
    }
}

impl Geometry {
    pub fn from_json(json_geometry: &Object) -> GeoJsonResult<Geometry> {
        match expect_string!(expect_property!(json_geometry, "type", "Missing 'type' field")) {
            "Point" => Ok(Geometry::Point(try!(Point::from_json(json_geometry)))),
            "MultiPoint" => Ok(Geometry::MultiPoint(try!(MultiPoint::from_json(json_geometry)))),
            "LineString" => Ok(Geometry::LineString(try!(LineString::from_json(json_geometry)))),
            "MultiLineString" => Ok(Geometry::MultiLineString(try!(MultiLineString::from_json(json_geometry)))),
            "Polygon" => Ok(Geometry::Polygon(try!(Polygon::from_json(json_geometry)))),
            "MultiPolygon" => Ok(Geometry::MultiPolygon(try!(MultiPolygon::from_json(json_geometry)))),
            "GeometryCollection" => Ok(Geometry::GeometryCollection(try!(GeometryCollection::from_json(json_geometry)))),
            _ => Err(GeoJsonError::new("Unknown geometry type")),
        }
    }
}

#[cfg(test)]

mod tests {
    use rustc_serialize::json::Json;
    use Geometry;
    use GeoJsonResult;

    #[test]
    fn test_match_geometry_type() {
        fn geom(json_str: &str) -> GeoJsonResult<Geometry> {
            let json = Json::from_str(json_str).unwrap();
            return Geometry::from_json(expect_object!(json));
        }

        match geom("{\"coordinates\":[],\"type\":\"Point\"}") {
            Ok(Geometry::Point(ref _geom)) => (),
            _ => panic!("expected Point")
        };

        match geom("{\"coordinates\":[],\"type\":\"MultiPoint\"}") {
            Ok(Geometry::MultiPoint(ref _geom)) => (),
            _ => panic!("expected MultiPoint")
        };

        match geom("{\"coordinates\":[],\"type\":\"LineString\"}") {
            Ok(Geometry::LineString(ref _geom)) => (),
            _ => panic!("expected LineString")
        };

        match geom("{\"coordinates\":[],\"type\":\"MultiLineString\"}") {
            Ok(Geometry::MultiLineString(ref _geom)) => (),
            _ => panic!("expected MultiLineString")
        };

        match geom("{\"coordinates\":[],\"type\":\"Polygon\"}") {
            Ok(Geometry::Polygon(ref _geom)) => (),
            _ => panic!("expected Polygon")
        };

        match geom("{\"coordinates\":[],\"type\":\"MultiPolygon\"}") {
            Ok(Geometry::MultiPolygon(ref _geom)) => (),
            _ => panic!("expected MultiPolygon")
        };

        match geom("{\"geometries\":[],\"type\":\"GeometryCollection\"}") {
            Ok(Geometry::GeometryCollection(ref _geom)) => (),
            _ => panic!("expected GeometryCollection")
        };

        match geom("{\"type\":\"something else\"}") {
            Ok(_) => panic!("expected error value"),
            Err(_) => ()
        };

        match geom("{}") {
            Ok(_) => panic!("expected error value"),
            Err(_) => ()
        };
    }
}
<commit_msg>Separate some logic, refactor<commit_after>// Copyright 2015 The GeoRust Developers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//	http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use rustc_serialize::json::{Json, ToJson, Object};
use {Point, MultiPoint, LineString, MultiLineString, Polygon, MultiPolygon, GeometryCollection, GeoJsonResult, GeoJsonError};

/// Geometry
#[derive(RustcEncodable, Clone, Debug)]
pub enum Geometry {
    Point(Point),
    MultiPoint(MultiPoint),
    LineString(LineString),
    MultiLineString(MultiLineString),
    Polygon(Polygon),
    MultiPolygon(MultiPolygon),
    GeometryCollection(GeometryCollection),
}

impl ToJson for Geometry {
    fn to_json(&self) -> Json {
        match *self {
            Geometry::Point(ref geom) => geom.to_json(),
            Geometry::MultiPoint(ref geom) => geom.to_json(),
            Geometry::LineString(ref geom) => geom.to_json(),
            Geometry::MultiLineString(ref geom) => geom.to_json(),
            Geometry::Polygon(ref geom) => geom.to_json(),
            Geometry::MultiPolygon(ref geom) => geom.to_json(),
            Geometry::GeometryCollection(ref geom) => geom.to_json(),
        }
    }
}

impl Geometry {
    pub fn from_json(json_geometry: &Object) -> GeoJsonResult<Geometry> {
        let type_json = expect_property!(json_geometry, "type", "Missing 'type' field");
        let type_string = expect_string!(type_json);
        let geom = match type_string {
            "Point" => Geometry::Point(try!(Point::from_json(json_geometry))),
            "MultiPoint" => Geometry::MultiPoint(try!(MultiPoint::from_json(json_geometry))),
            "LineString" => Geometry::LineString(try!(LineString::from_json(json_geometry))),
            "MultiLineString" => Geometry::MultiLineString(try!(MultiLineString::from_json(json_geometry))),
            "Polygon" => Geometry::Polygon(try!(Polygon::from_json(json_geometry))),
            "MultiPolygon" => Geometry::MultiPolygon(try!(MultiPolygon::from_json(json_geometry))),
            "GeometryCollection" => Geometry::GeometryCollection(try!(GeometryCollection::from_json(json_geometry))),
            _ => return Err(GeoJsonError::new("Unknown geometry type")),
        };
        Ok(geom)
    }
}

#[cfg(test)]

mod tests {
    use rustc_serialize::json::Json;
    use Geometry;
    use GeoJsonResult;

    #[test]
    fn test_match_geometry_type() {
        fn geom(json_str: &str) -> GeoJsonResult<Geometry> {
            let json = Json::from_str(json_str).unwrap();
            return Geometry::from_json(expect_object!(json));
        }

        match geom("{\"coordinates\":[],\"type\":\"Point\"}") {
            Ok(Geometry::Point(ref _geom)) => (),
            _ => panic!("expected Point")
        };

        match geom("{\"coordinates\":[],\"type\":\"MultiPoint\"}") {
            Ok(Geometry::MultiPoint(ref _geom)) => (),
            _ => panic!("expected MultiPoint")
        };

        match geom("{\"coordinates\":[],\"type\":\"LineString\"}") {
            Ok(Geometry::LineString(ref _geom)) => (),
            _ => panic!("expected LineString")
        };

        match geom("{\"coordinates\":[],\"type\":\"MultiLineString\"}") {
            Ok(Geometry::MultiLineString(ref _geom)) => (),
            _ => panic!("expected MultiLineString")
        };

        match geom("{\"coordinates\":[],\"type\":\"Polygon\"}") {
            Ok(Geometry::Polygon(ref _geom)) => (),
            _ => panic!("expected Polygon")
        };

        match geom("{\"coordinates\":[],\"type\":\"MultiPolygon\"}") {
            Ok(Geometry::MultiPolygon(ref _geom)) => (),
            _ => panic!("expected MultiPolygon")
        };

        match geom("{\"geometries\":[],\"type\":\"GeometryCollection\"}") {
            Ok(Geometry::GeometryCollection(ref _geom)) => (),
            _ => panic!("expected GeometryCollection")
        };

        match geom("{\"type\":\"something else\"}") {
            Ok(_) => panic!("expected error value"),
            Err(_) => ()
        };

        match geom("{}") {
            Ok(_) => panic!("expected error value"),
            Err(_) => ()
        };
    }
}
<|endoftext|>


<commit_before>use std::{collections::VecDeque, rc::Rc};

use crate::{api::prelude::*, proc_macros::*, shell::prelude::WindowRequest, theme_default::prelude::*};

// --- KEYS --
pub static STYLE_WINDOW: &str = "window";
// --- KEYS --

// internal type to handle dirty widgets.
type DirtyWidgets = Vec<Entity>;

#[derive(Clone)]
enum Action {
    WindowEvent(WindowEvent),
    FocusEvent(FocusEvent),
}

// The `WindowState` handles the window events.
#[derive(Default, AsAny)]
struct WindowState {
    actions: VecDeque<Action>,
    background: Brush,
    title: String,
}

impl WindowState {
    fn push_action(&mut self, action: Action) {
        self.actions.push_front(action);
    }

    fn resize(&self, width: f64, height: f64, ctx: &mut Context) {
        Window::bounds_mut(&mut ctx.window()).set_size(width, height);
        Window::constraint_mut(&mut ctx.window()).set_size(width, height);
    }

    fn active_changed(&self, active: bool, ctx: &mut Context) {
        Window::active_set(&mut ctx.widget(), active);

        // if !active {
        //     // remove focus if the window is not active
        //     if let Some(focused_widget) = ctx.window().get::<Global>("global").focused_widget {
        //         ctx.window().get_mut::<Global>("global").focused_widget = None;
        //         if ctx.get_widget(focused_widget).has::<bool>("focused") {
        //             ctx.get_widget(focused_widget).set("focused", false);
        //             ctx.get_widget(focused_widget).update_theme_by_state(false);
        //         }
        //     }
        // }
    }

    fn request_focus(&self, entity: Entity, ctx: &mut Context) {
        let mut focus_state: FocusState = Window::focus_state_clone(&ctx.widget());
        focus_state.request_focus(entity, ctx);
        Window::focus_state_set(&mut ctx.widget(), focus_state);
    }

    fn remove_focus(&self, entity: Entity, ctx: &mut Context) {
        let mut focus_state: FocusState = Window::focus_state_clone(&ctx.widget());
        focus_state.remove_focus(entity, ctx);
        Window::focus_state_set(&mut ctx.widget(), focus_state);
    }

    fn set_background(&mut self, ctx: &mut Context) {
        let background: Brush = ctx.widget().clone("background");
        if let Brush::SolidColor(color) = background {
            ctx.render_context_2_d().set_background(color);
        };
        self.background = background;
    }
}

impl State for WindowState {
    fn init(&mut self, _: &mut Registry, ctx: &mut Context) {
        self.set_background(ctx);
        self.title = ctx.widget().clone("title");
    }

    fn update(&mut self, _: &mut Registry, ctx: &mut Context) {
        if self.background != *Window::background_ref(&ctx.widget()) {
            self.set_background(ctx);
        }

        let window = ctx.widget();

        if !self.title.eq(Window::title_ref(&window)) {
            self.title = Window::title_clone(&window);
            ctx.send_window_request(WindowRequest::ChangeTitle(self.title.clone()));
        }

        if let Some(action) = self.actions.pop_front() {
            match action {
                Action::WindowEvent(window_event) => match window_event {
                    WindowEvent::Resize { width, height } => {
                        self.resize(width, height, ctx);
                    }
                    WindowEvent::ActiveChanged(active) => {
                        self.active_changed(active, ctx);
                    }
                    _ => {}
                },
                Action::FocusEvent(focus_event) => match focus_event {
                    FocusEvent::RequestFocus(entity) => {
                        self.request_focus(entity, ctx);
                    }
                    FocusEvent::RemoveFocus(entity) => {
                        self.remove_focus(entity, ctx);
                    }
                },
            }
        }
    }
}

widget!(
    /// The `Window` widget provides access to the properties of an application window.
    /// It also contains global properties like keyboard modifier and focused widget.
    ///
    /// **style:** `window`
    Window<WindowState>: ActivateHandler {
        /// Sets or shares the background property.
        background: Brush,

        /// Sets or shares the title property.
        title: String,

        /// Sets or shares the resizeable property.
        resizeable: bool,

        /// Sets or shares the property if this window should always be on top.
        always_on_top: bool,

        /// Sets or shares the flag if the window is borderless.
        borderless: bool,

        /// Sets or shares a value that describes if the current window is active.
        active: bool,

        /// Access the current keyboard state e.g. to check modifiers.
        keyboard_state: KeyboardState,

        /// Access the current window theme.
        theme: Theme,

        /// Access the current focus state.
        focus_state: FocusState,

        /// Internal property to handle dirty widgets.
        dirty_widgets: DirtyWidgets
    }
);

impl Window {
    fn on_window_event<H: Fn(&mut StatesContext, WindowEvent) -> bool + 'static>(
        self,
        handler: H,
    ) -> Self {
        self.insert_handler(WindowEventHandler {
            handler: Rc::new(handler),
        })
    }

    fn on_focus_event<H: Fn(&mut StatesContext, FocusEvent) -> bool + 'static>(
        self,
        handler: H,
    ) -> Self {
        self.insert_handler(FocusEventHandler {
            handler: Rc::new(handler),
        })
    }
}

impl Template for Window {
    fn template(self, id: Entity, _: &mut BuildContext) -> Self {
        self.name("Window")
            .background(colors::BRIGHT_GRAY_COLOR)
            .size(100.0, 100.0)
            .style(STYLE_WINDOW)
            .title("Window")
            .resizeable(false)
            .always_on_top(false)
            .on_window_event(move |ctx, event| {
                ctx.get_mut::<WindowState>(id)
                    .push_action(Action::WindowEvent(event));
                true
            })
            .on_focus_event(move |ctx, event| {
                ctx.get_mut::<WindowState>(id)
                    .push_action(Action::FocusEvent(event));
                true
            })
    }

    fn render_object(&self) -> Box<dyn RenderObject> {
        RectangleRenderObject.into()
    }

    fn layout(&self) -> Box<dyn Layout> {
        GridLayout::new().into()
    }
}
<commit_msg>cargo fmt<commit_after>use std::{collections::VecDeque, rc::Rc};

use crate::{
    api::prelude::*, proc_macros::*, shell::prelude::WindowRequest, theme_default::prelude::*,
};

// --- KEYS --
pub static STYLE_WINDOW: &str = "window";
// --- KEYS --

// internal type to handle dirty widgets.
type DirtyWidgets = Vec<Entity>;

#[derive(Clone)]
enum Action {
    WindowEvent(WindowEvent),
    FocusEvent(FocusEvent),
}

// The `WindowState` handles the window events.
#[derive(Default, AsAny)]
struct WindowState {
    actions: VecDeque<Action>,
    background: Brush,
    title: String,
}

impl WindowState {
    fn push_action(&mut self, action: Action) {
        self.actions.push_front(action);
    }

    fn resize(&self, width: f64, height: f64, ctx: &mut Context) {
        Window::bounds_mut(&mut ctx.window()).set_size(width, height);
        Window::constraint_mut(&mut ctx.window()).set_size(width, height);
    }

    fn active_changed(&self, active: bool, ctx: &mut Context) {
        Window::active_set(&mut ctx.widget(), active);

        // if !active {
        //     // remove focus if the window is not active
        //     if let Some(focused_widget) = ctx.window().get::<Global>("global").focused_widget {
        //         ctx.window().get_mut::<Global>("global").focused_widget = None;
        //         if ctx.get_widget(focused_widget).has::<bool>("focused") {
        //             ctx.get_widget(focused_widget).set("focused", false);
        //             ctx.get_widget(focused_widget).update_theme_by_state(false);
        //         }
        //     }
        // }
    }

    fn request_focus(&self, entity: Entity, ctx: &mut Context) {
        let mut focus_state: FocusState = Window::focus_state_clone(&ctx.widget());
        focus_state.request_focus(entity, ctx);
        Window::focus_state_set(&mut ctx.widget(), focus_state);
    }

    fn remove_focus(&self, entity: Entity, ctx: &mut Context) {
        let mut focus_state: FocusState = Window::focus_state_clone(&ctx.widget());
        focus_state.remove_focus(entity, ctx);
        Window::focus_state_set(&mut ctx.widget(), focus_state);
    }

    fn set_background(&mut self, ctx: &mut Context) {
        let background: Brush = ctx.widget().clone("background");
        if let Brush::SolidColor(color) = background {
            ctx.render_context_2_d().set_background(color);
        };
        self.background = background;
    }
}

impl State for WindowState {
    fn init(&mut self, _: &mut Registry, ctx: &mut Context) {
        self.set_background(ctx);
        self.title = ctx.widget().clone("title");
    }

    fn update(&mut self, _: &mut Registry, ctx: &mut Context) {
        if self.background != *Window::background_ref(&ctx.widget()) {
            self.set_background(ctx);
        }

        let window = ctx.widget();

        if !self.title.eq(Window::title_ref(&window)) {
            self.title = Window::title_clone(&window);
            ctx.send_window_request(WindowRequest::ChangeTitle(self.title.clone()));
        }

        if let Some(action) = self.actions.pop_front() {
            match action {
                Action::WindowEvent(window_event) => match window_event {
                    WindowEvent::Resize { width, height } => {
                        self.resize(width, height, ctx);
                    }
                    WindowEvent::ActiveChanged(active) => {
                        self.active_changed(active, ctx);
                    }
                    _ => {}
                },
                Action::FocusEvent(focus_event) => match focus_event {
                    FocusEvent::RequestFocus(entity) => {
                        self.request_focus(entity, ctx);
                    }
                    FocusEvent::RemoveFocus(entity) => {
                        self.remove_focus(entity, ctx);
                    }
                },
            }
        }
    }
}

widget!(
    /// The `Window` widget provides access to the properties of an application window.
    /// It also contains global properties like keyboard modifier and focused widget.
    ///
    /// **style:** `window`
    Window<WindowState>: ActivateHandler {
        /// Sets or shares the background property.
        background: Brush,

        /// Sets or shares the title property.
        title: String,

        /// Sets or shares the resizeable property.
        resizeable: bool,

        /// Sets or shares the property if this window should always be on top.
        always_on_top: bool,

        /// Sets or shares the flag if the window is borderless.
        borderless: bool,

        /// Sets or shares a value that describes if the current window is active.
        active: bool,

        /// Access the current keyboard state e.g. to check modifiers.
        keyboard_state: KeyboardState,

        /// Access the current window theme.
        theme: Theme,

        /// Access the current focus state.
        focus_state: FocusState,

        /// Internal property to handle dirty widgets.
        dirty_widgets: DirtyWidgets
    }
);

impl Window {
    fn on_window_event<H: Fn(&mut StatesContext, WindowEvent) -> bool + 'static>(
        self,
        handler: H,
    ) -> Self {
        self.insert_handler(WindowEventHandler {
            handler: Rc::new(handler),
        })
    }

    fn on_focus_event<H: Fn(&mut StatesContext, FocusEvent) -> bool + 'static>(
        self,
        handler: H,
    ) -> Self {
        self.insert_handler(FocusEventHandler {
            handler: Rc::new(handler),
        })
    }
}

impl Template for Window {
    fn template(self, id: Entity, _: &mut BuildContext) -> Self {
        self.name("Window")
            .background(colors::BRIGHT_GRAY_COLOR)
            .size(100.0, 100.0)
            .style(STYLE_WINDOW)
            .title("Window")
            .resizeable(false)
            .always_on_top(false)
            .on_window_event(move |ctx, event| {
                ctx.get_mut::<WindowState>(id)
                    .push_action(Action::WindowEvent(event));
                true
            })
            .on_focus_event(move |ctx, event| {
                ctx.get_mut::<WindowState>(id)
                    .push_action(Action::FocusEvent(event));
                true
            })
    }

    fn render_object(&self) -> Box<dyn RenderObject> {
        RectangleRenderObject.into()
    }

    fn layout(&self) -> Box<dyn Layout> {
        GridLayout::new().into()
    }
}
<|endoftext|>