<commit_before>//! Game loop.

// Local crate.
use GameEvent;
use GameWindow = game_window::GameWindow;
use GameIteratorSettings;
use GameIterator;
use KeyPress;
use KeyPressArgs;
use KeyRelease;
use KeyReleaseArgs;
use MouseMove;
use MouseMoveArgs;
use MouseRelativeMove;
use MouseRelativeMoveArgs;
use MousePress;
use MousePressArgs;
use MouseRelease;
use MouseReleaseArgs;
use MouseScroll;
use MouseScrollArgs;
use Render;
use RenderArgs;
use Update;
use UpdateArgs;

/// Implemented by game applications.
pub trait Game<W: GameWindow> {
    /// Render graphics.
    fn render(&mut self, _window: &mut W, _args: &RenderArgs) {}

    /// Update the physical state of the game.
    fn update(&mut self, _window: &mut W, _args: &UpdateArgs) {}

    /// Perform tasks for loading before showing anything.
    fn load(&mut self, _window: &mut W) {}

    /// User pressed a key.
    ///
    /// This can be overridden to handle key pressed events.
    fn key_press(&mut self, _window: &mut W, _args: &KeyPressArgs) {}

    /// User released a key.
    ///
    /// This can be overridden to handle key released events.
    fn key_release(&mut self, _window: &mut W, _args: &KeyReleaseArgs) {}

    /// Pressed a mouse button.
    fn mouse_press(&mut self, _window: &mut W, _args: &MousePressArgs) {}

    /// Released a mouse button.
    fn mouse_release(&mut self, _window: &mut W, _args: &MouseReleaseArgs) {}

    /// Moved mouse cursor.
    fn mouse_move(&mut self, _window: &mut W, _args: &MouseMoveArgs) {}

    /// Moved mouse relative, not bounded by cursor.
    fn mouse_relative_move(&mut self, _window: &mut W, _args: &MouseRelativeMoveArgs) {}

    /// Scrolled mouse.
    fn mouse_scroll(&mut self, _window: &mut W, _args: &MouseScrollArgs) {}

    /// Whether the cursor should be captured.
    ///
    /// When the cursor is captured, it is hidden from view
    /// and the cursor position does not change.
    /// Only relative mouse movements are registered.
    #[inline(always)]
    fn should_capture_cursor(&mut self) -> bool { false }

    /// Handles a game event.
    fn event(&mut self, game_window: &mut W, event: &mut GameEvent) {
        match *event {
            Render(ref mut args) => self.render(game_window, args),
            Update(ref mut args) => self.update(game_window, args),
            KeyPress(ref args) => self.key_press(game_window, args),
            KeyRelease(ref args) => self.key_release(game_window, args),
            MousePress(ref args) => self.mouse_press(game_window, args),
            MouseRelease(ref args) => self.mouse_release(game_window, args),
            MouseMove(ref args) => self.mouse_move(game_window, args),
            MouseRelativeMove(ref args) => self.mouse_relative_move(game_window, args),
            MouseScroll(ref args) => self.mouse_scroll(game_window, args),
        }
    }

    /// Executes a game loop.
    fn run(
        &mut self,
        game_window: &mut W,
        game_iter_settings: &GameIteratorSettings
    ) {
        self.load(game_window);

        let mut game_iter = GameIterator::new(
            game_window,
            game_iter_settings
        );

        let mut should_capture_cursor = self.should_capture_cursor();
        game_iter.game_window.capture_cursor(should_capture_cursor);
        loop {
            match game_iter.next() {
                None => break,
                Some(mut e) => self.event(game_iter.game_window, &mut e)
            }
            if  self.should_capture_cursor() != should_capture_cursor {
                should_capture_cursor = !should_capture_cursor;
                game_iter.game_window.capture_cursor(should_capture_cursor);
            }
        }
        game_iter.game_window.capture_cursor(false);
    }
}

<commit_msg>Remove `should_capture`<commit_after>//! Game loop.

// Local crate.
use GameEvent;
use GameWindow = game_window::GameWindow;
use GameIteratorSettings;
use GameIterator;
use KeyPress;
use KeyPressArgs;
use KeyRelease;
use KeyReleaseArgs;
use MouseMove;
use MouseMoveArgs;
use MouseRelativeMove;
use MouseRelativeMoveArgs;
use MousePress;
use MousePressArgs;
use MouseRelease;
use MouseReleaseArgs;
use MouseScroll;
use MouseScrollArgs;
use Render;
use RenderArgs;
use Update;
use UpdateArgs;

/// Implemented by game applications.
pub trait Game<W: GameWindow> {
    /// Render graphics.
    fn render(&mut self, _window: &mut W, _args: &RenderArgs) {}

    /// Update the physical state of the game.
    fn update(&mut self, _window: &mut W, _args: &UpdateArgs) {}

    /// Perform tasks for loading before showing anything.
    fn load(&mut self, _window: &mut W) {}

    /// User pressed a key.
    ///
    /// This can be overridden to handle key pressed events.
    fn key_press(&mut self, _window: &mut W, _args: &KeyPressArgs) {}

    /// User released a key.
    ///
    /// This can be overridden to handle key released events.
    fn key_release(&mut self, _window: &mut W, _args: &KeyReleaseArgs) {}

    /// Pressed a mouse button.
    fn mouse_press(&mut self, _window: &mut W, _args: &MousePressArgs) {}

    /// Released a mouse button.
    fn mouse_release(&mut self, _window: &mut W, _args: &MouseReleaseArgs) {}

    /// Moved mouse cursor.
    fn mouse_move(&mut self, _window: &mut W, _args: &MouseMoveArgs) {}

    /// Moved mouse relative, not bounded by cursor.
    fn mouse_relative_move(&mut self, _window: &mut W, _args: &MouseRelativeMoveArgs) {}

    /// Scrolled mouse.
    fn mouse_scroll(&mut self, _window: &mut W, _args: &MouseScrollArgs) {}

    /// Handles a game event.
    fn event(&mut self, game_window: &mut W, event: &mut GameEvent) {
        match *event {
            Render(ref mut args) => self.render(game_window, args),
            Update(ref mut args) => self.update(game_window, args),
            KeyPress(ref args) => self.key_press(game_window, args),
            KeyRelease(ref args) => self.key_release(game_window, args),
            MousePress(ref args) => self.mouse_press(game_window, args),
            MouseRelease(ref args) => self.mouse_release(game_window, args),
            MouseMove(ref args) => self.mouse_move(game_window, args),
            MouseRelativeMove(ref args) => self.mouse_relative_move(game_window, args),
            MouseScroll(ref args) => self.mouse_scroll(game_window, args),
        }
    }

    /// Executes a game loop.
    fn run(
        &mut self,
        game_window: &mut W,
        game_iter_settings: &GameIteratorSettings
    ) {
        self.load(game_window);

        let mut game_iter = GameIterator::new(
            game_window,
            game_iter_settings
        );

        loop {
            match game_iter.next() {
                None => break,
                Some(mut e) => self.event(game_iter.game_window, &mut e)
            }
        }
    }
}

<|endoftext|>


<commit_before>//! The Ref object is a helper over the link functionality, so one is able to create references to
//! files outside of the imag store.

use std::path::PathBuf;
use std::ops::Deref;
use std::ops::DerefMut;
use std::collections::BTreeMap;
use std::fs::File;
use std::io::Read;

use libimagstore::store::FileLockEntry;
use libimagstore::storeid::StoreId;
use libimagstore::storeid::IntoStoreId;
use libimagstore::store::Store;
use libimagerror::into::IntoError;

use toml::Value;
use crypto::sha1::Sha1;
use crypto::digest::Digest;

use error::RefErrorKind as REK;
use flags::RefFlags;
use result::Result;
use module_path::ModuleEntryPath;

pub struct Ref<'a>(FileLockEntry<'a>);

impl<'a> Ref<'a> {

    /// Try to get `si` as Ref object from the store
    pub fn get(store: &'a Store, si: StoreId) -> Result<Ref<'a>> {
        match store.get(si) {
            Err(e) => return Err(REK::StoreReadError.into_error_with_cause(Box::new(e))),
            Ok(None) => return Err(REK::RefNotInStore.into_error()),
            Ok(Some(fle)) => Ref::read_reference(&fle).map(|_| Ref(fle)),
        }
    }

    fn read_reference(fle: &FileLockEntry<'a>) -> Result<PathBuf> {
        match fle.get_header().read("ref.reference") {
            Ok(Some(Value::String(s))) => Ok(PathBuf::from(s)),
            Ok(Some(_)) => Err(REK::HeaderTypeError.into_error()),
            Ok(None)    => Err(REK::HeaderFieldMissingError.into_error()),
            Err(e)      => Err(REK::StoreReadError.into_error_with_cause(Box::new(e))),
        }
    }

    /// Create a Ref object which refers to `pb`
    pub fn create(store: &'a Store, pb: PathBuf, flags: RefFlags) -> Result<Ref<'a>> {
        if !pb.exists() {
            return Err(REK::RefTargetDoesNotExist.into_error());
        }
        if flags.get_content_hashing() && pb.is_dir() {
            return Err(REK::RefTargetCannotBeHashed.into_error());
        }

        let (mut fle, content_hash, permissions, canonical_path) = { // scope to be able to fold
            try!(File::open(pb.clone())
                .map_err(Box::new)
                .map_err(|e| REK::RefTargetFileCannotBeOpened.into_error_with_cause(e))

                // If we were able to open this file,
                // we hash the contents of the file and return (file, hash)
                .and_then(|mut file| {
                    let opt_contenthash = if flags.get_content_hashing() {
                        Some(hash_file_contents(&mut file))
                    } else {
                        None
                    };

                    Ok((file, opt_contenthash))
                })

                // and then we get the permissions if we have to
                // and return (file, content hash, permissions)
                .and_then(|(file, opt_contenthash)| {
                    let opt_permissions = if flags.get_permission_tracking() {
                        Some(try!(file
                                  .metadata()
                                  .map(|md| md.permissions())
                                  .map_err(Box::new)
                                  .map_err(|e| REK::RefTargetCannotReadPermissions.into_error_with_cause(e))
                        ))
                    } else {
                        None
                    };

                    Ok((file, opt_contenthash, opt_permissions))
                })

                // and then we try to canonicalize the PathBuf, because we want to store a
                // canonicalized path
                // and return (file, content hash, permissions, canonicalized path)
                .and_then(|(file, opt_contenthash, opt_permissions)| {
                    pb.canonicalize()
                        .map(|can| (file, opt_contenthash, opt_permissions, can))
                        // if PathBuf::canonicalize() failed, build an error from the return value
                        .map_err(|e| REK::PathCanonicalizationError.into_error_with_cause(Box::new(e)))
                })

                // and then we hash the canonicalized path
                // and return (file, content hash, permissions, canonicalized path, path hash)
                .and_then(|(file, opt_contenthash, opt_permissions, can)| {
                    let path_hash = try!(Ref::hash_path(&can)
                        .map_err(Box::new)
                        .map_err(|e| REK::PathHashingError.into_error_with_cause(e))
                    );

                    Ok((file, opt_contenthash, opt_permissions, can, path_hash))
                })

                // and then we convert the PathBuf of the canonicalized path to a String to be able
                // to save it in the Ref FileLockEntry obj
                // and return
                // (file, content hash, permissions, canonicalized path as String, path hash)
                .and_then(|(file, opt_conhash, opt_perm, can, path_hash)| {
                    match can.to_str().map(String::from) {
                        // UTF convert error in PathBuf::to_str(),
                        None      => Err(REK::PathUTF8Error.into_error()),
                        Some(can) => Ok((file, opt_conhash, opt_perm, can, path_hash))
                    }
                })

                // and then we create the FileLockEntry in the Store
                // and return (filelockentry, content hash, permissions, canonicalized path)
                .and_then(|(file, opt_conhash, opt_perm, can, path_hash)| {
                    let fle = try!(store
                                   .create(ModuleEntryPath::new(path_hash))
                                   .map_err(Box::new)
                                   .map_err(|e| REK::StoreWriteError.into_error_with_cause(e))
                    );

                    Ok((fle, opt_conhash, opt_perm, can))
                })
            )
        };

        for tpl in [
                Some(("ref",             Value::Table(BTreeMap::new()))),
                Some(("ref.permissions", Value::Table(BTreeMap::new()))),

                Some(("ref.path", Value::String(canonical_path))),

                content_hash.map(|h| ("ref.content_hash", Value::String(h))),
                permissions.map(|p| ("ref.permissions.ro", Value::Boolean(p.readonly()))),
            ].into_iter()
        {
            match tpl {
                &Some((ref s, ref v)) => {
                    match fle.get_header_mut().insert(s, v.clone()) {
                        Ok(false) => {
                            let e = REK::HeaderFieldAlreadyExistsError.into_error();
                            let e = Box::new(e);
                            let e = REK::HeaderFieldWriteError.into_error_with_cause(e);
                            return Err(e);
                        },
                        Err(e) => {
                            let e = Box::new(e);
                            let e = REK::HeaderFieldWriteError.into_error_with_cause(e);
                            return Err(e);
                        },
                        _ => (),
                    }
                }
                &None => {
                    debug!("Not going to insert.");
                }
            }
        }

        Ok(Ref(fle))
    }

    /// Creates a Hash from a PathBuf by making the PathBuf absolute and then running a hash
    /// algorithm on it
    fn hash_path(pb: &PathBuf) -> Result<String> {
        use std::io::Read;
        use crypto::sha1::Sha1;
        use crypto::digest::Digest;

        match pb.to_str() {
            Some(s) => {
                let mut hasher = Sha1::new();
                hasher.input_str(s);
                Ok(hasher.result_str())
            },
            None => return Err(REK::PathUTF8Error.into_error()),
        }
    }

    /// Get the hash of the link target which is stored in the ref object
    pub fn get_stored_hash(&self) -> Result<String> {
        match self.0.get_header().read("ref.content_hash") {
            // content hash stored...
            Ok(Some(Value::String(s))) => Ok(s),

            // content hash header field has wrong type
            Ok(Some(_)) => Err(REK::HeaderTypeError.into_error()),

            // content hash not stored
            Ok(None) => Err(REK::HeaderFieldMissingError.into_error()),

            // Error
            Err(e) => Err(REK::StoreReadError.into_error_with_cause(Box::new(e))),
        }
    }

    /// Get the hash of the link target by reading the link target and hashing the contents
    pub fn get_current_hash(&self) -> Result<String> {
        self.fs_file()
            .and_then(|pb| {
                File::open(pb)
                    .map_err(Box::new)
                    .map_err(|e| REK::IOError.into_error_with_cause(e))
            })
            .map(|mut file| hash_file_contents(&mut file))
    }

    /// check whether the pointer the Ref represents still points to a file which exists
    pub fn fs_link_exists(&self) -> Result<bool> {
        self.fs_file().map(|pathbuf| pathbuf.exists())
    }

    /// Alias for `r.fs_link_exists() && r.deref().is_file()`
    pub fn is_ref_to_file(&self) -> Result<bool> {
        self.fs_file().map(|pathbuf| pathbuf.is_file())
    }

    /// Alias for `r.fs_link_exists() && r.deref().is_dir()`
    pub fn is_ref_to_dir(&self) -> Result<bool> {
        self.fs_file().map(|pathbuf| pathbuf.is_dir())
    }

    /// Alias for `!Ref::fs_link_exists()`
    pub fn is_dangling(&self) -> Result<bool> {
        self.fs_link_exists().map(|b| !b)
    }

    /// check whether the pointer the Ref represents is valid
    /// This includes:
    ///     - Hashsum of the file is still the same as stored in the Ref
    ///     - file permissions are still valid
    pub fn fs_link_valid(&self) -> Result<bool> {
        match (self.fs_link_valid_permissions(), self.fs_link_valid_hash()) {
            (Ok(true) , Ok(true)) => Ok(true),
            (Ok(_)    , Ok(_))    => Ok(false),
            (Err(e)   , _)        => Err(e),
            (_        , Err(e))   => Err(e),
        }
    }

    /// Check whether the file permissions of the referenced file are equal to the stored
    /// permissions
    pub fn fs_link_valid_permissions(&self) -> Result<bool> {
        self.0
            .get_header()
            .read("ref.permissions.ro")
            .map_err(Box::new)
            .map_err(|e| REK::HeaderFieldReadError.into_error_with_cause(e))
            .and_then(|ro| {
                match ro {
                    Some(Value::Boolean(b)) => Ok(b),
                    Some(_)                 => Err(REK::HeaderTypeError.into_error()),
                    None                    => Err(REK::HeaderFieldMissingError.into_error()),
                }
            })
            .and_then(|ro| self.fs_file().map(|pb| (ro, pb)))
            .and_then(|(ro, pb)| {
                File::open(pb)
                    .map(|file| (ro, file))
                    .map_err(Box::new)
                    .map_err(|e| REK::HeaderFieldReadError.into_error_with_cause(e))
            })
            .and_then(|(ro, file)| {
                file
                    .metadata()
                    .map(|md| md.permissions())
                    .map(|perm| perm.readonly() == ro)
                    .map_err(Box::new)
                    .map_err(|e| REK::RefTargetCannotReadPermissions.into_error_with_cause(e))
            })
            .map_err(Box::new)
            .map_err(|e| REK::RefTargetCannotReadPermissions.into_error_with_cause(e))
    }

    /// Check whether the Hashsum of the referenced file is equal to the stored hashsum
    pub fn fs_link_valid_hash(&self) -> Result<bool> {
        let stored_hash  = try!(self.get_stored_hash());
        let current_hash = try!(self.get_current_hash());
        Ok(stored_hash == current_hash)
    }

    /// Update the Ref by re-checking the file from FS
    /// This errors if the file is not present or cannot be read()
    pub fn update_ref(&mut self) -> Result<()> {
        unimplemented!()
    }

    /// Get the path of the file which is reffered to by this Ref
    pub fn fs_file(&self) -> Result<PathBuf> {
        match self.0.get_header().read("ref.path") {
            Ok(Some(Value::String(ref s))) => Ok(PathBuf::from(s)),
            Ok(Some(_)) => Err(REK::HeaderTypeError.into_error()),
            Ok(None)    => Err(REK::HeaderFieldMissingError.into_error()),
            Err(e)      => Err(REK::StoreReadError.into_error_with_cause(Box::new(e))),
        }
    }

    /// Check whether there is a reference to the file at `pb`
    pub fn exists(store: &Store, pb: PathBuf) -> Result<bool> {
        pb.canonicalize()
            .map_err(Box::new)
            .map_err(|e| REK::PathCanonicalizationError.into_error_with_cause(e))
            .and_then(|can| {
                Ref::hash_path(&can)
                    .map_err(Box::new)
                    .map_err(|e| REK::PathHashingError.into_error_with_cause(e))
            })
            .and_then(|hash| {
                store.retrieve_for_module("ref").map(|iter| (hash, iter))
                    .map_err(Box::new)
                    .map_err(|e| REK::StoreReadError.into_error_with_cause(e))
            })
            .and_then(|(hash, possible_refs)| {
                // This is kind of a manual Iterator::filter() call what we do here, but with the
                // actual ::filter method we cannot return the error in a nice way, so we do it
                // manually here. If you can come up with a better version of this, feel free to
                // take this note as a todo.
                for r in possible_refs {
                    let contains_hash = match r.to_str() {
                        None => { // couldn't parse StoreId -> PathBuf -> &str
                            // TODO: How to report this?
                            return Err(REK::TypeConversionError.into_error());
                        },
                        Some(s) => s.contains(&hash[..]),
                    };

                    if !contains_hash {
                        continue;
                    }

                    match store.get(r) {
                        Ok(Some(fle)) => {
                            if Ref::read_reference(&fle).map(|path| path == pb).unwrap_or(false) {
                                return Ok(true)
                            }
                        },

                        Ok(None) => { // Something weird just happened
                            return Err(REK::StoreReadError.into_error());
                        },

                        Err(e) => {
                            return Err(REK::StoreReadError.into_error_with_cause(Box::new(e)));
                        },
                    }
                }

                Ok(false)
            })
    }

    /// Re-find a referenced file
    ///
    /// This function tries to re-find a ref by searching all directories in `search_roots` recursively
    /// for a file which matches the hash of the Ref `ref`.
    ///
    /// If `search_roots` is `None`, it starts at the filesystem root `/`.
    ///
    /// # Warning
    ///
    /// This option causes heavy I/O as it recursively searches the Filesystem.
    pub fn refind(&self, search_roots: Option<Vec<PathBuf>>) -> Option<PathBuf> {
        unimplemented!()
    }

}

impl<'a> Deref for Ref<'a> {
    type Target = FileLockEntry<'a>;

    fn deref(&self) -> &FileLockEntry<'a> {
        &self.0
    }

}

impl<'a> DerefMut for Ref<'a> {

    fn deref_mut(&mut self) -> &mut FileLockEntry<'a> {
        &mut self.0
    }

}

fn hash_file_contents(f: &mut File) -> String {
    let mut hasher = Sha1::new();
    let mut s = String::new();
    f.read_to_string(&mut s);
    hasher.input_str(&s[..]);
    hasher.result_str()
}

<commit_msg>Extract permissions getting into helper function<commit_after>//! The Ref object is a helper over the link functionality, so one is able to create references to
//! files outside of the imag store.

use std::path::PathBuf;
use std::ops::Deref;
use std::ops::DerefMut;
use std::collections::BTreeMap;
use std::fs::File;
use std::io::Read;
use std::fs::Permissions;

use libimagstore::store::FileLockEntry;
use libimagstore::storeid::StoreId;
use libimagstore::storeid::IntoStoreId;
use libimagstore::store::Store;
use libimagerror::into::IntoError;

use toml::Value;
use crypto::sha1::Sha1;
use crypto::digest::Digest;

use error::RefErrorKind as REK;
use flags::RefFlags;
use result::Result;
use module_path::ModuleEntryPath;

pub struct Ref<'a>(FileLockEntry<'a>);

impl<'a> Ref<'a> {

    /// Try to get `si` as Ref object from the store
    pub fn get(store: &'a Store, si: StoreId) -> Result<Ref<'a>> {
        match store.get(si) {
            Err(e) => return Err(REK::StoreReadError.into_error_with_cause(Box::new(e))),
            Ok(None) => return Err(REK::RefNotInStore.into_error()),
            Ok(Some(fle)) => Ref::read_reference(&fle).map(|_| Ref(fle)),
        }
    }

    fn read_reference(fle: &FileLockEntry<'a>) -> Result<PathBuf> {
        match fle.get_header().read("ref.reference") {
            Ok(Some(Value::String(s))) => Ok(PathBuf::from(s)),
            Ok(Some(_)) => Err(REK::HeaderTypeError.into_error()),
            Ok(None)    => Err(REK::HeaderFieldMissingError.into_error()),
            Err(e)      => Err(REK::StoreReadError.into_error_with_cause(Box::new(e))),
        }
    }

    /// Create a Ref object which refers to `pb`
    pub fn create(store: &'a Store, pb: PathBuf, flags: RefFlags) -> Result<Ref<'a>> {
        if !pb.exists() {
            return Err(REK::RefTargetDoesNotExist.into_error());
        }
        if flags.get_content_hashing() && pb.is_dir() {
            return Err(REK::RefTargetCannotBeHashed.into_error());
        }

        let (mut fle, content_hash, permissions, canonical_path) = { // scope to be able to fold
            try!(File::open(pb.clone())
                .map_err(Box::new)
                .map_err(|e| REK::RefTargetFileCannotBeOpened.into_error_with_cause(e))

                // If we were able to open this file,
                // we hash the contents of the file and return (file, hash)
                .and_then(|mut file| {
                    let opt_contenthash = if flags.get_content_hashing() {
                        Some(hash_file_contents(&mut file))
                    } else {
                        None
                    };

                    Ok((file, opt_contenthash))
                })

                // and then we get the permissions if we have to
                // and return (file, content hash, permissions)
                .and_then(|(file, opt_contenthash)| {
                    let opt_permissions = if flags.get_permission_tracking() {
                        Some(try!(file
                                  .metadata()
                                  .map(|md| md.permissions())
                                  .map_err(Box::new)
                                  .map_err(|e| REK::RefTargetCannotReadPermissions.into_error_with_cause(e))
                        ))
                    } else {
                        None
                    };

                    Ok((file, opt_contenthash, opt_permissions))
                })

                // and then we try to canonicalize the PathBuf, because we want to store a
                // canonicalized path
                // and return (file, content hash, permissions, canonicalized path)
                .and_then(|(file, opt_contenthash, opt_permissions)| {
                    pb.canonicalize()
                        .map(|can| (file, opt_contenthash, opt_permissions, can))
                        // if PathBuf::canonicalize() failed, build an error from the return value
                        .map_err(|e| REK::PathCanonicalizationError.into_error_with_cause(Box::new(e)))
                })

                // and then we hash the canonicalized path
                // and return (file, content hash, permissions, canonicalized path, path hash)
                .and_then(|(file, opt_contenthash, opt_permissions, can)| {
                    let path_hash = try!(Ref::hash_path(&can)
                        .map_err(Box::new)
                        .map_err(|e| REK::PathHashingError.into_error_with_cause(e))
                    );

                    Ok((file, opt_contenthash, opt_permissions, can, path_hash))
                })

                // and then we convert the PathBuf of the canonicalized path to a String to be able
                // to save it in the Ref FileLockEntry obj
                // and return
                // (file, content hash, permissions, canonicalized path as String, path hash)
                .and_then(|(file, opt_conhash, opt_perm, can, path_hash)| {
                    match can.to_str().map(String::from) {
                        // UTF convert error in PathBuf::to_str(),
                        None      => Err(REK::PathUTF8Error.into_error()),
                        Some(can) => Ok((file, opt_conhash, opt_perm, can, path_hash))
                    }
                })

                // and then we create the FileLockEntry in the Store
                // and return (filelockentry, content hash, permissions, canonicalized path)
                .and_then(|(file, opt_conhash, opt_perm, can, path_hash)| {
                    let fle = try!(store
                                   .create(ModuleEntryPath::new(path_hash))
                                   .map_err(Box::new)
                                   .map_err(|e| REK::StoreWriteError.into_error_with_cause(e))
                    );

                    Ok((fle, opt_conhash, opt_perm, can))
                })
            )
        };

        for tpl in [
                Some(("ref",             Value::Table(BTreeMap::new()))),
                Some(("ref.permissions", Value::Table(BTreeMap::new()))),

                Some(("ref.path", Value::String(canonical_path))),

                content_hash.map(|h| ("ref.content_hash", Value::String(h))),
                permissions.map(|p| ("ref.permissions.ro", Value::Boolean(p.readonly()))),
            ].into_iter()
        {
            match tpl {
                &Some((ref s, ref v)) => {
                    match fle.get_header_mut().insert(s, v.clone()) {
                        Ok(false) => {
                            let e = REK::HeaderFieldAlreadyExistsError.into_error();
                            let e = Box::new(e);
                            let e = REK::HeaderFieldWriteError.into_error_with_cause(e);
                            return Err(e);
                        },
                        Err(e) => {
                            let e = Box::new(e);
                            let e = REK::HeaderFieldWriteError.into_error_with_cause(e);
                            return Err(e);
                        },
                        _ => (),
                    }
                }
                &None => {
                    debug!("Not going to insert.");
                }
            }
        }

        Ok(Ref(fle))
    }

    /// Creates a Hash from a PathBuf by making the PathBuf absolute and then running a hash
    /// algorithm on it
    fn hash_path(pb: &PathBuf) -> Result<String> {
        use std::io::Read;
        use crypto::sha1::Sha1;
        use crypto::digest::Digest;

        match pb.to_str() {
            Some(s) => {
                let mut hasher = Sha1::new();
                hasher.input_str(s);
                Ok(hasher.result_str())
            },
            None => return Err(REK::PathUTF8Error.into_error()),
        }
    }

    /// Get the hash of the link target which is stored in the ref object
    pub fn get_stored_hash(&self) -> Result<String> {
        match self.0.get_header().read("ref.content_hash") {
            // content hash stored...
            Ok(Some(Value::String(s))) => Ok(s),

            // content hash header field has wrong type
            Ok(Some(_)) => Err(REK::HeaderTypeError.into_error()),

            // content hash not stored
            Ok(None) => Err(REK::HeaderFieldMissingError.into_error()),

            // Error
            Err(e) => Err(REK::StoreReadError.into_error_with_cause(Box::new(e))),
        }
    }

    /// Get the hash of the link target by reading the link target and hashing the contents
    pub fn get_current_hash(&self) -> Result<String> {
        self.fs_file()
            .and_then(|pb| {
                File::open(pb)
                    .map_err(Box::new)
                    .map_err(|e| REK::IOError.into_error_with_cause(e))
            })
            .map(|mut file| hash_file_contents(&mut file))
    }

    /// Get the permissions of the file which are present
    fn get_current_permissions(&self) -> Result<Permissions> {
        self.fs_file()
            .and_then(|pb| {
                File::open(pb)
                    .map_err(Box::new)
                    .map_err(|e| REK::HeaderFieldReadError.into_error_with_cause(e))
            })
            .and_then(|file| {
                file
                    .metadata()
                    .map(|md| md.permissions())
                    .map_err(Box::new)
                    .map_err(|e| REK::RefTargetCannotReadPermissions.into_error_with_cause(e))
            })
    }

    /// check whether the pointer the Ref represents still points to a file which exists
    pub fn fs_link_exists(&self) -> Result<bool> {
        self.fs_file().map(|pathbuf| pathbuf.exists())
    }

    /// Alias for `r.fs_link_exists() && r.deref().is_file()`
    pub fn is_ref_to_file(&self) -> Result<bool> {
        self.fs_file().map(|pathbuf| pathbuf.is_file())
    }

    /// Alias for `r.fs_link_exists() && r.deref().is_dir()`
    pub fn is_ref_to_dir(&self) -> Result<bool> {
        self.fs_file().map(|pathbuf| pathbuf.is_dir())
    }

    /// Alias for `!Ref::fs_link_exists()`
    pub fn is_dangling(&self) -> Result<bool> {
        self.fs_link_exists().map(|b| !b)
    }

    /// check whether the pointer the Ref represents is valid
    /// This includes:
    ///     - Hashsum of the file is still the same as stored in the Ref
    ///     - file permissions are still valid
    pub fn fs_link_valid(&self) -> Result<bool> {
        match (self.fs_link_valid_permissions(), self.fs_link_valid_hash()) {
            (Ok(true) , Ok(true)) => Ok(true),
            (Ok(_)    , Ok(_))    => Ok(false),
            (Err(e)   , _)        => Err(e),
            (_        , Err(e))   => Err(e),
        }
    }

    /// Check whether the file permissions of the referenced file are equal to the stored
    /// permissions
    pub fn fs_link_valid_permissions(&self) -> Result<bool> {
        self.0
            .get_header()
            .read("ref.permissions.ro")
            .map_err(Box::new)
            .map_err(|e| REK::HeaderFieldReadError.into_error_with_cause(e))
            .and_then(|ro| {
                match ro {
                    Some(Value::Boolean(b)) => Ok(b),
                    Some(_)                 => Err(REK::HeaderTypeError.into_error()),
                    None                    => Err(REK::HeaderFieldMissingError.into_error()),
                }
            })
            .and_then(|ro| self.get_current_permissions().map(|perm| ro == perm.readonly()))
            .map_err(Box::new)
            .map_err(|e| REK::RefTargetCannotReadPermissions.into_error_with_cause(e))
    }

    /// Check whether the Hashsum of the referenced file is equal to the stored hashsum
    pub fn fs_link_valid_hash(&self) -> Result<bool> {
        let stored_hash  = try!(self.get_stored_hash());
        let current_hash = try!(self.get_current_hash());
        Ok(stored_hash == current_hash)
    }

    /// Update the Ref by re-checking the file from FS
    /// This errors if the file is not present or cannot be read()
    pub fn update_ref(&mut self) -> Result<()> {
        unimplemented!()
    }

    /// Get the path of the file which is reffered to by this Ref
    pub fn fs_file(&self) -> Result<PathBuf> {
        match self.0.get_header().read("ref.path") {
            Ok(Some(Value::String(ref s))) => Ok(PathBuf::from(s)),
            Ok(Some(_)) => Err(REK::HeaderTypeError.into_error()),
            Ok(None)    => Err(REK::HeaderFieldMissingError.into_error()),
            Err(e)      => Err(REK::StoreReadError.into_error_with_cause(Box::new(e))),
        }
    }

    /// Check whether there is a reference to the file at `pb`
    pub fn exists(store: &Store, pb: PathBuf) -> Result<bool> {
        pb.canonicalize()
            .map_err(Box::new)
            .map_err(|e| REK::PathCanonicalizationError.into_error_with_cause(e))
            .and_then(|can| {
                Ref::hash_path(&can)
                    .map_err(Box::new)
                    .map_err(|e| REK::PathHashingError.into_error_with_cause(e))
            })
            .and_then(|hash| {
                store.retrieve_for_module("ref").map(|iter| (hash, iter))
                    .map_err(Box::new)
                    .map_err(|e| REK::StoreReadError.into_error_with_cause(e))
            })
            .and_then(|(hash, possible_refs)| {
                // This is kind of a manual Iterator::filter() call what we do here, but with the
                // actual ::filter method we cannot return the error in a nice way, so we do it
                // manually here. If you can come up with a better version of this, feel free to
                // take this note as a todo.
                for r in possible_refs {
                    let contains_hash = match r.to_str() {
                        None => { // couldn't parse StoreId -> PathBuf -> &str
                            // TODO: How to report this?
                            return Err(REK::TypeConversionError.into_error());
                        },
                        Some(s) => s.contains(&hash[..]),
                    };

                    if !contains_hash {
                        continue;
                    }

                    match store.get(r) {
                        Ok(Some(fle)) => {
                            if Ref::read_reference(&fle).map(|path| path == pb).unwrap_or(false) {
                                return Ok(true)
                            }
                        },

                        Ok(None) => { // Something weird just happened
                            return Err(REK::StoreReadError.into_error());
                        },

                        Err(e) => {
                            return Err(REK::StoreReadError.into_error_with_cause(Box::new(e)));
                        },
                    }
                }

                Ok(false)
            })
    }

    /// Re-find a referenced file
    ///
    /// This function tries to re-find a ref by searching all directories in `search_roots` recursively
    /// for a file which matches the hash of the Ref `ref`.
    ///
    /// If `search_roots` is `None`, it starts at the filesystem root `/`.
    ///
    /// # Warning
    ///
    /// This option causes heavy I/O as it recursively searches the Filesystem.
    pub fn refind(&self, search_roots: Option<Vec<PathBuf>>) -> Option<PathBuf> {
        unimplemented!()
    }

}

impl<'a> Deref for Ref<'a> {
    type Target = FileLockEntry<'a>;

    fn deref(&self) -> &FileLockEntry<'a> {
        &self.0
    }

}

impl<'a> DerefMut for Ref<'a> {

    fn deref_mut(&mut self) -> &mut FileLockEntry<'a> {
        &mut self.0
    }

}

fn hash_file_contents(f: &mut File) -> String {
    let mut hasher = Sha1::new();
    let mut s = String::new();
    f.read_to_string(&mut s);
    hasher.input_str(&s[..]);
    hasher.result_str()
}

<|endoftext|>


<commit_before>extern crate scoped_threadpool;
extern crate thread_local;
extern crate hwloc;

use matrix::{Scalar,Mat};
use core::marker::{PhantomData};
use thread_comm::{ThreadComm,ThreadInfo};
use composables::{GemmNode,AlgorithmStep};

use std::sync::{Arc,Mutex};
use std::cell::{RefCell};
use self::scoped_threadpool::Pool;
use self::thread_local::ThreadLocal;
use self::hwloc::{Topology, ObjectType, CPUBIND_THREAD, CpuSet};
use libc;

fn cpuset_for_core(topology: &Topology, idx: usize) -> CpuSet {
    let cores = (*topology).objects_with_type(&ObjectType::Core).unwrap();
    match cores.get(idx) {
        Some(val) => val.cpuset().unwrap(),
        None => panic!("No Core found with id {}", idx)
    }
}

pub struct SpawnThreads<T: Scalar, At: Mat<T>, Bt: Mat<T>, Ct: Mat<T>, S: GemmNode<T, At, Bt, Ct>>
    where S: Send {
    //child: Arc<S>,
    n_threads: usize,
    pool: Pool,

    cntl_cache: Arc<ThreadLocal<RefCell<S>>>,

    _t: PhantomData<T>,
    _at: PhantomData<At>,
    _bt: PhantomData<Bt>,
    _ct: PhantomData<Ct>,
}
impl<T: Scalar,At: Mat<T>, Bt: Mat<T>, Ct: Mat<T>, S: GemmNode<T, At, Bt, Ct>>
    SpawnThreads <T,At,Bt,Ct,S>
    where S: Send {
    pub fn set_n_threads(&mut self, n_threads: usize){
        self.n_threads = n_threads;
        self.pool = Pool::new(n_threads as u32);

        //TODO: Clear control cache more robustly
        Arc::get_mut(&mut self.cntl_cache).expect("").clear();
        self.bind_threads();
    }
    fn bind_threads(&mut self) {
        //Get topology
        let topo = Arc::new(Mutex::new(Topology::new()));
/*        let num_cores = {
            let topo_rc = topo.clone();
            let topo_locked = topo_rc.lock().unwrap();
            (*topo_locked).objects_with_type(&ObjectType::Core).unwrap().len()
        };*/
        let nthr = self.n_threads;
        self.pool.scoped(|scope| {
            for id in 0..nthr {
                let child_topo = topo.clone();
                scope.execute( move || {
                    let tid = unsafe { libc::pthread_self() };
                    {
                    let mut locked_topo = child_topo.lock().unwrap();
//                    let before = locked_topo.get_cpubind_for_thread(tid, CPUBIND_THREAD);
                    let bind_to = cpuset_for_core(&*locked_topo, id);
                    //Doesn't matter if it worked or not
                    let _ = locked_topo.set_cpubind_for_thread(tid, bind_to, CPUBIND_THREAD);
//                    let after = locked_topo.get_cpubind_for_thread(tid, CPUBIND_THREAD);
    //                    println!("Thread {}: Before {:?}, After {:?}", id, before, after);
                    }
                });
            }
        });
    }
}
impl<T: Scalar, At: Mat<T>, Bt: Mat<T>, Ct: Mat<T>, S: GemmNode<T, At, Bt, Ct>>
    GemmNode<T, At, Bt, Ct> for SpawnThreads<T, At, Bt, Ct, S>
    where S: Send {
    #[inline(always)]
    unsafe fn run(&mut self, a: &mut At, b: &mut Bt, c:&mut Ct, _thr: &ThreadInfo<T>) -> () {
        //What we do here:
        //3. Spawn a bunch of threads. How many?
        //  For now store these in cntl tree.
        //  This will be easy to change since this cntl tree will be the root (so the 'outside' has
        //  ownership.
        //4. Set cpu affinity for each thread.

        //Should the thread communicator be cached???
        //Probably this is cheap so don't worry about it
        let global_comm : Arc<ThreadComm<T>> = Arc::new(ThreadComm::new(self.n_threads));

        //Make some shallow copies here to pass into the scoped,
        //because self.pool borrows self as mutable
        let nthr = self.n_threads;
        let cache = self.cntl_cache.clone();

//        println!("Found {} cores.", num_cores);

        self.pool.scoped(|scope| {
            for id in 0..nthr {
                //Make some shallow copies because of borrow rules
                let mut my_a = a.make_alias();
                let mut my_b = b.make_alias();
                let mut my_c = c.make_alias();
                let my_comm  = global_comm.clone();
                let my_cache = cache.clone();
                //let child_topo = topo.clone();

                scope.execute( move || {
                    //Set CPU affinity
/*                    let tid = unsafe { libc::pthread_self() };
                    {
                    let mut locked_topo = child_topo.lock().unwrap();
                    let before = locked_topo.get_cpubind_for_thread(tid, CPUBIND_THREAD);
                    let bind_to = cpuset_for_core(&*locked_topo, id);
                    let result = locked_topo.set_cpubind_for_thread(tid, bind_to, CPUBIND_THREAD);
                    let after = locked_topo.get_cpubind_for_thread(tid, CPUBIND_THREAD);
//                    println!("Thread {}: Before {:?}, After {:?}", id, before, after);
                    }*/

                    //Get this thread's control tree
                    let thr = ThreadInfo::new(id, my_comm);
                    let cntl_tree_cell = my_cache.get_or(|| Box::new(RefCell::new(S::new())));

                    //Run subproblem
                    cntl_tree_cell.borrow_mut().run(&mut my_a, &mut my_b, &mut my_c, &thr);
                });
            }
        });
    }
    fn new() -> SpawnThreads<T, At, Bt, Ct, S>{
        SpawnThreads{ n_threads : 1, pool: Pool::new(1),
                 cntl_cache: Arc::new(ThreadLocal::new()),
                 _t: PhantomData, _at:PhantomData, _bt: PhantomData, _ct: PhantomData }
    }
    fn hierarchy_description( ) -> Vec<AlgorithmStep> {
        S::hierarchy_description()
    }
}

<commit_msg>Fixing bug when no barrier during GEMM<commit_after>extern crate scoped_threadpool;
extern crate thread_local;
extern crate hwloc;

use matrix::{Scalar,Mat};
use core::marker::{PhantomData};
use thread_comm::{ThreadComm,ThreadInfo};
use composables::{GemmNode,AlgorithmStep};

use std::sync::{Arc,Mutex};
use std::cell::{RefCell};
use self::scoped_threadpool::Pool;
use self::thread_local::ThreadLocal;
use self::hwloc::{Topology, ObjectType, CPUBIND_THREAD, CpuSet};
use libc;

fn cpuset_for_core(topology: &Topology, idx: usize) -> CpuSet {
    let cores = (*topology).objects_with_type(&ObjectType::Core).unwrap();
    match cores.get(idx) {
        Some(val) => val.cpuset().unwrap(),
        None => panic!("No Core found with id {}", idx)
    }
}

pub struct SpawnThreads<T: Scalar, At: Mat<T>, Bt: Mat<T>, Ct: Mat<T>, S: GemmNode<T, At, Bt, Ct>>
    where S: Send {
    n_threads: usize,
    pool: Pool,

    cntl_cache: Arc<ThreadLocal<RefCell<S>>>,

    _t: PhantomData<T>,
    _at: PhantomData<At>,
    _bt: PhantomData<Bt>,
    _ct: PhantomData<Ct>,
}
impl<T: Scalar,At: Mat<T>, Bt: Mat<T>, Ct: Mat<T>, S: GemmNode<T, At, Bt, Ct>>
    SpawnThreads <T,At,Bt,Ct,S>
    where S: Send {
    pub fn set_n_threads(&mut self, n_threads: usize){
        self.n_threads = n_threads;
        self.pool = Pool::new(n_threads as u32);

        //TODO: Clear control cache more robustly
        Arc::get_mut(&mut self.cntl_cache).expect("").clear();
        self.bind_threads();
    }
    fn bind_threads(&mut self) {
        //Get topology
        let topo = Arc::new(Mutex::new(Topology::new()));
/*        let num_cores = {
            let topo_rc = topo.clone();
            let topo_locked = topo_rc.lock().unwrap();
            (*topo_locked).objects_with_type(&ObjectType::Core).unwrap().len()
        };*/
        let nthr = self.n_threads;
        self.pool.scoped(|scope| {
            for id in 0..nthr {
                let child_topo = topo.clone();
                scope.execute( move || {
                    let tid = unsafe { libc::pthread_self() };
                    {
                    let mut locked_topo = child_topo.lock().unwrap();
                    let before = locked_topo.get_cpubind_for_thread(tid, CPUBIND_THREAD);
                    let bind_to = cpuset_for_core(&*locked_topo, id);
                    //Doesn't matter if it worked or not
                    let _ = locked_topo.set_cpubind_for_thread(tid, bind_to, CPUBIND_THREAD);
                    let after = locked_topo.get_cpubind_for_thread(tid, CPUBIND_THREAD);
//                    println!("Thread {}: Before {:?}, After {:?}", id, before, after);
                    }
                });
            }
        });
    }
}
impl<T: Scalar, At: Mat<T>, Bt: Mat<T>, Ct: Mat<T>, S: GemmNode<T, At, Bt, Ct>>
    GemmNode<T, At, Bt, Ct> for SpawnThreads<T, At, Bt, Ct, S>
    where S: Send {
    #[inline(always)]
    unsafe fn run(&mut self, a: &mut At, b: &mut Bt, c:&mut Ct, _thr: &ThreadInfo<T>) -> () {
        //Should the thread communicator be cached???
        //Probably this is cheap so don't worry about it
        let global_comm : Arc<ThreadComm<T>> = Arc::new(ThreadComm::new(self.n_threads));

        //Make some shallow copies here to pass into the scoped,
        //because self.pool borrows self as mutable
        let num_threads = self.n_threads;
        let cache = self.cntl_cache.clone();

        self.pool.scoped(|scope| {
            for id in 0..num_threads {
                //Make some shallow copies because of borrow rules
                let mut my_a = a.make_alias();
                let mut my_b = b.make_alias();
                let mut my_c = c.make_alias();
                let my_comm  = global_comm.clone();
                let my_cache = cache.clone();

                scope.execute( move || {
                    //Make this thread's communicator holder
                    let thr = ThreadInfo::new(id, my_comm);

                    //We need to have a barrier here to force multiple threads to actually spawn.
                    thr.barrier();

                    //Read this thread's cached control tree
                    let cntl_tree_cell = my_cache.get_or(|| Box::new(RefCell::new(S::new())));

                    //Run subproblem
                    cntl_tree_cell.borrow_mut().run(&mut my_a, &mut my_b, &mut my_c, &thr);
                });
            }
        });
    }
    fn new() -> SpawnThreads<T, At, Bt, Ct, S>{
        SpawnThreads{ n_threads : 1, pool: Pool::new(1),
                 cntl_cache: Arc::new(ThreadLocal::new()),
                 _t: PhantomData, _at:PhantomData, _bt: PhantomData, _ct: PhantomData }
    }
    fn hierarchy_description( ) -> Vec<AlgorithmStep> {
        S::hierarchy_description()
    }
}

<|endoftext|>


<commit_before>#[macro_use] extern crate log;
#[macro_use] extern crate serde_derive;

extern crate toml;
extern crate serde;
extern crate serde_json;
extern crate openssl;
extern crate sozu_lib as sozu;

pub mod config;
pub mod data;
pub mod state;
<commit_msg>reexport sozu::messages<commit_after>#[macro_use] extern crate log;
#[macro_use] extern crate serde_derive;

extern crate toml;
extern crate serde;
extern crate serde_json;
extern crate openssl;
extern crate sozu_lib as sozu;

pub mod config;
pub mod data;
pub mod state;

pub use sozu::messages::*;
<|endoftext|>


<commit_before>extern crate image;

use std::env::args;
use std::process::exit;
use std::fs::File;
use std::path::Path;
use image::GenericImage;

fn get_top_left(in_path: &str) -> u32 {
	let im = image::open(&Path::new(in_path)).unwrap();
	for x in 0..(im.dimensions().1) {
		for y in 0..(im.dimensions().0) {
			let col = im.get_pixel(y, x);
			if col[0] != 255 && col[1] != 255 && col[2] != 255 {
				return x;
			}
		}
	}
	unreachable!();
}

fn get_top_right(in_path: &str) -> u32 {
	let im = image::open(&Path::new(in_path)).unwrap();
	for x in 0..(im.dimensions().0) {
		for y in 0..(im.dimensions().1) {
			let col = im.get_pixel(x, y);
			if col[0] != 255 && col[1] != 255 && col[2] != 255 {
				return x;
			}
		}
	}
	unreachable!();
}

fn get_lower_left(in_path: &str) -> u32 {
	let im = image::open(&Path::new(in_path)).unwrap();
	let mut x = im.dimensions().1 as i32 - 1;
	// Using while loop as there is no reliable way
	// to use custom steps in range() currently
	while x >= 0 {
		let mut y = im.dimensions().0 as i32 - 1;
		while y >= 0 {
			let col = im.get_pixel(y as u32, x as u32);
			if col[0] != 255 && col[1] != 255 && col[2] != 255 {
				return x as u32 + 1;
			}
			y -= 1;
		}
		x -= 1;
	}
	unreachable!();
}

fn get_lower_right(in_path: &str) -> u32 {
	let im = image::open(&Path::new(in_path)).unwrap();
	let mut x = im.dimensions().0 as i32 - 1;
	// Using while loop as there is no reliable way
	// to use custom steps in range() currently
	while x >= 0 {
		let mut y = im.dimensions().1 as i32 - 1;
		while y >= 0 {
			let col = im.get_pixel(x as u32, y as u32);
			if col[0] != 255 && col[1] != 255 && col[2] != 255 {
				return x as u32 + 1;
			}
			y -= 1;
		}
		x -= 1;
	}
	unreachable!();
}

fn main() {
	let args: Vec<_> = args().collect();

	let (in_path, out_path) = if args.len() == 3 {
		(args[1].as_str(), args[2].as_str())
	} else {
		println!("usage: {} <input_image> <output_image>", args[0]);
		exit(1)
	};

	// Top left corner
	let (b, a) = (get_top_left(in_path), get_top_right(in_path));
	// Lower right corner
	let (y, x) = (get_lower_left(in_path), get_lower_right(in_path));

	println!("Cropping area ({0}, {1}, {2}, {3}) from {4} to {5}",
	a, b, x, y, in_path, out_path);

	let mut im = image::open(&Path::new(in_path)).unwrap();
	let subim = im.crop(a, b, x - a, y - b);

	let ref mut fout = File::create(&Path::new(out_path)).unwrap();

	let _ = subim.save(fout, image::JPEG).unwrap();
}
<commit_msg>Crop all images in a directory at once<commit_after>extern crate clap;
extern crate image;

use std::fs;
//use std::env::args;
//use std::process::exit;
use std::path::Path;
use clap::{Arg, App};
use image::GenericImage;

fn get_top_left(input_path: &str) -> u32 {
	let im = image::open(&Path::new(input_path)).unwrap();
	for x in 0..(im.dimensions().1) {
		for y in 0..(im.dimensions().0) {
			let col = im.get_pixel(y, x);
			if col[0] != 255 && col[1] != 255 && col[2] != 255 {
				return x;
			}
		}
	}
	unreachable!();
}

fn get_top_right(input_path: &str) -> u32 {
	let im = image::open(&Path::new(input_path)).unwrap();
	for x in 0..(im.dimensions().0) {
		for y in 0..(im.dimensions().1) {
			let col = im.get_pixel(x, y);
			if col[0] != 255 && col[1] != 255 && col[2] != 255 {
				return x;
			}
		}
	}
	unreachable!();
}

fn get_lower_left(input_path: &str) -> u32 {
	let im = image::open(&Path::new(input_path)).unwrap();
	let mut x = im.dimensions().1 as i32 - 1;
	// Using while loop as there is no reliable way
	// to use custom steps in range() currently
	while x >= 0 {
		let mut y = im.dimensions().0 as i32 - 1;
		while y >= 0 {
			let col = im.get_pixel(y as u32, x as u32);
			if col[0] != 255 && col[1] != 255 && col[2] != 255 {
				return x as u32 + 1;
			}
			y -= 1;
		}
		x -= 1;
	}
	unreachable!();
}

fn get_lower_right(input_path: &str) -> u32 {
	let im = image::open(&Path::new(input_path)).unwrap();
	let mut x = im.dimensions().0 as i32 - 1;
	// Using while loop as there is no reliable way
	// to use custom steps in range() currently
	while x >= 0 {
		let mut y = im.dimensions().1 as i32 - 1;
		while y >= 0 {
			let col = im.get_pixel(x as u32, y as u32);
			if col[0] != 255 && col[1] != 255 && col[2] != 255 {
				return x as u32 + 1;
			}
			y -= 1;
		}
		x -= 1;
	}
	unreachable!();
}

fn crop_image(input_path: &str, output_path: &str) {
	// Top left corner
	let (b, a) = (get_top_left(input_path), get_top_right(input_path));
	// Lower right corner
	let (y, x) = (get_lower_left(input_path), get_lower_right(input_path));

	println!("Cropping area ({0}, {1}, {2}, {3}) from {4} to {5}",
	a, b, x, y, input_path, output_path);

	let mut im = image::open(&Path::new(input_path)).unwrap();
	let subim = im.crop(a, b, x - a, y - b);

	let ref mut fout = fs::File::create(&Path::new(output_path)).unwrap();

	let _ = subim.save(fout, image::JPEG).unwrap();
}

fn main() {
	/* Hold Your Breath */
	let arguments = App::new("auto-image-cropper")
	                    .version("0.1.2")
	                    .author("Ritiek Malhotra <ritiekmalhotra123@gmail.com")
	                    .about("Removes extra white boundaries from images to correctly resize canvas.")

	                    .arg(Arg::with_name("input")
	                    .short("i")
	                    .long("input")
	                    .value_name("LOCATION")
	                    .required(true)
	                    .takes_value(true)
	                    .help("location of input image/directory"))

	                    .arg(Arg::with_name("output")
	                    .short("o")
	                    .long("output")
	                    .value_name("LOCATION")
	                    .takes_value(true)
	                    .help("location of output image/directory"))

	                    .get_matches();

	let input_path = arguments.value_of("input").unwrap();
	let output_path = arguments.value_of("output")
	                        .unwrap_or(input_path);
	let path_type = fs::metadata(input_path).unwrap();

	if path_type.is_file() {
		crop_image(input_path, output_path);

	} else {
		let input_files = fs::read_dir(input_path).unwrap();

		if !Path::new(output_path).exists() {
			let _ = fs::create_dir(output_path);
		}

		for file in input_files {
			let img_in = file.unwrap().path();
			let img_name = img_in.file_name();
			let img_out = Path::new(output_path).join(img_name.unwrap());

			crop_image(&(img_in.display().to_string()),
			           &(img_out.into_os_string().into_string().unwrap()));
		}
	}

}
<|endoftext|>


<commit_before>#![allow(dead_code)]

#[macro_use]
extern crate itertools;
extern crate rand;
extern crate num;
extern crate rustty;

mod frontends;
mod ecs;
mod math;
mod knowledge;
mod spatial_hash;
mod util;
mod direction;
mod grid;

fn main() {

    let mut sh = spatial_hash::SpatialHashTable::new();
    let mut ctx = ecs::EcsCtx::new();
    let mut ids = util::LeakyReserver::new();

    let mut g = ecs::EcsAction::new();

    let strings = ["&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&",
                   "&,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,&",
                   "&,,############################,,,,,,&",
                   "&,,#.........#................#,,&,,,&",
                   "&,,#.........#................#,,,&,,&",
                   "&,,#..........................#,,&,,,&",
                   "&&,#.........#................#,,,,,,&",
                   "&,&#.........##########.#######,,,,,,&",
                   "&,,#.........#,,,,,,,,,,,,,,,,,,,,,,,&",
                   "&&,#.........#,,,,,,,,,&,,,,,,,&,&,&,&",
                   "&,,#.........#,,,,,&,,,,,,,,&,,,,,,,,&",
                   "&,,#..........,,,,,,&,,,,,,,,,,,,,,,,&",
                   "&&,#.........#,,,,,&,,,,,,,,,&,,,,,,,&",
                   "&,,#.........#,,,,,,,,,,&,,&,,,&,&,,,&",
                   "&,&#.........#,,,,@,,,,&,,,,,,,,,,,,,&",
                   "&,,###########,,,,,,,&,,,,,,,&,&,,,,,&",
                   "&,,&,,,,,,,,,,,,,,,,,&,,,,&,,,,,,,,,,&",
                   "&,&,,,,,,,,,,,,&,,,,,,,,,,,,,,,,,,,,,&",
                   "&,,,&,,,,,,,,,,,,,,,,&,,,,,#########,&",
                   "&,&,,,&,,,,,&,,&,,,,&,,,,,,#.......#,&",
                   "&,,,,,&,,,,,,,,,&,,,,&,,,,,#.......#,&",
                   "&,,,,,,,,,&,,,,,,,,,,,,,&,,........#,&",
                   "&,&,&,,,,&&,,,&,&,,,,,,,&,,#.......#,&",
                   "&,,,,,,,,,,,,,,,,,,,&,,,,,,#.......#,&",
                   "&,,,&,,,,,,,&,,,,,,,,,,,,,,#########,&",
                   "&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&"];

    let mut y = 0;
    for line in &strings {
        let mut x = 0;
        for ch in line.chars() {
            let coord = math::Coord::new(x, y);
            match ch {
                '#' => {
                    ecs::prototypes::wall(g.entity_mut(ids.reserve()), coord);
                    ecs::prototypes::floor(g.entity_mut(ids.reserve()), coord);
                }
                '&' => {
                    ecs::prototypes::tree(g.entity_mut(ids.reserve()), coord);
                    ecs::prototypes::outside_floor(g.entity_mut(ids.reserve()), coord);
                }
                '.' => {
                    ecs::prototypes::floor(g.entity_mut(ids.reserve()), coord);
                }
                ',' => {
                    ecs::prototypes::outside_floor(g.entity_mut(ids.reserve()), coord);
                }
                '@' => {
                    ecs::prototypes::pc(g.entity_mut(ids.reserve()), coord);
                    ecs::prototypes::outside_floor(g.entity_mut(ids.reserve()), coord);
                }
                _ => panic!(),
            }
            x += 1;
        }
        y += 1;
    }

    sh.update(&ctx, &g);
    ctx.commit(&mut g);

    println!("Hello, world!");
}
<commit_msg>Added test printout<commit_after>#![allow(dead_code)]

#[macro_use]
extern crate itertools;
extern crate rand;
extern crate num;
extern crate rustty;

mod frontends;
mod ecs;
mod math;
mod knowledge;
mod spatial_hash;
mod util;
mod direction;
mod grid;

fn main() {

    let mut sh = spatial_hash::SpatialHashTable::new();
    let mut ctx = ecs::EcsCtx::new();
    let mut ids = util::LeakyReserver::new();

    let mut g = ecs::EcsAction::new();

    let strings = ["&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&",
                   "&,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,&",
                   "&,,############################,,,,,,&",
                   "&,,#.........#................#,,&,,,&",
                   "&,,#.........#................#,,,&,,&",
                   "&,,#..........................#,,&,,,&",
                   "&&,#.........#................#,,,,,,&",
                   "&,&#.........##########.#######,,,,,,&",
                   "&,,#.........#,,,,,,,,,,,,,,,,,,,,,,,&",
                   "&&,#.........#,,,,,,,,,&,,,,,,,&,&,&,&",
                   "&,,#.........#,,,,,&,,,,,,,,&,,,,,,,,&",
                   "&,,#..........,,,,,,&,,,,,,,,,,,,,,,,&",
                   "&&,#.........#,,,,,&,,,,,,,,,&,,,,,,,&",
                   "&,,#.........#,,,,,,,,,,&,,&,,,&,&,,,&",
                   "&,&#.........#,,,,@,,,,&,,,,,,,,,,,,,&",
                   "&,,###########,,,,,,,&,,,,,,,&,&,,,,,&",
                   "&,,&,,,,,,,,,,,,,,,,,&,,,,&,,,,,,,,,,&",
                   "&,&,,,,,,,,,,,,&,,,,,,,,,,,,,,,,,,,,,&",
                   "&,,,&,,,,,,,,,,,,,,,,&,,,,,#########,&",
                   "&,&,,,&,,,,,&,,&,,,,&,,,,,,#.......#,&",
                   "&,,,,,&,,,,,,,,,&,,,,&,,,,,#.......#,&",
                   "&,,,,,,,,,&,,,,,,,,,,,,,&,,........#,&",
                   "&,&,&,,,,&&,,,&,&,,,,,,,&,,#.......#,&",
                   "&,,,,,,,,,,,,,,,,,,,&,,,,,,#.......#,&",
                   "&,,,&,,,,,,,&,,,,,,,,,,,,,,#########,&",
                   "&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&"];

    let height = strings.len();
    let width = strings[0].len();

    let mut y = 0;
    for line in &strings {
        let mut x = 0;
        for ch in line.chars() {
            let coord = math::Coord::new(x, y);
            match ch {
                '#' => {
                    ecs::prototypes::wall(g.entity_mut(ids.reserve()), coord);
                    ecs::prototypes::floor(g.entity_mut(ids.reserve()), coord);
                }
                '&' => {
                    ecs::prototypes::tree(g.entity_mut(ids.reserve()), coord);
                    ecs::prototypes::outside_floor(g.entity_mut(ids.reserve()), coord);
                }
                '.' => {
                    ecs::prototypes::floor(g.entity_mut(ids.reserve()), coord);
                }
                ',' => {
                    ecs::prototypes::outside_floor(g.entity_mut(ids.reserve()), coord);
                }
                '@' => {
                    ecs::prototypes::pc(g.entity_mut(ids.reserve()), coord);
                    ecs::prototypes::outside_floor(g.entity_mut(ids.reserve()), coord);
                }
                _ => panic!(),
            }
            x += 1;
        }
        y += 1;
    }

    sh.update(&ctx, &g);
    ctx.commit(&mut g);

    for i in 0..height {
        for j in 0..width {
            let coord = math::Coord::new(j as isize, i as isize);
            if let Some(cell) = sh.get(coord) {
                if cell.solid() {
                    print!("#");
                } else {
                    print!(" ");
                }
            }
        }
        print!("\n");
    }
}
<|endoftext|>


<commit_before>extern crate byteorder;
extern crate regex;
extern crate flate2;
extern crate getopts;
extern crate tempdir;

use std::fs::File;
use std::io;
use std::io::{BufReader, BufWriter, Write};
use std::path::Path;
use std::env;
use std::process::exit;
use std::process::Command;
use std::error::Error;

use getopts::Options;

use tempdir::TempDir;

/** Create an InvalidData io::Error with the description being a
 * formatted string */
macro_rules! io_error {
    ($fmtstr:tt) => { io_error!($fmtstr,) };
    ($fmtstr:tt, $( $args:expr ),* ) => {
        Err(::std::io::Error::new(::std::io::ErrorKind::InvalidData,
                                  format!($fmtstr, $( $args ),* )));
    };
}

/** Alternative to println! that prints to stderr instead of stdout */
macro_rules! printerrln {
    ($fmtstr:tt) => { printerrln!($fmtstr,) };
    ($fmtstr:tt, $( $args:expr ),* ) => {
        writeln!(&mut io::stderr(), $fmtstr, $( $args ),* ).silent_unwrap();
    }
}

pub mod data;
mod write;
mod read;
mod string_write;
mod string_read;
#[cfg(test)]
mod tests;

/// Trait used in place of unwrap for printing to stdout/stderr, since if that
/// errors the program should simply exit with no further output
trait SilentUnwrap<T> {
    fn silent_unwrap(self) -> T;
}
impl<T> SilentUnwrap<T> for io::Result<T> {
    fn silent_unwrap(self) -> T {
        match self {
            Ok(t) => t,
            Err(_) => std::process::exit(1),
        }
    }
}

fn main() {
    let args: Vec<String> = env::args().collect();

    let mut opts = Options::new();
    opts.optflagopt("e", "edit", "edit a NBT file with your $EDITOR.
    If [FILE] is specified, then that file is edited in place, but specifying --input and/or --output will override the input/output.
    If no file is specified, default to read from --input and writing to --output.", "FILE");
    opts.optflagopt("p", "print", "print NBT file to text format. Adding an argument to this is the same as specifying --input", "FILE");
    opts.optflagopt("r", "reverse", "reverse a file in text format to NBT format. Adding an argument to this is the same as specifying --input", "FILE");
    opts.optopt("i",
                "input",
                "specify the input file, defaults to stdin",
                "FILE");
    opts.optopt("o",
                "output",
                "specify the output file, defaults to stdout",
                "FILE");
    opts.optflag("h", "help", "print the help menu");
    opts.optflag("", "version", "print program version");

    let matches = match opts.parse(&args[1..]) {
        Ok(x) => x,
        Err(e) => {
            error(&format!("Error parsing options: {:?}", e.description()))
        },
    };

    if matches.opt_present("h") {
        print_usage(opts);
        return;
    }

    if matches.opt_present("version") {
        println!("{} {} {}",
                 env!("CARGO_PKG_NAME"),
                 env!("CARGO_PKG_VERSION"),
                 /* See build.rs for the git-revision.txt file */
                 include!(concat!(env!("OUT_DIR"), "/git-revision.txt")));
        println!("{}", env!("CARGO_PKG_HOMEPAGE"));
        return;
    }

    /* Figure out the input file, by trying to read the arguments for all of
     * --input, --edit, --print and --reverse, prioritizing --input over the
     * other arguments, if none of the arguments are specified but there is a
     * free argument, use that, else we finally default to - (stdin) */
    let input = match matches.opt_str("input") {
        Some(x) => x,
        None => {
            match matches.opts_str(&["edit".to_string(),
                                     "print".to_string(),
                                     "reverse".to_string()]) {

                Some(x) => x,
                None if matches.free.len() > 0 => matches.free[0].clone(),
                None => "-".to_string(),
            }
        },
    };

    /* Analogous to the input */
    let output = match matches.opt_str("output") {
        Some(x) => x,
        None => {
            match matches.opt_str("edit") {
                Some(x) => x,
                None if matches.free.len() > 0 => matches.free[0].clone(),
                None => "-".to_string(),
            }
        },
    };

    if matches.opt_present("print") {
        print(&input, &output);
        return;
    }

    if matches.opt_present("reverse") {
        reverse(&input, &output);
        return;
    }

    /* Default to --edit */
    edit(&input, &output);
}

/** When the user wants to edit a specific file in place */
fn edit(input: &str, output: &str) {

    /* First we read the NBT data from the input */
    let nbt = if input == "-" {
        // let mut f = BufReader::new(io::stdin());
        let f = io::stdin();
        let mut f = f.lock();
        match read::read_file(&mut f) {
            Ok(x) => x,
            Err(_) => {
                error(&format!("Unable to parse {}, are you sure it's an NBT file?",
                              input))
            },
        }
    } else {
        let path: &Path = Path::new(input);
        let f = match File::open(path) {
            Ok(f) => f,
            Err(_) => error(&format!("Unable to open file {}", input)),
        };
        let mut f = BufReader::new(f);

        match read::read_file(&mut f) {
            Ok(x) => x,
            Err(_) => {
                error(&format!("Unable to parse {}, are you sure it's an NBT file?",
                              input))
            },
        }
    };

    /* Then we create a temporary file and write the NBT data in text format
     * to the temporary file */
    let tmpdir = match TempDir::new("nbted") {
        Ok(x) => x,
        Err(e) => {
            error(&format!("Unable to create temporary directory: {:?}",
                          e.description()))
        },
    };

    let tmp = match Path::new(input).file_name() {
        Some(x) => {
            let mut x = x.to_os_string();
            x.push(".txt");
            x
        },
        None => error(&format!("Error reading file name")),
    };
    let tmp_path = tmpdir.path().join(tmp);

    {
        let mut f = match File::create(&tmp_path) {
            Ok(x) => x,
            Err(e) => {
                error(&format!("Unable to create temporary file: {:?}",
                              e.description()))
            },
        };

        match string_write::write_file(&mut f, &nbt) {
            Ok(()) => (),
            Err(e) => {
                error(&format!("Unable to write temporary file: {:?}",
                              e.description()))
            },
        };

        match f.sync_all() {
            Ok(()) => (),
            Err(e) => {
                error(&format!("Unable to synchronize file: {:?}",
                              e.description()))
            },
        }
    }

    let new_nbt = {
        let mut new_nbt = open_editor(&tmp_path);

        while let Err(e) = new_nbt {
            printerrln!("Unable to parse edited file: {}.\n\
            Do you want to open the file for editing again? (y/N)",
                        e.description());

            let mut line = String::new();
            match io::stdin().read_line(&mut line) {
                Ok(_) => (),
                Err(e) => {
                    error(&format!("Error reading from stdin: {}\n\
                                   Nothing was changed.",
                                  e.description()))
                },
            }

            if line.trim() == "y" {
                new_nbt = open_editor(&tmp_path);
            } else {
                printerrln!("Exiting ... File is unchanged.");
                std::process::exit(1);
            }
        }

        new_nbt.expect("new_nbt was Error")
    };

    if nbt == new_nbt {
        printerrln!("No changes, will do nothing.");
        return;
    }

    /* And finally we write the edited nbt (new_nbt) into the output file */
    if output == "-" {
        let mut f = io::stdout();
        write::write_file(&mut f, &new_nbt).silent_unwrap();
    } else {
        let path: &Path = Path::new(output);
        let f = match File::create(&path) {
            Ok(x) => x,
            Err(e) => {
                error(&format!("Unable to write to output NBT file {}: {:?}.\n\
            Nothing was changed",
                              output,
                              e.description()))
            },
        };
        let mut f = BufWriter::new(f);

        match write::write_file(&mut f, &new_nbt) {
            Ok(()) => (),
            Err(e) => {
                error(&format!("Error writing NBT file {}: {:?}.\n\
            State of NBT file is unknown, consider restoring it from a backup.",
                              output,
                              e.description()))
            },
        }
    }

    printerrln!("File edited successfully.");
}

/// Open the user's $EDITOR on the temporary file, wait until the editor is
/// closed again, read the temporary file and attempt to parse it into NBT,
/// returning the result.
fn open_editor(tmp_path: &Path) -> io::Result<data::NBTFile> {
    let editor = match env::var("EDITOR") {
        Ok(x) => x,
        Err(_) => {
            match env::var("VISUAL") {
                Ok(x) => x,
                Err(_) => error("Unable to find $EDITOR"),
            }
        },
    };

    let mut cmd = Command::new(editor);
    cmd.arg(&tmp_path.as_os_str());
    let mut cmd = match cmd.spawn() {
        Ok(x) => x,
        Err(e) => {
            error(&format!("Error opening editor: {:?}", e.description()))
        },
    };

    match cmd.wait() {
        Ok(x) if x.success() => (),
        Err(e) => {
            error(&format!("Error executing editor: {:?}", e.description()))
        },
        _ => error("Editor did not exit correctly"),
    }

    /* Then we parse the text format in the temporary file into NBT */
    let mut f = match File::open(&tmp_path) {
        Ok(x) => x,
        Err(e) => {
            error(&format!("Unable to read temporary file: {:?}.\n\
        Nothing was changed",
                          e.description()))
        },
    };

    string_read::read_file(&mut f)
}

/** When the user wants to print an NBT file to text format */
fn print(input: &str, output: &str) {
    /* First we read a NBTFile from the input */
    let nbt = if input == "-" {
        let f = io::stdin();
        let mut f = f.lock();
        match read::read_file(&mut f) {
            Ok(x) => x,
            Err(_) => {
                error(&format!("Unable to parse {}, are you sure it's an NBT file?",
                              input))
            },
        }
    } else {
        let path: &Path = Path::new(input);
        let f = match File::open(path) {
            Ok(f) => f,
            Err(_) => error(&format!("Unable to open file {}", input)),
        };
        let mut f = BufReader::new(f);

        match read::read_file(&mut f) {
            Ok(x) => x,
            Err(_) => {
                error(&format!("Unable to parse {}, are you sure it's an NBT file?",
                              input))
            },
        }
    };

    /* Then we write the NBTFile to the output in text format */
    if output == "-" {
        let mut f = io::stdout();
        string_write::write_file(&mut f, &nbt).silent_unwrap();
    } else {
        let path: &Path = Path::new(output);
        let f = match File::create(&path) {
            Ok(x) => x,
            Err(e) => {
                error(&format!("Unable to write to output NBT file {}: {:?}.\n\
            Nothing was changed",
                              output,
                              e.description()))
            },
        };
        let mut f = BufWriter::new(f);

        match string_write::write_file(&mut f, &nbt) {
            Ok(()) => (),
            Err(e) => {
                error(&format!("Error writing NBT file {}: {:?}.\n\
            State of NBT file is unknown, consider restoring it from a backup.",
                              output,
                              e.description()))
            },
        }
    }
}

/** When the user wants to convert a text format file into an NBT file */
fn reverse(input: &str, output: &str) {
    /* First we read the input file in the text format */
    let path: &Path = Path::new(input);
    let mut f = match File::open(&path) {
        Ok(x) => x,
        Err(e) => {
            error(&format!("Unable to read text file {}: {:?}",
                          input,
                          e.description()))
        },
    };

    let nbt = match string_read::read_file(&mut f) {
        Ok(x) => x,
        Err(e) => {
            error(&format!("Unable to parse text file {}: {:?}",
                          input,
                          e.description()))
        },
    };

    /* Then we write the parsed NBT to the output file in NBT format */
    if output == "-" {
        let mut f = io::stdout();
        write::write_file(&mut f, &nbt).silent_unwrap();
    } else {
        let path: &Path = Path::new(output);
        let f = match File::create(&path) {
            Ok(x) => x,
            Err(e) => {
                error(&format!("Unable to write to output NBT file {}: {:?}.\n\
            Nothing was changed",
                              output,
                              e.description()))
            },
        };
        let mut f = BufWriter::new(f);

        match write::write_file(&mut f, &nbt) {
            Ok(()) => (),
            Err(e) => {
                error(&format!("Error writing NBT file {}: {:?}.\n\
            State of NBT file is unknown, consider restoring it from a backup.",
                              output,
                              e.description()))
            },
        }
    }
}

/** Print the given message and exit with status code 1 */
fn error(message: &str) -> ! {
    printerrln!("Error: {}", message);
    printerrln!("Run with --help for help, or read the manpage.");
    exit(1);
}

fn print_usage(opts: Options) {
    let brief = "Usage: nbted [options] FILE";
    print!("{}", opts.usage(&brief));
    println!("\nFor detailed usage information, read the manpage.");
}
<commit_msg>Lock stdout<commit_after>extern crate byteorder;
extern crate regex;
extern crate flate2;
extern crate getopts;
extern crate tempdir;

use std::fs::File;
use std::io;
use std::io::{BufReader, BufWriter, Write};
use std::path::Path;
use std::env;
use std::process::exit;
use std::process::Command;
use std::error::Error;

use getopts::Options;

use tempdir::TempDir;

/** Create an InvalidData io::Error with the description being a
 * formatted string */
macro_rules! io_error {
    ($fmtstr:tt) => { io_error!($fmtstr,) };
    ($fmtstr:tt, $( $args:expr ),* ) => {
        Err(::std::io::Error::new(::std::io::ErrorKind::InvalidData,
                                  format!($fmtstr, $( $args ),* )));
    };
}

/** Alternative to println! that prints to stderr instead of stdout */
macro_rules! printerrln {
    ($fmtstr:tt) => { printerrln!($fmtstr,) };
    ($fmtstr:tt, $( $args:expr ),* ) => {
        writeln!(&mut io::stderr(), $fmtstr, $( $args ),* ).silent_unwrap();
    }
}

pub mod data;
mod write;
mod read;
mod string_write;
mod string_read;
#[cfg(test)]
mod tests;

/// Trait used in place of unwrap for printing to stdout/stderr, since if that
/// errors the program should simply exit with no further output
trait SilentUnwrap<T> {
    fn silent_unwrap(self) -> T;
}
impl<T> SilentUnwrap<T> for io::Result<T> {
    fn silent_unwrap(self) -> T {
        match self {
            Ok(t) => t,
            Err(_) => std::process::exit(1),
        }
    }
}

fn main() {
    let args: Vec<String> = env::args().collect();

    let mut opts = Options::new();
    opts.optflagopt("e", "edit", "edit a NBT file with your $EDITOR.
    If [FILE] is specified, then that file is edited in place, but specifying --input and/or --output will override the input/output.
    If no file is specified, default to read from --input and writing to --output.", "FILE");
    opts.optflagopt("p", "print", "print NBT file to text format. Adding an argument to this is the same as specifying --input", "FILE");
    opts.optflagopt("r", "reverse", "reverse a file in text format to NBT format. Adding an argument to this is the same as specifying --input", "FILE");
    opts.optopt("i",
                "input",
                "specify the input file, defaults to stdin",
                "FILE");
    opts.optopt("o",
                "output",
                "specify the output file, defaults to stdout",
                "FILE");
    opts.optflag("h", "help", "print the help menu");
    opts.optflag("", "version", "print program version");

    let matches = match opts.parse(&args[1..]) {
        Ok(x) => x,
        Err(e) => {
            error(&format!("Error parsing options: {:?}", e.description()))
        },
    };

    if matches.opt_present("h") {
        print_usage(opts);
        return;
    }

    if matches.opt_present("version") {
        println!("{} {} {}",
                 env!("CARGO_PKG_NAME"),
                 env!("CARGO_PKG_VERSION"),
                 /* See build.rs for the git-revision.txt file */
                 include!(concat!(env!("OUT_DIR"), "/git-revision.txt")));
        println!("{}", env!("CARGO_PKG_HOMEPAGE"));
        return;
    }

    /* Figure out the input file, by trying to read the arguments for all of
     * --input, --edit, --print and --reverse, prioritizing --input over the
     * other arguments, if none of the arguments are specified but there is a
     * free argument, use that, else we finally default to - (stdin) */
    let input = match matches.opt_str("input") {
        Some(x) => x,
        None => {
            match matches.opts_str(&["edit".to_string(),
                                     "print".to_string(),
                                     "reverse".to_string()]) {

                Some(x) => x,
                None if matches.free.len() > 0 => matches.free[0].clone(),
                None => "-".to_string(),
            }
        },
    };

    /* Analogous to the input */
    let output = match matches.opt_str("output") {
        Some(x) => x,
        None => {
            match matches.opt_str("edit") {
                Some(x) => x,
                None if matches.free.len() > 0 => matches.free[0].clone(),
                None => "-".to_string(),
            }
        },
    };

    if matches.opt_present("print") {
        print(&input, &output);
        return;
    }

    if matches.opt_present("reverse") {
        reverse(&input, &output);
        return;
    }

    /* Default to --edit */
    edit(&input, &output);
}

/** When the user wants to edit a specific file in place */
fn edit(input: &str, output: &str) {

    /* First we read the NBT data from the input */
    let nbt = if input == "-" {
        // let mut f = BufReader::new(io::stdin());
        let f = io::stdin();
        let mut f = f.lock();
        match read::read_file(&mut f) {
            Ok(x) => x,
            Err(_) => {
                error(&format!("Unable to parse {}, are you sure it's an NBT file?",
                              input))
            },
        }
    } else {
        let path: &Path = Path::new(input);
        let f = match File::open(path) {
            Ok(f) => f,
            Err(_) => error(&format!("Unable to open file {}", input)),
        };
        let mut f = BufReader::new(f);

        match read::read_file(&mut f) {
            Ok(x) => x,
            Err(_) => {
                error(&format!("Unable to parse {}, are you sure it's an NBT file?",
                              input))
            },
        }
    };

    /* Then we create a temporary file and write the NBT data in text format
     * to the temporary file */
    let tmpdir = match TempDir::new("nbted") {
        Ok(x) => x,
        Err(e) => {
            error(&format!("Unable to create temporary directory: {:?}",
                          e.description()))
        },
    };

    let tmp = match Path::new(input).file_name() {
        Some(x) => {
            let mut x = x.to_os_string();
            x.push(".txt");
            x
        },
        None => error(&format!("Error reading file name")),
    };
    let tmp_path = tmpdir.path().join(tmp);

    {
        let mut f = match File::create(&tmp_path) {
            Ok(x) => x,
            Err(e) => {
                error(&format!("Unable to create temporary file: {:?}",
                              e.description()))
            },
        };

        match string_write::write_file(&mut f, &nbt) {
            Ok(()) => (),
            Err(e) => {
                error(&format!("Unable to write temporary file: {:?}",
                              e.description()))
            },
        };

        match f.sync_all() {
            Ok(()) => (),
            Err(e) => {
                error(&format!("Unable to synchronize file: {:?}",
                              e.description()))
            },
        }
    }

    let new_nbt = {
        let mut new_nbt = open_editor(&tmp_path);

        while let Err(e) = new_nbt {
            printerrln!("Unable to parse edited file: {}.\n\
            Do you want to open the file for editing again? (y/N)",
                        e.description());

            let mut line = String::new();
            match io::stdin().read_line(&mut line) {
                Ok(_) => (),
                Err(e) => {
                    error(&format!("Error reading from stdin: {}\n\
                                   Nothing was changed.",
                                  e.description()))
                },
            }

            if line.trim() == "y" {
                new_nbt = open_editor(&tmp_path);
            } else {
                printerrln!("Exiting ... File is unchanged.");
                std::process::exit(1);
            }
        }

        new_nbt.expect("new_nbt was Error")
    };

    if nbt == new_nbt {
        printerrln!("No changes, will do nothing.");
        return;
    }

    /* And finally we write the edited nbt (new_nbt) into the output file */
    if output == "-" {
        let f = io::stdout();
        let mut f = f.lock();
        write::write_file(&mut f, &new_nbt).silent_unwrap();
    } else {
        let path: &Path = Path::new(output);
        let f = match File::create(&path) {
            Ok(x) => x,
            Err(e) => {
                error(&format!("Unable to write to output NBT file {}: {:?}.\n\
            Nothing was changed",
                              output,
                              e.description()))
            },
        };
        let mut f = BufWriter::new(f);

        match write::write_file(&mut f, &new_nbt) {
            Ok(()) => (),
            Err(e) => {
                error(&format!("Error writing NBT file {}: {:?}.\n\
            State of NBT file is unknown, consider restoring it from a backup.",
                              output,
                              e.description()))
            },
        }
    }

    printerrln!("File edited successfully.");
}

/// Open the user's $EDITOR on the temporary file, wait until the editor is
/// closed again, read the temporary file and attempt to parse it into NBT,
/// returning the result.
fn open_editor(tmp_path: &Path) -> io::Result<data::NBTFile> {
    let editor = match env::var("EDITOR") {
        Ok(x) => x,
        Err(_) => {
            match env::var("VISUAL") {
                Ok(x) => x,
                Err(_) => error("Unable to find $EDITOR"),
            }
        },
    };

    let mut cmd = Command::new(editor);
    cmd.arg(&tmp_path.as_os_str());
    let mut cmd = match cmd.spawn() {
        Ok(x) => x,
        Err(e) => {
            error(&format!("Error opening editor: {:?}", e.description()))
        },
    };

    match cmd.wait() {
        Ok(x) if x.success() => (),
        Err(e) => {
            error(&format!("Error executing editor: {:?}", e.description()))
        },
        _ => error("Editor did not exit correctly"),
    }

    /* Then we parse the text format in the temporary file into NBT */
    let mut f = match File::open(&tmp_path) {
        Ok(x) => x,
        Err(e) => {
            error(&format!("Unable to read temporary file: {:?}.\n\
        Nothing was changed",
                          e.description()))
        },
    };

    string_read::read_file(&mut f)
}

/** When the user wants to print an NBT file to text format */
fn print(input: &str, output: &str) {
    /* First we read a NBTFile from the input */
    let nbt = if input == "-" {
        let f = io::stdin();
        let mut f = f.lock();
        match read::read_file(&mut f) {
            Ok(x) => x,
            Err(_) => {
                error(&format!("Unable to parse {}, are you sure it's an NBT file?",
                              input))
            },
        }
    } else {
        let path: &Path = Path::new(input);
        let f = match File::open(path) {
            Ok(f) => f,
            Err(_) => error(&format!("Unable to open file {}", input)),
        };
        let mut f = BufReader::new(f);

        match read::read_file(&mut f) {
            Ok(x) => x,
            Err(_) => {
                error(&format!("Unable to parse {}, are you sure it's an NBT file?",
                              input))
            },
        }
    };

    /* Then we write the NBTFile to the output in text format */
    if output == "-" {
        let f = io::stdout();
        let mut f = f.lock();
        string_write::write_file(&mut f, &nbt).silent_unwrap();
    } else {
        let path: &Path = Path::new(output);
        let f = match File::create(&path) {
            Ok(x) => x,
            Err(e) => {
                error(&format!("Unable to write to output NBT file {}: {:?}.\n\
            Nothing was changed",
                              output,
                              e.description()))
            },
        };
        let mut f = BufWriter::new(f);

        match string_write::write_file(&mut f, &nbt) {
            Ok(()) => (),
            Err(e) => {
                error(&format!("Error writing NBT file {}: {:?}.\n\
            State of NBT file is unknown, consider restoring it from a backup.",
                              output,
                              e.description()))
            },
        }
    }
}

/** When the user wants to convert a text format file into an NBT file */
fn reverse(input: &str, output: &str) {
    /* First we read the input file in the text format */
    let path: &Path = Path::new(input);
    let mut f = match File::open(&path) {
        Ok(x) => x,
        Err(e) => {
            error(&format!("Unable to read text file {}: {:?}",
                          input,
                          e.description()))
        },
    };

    let nbt = match string_read::read_file(&mut f) {
        Ok(x) => x,
        Err(e) => {
            error(&format!("Unable to parse text file {}: {:?}",
                          input,
                          e.description()))
        },
    };

    /* Then we write the parsed NBT to the output file in NBT format */
    if output == "-" {
        let f = io::stdout();
        let mut f = f.lock();
        write::write_file(&mut f, &nbt).silent_unwrap();
    } else {
        let path: &Path = Path::new(output);
        let f = match File::create(&path) {
            Ok(x) => x,
            Err(e) => {
                error(&format!("Unable to write to output NBT file {}: {:?}.\n\
            Nothing was changed",
                              output,
                              e.description()))
            },
        };
        let mut f = BufWriter::new(f);

        match write::write_file(&mut f, &nbt) {
            Ok(()) => (),
            Err(e) => {
                error(&format!("Error writing NBT file {}: {:?}.\n\
            State of NBT file is unknown, consider restoring it from a backup.",
                              output,
                              e.description()))
            },
        }
    }
}

/** Print the given message and exit with status code 1 */
fn error(message: &str) -> ! {
    printerrln!("Error: {}", message);
    printerrln!("Run with --help for help, or read the manpage.");
    exit(1);
}

fn print_usage(opts: Options) {
    let brief = "Usage: nbted [options] FILE";
    print!("{}", opts.usage(&brief));
    println!("\nFor detailed usage information, read the manpage.");
}
<|endoftext|>