<commit_before>use std::sync::Arc;
use std::collections::{HashMap, VecDeque};
use std::{fs, fmt, path};
use std::io::{self, Seek, SeekFrom, Write, Read};
use std::path::PathBuf;
use torrent::Info;
use util::hash_to_id;
use ring::digest;
use amy;
use {handle, CONFIG};

const POLL_INT_MS: usize = 1000;

pub struct Disk {
    poll: amy::Poller,
    ch: handle::Handle<Request, Response>,
    files: FileCache,
    active: VecDeque<Request>,
}

struct FileCache {
    files: HashMap<path::PathBuf, fs::File>,
}

pub enum Request {
    Write {
        tid: usize,
        data: Box<[u8; 16_384]>,
        locations: Vec<Location>,
        path: Option<String>,
    },
    Read {
        data: Box<[u8; 16_384]>,
        locations: Vec<Location>,
        context: Ctx,
        path: Option<String>,
    },
    Serialize {
        tid: usize,
        data: Vec<u8>,
        hash: [u8; 20],
    },
    Delete {
        tid: usize,
        hash: [u8; 20],
        files: Vec<PathBuf>,
        path: Option<String>,
    },

    Validate {
        tid: usize,
        info: Arc<Info>,
        path: Option<String>,
        idx: u32,
        invalid: Vec<u32>,
    },
    Shutdown,
}

enum JobRes {
    Resp(Response),
    Done,
    Paused(Request),
}

pub struct Ctx {
    pub pid: usize,
    pub tid: usize,
    pub idx: u32,
    pub begin: u32,
    pub length: u32,
}

impl Ctx {
    pub fn new(pid: usize, tid: usize, idx: u32, begin: u32, length: u32) -> Ctx {
        Ctx {
            pid,
            tid,
            idx,
            begin,
            length,
        }
    }
}

impl FileCache {
    pub fn new() -> FileCache {
        FileCache { files: HashMap::new() }
    }

    pub fn get_file<F: FnOnce(&mut fs::File) -> io::Result<()>>(
        &mut self,
        path: &path::Path,
        f: F,
    ) -> io::Result<()> {
        if self.files.contains_key(path) {
            f(self.files.get_mut(path).unwrap())?;
        } else {
            // TODO: LRU maybe?
            if self.files.len() >= CONFIG.net.max_open_files {
                let removal = self.files.iter().map(|(id, _)| id.clone()).next().unwrap();
                self.files.remove(&removal);
            }
            fs::create_dir_all(path.parent().unwrap())?;
            let mut file = fs::OpenOptions::new()
                .write(true)
                .create(true)
                .read(true)
                .open(path)?;
            f(&mut file)?;
            self.files.insert(path.to_path_buf(), file);
        }
        Ok(())
    }

    pub fn remove_file(&mut self, path: &path::Path) {
        self.files.remove(path);
    }
}

impl Request {
    pub fn write(
        tid: usize,
        data: Box<[u8; 16_384]>,
        locations: Vec<Location>,
        path: Option<String>,
    ) -> Request {
        Request::Write {
            tid,
            data,
            locations,
            path,
        }
    }

    pub fn read(
        context: Ctx,
        data: Box<[u8; 16_384]>,
        locations: Vec<Location>,
        path: Option<String>,
    ) -> Request {
        Request::Read {
            context,
            data,
            locations,
            path,
        }
    }

    pub fn serialize(tid: usize, data: Vec<u8>, hash: [u8; 20]) -> Request {
        Request::Serialize { tid, data, hash }
    }

    pub fn validate(tid: usize, info: Arc<Info>, path: Option<String>) -> Request {
        Request::Validate {
            tid,
            info,
            path,
            idx: 0,
            invalid: Vec::new(),
        }
    }

    pub fn delete(
        tid: usize,
        hash: [u8; 20],
        files: Vec<PathBuf>,
        path: Option<String>,
    ) -> Request {
        Request::Delete {
            tid,
            hash,
            files,
            path,
        }
    }

    pub fn shutdown() -> Request {
        Request::Shutdown
    }

    fn execute(self, fc: &mut FileCache) -> io::Result<JobRes> {
        let sd = &CONFIG.disk.session;
        let dd = &CONFIG.disk.directory;
        match self {
            Request::Write {
                data,
                locations,
                path,
                ..
            } => {
                for loc in locations {
                    let mut pb = path::PathBuf::from(path.as_ref().unwrap_or(dd));
                    pb.push(&loc.file);
                    fc.get_file(&pb, |f| {
                        f.seek(SeekFrom::Start(loc.offset))?;
                        f.write_all(&data[loc.start..loc.end])?;
                        Ok(())
                    })?;
                }
            }
            Request::Read {
                context,
                mut data,
                locations,
                path,
                ..
            } => {
                for loc in locations {
                    let mut pb = path::PathBuf::from(path.as_ref().unwrap_or(dd));
                    pb.push(&loc.file);
                    fc.get_file(&pb, |f| {
                        f.seek(SeekFrom::Start(loc.offset))?;
                        f.read_exact(&mut data[loc.start..loc.end])?;
                        Ok(())
                    })?;
                }
                let data = Arc::new(data);
                return Ok(JobRes::Resp(Response::read(context, data)));
            }
            Request::Serialize { data, hash, .. } => {
                let mut pb = path::PathBuf::from(sd);
                pb.push(hash_to_id(&hash));
                let mut f = fs::OpenOptions::new().write(true).create(true).open(&pb)?;
                f.write_all(&data)?;
            }
            Request::Delete { hash, files, path, .. } => {
                let mut spb = path::PathBuf::from(sd);
                spb.push(hash_to_id(&hash));
                fs::remove_file(spb)?;

                for file in files {
                    let mut pb = path::PathBuf::from(path.as_ref().unwrap_or(dd));
                    pb.push(&file);
                    fc.remove_file(&pb);
                }
            }
            Request::Validate {
                tid,
                info,
                path,
                mut idx,
                mut invalid,
            } => {
                let mut buf = vec![0u8; info.piece_len as usize];
                let mut pb = path::PathBuf::from(path.as_ref().unwrap_or(dd));
                let mut cf = pb.clone();

                let mut f = fs::OpenOptions::new().read(true).open(&pb);

                let mut max = 0;

                while idx < info.pieces() && max < 10 {
                    let mut valid = true;
                    let mut ctx = digest::Context::new(&digest::SHA1);
                    let locs = info.piece_disk_locs(idx);
                    let mut pos = 0;
                    for loc in locs {
                        if loc.file != cf {
                            pb = path::PathBuf::from(path.as_ref().unwrap_or(dd));
                            pb.push(&loc.file);
                            f = fs::OpenOptions::new().read(true).open(&pb);
                            cf = loc.file.clone();
                        }
                        // Because this is pausable/resumable, we need to seek to the proper
                        // file position.
                        f.as_mut()
                            .map(|file| file.seek(SeekFrom::Start(loc.offset)))
                            .ok();
                        if let Ok(Ok(amnt)) = f.as_mut().map(|file| file.read(&mut buf[pos..])) {
                            ctx.update(&buf[pos..pos + amnt]);
                            pos += amnt;
                        } else {
                            valid = false;
                        }
                    }
                    let digest = ctx.finish();
                    if !valid || digest.as_ref() != &info.hashes[idx as usize][..] {
                        invalid.push(idx);
                    }

                    max += 1;
                    idx += 1;
                }
                if idx == info.pieces() {
                    return Ok(JobRes::Resp(Response::validation_complete(tid, invalid)));
                } else {
                    return Ok(JobRes::Paused(Request::Validate {
                        tid,
                        info,
                        path,
                        idx,
                        invalid,
                    }));
                }
            }
            Request::Shutdown => unreachable!(),
        }
        Ok(JobRes::Done)
    }

    pub fn tid(&self) -> usize {
        match *self {
            Request::Serialize { tid, .. } |
            Request::Validate { tid, .. } |
            Request::Delete { tid, .. } |
            Request::Write { tid, .. } => tid,
            Request::Read { ref context, .. } => context.tid,
            Request::Shutdown => unreachable!(),
        }
    }
}

impl fmt::Debug for Request {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "disk::Request")
    }
}

pub struct Location {
    pub file: PathBuf,
    pub offset: u64,
    pub start: usize,
    pub end: usize,
}

impl Location {
    pub fn new(file: PathBuf, offset: u64, start: u64, end: u64) -> Location {
        Location {
            file,
            offset,
            start: start as usize,
            end: end as usize,
        }
    }
}

pub enum Response {
    Read {
        context: Ctx,
        data: Arc<Box<[u8; 16_384]>>,
    },
    ValidationComplete { tid: usize, invalid: Vec<u32> },
    Error { tid: usize, err: io::Error },
}

impl Response {
    pub fn read(context: Ctx, data: Arc<Box<[u8; 16_384]>>) -> Response {
        Response::Read { context, data }
    }

    pub fn error(tid: usize, err: io::Error) -> Response {
        Response::Error { tid, err }
    }

    pub fn validation_complete(tid: usize, invalid: Vec<u32>) -> Response {
        Response::ValidationComplete { tid, invalid }
    }

    pub fn tid(&self) -> usize {
        match *self {
            Response::Read { ref context, .. } => context.tid,
            Response::ValidationComplete { tid, .. } |
            Response::Error { tid, .. } => tid,
        }
    }
}

impl fmt::Debug for Response {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "disk::Response")
    }
}

impl Disk {
    pub fn new(poll: amy::Poller, ch: handle::Handle<Request, Response>) -> Disk {
        Disk {
            poll,
            ch,
            files: FileCache::new(),
            active: VecDeque::new(),
        }
    }

    pub fn run(&mut self) {
        let sd = &CONFIG.disk.session;
        fs::create_dir_all(sd).unwrap();

        loop {
            match self.poll.wait(POLL_INT_MS) {
                Ok(_) => {
                    if self.handle_events() {
                        break;
                    }
                }
                Err(e) => {
                    error!("Failed to poll for events: {:?}", e);
                }
            }
            if !self.active.is_empty() {
                if self.handle_active() {
                    break;
                }
            }
        }
    }

    fn handle_active(&mut self) -> bool {
        while let Some(j) = self.active.pop_front() {
            let tid = j.tid();
            match j.execute(&mut self.files) {
                Ok(JobRes::Resp(r)) => {
                    self.ch.send(r).ok();
                }
                Ok(JobRes::Paused(s)) => {
                    self.active.push_front(s);
                }
                Ok(JobRes::Done) => {}
                Err(e) => {
                    self.ch.send(Response::error(tid, e)).ok();
                }
            }
            match self.poll.wait(0) {
                Ok(_) => {
                    if self.handle_events() {
                        return true;
                    }
                }
                Err(e) => {
                    error!("Failed to poll for events: {:?}", e);
                }
            }
        }
        false
    }

    pub fn handle_events(&mut self) -> bool {
        loop {
            match self.ch.recv() {
                Ok(Request::Shutdown) => {
                    return true;
                }
                Ok(r) => {
                    trace!("Handling disk job!");
                    let tid = r.tid();
                    match r.execute(&mut self.files) {
                        Ok(JobRes::Resp(r)) => {
                            self.ch.send(r).ok();
                        }
                        Ok(JobRes::Paused(s)) => {
                            self.active.push_back(s);
                        }
                        Ok(JobRes::Done) => {}
                        Err(e) => {
                            self.ch.send(Response::error(tid, e)).ok();
                        }
                    }
                }
                _ => break,
            }
        }
        false
    }
}

pub fn start(creg: &mut amy::Registrar) -> io::Result<handle::Handle<Response, Request>> {
    let poll = amy::Poller::new()?;
    let mut reg = poll.get_registrar()?;
    let (ch, dh) = handle::Handle::new(creg, &mut reg)?;
    dh.run("disk", move |h| Disk::new(poll, h).run());
    Ok(ch)
}
<commit_msg>Use time interval to pause validation job<commit_after>use std::sync::Arc;
use std::collections::{HashMap, VecDeque};
use std::{fs, fmt, path, time};
use std::io::{self, Seek, SeekFrom, Write, Read};
use std::path::PathBuf;
use torrent::Info;
use util::hash_to_id;
use ring::digest;
use amy;
use {handle, CONFIG};

const POLL_INT_MS: usize = 1000;
const JOB_TIME_SLICE: u64 = 1;

pub struct Disk {
    poll: amy::Poller,
    ch: handle::Handle<Request, Response>,
    files: FileCache,
    active: VecDeque<Request>,
}

struct FileCache {
    files: HashMap<path::PathBuf, fs::File>,
}

pub enum Request {
    Write {
        tid: usize,
        data: Box<[u8; 16_384]>,
        locations: Vec<Location>,
        path: Option<String>,
    },
    Read {
        data: Box<[u8; 16_384]>,
        locations: Vec<Location>,
        context: Ctx,
        path: Option<String>,
    },
    Serialize {
        tid: usize,
        data: Vec<u8>,
        hash: [u8; 20],
    },
    Delete {
        tid: usize,
        hash: [u8; 20],
        files: Vec<PathBuf>,
        path: Option<String>,
    },

    Validate {
        tid: usize,
        info: Arc<Info>,
        path: Option<String>,
        idx: u32,
        invalid: Vec<u32>,
    },
    Shutdown,
}

enum JobRes {
    Resp(Response),
    Done,
    Paused(Request),
}

pub struct Ctx {
    pub pid: usize,
    pub tid: usize,
    pub idx: u32,
    pub begin: u32,
    pub length: u32,
}

impl Ctx {
    pub fn new(pid: usize, tid: usize, idx: u32, begin: u32, length: u32) -> Ctx {
        Ctx {
            pid,
            tid,
            idx,
            begin,
            length,
        }
    }
}

impl FileCache {
    pub fn new() -> FileCache {
        FileCache { files: HashMap::new() }
    }

    pub fn get_file<F: FnOnce(&mut fs::File) -> io::Result<()>>(
        &mut self,
        path: &path::Path,
        f: F,
    ) -> io::Result<()> {
        if self.files.contains_key(path) {
            f(self.files.get_mut(path).unwrap())?;
        } else {
            // TODO: LRU maybe?
            if self.files.len() >= CONFIG.net.max_open_files {
                let removal = self.files.iter().map(|(id, _)| id.clone()).next().unwrap();
                self.files.remove(&removal);
            }
            fs::create_dir_all(path.parent().unwrap())?;
            let mut file = fs::OpenOptions::new()
                .write(true)
                .create(true)
                .read(true)
                .open(path)?;
            f(&mut file)?;
            self.files.insert(path.to_path_buf(), file);
        }
        Ok(())
    }

    pub fn remove_file(&mut self, path: &path::Path) {
        self.files.remove(path);
    }
}

impl Request {
    pub fn write(
        tid: usize,
        data: Box<[u8; 16_384]>,
        locations: Vec<Location>,
        path: Option<String>,
    ) -> Request {
        Request::Write {
            tid,
            data,
            locations,
            path,
        }
    }

    pub fn read(
        context: Ctx,
        data: Box<[u8; 16_384]>,
        locations: Vec<Location>,
        path: Option<String>,
    ) -> Request {
        Request::Read {
            context,
            data,
            locations,
            path,
        }
    }

    pub fn serialize(tid: usize, data: Vec<u8>, hash: [u8; 20]) -> Request {
        Request::Serialize { tid, data, hash }
    }

    pub fn validate(tid: usize, info: Arc<Info>, path: Option<String>) -> Request {
        Request::Validate {
            tid,
            info,
            path,
            idx: 0,
            invalid: Vec::new(),
        }
    }

    pub fn delete(
        tid: usize,
        hash: [u8; 20],
        files: Vec<PathBuf>,
        path: Option<String>,
    ) -> Request {
        Request::Delete {
            tid,
            hash,
            files,
            path,
        }
    }

    pub fn shutdown() -> Request {
        Request::Shutdown
    }

    fn execute(self, fc: &mut FileCache) -> io::Result<JobRes> {
        let sd = &CONFIG.disk.session;
        let dd = &CONFIG.disk.directory;
        match self {
            Request::Write {
                data,
                locations,
                path,
                ..
            } => {
                for loc in locations {
                    let mut pb = path::PathBuf::from(path.as_ref().unwrap_or(dd));
                    pb.push(&loc.file);
                    fc.get_file(&pb, |f| {
                        f.seek(SeekFrom::Start(loc.offset))?;
                        f.write_all(&data[loc.start..loc.end])?;
                        Ok(())
                    })?;
                }
            }
            Request::Read {
                context,
                mut data,
                locations,
                path,
                ..
            } => {
                for loc in locations {
                    let mut pb = path::PathBuf::from(path.as_ref().unwrap_or(dd));
                    pb.push(&loc.file);
                    fc.get_file(&pb, |f| {
                        f.seek(SeekFrom::Start(loc.offset))?;
                        f.read_exact(&mut data[loc.start..loc.end])?;
                        Ok(())
                    })?;
                }
                let data = Arc::new(data);
                return Ok(JobRes::Resp(Response::read(context, data)));
            }
            Request::Serialize { data, hash, .. } => {
                let mut pb = path::PathBuf::from(sd);
                pb.push(hash_to_id(&hash));
                let mut f = fs::OpenOptions::new().write(true).create(true).open(&pb)?;
                f.write_all(&data)?;
            }
            Request::Delete { hash, files, path, .. } => {
                let mut spb = path::PathBuf::from(sd);
                spb.push(hash_to_id(&hash));
                fs::remove_file(spb)?;

                for file in files {
                    let mut pb = path::PathBuf::from(path.as_ref().unwrap_or(dd));
                    pb.push(&file);
                    fc.remove_file(&pb);
                }
            }
            Request::Validate {
                tid,
                info,
                path,
                mut idx,
                mut invalid,
            } => {
                let mut buf = vec![0u8; info.piece_len as usize];
                let mut pb = path::PathBuf::from(path.as_ref().unwrap_or(dd));
                let mut cf = pb.clone();

                let mut f = fs::OpenOptions::new().read(true).open(&pb);

                let start = time::Instant::now();

                while idx < info.pieces() &&
                    start.elapsed() < time::Duration::from_secs(JOB_TIME_SLICE)
                {
                    let mut valid = true;
                    let mut ctx = digest::Context::new(&digest::SHA1);
                    let locs = info.piece_disk_locs(idx);
                    let mut pos = 0;
                    for loc in locs {
                        if loc.file != cf {
                            pb = path::PathBuf::from(path.as_ref().unwrap_or(dd));
                            pb.push(&loc.file);
                            f = fs::OpenOptions::new().read(true).open(&pb);
                            cf = loc.file.clone();
                        }
                        // Because this is pausable/resumable, we need to seek to the proper
                        // file position.
                        f.as_mut()
                            .map(|file| file.seek(SeekFrom::Start(loc.offset)))
                            .ok();
                        if let Ok(Ok(amnt)) = f.as_mut().map(|file| file.read(&mut buf[pos..])) {
                            ctx.update(&buf[pos..pos + amnt]);
                            pos += amnt;
                        } else {
                            valid = false;
                        }
                    }
                    let digest = ctx.finish();
                    if !valid || digest.as_ref() != &info.hashes[idx as usize][..] {
                        invalid.push(idx);
                    }

                    idx += 1;
                }
                if idx == info.pieces() {
                    return Ok(JobRes::Resp(Response::validation_complete(tid, invalid)));
                } else {
                    return Ok(JobRes::Paused(Request::Validate {
                        tid,
                        info,
                        path,
                        idx,
                        invalid,
                    }));
                }
            }
            Request::Shutdown => unreachable!(),
        }
        Ok(JobRes::Done)
    }

    pub fn tid(&self) -> usize {
        match *self {
            Request::Serialize { tid, .. } |
            Request::Validate { tid, .. } |
            Request::Delete { tid, .. } |
            Request::Write { tid, .. } => tid,
            Request::Read { ref context, .. } => context.tid,
            Request::Shutdown => unreachable!(),
        }
    }
}

impl fmt::Debug for Request {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "disk::Request")
    }
}

pub struct Location {
    pub file: PathBuf,
    pub offset: u64,
    pub start: usize,
    pub end: usize,
}

impl Location {
    pub fn new(file: PathBuf, offset: u64, start: u64, end: u64) -> Location {
        Location {
            file,
            offset,
            start: start as usize,
            end: end as usize,
        }
    }
}

pub enum Response {
    Read {
        context: Ctx,
        data: Arc<Box<[u8; 16_384]>>,
    },
    ValidationComplete { tid: usize, invalid: Vec<u32> },
    Error { tid: usize, err: io::Error },
}

impl Response {
    pub fn read(context: Ctx, data: Arc<Box<[u8; 16_384]>>) -> Response {
        Response::Read { context, data }
    }

    pub fn error(tid: usize, err: io::Error) -> Response {
        Response::Error { tid, err }
    }

    pub fn validation_complete(tid: usize, invalid: Vec<u32>) -> Response {
        Response::ValidationComplete { tid, invalid }
    }

    pub fn tid(&self) -> usize {
        match *self {
            Response::Read { ref context, .. } => context.tid,
            Response::ValidationComplete { tid, .. } |
            Response::Error { tid, .. } => tid,
        }
    }
}

impl fmt::Debug for Response {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "disk::Response")
    }
}

impl Disk {
    pub fn new(poll: amy::Poller, ch: handle::Handle<Request, Response>) -> Disk {
        Disk {
            poll,
            ch,
            files: FileCache::new(),
            active: VecDeque::new(),
        }
    }

    pub fn run(&mut self) {
        let sd = &CONFIG.disk.session;
        fs::create_dir_all(sd).unwrap();

        loop {
            match self.poll.wait(POLL_INT_MS) {
                Ok(_) => {
                    if self.handle_events() {
                        break;
                    }
                }
                Err(e) => {
                    error!("Failed to poll for events: {:?}", e);
                }
            }
            if !self.active.is_empty() {
                if self.handle_active() {
                    break;
                }
            }
        }
    }

    fn handle_active(&mut self) -> bool {
        while let Some(j) = self.active.pop_front() {
            let tid = j.tid();
            match j.execute(&mut self.files) {
                Ok(JobRes::Resp(r)) => {
                    self.ch.send(r).ok();
                }
                Ok(JobRes::Paused(s)) => {
                    self.active.push_front(s);
                }
                Ok(JobRes::Done) => {}
                Err(e) => {
                    self.ch.send(Response::error(tid, e)).ok();
                }
            }
            match self.poll.wait(0) {
                Ok(_) => {
                    if self.handle_events() {
                        return true;
                    }
                }
                Err(e) => {
                    error!("Failed to poll for events: {:?}", e);
                }
            }
        }
        false
    }

    pub fn handle_events(&mut self) -> bool {
        loop {
            match self.ch.recv() {
                Ok(Request::Shutdown) => {
                    return true;
                }
                Ok(r) => {
                    trace!("Handling disk job!");
                    let tid = r.tid();
                    match r.execute(&mut self.files) {
                        Ok(JobRes::Resp(r)) => {
                            self.ch.send(r).ok();
                        }
                        Ok(JobRes::Paused(s)) => {
                            self.active.push_back(s);
                        }
                        Ok(JobRes::Done) => {}
                        Err(e) => {
                            self.ch.send(Response::error(tid, e)).ok();
                        }
                    }
                }
                _ => break,
            }
        }
        false
    }
}

pub fn start(creg: &mut amy::Registrar) -> io::Result<handle::Handle<Response, Request>> {
    let poll = amy::Poller::new()?;
    let mut reg = poll.get_registrar()?;
    let (ch, dh) = handle::Handle::new(creg, &mut reg)?;
    dh.run("disk", move |h| Disk::new(poll, h).run());
    Ok(ch)
}
<|endoftext|>


<commit_before>//! `las` file management.

use std::f64;
use std::fs;
use std::io::{BufReader, BufWriter, Seek, Read, Write};
use std::path::Path;

use byteorder::{LittleEndian, WriteBytesExt};

use Result;
use error::Error;
use header::Header;
use io::write_zeros;
use point::Point;
use scale::descale;
use reader::Reader;
use vlr::Vlr;

/// A las file.
#[derive(Debug, PartialEq)]
pub struct File {
    header: Header,
    vlrs: Vec<Vlr>,
    points: Vec<Point>,
}

impl File {
    /// Reads a las file from the filesystem.
    ///
    /// # Examples
    ///
    /// ```
    /// use las::file::File;
    /// let file = File::from_path("data/1.0_0.las").unwrap();
    /// ```
    pub fn from_path<P: AsRef<Path>>(path: P) -> Result<File> {
        let reader = BufReader::new(try!(fs::File::open(path)));
        File::read_from(reader)
    }

    /// Reads a las file from a `Read`.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::fs;
    /// use las::file::File;
    /// let reader = fs::File::open("data/1.0_0.las").unwrap();
    /// let file = File::read_from(reader).unwrap();
    /// ```
    pub fn read_from<R: Read + Seek>(reader: R) -> Result<File> {
        let mut file = File::new();
        let mut reader = try!(Reader::new(reader));
        file.header = reader.header();
        file.vlrs = (*reader.vlrs()).clone();
        file.points.reserve(reader.npoints() as usize);
        loop {
            match try!(reader.next_point()) {
                Some(point) => file.points.push(point),
                None => break,
            }
        }
        Ok(file)
    }

    /// Creates a new, empty las file.
    ///
    /// # Examples
    ///
    /// ```
    /// use las::file::File;
    /// let file = File::new();
    /// ```
    pub fn new() -> File {
        File {
            header: Header::new(),
            vlrs: Vec::new(),
            points: Vec::new(),
        }
    }

    /// Sets the header for this file.
    ///
    /// Since the header contains so much metadata, we might want to construct a header elsewhere
    /// then set it to the file just before write.
    ///
    /// # Examples
    ///
    /// ```
    /// use las::file::File;
    /// use las::header::Header;
    /// let mut file = File::new();
    /// let header = Header::new();
    /// file.set_header(header);
    /// ```
    pub fn set_header(&mut self, header: Header) {
        self.header = header;
    }

    /// Returns a reference to a vector of this file's points.
    ///
    /// # Examples
    ///
    /// ```
    /// use las::file::File;
    /// let file = File::from_path("data/1.0_0.las").unwrap();
    /// let points = file.points();
    /// ```
    pub fn points(&self) -> &Vec<Point> {
        &self.points
    }

    /// Adds a point to this lasfile.
    ///
    /// # Examples
    ///
    /// ```
    /// use las::file::File;
    /// use las::point::Point;
    /// let mut file = File::new();
    /// let point = Point::new();
    /// file.add_point(point);
    /// ```
    pub fn add_point(&mut self, point: Point) {
        self.points.push(point);
    }

    /// Writes out this las file to a `Path`.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::fs::remove_file;
    /// use las::file::File;
    /// let mut file = File::new();
    /// file.to_path("temp.las", true).unwrap();
    /// remove_file("temp.las");
    /// ```
    pub fn to_path<P: AsRef<Path>>(&mut self, path: P, auto_offsets: bool) -> Result<()> {
        let ref mut writer = BufWriter::new(try!(fs::File::create(path)));
        self.write_to(writer, auto_offsets)
    }

    /// Writes this las file to a `Write`.
    ///
    /// If auto_offsets is true, reasonable offset values will be calculated and written to the
    /// header before the file is written.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::io::Cursor;
    /// use las::file::File;
    /// let mut file = File::from_path("data/1.0_0.las").unwrap();
    /// let ref mut cursor = Cursor::new(Vec::new());
    /// file.write_to(cursor, true).unwrap();
    /// ```
    pub fn write_to<W: Write>(&mut self, writer: &mut W, auto_offsets: bool) -> Result<()> {
        self.header.calculate_size();
        self.header.number_of_point_records = self.points.len() as u32;
        self.header.offset_to_point_data = self.header.header_size as u32 +
                                           self.vlrs.iter().fold(0, |a, v| a + v.len());
        self.header.point_data_record_length = self.header.point_data_format.record_length();

        let mut number_of_points_by_return = [0u32; 5];
        let mut x_min = f64::MAX;
        let mut y_min = f64::MAX;
        let mut z_min = f64::MAX;
        let mut x_max = f64::MIN;
        let mut y_max = f64::MIN;
        let mut z_max = f64::MIN;
        for point in &self.points {
            let return_number = point.return_number.as_u8();
            if return_number > 0 {
                number_of_points_by_return[(return_number - 1) as usize] += 1;
            }
            if point.x < x_min {
                x_min = point.x;
            }
            if point.y < y_min {
                y_min = point.y;
            }
            if point.z < z_min {
                z_min = point.z;
            }
            if point.x > x_max {
                x_max = point.x;
            }
            if point.y > y_max {
                y_max = point.y;
            }
            if point.z > z_max {
                z_max = point.z;
            }
        }
        self.header.x_min = x_min;
        self.header.y_min = y_min;
        self.header.z_min = z_min;
        self.header.x_max = x_max;
        self.header.y_max = y_max;
        self.header.z_max = z_max;

        if auto_offsets {
            self.header.x_offset = (x_min + x_max) / 2.0;
            self.header.y_offset = (y_min + y_max) / 2.0;
            self.header.z_offset = (z_min + z_max) / 2.0;
        }

        let mut bytes_written = try!(self.header.write_to(writer)) as usize;
        if bytes_written < self.header.header_size as usize {
            bytes_written += try!(write_zeros(writer,
                                              self.header.header_size as usize - bytes_written));
        }
        for vlr in &self.vlrs {
            bytes_written += try!(vlr.write_to(writer)) as usize;
        }
        if bytes_written < self.header.offset_to_point_data as usize {
            try!(write_zeros(writer,
                             self.header.offset_to_point_data as usize - bytes_written));
        }
        for point in &self.points {
            try!(self.write_point_to(writer, point));
        }
        Ok(())
    }

    fn write_point_to<W: Write>(&self, writer: &mut W, point: &Point) -> Result<()> {
        try!(writer.write_i32::<LittleEndian>(descale(point.x,
                                                      self.header.x_scale_factor,
                                                      self.header.x_offset)));
        try!(writer.write_i32::<LittleEndian>(descale(point.y,
                                                      self.header.y_scale_factor,
                                                      self.header.y_offset)));
        try!(writer.write_i32::<LittleEndian>(descale(point.z,
                                                      self.header.z_scale_factor,
                                                      self.header.z_offset)));
        try!(writer.write_u16::<LittleEndian>(point.intensity));
        let byte = point.return_number.as_u8() + (point.number_of_returns.as_u8() << 3) +
                   (point.scan_direction.as_u8() << 6) +
                   ((point.edge_of_flight_line as u8) << 7);
        try!(writer.write_u8(byte));
        let byte = point.classification.as_u8() + ((point.synthetic as u8) << 5) +
                   ((point.key_point as u8) << 6) +
                   ((point.withheld as u8) << 7);
        try!(writer.write_u8(byte));
        try!(writer.write_i8(point.scan_angle_rank));
        try!(writer.write_u8(point.user_data));
        try!(writer.write_u16::<LittleEndian>(point.point_source_id));
        if self.header.point_data_format.has_time() {
            match point.gps_time {
                Some(gps_time) => try!(writer.write_f64::<LittleEndian>(gps_time)),
                None => {
                    return Err(Error::PointFormat(self.header.point_data_format,
                                                     "gps_time".to_string()))
                }
            }
        }
        if self.header.point_data_format.has_color() {
            match point.red {
                Some(red) => try!(writer.write_u16::<LittleEndian>(red)),
                None => {
                    return Err(Error::PointFormat(self.header.point_data_format,
                                                     "red".to_string()))
                }
            }
            match point.green {
                Some(green) => try!(writer.write_u16::<LittleEndian>(green)),
                None => {
                    return Err(Error::PointFormat(self.header.point_data_format,
                                                     "green".to_string()))
                }
            }
            match point.blue {
                Some(blue) => try!(writer.write_u16::<LittleEndian>(blue)),
                None => {
                    return Err(Error::PointFormat(self.header.point_data_format,
                                                     "blue".to_string()))
                }
            }
        }
        match point.extra_bytes {
            Some(ref bytes) => try!(writer.write_all(&bytes[..])),
            None => {}
        }
        Ok(())
    }
}

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

    use std::fs::remove_file;
    use std::io::Cursor;
    use std::path::Path;

    use point::Point;

    fn roundtrip<P: AsRef<Path>>(path: P) {
        let mut lasfile = File::from_path(path).unwrap();
        let ref mut cursor = Cursor::new(Vec::new());
        lasfile.write_to(cursor, false).unwrap();
        cursor.set_position(0);
        let lasfile2 = File::read_from(cursor).unwrap();
        assert_eq!(lasfile, lasfile2);
    }

    #[test]
    fn roundtrip_1_0_0() {
        roundtrip("data/1.0_0.las");
    }

    #[test]
    fn roundtrip_1_0_1() {
        roundtrip("data/1.0_1.las");
    }

    #[test]
    fn roundtrip_1_1_0() {
        roundtrip("data/1.1_0.las");
    }

    #[test]
    fn roundtrip_1_1_1() {
        roundtrip("data/1.1_1.las");
    }

    #[test]
    fn roundtrip_1_2_0() {
        roundtrip("data/1.2_0.las");
    }

    #[test]
    fn roundtrip_1_2_1() {
        roundtrip("data/1.2_1.las");
    }

    #[test]
    fn roundtrip_1_2_2() {
        roundtrip("data/1.2_2.las");
    }

    #[test]
    fn roundtrip_1_2_3() {
        roundtrip("data/1.2_3.las");
    }

    /// This file is good as it exercieses a weird use case, but the test fails at the moment. I'm
    /// not sure why, so I'm going to keep it around but ignore it.
    #[test]
    #[ignore]
    fn roundtrip_extrabytes() {
        roundtrip("data/extrabytes.las");
    }

    #[test]
    fn point_format_1_has_gps_time() {
        let lasfile = File::from_path("data/1.0_1.las").unwrap();
        let ref point = lasfile.points()[0];
        assert!(point.gps_time.is_some());
    }

    #[test]
    fn point_format_2_has_color() {
        let lasfile = File::from_path("data/1.2_2.las").unwrap();
        let ref point = lasfile.points()[0];
        assert!(point.red.is_some());
        assert!(point.green.is_some());
        assert!(point.blue.is_some());
    }

    #[test]
    fn write_one_point() {
        let mut point = Point::new();
        point.x = 1.0;
        point.y = 2.0;
        point.z = 3.0;
        let mut lasfile = File::new();
        lasfile.add_point(point);
        lasfile.to_path("temp.las", false).unwrap();

        let lasfile = File::from_path("temp.las").unwrap();
        let ref point = lasfile.points()[0];
        assert_eq!(1.0, point.x);
        assert_eq!(2.0, point.y);
        assert_eq!(3.0, point.z);

        remove_file("temp.las").unwrap();
    }
}
<commit_msg>Add getter for a file header<commit_after>//! `las` file management.

use std::f64;
use std::fs;
use std::io::{BufReader, BufWriter, Seek, Read, Write};
use std::path::Path;

use byteorder::{LittleEndian, WriteBytesExt};

use Result;
use error::Error;
use header::Header;
use io::write_zeros;
use point::Point;
use scale::descale;
use reader::Reader;
use vlr::Vlr;

/// A las file.
#[derive(Debug, PartialEq)]
pub struct File {
    header: Header,
    vlrs: Vec<Vlr>,
    points: Vec<Point>,
}

impl File {
    /// Reads a las file from the filesystem.
    ///
    /// # Examples
    ///
    /// ```
    /// use las::file::File;
    /// let file = File::from_path("data/1.0_0.las").unwrap();
    /// ```
    pub fn from_path<P: AsRef<Path>>(path: P) -> Result<File> {
        let reader = BufReader::new(try!(fs::File::open(path)));
        File::read_from(reader)
    }

    /// Reads a las file from a `Read`.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::fs;
    /// use las::file::File;
    /// let reader = fs::File::open("data/1.0_0.las").unwrap();
    /// let file = File::read_from(reader).unwrap();
    /// ```
    pub fn read_from<R: Read + Seek>(reader: R) -> Result<File> {
        let mut file = File::new();
        let mut reader = try!(Reader::new(reader));
        file.header = reader.header();
        file.vlrs = (*reader.vlrs()).clone();
        file.points.reserve(reader.npoints() as usize);
        loop {
            match try!(reader.next_point()) {
                Some(point) => file.points.push(point),
                None => break,
            }
        }
        Ok(file)
    }

    /// Creates a new, empty las file.
    ///
    /// # Examples
    ///
    /// ```
    /// use las::file::File;
    /// let file = File::new();
    /// ```
    pub fn new() -> File {
        File {
            header: Header::new(),
            vlrs: Vec::new(),
            points: Vec::new(),
        }
    }

    /// Sets the header for this file.
    ///
    /// Since the header contains so much metadata, we might want to construct a header elsewhere
    /// then set it to the file just before write.
    ///
    /// # Examples
    ///
    /// ```
    /// use las::file::File;
    /// use las::header::Header;
    /// let mut file = File::new();
    /// let header = Header::new();
    /// file.set_header(header);
    /// ```
    pub fn set_header(&mut self, header: Header) {
        self.header = header;
    }

    /// Gets a reference to this file's header.
    ///
    /// # Examples
    ///
    /// ```
    /// use las::file::File;
    /// let file = File::from_path("data/1.0_0.las").unwrap();
    /// let header = file.header();
    /// ```
    pub fn header(&self) -> &Header {
        &self.header
    }

    /// Returns a reference to a vector of this file's points.
    ///
    /// # Examples
    ///
    /// ```
    /// use las::file::File;
    /// let file = File::from_path("data/1.0_0.las").unwrap();
    /// let points = file.points();
    /// ```
    pub fn points(&self) -> &Vec<Point> {
        &self.points
    }

    /// Adds a point to this lasfile.
    ///
    /// # Examples
    ///
    /// ```
    /// use las::file::File;
    /// use las::point::Point;
    /// let mut file = File::new();
    /// let point = Point::new();
    /// file.add_point(point);
    /// ```
    pub fn add_point(&mut self, point: Point) {
        self.points.push(point);
    }

    /// Writes out this las file to a `Path`.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::fs::remove_file;
    /// use las::file::File;
    /// let mut file = File::new();
    /// file.to_path("temp.las", true).unwrap();
    /// remove_file("temp.las");
    /// ```
    pub fn to_path<P: AsRef<Path>>(&mut self, path: P, auto_offsets: bool) -> Result<()> {
        let ref mut writer = BufWriter::new(try!(fs::File::create(path)));
        self.write_to(writer, auto_offsets)
    }

    /// Writes this las file to a `Write`.
    ///
    /// If auto_offsets is true, reasonable offset values will be calculated and written to the
    /// header before the file is written.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::io::Cursor;
    /// use las::file::File;
    /// let mut file = File::from_path("data/1.0_0.las").unwrap();
    /// let ref mut cursor = Cursor::new(Vec::new());
    /// file.write_to(cursor, true).unwrap();
    /// ```
    pub fn write_to<W: Write>(&mut self, writer: &mut W, auto_offsets: bool) -> Result<()> {
        self.header.calculate_size();
        self.header.number_of_point_records = self.points.len() as u32;
        self.header.offset_to_point_data = self.header.header_size as u32 +
                                           self.vlrs.iter().fold(0, |a, v| a + v.len());
        self.header.point_data_record_length = self.header.point_data_format.record_length();

        let mut number_of_points_by_return = [0u32; 5];
        let mut x_min = f64::MAX;
        let mut y_min = f64::MAX;
        let mut z_min = f64::MAX;
        let mut x_max = f64::MIN;
        let mut y_max = f64::MIN;
        let mut z_max = f64::MIN;
        for point in &self.points {
            let return_number = point.return_number.as_u8();
            if return_number > 0 {
                number_of_points_by_return[(return_number - 1) as usize] += 1;
            }
            if point.x < x_min {
                x_min = point.x;
            }
            if point.y < y_min {
                y_min = point.y;
            }
            if point.z < z_min {
                z_min = point.z;
            }
            if point.x > x_max {
                x_max = point.x;
            }
            if point.y > y_max {
                y_max = point.y;
            }
            if point.z > z_max {
                z_max = point.z;
            }
        }
        self.header.x_min = x_min;
        self.header.y_min = y_min;
        self.header.z_min = z_min;
        self.header.x_max = x_max;
        self.header.y_max = y_max;
        self.header.z_max = z_max;

        if auto_offsets {
            self.header.x_offset = (x_min + x_max) / 2.0;
            self.header.y_offset = (y_min + y_max) / 2.0;
            self.header.z_offset = (z_min + z_max) / 2.0;
        }

        let mut bytes_written = try!(self.header.write_to(writer)) as usize;
        if bytes_written < self.header.header_size as usize {
            bytes_written += try!(write_zeros(writer,
                                              self.header.header_size as usize - bytes_written));
        }
        for vlr in &self.vlrs {
            bytes_written += try!(vlr.write_to(writer)) as usize;
        }
        if bytes_written < self.header.offset_to_point_data as usize {
            try!(write_zeros(writer,
                             self.header.offset_to_point_data as usize - bytes_written));
        }
        for point in &self.points {
            try!(self.write_point_to(writer, point));
        }
        Ok(())
    }

    fn write_point_to<W: Write>(&self, writer: &mut W, point: &Point) -> Result<()> {
        try!(writer.write_i32::<LittleEndian>(descale(point.x,
                                                      self.header.x_scale_factor,
                                                      self.header.x_offset)));
        try!(writer.write_i32::<LittleEndian>(descale(point.y,
                                                      self.header.y_scale_factor,
                                                      self.header.y_offset)));
        try!(writer.write_i32::<LittleEndian>(descale(point.z,
                                                      self.header.z_scale_factor,
                                                      self.header.z_offset)));
        try!(writer.write_u16::<LittleEndian>(point.intensity));
        let byte = point.return_number.as_u8() + (point.number_of_returns.as_u8() << 3) +
                   (point.scan_direction.as_u8() << 6) +
                   ((point.edge_of_flight_line as u8) << 7);
        try!(writer.write_u8(byte));
        let byte = point.classification.as_u8() + ((point.synthetic as u8) << 5) +
                   ((point.key_point as u8) << 6) +
                   ((point.withheld as u8) << 7);
        try!(writer.write_u8(byte));
        try!(writer.write_i8(point.scan_angle_rank));
        try!(writer.write_u8(point.user_data));
        try!(writer.write_u16::<LittleEndian>(point.point_source_id));
        if self.header.point_data_format.has_time() {
            match point.gps_time {
                Some(gps_time) => try!(writer.write_f64::<LittleEndian>(gps_time)),
                None => {
                    return Err(Error::PointFormat(self.header.point_data_format,
                                                     "gps_time".to_string()))
                }
            }
        }
        if self.header.point_data_format.has_color() {
            match point.red {
                Some(red) => try!(writer.write_u16::<LittleEndian>(red)),
                None => {
                    return Err(Error::PointFormat(self.header.point_data_format,
                                                     "red".to_string()))
                }
            }
            match point.green {
                Some(green) => try!(writer.write_u16::<LittleEndian>(green)),
                None => {
                    return Err(Error::PointFormat(self.header.point_data_format,
                                                     "green".to_string()))
                }
            }
            match point.blue {
                Some(blue) => try!(writer.write_u16::<LittleEndian>(blue)),
                None => {
                    return Err(Error::PointFormat(self.header.point_data_format,
                                                     "blue".to_string()))
                }
            }
        }
        match point.extra_bytes {
            Some(ref bytes) => try!(writer.write_all(&bytes[..])),
            None => {}
        }
        Ok(())
    }
}

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

    use std::fs::remove_file;
    use std::io::Cursor;
    use std::path::Path;

    use point::Point;

    fn roundtrip<P: AsRef<Path>>(path: P) {
        let mut lasfile = File::from_path(path).unwrap();
        let ref mut cursor = Cursor::new(Vec::new());
        lasfile.write_to(cursor, false).unwrap();
        cursor.set_position(0);
        let lasfile2 = File::read_from(cursor).unwrap();
        assert_eq!(lasfile, lasfile2);
    }

    #[test]
    fn roundtrip_1_0_0() {
        roundtrip("data/1.0_0.las");
    }

    #[test]
    fn roundtrip_1_0_1() {
        roundtrip("data/1.0_1.las");
    }

    #[test]
    fn roundtrip_1_1_0() {
        roundtrip("data/1.1_0.las");
    }

    #[test]
    fn roundtrip_1_1_1() {
        roundtrip("data/1.1_1.las");
    }

    #[test]
    fn roundtrip_1_2_0() {
        roundtrip("data/1.2_0.las");
    }

    #[test]
    fn roundtrip_1_2_1() {
        roundtrip("data/1.2_1.las");
    }

    #[test]
    fn roundtrip_1_2_2() {
        roundtrip("data/1.2_2.las");
    }

    #[test]
    fn roundtrip_1_2_3() {
        roundtrip("data/1.2_3.las");
    }

    /// This file is good as it exercieses a weird use case, but the test fails at the moment. I'm
    /// not sure why, so I'm going to keep it around but ignore it.
    #[test]
    #[ignore]
    fn roundtrip_extrabytes() {
        roundtrip("data/extrabytes.las");
    }

    #[test]
    fn point_format_1_has_gps_time() {
        let lasfile = File::from_path("data/1.0_1.las").unwrap();
        let ref point = lasfile.points()[0];
        assert!(point.gps_time.is_some());
    }

    #[test]
    fn point_format_2_has_color() {
        let lasfile = File::from_path("data/1.2_2.las").unwrap();
        let ref point = lasfile.points()[0];
        assert!(point.red.is_some());
        assert!(point.green.is_some());
        assert!(point.blue.is_some());
    }

    #[test]
    fn write_one_point() {
        let mut point = Point::new();
        point.x = 1.0;
        point.y = 2.0;
        point.z = 3.0;
        let mut lasfile = File::new();
        lasfile.add_point(point);
        lasfile.to_path("temp.las", false).unwrap();

        let lasfile = File::from_path("temp.las").unwrap();
        let ref point = lasfile.points()[0];
        assert_eq!(1.0, point.x);
        assert_eq!(2.0, point.y);
        assert_eq!(3.0, point.z);

        remove_file("temp.las").unwrap();
    }
}
<|endoftext|>


<commit_before>const ADD_A: (u32, bool) = 5u32.overflowing_add(2);
const ADD_B: (u32, bool) = u32::max_value().overflowing_add(1);

const SUB_A: (u32, bool) = 5u32.overflowing_sub(2);
const SUB_B: (u32, bool) = 0u32.overflowing_sub(1);

const MUL_A: (u32, bool) = 5u32.overflowing_mul(2);
const MUL_B: (u32, bool) = 1_000_000_000u32.overflowing_mul(10);

const SHL_A: (u32, bool) = 0x1u32.overflowing_shl(4);
const SHL_B: (u32, bool) = 0x1u32.overflowing_shl(132);

const SHR_A: (u32, bool) = 0x10u32.overflowing_shr(4);
const SHR_B: (u32, bool) = 0x10u32.overflowing_shr(132);

fn ident<T>(ident: T) -> T {
    ident
}

fn main() {
    assert_eq!(ADD_A, ident((7, false)));
    assert_eq!(ADD_B, ident((0, true)));

    assert_eq!(SUB_A, ident((3, false)));
    assert_eq!(SUB_B, ident((u32::max_value(), true)));

    assert_eq!(MUL_A, ident((10, false)));
    assert_eq!(MUL_B, ident((1410065408, true)));

    assert_eq!(SHL_A, ident((0x10, false)));
    assert_eq!(SHL_B, ident((0x10, true)));

    assert_eq!(SHR_A, ident((0x1, false)));
    assert_eq!(SHR_B, ident((0x1, true)));
}
<commit_msg>const-int-overflowing.rs += overflowing_neg<commit_after>const ADD_A: (u32, bool) = 5u32.overflowing_add(2);
const ADD_B: (u32, bool) = u32::max_value().overflowing_add(1);

const SUB_A: (u32, bool) = 5u32.overflowing_sub(2);
const SUB_B: (u32, bool) = 0u32.overflowing_sub(1);

const MUL_A: (u32, bool) = 5u32.overflowing_mul(2);
const MUL_B: (u32, bool) = 1_000_000_000u32.overflowing_mul(10);

const SHL_A: (u32, bool) = 0x1u32.overflowing_shl(4);
const SHL_B: (u32, bool) = 0x1u32.overflowing_shl(132);

const SHR_A: (u32, bool) = 0x10u32.overflowing_shr(4);
const SHR_B: (u32, bool) = 0x10u32.overflowing_shr(132);

const NEG_A: (u32, bool) = 0.overflowing_neg();
const NEG_B: (u32, bool) = core::u32::MAX.overflowing_neg();

fn ident<T>(ident: T) -> T {
    ident
}

fn main() {
    assert_eq!(ADD_A, ident((7, false)));
    assert_eq!(ADD_B, ident((0, true)));

    assert_eq!(SUB_A, ident((3, false)));
    assert_eq!(SUB_B, ident((u32::max_value(), true)));

    assert_eq!(MUL_A, ident((10, false)));
    assert_eq!(MUL_B, ident((1410065408, true)));

    assert_eq!(SHL_A, ident((0x10, false)));
    assert_eq!(SHL_B, ident((0x10, true)));

    assert_eq!(SHR_A, ident((0x1, false)));
    assert_eq!(SHR_B, ident((0x1, true)));

    assert_eq!(NEG_A, ident((0, false)));
    assert_eq!(NEG_B, ident((1, true)));
}
<|endoftext|>


<commit_before>
use std::num::Float;
use sdl::video::{Surface};
use sdl::event::{Key};

use world::{World};
use math::{LineSeg, Vec2, Mat3};
use input::{InputState};

const SPEED: f32 = 0.4;
const TURN: f32 = 0.03;
const FOV_DIV: f32 = 500.0;
const WALL: f32 = 3000.0;

pub struct Game<'a> {
    pub pos: Vec2,
    pub face_angle: f32,
    pub world: &'a World,
    pub show_map: bool,
}

impl<'a> Game<'a> {
    pub fn step(&mut self, input: &InputState) {
        if input.has_key(Key::Left)  { self.face_angle -= TURN; }
        if input.has_key(Key::Right) { self.face_angle += TURN; }

        if input.has_key(Key::W) { self.do_move( 1.0,  0.0); }
        if input.has_key(Key::S) { self.do_move(-1.0,  0.0); }
        if input.has_key(Key::A) { self.do_move( 0.0, -1.0); }
        if input.has_key(Key::D) { self.do_move( 0.0,  1.0); }

        self.show_map = input.has_key(Key::Tab);
    }

    fn do_move(&mut self, para: f32, perp: f32) {
        let sin = SPEED*Float::sin(self.face_angle);
        let cos = SPEED*Float::cos(self.face_angle);
        self.pos.x +=  sin*para + cos*perp;
        self.pos.y += -cos*para + sin*perp;
    }

    pub fn render(&self, surf: &Surface) {
        if self.show_map {
            self.render_map(&surf);
        } else {
            self.render_game(&surf);
        }
    }

    fn render_map(&self, surf: &Surface) {
        let trans = Mat3::rotation(-self.face_angle)
                  * Mat3::translation(-self.pos);

        surf.clear();
        for wall in self.world.walls.iter() {
            draw_seg(&surf, wall.transform(trans), 0xFF, 0x00, 0x00);
        }

        let w = surf.get_width() as usize;
        for x in 0..w {
            let offset = ((x as f32) - (w as f32) / 2.0) / FOV_DIV;

            self.world.cast_ray(self.pos, self.face_angle + offset).map(|(_, pos, _)| {
                let draw_sg = LineSeg::new(self.pos.x, self.pos.y, pos.x, pos.y);
                draw_seg(&surf, draw_sg.transform(trans), 0x00, 0xFF, 0x00);
            });
        }
    }

    fn render_game(&self, surf: &Surface) {
        let w = surf.get_width() as usize;
        let h = surf.get_height() as usize;

        surf.clear();
        surf.with_lock(|pixels| {
            for x in 0..w {
                let offset = ((x as f32) - (w as f32) / 2.0) / FOV_DIV;

                let (top, bottom, along) = match self.world.cast_ray(self.pos, self.face_angle + offset) {
                    Some((dist, _, along)) => {
                        let px_height = (WALL / (dist * Float::cos(offset))) as usize;
                        let top = if h > px_height { (h - px_height) / 2 } else { 0 };
                        (top, h-top, along)
                    }
                    None => { (h/2, h/2, 0.0) }
                };

                for y in 0..top      { put_px(pixels, w, x, y,  0x00, 0x66, 0xFF); }
                for y in bottom..h   { put_px(pixels, w, x, y,  0x00, 0xBB, 0x00); }

                let brightness = ((0xFF - top) as f32) / 255.0;
                for y in top..bottom {
                    let look_x = (along * 36.0) as u8;
                    let tex_lookup = ((along * 36.0) as u8)
                                   ^ ((255.0*(((y-top)as f32)/(bottom-top)as f32)) as u8);
                    let color = ((tex_lookup as f32) * brightness) as u8;
                    put_px(pixels, w, x, y, color, 0x00, 0x00);
                }
            }
            true
        });
    }
}

fn put_px(pixels: &mut [u8], w: usize, x: usize, y: usize, r: u8, g: u8, b: u8) {
    pixels[3*(w*y+x) + 0] = b;
    pixels[3*(w*y+x) + 1] = g;
    pixels[3*(w*y+x) + 2] = r;
}

fn draw_seg(surf: &Surface, seg: LineSeg, r: u8, g: u8, b: u8) {
    let w = surf.get_width() as usize;
    let h = surf.get_height() as usize;
    let len = seg.get_length();

    surf.with_lock(|pixels| {
        for t in 0..(len as usize) {
            let pt = seg.at(t as f32 / len);
            let ux = (pt.x + (w/2) as f32) as usize;
            let uy = (pt.y + (h/2) as f32) as usize;

            if ux < w && uy < h {
                put_px(pixels, w, ux, uy, r, g, b);
            }
        };
        true
    });
}
<commit_msg>Removed dead code<commit_after>
use std::num::Float;
use sdl::video::{Surface};
use sdl::event::{Key};

use world::{World};
use math::{LineSeg, Vec2, Mat3};
use input::{InputState};

const SPEED: f32 = 0.4;
const TURN: f32 = 0.03;
const FOV_DIV: f32 = 500.0;
const WALL: f32 = 3000.0;

pub struct Game<'a> {
    pub pos: Vec2,
    pub face_angle: f32,
    pub world: &'a World,
    pub show_map: bool,
}

impl<'a> Game<'a> {
    pub fn step(&mut self, input: &InputState) {
        if input.has_key(Key::Left)  { self.face_angle -= TURN; }
        if input.has_key(Key::Right) { self.face_angle += TURN; }

        if input.has_key(Key::W) { self.do_move( 1.0,  0.0); }
        if input.has_key(Key::S) { self.do_move(-1.0,  0.0); }
        if input.has_key(Key::A) { self.do_move( 0.0, -1.0); }
        if input.has_key(Key::D) { self.do_move( 0.0,  1.0); }

        self.show_map = input.has_key(Key::Tab);
    }

    fn do_move(&mut self, para: f32, perp: f32) {
        let sin = SPEED*Float::sin(self.face_angle);
        let cos = SPEED*Float::cos(self.face_angle);
        self.pos.x +=  sin*para + cos*perp;
        self.pos.y += -cos*para + sin*perp;
    }

    pub fn render(&self, surf: &Surface) {
        if self.show_map {
            self.render_map(&surf);
        } else {
            self.render_game(&surf);
        }
    }

    fn render_map(&self, surf: &Surface) {
        let trans = Mat3::rotation(-self.face_angle)
                  * Mat3::translation(-self.pos);

        surf.clear();
        for wall in self.world.walls.iter() {
            draw_seg(&surf, wall.transform(trans), 0xFF, 0x00, 0x00);
        }

        let w = surf.get_width() as usize;
        for x in 0..w {
            let offset = ((x as f32) - (w as f32) / 2.0) / FOV_DIV;

            self.world.cast_ray(self.pos, self.face_angle + offset).map(|(_, pos, _)| {
                let draw_sg = LineSeg::new(self.pos.x, self.pos.y, pos.x, pos.y);
                draw_seg(&surf, draw_sg.transform(trans), 0x00, 0xFF, 0x00);
            });
        }
    }

    fn render_game(&self, surf: &Surface) {
        let w = surf.get_width() as usize;
        let h = surf.get_height() as usize;

        surf.clear();
        surf.with_lock(|pixels| {
            for x in 0..w {
                let offset = ((x as f32) - (w as f32) / 2.0) / FOV_DIV;

                let (top, bottom, along) = match self.world.cast_ray(self.pos, self.face_angle + offset) {
                    Some((dist, _, along)) => {
                        let px_height = (WALL / (dist * Float::cos(offset))) as usize;
                        let top = if h > px_height { (h - px_height) / 2 } else { 0 };
                        (top, h-top, along)
                    }
                    None => { (h/2, h/2, 0.0) }
                };

                for y in 0..top      { put_px(pixels, w, x, y,  0x00, 0x66, 0xFF); }
                for y in bottom..h   { put_px(pixels, w, x, y,  0x00, 0xBB, 0x00); }

                let brightness = ((0xFF - top) as f32) / 255.0;
                for y in top..bottom {
                    let tex_lookup = ((along * 36.0) as u8)
                                   ^ ((255.0*(((y-top)as f32)/(bottom-top)as f32)) as u8);
                    let color = ((tex_lookup as f32) * brightness) as u8;
                    put_px(pixels, w, x, y, color, 0x00, 0x00);
                }
            }
            true
        });
    }
}

fn put_px(pixels: &mut [u8], w: usize, x: usize, y: usize, r: u8, g: u8, b: u8) {
    pixels[3*(w*y+x) + 0] = b;
    pixels[3*(w*y+x) + 1] = g;
    pixels[3*(w*y+x) + 2] = r;
}

fn draw_seg(surf: &Surface, seg: LineSeg, r: u8, g: u8, b: u8) {
    let w = surf.get_width() as usize;
    let h = surf.get_height() as usize;
    let len = seg.get_length();

    surf.with_lock(|pixels| {
        for t in 0..(len as usize) {
            let pt = seg.at(t as f32 / len);
            let ux = (pt.x + (w/2) as f32) as usize;
            let uy = (pt.y + (h/2) as f32) as usize;

            if ux < w && uy < h {
                put_px(pixels, w, ux, uy, r, g, b);
            }
        };
        true
    });
}
<|endoftext|>


<commit_before>
use std::num::Float;
use sdl::video::{Surface};
use sdl::event::{Key};

use map::{Map};
use math::{LineSeg, Vec2, Mat3};
use input::{InputState};
use math;

pub struct Game<'a> {
    pub pos: Vec2,
    pub face_angle: f32,
    pub map: &'a Map,
    pub show_map: bool,
}

const SPEED: f32 = 0.4;
const TURN: f32 = 0.03;
const FOV_DIV: f32 = 500.0;
const WALL: f32 = 3000.0;

impl<'a> Game<'a> {

    pub fn step(&mut self, input: &InputState) {
        if input.has_key(Key::Left) {
            self.face_angle -= TURN;
        }
        if input.has_key(Key::Right) {
            self.face_angle += TURN;
        }
        if input.has_key(Key::W) {
            self.pos.x += SPEED*Float::sin(self.face_angle);
            self.pos.y -= SPEED*Float::cos(self.face_angle);
        }
        if input.has_key(Key::S) {
            self.pos.x -= SPEED*Float::sin(self.face_angle);
            self.pos.y += SPEED*Float::cos(self.face_angle);
        }
        if input.has_key(Key::A) {
            self.pos.x -= SPEED*Float::cos(self.face_angle);
            self.pos.y -= SPEED*Float::sin(self.face_angle);
        }
        if input.has_key(Key::D) {
            self.pos.x += SPEED*Float::cos(self.face_angle);
            self.pos.y += SPEED*Float::sin(self.face_angle);
        }
        self.show_map = input.has_key(Key::Tab);
    }

    pub fn render(&self, surf: &Surface) {
        if self.show_map {
            self.render_lines(&surf);
        } else {
            self.render_cast(&surf);
        }
    }

    fn render_lines(&self, surf: &Surface) {
        let trans = Mat3::rotation(-self.face_angle)
                  * Mat3::translation(-self.pos);

        surf.clear();
        for wall in self.map.walls.iter() {
            draw_seg(&surf, wall.transform(trans), 0xFF, 0x00, 0x00);
        }

        let w = surf.get_width() as usize;
        for x in 0..w {
            let offset = ((x as f32) - (w as f32) / 2.0) / FOV_DIV;
            let (q1,q2) = self.cast_ray(offset);
            match q1 {
                Some(_) => {
                    let draw_sg = LineSeg::new(self.pos.x, self.pos.y, q2.x, q2.y);
                    draw_seg(&surf, draw_sg.transform(trans), 0x00, 0xFF, 0x00);
                }
                None => {}
            }
        }
    }

    fn render_cast(&self, surf: &Surface) {
        let w = surf.get_width() as usize;
        let h = surf.get_height() as usize;

        surf.clear();
        surf.with_lock(|pixels| {
            for x in 0..w {
                let offset = ((x as f32) - (w as f32) / 2.0) / FOV_DIV;
                let (q1,_) = self.cast_ray(offset);
                match q1 {
                    Some(dist) => {
                        let ray_height = (WALL / dist) as usize;
                        let top = if h > ray_height { (h - ray_height) / 2 } else { 0 };
                        let bottom = h - top;
                        let proto_color = 0xFF-(h/2) + ray_height/2;
                        let color = if proto_color > 0xFF { 0xFF } else { proto_color as u8 };
                        for y in 0..top {
                            put_px(pixels, w, x, y, 0, 0x66, 0xFF);
                        }
                        for y in top..bottom {
                            put_px(pixels, w, x, y, color, 0, 0);
                        }
                        for y in bottom..h {
                            put_px(pixels, w, x, y, 0, 0xBB, 0);
                        }
                    }
                    None => {
                        for y in 0..(h/2) {
                            put_px(pixels, w, x, y, 0, 0x66, 0xFF);
                        }
                        for y in (h/2)..h {
                            put_px(pixels, w, x, y, 0, 0xBB, 0);
                        }
                    }
                }
            }
            true
        });
    }

    fn cast_ray(&self, offset: f32) -> (Option<f32>, Vec2) {
        let ray = LineSeg::new(
            self.pos.x,
            self.pos.y,
            self.pos.x + 1000.0*Float::sin(self.face_angle + offset),
            self.pos.y - 1000.0*Float::cos(self.face_angle + offset)
        );

        let cos_offset = Float::cos(offset);

        let (dist,pos) = self.map.walls.iter()
            .filter_map(|&wall| ray.intersects_at(wall).map(|t| wall.at(t)))
            .map(|int| ((int - self.pos).get_length() * cos_offset, int))
            .fold((Float::max_value(), math::V2_ORIGIN), |(shortest, _), (dist, pos)| {
                (if dist < shortest { dist } else { shortest }, pos)
            });

        if dist < 1.0e5 {
            (Some(dist), pos)
        } else {
            (None, math::V2_ORIGIN)
        }
    }
}

fn put_px(pixels: &mut [u8], w: usize, x: usize, y: usize, r: u8, g: u8, b: u8) {
    pixels[3*(w*y+x) + 0] = b;
    pixels[3*(w*y+x) + 1] = g;
    pixels[3*(w*y+x) + 2] = r;
}

fn draw_seg(surf: &Surface, seg: LineSeg, r: u8, g: u8, b: u8) {
    let w = surf.get_width() as usize;
    let h = surf.get_height() as usize;
    let len = seg.get_length();

    surf.with_lock(|pixels| {
        for t in 0..(len as usize) {
            let pt = seg.at(t as f32 / len);
            let ux = (pt.x + (w/2) as f32) as usize;
            let uy = (pt.y + (h/2) as f32) as usize;

            if ux < w && uy < h {
                put_px(pixels, w, ux, uy, r, g, b);
            }
        };
        true
    });
}
<commit_msg>Simplified result of cast_ray<commit_after>
use std::num::Float;
use sdl::video::{Surface};
use sdl::event::{Key};

use map::{Map};
use math::{LineSeg, Vec2, Mat3};
use input::{InputState};
use math;

pub struct Game<'a> {
    pub pos: Vec2,
    pub face_angle: f32,
    pub map: &'a Map,
    pub show_map: bool,
}

const SPEED: f32 = 0.4;
const TURN: f32 = 0.03;
const FOV_DIV: f32 = 500.0;
const WALL: f32 = 3000.0;

impl<'a> Game<'a> {

    pub fn step(&mut self, input: &InputState) {
        if input.has_key(Key::Left) {
            self.face_angle -= TURN;
        }
        if input.has_key(Key::Right) {
            self.face_angle += TURN;
        }
        if input.has_key(Key::W) {
            self.pos.x += SPEED*Float::sin(self.face_angle);
            self.pos.y -= SPEED*Float::cos(self.face_angle);
        }
        if input.has_key(Key::S) {
            self.pos.x -= SPEED*Float::sin(self.face_angle);
            self.pos.y += SPEED*Float::cos(self.face_angle);
        }
        if input.has_key(Key::A) {
            self.pos.x -= SPEED*Float::cos(self.face_angle);
            self.pos.y -= SPEED*Float::sin(self.face_angle);
        }
        if input.has_key(Key::D) {
            self.pos.x += SPEED*Float::cos(self.face_angle);
            self.pos.y += SPEED*Float::sin(self.face_angle);
        }
        self.show_map = input.has_key(Key::Tab);
    }

    pub fn render(&self, surf: &Surface) {
        if self.show_map {
            self.render_lines(&surf);
        } else {
            self.render_cast(&surf);
        }
    }

    fn render_lines(&self, surf: &Surface) {
        let trans = Mat3::rotation(-self.face_angle)
                  * Mat3::translation(-self.pos);

        surf.clear();
        for wall in self.map.walls.iter() {
            draw_seg(&surf, wall.transform(trans), 0xFF, 0x00, 0x00);
        }

        let w = surf.get_width() as usize;
        for x in 0..w {
            let offset = ((x as f32) - (w as f32) / 2.0) / FOV_DIV;
            match self.cast_ray(offset) {
                Some((_, pos)) => {
                    let draw_sg = LineSeg::new(self.pos.x, self.pos.y, pos.x, pos.y);
                    draw_seg(&surf, draw_sg.transform(trans), 0x00, 0xFF, 0x00);
                }
                None => {}
            }
        }
    }

    fn render_cast(&self, surf: &Surface) {
        let w = surf.get_width() as usize;
        let h = surf.get_height() as usize;

        surf.clear();
        surf.with_lock(|pixels| {
            for x in 0..w {
                let offset = ((x as f32) - (w as f32) / 2.0) / FOV_DIV;
                match self.cast_ray(offset) {
                    Some((dist, _)) => {
                        let ray_height = (WALL / dist) as usize;
                        let top = if h > ray_height { (h - ray_height) / 2 } else { 0 };
                        let bottom = h - top;
                        let proto_color = 0xFF-(h/2) + ray_height/2;
                        let color = if proto_color > 0xFF { 0xFF } else { proto_color as u8 };
                        for y in 0..top {
                            put_px(pixels, w, x, y, 0, 0x66, 0xFF);
                        }
                        for y in top..bottom {
                            put_px(pixels, w, x, y, color, 0, 0);
                        }
                        for y in bottom..h {
                            put_px(pixels, w, x, y, 0, 0xBB, 0);
                        }
                    }
                    None => {
                        for y in 0..(h/2) {
                            put_px(pixels, w, x, y, 0, 0x66, 0xFF);
                        }
                        for y in (h/2)..h {
                            put_px(pixels, w, x, y, 0, 0xBB, 0);
                        }
                    }
                }
            }
            true
        });
    }

    fn cast_ray(&self, offset: f32) -> Option<(f32, Vec2)> {
        let ray = LineSeg::new(
            self.pos.x,
            self.pos.y,
            self.pos.x + 1000.0*Float::sin(self.face_angle + offset),
            self.pos.y - 1000.0*Float::cos(self.face_angle + offset)
        );

        let cos_offset = Float::cos(offset);

        let (dist, pos) = self.map.walls.iter()
            .filter_map(|&wall| ray.intersects_at(wall).map(|t| wall.at(t)))
            .map(|int| ((int - self.pos).get_length() * cos_offset, int))
            .fold((Float::max_value(), math::V2_ORIGIN), |(shortest, _), (dist, pos)| {
                (if dist < shortest { dist } else { shortest }, pos)
            });

        if dist < 1.0e5 { Some((dist, pos)) } else { None }
    }
}

fn put_px(pixels: &mut [u8], w: usize, x: usize, y: usize, r: u8, g: u8, b: u8) {
    pixels[3*(w*y+x) + 0] = b;
    pixels[3*(w*y+x) + 1] = g;
    pixels[3*(w*y+x) + 2] = r;
}

fn draw_seg(surf: &Surface, seg: LineSeg, r: u8, g: u8, b: u8) {
    let w = surf.get_width() as usize;
    let h = surf.get_height() as usize;
    let len = seg.get_length();

    surf.with_lock(|pixels| {
        for t in 0..(len as usize) {
            let pt = seg.at(t as f32 / len);
            let ux = (pt.x + (w/2) as f32) as usize;
            let uy = (pt.y + (h/2) as f32) as usize;

            if ux < w && uy < h {
                put_px(pixels, w, ux, uy, r, g, b);
            }
        };
        true
    });
}
<|endoftext|>


<commit_before>
use std::cmp;

use colored::*;
use card::*;

pub struct Game{
    deck:           Deck,
    tableau:        Vec<Stack>,
    target:         Vec<Stack>,
    open_tableau:   Vec<Stack>,
    stack:          Stack,
    round:          u16
}

impl Game{

    pub fn new() -> Game{
        let mut deck = Deck::new();
        deck.shuffle();

        let tableau = (0..7).map(|_| Stack::new()).collect();
        let target = (0..4).map(|_| Stack::new()).collect();
        let open_tableau = (0..7).map(|_| Stack::new()).collect();

        Game{
            deck:           deck,
            tableau:        tableau,
            open_tableau:   open_tableau,
            target:         target,
            stack:          Stack::new(),
            round:          0
        }
    }

    pub fn start(&mut self){
        println!("\n\t\t--------------------");
        println!("{}", "\t\tWelcome to Solitaire!".bold());
        println!("\t\t--------------------\n");

        // deal cards
        let tableau_len = self.tableau.len();
        let mut stacks_to_deal = tableau_len;

        while stacks_to_deal > 0 {
            for n in 1..(stacks_to_deal + 1){
                let card = self.deck.take(1);
                self.tableau[tableau_len - n].add_to_top(card);
            }
            stacks_to_deal = stacks_to_deal - 1;
        }

        for n in 0..(tableau_len){
            let card = self.tableau[n].take(1);
            self.open_tableau[n].add_to_top(card);
        }

        self.display_board();
    }

    pub fn display_board(&self){
        // deck
        let deck = "?".blue();

        // stack
        let stack = if self.stack.count() > 0 {
            if let Some(card) = self.stack.show(self.stack.count() - 1){
                card.to_string()
            }else{
                "  ".to_string()
            }
        } else {
            "".to_string()
        };

        // targets
        let target = &self.target.iter().map(|stack|
            if stack.count() > 0 {
                if let Some(card) = stack.show(stack.count() - 1){
                    card.to_string()
                }else{
                    " ".to_string()
                }
            } else {
                " ".to_string()
            }
        ).collect::<Vec<String>>();

        println!("\n\n{}: {}", "Moves".bold(), self.round);

        println!("\n\n<h>\t<s>\t\t\t<a>\t<b>\t<c>\t<d>");

        println!("[{}]\t{}\t\t\t[{}]\t[{}]\t[{}]\t[{}]", deck, stack, target[0], target[1], target[2], target[3]);

        let stacks = &self.tableau.iter().map(|stack|
            stack.count().to_string().blue()
        ).collect::<Vec<ColoredString>>();

        // stacks
      println!("\n\t<1>\t<2>\t<3>\t<4>\t<5>\t<6>\t<7>");
        println!("\t[{}]\t[{}]\t[{}]\t[{}]\t[{}]\t[{}]\t[{}]", stacks[0], stacks[1], stacks[2], stacks[3],
            stacks[4], stacks[5], stacks[6]);

        let mut longest_stack = 0;
        let mut stack_iter = 0;

        for stack in &self.open_tableau{
            longest_stack = cmp::max(stack.count(), longest_stack);
        }

        while stack_iter < longest_stack {
            let stacks = &self.open_tableau.iter().map(|stack|

                if let Some(card) = stack.show(stack_iter){
                    card.to_string()
                }else{
                    "  ".to_string()
                }

            ).collect::<Vec<String>>();
            // cards
            println!("\t{}\t{}\t{}\t{}\t{}\t{}\t{}\t\t:{}", stacks[0], stacks[1], stacks[2],
                stacks[3], stacks[4], stacks[5], stacks[6], stack_iter);

            stack_iter = stack_iter + 1;
        }

        println!("\n");
    }

    pub fn deal(&mut self){
        self.round = self.round + 1;
        let take = cmp::min(3, self.deck.count());

        if take > 0 {
            let card = self.deck.take(take);
            self.stack.add_to_top(card);
        } else {
            let count = self.stack.count();
            let cards = self.stack.take(count);
            self.deck.add_to_top(cards);
        }
    }

    pub fn move_card(&mut self, source:&str, target:&str){

        // todo - move king to empty space
        // todo - move stacks of cards
        // todo - move suits up to targets

        println!("Checking move {} to {}", source, target);

        let empty_target_stack = Stack::new();
        let empty_source_stack = Stack::new();

        let mut target_stack: Stack = match self.get_target_stack(target){
            Some(target_stack)  => target_stack,
            None                => empty_target_stack
        };

        let mut source_stack: Stack = match self.get_source_stack(source){
            Some(source_stack)  => source_stack,
            None                => empty_source_stack
        };

        // todo - clean this horrible shit up
        if let Some(target_card) = target_stack.show(target_stack.count() - 1) {

            println!("{}", target_card);
            if let Some(previous_rank) = target_card.previous_rank() {
                println!("{} expects a {:?} {:?}", target_card, target_card.alternate_colour(), previous_rank);

                if let Some(source_card) = source_stack.show(source_stack.count() - 1){
                    if source_card.colour == target_card.alternate_colour() && source_card.rank == previous_rank {
                        let card = source_stack.take(1);
                        println!("moving {}", card[0]);
                        println!("{:?}", target_stack);
                        target_stack.add_to_top(card);
                        println!("{:?}", target_stack);

                        match &*target {
                            "a" => {self.target[0] = target_stack;},
                            "b" => {self.target[1] = target_stack;},
                            "c" => {self.target[2] = target_stack;},
                            "d" => {self.target[3] = target_stack;},
                            "1" => {self.open_tableau[0] = target_stack;},
                            "2" => {self.open_tableau[1] = target_stack;},
                            "3" => {self.open_tableau[2] = target_stack;},
                            "4" => {self.open_tableau[3] = target_stack;},
                            "5" => {self.open_tableau[4] = target_stack;},
                            "6" => {self.open_tableau[5] = target_stack;},
                            "7" => {self.open_tableau[6] = target_stack;},
                            _   => {}
                        }

                        match &*source {
                            "s" => {self.stack = source_stack;},
                            "1" => {self.open_tableau[0] = source_stack;},
                            "2" => {self.open_tableau[1] = source_stack;},
                            "3" => {self.open_tableau[2] = source_stack;},
                            "4" => {self.open_tableau[3] = source_stack;},
                            "5" => {self.open_tableau[4] = source_stack;},
                            "6" => {self.open_tableau[5] = source_stack;},
                            "7" => {self.open_tableau[6] = source_stack;},
                            _   => {}
                        }

                        println!("{:?}", self.open_tableau);
                    }
                }
            }
        }

    }

    pub fn deal_stack(&mut self, source:&str){
        println!("Checking stack {}", source);
    }

    fn get_target_stack(&self, target:&str)->Option<Stack>{
        let open_tableau = &self.open_tableau;
        let target_ref = &self.target;
        match &*target {
            "a" => Some(target_ref[0].clone()),
            "b" => Some(target_ref[1].clone()),
            "c" => Some(target_ref[2].clone()),
            "d" => Some(target_ref[3].clone()),
            "1" => Some(open_tableau[0].clone()),
            "2" => Some(open_tableau[1].clone()),
            "3" => Some(open_tableau[2].clone()),
            "4" => Some(open_tableau[3].clone()),
            "5" => Some(open_tableau[4].clone()),
            "6" => Some(open_tableau[5].clone()),
            "7" => Some(open_tableau[6].clone()),
            _   => None
        }

    }

    fn get_source_stack(&self, source:&str)->Option<Stack>{

        let open_tableau = &self.open_tableau;

        match &*source {
            "s" => Some(self.stack.clone()),
            "1" => Some(open_tableau[0].clone()),
            "2" => Some(open_tableau[1].clone()),
            "3" => Some(open_tableau[2].clone()),
            "4" => Some(open_tableau[3].clone()),
            "5" => Some(open_tableau[4].clone()),
            "6" => Some(open_tableau[5].clone()),
            "7" => Some(open_tableau[6].clone()),
            _   => None
        }
    }
}<commit_msg>removed a load of debug stuff<commit_after>
use std::cmp;

use colored::*;
use card::*;

pub struct Game{
    deck:           Deck,
    tableau:        Vec<Stack>,
    target:         Vec<Stack>,
    open_tableau:   Vec<Stack>,
    stack:          Stack,
    round:          u16
}

impl Game{

    pub fn new() -> Game{
        let mut deck = Deck::new();
        deck.shuffle();

        let tableau = (0..7).map(|_| Stack::new()).collect();
        let target = (0..4).map(|_| Stack::new()).collect();
        let open_tableau = (0..7).map(|_| Stack::new()).collect();

        Game{
            deck:           deck,
            tableau:        tableau,
            open_tableau:   open_tableau,
            target:         target,
            stack:          Stack::new(),
            round:          0
        }
    }

    pub fn start(&mut self){
        println!("\n\t\t--------------------");
        println!("{}", "\t\tWelcome to Solitaire!".bold());
        println!("\t\t--------------------\n");

        // deal cards
        let tableau_len = self.tableau.len();
        let mut stacks_to_deal = tableau_len;

        while stacks_to_deal > 0 {
            for n in 1..(stacks_to_deal + 1){
                let card = self.deck.take(1);
                self.tableau[tableau_len - n].add_to_top(card);
            }
            stacks_to_deal = stacks_to_deal - 1;
        }

        for n in 0..(tableau_len){
            let card = self.tableau[n].take(1);
            self.open_tableau[n].add_to_top(card);
        }

        self.display_board();
    }

    pub fn display_board(&self){
        // deck
        let deck = "?".blue();

        // stack
        let stack = if self.stack.count() > 0 {
            if let Some(card) = self.stack.show(self.stack.count() - 1){
                card.to_string()
            }else{
                "  ".to_string()
            }
        } else {
            "".to_string()
        };

        // targets
        let target = &self.target.iter().map(|stack|
            if stack.count() > 0 {
                if let Some(card) = stack.show(stack.count() - 1){
                    card.to_string()
                }else{
                    " ".to_string()
                }
            } else {
                " ".to_string()
            }
        ).collect::<Vec<String>>();

        println!("\n\n{}: {}", "Moves".bold(), self.round);

        println!("\n\n<h>\t<s>\t\t\t<a>\t<b>\t<c>\t<d>");

        println!("[{}]\t{}\t\t\t[{}]\t[{}]\t[{}]\t[{}]", deck, stack, target[0], target[1], target[2], target[3]);

        let stacks = &self.tableau.iter().map(|stack|
            stack.count().to_string().blue()
        ).collect::<Vec<ColoredString>>();

        // stacks
      println!("\n\t<1>\t<2>\t<3>\t<4>\t<5>\t<6>\t<7>");
        println!("\t[{}]\t[{}]\t[{}]\t[{}]\t[{}]\t[{}]\t[{}]", stacks[0], stacks[1], stacks[2], stacks[3],
            stacks[4], stacks[5], stacks[6]);

        let mut longest_stack = 0;
        let mut stack_iter = 0;

        for stack in &self.open_tableau{
            longest_stack = cmp::max(stack.count(), longest_stack);
        }

        while stack_iter < longest_stack {
            let stacks = &self.open_tableau.iter().map(|stack|

                if let Some(card) = stack.show(stack_iter){
                    card.to_string()
                }else{
                    "  ".to_string()
                }

            ).collect::<Vec<String>>();
            // cards
            println!("\t{}\t{}\t{}\t{}\t{}\t{}\t{}\t\t:{}", stacks[0], stacks[1], stacks[2],
                stacks[3], stacks[4], stacks[5], stacks[6], stack_iter);

            stack_iter = stack_iter + 1;
        }

        println!("\n");
    }

    pub fn deal(&mut self){
        self.round = self.round + 1;
        let take = cmp::min(3, self.deck.count());

        if take > 0 {
            let card = self.deck.take(take);
            self.stack.add_to_top(card);
        } else {
            let count = self.stack.count();
            let cards = self.stack.take(count);
            self.deck.add_to_top(cards);
        }
    }

    pub fn move_card(&mut self, source:&str, target:&str){

        // todo - move king to empty space
        // todo - move stacks of cards
        // todo - move suits up to targets

        println!("Checking move {} to {}", source, target);

        let empty_target_stack = Stack::new();
        let empty_source_stack = Stack::new();

        let mut target_stack: Stack = match self.get_target_stack(target){
            Some(target_stack)  => target_stack,
            None                => empty_target_stack
        };

        let mut source_stack: Stack = match self.get_source_stack(source){
            Some(source_stack)  => source_stack,
            None                => empty_source_stack
        };

        // todo - clean this horrible shit up
        if let Some(target_card) = target_stack.show(target_stack.count() - 1) {

            if let Some(previous_rank) = target_card.previous_rank() {

                if let Some(source_card) = source_stack.show(source_stack.count() - 1){
                    if source_card.colour == target_card.alternate_colour() && source_card.rank == previous_rank {
                        let card = source_stack.take(1);
                        target_stack.add_to_top(card);

                        match &*target {
                            "a" => {self.target[0] = target_stack;},
                            "b" => {self.target[1] = target_stack;},
                            "c" => {self.target[2] = target_stack;},
                            "d" => {self.target[3] = target_stack;},
                            "1" => {self.open_tableau[0] = target_stack;},
                            "2" => {self.open_tableau[1] = target_stack;},
                            "3" => {self.open_tableau[2] = target_stack;},
                            "4" => {self.open_tableau[3] = target_stack;},
                            "5" => {self.open_tableau[4] = target_stack;},
                            "6" => {self.open_tableau[5] = target_stack;},
                            "7" => {self.open_tableau[6] = target_stack;},
                            _   => {}
                        }

                        match &*source {
                            "s" => {self.stack = source_stack;},
                            "1" => {self.open_tableau[0] = source_stack;},
                            "2" => {self.open_tableau[1] = source_stack;},
                            "3" => {self.open_tableau[2] = source_stack;},
                            "4" => {self.open_tableau[3] = source_stack;},
                            "5" => {self.open_tableau[4] = source_stack;},
                            "6" => {self.open_tableau[5] = source_stack;},
                            "7" => {self.open_tableau[6] = source_stack;},
                            _   => {}
                        }
                    }
                }
            }
        }

    }

    pub fn deal_stack(&mut self, source:&str){
        println!("Checking stack {}", source);
    }

    fn get_target_stack(&self, target:&str)->Option<Stack>{
        let open_tableau = &self.open_tableau;
        let target_ref = &self.target;
        match &*target {
            "a" => Some(target_ref[0].clone()),
            "b" => Some(target_ref[1].clone()),
            "c" => Some(target_ref[2].clone()),
            "d" => Some(target_ref[3].clone()),
            "1" => Some(open_tableau[0].clone()),
            "2" => Some(open_tableau[1].clone()),
            "3" => Some(open_tableau[2].clone()),
            "4" => Some(open_tableau[3].clone()),
            "5" => Some(open_tableau[4].clone()),
            "6" => Some(open_tableau[5].clone()),
            "7" => Some(open_tableau[6].clone()),
            _   => None
        }

    }

    fn get_source_stack(&self, source:&str)->Option<Stack>{

        let open_tableau = &self.open_tableau;

        match &*source {
            "s" => Some(self.stack.clone()),
            "1" => Some(open_tableau[0].clone()),
            "2" => Some(open_tableau[1].clone()),
            "3" => Some(open_tableau[2].clone()),
            "4" => Some(open_tableau[3].clone()),
            "5" => Some(open_tableau[4].clone()),
            "6" => Some(open_tableau[5].clone()),
            "7" => Some(open_tableau[6].clone()),
            _   => None
        }
    }
}<|endoftext|>


<commit_before>//! Lua functionality

use hlua;
use hlua::Lua;
use std::thread;
use std::time::Duration;
use std::fs::{File};
use std::path::Path;
use std::io::Write;
use std::sync::Mutex;
use std::sync::mpsc::{channel, Sender, Receiver};

lazy_static! {
    static ref SENDER: Mutex<Sender<LuaQuery>> = {
        let (tx, rx) = channel::<LuaQuery>();
        Mutex::new(tx)
    };
    static ref RECEIVER: Mutex<Receiver<LuaResponse>> = {
        let (tx, rx) = channel::<LuaResponse>();
        Mutex::new(rx)
    };
}

/// Messages sent to the lua thread
pub enum LuaQuery {
    /// Execute a string
    Execute(String),
    /// Execute a file
    ExecuteFile(String),
    /// Get a variable
    GetVariable(String),
    /// Set a value
    SetValue {
        name: Box<::std::borrow::Borrow<str> + Sized>,
        val: Box<hlua::Push<&'static mut Lua<'static>> + Sized>
    },
    /// Create a new array
    EmptyArray(String),
}

/// Messages received from lua thread
pub enum LuaResponse {
    Error(hlua::LuaError),
    Function(hlua::functions_read::LuaFunction<String>)
}

unsafe impl Send for LuaQuery { }
unsafe impl Send for LuaResponse { }
unsafe impl Sync for LuaQuery { }
unsafe impl Sync for LuaResponse { }

pub fn init() {
    print!("[lua] Initializing...");
    let (query_tx, query_rx) = channel::<LuaQuery>();
    let (answer_tx, answer_rx) = channel::<LuaResponse>();
    {
        let mut sender = SENDER.lock().unwrap();
        let mut receiver = RECEIVER.lock().unwrap();

        *sender = query_tx;
        *receiver = answer_rx;
    }

    thread::spawn(move || {
        println!("[lua] Inside thread!");
        let receiver = query_rx;
        let sender = answer_tx;
        let mut lua = Lua::new();
        print!("[lua] Loading libs...");
        lua.openlibs();
        println!(" done!");
        println!("[lua] Creating init file");
        let mut file = File::create("/tmp/init.lua").unwrap();
        file.write(b"print('Hello world!')").unwrap();
        println!("[lua] Created hello world file!");
        println!("[lua] Executing init.lua...");
        lua.execute_from_reader::<(), File>(File::open("/tmp/init.lua")
                                            .unwrap()).unwrap();
        println!("[lua] Done!");
        thread::sleep(Duration::from_secs(10));
        println!("[lua] Exiting thread...");
    });
    println!(" created thread.")
}
<commit_msg>Add variants to LuaQuery struct<commit_after>//! Lua functionality

use hlua;
use hlua::{Lua, LuaError};
use std::thread;
use std::time::Duration;
use std::fs::{File};
use std::path::Path;
use std::io::Write;
use std::sync::{Mutex, RwLock};
use std::sync::mpsc::{channel, Sender, Receiver};

lazy_static! {
    /// Sends requests to the lua thread
    static ref SENDER: Mutex<Sender<LuaQuery>> = {
        let (tx, rx) = channel::<LuaQuery>();
        Mutex::new(tx)
    };

    /// Receives data back from the lua thread
    /// This should only be accessed by the lua thread itself.
    static ref RECEIVER: Mutex<Receiver<LuaResponse>> = {
        let (tx, rx) = channel::<LuaResponse>();
        Mutex::new(rx)
    };

    /// Whether the lua thread is currently running
    pub static ref RUNNING: RwLock<bool> = RwLock::new(false);
}

/// Messages sent to the lua thread
pub enum LuaQuery {
    /// Halt the lua thread
    Terminate,
    // Restart the lua thread
    Restart,
    /// Execute a string
    Execute(String),
    /// Execute a file
    ExecuteFile(String),
    /// Get a variable
    GetVariable(String),
    /// Set a value
    SetValue {
        name: Box<::std::borrow::Borrow<str> + Sized>,
        val: Box<hlua::Push<&'static mut Lua<'static>> + Sized>
    },
    /// Create a new array
    EmptyArray(String),
}

/// Messages received from lua thread
pub enum LuaResponse {
    /// Lua error
    Error(hlua::LuaError),
    /// A function is returned
    Function(hlua::functions_read::LuaFunction<String>)
}

unsafe impl Send for LuaQuery { }
unsafe impl Send for LuaResponse { }
unsafe impl Sync for LuaQuery { }
unsafe impl Sync for LuaResponse { }

pub fn init() {
    print!("[lua] Initializing...");
    let (query_tx, query_rx) = channel::<LuaQuery>();
    let (answer_tx, answer_rx) = channel::<LuaResponse>();
    {
        let mut sender = SENDER.lock().unwrap();
        let mut receiver = RECEIVER.lock().unwrap();

        *sender = query_tx;
        *receiver = answer_rx;
    }

    thread::spawn(move || {
        println!("[lua] Inside thread!");
        let receiver = query_rx;
        let sender = answer_tx;
        let mut lua = Lua::new();
        print!("[lua] Loading libs...");
        lua.openlibs();
        println!(" done!");
        println!("[lua] Creating init file");
        let mut file = File::create("/tmp/init.lua").unwrap();
        file.write(b"print('Hello world!')").unwrap();
        println!("[lua] Created hello world file!");
        println!("[lua] Executing init.lua...");
        lua.execute_from_reader::<(), File>(File::open("/tmp/init.lua")
                                            .unwrap()).unwrap();
        println!("[lua] Done!");
        thread::sleep(Duration::from_secs(10));
        println!("[lua] Exiting thread...");
    });
    println!(" created thread.")
}
<|endoftext|>