<commit_before>extern crate nalgebra as na;
extern crate rand;

use na::{Point3, Vector3, Rotation3, UnitQuaternion, Rotation};
use rand::random;

#[test]
fn test_quaternion_as_matrix() {
    for _ in 0usize .. 10000 {
        let axis_angle: Vector3<f64> = random();

        assert!(na::approx_eq(&UnitQuaternion::new(axis_angle).to_rotation_matrix(), &Rotation3::new(axis_angle)))
    }
}

#[test]
fn test_quaternion_mul_vec_or_point_as_matrix() {
    for _ in 0usize .. 10000 {
        let axis_angle: Vector3<f64> = random();
        let vector: Vector3<f64> = random();
        let point: Point3<f64> = random();

        let matrix  = Rotation3::new(axis_angle);
        let quaternion = UnitQuaternion::new(axis_angle);

        assert!(na::approx_eq(&(matrix * vector), &(quaternion * vector)));
        assert!(na::approx_eq(&(matrix * point), &(quaternion * point)));
        assert!(na::approx_eq(&(vector * matrix), &(vector * quaternion)));
        assert!(na::approx_eq(&(point * matrix), &(point * quaternion)));
    }
}

#[test]
fn test_quaternion_div_quaternion() {
    for _ in 0usize .. 10000 {
        let axis_angle1: Vector3<f64> = random();
        let axis_angle2: Vector3<f64> = random();

        let r1 = Rotation3::new(axis_angle1);
        let r2 = na::inverse(&Rotation3::new(axis_angle2)).unwrap();

        let q1 = UnitQuaternion::new(axis_angle1);
        let q2 = UnitQuaternion::new(axis_angle2);

        assert!(na::approx_eq(&(q1 / q2).to_rotation_matrix(), &(r1 * r2)))
    }
}

#[test]
fn test_quaternion_to_axis_angle() {
    for _ in 0usize .. 10000 {
        let axis_angle: Vector3<f64> = random();

        let q = UnitQuaternion::new(axis_angle);

        println!("{:?} {:?}", q.rotation(), axis_angle);
        assert!(na::approx_eq(&q.rotation(), &axis_angle))
    }
}

#[test]
fn test_quaternion_euler_angles() {
    for _ in 0usize .. 10000 {
        let angles: Vector3<f64> = random();

        let q = UnitQuaternion::new_with_euler_angles(angles.x, angles.y, angles.z);
        let m = Rotation3::new_with_euler_angles(angles.x, angles.y, angles.z);

        assert!(na::approx_eq(&q.to_rotation_matrix(), &m))
    }
}

#[test]
fn test_quaternion_rotation_between() {
    let q1: UnitQuaternion<f64> = random();
    let q2: UnitQuaternion<f64> = random();

    let delta = na::rotation_between(&q1, &q2);

    assert!(na::approx_eq(&(delta * q1), &q2))
}

#[test]
fn test_quaternion_angle_between() {
    let q1: UnitQuaternion<f64> = random();
    let q2: UnitQuaternion<f64> = random();

    let delta = na::rotation_between(&q1, &q2);
    let delta_angle = na::angle_between(&q1, &q2);

    assert!(na::approx_eq(&na::norm(&na::rotation(&delta)), &delta_angle))
}
<commit_msg>Added a test for Quaternion::exp().<commit_after>extern crate nalgebra as na;
extern crate rand;

use na::{Point3, Quaternion, Vector3, Rotation3, UnitQuaternion, Rotation, one};
use rand::random;

#[test]
fn test_quaternion_as_matrix() {
    for _ in 0usize .. 10000 {
        let axis_angle: Vector3<f64> = random();

        assert!(na::approx_eq(&UnitQuaternion::new(axis_angle).to_rotation_matrix(), &Rotation3::new(axis_angle)))
    }
}

#[test]
fn test_quaternion_mul_vec_or_point_as_matrix() {
    for _ in 0usize .. 10000 {
        let axis_angle: Vector3<f64> = random();
        let vector: Vector3<f64> = random();
        let point: Point3<f64> = random();

        let matrix  = Rotation3::new(axis_angle);
        let quaternion = UnitQuaternion::new(axis_angle);

        assert!(na::approx_eq(&(matrix * vector), &(quaternion * vector)));
        assert!(na::approx_eq(&(matrix * point), &(quaternion * point)));
        assert!(na::approx_eq(&(vector * matrix), &(vector * quaternion)));
        assert!(na::approx_eq(&(point * matrix), &(point * quaternion)));
    }
}

#[test]
fn test_quaternion_div_quaternion() {
    for _ in 0usize .. 10000 {
        let axis_angle1: Vector3<f64> = random();
        let axis_angle2: Vector3<f64> = random();

        let r1 = Rotation3::new(axis_angle1);
        let r2 = na::inverse(&Rotation3::new(axis_angle2)).unwrap();

        let q1 = UnitQuaternion::new(axis_angle1);
        let q2 = UnitQuaternion::new(axis_angle2);

        assert!(na::approx_eq(&(q1 / q2).to_rotation_matrix(), &(r1 * r2)))
    }
}

#[test]
fn test_quaternion_to_axis_angle() {
    for _ in 0usize .. 10000 {
        let axis_angle: Vector3<f64> = random();

        let q = UnitQuaternion::new(axis_angle);

        println!("{:?} {:?}", q.rotation(), axis_angle);
        assert!(na::approx_eq(&q.rotation(), &axis_angle))
    }
}

#[test]
fn test_quaternion_euler_angles() {
    for _ in 0usize .. 10000 {
        let angles: Vector3<f64> = random();

        let q = UnitQuaternion::new_with_euler_angles(angles.x, angles.y, angles.z);
        let m = Rotation3::new_with_euler_angles(angles.x, angles.y, angles.z);

        assert!(na::approx_eq(&q.to_rotation_matrix(), &m))
    }
}

#[test]
fn test_quaternion_rotation_between() {
    let q1: UnitQuaternion<f64> = random();
    let q2: UnitQuaternion<f64> = random();

    let delta = na::rotation_between(&q1, &q2);

    assert!(na::approx_eq(&(delta * q1), &q2))
}

#[test]
fn test_quaternion_angle_between() {
    let q1: UnitQuaternion<f64> = random();
    let q2: UnitQuaternion<f64> = random();

    let delta = na::rotation_between(&q1, &q2);
    let delta_angle = na::angle_between(&q1, &q2);

    assert!(na::approx_eq(&na::norm(&na::rotation(&delta)), &delta_angle))
}

#[test]
fn test_quaternion_exp_zero_is_one() {
    let q = Quaternion::new(0., 0., 0., 0.);
    assert!(na::approx_eq(&q.exp(), &one()))
}
<|endoftext|>


<commit_before>//! Simple packet-based protocol definitions in Rust.
//!
//! * The `Parcel` trait defines any type that can be serialized
//!   to a connection.
//! * The `wire` module deals with transmission of `Parcel`s.

pub use self::enum_ty::Enum;
pub use self::parcel::Parcel;
pub use self::errors::{Error, ErrorKind, ResultExt, CharTryFromError, TryFromIntError};
pub use self::settings::*;

// Must go first because it defines common macros.
#[macro_use]
mod packet;

mod settings;
#[macro_use]
pub mod types;
#[macro_use]
pub mod wire;

mod enum_ty;
mod errors;
mod parcel;
pub mod util;

extern crate byteorder;
extern crate flate2;
#[macro_use]
extern crate error_chain;

#[cfg(feature = "uuid")]
extern crate uuid;
extern crate num_traits;

/// The default byte ordering.
pub type DefaultByteOrder = ::byteorder::BigEndian;

<commit_msg>Add documentation<commit_after>//! Simple packet-based protocol definitions in Rust.
//!
//! * The `Parcel` trait defines any type that can be serialized
//!   to a connection.
//! * The `wire` module deals with transmission of `Parcel`s.
//!
//! # Examples
//!
//! ## Get the raw bytes representing a parcel.
//!
//! ```
//! extern crate protocol;
//! #[macro_use] extern crate protocol_derive;
//!
//! #[derive(Protocol, Debug, PartialEq)]
//! pub struct Health(pub u8);
//!
//! #[derive(Protocol, Debug, PartialEq)]
//! pub struct Player {
//!     pub name: String,
//!     pub health: Health,
//!     pub position: (i16, i16, i16),
//!     pub velocity: (i16, i16, i16),
//! }
//!
//! use protocol::Parcel;
//!
//! assert_eq!(vec![
//!     0, 0, 0, 3, // "Bob" string length prefix
//!     b'B', b'o', b'b', // The string "Bob".
//!     100, // The health byte.
//!     0, 10, 0, 81, 255, 151, // 2-byte x-position, y-position, and z-position.
//!     0, 0, 0, 0, 0, 0, // 2-byte x-velocity, y-velocity, and z-velocity.
//! ], Player {
//!     name: "Bob".to_owned(),
//!     health: Health(100),
//!     position: (10, 81, -105),
//!     velocity: (0, 0, 0),
//! }.raw_bytes(&protocol::Settings::default()).unwrap());
//! ```
//!
//! ## Enums
//!
//! Every enum has a value for each variant used to distinguish
//! between each variant. This can be as simple as a 32-bit integer
//! representing the variant's index. This is called the discriminator.
//!
//! ### String discriminators
//!
//! ```
//! extern crate protocol;
//! #[macro_use] extern crate protocol_derive;
//!
//! // This enum, like all enums, defaults to using String discriminators.
//! #[derive(Protocol, Debug, PartialEq)]
//! enum Foo { A, B, C }
//!
//! // This enum explicitly specifies a string discriminant.
//! //
//! // This is the default anyway and thus it is identical in layout
//! // to the previous enum.
//! #[derive(Protocol, Debug, PartialEq)]
//! #[protocol(discriminant = "string")]
//! enum Bar { A, B, C }
//! ```
//!
//! By default, enums have `String` discriminators. This means that
//! when serializing enum values to and from bytes, unless otherwise
//! specified the variant's full name will be transmitted as a string.
//!
//! This allows variants to be added, modified, or reordered in
//! a backwards-compatible way.
//!
//! ### Integer discriminators
//!
//! ```
//! extern crate protocol;
//! #[macro_use] extern crate protocol_derive;
//!
//! // An enum with integer discriminators.
//! #[derive(Protocol, Debug, PartialEq)]
//! #[protocol(discriminant = "integer")]
//! enum Baz {
//!     Fizz = 0, // override the default first discriminator of 1
//!     Buzz, // defaults to `prior discriminator + 1`, therefore has a discriminator of 1.
//!     Bing, // defaults to `prior discriminator + 1`, therefore has a discriminator of 2.
//!     Boo = 1234,
//!     Beez, // defaults to `prior discriminator + 1`, therefore has a discriminator of 1235.
//! }
//!
//! use protocol::{Enum, Parcel};
//!
//! // By default, integer discriminators are 32-bits.
//! assert_eq!([0u8, 0, 0, 2], &Baz::Bing.discriminator().raw_bytes(&protocol::Settings::default()).unwrap()[..]);
//!
//! assert_eq!(0, Baz::Fizz.discriminator());
//! assert_eq!(1, Baz::Buzz.discriminator());
//! assert_eq!(2, Baz::Bing.discriminator());
//! assert_eq!(1234, Baz::Boo.discriminator());
//! assert_eq!(1235, Baz::Beez.discriminator());
//! ```
//!
//! It is possible to set the underlying integer type via the `#[repr(<type>)]` attribute.
//!
//! ```
//! extern crate protocol;
//! #[macro_use] extern crate protocol_derive;
//!
//! #[derive(Protocol, Debug, PartialEq)]
//! #[protocol(discriminant = "integer")]
//! #[repr(u8)] // Force discriminators to be 8-bit.
//! pub enum Hello {
//!     World(String), SolarSystem(String), LocalGroup(String),
//!     Universe(String), Everything(String),
//! }
//!
//! use protocol::{Enum, Parcel};
//!
//! assert_eq!([2, // 1-byte discriminator
//!             0, 0, 0, 3, // string length
//!             b'f', b'o', b'o', // the string
//!             ], &Hello::SolarSystem("foo".to_owned()).raw_bytes(&protocol::Settings::default()).unwrap()[..]);
//! ```
//!
//! Discriminators can be overriden on a per-variant basis via
//! the `#[protocol(discriminator(<value>))]` attribute.
//!
//! In the case of trivial enums with no fields,
//! the `Variant = <discriminator>` syntax may be used.
//!
//! ```
//! extern crate protocol;
//! #[macro_use] extern crate protocol_derive;
//!
//! #[derive(Protocol, Debug, PartialEq)]
//! #[protocol(discriminant = "integer")]
//! #[repr(u8)] // Force discriminators to be 8-bit.
//! enum Numbered {
//!     A = 1, B = 2, C = 111, D = 67,
//! }
//!
//! #[derive(Protocol, Debug, PartialEq)]
//! #[protocol(discriminant = "integer")]
//! #[repr(u8)] // Force discriminators to be 8-bit.
//! pub enum Hello {
//!     World(String), SolarSystem(String), LocalGroup(String),
//!     #[protocol(discriminator(100))]
//!     Universe(String),
//!     Everything(String),
//! }
//!
//! use protocol::{Enum, Parcel};
//!
//! assert_eq!(111, Numbered::C.discriminator());
//! assert_eq!(67, Numbered::D.discriminator());
//!
//! assert_eq!(1, Hello::World("foo".to_owned()).discriminator());
//! assert_eq!(100, Hello::Universe("foo".to_owned()).discriminator());
//! assert_eq!(101, Hello::Everything("foo".to_owned()).discriminator());
//!
//! ```

pub use self::enum_ty::Enum;
pub use self::parcel::Parcel;
pub use self::errors::{Error, ErrorKind, ResultExt, CharTryFromError, TryFromIntError};
pub use self::settings::*;

// Must go first because it defines common macros.
#[macro_use]
mod packet;

mod settings;
#[macro_use]
pub mod types;
#[macro_use]
pub mod wire;

mod enum_ty;
mod errors;
mod parcel;
pub mod util;

extern crate byteorder;
extern crate flate2;
#[macro_use]
extern crate error_chain;

#[cfg(feature = "uuid")]
extern crate uuid;
extern crate num_traits;

/// The default byte ordering.
pub type DefaultByteOrder = ::byteorder::BigEndian;

<|endoftext|>


<commit_before>#![feature(proc_macro, conservative_impl_trait, generators)]
extern crate bytes;
extern crate futures_await as futures;
extern crate tokio_core;
extern crate tokio_io;
extern crate time;

mod cmd;
mod ftp;
mod error;
mod codec;

use std::result;
use std::io;
use std::env;
use futures::prelude::*;
use tokio_core::reactor::Core;
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use tokio_core::reactor::Handle;
use tokio_core::net::TcpListener;

use tokio_core::net::TcpStream;

use futures::{Sink, Stream};
use futures::stream::SplitSink;

use tokio_io::AsyncRead;
use tokio_io::codec::Framed;
use codec::FtpCodec;
use error::Result;
use ftp::{Answer, ResultCode};
use std::path::PathBuf;
use std::path::StripPrefixError;

use futures::stream::SplitStream;
use codec::BytesCodec;

use cmd::Command;
use cmd::TransferType;

use std::ffi::OsString;
use std::fs::{create_dir, remove_dir_all};
use std::fs::Metadata;

type DataReader = SplitStream<Framed<TcpStream, BytesCodec>>;
type DataWriter = SplitSink<Framed<TcpStream,BytesCodec>>;
type Writer = SplitSink<Framed<TcpStream, FtpCodec>>;

fn get_parent(path: PathBuf) -> Option<PathBuf> {
    path.parent().map(|p| p.to_path_buf())
}

fn get_filename(path: PathBuf) -> Option<OsString> {
    path.file_name().map(|p| p.to_os_string())
}

struct Client {
    data_port: Option<u16>,
    data_reader: Option<DataReader>,
    data_writer: Option<DataWriter>,
    handle: Handle,
    cwd: PathBuf,
    server_root: PathBuf,
    transfer_type: TransferType,
    writer: Writer,
}

impl Client {
    fn new(handle: Handle, writer: Writer, server_root: PathBuf) -> Client {
        Client {
            data_port: None,
            data_reader: None,
            data_writer: None,
            handle,
            cwd: PathBuf::from("/"),
            server_root,
            transfer_type: TransferType::Ascii,
            writer,
        }
    }
    #[async]
    fn send(mut self, answer: Answer) -> Result<Self> {
        self.writer = await!(self.writer.send(answer))?;
        Ok(self)
    }

    fn complete_path(self, path: PathBuf) -> (Self, result::Result<PathBuf, io::Error>) {
        let directory = self.server_root.join(if path.has_root() {
            path.iter().skip(1).collect()
        } else {
            path
        });
        let dir = directory.canonicalize();
        if let Ok(ref dir) = dir {
            if !dir.starts_with(&self.server_root) {
                return (self, Err(io::ErrorKind::PermissionDenied.into()));
            }
        }
        (self,dir)
    }
    fn strip_prefix(self, dir:PathBuf) -> (Self, result::Result<PathBuf, StripPrefixError>) {
        let res = dir.strip_prefix(&self.server_root).map(|p| p.to_path_buf());
        (self,res)
    }

    #[async]
    fn cwd(mut self, directory: PathBuf) -> Result<Self>{
        let path = self.cwd.join(&directory);
        let (new_self, res) = self.complete_path(path);
        self = new_self;
        if let Ok(dir) = res {
            let (new_self, res) = self.strip_prefix(dir);
            self = new_self;
            if let Ok(prefix) = res {
                self.cwd = prefix.to_path_buf();
                self = await!(self.send(Answer::new(ResultCode::Ok, &format!("Directory changed to \"{}\"", directory.display()))))?;
                return Ok(self)
            }
        }
        self = await!(self.send(Answer::new(ResultCode::FileNotFound, "No such file or directory")))?;
        return Ok(self);
    }
    #[async]
    fn pasv(mut self) -> Result<Self> {
        let port =
            if let Some(port) = self.data_port {
                port
            } else {
                0
            };
        if self.data_writer.is_some() {
            self = await!(self.send(Answer::new(ResultCode::DataConnectionAlreadyOpen,"Already listening...")))?;
            return Ok(self);
        }

        let addr= SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127,0,0,1)),port);
        let listener = TcpListener::bind(&addr, &self.handle)?;
        let port = listener.local_addr()?.port();

        self = await!(self.send(Answer::new(ResultCode::EnteringPassiveMode, &format!("127,0,0,1,{},{}", port>>8, port & 0xFF))))?;

        println!("Waiting clients on the port {}...", port);

        #[async]
        for(stream, _rest) in listener.incoming()  {
            let (writer, reader) = stream.framed(BytesCodec).split();
            self.data_writer = Some(writer);
            self.data_reader = Some(reader);
            break;
        }
        Ok(self)
    }

    #[async]
    fn quit(mut self) -> Result<Self> {
        if self.data_writer.is_some() {
            unimplemented!();
        } else {
            self = await!(self.send(Answer::new(ResultCode::ServiceClosingControlConnection, "Closing connection...")))?;
            self.writer.close()?;
        }
        Ok(self)
    }

    #[async]
    fn mkd(mut self, path: PathBuf) -> Result<Self> {
        let path = self.cwd.join(&path);
        let parent = get_parent(path.clone());
        if let Some(parent) = parent {
            let parent = parent.to_path_buf();
            let(new_self, res) = self.complete_path(parent);
            self = new_self;
            if let Ok(mut dir) = res {
                if dir.is_dir() {
                    let filename = get_filename(path);
                    if let Some(filename) = filename {
                        dir.push(filename);
                        if create_dir(dir).is_ok() {
                            self = await!(self.send(Answer::new(ResultCode::PATHNAMECreated,"Folder sucessfully crated!")))?;
                            return Ok(self);
                        }
                    }
                }
            }
        }
        self = await!(self.send(Answer::new(ResultCode::FileNotFound, "Could not create folder")))?;
        Ok(self)
    }

    #[async]
    fn rmd(mut self, directory: PathBuf) -> Result<Self> {
        let path = self.cwd.join(&directory);
        let (new_self, res) = self.complete_path(path);
        self = new_self;
        if let Ok(dir) = res {
            if remove_dir_all(dir).is_ok() {
                self = await!(self.send(Answer::new(ResultCode::RequestedFileActionOkay,
                                                    "Folder successfully removed")))?;
                return Ok(self);
            }
        }
        self = await!(self.send(Answer::new(ResultCode::FileNotFound,
                                            "Couldn't remove folder")))?;
        Ok(self)
    }

    #[async]
    fn send_data(mut self, data: Vec<u8>) -> Result<Self> {
        if let Some(writer) = self.data_writer {
            self.data_writer = Some(await!(writer.send(data))?);
        }
        Ok(self);
    }

    fn close_data_connection(&mut self) {
        self.data_reader = None;
        self.data_writer = None;
    }
    const MONTHS: [&'static str; 12] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};

    fn add_file_info(path: PathBuf, out: &mut Vec<u8>) {
        let extra = if path.is_dir() {"/"} else {""};
        let is_dir = if path.is_dir() {"d"}  else {"-"};
        let meta = match ::std::fs::metadata(&path) {
            Ok(meta) => path,
            _=> return,
        },
        _=>return,
    };
    let rights = if meta.permissions().readonly() {
        "r--r--r--"
    } else {
        "rw-rw-rw-"
    };
    let file_str = format!("{is_dir}{rights} {links} {owner} {group} {size} {month} {day} {hour}:{min} {path}{extra}\r\n", is_dir=is_dir, rights=rights, links=1,
                           owner="anonymous",
                           group="anonymous",
                           month=MONTS[time.tm_mon as usize],
                           day=time.tm_mday,
                           hour=time.tm_hour,
                           min=time.tm_min,
                           path=path,
                           extra=extra);
    out.extend(file_str.as_bytes());
    println!("==> {:?}", &file_str);

}
#[async]
fn list(mut self, path: Option<PathBuf>) -> Result<Self> {
    if self.data_writer.is_some() {
        let path = self.cwd.join(path.unwrap_or_default());
        let directory = PathBuf::from(&path);
        let (new_self, res) = self.complete_path(directory);
        self = ne_self;
        if  let Ok(path) = res {
            self = await!(self.send(Answer::new(ResultCode::DataConnectionAlreadyOpen, "Starting to list directory...")));

            let mut out = vec![];
            if path.is_dir() {
                if let Ok(dir) = read_dir(path) {
                    for entry in dir {
                        if let Ok(entry) = entry {
                            add_file_info(entry.path(),&mut out);
                        }
                    }
                } else {
                    self =  await!(self.send(Answer::new(ResultCode::InvalidParameterOrArgument, "No such file or directory")))?;
                    return Ok(self);
                }
            } else {
                add_file_info(entry.path(),&mut out);
            }
            self = await!(self.send_data(out))?;
            println!("-> and done!");
        } else {
            self = await!(self.send(Answer::new(ResultCode::InvalidParameterOrArgument)))?;
        }
    } else {
        self = await!(self.send(Answer::new(ResultCode::ConnectionClosed, "No opened data connection")))?;
    }
    if self.data_writer.is_some() {
        self.close_data_connection();
        self = await!(self.send(Answer::new(ResultCode::ClosingDataConnection, "Transfer done")))?;
    }
    Ok(self);
}

#[cfg(windows)]
fn get_file_info(meta: &Metadata) -> (time::Tm, u64) {
    use std::os::windows::prelude::*;
    (time::at(time::Timespec::new(meta.last_write_time())),
    meta.file_size())
}
#[cfg(not(windows))]
fn get_file_info(meta: &Metadata) -> (time::Tm, u64) {
    use std::os::unix::prelude::*;
    (time::at(time::Timespec::new(meta.mtime(), 0)), meta.size())
}

#[async]
fn handle_cmd(mut self, cmd: Command) -> Result<Self> {
    println!("Received command: {:?}", cmd);
    match cmd {
        Command::List(path) => self = await!(self.list(path))?,
        Command::Mkd(path) => self = await!(self.mkd(path))?,
        Command::Rmd(path) => self = await!(self.rmd(path))?,
        Command::Quit => self = await!(self.quit())?,
        Command::Pasv => self = await!(self.pasv())?,
        Command::Type(typ) => {
            self.transfer_type = typ;
            self = await!(self.send(Answer::new(ResultCode::Ok, "Transfer type changed successfully")))?;
        },
        Command::Cwd(directory) => {
            self= await!(self.cwd(directory))?;
        },
        Command::User(content) => {
            if content.is_empty() {
                self = await!(self.send(Answer::new(ResultCode::InvalidParameterOrArgument,"Invalid username")))?;
            } else {
                self = await!(self.send(Answer::new(ResultCode::UserloggedIn,&format!("Welcome {}",content))))?;
            }
        },
        Command::Pwd => {
            let msg = format!("{}", self.cwd.to_str().unwrap_or(""));
            if !msg.is_empty() {
                let message = format!("\"/{}\" ",msg);
                self = await!(self.send(Answer::new(ResultCode::PATHNAMECreated, &message)))?;
            } else {
                self = await!(self.send(Answer::new(ResultCode::FileNotFound, "No such file or directory")))?;
            }
        },
        Command::Unknown(s) => self = await!(self.send(Answer::new(ResultCode::UnknownCommand, &format!("\"{}\": Not Implemented",s))))?,
        _=>  self = await!(self.send(Answer::new(ResultCode::CommandNotImplemented, "Not implemented")))?,
    }
    return Ok(self);
}
}

#[async]
fn client(stream: TcpStream, handle: Handle,  server_root: PathBuf) -> Result<()>{
    let (writer, reader) = stream.framed(FtpCodec).split();
    let writer = await!(writer.send(Answer::new(ResultCode::ServiceReadyForNewUser, "Welcome to this FTP Server!")))?;
    let mut client = Client::new(handle, writer, server_root);
    #[async]
    for cmd in reader {
        client = await!(client.handle_cmd(cmd))?;
    }
    println!("Client closed");
    return Ok(());;
}

#[async]
fn handle_client(stream: TcpStream, handle: Handle, server_root: PathBuf) -> result::Result<(), ()> {
    await!(client(stream, handle, server_root)).map_err(|error| println!("Error handling client: {}", error))
}

#[async]
fn server(handle: Handle, server_root: PathBuf) -> io::Result<()> {
    let port = 1234;
    let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127,0,0,1)), port);
    let listener = TcpListener::bind(&addr,&handle)?;
    println!("Waiting clients on port {}...",port);
    #[async]
    for (stream, addr) in listener.incoming() {
        let address = format!("[address : {}]",addr);
        println!("New client {}", address);
        handle.spawn(handle_client(stream, handle.clone() ,server_root.clone()));
        println!("Waiting another client...");
    }
    return Ok(());
}

fn main() {
    let mut core = Core::new().expect("Cannot create tokio Core");
    let handle = core.handle();
    match env::current_dir() {
        Ok(server_root) => {
            if let Err(error) = core.run(server(handle, server_root)) {
                println!("Error running the server: {}", error);
            }
        },
        Err(e) => println!("Could not start server: {:?}",e),

    }

}
<commit_msg>Fixed error in rust code to execute server<commit_after>#![feature(proc_macro, conservative_impl_trait, generators)]
extern crate bytes;
extern crate futures_await as futures;
extern crate tokio_core;
extern crate tokio_io;
extern crate time;

mod cmd;
mod ftp;
mod error;
mod codec;

use std::result;
use std::io;
use std::env;
use futures::prelude::*;
use tokio_core::reactor::Core;
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use tokio_core::reactor::Handle;
use tokio_core::net::TcpListener;

use tokio_core::net::TcpStream;

use futures::{Sink, Stream};
use futures::stream::SplitSink;

use tokio_io::AsyncRead;
use tokio_io::codec::Framed;
use codec::FtpCodec;
use error::Result;
use ftp::{Answer, ResultCode};
use std::path::PathBuf;
use std::path::StripPrefixError;

use futures::stream::SplitStream;
use codec::BytesCodec;

use cmd::Command;
use cmd::TransferType;

use std::ffi::OsString;
use std::fs::{create_dir, remove_dir_all, read_dir};
use std::fs::Metadata;

type DataReader = SplitStream<Framed<TcpStream, BytesCodec>>;
type DataWriter = SplitSink<Framed<TcpStream,BytesCodec>>;
type Writer = SplitSink<Framed<TcpStream, FtpCodec>>;

 const MONTHS: [&'static str; 12] = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];

fn get_parent(path: PathBuf) -> Option<PathBuf> {
    path.parent().map(|p| p.to_path_buf())
}

fn get_filename(path: PathBuf) -> Option<OsString> {
    path.file_name().map(|p| p.to_os_string())
}

#[cfg(windows)]
fn get_file_info(meta: &Metadata) -> (time::Tm, u64) {
    use std::os::windows::prelude::*;
    (time::at(time::Timespec::new((meta.last_write_time() / 10_000_000) as i64, 0)),
              meta.file_size())
}
#[cfg(not(windows))]
fn get_file_info(meta: &Metadata) -> (time::Tm, u64) {
    use std::os::unix::prelude::*;
    (time::at(time::Timespec::new(meta.mtime(), 0)), meta.size())
}

// If an error occurs when we try to get file's information, we just return and don't send its info.
fn add_file_info(path: PathBuf, out: &mut Vec<u8>) {
    let extra = if path.is_dir() { "/" } else { "" };
    let is_dir = if path.is_dir() { "d" } else { "-" };

    let meta = match ::std::fs::metadata(&path) {
        Ok(meta) => meta,
        _ => return,
    };
    let (time, file_size) = get_file_info(&meta);
    let path = match path.to_str() {
        Some(path) => match path.split("/").last() {
            Some(path) => path,
            _ => return,
        },
        _ => return,
    };
    // TODO: maybe improve how we get rights in here?
    let rights = if meta.permissions().readonly() {
        "r--r--r--"
    } else {
        "rw-rw-rw-"
    };
    let file_str = format!("{is_dir}{rights} {links} {owner} {group} {size} {month} {day} {hour}:{min} {path}{extra}\r\n",
                           is_dir=is_dir,
                           rights=rights,
                           links=1, // number of links
                           owner="anonymous", // owner name
                           group="anonymous", // group name
                           size=file_size,
                           month=MONTHS[time.tm_mon as usize],
                           day=time.tm_mday,
                           hour=time.tm_hour,
                           min=time.tm_min,
                           path=path,
                           extra=extra);
    out.extend(file_str.as_bytes());
    println!("==> {:?}", &file_str);
}

struct Client {
    data_port: Option<u16>,
    data_reader: Option<DataReader>,
    data_writer: Option<DataWriter>,
    handle: Handle,
    cwd: PathBuf,
    server_root: PathBuf,
    transfer_type: TransferType,
    writer: Writer,
}

impl Client {
    fn new(handle: Handle, writer: Writer, server_root: PathBuf) -> Client {
        Client {
            data_port: None,
            data_reader: None,
            data_writer: None,
            handle,
            cwd: PathBuf::from("/"),
            server_root,
            transfer_type: TransferType::Ascii,
            writer,
        }
    }
    #[async]
    fn send(mut self, answer: Answer) -> Result<Self> {
        self.writer = await!(self.writer.send(answer))?;
        Ok(self)
    }

    fn complete_path(self, path: PathBuf) -> (Self, result::Result<PathBuf, io::Error>) {
        let directory = self.server_root.join(if path.has_root() {
            path.iter().skip(1).collect()
        } else {
            path
        });
        let dir = directory.canonicalize();
        if let Ok(ref dir) = dir {
            if !dir.starts_with(&self.server_root) {
                return (self, Err(io::ErrorKind::PermissionDenied.into()));
            }
        }
        (self,dir)
    }
    fn strip_prefix(self, dir:PathBuf) -> (Self, result::Result<PathBuf, StripPrefixError>) {
        let res = dir.strip_prefix(&self.server_root).map(|p| p.to_path_buf());
        (self,res)
    }

    #[async]
    fn cwd(mut self, directory: PathBuf) -> Result<Self>{
        let path = self.cwd.join(&directory);
        let (new_self, res) = self.complete_path(path);
        self = new_self;
        if let Ok(dir) = res {
            let (new_self, res) = self.strip_prefix(dir);
            self = new_self;
            if let Ok(prefix) = res {
                self.cwd = prefix.to_path_buf();
                self = await!(self.send(Answer::new(ResultCode::Ok, &format!("Directory changed to \"{}\"", directory.display()))))?;
                return Ok(self)
            }
        }
        self = await!(self.send(Answer::new(ResultCode::FileNotFound, "No such file or directory")))?;
        return Ok(self);
    }
    #[async]
    fn pasv(mut self) -> Result<Self> {
        let port =
            if let Some(port) = self.data_port {
                port
            } else {
                0
            };
        if self.data_writer.is_some() {
            self = await!(self.send(Answer::new(ResultCode::DataConnectionAlreadyOpen,"Already listening...")))?;
            return Ok(self);
        }

        let addr= SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127,0,0,1)),port);
        let listener = TcpListener::bind(&addr, &self.handle)?;
        let port = listener.local_addr()?.port();

        self = await!(self.send(Answer::new(ResultCode::EnteringPassiveMode, &format!("127,0,0,1,{},{}", port>>8, port & 0xFF))))?;

        println!("Waiting clients on the port {}...", port);

        #[async]
        for(stream, _rest) in listener.incoming()  {
            let (writer, reader) = stream.framed(BytesCodec).split();
            self.data_writer = Some(writer);
            self.data_reader = Some(reader);
            break;
        }
        Ok(self)
    }

    #[async]
    fn quit(mut self) -> Result<Self> {
        if self.data_writer.is_some() {
            unimplemented!();
        } else {
            self = await!(self.send(Answer::new(ResultCode::ServiceClosingControlConnection, "Closing connection...")))?;
            self.writer.close()?;
        }
        Ok(self)
    }

    #[async]
    fn mkd(mut self, path: PathBuf) -> Result<Self> {
        let path = self.cwd.join(&path);
        let parent = get_parent(path.clone());
        if let Some(parent) = parent {
            let parent = parent.to_path_buf();
            let(new_self, res) = self.complete_path(parent);
            self = new_self;
            if let Ok(mut dir) = res {
                if dir.is_dir() {
                    let filename = get_filename(path);
                    if let Some(filename) = filename {
                        dir.push(filename);
                        if create_dir(dir).is_ok() {
                            self = await!(self.send(Answer::new(ResultCode::PATHNAMECreated,"Folder sucessfully crated!")))?;
                            return Ok(self);
                        }
                    }
                }
            }
        }
        self = await!(self.send(Answer::new(ResultCode::FileNotFound, "Could not create folder")))?;
        Ok(self)
    }

    #[async]
    fn rmd(mut self, directory: PathBuf) -> Result<Self> {
        let path = self.cwd.join(&directory);
        let (new_self, res) = self.complete_path(path);
        self = new_self;
        if let Ok(dir) = res {
            if remove_dir_all(dir).is_ok() {
                self = await!(self.send(Answer::new(ResultCode::RequestedFileActionOkay,
                                                    "Folder successfully removed")))?;
                return Ok(self);
            }
        }
        self = await!(self.send(Answer::new(ResultCode::FileNotFound,
                                            "Couldn't remove folder")))?;
        Ok(self)
    }

    #[async]
    fn send_data(mut self, data: Vec<u8>) -> Result<Self> {
        if let Some(writer) = self.data_writer {
            self.data_writer = Some(await!(writer.send(data))?);
        }
        Ok(self)
    }

    fn close_data_connection(&mut self) {
        self.data_reader = None;
        self.data_writer = None;
    }

#[async]
fn list(mut self, path: Option<PathBuf>) -> Result<Self> {
    if self.data_writer.is_some() {
        let path = self.cwd.join(path.unwrap_or_default());
        let directory = PathBuf::from(&path);
        let (new_self, res) = self.complete_path(directory);
        self = new_self;
        if  let Ok(path) = res {
            self = await!(self.send(Answer::new(ResultCode::DataConnectionAlreadyOpen, "Starting to list directory...")))?;

            let mut out = vec![];
            if path.is_dir() {
                if let Ok(dir) = read_dir(path) {
                    for entry in dir {
                        if let Ok(entry) = entry {
                            add_file_info(entry.path(),&mut out);
                        }
                    }
                } else {
                    self =  await!(self.send(Answer::new(ResultCode::InvalidParameterOrArgument, "No such file or directory")))?;
                    return Ok(self);
                }
            } else {
                add_file_info(path,&mut out);
            }
            self = await!(self.send_data(out))?;
            println!("-> and done!");
        } else {
                self = await!(self.send(Answer::new(ResultCode::InvalidParameterOrArgument,
                                                    "No such file or directory")))?;
        }
    } else {
        self = await!(self.send(Answer::new(ResultCode::ConnectionClosed, "No opened data connection")))?;
    }
    if self.data_writer.is_some() {
        self.close_data_connection();
        self = await!(self.send(Answer::new(ResultCode::ClosingDataConnection, "Transfer done")))?;
    }
    Ok(self)
}

#[async]
fn handle_cmd(mut self, cmd: Command) -> Result<Self> {
    println!("Received command: {:?}", cmd);
    match cmd {
        Command::List(path) => self = await!(self.list(path))?,
        Command::Mkd(path) => self = await!(self.mkd(path))?,
        Command::Rmd(path) => self = await!(self.rmd(path))?,
        Command::Quit => self = await!(self.quit())?,
        Command::Pasv => self = await!(self.pasv())?,
        Command::Type(typ) => {
            self.transfer_type = typ;
            self = await!(self.send(Answer::new(ResultCode::Ok, "Transfer type changed successfully")))?;
        },
        Command::Cwd(directory) => {
            self= await!(self.cwd(directory))?;
        },
        Command::User(content) => {
            if content.is_empty() {
                self = await!(self.send(Answer::new(ResultCode::InvalidParameterOrArgument,"Invalid username")))?;
            } else {
                self = await!(self.send(Answer::new(ResultCode::UserloggedIn,&format!("Welcome {}",content))))?;
            }
        },
        Command::Pwd => {
            let msg = format!("{}", self.cwd.to_str().unwrap_or(""));
            if !msg.is_empty() {
                let message = format!("\"/{}\" ",msg);
                self = await!(self.send(Answer::new(ResultCode::PATHNAMECreated, &message)))?;
            } else {
                self = await!(self.send(Answer::new(ResultCode::FileNotFound, "No such file or directory")))?;
            }
        },
        Command::Unknown(s) => self = await!(self.send(Answer::new(ResultCode::UnknownCommand, &format!("\"{}\": Not Implemented",s))))?,
        _=>  self = await!(self.send(Answer::new(ResultCode::CommandNotImplemented, "Not implemented")))?,
    }
    return Ok(self);
}
}

#[async]
fn client(stream: TcpStream, handle: Handle,  server_root: PathBuf) -> Result<()>{
    let (writer, reader) = stream.framed(FtpCodec).split();
    let writer = await!(writer.send(Answer::new(ResultCode::ServiceReadyForNewUser, "Welcome to this FTP Server!")))?;
    let mut client = Client::new(handle, writer, server_root);
    #[async]
    for cmd in reader {
        client = await!(client.handle_cmd(cmd))?;
    }
    println!("Client closed");
    return Ok(());;
}

#[async]
fn handle_client(stream: TcpStream, handle: Handle, server_root: PathBuf) -> result::Result<(), ()> {
    await!(client(stream, handle, server_root)).map_err(|error| println!("Error handling client: {}", error))
}

#[async]
fn server(handle: Handle, server_root: PathBuf) -> io::Result<()> {
    let port = 1234;
    let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127,0,0,1)), port);
    let listener = TcpListener::bind(&addr,&handle)?;
    println!("Waiting clients on port {}...",port);
    #[async]
    for (stream, addr) in listener.incoming() {
        let address = format!("[address : {}]",addr);
        println!("New client {}", address);
        handle.spawn(handle_client(stream, handle.clone() ,server_root.clone()));
        println!("Waiting another client...");
    }
    return Ok(());
}

fn main() {
    let mut core = Core::new().expect("Cannot create tokio Core");
    let handle = core.handle();
    match env::current_dir() {
        Ok(server_root) => {
            if let Err(error) = core.run(server(handle, server_root)) {
                println!("Error running the server: {}", error);
            }
        },
        Err(e) => println!("Could not start server: {:?}",e),

    }
}
<|endoftext|>


<commit_before>use std::collections::{HashMap, HashSet};
use std::hash::Hash;
use std::borrow::Borrow;

#[derive(Debug,Clone)]
pub struct ForkTable<'a,K: 'a + Eq + Hash,V: 'a>  {
    table: HashMap<K, V>,
    whiteouts: HashSet<K>,
    parent: Option<&'a ForkTable<'a,K,V>>
}

impl<'a,K,V> ForkTable<'a, K, V> where K: Eq + Hash {

    /// Returns a reference to the value corresponding to the key.
    ///
    /// If the key is defined in this level of the table, or in any
    /// of its' parents, a reference to the associated value will be
    /// returned.
    ///
    /// The key may be any borrowed form of the map's key type, but
    /// `Hash` and `Eq` on the borrowed form *must* match those for
    /// the key type.
    ///
    pub fn get<Q: ?Sized>(&self, key: &Q) -> Option<&V>
        where K: Borrow<Q>, Q: Hash + Eq {
            unimplemented!()
    }

    /// Returns a mutable reference to the value corresponding to the key.
    ///
    /// If the key is defined in this level of the table, or in any
    /// of its' parents, a reference to the associated value will be
    /// returned.
    ///
    /// The key may be any borrowed form of the map's key type, but
    /// `Hash` and `Eq` on the borrowed form *must* match those for
    /// the key type.
    ///
    pub fn get_mut<Q: ?Sized>(&mut self, key: &Q) -> Option<&mut V>
        where K: Borrow<Q>, Q: Hash + Eq {
            unimplemented!()
    }

    pub fn insert(&mut self, k: K, v: V) -> Option<V> {
        unimplemented!()
    }

    /// Returns true if the map contains a value for the specified key.
    ///
    /// The key may be any borrowed form of the map's key type, but
    /// `Hash` and `Eq` on the borrowed form *must* match those for
    /// the key type.
    ///
    pub fn contains_key(&self, key: &K) -> bool  {
        self.table.contains_key(key)
    }

    /// Returns true if the key is defined in this level of the table, or
    /// in any of its' parents and is not whited out.
    ///
    /// The key may be any borrowed form of the map's key type, but
    /// `Hash` and `Eq` on the borrowed form *must* match those for
    /// the key type.
    ///
    pub fn chain_contains_key(&self, key: &K) -> bool {
        self.table.contains_key(key) ||
        (self.whiteouts.contains(k) &&
            self.parent
                .map(|p| p.chain_contains_key(key))
                .unwrap_or(false))
    }
}<commit_msg>Fix incorrect name<commit_after>use std::collections::{HashMap, HashSet};
use std::hash::Hash;
use std::borrow::Borrow;

#[derive(Debug,Clone)]
pub struct ForkTable<'a,K: 'a + Eq + Hash,V: 'a>  {
    table: HashMap<K, V>,
    whiteouts: HashSet<K>,
    parent: Option<&'a ForkTable<'a,K,V>>
}

impl<'a,K,V> ForkTable<'a, K, V> where K: Eq + Hash {

    /// Returns a reference to the value corresponding to the key.
    ///
    /// If the key is defined in this level of the table, or in any
    /// of its' parents, a reference to the associated value will be
    /// returned.
    ///
    /// The key may be any borrowed form of the map's key type, but
    /// `Hash` and `Eq` on the borrowed form *must* match those for
    /// the key type.
    ///
    pub fn get<Q: ?Sized>(&self, key: &Q) -> Option<&V>
        where K: Borrow<Q>, Q: Hash + Eq {
            unimplemented!()
    }

    /// Returns a mutable reference to the value corresponding to the key.
    ///
    /// If the key is defined in this level of the table, or in any
    /// of its' parents, a reference to the associated value will be
    /// returned.
    ///
    /// The key may be any borrowed form of the map's key type, but
    /// `Hash` and `Eq` on the borrowed form *must* match those for
    /// the key type.
    ///
    pub fn get_mut<Q: ?Sized>(&mut self, key: &Q) -> Option<&mut V>
        where K: Borrow<Q>, Q: Hash + Eq {
            unimplemented!()
    }

    pub fn insert(&mut self, k: K, v: V) -> Option<V> {
        unimplemented!()
    }

    /// Returns true if the map contains a value for the specified key.
    ///
    /// The key may be any borrowed form of the map's key type, but
    /// `Hash` and `Eq` on the borrowed form *must* match those for
    /// the key type.
    ///
    pub fn contains_key(&self, key: &K) -> bool  {
        self.table.contains_key(key)
    }

    /// Returns true if the key is defined in this level of the table, or
    /// in any of its' parents and is not whited out.
    ///
    /// The key may be any borrowed form of the map's key type, but
    /// `Hash` and `Eq` on the borrowed form *must* match those for
    /// the key type.
    ///
    pub fn chain_contains_key(&self, key: &K) -> bool {
        self.table.contains_key(key) ||
        (self.whiteouts.contains(key) &&
            self.parent
                .map(|p| p.chain_contains_key(key))
                .unwrap_or(false))
    }
}<|endoftext|>


<commit_before>use alloc::borrow::Cow;
use alloc::vec::Vec;
use std::slice;

use goblin::pe;

use {
    Machine, Object, ObjectSection, ObjectSegment, SectionKind, Symbol, SymbolKind, SymbolMap,
};

/// A PE object file.
#[derive(Debug)]
pub struct PeFile<'data> {
    pe: pe::PE<'data>,
    data: &'data [u8],
}

/// An iterator over the loadable sections of a `PeFile`.
#[derive(Debug)]
pub struct PeSegmentIterator<'data, 'file>
where
    'data: 'file,
{
    file: &'file PeFile<'data>,
    iter: slice::Iter<'file, pe::section_table::SectionTable>,
}

/// A loadable section of a `PeFile`.
#[derive(Debug)]
pub struct PeSegment<'data, 'file>
where
    'data: 'file,
{
    file: &'file PeFile<'data>,
    section: &'file pe::section_table::SectionTable,
}

/// An iterator over the sections of a `PeFile`.
#[derive(Debug)]
pub struct PeSectionIterator<'data, 'file>
where
    'data: 'file,
{
    file: &'file PeFile<'data>,
    iter: slice::Iter<'file, pe::section_table::SectionTable>,
}

/// A section of a `PeFile`.
#[derive(Debug)]
pub struct PeSection<'data, 'file>
where
    'data: 'file,
{
    file: &'file PeFile<'data>,
    section: &'file pe::section_table::SectionTable,
}

/// An iterator over the symbols of a `PeFile`.
#[derive(Debug)]
pub struct PeSymbolIterator<'data, 'file>
where
    'data: 'file,
{
    exports: slice::Iter<'file, pe::export::Export<'data>>,
    imports: slice::Iter<'file, pe::import::Import<'data>>,
}

impl<'data> PeFile<'data> {
    /// Get the PE headers of the file.
    // TODO: this is temporary to allow access to features this crate doesn't provide yet
    #[inline]
    pub fn pe(&self) -> &pe::PE<'data> {
        &self.pe
    }

    /// Parse the raw PE file data.
    pub fn parse(data: &'data [u8]) -> Result<Self, &'static str> {
        let pe = pe::PE::parse(data).map_err(|_| "Could not parse PE header")?;
        Ok(PeFile { pe, data })
    }
}

impl<'data, 'file> Object<'data, 'file> for PeFile<'data>
where
    'data: 'file,
{
    type Segment = PeSegment<'data, 'file>;
    type SegmentIterator = PeSegmentIterator<'data, 'file>;
    type Section = PeSection<'data, 'file>;
    type SectionIterator = PeSectionIterator<'data, 'file>;
    type SymbolIterator = PeSymbolIterator<'data, 'file>;

    fn machine(&self) -> Machine {
        match self.pe.header.coff_header.machine {
            // TODO: Arm/Arm64
            pe::header::COFF_MACHINE_X86 => Machine::X86,
            pe::header::COFF_MACHINE_X86_64 => Machine::X86_64,
            _ => Machine::Other,
        }
    }

    fn segments(&'file self) -> PeSegmentIterator<'data, 'file> {
        PeSegmentIterator {
            file: self,
            iter: self.pe.sections.iter(),
        }
    }

    fn section_data_by_name(&self, section_name: &str) -> Option<Cow<'data, [u8]>> {
        for section in &self.pe.sections {
            if let Ok(name) = section.name() {
                if name == section_name {
                    return Some(Cow::from(
                        &self.data[section.pointer_to_raw_data as usize..]
                            [..section.virtual_size as usize],
                    ));
                }
            }
        }
        None
    }

    fn sections(&'file self) -> PeSectionIterator<'data, 'file> {
        PeSectionIterator {
            file: self,
            iter: self.pe.sections.iter(),
        }
    }

    fn symbols(&'file self) -> PeSymbolIterator<'data, 'file> {
        // TODO: return COFF symbols for object files
        PeSymbolIterator {
            exports: [].iter(),
            imports: [].iter(),
        }
    }

    fn dynamic_symbols(&'file self) -> PeSymbolIterator<'data, 'file> {
        PeSymbolIterator {
            exports: self.pe.exports.iter(),
            imports: self.pe.imports.iter(),
        }
    }

    fn symbol_map(&self) -> SymbolMap<'data> {
        // TODO: untested
        let mut symbols: Vec<_> = self.symbols().filter(SymbolMap::filter).collect();
        symbols.sort_by_key(|x| x.address);
        SymbolMap { symbols }
    }

    #[inline]
    fn is_little_endian(&self) -> bool {
        // TODO: always little endian?  The COFF header has some bits in the
        // characteristics flags, but these are obsolete.
        true
    }

    fn has_debug_symbols(&self) -> bool {
        // TODO: check if CodeView-in-PE still works
        for section in &self.pe.sections {
            if let Ok(name) = section.name() {
                if name == ".debug_info" {
                    return true;
                }
            }
        }
        false
    }

    fn entry(&self) -> u64 {
        self.pe.entry as u64
    }
}

impl<'data, 'file> Iterator for PeSegmentIterator<'data, 'file> {
    type Item = PeSegment<'data, 'file>;

    fn next(&mut self) -> Option<Self::Item> {
        self.iter.next().map(|section| PeSegment {
            file: self.file,
            section,
        })
    }
}

impl<'data, 'file> ObjectSegment<'data> for PeSegment<'data, 'file> {
    #[inline]
    fn address(&self) -> u64 {
        u64::from(self.section.virtual_address)
    }

    #[inline]
    fn size(&self) -> u64 {
        u64::from(self.section.virtual_size)
    }

    fn data(&self) -> &'data [u8] {
        &self.file.data[self.section.pointer_to_raw_data as usize..]
            [..self.section.size_of_raw_data as usize]
    }

    #[inline]
    fn name(&self) -> Option<&str> {
        self.section.name().ok()
    }
}

impl<'data, 'file> Iterator for PeSectionIterator<'data, 'file> {
    type Item = PeSection<'data, 'file>;

    fn next(&mut self) -> Option<Self::Item> {
        self.iter.next().map(|section| PeSection {
            file: self.file,
            section,
        })
    }
}

impl<'data, 'file> ObjectSection<'data> for PeSection<'data, 'file> {
    #[inline]
    fn address(&self) -> u64 {
        u64::from(self.section.virtual_address)
    }

    #[inline]
    fn size(&self) -> u64 {
        u64::from(self.section.virtual_size)
    }

    fn data(&self) -> Cow<'data, [u8]> {
        Cow::from(
            &self.file.data[self.section.pointer_to_raw_data as usize..]
                [..self.section.size_of_raw_data as usize],
        )
    }

    fn name(&self) -> Option<&str> {
        self.section.name().ok()
    }

    #[inline]
    fn segment_name(&self) -> Option<&str> {
        None
    }

    #[inline]
    fn kind(&self) -> SectionKind {
        if self.section.characteristics
            & (pe::section_table::IMAGE_SCN_CNT_CODE | pe::section_table::IMAGE_SCN_MEM_EXECUTE)
            != 0
        {
            SectionKind::Text
        } else if self.section.characteristics & pe::section_table::IMAGE_SCN_CNT_INITIALIZED_DATA
            != 0
        {
            SectionKind::Data
        } else if self.section.characteristics & pe::section_table::IMAGE_SCN_CNT_UNINITIALIZED_DATA
            != 0
        {
            SectionKind::UninitializedData
        } else {
            SectionKind::Unknown
        }
    }
}

impl<'data, 'file> Iterator for PeSymbolIterator<'data, 'file> {
    type Item = Symbol<'data>;

    fn next(&mut self) -> Option<Self::Item> {
        if let Some(export) = self.exports.next() {
            return Some(Symbol {
                kind: SymbolKind::Unknown,
                section_kind: Some(SectionKind::Unknown),
                global: true,
                name: export.name,
                address: export.rva as u64,
                size: 0,
            });
        }
        if let Some(import) = self.imports.next() {
            let name = match import.name {
                Cow::Borrowed(name) => Some(name),
                _ => None,
            };
            return Some(Symbol {
                kind: SymbolKind::Unknown,
                section_kind: None,
                global: true,
                name: name,
                address: 0,
                size: 0,
            });
        }
        None
    }
}
<commit_msg>Issue #40 -- Use min of section virtual_size and size_of_raw_data<commit_after>use alloc::borrow::Cow;
use alloc::vec::Vec;
use std::cmp;
use std::slice;

use goblin::pe;

use {
    Machine, Object, ObjectSection, ObjectSegment, SectionKind, Symbol, SymbolKind, SymbolMap,
};

/// A PE object file.
#[derive(Debug)]
pub struct PeFile<'data> {
    pe: pe::PE<'data>,
    data: &'data [u8],
}

/// An iterator over the loadable sections of a `PeFile`.
#[derive(Debug)]
pub struct PeSegmentIterator<'data, 'file>
where
    'data: 'file,
{
    file: &'file PeFile<'data>,
    iter: slice::Iter<'file, pe::section_table::SectionTable>,
}

/// A loadable section of a `PeFile`.
#[derive(Debug)]
pub struct PeSegment<'data, 'file>
where
    'data: 'file,
{
    file: &'file PeFile<'data>,
    section: &'file pe::section_table::SectionTable,
}

/// An iterator over the sections of a `PeFile`.
#[derive(Debug)]
pub struct PeSectionIterator<'data, 'file>
where
    'data: 'file,
{
    file: &'file PeFile<'data>,
    iter: slice::Iter<'file, pe::section_table::SectionTable>,
}

/// A section of a `PeFile`.
#[derive(Debug)]
pub struct PeSection<'data, 'file>
where
    'data: 'file,
{
    file: &'file PeFile<'data>,
    section: &'file pe::section_table::SectionTable,
}

/// An iterator over the symbols of a `PeFile`.
#[derive(Debug)]
pub struct PeSymbolIterator<'data, 'file>
where
    'data: 'file,
{
    exports: slice::Iter<'file, pe::export::Export<'data>>,
    imports: slice::Iter<'file, pe::import::Import<'data>>,
}

impl<'data> PeFile<'data> {
    /// Get the PE headers of the file.
    // TODO: this is temporary to allow access to features this crate doesn't provide yet
    #[inline]
    pub fn pe(&self) -> &pe::PE<'data> {
        &self.pe
    }

    /// Parse the raw PE file data.
    pub fn parse(data: &'data [u8]) -> Result<Self, &'static str> {
        let pe = pe::PE::parse(data).map_err(|_| "Could not parse PE header")?;
        Ok(PeFile { pe, data })
    }
}

impl<'data, 'file> Object<'data, 'file> for PeFile<'data>
where
    'data: 'file,
{
    type Segment = PeSegment<'data, 'file>;
    type SegmentIterator = PeSegmentIterator<'data, 'file>;
    type Section = PeSection<'data, 'file>;
    type SectionIterator = PeSectionIterator<'data, 'file>;
    type SymbolIterator = PeSymbolIterator<'data, 'file>;

    fn machine(&self) -> Machine {
        match self.pe.header.coff_header.machine {
            // TODO: Arm/Arm64
            pe::header::COFF_MACHINE_X86 => Machine::X86,
            pe::header::COFF_MACHINE_X86_64 => Machine::X86_64,
            _ => Machine::Other,
        }
    }

    fn segments(&'file self) -> PeSegmentIterator<'data, 'file> {
        PeSegmentIterator {
            file: self,
            iter: self.pe.sections.iter(),
        }
    }

    fn section_data_by_name(&self, section_name: &str) -> Option<Cow<'data, [u8]>> {
        for section in &self.pe.sections {
            if let Ok(name) = section.name() {
                if name == section_name {
                    return Some(Cow::from(
                        &self.data[section.pointer_to_raw_data as usize..]
                            [..cmp::min(section.virtual_size, section.size_of_raw_data) as usize]
                    ));
                }
            }
        }
        None
    }

    fn sections(&'file self) -> PeSectionIterator<'data, 'file> {
        PeSectionIterator {
            file: self,
            iter: self.pe.sections.iter(),
        }
    }

    fn symbols(&'file self) -> PeSymbolIterator<'data, 'file> {
        // TODO: return COFF symbols for object files
        PeSymbolIterator {
            exports: [].iter(),
            imports: [].iter(),
        }
    }

    fn dynamic_symbols(&'file self) -> PeSymbolIterator<'data, 'file> {
        PeSymbolIterator {
            exports: self.pe.exports.iter(),
            imports: self.pe.imports.iter(),
        }
    }

    fn symbol_map(&self) -> SymbolMap<'data> {
        // TODO: untested
        let mut symbols: Vec<_> = self.symbols().filter(SymbolMap::filter).collect();
        symbols.sort_by_key(|x| x.address);
        SymbolMap { symbols }
    }

    #[inline]
    fn is_little_endian(&self) -> bool {
        // TODO: always little endian?  The COFF header has some bits in the
        // characteristics flags, but these are obsolete.
        true
    }

    fn has_debug_symbols(&self) -> bool {
        // TODO: check if CodeView-in-PE still works
        for section in &self.pe.sections {
            if let Ok(name) = section.name() {
                if name == ".debug_info" {
                    return true;
                }
            }
        }
        false
    }

    fn entry(&self) -> u64 {
        self.pe.entry as u64
    }
}

impl<'data, 'file> Iterator for PeSegmentIterator<'data, 'file> {
    type Item = PeSegment<'data, 'file>;

    fn next(&mut self) -> Option<Self::Item> {
        self.iter.next().map(|section| PeSegment {
            file: self.file,
            section,
        })
    }
}

impl<'data, 'file> ObjectSegment<'data> for PeSegment<'data, 'file> {
    #[inline]
    fn address(&self) -> u64 {
        u64::from(self.section.virtual_address)
    }

    #[inline]
    fn size(&self) -> u64 {
        u64::from(self.section.virtual_size)
    }

    fn data(&self) -> &'data [u8] {
        &self.file.data[self.section.pointer_to_raw_data as usize..]
            [..self.section.size_of_raw_data as usize]
    }

    #[inline]
    fn name(&self) -> Option<&str> {
        self.section.name().ok()
    }
}

impl<'data, 'file> Iterator for PeSectionIterator<'data, 'file> {
    type Item = PeSection<'data, 'file>;

    fn next(&mut self) -> Option<Self::Item> {
        self.iter.next().map(|section| PeSection {
            file: self.file,
            section,
        })
    }
}

impl<'data, 'file> ObjectSection<'data> for PeSection<'data, 'file> {
    #[inline]
    fn address(&self) -> u64 {
        u64::from(self.section.virtual_address)
    }

    #[inline]
    fn size(&self) -> u64 {
        u64::from(self.section.virtual_size)
    }

    fn data(&self) -> Cow<'data, [u8]> {
        Cow::from(
            &self.file.data[self.section.pointer_to_raw_data as usize..]
                [..self.section.size_of_raw_data as usize],
        )
    }

    fn name(&self) -> Option<&str> {
        self.section.name().ok()
    }

    #[inline]
    fn segment_name(&self) -> Option<&str> {
        None
    }

    #[inline]
    fn kind(&self) -> SectionKind {
        if self.section.characteristics
            & (pe::section_table::IMAGE_SCN_CNT_CODE | pe::section_table::IMAGE_SCN_MEM_EXECUTE)
            != 0
        {
            SectionKind::Text
        } else if self.section.characteristics & pe::section_table::IMAGE_SCN_CNT_INITIALIZED_DATA
            != 0
        {
            SectionKind::Data
        } else if self.section.characteristics & pe::section_table::IMAGE_SCN_CNT_UNINITIALIZED_DATA
            != 0
        {
            SectionKind::UninitializedData
        } else {
            SectionKind::Unknown
        }
    }
}

impl<'data, 'file> Iterator for PeSymbolIterator<'data, 'file> {
    type Item = Symbol<'data>;

    fn next(&mut self) -> Option<Self::Item> {
        if let Some(export) = self.exports.next() {
            return Some(Symbol {
                kind: SymbolKind::Unknown,
                section_kind: Some(SectionKind::Unknown),
                global: true,
                name: export.name,
                address: export.rva as u64,
                size: 0,
            });
        }
        if let Some(import) = self.imports.next() {
            let name = match import.name {
                Cow::Borrowed(name) => Some(name),
                _ => None,
            };
            return Some(Symbol {
                kind: SymbolKind::Unknown,
                section_kind: None,
                global: true,
                name: name,
                address: 0,
                size: 0,
            });
        }
        None
    }
}
<|endoftext|>


<commit_before>use std::cell::RefCell;
use std::collections::HashMap;
use std::fs::copy;
use std::path::Path;
use std::str::FromStr;
use std::rc::Rc;

use iup;
use iup::prelude::*;
use iup::control::{Button, Label, List, Text};
use iup::dialog::{FileDlg};
use iup::element::{Handle};
use iup::led;

use time;
use xml;

use parser::{read_path, write_path};
use property::{Property, PropertyMap};

// Since we need to share mutable state with 'static ui callbacks,
// we clone a refcounted cell for moving into each callback.
type PropertyMapRc = Rc<RefCell<PropertyMap>>;

// LED dialog specification.
static DIALOG: &'static str = include_str!("../resources/ui.led");

// Get an element from handle
fn from_handle<E>(handle: iup::element::Handle) -> E where E: Element {
    E::from_handle(handle).unwrap()
}

// Get an element from named handle
fn from_name<E>(name: &str) -> E where E: Element {
    E::from_handle(E::from_name(name).unwrap()).unwrap()
}

// Data-bind an element to a numeric property value.
//
// Value of the element is set to the current value of the property, and
// changes to element value are written back to the property map.
//
// @param props {PropertyMapRc} a cloned refcounted property map.
//
fn bind<T, E>(elem: &mut E, props: PropertyMapRc, key: &'static str)
    where Property: From<T>,
          T: FromStr + Default,
          E: Element + ValueChangedCb {

    // Remove previous bindings, if any.
    elem.remove_valuechanged_cb();

    if let Some(ref prop) = props.borrow().get(key) {
        elem.set_attrib("VALUE", prop.to_string());
    }
    elem.set_valuechanged_cb(move |(elem,): (E,)| {
        if let Some(ref value) = elem.attrib("VALUE") {
            if let Ok(v) = T::from_str(value) {
                props.borrow_mut().insert(key.to_string(), Property::from(v));
            } else {
                props.borrow_mut().insert(key.to_string(), Property::from(T::default()));
            }
        }
    });
}

// Data-bind an element to a list property value, at given index.
//
// Value of the element is set to the current value of the property at index,
// and changes to element value are written back to the property map.
//
// @param props {PropertyMapRc} a cloned refcounted property map.
//
fn bind_list<E>(elem: &mut E, props: PropertyMapRc, key: &'static str, index: usize)
    where E: Element + ValueChangedCb {

    // Remove previous bindings, if any.
    elem.remove_valuechanged_cb();

    if let Some(&Property::List(ref v)) = props.borrow().get(key) {
        elem.set_attrib("VALUE", v[index].to_string());
    }
    elem.set_valuechanged_cb(move |(elem,): (E,)| {
        if let Some(ref value) = elem.attrib("VALUE") {
            if let Some(&mut Property::List(ref mut v)) = props.borrow_mut().get_mut(key) {
                v[index] = value.to_string();
            }
        }
    });
}

macro_rules! bind_stat {
    ($i:ident, $p:expr, $e:expr) => {
        bind::<f32,_>(&mut from_name::<Text>(stringify!($i)), $p.clone(), $e);
    }
}

macro_rules! bind_skill {
    ($i:ident, $p:expr, $n:expr) => {
        bind_list::<_>(&mut from_handle::<Text>($i), $p.clone(), "SkillPoints", $n);
    }
}

// Data-bind all elements relevant to a party member.
//
// @param props {PropertyMapRc} a cloned refcounted property map.
//
fn bind_member(props: PropertyMapRc) {
    bind_stat!(text_int, props, "Int");
    bind_stat!(text_dex, props, "Dex");
    bind_stat!(text_str, props, "Str");
    bind_stat!(text_occ, props, "Occ");
    bind_stat!(text_per, props, "Per");

    bind_stat!(text_wpn_sword,  props, "WpnSword");
    bind_stat!(text_wpn_short,  props, "WpnShortSword");
    bind_stat!(text_wpn_blunt,  props, "WpnSceptor");
    bind_stat!(text_wpn_cleave, props, "WpnAxe");
    bind_stat!(text_wpn_whip,   props, "WpnWhip");
    bind_stat!(text_wpn_bow,    props, "WpnBow");
    bind_stat!(text_wpn_xbow,   props, "WpnXbow");
    bind_stat!(text_wpn_elixir, props, "WpnElixir");

    if let Some(&mut Property::List(ref mut v)) = props.borrow_mut().get_mut("SkillPoints") {
        while v.len() < 115 {
            v.push("0".to_string())
        }
    }
    if let Some(apt_grid) = Handle::from_named("apt_grid") {
        for i in 1..7 {
            if let Some(child) = apt_grid.child((i - 1) * 2 + 1) {
                bind_skill!(child, props, i);
            }
        }
    }
    if let Some(skill_grid) = Handle::from_named("skill_grid") {
        for i in 7..115 {
            if let Some(child) = skill_grid.child((i - 7) * 2 + 1) {
                bind_skill!(child, props, i);
            }
        }
    }
}

/// Ui entry point.
///
/// Starts by showing a direction selection dialog; after the user selects a directory,
/// the game is loaded from that directory and values bound to the ui elements.
///
pub fn ui_loop() -> Result<(), String> {
    match iup::with_iup(|| {
        // See also led::load(path) to load from a file
        led::load_buffer(DIALOG).unwrap();

        // Select saved game location
        let mut dlg_open = from_name::<FileDlg>("dlg_open");
        let dir = match dlg_open.popup(DialogPos::CenterParent, DialogPos::CenterParent) {
            Ok(..) => match dlg_open.attrib("STATUS") {
                Some(ref s) if s == "0" => {
                    dlg_open.attrib("VALUE").unwrap()
                },
                _ => return Err("File selection cancelled.".to_string())
            },
            _ => return Err("File selection failed.".to_string())
        };

        // Read game and party member files
        let game = Rc::new(RefCell::new({
            let path = Path::new(&dir).join("Game.txt");
            match read_path(path.as_path()) {
                Ok(v) => v,
                Err(e) => {
                    return Err(format!("Cannot read {:?}: {}", path, e))
                }
            }
        }));
        let party = Rc::new(RefCell::new({
            let mut members: Vec<PropertyMapRc> = Vec::new();
            if let Some(&Property::String(ref ids)) = game.borrow().get("PartyIDs") {
                for id in ids.split(",") {
                    if id == "0" {
                        continue;
                    }
                    let path = Path::new(&dir).join("Party".to_string() + id + ".txt");
                    match read_path(path.as_path()) {
                        Ok(v) => members.push(Rc::new(RefCell::new(v))),
                        Err(e) => {
                            return Err(format!("Cannot read {:?}: {}", path, e))
                        }
                    };
                }
            }
            members
        }));

        let mut text_emeralds = from_name::<Text>("text_emeralds");
        bind::<u32,_>(&mut text_emeralds, game.clone(), "Emeralds");

        let mut list_party = from_name::<List>("list_party");
        let mut list_party_items: Vec<String> = Vec::new();
        for member in party.borrow().iter() {
            if let Some(&Property::String(ref name)) = member.borrow().get("Name") {
                if let Some(&Property::Float(level)) = member.borrow().get("Level") {
                    list_party_items.push(format!("{} ({})", name, level));
                }
            }
        }

        let skills = load_skills();
        if let Some(mut apt_grid) = Handle::from_named("apt_grid") {
            while let Some(mut child) = apt_grid.child(0) {
                child.detach().destroy();
            }
            for i in 1..7 {
                let mut label = Label::new()
                    .set_attrib("SIZE", "92x12".to_string())
                    .set_attrib("TITLE", "(empty)".to_string());
                let mut text = Text::new_spin()
                    .set_attrib("SIZE", "36x12".to_string())
                    .set_attrib("SPINMAX", "99".to_string())
                    .set_attrib("MASKINT", "0:99".to_string())
                    .set_attrib("ALIGNMENT", "ARIGHT".to_string());
                if let Some(ref skill) = skills.get(&i) {
                    label.set_attrib("TITLE", skill.name.to_string());
                } else {
                    label.set_attrib("ACTIVE", "NO");
                    text.set_attrib("ACTIVE", "NO");
                }
                apt_grid.append(label).unwrap();
                apt_grid.append(text).unwrap();
            }
        }
        if let Some(mut skill_grid) = Handle::from_named("skill_grid") {
            while let Some(mut child) = skill_grid.child(0) {
                child.detach().destroy();
            }
            for i in 7..115 {
                let mut label = Label::new()
                    .set_attrib("SIZE", "92x12".to_string())
                    .set_attrib("TITLE", "(empty)".to_string());
                let mut text = Text::new_spin()
                    .set_attrib("SIZE", "36x12".to_string())
                    .set_attrib("SPINMAX", "99".to_string())
                    .set_attrib("MASKINT", "0:99".to_string())
                    .set_attrib("ALIGNMENT", "ARIGHT".to_string());
                if let Some(ref skill) = skills.get(&i) {
                    label.set_attrib("TITLE", skill.name.to_string());
                } else {
                    label.set_attrib("ACTIVE", "NO");
                    text.set_attrib("ACTIVE", "NO");
                }
                skill_grid.append(label).unwrap();
                skill_grid.append(text).unwrap();
            }
        }

        let party_clone = party.clone();
        list_party.set_items(list_party_items);
        list_party.set_action(move |(_, _, i, _)| {
            let member = party_clone.borrow()[i as usize - 1].clone();
            bind_member(member);
        });
        if let Some(&ref member) = party.borrow().first() {
            bind_member(member.clone());
        }

        // Write game and party member files on save
        let mut button_save = from_name::<Button>("button_save");
        {
            let game_clone = game.clone();
            let party_clone = party.clone();
            button_save.set_action(move |_| {
                let timestamp = time::strftime("%FT%H.%M.%SZ.txt", &time::now_utc()).unwrap();
                let path = Path::new(&dir).join("Game.txt");
                copy(path.as_path(), path.with_extension(&timestamp)).unwrap();
                write_path(path.as_path(), &game_clone.borrow()).unwrap();
                for member in party_clone.borrow().iter() {

                    // Validate
                    let mut combat_skills: Vec<String> = Vec::new();
                    let mut spell_skills: Vec<String> = Vec::new();
                    if let Some(&Property::List(ref v)) = member.borrow().get("SkillPoints") {
                        let spell_grade = {
                            if let Some(&Property::Float(int_mod)) = member.borrow().get("Int") {
                                if let Some(&Property::Float(occ_mod)) = member.borrow().get("Occ") {
                                    int_mod + occ_mod + 20f32
                                } else {
                                    0f32
                                }
                            } else {
                                0f32
                            }
                        };
                        for (i, ref val) in v.iter().enumerate() {
                            match val.parse::<u32>() {
                                // Combat skills (1-3)
                                Ok(n) if i >= 7 && i <= 61 && n > 0 => {
                                    if let Some(ref skill) = skills.get(&i) {
                                        combat_skills.push(skill.internal.to_string())
                                    }
                                }
                                // Spell skills (1)
                                Ok(n) if i >= 62 && i <= 72 && (n > 0 || spell_grade >= 21f32) => {
                                    if let Some(ref skill) = skills.get(&i) {
                                        spell_skills.push(skill.internal.to_string())
                                    }
                                }
                                // Spell skills (2)
                                Ok(n) if i >= 73 && i <= 86 && (n > 0 || spell_grade >= 26f32) => {
                                    if let Some(ref skill) = skills.get(&i) {
                                        spell_skills.push(skill.internal.to_string())
                                    }
                                }
                                // Spell skills (3)
                                Ok(n) if i >= 87 && i <= 114 && (n > 0 || spell_grade >= 32f32) => {
                                    if let Some(ref skill) = skills.get(&i) {
                                        spell_skills.push(skill.internal.to_string())
                                    }
                                }
                                _ => {}
                            }
                        }
                    }
                    if let Some(&mut Property::List(ref mut v)) = member.borrow_mut().get_mut("CombatSelects") {
                        v.retain(|ref x| combat_skills.contains(&x));
                    }
                    if let Some(&mut Property::List(ref mut v)) = member.borrow_mut().get_mut("SpellFavorites") {
                        v.retain(|ref x| spell_skills.contains(&x));
                    }
                    member.borrow_mut().insert("CombatSkills".to_string(), Property::List(combat_skills));
                    member.borrow_mut().insert("SpellSkills".to_string(), Property::List(spell_skills));

                    if let Some(&Property::String(ref id)) = member.borrow().get("PartyID") {
                        let path = Path::new(&dir).join("Party".to_string() + id + ".txt");
                        copy(path.as_path(), path.with_extension(&timestamp)).unwrap();
                        write_path(path.as_path(), &member.borrow()).unwrap();
                    }
                }
            });
        }
        let mut button_close = from_name::<Button>("button_close");
        button_close.set_action(|_| {
            CallbackReturn::Close
        });

        let mut dlg = from_name::<Dialog>("dlg");
        dlg.show()

    }) {
        Err(iup::InitError::UserError(s)) => Err(s),
        Err(e) => Err(format!("{:?}", e)),
        _ => Ok(())
    }
}

struct Skill {
    name: String,
    internal: String,
}

fn load_skills() -> HashMap<usize, Skill> {
    let mut skills: HashMap<usize, Skill> = HashMap::with_capacity(115);
    if let Ok(elem) = include_str!("../resources/skills.xml").parse::<xml::Element>() {
        for child in elem.get_children("skill", None) {
            let name = child.get_children("name", None).nth(0).map(|ref e| e.content_str());
            let desc = child.get_children("description", None).nth(0).map(|ref e| e.content_str());
            match name {
                Some(ref name) if !name.is_empty() => {
                    match desc {
                        Some(ref desc) if !desc.is_empty() => {
                            let id = child.attributes
                                .get(&("number".to_string(), None)).unwrap()
                                .parse::<usize>()
                                .unwrap();
                            let internal = child.attributes
                                .get(&("spritename".to_string(), None)).unwrap_or(name);
                            skills.insert(id, Skill {
                                name: name.to_owned(),
                                internal: internal.to_owned(),
                            });
                        }
                        _ => {}
                    }
                }
                _ => {}
            }
        }
    }
    skills
}
<commit_msg>Refactor duplicate control creation into a helper fn<commit_after>use std::cell::RefCell;
use std::collections::HashMap;
use std::fs::copy;
use std::path::Path;
use std::str::FromStr;
use std::rc::Rc;

use iup;
use iup::prelude::*;
use iup::control::{Button, Label, List, Text};
use iup::dialog::{FileDlg};
use iup::element::{Handle};
use iup::led;

use time;
use xml;

use parser::{read_path, write_path};
use property::{Property, PropertyMap};

// Since we need to share mutable state with 'static ui callbacks,
// we clone a refcounted cell for moving into each callback.
type PropertyMapRc = Rc<RefCell<PropertyMap>>;

// LED dialog specification.
static DIALOG: &'static str = include_str!("../resources/ui.led");

// Get an element from handle
fn from_handle<E>(handle: iup::element::Handle) -> E where E: Element {
    E::from_handle(handle).unwrap()
}

// Get an element from named handle
fn from_name<E>(name: &str) -> E where E: Element {
    E::from_handle(E::from_name(name).unwrap()).unwrap()
}

// Data-bind an element to a numeric property value.
//
// Value of the element is set to the current value of the property, and
// changes to element value are written back to the property map.
//
// @param props {PropertyMapRc} a cloned refcounted property map.
//
fn bind<T, E>(elem: &mut E, props: PropertyMapRc, key: &'static str)
    where Property: From<T>,
          T: FromStr + Default,
          E: Element + ValueChangedCb {

    // Remove previous bindings, if any.
    elem.remove_valuechanged_cb();

    if let Some(ref prop) = props.borrow().get(key) {
        elem.set_attrib("VALUE", prop.to_string());
    }
    elem.set_valuechanged_cb(move |(elem,): (E,)| {
        if let Some(ref value) = elem.attrib("VALUE") {
            if let Ok(v) = T::from_str(value) {
                props.borrow_mut().insert(key.to_string(), Property::from(v));
            } else {
                props.borrow_mut().insert(key.to_string(), Property::from(T::default()));
            }
        }
    });
}

// Data-bind an element to a list property value, at given index.
//
// Value of the element is set to the current value of the property at index,
// and changes to element value are written back to the property map.
//
// @param props {PropertyMapRc} a cloned refcounted property map.
//
fn bind_list<E>(elem: &mut E, props: PropertyMapRc, key: &'static str, index: usize)
    where E: Element + ValueChangedCb {

    // Remove previous bindings, if any.
    elem.remove_valuechanged_cb();

    if let Some(&Property::List(ref v)) = props.borrow().get(key) {
        elem.set_attrib("VALUE", v[index].to_string());
    }
    elem.set_valuechanged_cb(move |(elem,): (E,)| {
        if let Some(ref value) = elem.attrib("VALUE") {
            if let Some(&mut Property::List(ref mut v)) = props.borrow_mut().get_mut(key) {
                v[index] = value.to_string();
            }
        }
    });
}

macro_rules! bind_stat {
    ($i:ident, $p:expr, $e:expr) => {
        bind::<f32,_>(&mut from_name::<Text>(stringify!($i)), $p.clone(), $e);
    }
}

macro_rules! bind_skill {
    ($i:ident, $p:expr, $n:expr) => {
        bind_list::<_>(&mut from_handle::<Text>($i), $p.clone(), "SkillPoints", $n);
    }
}

// Creates a pair of (label, text) for inputting numeric values.
// @param title if specified, it is used for the label, otherwise the controls are disabled.
fn make_control_pair(title: Option<&String>) -> (Label, Text) {
    let mut label = Label::new()
        .set_attrib("SIZE", "92x12".to_string())
        .set_attrib("TITLE", "(empty)".to_string());
    let mut text = Text::new_spin()
        .set_attrib("SIZE", "36x12".to_string())
        .set_attrib("SPINMAX", "99".to_string())
        .set_attrib("MASKINT", "0:99".to_string())
        .set_attrib("ALIGNMENT", "ARIGHT".to_string());
    if let Some(ref s) = title {
        label.set_attrib("TITLE", s.to_string());
    } else {
        label.set_attrib("ACTIVE", "NO");
        text.set_attrib("ACTIVE", "NO");
    }
    (label, text)
}

// Data-bind all elements relevant to a party member.
//
// @param props {PropertyMapRc} a cloned refcounted property map.
//
fn bind_member(props: PropertyMapRc) {
    bind_stat!(text_int, props, "Int");
    bind_stat!(text_dex, props, "Dex");
    bind_stat!(text_str, props, "Str");
    bind_stat!(text_occ, props, "Occ");
    bind_stat!(text_per, props, "Per");

    bind_stat!(text_wpn_sword,  props, "WpnSword");
    bind_stat!(text_wpn_short,  props, "WpnShortSword");
    bind_stat!(text_wpn_blunt,  props, "WpnSceptor");
    bind_stat!(text_wpn_cleave, props, "WpnAxe");
    bind_stat!(text_wpn_whip,   props, "WpnWhip");
    bind_stat!(text_wpn_bow,    props, "WpnBow");
    bind_stat!(text_wpn_xbow,   props, "WpnXbow");
    bind_stat!(text_wpn_elixir, props, "WpnElixir");

    if let Some(&mut Property::List(ref mut v)) = props.borrow_mut().get_mut("SkillPoints") {
        while v.len() < 115 {
            v.push("0".to_string())
        }
    }
    if let Some(apt_grid) = Handle::from_named("apt_grid") {
        for i in 1..7 {
            if let Some(child) = apt_grid.child((i - 1) * 2 + 1) {
                bind_skill!(child, props, i);
            }
        }
    }
    if let Some(skill_grid) = Handle::from_named("skill_grid") {
        for i in 7..115 {
            if let Some(child) = skill_grid.child((i - 7) * 2 + 1) {
                bind_skill!(child, props, i);
            }
        }
    }
}

/// Ui entry point.
///
/// Starts by showing a direction selection dialog; after the user selects a directory,
/// the game is loaded from that directory and values bound to the ui elements.
///
pub fn ui_loop() -> Result<(), String> {
    match iup::with_iup(|| {
        // See also led::load(path) to load from a file
        led::load_buffer(DIALOG).unwrap();

        // Select saved game location
        let mut dlg_open = from_name::<FileDlg>("dlg_open");
        let dir = match dlg_open.popup(DialogPos::CenterParent, DialogPos::CenterParent) {
            Ok(..) => match dlg_open.attrib("STATUS") {
                Some(ref s) if s == "0" => {
                    dlg_open.attrib("VALUE").unwrap()
                },
                _ => return Err("File selection cancelled.".to_string())
            },
            _ => return Err("File selection failed.".to_string())
        };

        // Read game and party member files
        let game = Rc::new(RefCell::new({
            let path = Path::new(&dir).join("Game.txt");
            match read_path(path.as_path()) {
                Ok(v) => v,
                Err(e) => {
                    return Err(format!("Cannot read {:?}: {}", path, e))
                }
            }
        }));
        let party = Rc::new(RefCell::new({
            let mut members: Vec<PropertyMapRc> = Vec::new();
            if let Some(&Property::String(ref ids)) = game.borrow().get("PartyIDs") {
                for id in ids.split(",") {
                    if id == "0" {
                        continue;
                    }
                    let path = Path::new(&dir).join("Party".to_string() + id + ".txt");
                    match read_path(path.as_path()) {
                        Ok(v) => members.push(Rc::new(RefCell::new(v))),
                        Err(e) => {
                            return Err(format!("Cannot read {:?}: {}", path, e))
                        }
                    };
                }
            }
            members
        }));

        let mut text_emeralds = from_name::<Text>("text_emeralds");
        bind::<u32,_>(&mut text_emeralds, game.clone(), "Emeralds");

        let mut list_party = from_name::<List>("list_party");
        let mut list_party_items: Vec<String> = Vec::new();
        for member in party.borrow().iter() {
            if let Some(&Property::String(ref name)) = member.borrow().get("Name") {
                if let Some(&Property::Float(level)) = member.borrow().get("Level") {
                    list_party_items.push(format!("{} ({})", name, level));
                }
            }
        }

        let skills = load_skills();
        if let Some(mut apt_grid) = Handle::from_named("apt_grid") {
            while let Some(mut child) = apt_grid.child(0) {
                child.detach().destroy();
            }
            for i in 1..7 {
                let (label, text) = make_control_pair(skills.get(&i).map(|ref x| &(x.name)));
                apt_grid.append(label).unwrap();
                apt_grid.append(text).unwrap();
            }
        }
        if let Some(mut skill_grid) = Handle::from_named("skill_grid") {
            while let Some(mut child) = skill_grid.child(0) {
                child.detach().destroy();
            }
            for i in 7..115 {
                let (label, text) = make_control_pair(skills.get(&i).map(|ref x| &(x.name)));
                skill_grid.append(label).unwrap();
                skill_grid.append(text).unwrap();
            }
        }

        let party_clone = party.clone();
        list_party.set_items(list_party_items);
        list_party.set_action(move |(_, _, i, _)| {
            let member = party_clone.borrow()[i as usize - 1].clone();
            bind_member(member);
        });
        if let Some(&ref member) = party.borrow().first() {
            bind_member(member.clone());
        }

        // Write game and party member files on save
        let mut button_save = from_name::<Button>("button_save");
        {
            let game_clone = game.clone();
            let party_clone = party.clone();
            button_save.set_action(move |_| {
                let timestamp = time::strftime("%FT%H.%M.%SZ.txt", &time::now_utc()).unwrap();
                let path = Path::new(&dir).join("Game.txt");
                copy(path.as_path(), path.with_extension(&timestamp)).unwrap();
                write_path(path.as_path(), &game_clone.borrow()).unwrap();
                for member in party_clone.borrow().iter() {

                    // Validate
                    let mut combat_skills: Vec<String> = Vec::new();
                    let mut spell_skills: Vec<String> = Vec::new();
                    if let Some(&Property::List(ref v)) = member.borrow().get("SkillPoints") {
                        let spell_grade = {
                            if let Some(&Property::Float(int_mod)) = member.borrow().get("Int") {
                                if let Some(&Property::Float(occ_mod)) = member.borrow().get("Occ") {
                                    int_mod + occ_mod + 20f32
                                } else {
                                    0f32
                                }
                            } else {
                                0f32
                            }
                        };
                        for (i, ref val) in v.iter().enumerate() {
                            match val.parse::<u32>() {
                                // Combat skills (1-3)
                                Ok(n) if i >= 7 && i <= 61 && n > 0 => {
                                    if let Some(ref skill) = skills.get(&i) {
                                        combat_skills.push(skill.internal.to_string())
                                    }
                                }
                                // Spell skills (1)
                                Ok(n) if i >= 62 && i <= 72 && (n > 0 || spell_grade >= 21f32) => {
                                    if let Some(ref skill) = skills.get(&i) {
                                        spell_skills.push(skill.internal.to_string())
                                    }
                                }
                                // Spell skills (2)
                                Ok(n) if i >= 73 && i <= 86 && (n > 0 || spell_grade >= 26f32) => {
                                    if let Some(ref skill) = skills.get(&i) {
                                        spell_skills.push(skill.internal.to_string())
                                    }
                                }
                                // Spell skills (3)
                                Ok(n) if i >= 87 && i <= 114 && (n > 0 || spell_grade >= 32f32) => {
                                    if let Some(ref skill) = skills.get(&i) {
                                        spell_skills.push(skill.internal.to_string())
                                    }
                                }
                                _ => {}
                            }
                        }
                    }
                    if let Some(&mut Property::List(ref mut v)) = member.borrow_mut().get_mut("CombatSelects") {
                        v.retain(|ref x| combat_skills.contains(&x));
                    }
                    if let Some(&mut Property::List(ref mut v)) = member.borrow_mut().get_mut("SpellFavorites") {
                        v.retain(|ref x| spell_skills.contains(&x));
                    }
                    member.borrow_mut().insert("CombatSkills".to_string(), Property::List(combat_skills));
                    member.borrow_mut().insert("SpellSkills".to_string(), Property::List(spell_skills));

                    if let Some(&Property::String(ref id)) = member.borrow().get("PartyID") {
                        let path = Path::new(&dir).join("Party".to_string() + id + ".txt");
                        copy(path.as_path(), path.with_extension(&timestamp)).unwrap();
                        write_path(path.as_path(), &member.borrow()).unwrap();
                    }
                }
            });
        }
        let mut button_close = from_name::<Button>("button_close");
        button_close.set_action(|_| {
            CallbackReturn::Close
        });

        let mut dlg = from_name::<Dialog>("dlg");
        dlg.show()

    }) {
        Err(iup::InitError::UserError(s)) => Err(s),
        Err(e) => Err(format!("{:?}", e)),
        _ => Ok(())
    }
}

struct Skill {
    name: String,
    internal: String,
}

fn load_skills() -> HashMap<usize, Skill> {
    let mut skills: HashMap<usize, Skill> = HashMap::with_capacity(115);
    if let Ok(elem) = include_str!("../resources/skills.xml").parse::<xml::Element>() {
        for child in elem.get_children("skill", None) {
            let name = child.get_children("name", None).nth(0).map(|ref e| e.content_str());
            let desc = child.get_children("description", None).nth(0).map(|ref e| e.content_str());
            match name {
                Some(ref name) if !name.is_empty() => {
                    match desc {
                        Some(ref desc) if !desc.is_empty() => {
                            let id = child.attributes
                                .get(&("number".to_string(), None)).unwrap()
                                .parse::<usize>()
                                .unwrap();
                            let internal = child.attributes
                                .get(&("spritename".to_string(), None)).unwrap_or(name);
                            skills.insert(id, Skill {
                                name: name.to_owned(),
                                internal: internal.to_owned(),
                            });
                        }
                        _ => {}
                    }
                }
                _ => {}
            }
        }
    }
    skills
}
<|endoftext|>