<commit_before>// Copyright 2020 Google LLC
//
// Use of this source code is governed by an MIT-style license that can be found
// in the LICENSE file or at https://opensource.org/licenses/MIT.

//! Linux-specific utilities for working with the filesystem.

use std::path::Path;

// TODO: Document behaviour for symlinks.
/// Collects extended flags of the specified file.
///
/// The returned mask represents file attributes specific to the Linux extended
/// file system. Normally, they can be inspected using the `lsattr` command and
/// can be set through the `chattr` command.
///
/// See the [Wikipedia] article and the [man] page for more details.
///
/// [Wikipedia]: https://en.wikipedia.org/wiki/Chattr
/// [man]: https://linux.die.net/man/1/chattr
///
/// # Examples
///
/// ```no_run
/// const FS_FL_USER_VISIBLE: u32 = 0x0003DFFF;
///
/// let flags = rrg::fs::linux::flags("/tmp/foo").unwrap();
/// assert_eq!(flags & FS_FL_USER_VISIBLE, 0);
/// ```
pub fn flags<P>(path: P) -> std::io::Result<u32> where
    P: AsRef<Path>
{
    let file = std::fs::File::open(path)?;

    let mut flags = 0;
    let code = unsafe {
        // This block is safe: we simply pass a raw file descriptor (that is
        // valid until the end of the scope of this function) because this is
        // what the low-level API expects.
        use std::os::unix::io::AsRawFd as _;
        ioctls::fs_ioc_getflags(file.as_raw_fd(), &mut flags)
    };

    if code == 0 {
        Ok(flags as u32)
    } else {
        Err(std::io::Error::from_raw_os_error(code))
    }
}

pub fn ext_attr_names<P>(path: P) -> std::io::Result<Vec<std::ffi::OsString>>
where
    P: AsRef<Path>,
{
    extern "C" {
        // https://linux.die.net/man/2/llistxattr
        fn llistxattr(
            path: *const libc::c_char,
            list: *mut libc::c_char,
            size: libc::size_t,
        ) -> libc::ssize_t;
    }

    use std::os::unix::ffi::OsStrExt as _;

    let os_str_path = path.as_ref().as_os_str();
    let c_str_path = std::ffi::CString::new(os_str_path.as_bytes())
        // It is not possible to have a null byte in a Linux path.
        .expect("path with a null character");

    // SAFETY: The correctness of the path is guaranteed by conversion to the
    // `CString` type above. The rest is just is a FFI call respecting the spec.
    let len = unsafe {
        // First we call `llistxattr` with empty buffer to get the size of the
        // buffer that will collect the actual results.
        llistxattr(c_str_path.as_ptr(), std::ptr::null_mut(), 0)
    };
    if len < 0 {
        return Err(std::io::Error::last_os_error());
    }

    let mut buf = vec![0; len as usize];

    // SAFETY: The correctness of the path is guaranteed by conversion to the
    // `CString` type above. For remaining parameters we provide a buffer with
    // length obtained through the previous call and we explicitly supply its
    // length as well. In case the result length increases between the calls the
    // system will report an error but will not cause memory issues.
    let len = unsafe {
        // Now we can call `llistxattr` with the actual buffer of the size we
        // determined by the previous call.
        llistxattr(c_str_path.as_ptr(), buf.as_mut_ptr(), buf.len())
    };
    if len < 0 {
        return Err(std::io::Error::last_os_error());
    }

    // It is possible that the `len` value is smaller than as returned by the
    // first `llistxattr` call if somebody tampered with the file in the mean-
    // time. To avoid having spurious empty slices (as each following null byte
    // would cause the `split_inclusive` iterator to yield one), we trim the
    // buffer to the acutally returned length.
    //
    // Note that it is impossible for the `len` value to increase, as otherwise
    // the `llistxattr` would fail.
    let result = buf[..len as usize]
        .split_inclusive(|byte| *byte == 0)
        .map(|slice| {
            // SAFETY: The slice is guaranteed to be terminated with a null byte
            // and not contain any null bytes elsewhere because of the splitting
            // above. This holds true also for the last slice provided by the
            // iterator.
            let c_str = unsafe {
                std::ffi::CStr::from_ptr(slice.as_ptr())
            };

            std::ffi::OsStr::from_bytes(c_str.to_bytes()).to_os_string()
        })
        .collect();

    Ok(result)
}

#[cfg(test)]
mod tests {

    use std::fs::File;

    use super::*;

    // TODO: Write tests for symlinks.

    #[test]
    fn test_flags_non_existing() {
        let tempdir = tempfile::tempdir().unwrap();

        assert!(flags(tempdir.path().join("foo")).is_err());
    }

    #[test]
    fn test_flags_noatime() {
        // https://elixir.bootlin.com/linux/v5.8.14/source/include/uapi/linux/fs.h#L245
        const FS_NOATIME_FL: std::os::raw::c_long = 0x00000080;

        let tempdir = tempfile::tempdir().unwrap();
        let tempfile = File::create(tempdir.path().join("foo")).unwrap();

        unsafe {
            use std::os::unix::io::AsRawFd as _;
            let fd = tempfile.as_raw_fd();

            assert_eq!(ioctls::fs_ioc_setflags(fd, &FS_NOATIME_FL), 0);
        }

        let flags = flags(tempdir.path().join("foo")).unwrap();
        assert_eq!(flags & FS_NOATIME_FL as u32, FS_NOATIME_FL as u32);
    }

    #[test]
    fn ext_attr_names_none() {
        let tempfile = tempfile::NamedTempFile::new().unwrap();

        let ext_attr_names = ext_attr_names(tempfile.path()).unwrap();
        assert!(ext_attr_names.is_empty());
    }

    #[cfg(feature = "test-setfattr")]
    #[test]
    fn ext_attr_names_single() {
        let tempfile = tempfile::NamedTempFile::new().unwrap();
        setfattr(tempfile.path(), "user.foo", b"");

        let ext_attr_names = ext_attr_names(tempfile.path()).unwrap();
        assert_eq!(ext_attr_names, vec!["user.foo"]);
    }

    #[cfg(feature = "test-setfattr")]
    #[test]
    fn ext_attr_names_multiple() {
        let tempfile = tempfile::NamedTempFile::new().unwrap();
        setfattr(tempfile.path(), "user.foo", b"");
        setfattr(tempfile.path(), "user.bar", b"");
        setfattr(tempfile.path(), "user.baz", b"");

        let ext_attr_names = ext_attr_names(tempfile.path()).unwrap();
        assert_eq!(ext_attr_names, vec!["user.foo", "user.bar", "user.baz"]);
    }

    #[cfg(feature = "test-setfattr")]
    fn setfattr<P, S>(path: P, name: S, value: &[u8])
    where
        P: AsRef<std::path::Path>,
        S: AsRef<std::ffi::OsStr>,
    {
        use std::os::unix::ffi::OsStrExt as _;

        assert! {
            std::process::Command::new("setfattr")
                .arg("--name").arg(name)
                .arg("--value").arg(std::ffi::OsStr::from_bytes(value))
                .arg(path.as_ref().as_os_str())
                .status()
                .unwrap()
                .success()
        };
    }
}
<commit_msg>Create a wrapper for the `lgetxattr` call.<commit_after>// Copyright 2020 Google LLC
//
// Use of this source code is governed by an MIT-style license that can be found
// in the LICENSE file or at https://opensource.org/licenses/MIT.

//! Linux-specific utilities for working with the filesystem.

use std::path::Path;

// TODO: Document behaviour for symlinks.
/// Collects extended flags of the specified file.
///
/// The returned mask represents file attributes specific to the Linux extended
/// file system. Normally, they can be inspected using the `lsattr` command and
/// can be set through the `chattr` command.
///
/// See the [Wikipedia] article and the [man] page for more details.
///
/// [Wikipedia]: https://en.wikipedia.org/wiki/Chattr
/// [man]: https://linux.die.net/man/1/chattr
///
/// # Examples
///
/// ```no_run
/// const FS_FL_USER_VISIBLE: u32 = 0x0003DFFF;
///
/// let flags = rrg::fs::linux::flags("/tmp/foo").unwrap();
/// assert_eq!(flags & FS_FL_USER_VISIBLE, 0);
/// ```
pub fn flags<P>(path: P) -> std::io::Result<u32> where
    P: AsRef<Path>
{
    let file = std::fs::File::open(path)?;

    let mut flags = 0;
    let code = unsafe {
        // This block is safe: we simply pass a raw file descriptor (that is
        // valid until the end of the scope of this function) because this is
        // what the low-level API expects.
        use std::os::unix::io::AsRawFd as _;
        ioctls::fs_ioc_getflags(file.as_raw_fd(), &mut flags)
    };

    if code == 0 {
        Ok(flags as u32)
    } else {
        Err(std::io::Error::from_raw_os_error(code))
    }
}

pub fn ext_attr_names<P>(path: P) -> std::io::Result<Vec<std::ffi::OsString>>
where
    P: AsRef<Path>,
{
    extern "C" {
        // https://linux.die.net/man/2/llistxattr
        fn llistxattr(
            path: *const libc::c_char,
            list: *mut libc::c_char,
            size: libc::size_t,
        ) -> libc::ssize_t;
    }

    use std::os::unix::ffi::OsStrExt as _;

    let os_str_path = path.as_ref().as_os_str();
    let c_str_path = std::ffi::CString::new(os_str_path.as_bytes())
        // It is not possible to have a null byte in a Linux path.
        .expect("path with a null character");

    // SAFETY: The correctness of the path is guaranteed by conversion to the
    // `CString` type above. The rest is just is a FFI call respecting the spec.
    let len = unsafe {
        // First we call `llistxattr` with empty buffer to get the size of the
        // buffer that will collect the actual results.
        llistxattr(c_str_path.as_ptr(), std::ptr::null_mut(), 0)
    };
    if len < 0 {
        return Err(std::io::Error::last_os_error());
    }

    let mut buf = vec![0; len as usize];

    // SAFETY: The correctness of the path is guaranteed by conversion to the
    // `CString` type above. For remaining parameters we provide a buffer with
    // length obtained through the previous call and we explicitly supply its
    // length as well. In case the result length increases between the calls the
    // system will report an error but will not cause memory issues.
    let len = unsafe {
        // Now we can call `llistxattr` with the actual buffer of the size we
        // determined by the previous call.
        llistxattr(c_str_path.as_ptr(), buf.as_mut_ptr(), buf.len())
    };
    if len < 0 {
        return Err(std::io::Error::last_os_error());
    }

    // It is possible that the `len` value is smaller than as returned by the
    // first `llistxattr` call if somebody tampered with the file in the mean-
    // time. To avoid having spurious empty slices (as each following null byte
    // would cause the `split_inclusive` iterator to yield one), we trim the
    // buffer to the acutally returned length.
    //
    // Note that it is impossible for the `len` value to increase, as otherwise
    // the `llistxattr` would fail.
    let result = buf[..len as usize]
        .split_inclusive(|byte| *byte == 0)
        .map(|slice| {
            // SAFETY: The slice is guaranteed to be terminated with a null byte
            // and not contain any null bytes elsewhere because of the splitting
            // above. This holds true also for the last slice provided by the
            // iterator.
            let c_str = unsafe {
                std::ffi::CStr::from_ptr(slice.as_ptr())
            };

            std::ffi::OsStr::from_bytes(c_str.to_bytes()).to_os_string()
        })
        .collect();

    Ok(result)
}

pub fn ext_attr_value<P, S>(path: P, name: S) -> std::io::Result<Vec<u8>>
where
    P: AsRef<Path>,
    S: AsRef<std::ffi::OsStr>,
{
    extern "C" {
        // https://man7.org/linux/man-pages/man2/getxattr.2.html
        fn lgetxattr(
            path: *const libc::c_char,
            name: *const libc::c_char,
            value: *mut libc::c_void,
            size: libc::size_t,
        ) -> libc::ssize_t;
    }

    use std::os::unix::ffi::OsStrExt as _;

    let os_str_path = path.as_ref().as_os_str();
    let c_str_path = std::ffi::CString::new(os_str_path.as_bytes())
        // It is not possible to have a null byte in a Linux path.
        .expect("path with a null character");

    let c_str_name = std::ffi::CString::new(name.as_ref().as_bytes())
        // While `name` as returned by the `ext_attr_names` function cannot have
        // null bytes inside, we cannot guarantee that the user doesn't supply
        // a bogus string here. Thus, we have to do proper error handling here.
        .map_err(|error| {
            std::io::Error::new(std::io::ErrorKind::InvalidInput, error)
        })?;

    // SAFETY: The correctness of the path and name is guaranteed by conversion
    // to the `CString` type above. The rest is just is a FFI call respecting
    // the spec.
    let len = unsafe {
        // First we call `lgetxattr` with empty buffer to get the size of the
        // buffer that will collect the actual results.
        lgetxattr(c_str_path.as_ptr(), c_str_name.as_ptr(), std::ptr::null_mut(), 0)
    };
    if len < 0 {
        return Err(std::io::Error::last_os_error());
    }

    let mut buf = vec![0; len as usize];
    let buf_ptr = buf.as_mut_ptr() as *mut libc::c_void;

    // SAFETY: The correctness of the path and name is guaranteed by conversion
    // to the `CString` type above. For remaining parameters we provide a buffer
    // with length obtained through the previous call and we explicitly supply
    // its length as well. In case the result length increases between the calls
    // the system will report an error but will not cause memory issues.
    let len = unsafe {
        // Now we can call `lgetxattr` with the actual buffer of the size we
        // determined by the previous call.
        lgetxattr(c_str_path.as_ptr(), c_str_name.as_ptr(), buf_ptr, buf.len())
    };
    if len < 0 {
        return Err(std::io::Error::last_os_error());
    }

    // In very rare cases (if the users tampers with the file inbetween calls to
    // the `lgetxattr`), the length value can decrease. To avoid returning any
    // garbage at the end, we have to truncate the buffer to its real length.
    buf.truncate(len as usize);
    Ok(buf)
}

#[cfg(test)]
mod tests {

    use std::fs::File;

    use super::*;

    // TODO: Write tests for symlinks.

    #[test]
    fn test_flags_non_existing() {
        let tempdir = tempfile::tempdir().unwrap();

        assert!(flags(tempdir.path().join("foo")).is_err());
    }

    #[test]
    fn test_flags_noatime() {
        // https://elixir.bootlin.com/linux/v5.8.14/source/include/uapi/linux/fs.h#L245
        const FS_NOATIME_FL: std::os::raw::c_long = 0x00000080;

        let tempdir = tempfile::tempdir().unwrap();
        let tempfile = File::create(tempdir.path().join("foo")).unwrap();

        unsafe {
            use std::os::unix::io::AsRawFd as _;
            let fd = tempfile.as_raw_fd();

            assert_eq!(ioctls::fs_ioc_setflags(fd, &FS_NOATIME_FL), 0);
        }

        let flags = flags(tempdir.path().join("foo")).unwrap();
        assert_eq!(flags & FS_NOATIME_FL as u32, FS_NOATIME_FL as u32);
    }

    #[test]
    fn ext_attr_names_none() {
        let tempfile = tempfile::NamedTempFile::new().unwrap();

        let ext_attr_names = ext_attr_names(tempfile.path()).unwrap();
        assert!(ext_attr_names.is_empty());
    }

    #[cfg(feature = "test-setfattr")]
    #[test]
    fn ext_attr_names_single() {
        let tempfile = tempfile::NamedTempFile::new().unwrap();
        setfattr(tempfile.path(), "user.foo", b"");

        let ext_attr_names = ext_attr_names(tempfile.path()).unwrap();
        assert_eq!(ext_attr_names, vec!["user.foo"]);
    }

    #[cfg(feature = "test-setfattr")]
    #[test]
    fn ext_attr_names_multiple() {
        let tempfile = tempfile::NamedTempFile::new().unwrap();
        setfattr(tempfile.path(), "user.foo", b"");
        setfattr(tempfile.path(), "user.bar", b"");
        setfattr(tempfile.path(), "user.baz", b"");

        let ext_attr_names = ext_attr_names(tempfile.path()).unwrap();
        assert_eq!(ext_attr_names, vec!["user.foo", "user.bar", "user.baz"]);
    }

    #[test]
    fn ext_attr_value_not_existing() {
        let tempfile = tempfile::NamedTempFile::new().unwrap();

        let error = ext_attr_value(tempfile.path(), "user.foo").unwrap_err();
        assert_eq!(error.raw_os_error(), Some(libc::ENODATA));
    }

    #[cfg(feature = "test-setfattr")]
    #[test]
    fn ext_attr_value_single() {
        let tempfile = tempfile::NamedTempFile::new().unwrap();
        setfattr(tempfile.path(), "user.foo", b"bar");

        let value = ext_attr_value(tempfile.path(), "user.foo").unwrap();
        assert_eq!(value, b"bar");
    }

    #[cfg(feature = "test-setfattr")]
    #[test]
    fn ext_attr_value_multiple() {
        let tempfile = tempfile::NamedTempFile::new().unwrap();
        setfattr(tempfile.path(), "user.foo", b"quux");
        setfattr(tempfile.path(), "user.bar", b"norf");

        let foo_value = ext_attr_value(tempfile.path(), "user.foo").unwrap();
        assert_eq!(foo_value, b"quux");

        let bar_value = ext_attr_value(tempfile.path(), "user.bar").unwrap();
        assert_eq!(bar_value, b"norf");
    }

    #[cfg(feature = "test-setfattr")]
    fn setfattr<P, S>(path: P, name: S, value: &[u8])
    where
        P: AsRef<std::path::Path>,
        S: AsRef<std::ffi::OsStr>,
    {
        use std::os::unix::ffi::OsStrExt as _;

        assert! {
            std::process::Command::new("setfattr")
                .arg("--name").arg(name)
                .arg("--value").arg(std::ffi::OsStr::from_bytes(value))
                .arg(path.as_ref().as_os_str())
                .status()
                .unwrap()
                .success()
        };
    }
}
<|endoftext|>


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

use game::*;
use game::data::*;
use ecs::*;
use util::{LeakyReserver, Schedule};
use coord::Coord;

pub struct EntityIdReserver(RefCell<LeakyReserver<EntityId>>);

impl EntityIdReserver {
    pub fn new() -> Self {
        EntityIdReserver(RefCell::new(LeakyReserver::new()))
    }

    pub fn new_id(&self) -> EntityId {
        self.0.borrow_mut().reserve()
    }
}

#[derive(Serialize, Deserialize)]
pub struct SerializableEntityIdReserver(LeakyReserver<EntityId>);

impl From<EntityIdReserver> for SerializableEntityIdReserver {
    fn from(r: EntityIdReserver) -> Self {
        SerializableEntityIdReserver(r.0.into_inner())
    }
}

impl From<SerializableEntityIdReserver> for EntityIdReserver {
    fn from(r: SerializableEntityIdReserver) -> Self {
        EntityIdReserver(RefCell::new(r.0))
    }
}

enum MainMenuSelection {
    NewGame,
    Quit,
    Continue,
    SaveAndQuit,
}

pub enum GameOverReason {
    PlayerDied,
}

pub enum ExitReason {
    GameOver(GameOverReason),
    Pause,
}

pub struct GameCtx<Renderer: KnowledgeRenderer, Input: InputSource> {
    renderer: RefCell<Renderer>,
    input_source: Input,
    pc_observer: Shadowcast,
    behaviour_ctx: BehaviourCtx<Renderer>,
    rule_reactions: Vec<Reaction>,
    ecs_action: EcsAction,
    action_schedule: Schedule<ActionArgs>,
    width: usize,
    height: usize,
    rng: GameRng,
    language: Box<Language>,
}

#[derive(Clone, Copy, Serialize, Deserialize)]
pub struct GlobalIds {
    pc_id: EntityId,
    level_id: LevelId,
}

pub struct GameState {
    levels: LevelTable,
    global_ids: Option<GlobalIds>,
    entity_ids: EntityIdReserver,
    turn_id: u64,
    action_id: u64,
}

impl GameState {
    pub fn new() -> Self {
        GameState {
            levels: LevelTable::new(),
            global_ids: None,
            entity_ids: EntityIdReserver::new(),
            turn_id: 0,
            action_id: 0,
        }
    }
}

#[derive(Serialize, Deserialize)]
pub struct SerializableGameState {
    levels: SerializableLevelTable,
    global_ids: Option<GlobalIds>,
    entity_ids: SerializableEntityIdReserver,
    turn_id: u64,
    action_id: u64,
}

impl From<GameState> for SerializableGameState {
    fn from(game_state: GameState) -> Self {
        let GameState { levels, global_ids, entity_ids, turn_id, action_id } = game_state;
        SerializableGameState {
            levels: SerializableLevelTable::from(levels),
            global_ids: global_ids,
            entity_ids: SerializableEntityIdReserver::from(entity_ids),
            turn_id: turn_id,
            action_id: action_id,
        }
    }
}

impl From<SerializableGameState> for GameState {
    fn from(game_state: SerializableGameState) -> Self {
        let SerializableGameState { levels, global_ids, entity_ids, turn_id, action_id } = game_state;
        GameState {
            levels: LevelTable::from(levels),
            global_ids: global_ids,
            entity_ids: EntityIdReserver::from(entity_ids),
            turn_id: turn_id,
            action_id: action_id,
        }
    }
}

impl<Renderer: KnowledgeRenderer, Input: 'static + InputSource + Clone> GameCtx<Renderer, Input> {
    pub fn new(renderer: Renderer, input_source: Input, seed: usize, width: usize, height: usize) -> Self {
        GameCtx {
            renderer: RefCell::new(renderer),
            input_source: input_source.clone(),
            pc_observer: Shadowcast::new(),
            behaviour_ctx: BehaviourCtx::new(input_source),
            rule_reactions: Vec::new(),
            ecs_action: EcsAction::new(),
            action_schedule: Schedule::new(),
            width: width,
            height: height,
            rng: GameRng::new(seed),
            language: Box::new(languages::English),
        }
    }

    pub fn run(&mut self, args: Arguments) -> GameResult<()> {

        let mut current_game_state = save_file::load(args.user_path.as_path());

        loop {

            self.renderer.borrow_mut().reset_buffers();

            let control_map = control_spec::from_file(args.user_path.join(user_files::CONTROL)).unwrap_or_default();

            let mut menu = Menu::new();

            if current_game_state.is_some() {
                menu.push(MenuItem::new(MenuMessageType::Continue, MainMenuSelection::Continue));
            }

            menu.push(MenuItem::new(MenuMessageType::NewGame, MainMenuSelection::NewGame));

            if current_game_state.is_some() {
                menu.push(MenuItem::new(MenuMessageType::SaveAndQuit, MainMenuSelection::SaveAndQuit));
            } else {
                menu.push(MenuItem::new(MenuMessageType::Quit, MainMenuSelection::Quit));
            }

            let item = menu_operation::run(
                self.renderer.borrow_mut().deref_mut(),
                &mut self.input_source,
                Some(MessageType::Title),
                &self.language,
                menu);

            let mut game_state = match item {
                MainMenuSelection::Quit => {
                    return Ok(());
                }
                MainMenuSelection::SaveAndQuit => {
                    let game_state = current_game_state.take().expect("Missing game state");
                    save_file::save(args.user_path.as_path(), game_state);
                    return Ok(());
                }
                MainMenuSelection::NewGame => {
                    let mut game_state = GameState::new();

                    self.init_demo(&mut game_state);
                    self.intro_message(&control_map);
                    self.welcome_message(&game_state);

                    game_state
                }
                MainMenuSelection::Continue => current_game_state.take().expect("Missing game state"),
            };

            Self::install_control_map(&mut game_state, control_map);

            match self.game_loop(&mut game_state)? {
                ExitReason::Pause => {
                    current_game_state = Some(game_state);
                }
                ExitReason::GameOver(reason) => {
                    match reason {
                        GameOverReason::PlayerDied => {
                            self.death_message(&game_state);
                            self.input_source.next_input();
                        }
                    }
                    current_game_state = None;
                }
            }

        }
    }

    fn install_control_map(game_state: &mut GameState, control_map: ControlMap) {
        let GlobalIds { pc_id, level_id } = game_state.global_ids.expect("Uninitialised game state");

        let level = game_state.levels.level_mut(level_id);
        level.ecs.insert_control_map(pc_id, control_map);
    }

    fn game_loop(&mut self, game_state: &mut GameState) -> GameResult<ExitReason> {
        loop {

            let GlobalIds { pc_id, level_id } = game_state.global_ids.expect("Uninitialised game state");

            game_state.turn_id += 1;

            let resolution = {
                let level = game_state.levels.level_mut(level_id);
                if let Some(turn_event) = level.turn_schedule.next() {

                    TurnEnv {
                        turn_id: game_state.turn_id,
                        action_id: &mut game_state.action_id,
                        level_id: level_id,
                        entity_id: turn_event.event,
                        pc_id: pc_id,
                        renderer: &self.renderer,
                        ecs: &mut level.ecs,
                        spatial_hash: &mut level.spatial_hash,
                        behaviour_ctx: &self.behaviour_ctx,
                        rule_reactions: &mut self.rule_reactions,
                        ecs_action: &mut self.ecs_action,
                        action_schedule: &mut self.action_schedule,
                        turn_schedule: &mut level.turn_schedule,
                        pc_observer: &self.pc_observer,
                        entity_ids: &game_state.entity_ids,
                        rng: &self.rng,
                        language: &self.language,
                    }.turn()?

                } else {
                    return Err(GameError::ScheduleEmpty);
                }
            };

            match resolution {
                TurnResolution::Pause(entity_id) => {
                    let level = game_state.levels.level_mut(level_id);
                    let old_ticket = level.ecs.schedule_ticket(entity_id).expect("Expected schedule_ticket component");
                    let new_ticket = level.turn_schedule.insert_with_ticket(entity_id, 0, old_ticket);
                    level.ecs.insert_schedule_ticket(entity_id, new_ticket);
                    return Ok(ExitReason::Pause);
                }
                TurnResolution::Schedule(entity_id, delay) => {
                    let level = game_state.levels.level_mut(level_id);
                    let ticket = level.turn_schedule.insert(entity_id, delay);
                    level.ecs.insert_schedule_ticket(entity_id, ticket);
                }
                TurnResolution::LevelSwitch(level_switch) => {
                    self.switch_level(level_switch, game_state);
                }
                TurnResolution::GameOver(reason) => {
                    return Ok(ExitReason::GameOver(reason));
                }
            }
        }
    }

    fn welcome_message(&self, game_state: &GameState) {
        self.add_message(game_state, MessageType::Welcome);
    }

    fn death_message(&self, game_state: &GameState) {
        let GlobalIds { pc_id, level_id } = game_state.global_ids.expect("Unitialised game state");
        self.add_message(game_state, MessageType::YouDied);
        let entity = game_state.levels.level(level_id).ecs.entity(pc_id);
        self.renderer.borrow_mut().update_and_publish_all_windows_for_entity_with_overlay(
            game_state.action_id,
            level_id,
            entity,
            &self.language,
            &RenderOverlay::Death);
    }

    fn add_message(&self, game_state: &GameState, message: MessageType) {
        let GlobalIds { pc_id, level_id } = game_state.global_ids.expect("Unitialised game state");

        let ref ecs = game_state.levels.level(level_id).ecs;
        ecs.message_log_borrow_mut(pc_id).expect("Expected message log component").add(message);
    }

    fn intro_message(&mut self, control_map: &ControlMap) {

        let mut message = Message::new();

        self.language.translate(MessageType::Intro, &mut message);
        message.push(MessagePart::Newline);
        message.push(MessagePart::Newline);
        self.language.translate(MessageType::PressAnyKey, &mut message);
        message.push(MessagePart::Newline);
        message.push(MessagePart::Newline);
        message.push(MessagePart::Newline);
        self.language.translate_controls(control_map, &mut message);

        display_message_scrolling(self.renderer.borrow_mut().deref_mut(), &mut self.input_source, &message, true);
    }

    fn init_demo(&mut self, game_state: &mut GameState) {

        let pc_id = game_state.entity_ids.new_id();

        let mut action = EcsAction::new();
        prototypes::pc(action.entity_mut(pc_id), Coord::new(0, 0));

        let level = Level::new_with_pc(TerrainType::DemoA,
                                       pc_id,
                                       &mut action,
                                       &game_state.entity_ids,
                                       &self.rng,
                                       game_state.action_id);

        game_state.action_id += 1;

        let level_id = game_state.levels.add_level(level);

        game_state.global_ids = Some(GlobalIds {
            pc_id: pc_id,
            level_id: level_id,
        });
    }

    fn switch_level(&mut self, level_switch: LevelSwitch, game_state: &mut GameState) {
        let global_ids = game_state.global_ids.as_mut().expect("Unitialised game state");

        let mut pc_insert = EcsAction::new();
        let mut pc_remove = EcsAction::new();

        {
            let current_level = game_state.levels.level_mut(global_ids.level_id);
            pc_remove.remove_entity_by_id(global_ids.pc_id, &current_level.ecs);
            current_level.commit_into(&mut pc_remove, &mut pc_insert, game_state.action_id);
        }

        game_state.action_id += 1;

        let new_level = Level::new_with_pc(level_switch.terrain_type,
                                           global_ids.pc_id,
                                           &mut pc_insert,
                                           &game_state.entity_ids,
                                           &self.rng,
                                           game_state.action_id);
        game_state.action_id += 1;

        global_ids.level_id = game_state.levels.add_level(new_level);
    }
}
<commit_msg>Delete save file when game ends<commit_after>use std::cell::RefCell;
use std::ops::DerefMut;

use game::*;
use game::data::*;
use ecs::*;
use util::{LeakyReserver, Schedule};
use coord::Coord;

pub struct EntityIdReserver(RefCell<LeakyReserver<EntityId>>);

impl EntityIdReserver {
    pub fn new() -> Self {
        EntityIdReserver(RefCell::new(LeakyReserver::new()))
    }

    pub fn new_id(&self) -> EntityId {
        self.0.borrow_mut().reserve()
    }
}

#[derive(Serialize, Deserialize)]
pub struct SerializableEntityIdReserver(LeakyReserver<EntityId>);

impl From<EntityIdReserver> for SerializableEntityIdReserver {
    fn from(r: EntityIdReserver) -> Self {
        SerializableEntityIdReserver(r.0.into_inner())
    }
}

impl From<SerializableEntityIdReserver> for EntityIdReserver {
    fn from(r: SerializableEntityIdReserver) -> Self {
        EntityIdReserver(RefCell::new(r.0))
    }
}

enum MainMenuSelection {
    NewGame,
    Quit,
    Continue,
    SaveAndQuit,
}

pub enum GameOverReason {
    PlayerDied,
}

pub enum ExitReason {
    GameOver(GameOverReason),
    Pause,
}

pub struct GameCtx<Renderer: KnowledgeRenderer, Input: InputSource> {
    renderer: RefCell<Renderer>,
    input_source: Input,
    pc_observer: Shadowcast,
    behaviour_ctx: BehaviourCtx<Renderer>,
    rule_reactions: Vec<Reaction>,
    ecs_action: EcsAction,
    action_schedule: Schedule<ActionArgs>,
    width: usize,
    height: usize,
    rng: GameRng,
    language: Box<Language>,
}

#[derive(Clone, Copy, Serialize, Deserialize)]
pub struct GlobalIds {
    pc_id: EntityId,
    level_id: LevelId,
}

pub struct GameState {
    levels: LevelTable,
    global_ids: Option<GlobalIds>,
    entity_ids: EntityIdReserver,
    turn_id: u64,
    action_id: u64,
}

impl GameState {
    pub fn new() -> Self {
        GameState {
            levels: LevelTable::new(),
            global_ids: None,
            entity_ids: EntityIdReserver::new(),
            turn_id: 0,
            action_id: 0,
        }
    }
}

#[derive(Serialize, Deserialize)]
pub struct SerializableGameState {
    levels: SerializableLevelTable,
    global_ids: Option<GlobalIds>,
    entity_ids: SerializableEntityIdReserver,
    turn_id: u64,
    action_id: u64,
}

impl From<GameState> for SerializableGameState {
    fn from(game_state: GameState) -> Self {
        let GameState { levels, global_ids, entity_ids, turn_id, action_id } = game_state;
        SerializableGameState {
            levels: SerializableLevelTable::from(levels),
            global_ids: global_ids,
            entity_ids: SerializableEntityIdReserver::from(entity_ids),
            turn_id: turn_id,
            action_id: action_id,
        }
    }
}

impl From<SerializableGameState> for GameState {
    fn from(game_state: SerializableGameState) -> Self {
        let SerializableGameState { levels, global_ids, entity_ids, turn_id, action_id } = game_state;
        GameState {
            levels: LevelTable::from(levels),
            global_ids: global_ids,
            entity_ids: EntityIdReserver::from(entity_ids),
            turn_id: turn_id,
            action_id: action_id,
        }
    }
}

impl<Renderer: KnowledgeRenderer, Input: 'static + InputSource + Clone> GameCtx<Renderer, Input> {
    pub fn new(renderer: Renderer, input_source: Input, seed: usize, width: usize, height: usize) -> Self {
        GameCtx {
            renderer: RefCell::new(renderer),
            input_source: input_source.clone(),
            pc_observer: Shadowcast::new(),
            behaviour_ctx: BehaviourCtx::new(input_source),
            rule_reactions: Vec::new(),
            ecs_action: EcsAction::new(),
            action_schedule: Schedule::new(),
            width: width,
            height: height,
            rng: GameRng::new(seed),
            language: Box::new(languages::English),
        }
    }

    pub fn run(&mut self, args: Arguments) -> GameResult<()> {

        let mut current_game_state = save_file::load(args.user_path.as_path());

        loop {

            self.renderer.borrow_mut().reset_buffers();

            let control_map = control_spec::from_file(args.user_path.join(user_files::CONTROL)).unwrap_or_default();

            let mut menu = Menu::new();

            if current_game_state.is_some() {
                menu.push(MenuItem::new(MenuMessageType::Continue, MainMenuSelection::Continue));
            }

            menu.push(MenuItem::new(MenuMessageType::NewGame, MainMenuSelection::NewGame));

            if current_game_state.is_some() {
                menu.push(MenuItem::new(MenuMessageType::SaveAndQuit, MainMenuSelection::SaveAndQuit));
            } else {
                menu.push(MenuItem::new(MenuMessageType::Quit, MainMenuSelection::Quit));
            }

            let item = menu_operation::run(
                self.renderer.borrow_mut().deref_mut(),
                &mut self.input_source,
                Some(MessageType::Title),
                &self.language,
                menu);

            let mut game_state = match item {
                MainMenuSelection::Quit => {
                    return Ok(());
                }
                MainMenuSelection::SaveAndQuit => {
                    let game_state = current_game_state.take().expect("Missing game state");
                    save_file::save(args.user_path.as_path(), game_state);
                    return Ok(());
                }
                MainMenuSelection::NewGame => {
                    let mut game_state = GameState::new();

                    self.init_demo(&mut game_state);
                    self.intro_message(&control_map);
                    self.welcome_message(&game_state);

                    game_state
                }
                MainMenuSelection::Continue => current_game_state.take().expect("Missing game state"),
            };

            Self::install_control_map(&mut game_state, control_map);

            match self.game_loop(&mut game_state)? {
                ExitReason::Pause => {
                    current_game_state = Some(game_state);
                }
                ExitReason::GameOver(reason) => {
                    match reason {
                        GameOverReason::PlayerDied => {
                            self.death_message(&game_state);
                            self.input_source.next_input();
                        }
                    }
                    current_game_state = None;
                    save_file::delete(args.user_path.as_path());
                }
            }

        }
    }

    fn install_control_map(game_state: &mut GameState, control_map: ControlMap) {
        let GlobalIds { pc_id, level_id } = game_state.global_ids.expect("Uninitialised game state");

        let level = game_state.levels.level_mut(level_id);
        level.ecs.insert_control_map(pc_id, control_map);
    }

    fn game_loop(&mut self, game_state: &mut GameState) -> GameResult<ExitReason> {
        loop {

            let GlobalIds { pc_id, level_id } = game_state.global_ids.expect("Uninitialised game state");

            game_state.turn_id += 1;

            let resolution = {
                let level = game_state.levels.level_mut(level_id);
                if let Some(turn_event) = level.turn_schedule.next() {

                    TurnEnv {
                        turn_id: game_state.turn_id,
                        action_id: &mut game_state.action_id,
                        level_id: level_id,
                        entity_id: turn_event.event,
                        pc_id: pc_id,
                        renderer: &self.renderer,
                        ecs: &mut level.ecs,
                        spatial_hash: &mut level.spatial_hash,
                        behaviour_ctx: &self.behaviour_ctx,
                        rule_reactions: &mut self.rule_reactions,
                        ecs_action: &mut self.ecs_action,
                        action_schedule: &mut self.action_schedule,
                        turn_schedule: &mut level.turn_schedule,
                        pc_observer: &self.pc_observer,
                        entity_ids: &game_state.entity_ids,
                        rng: &self.rng,
                        language: &self.language,
                    }.turn()?

                } else {
                    return Err(GameError::ScheduleEmpty);
                }
            };

            match resolution {
                TurnResolution::Pause(entity_id) => {
                    let level = game_state.levels.level_mut(level_id);
                    let old_ticket = level.ecs.schedule_ticket(entity_id).expect("Expected schedule_ticket component");
                    let new_ticket = level.turn_schedule.insert_with_ticket(entity_id, 0, old_ticket);
                    level.ecs.insert_schedule_ticket(entity_id, new_ticket);
                    return Ok(ExitReason::Pause);
                }
                TurnResolution::Schedule(entity_id, delay) => {
                    let level = game_state.levels.level_mut(level_id);
                    let ticket = level.turn_schedule.insert(entity_id, delay);
                    level.ecs.insert_schedule_ticket(entity_id, ticket);
                }
                TurnResolution::LevelSwitch(level_switch) => {
                    self.switch_level(level_switch, game_state);
                }
                TurnResolution::GameOver(reason) => {
                    return Ok(ExitReason::GameOver(reason));
                }
            }
        }
    }

    fn welcome_message(&self, game_state: &GameState) {
        self.add_message(game_state, MessageType::Welcome);
    }

    fn death_message(&self, game_state: &GameState) {
        let GlobalIds { pc_id, level_id } = game_state.global_ids.expect("Unitialised game state");
        self.add_message(game_state, MessageType::YouDied);
        let entity = game_state.levels.level(level_id).ecs.entity(pc_id);
        self.renderer.borrow_mut().update_and_publish_all_windows_for_entity_with_overlay(
            game_state.action_id,
            level_id,
            entity,
            &self.language,
            &RenderOverlay::Death);
    }

    fn add_message(&self, game_state: &GameState, message: MessageType) {
        let GlobalIds { pc_id, level_id } = game_state.global_ids.expect("Unitialised game state");

        let ref ecs = game_state.levels.level(level_id).ecs;
        ecs.message_log_borrow_mut(pc_id).expect("Expected message log component").add(message);
    }

    fn intro_message(&mut self, control_map: &ControlMap) {

        let mut message = Message::new();

        self.language.translate(MessageType::Intro, &mut message);
        message.push(MessagePart::Newline);
        message.push(MessagePart::Newline);
        self.language.translate(MessageType::PressAnyKey, &mut message);
        message.push(MessagePart::Newline);
        message.push(MessagePart::Newline);
        message.push(MessagePart::Newline);
        self.language.translate_controls(control_map, &mut message);

        display_message_scrolling(self.renderer.borrow_mut().deref_mut(), &mut self.input_source, &message, true);
    }

    fn init_demo(&mut self, game_state: &mut GameState) {

        let pc_id = game_state.entity_ids.new_id();

        let mut action = EcsAction::new();
        prototypes::pc(action.entity_mut(pc_id), Coord::new(0, 0));

        let level = Level::new_with_pc(TerrainType::DemoA,
                                       pc_id,
                                       &mut action,
                                       &game_state.entity_ids,
                                       &self.rng,
                                       game_state.action_id);

        game_state.action_id += 1;

        let level_id = game_state.levels.add_level(level);

        game_state.global_ids = Some(GlobalIds {
            pc_id: pc_id,
            level_id: level_id,
        });
    }

    fn switch_level(&mut self, level_switch: LevelSwitch, game_state: &mut GameState) {
        let global_ids = game_state.global_ids.as_mut().expect("Unitialised game state");

        let mut pc_insert = EcsAction::new();
        let mut pc_remove = EcsAction::new();

        {
            let current_level = game_state.levels.level_mut(global_ids.level_id);
            pc_remove.remove_entity_by_id(global_ids.pc_id, &current_level.ecs);
            current_level.commit_into(&mut pc_remove, &mut pc_insert, game_state.action_id);
        }

        game_state.action_id += 1;

        let new_level = Level::new_with_pc(level_switch.terrain_type,
                                           global_ids.pc_id,
                                           &mut pc_insert,
                                           &game_state.entity_ids,
                                           &self.rng,
                                           game_state.action_id);
        game_state.action_id += 1;

        global_ids.level_id = game_state.levels.add_level(new_level);
    }
}
<|endoftext|>


<commit_before>//! Implements the game representation

use super::piece::Piece;
use super::color::{Color};
use super::square::{File,Square};
use super::moves::Plies;
use super::board::Board;
use super::castle::{CastlingData,Side};

/// A complete representation of a chess position.
#[deriving(Clone)]
pub struct Position {
    data: Board,
    side_to_move: Color,
    castling: CastlingData,
    en_passant: Option<File>,
    ply_count: Plies,
}
impl Position {
    pub fn new() -> Position {
        Position {
            data: Board::new(),
            side_to_move: Color::White,
            castling: CastlingData::new(),
            en_passant: None,
            ply_count: Plies(0),
        }
    }
    pub fn at(&self, s: Square) -> Option<Piece> {
        self.data.at(s)
    }
    pub fn set_at_mut(&mut self, s: Square, val: Piece) {
        self.data.set_at_mut(s, val);
    }
    pub fn remove_at_mut(&mut self, s: Square) {
        self.data.remove_at_mut(s);
    }
    pub fn side_to_move(&self) -> Color {
        self.side_to_move
    }
    pub fn set_side_to_move_mut(&mut self, c: Color) {
        self.side_to_move = c;
    }
    pub fn can_castle(&self, side: Side, c: Color) -> bool {
        self.castling.get(side, c)
    }
    pub fn can_castle_now(&self, side: Side, c: Color) -> bool {
        //TODO Implement Position.can_castle_now().
        unimplemented!();
    }
    pub fn set_castle_mut(&mut self, side:Side, c:Color, val: bool) {
        self.castling.set_mut(side, c, val);
    }
    pub fn en_passant(&self) -> Option<File> {
        self.en_passant
    }
    pub fn has_en_passant(&self) -> bool {
        self.en_passant.is_some()
    }
    pub fn set_en_passant_mut(&mut self, val: Option<File>) {
        self.en_passant = val;
    }
    pub fn ply_count(&self) -> Plies {
        self.ply_count
    }
    pub fn set_ply_count_mut(&mut self, val: Plies) {
        self.ply_count = val;
    }
}
<commit_msg>Implement can_castle_now().<commit_after>//! Implements the game representation

use super::piece::Piece;
use super::color::{Color};
use super::square::{File,Square};
use super::moves::Plies;
use super::board::Board;
use super::castle::{mod,CastlingData,Side};

/// A complete representation of a chess position.
#[deriving(Clone)]
pub struct Position {
    data: Board,
    side_to_move: Color,
    castling: CastlingData,
    en_passant: Option<File>,
    ply_count: Plies,
}
impl Position {
    pub fn new() -> Position {
        Position {
            data: Board::new(),
            side_to_move: Color::White,
            castling: CastlingData::new(),
            en_passant: None,
            ply_count: Plies(0),
        }
    }
    pub fn at(&self, s: Square) -> Option<Piece> {
        self.data.at(s)
    }
    pub fn set_at_mut(&mut self, s: Square, val: Piece) {
        self.data.set_at_mut(s, val);
    }
    pub fn remove_at_mut(&mut self, s: Square) {
        self.data.remove_at_mut(s);
    }
    pub fn side_to_move(&self) -> Color {
        self.side_to_move
    }
    pub fn set_side_to_move_mut(&mut self, c: Color) {
        self.side_to_move = c;
    }
    pub fn can_castle(&self, side: Side, c: Color) -> bool {
        self.castling.get(side, c)
    }
    // Does not check for castling out of check, through check, or into check.
    pub fn can_castle_now(&self, side: Side, c: Color) -> bool {
        self.can_castle(side, c) &&
            castle::require_empty_squares(side, c).iter().all( |x| self.at(*x) == None )
    }
    pub fn set_castle_mut(&mut self, side:Side, c:Color, val: bool) {
        self.castling.set_mut(side, c, val);
    }
    pub fn en_passant(&self) -> Option<File> {
        self.en_passant
    }
    pub fn has_en_passant(&self) -> bool {
        self.en_passant.is_some()
    }
    pub fn set_en_passant_mut(&mut self, val: Option<File>) {
        self.en_passant = val;
    }
    pub fn ply_count(&self) -> Plies {
        self.ply_count
    }
    pub fn set_ply_count_mut(&mut self, val: Plies) {
        self.ply_count = val;
    }
}
<|endoftext|>


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

use graphics;
use piston_window::*;

use geometry::{Point, Vector};
use turtle::{Turtle, TurtleProgram};

const WHITE: [f32; 4] = [1.0, 1.0, 1.0, 1.0];
const BLACK: [f32; 4] = [0.0, 0.0, 0.0, 1.0];

#[derive(Debug)]
struct WindowHandler {
    redraw: [bool; 2],
}

/// Renders a TurtleProgram in a PistonWindow.
pub fn run(program: &TurtleProgram) {

    let opengl = OpenGL::V3_2;
    let window: PistonWindow = WindowSettings::new("Fractal", [800, 600])
                                   .opengl(opengl)
                                   .exit_on_esc(true)
                                   .build()
                                   .unwrap_or_else(|e| panic!("Failed to build Window: {}", e));

    let mut window_handler = WindowHandler { redraw: [true; 2] };

    let mut frame_num: u32 = 0;
    let mut old_size: Size = Size {
        width: 0,
        height: 0,
    };
    for e in window {
        e.draw_2d(|context, gfx| {
            let size = e.size();
            // Size doesn't implement PartialEq, so we have to check ourselves.
            if size.width != old_size.width || size.height != old_size.height {
                println!("resized");
                old_size = size;
                window_handler.window_resized();
            }
            frame_num += 1;
            println!("Render frame {}, window: {:?}", frame_num, size);
            window_handler.render_frame(size, context, gfx, program, frame_num);
        });
        //     // Some(Event::Input(i)) => {
        //     //     match i {
        //     //         Input::Press(Button::Keyboard(k)) => {
        //     //             match k {
        //     //                 Key::Up => {}
        //     //                 Key::Down => {}
        //     //                 _ => {}
        //     //             }
        //     //         }
        //     //         _ => {}
        //     //     }
        //     // }
        //     _ => {}
        // }
    }
}

impl WindowHandler {
    /// When the window is resized, we need to plan to re-render.
    pub fn window_resized(&mut self) {
        self.redraw[0] = true;
        self.redraw[1] = true;
    }

    pub fn render_frame<G, T>(&mut self,
                              window_size: Size,
                              context: graphics::context::Context,
                              gfx: &mut G,
                              program: &TurtleProgram,
                              frame_num: u32)
        where T: ImageSize,
              G: Graphics<Texture = T>
    {
        use graphics::*;
        let redraw = self.redraw[(frame_num % 2) as usize];
        if redraw {
            println!("Redrawing frame {}", frame_num % 2);
            clear(WHITE, gfx);

            let mut turtle = GlTurtle::new(gfx, window_size, context);
            WindowHandler::turtledraw(program, &mut turtle);

            println!("Done redrawing frame");
            self.redraw[(frame_num % 2) as usize] = false;
        }
    }

    fn turtledraw(program: &TurtleProgram, turtle: &mut Turtle) {
        let init_turtle_steps = program.init_turtle();

        for action in init_turtle_steps {
            turtle.perform(action)
        }

        for action in program.turtle_program_iter() {
            turtle.perform(action)
        }
        turtle.up();
    }
}

/// An implementation of a Turtle within an OpenGL (rather, a gfx) context.
pub struct GlTurtle<'a, G, T>
    where T: ImageSize,
          G: Graphics<Texture = T> + 'a
{
    gfx: &'a mut G,
    window_size: Size,
    context: graphics::context::Context,

    position: Point,
    angle: f64,
    down: bool,
}

impl<'a, G, T> GlTurtle<'a, G, T>
    where T: ImageSize,
          G: Graphics<Texture = T> + 'a
{
    pub fn new(gfx: &'a mut G,
               window_size: Size,
               context: graphics::context::Context)
               -> GlTurtle<'a, G, T> {
        GlTurtle {
            gfx: gfx,
            window_size: window_size,
            context: context,
            position: Point { x: 0.0, y: 0.0 },
            angle: 0.0,
            down: true,
        }
    }
}

impl<'a, G, T> Turtle for GlTurtle<'a, G, T>
    where T: ImageSize,
          G: Graphics<Texture = T> + 'a
{
    fn forward(&mut self, distance: f64) {
        use graphics::*;

        let old_pos = self.position;
        let new_pos = self.position.point_at(Vector {
            direction: self.angle,
            magnitude: distance,
        });

        if self.down {
            let screen_width = self.window_size.width;
            let screen_height = self.window_size.height;

            let startx = (screen_width / 4) as f64;
            let starty = (screen_height / 2) as f64;
            let endx = (3 * screen_width / 4) as f64;
            // let endy = (screen_height / 2) as f64;

            let linesize = (startx - endx).abs() as f64;

            let transform = self.context
                                .transform
                                .trans(startx, starty)
                                .zoom(linesize)
                                .flip_v()
                                .trans(0.0, 0.0);

            // Line::new(BLACK, 1.0).draw([old_pos.x*linesize, old_pos.y*linesize,
            // new_pos.x*linesize, new_pos.y*linesize],
            Line::new(BLACK, 0.5 / linesize).draw([old_pos.x, old_pos.y, new_pos.x, new_pos.y],
                                                  default_draw_state(),
                                                  transform,
                                                  self.gfx);
        }

        self.position = new_pos;
    }

    fn set_pos(&mut self, new_pos: Point) {
        self.position = new_pos;
    }

    fn set_rad(&mut self, new_rad: f64) {
        self.angle = new_rad;
    }

    fn turn_rad(&mut self, radians: f64) {
        use std::f64::consts::PI;
        self.angle = (self.angle + radians) % (2.0 * PI);
    }

    fn down(&mut self) {
        self.down = true;
    }

    fn up(&mut self) {
        self.down = false;
    }
}
<commit_msg>Split WindowHandler into a trait and an implementation.<commit_after>// Copyright (c) 2015-2016 William (B.J.) Snow Orvis
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use graphics;
use piston_window::*;

use geometry::{Point, Vector};
use turtle::{Turtle, TurtleProgram};

const WHITE: [f32; 4] = [1.0, 1.0, 1.0, 1.0];
const BLACK: [f32; 4] = [0.0, 0.0, 0.0, 1.0];

/// Renders a TurtleProgram in a PistonWindow.
pub fn run(program: &TurtleProgram) {

    let opengl = OpenGL::V3_2;
    let window: PistonWindow = WindowSettings::new("Fractal", [800, 600])
                                   .opengl(opengl)
                                   .exit_on_esc(true)
                                   .build()
                                   .unwrap_or_else(|e| panic!("Failed to build Window: {}", e));

    let mut window_handler = DoubleBufferedWindowHandler::new();

    let mut frame_num: u32 = 0;
    let mut old_size: Size = Size {
        width: 0,
        height: 0,
    };
    for e in window {
        e.draw_2d(|context, gfx| {
            let size = e.size();
            // Size doesn't implement PartialEq, so we have to check ourselves.
            if size.width != old_size.width || size.height != old_size.height {
                println!("resized");
                old_size = size;
                window_handler.window_resized();
            }
            frame_num += 1;
            println!("Render frame {}, window: {:?}", frame_num, size);
            window_handler.render_frame(size, context, gfx, program, frame_num);
        });
        //     // Some(Event::Input(i)) => {
        //     //     match i {
        //     //         Input::Press(Button::Keyboard(k)) => {
        //     //             match k {
        //     //                 Key::Up => {}
        //     //                 Key::Down => {}
        //     //                 _ => {}
        //     //             }
        //     //         }
        //     //         _ => {}
        //     //     }
        //     // }
        //     _ => {}
        // }
    }
}

pub trait WindowHandler {
    /// When the window is resized, we may need to plan to re-render.
    fn window_resized(&mut self);

    /// Render a frame.
    fn render_frame<G, T>(&mut self,
                          window_size: Size,
                          context: graphics::context::Context,
                          gfx: &mut G,
                          program: &TurtleProgram,
                          frame_num: u32)
        where T: ImageSize,
              G: Graphics<Texture = T>;
}

/// WindowHandler that renders an entire turtle program per-frame, and optimizes re-renders
/// by only rendering twice (once for each buffer).
#[derive(Debug)]
struct DoubleBufferedWindowHandler {
    /// Whether we need to re-render for double-buffered frames.
    redraw: [bool; 2],
}

impl DoubleBufferedWindowHandler {
    pub fn new() -> DoubleBufferedWindowHandler {
        DoubleBufferedWindowHandler { redraw: [true; 2] }
    }

    fn turtledraw(program: &TurtleProgram, turtle: &mut Turtle) {
        let init_turtle_steps = program.init_turtle();

        for action in init_turtle_steps {
            turtle.perform(action)
        }

        for action in program.turtle_program_iter() {
            turtle.perform(action)
        }
        turtle.up();
    }
}

impl WindowHandler for DoubleBufferedWindowHandler {
    fn window_resized(&mut self) {
        self.redraw[0] = true;
        self.redraw[1] = true;
    }

    fn render_frame<G, T>(&mut self,
                          window_size: Size,
                          context: graphics::context::Context,
                          gfx: &mut G,
                          program: &TurtleProgram,
                          frame_num: u32)
        where T: ImageSize,
              G: Graphics<Texture = T>
    {
        use graphics::*;
        let redraw = self.redraw[(frame_num % 2) as usize];
        if redraw {
            println!("Redrawing frame {}", frame_num % 2);
            clear(WHITE, gfx);

            let mut turtle = GlTurtle::new(gfx, window_size, context);
            DoubleBufferedWindowHandler::turtledraw(program, &mut turtle);

            println!("Done redrawing frame");
            self.redraw[(frame_num % 2) as usize] = false;
        }
    }
}

/// An implementation of a Turtle within an OpenGL (rather, a gfx) context.
pub struct GlTurtle<'a, G, T>
    where T: ImageSize,
          G: Graphics<Texture = T> + 'a
{
    gfx: &'a mut G,
    window_size: Size,
    context: graphics::context::Context,

    position: Point,
    angle: f64,
    down: bool,
}

impl<'a, G, T> GlTurtle<'a, G, T>
    where T: ImageSize,
          G: Graphics<Texture = T> + 'a
{
    pub fn new(gfx: &'a mut G,
               window_size: Size,
               context: graphics::context::Context)
               -> GlTurtle<'a, G, T> {
        GlTurtle {
            gfx: gfx,
            window_size: window_size,
            context: context,
            position: Point { x: 0.0, y: 0.0 },
            angle: 0.0,
            down: true,
        }
    }
}

impl<'a, G, T> Turtle for GlTurtle<'a, G, T>
    where T: ImageSize,
          G: Graphics<Texture = T> + 'a
{
    fn forward(&mut self, distance: f64) {
        use graphics::*;

        let old_pos = self.position;
        let new_pos = self.position.point_at(Vector {
            direction: self.angle,
            magnitude: distance,
        });

        if self.down {
            let screen_width = self.window_size.width;
            let screen_height = self.window_size.height;

            let startx = (screen_width / 4) as f64;
            let starty = (screen_height / 2) as f64;
            let endx = (3 * screen_width / 4) as f64;
            // let endy = (screen_height / 2) as f64;

            let linesize = (startx - endx).abs() as f64;

            let transform = self.context
                                .transform
                                .trans(startx, starty)
                                .zoom(linesize)
                                .flip_v()
                                .trans(0.0, 0.0);

            // Line::new(BLACK, 1.0).draw([old_pos.x*linesize, old_pos.y*linesize,
            // new_pos.x*linesize, new_pos.y*linesize],
            Line::new(BLACK, 0.5 / linesize).draw([old_pos.x, old_pos.y, new_pos.x, new_pos.y],
                                                  default_draw_state(),
                                                  transform,
                                                  self.gfx);
        }

        self.position = new_pos;
    }

    fn set_pos(&mut self, new_pos: Point) {
        self.position = new_pos;
    }

    fn set_rad(&mut self, new_rad: f64) {
        self.angle = new_rad;
    }

    fn turn_rad(&mut self, radians: f64) {
        use std::f64::consts::PI;
        self.angle = (self.angle + radians) % (2.0 * PI);
    }

    fn down(&mut self) {
        self.down = true;
    }

    fn up(&mut self) {
        self.down = false;
    }
}
<|endoftext|>


<commit_before>//! The HTTP module which provides functions for performing requests to
//! endpoints in Discord's API.
//!
//! An important function of the REST API is ratelimiting. Requests to endpoints
//! are ratelimited to prevent spam, and once ratelimited Discord will stop
//! performing requests. The library implements protection to pre-emptively
//! ratelimit, to ensure that no wasted requests are made.
//!
//! The HTTP module comprises of two types of requests:
//!
//! - REST API requests, which require an authorization token;
//! - Other requests, which do not require an authorization token.
//!
//! The former require a [`Client`] to have logged in, while the latter may be
//! made regardless of any other usage of the library.
//!
//! If a request spuriously fails, it will be retried once.
//!
//! Note that you may want to perform requests through a [model]s'
//! instance methods where possible, as they each offer different
//! levels of a high-level interface to the HTTP module.
//!
//! [`Client`]: crate::Client
//! [model]: crate::model

pub mod client;
pub mod error;
pub mod multipart;
pub mod ratelimiting;
pub mod request;
pub mod routing;
pub mod typing;
mod utils;

use std::sync::Arc;

use reqwest::Method;
pub use reqwest::StatusCode;

pub use self::client::*;
pub use self::error::Error as HttpError;
use self::request::Request;
pub use self::typing::*;
#[cfg(feature = "cache")]
use crate::cache::Cache;
#[cfg(feature = "client")]
use crate::client::Context;
use crate::model::prelude::*;
#[cfg(feature = "client")]
use crate::CacheAndHttp;

/// This trait will be required by functions that need [`Http`] and can
/// optionally use a [`Cache`] to potentially avoid REST-requests.
///
/// The types [`Context`], [`Cache`], and [`Http`] implement this trait
/// and thus passing these to functions expecting `impl CacheHttp` is possible.
///
/// In a situation where you have the `cache`-feature enabled but you do not
/// pass a cache, the function will behave as if no `cache`-feature is active.
///
/// If you are calling a function that expects `impl CacheHttp` as argument
/// and you wish to utilise the `cache`-feature but you got no access to a
/// [`Context`], you can pass a tuple of `(CacheRwLock, Http)`.
pub trait CacheHttp: Send + Sync {
    fn http(&self) -> &Http;

    #[cfg(feature = "cache")]
    #[must_use]
    fn cache(&self) -> Option<&Arc<Cache>> {
        None
    }
}

impl<T> CacheHttp for &T
where
    T: CacheHttp,
{
    fn http(&self) -> &Http {
        (*self).http()
    }
    #[cfg(feature = "cache")]
    fn cache(&self) -> Option<&Arc<Cache>> {
        (*self).cache()
    }
}

#[cfg(feature = "client")]
impl CacheHttp for Context {
    fn http(&self) -> &Http {
        &self.http
    }
    #[cfg(feature = "cache")]
    fn cache(&self) -> Option<&Arc<Cache>> {
        Some(&self.cache)
    }
}

#[cfg(feature = "client")]
impl CacheHttp for CacheAndHttp {
    fn http(&self) -> &Http {
        &self.http
    }
    #[cfg(feature = "cache")]
    fn cache(&self) -> Option<&Arc<Cache>> {
        Some(&self.cache)
    }
}

#[cfg(feature = "client")]
impl CacheHttp for Arc<CacheAndHttp> {
    fn http(&self) -> &Http {
        &self.http
    }
    #[cfg(feature = "cache")]
    fn cache(&self) -> Option<&Arc<Cache>> {
        Some(&self.cache)
    }
}

#[cfg(feature = "cache")]
impl CacheHttp for (&Arc<Cache>, &Http) {
    fn cache(&self) -> Option<&Arc<Cache>> {
        Some(self.0)
    }
    fn http(&self) -> &Http {
        self.1
    }
}

impl CacheHttp for Http {
    fn http(&self) -> &Http {
        self
    }
}

impl CacheHttp for Arc<Http> {
    fn http(&self) -> &Http {
        &*self
    }
}

#[cfg(feature = "cache")]
impl AsRef<Cache> for (&Arc<Cache>, &Http) {
    fn as_ref(&self) -> &Cache {
        &**self.0
    }
}

#[cfg(feature = "cache")]
impl AsRef<Http> for (&Arc<Cache>, &Http) {
    fn as_ref(&self) -> &Http {
        self.1
    }
}

/// An method used for ratelimiting special routes.
///
/// This is needed because [`reqwest`]'s [`Method`] enum does not derive Copy.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum LightMethod {
    /// Indicates that a route is for the `DELETE` method only.
    Delete,
    /// Indicates that a route is for the `GET` method only.
    Get,
    /// Indicates that a route is for the `PATCH` method only.
    Patch,
    /// Indicates that a route is for the `POST` method only.
    Post,
    /// Indicates that a route is for the `PUT` method only.
    Put,
}

impl LightMethod {
    #[must_use]
    pub fn reqwest_method(self) -> Method {
        match self {
            Self::Delete => Method::DELETE,
            Self::Get => Method::GET,
            Self::Patch => Method::PATCH,
            Self::Post => Method::POST,
            Self::Put => Method::PUT,
        }
    }
}

/// Representation of the method of a query to send for the [`get_guilds`]
/// function.
///
/// [`get_guilds`]: Http::get_guilds
#[non_exhaustive]
pub enum GuildPagination {
    /// The Id to get the guilds after.
    After(GuildId),
    /// The Id to get the guilds before.
    Before(GuildId),
}

/// Representation of the method of a query to send for the [`get_scheduled_event_users`] function.
///
/// [`get_scheduled_event_users`]: Http::get_scheduled_event_users
#[non_exhaustive]
pub enum UserPagination {
    /// The Id to get the users after.
    After(UserId),
    /// The Id to get the users before.
    Before(UserId),
}
<commit_msg>Auto-impl `CacheHttp` for `Arc<T>` if `T` also implements it (#2003)<commit_after>//! The HTTP module which provides functions for performing requests to
//! endpoints in Discord's API.
//!
//! An important function of the REST API is ratelimiting. Requests to endpoints
//! are ratelimited to prevent spam, and once ratelimited Discord will stop
//! performing requests. The library implements protection to pre-emptively
//! ratelimit, to ensure that no wasted requests are made.
//!
//! The HTTP module comprises of two types of requests:
//!
//! - REST API requests, which require an authorization token;
//! - Other requests, which do not require an authorization token.
//!
//! The former require a [`Client`] to have logged in, while the latter may be
//! made regardless of any other usage of the library.
//!
//! If a request spuriously fails, it will be retried once.
//!
//! Note that you may want to perform requests through a [model]s'
//! instance methods where possible, as they each offer different
//! levels of a high-level interface to the HTTP module.
//!
//! [`Client`]: crate::Client
//! [model]: crate::model

pub mod client;
pub mod error;
pub mod multipart;
pub mod ratelimiting;
pub mod request;
pub mod routing;
pub mod typing;
mod utils;

use std::sync::Arc;

use reqwest::Method;
pub use reqwest::StatusCode;

pub use self::client::*;
pub use self::error::Error as HttpError;
use self::request::Request;
pub use self::typing::*;
#[cfg(feature = "cache")]
use crate::cache::Cache;
#[cfg(feature = "client")]
use crate::client::Context;
use crate::model::prelude::*;
#[cfg(feature = "client")]
use crate::CacheAndHttp;

/// This trait will be required by functions that need [`Http`] and can
/// optionally use a [`Cache`] to potentially avoid REST-requests.
///
/// The types [`Context`], [`Cache`], and [`Http`] implement this trait
/// and thus passing these to functions expecting `impl CacheHttp` is possible.
///
/// In a situation where you have the `cache`-feature enabled but you do not
/// pass a cache, the function will behave as if no `cache`-feature is active.
///
/// If you are calling a function that expects `impl CacheHttp` as argument
/// and you wish to utilise the `cache`-feature but you got no access to a
/// [`Context`], you can pass a tuple of `(CacheRwLock, Http)`.
pub trait CacheHttp: Send + Sync {
    fn http(&self) -> &Http;

    #[cfg(feature = "cache")]
    #[must_use]
    fn cache(&self) -> Option<&Arc<Cache>> {
        None
    }
}

impl<T> CacheHttp for &T
where
    T: CacheHttp,
{
    fn http(&self) -> &Http {
        (*self).http()
    }
    #[cfg(feature = "cache")]
    fn cache(&self) -> Option<&Arc<Cache>> {
        (*self).cache()
    }
}

impl<T> CacheHttp for Arc<T>
where
    T: CacheHttp,
{
    fn http(&self) -> &Http {
        (**self).http()
    }
    #[cfg(feature = "cache")]
    fn cache(&self) -> Option<&Arc<Cache>> {
        (**self).cache()
    }
}

#[cfg(feature = "client")]
impl CacheHttp for Context {
    fn http(&self) -> &Http {
        &self.http
    }
    #[cfg(feature = "cache")]
    fn cache(&self) -> Option<&Arc<Cache>> {
        Some(&self.cache)
    }
}

#[cfg(feature = "client")]
impl CacheHttp for CacheAndHttp {
    fn http(&self) -> &Http {
        &self.http
    }
    #[cfg(feature = "cache")]
    fn cache(&self) -> Option<&Arc<Cache>> {
        Some(&self.cache)
    }
}

#[cfg(feature = "cache")]
impl CacheHttp for (&Arc<Cache>, &Http) {
    fn cache(&self) -> Option<&Arc<Cache>> {
        Some(self.0)
    }
    fn http(&self) -> &Http {
        self.1
    }
}

impl CacheHttp for Http {
    fn http(&self) -> &Http {
        self
    }
}

#[cfg(feature = "cache")]
impl AsRef<Cache> for (&Arc<Cache>, &Http) {
    fn as_ref(&self) -> &Cache {
        &**self.0
    }
}

#[cfg(feature = "cache")]
impl AsRef<Http> for (&Arc<Cache>, &Http) {
    fn as_ref(&self) -> &Http {
        self.1
    }
}

/// An method used for ratelimiting special routes.
///
/// This is needed because [`reqwest`]'s [`Method`] enum does not derive Copy.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum LightMethod {
    /// Indicates that a route is for the `DELETE` method only.
    Delete,
    /// Indicates that a route is for the `GET` method only.
    Get,
    /// Indicates that a route is for the `PATCH` method only.
    Patch,
    /// Indicates that a route is for the `POST` method only.
    Post,
    /// Indicates that a route is for the `PUT` method only.
    Put,
}

impl LightMethod {
    #[must_use]
    pub fn reqwest_method(self) -> Method {
        match self {
            Self::Delete => Method::DELETE,
            Self::Get => Method::GET,
            Self::Patch => Method::PATCH,
            Self::Post => Method::POST,
            Self::Put => Method::PUT,
        }
    }
}

/// Representation of the method of a query to send for the [`get_guilds`]
/// function.
///
/// [`get_guilds`]: Http::get_guilds
#[non_exhaustive]
pub enum GuildPagination {
    /// The Id to get the guilds after.
    After(GuildId),
    /// The Id to get the guilds before.
    Before(GuildId),
}

/// Representation of the method of a query to send for the [`get_scheduled_event_users`] function.
///
/// [`get_scheduled_event_users`]: Http::get_scheduled_event_users
#[non_exhaustive]
pub enum UserPagination {
    /// The Id to get the users after.
    After(UserId),
    /// The Id to get the users before.
    Before(UserId),
}
<|endoftext|>


<commit_before>// compiler/abstract_tree.rs

use std::collections::HashMap;

use utils::{Result, Position, IR, err_position};
use self::AbstractTree::*;

static BLOCK_IDENTIFIER: &'static str = "block";

/// TokenType is supposed to relay any information
/// about the Token that would be known from the first
/// pass of parsing. For example, the difference between
/// an int literal `3` or a float literal `4.3` is determined by the
/// string representation.
///
/// A `Symbol` type is the most basic - representing an ident of the language.
#[derive(Debug)]
pub enum TokenType {
    Symbol,
    Str,
    Int,
    Float,
}

pub struct QBEBuilder<'a> {
    abstract_tree: Option<AbstractTree<'a>>,
    transformations: HashMap<&'a str, fn(&mut AbstractTree) -> Result<IR>>,
}

pub mod qbe_backend {
    use super::{QBEBuilder, AbstractTree};
    use std::collections::HashMap;
    pub fn new<'a>(a: AbstractTree<'a>) -> QBEBuilder<'a> {
        QBEBuilder { abstract_tree: Some(a), transformations: HashMap::new() }
    }
}

impl<'a> QBEBuilder<'a> {
    pub fn handle(mut self, key: &'a str, f: fn(&mut AbstractTree) -> Result<IR>) -> QBEBuilder<'a> {
        self.transformations.insert(key, f);
        self
    }

    pub fn compile(&mut self) -> Result<IR> {
        let mut abstract_tree = self.abstract_tree.take().unwrap();
        self.compile_inner(&mut abstract_tree)
    }

    pub fn compile_function_call(&mut self, tree: &mut AbstractTree) -> Result<IR> {
        match tree {
            &mut Node(ref mut ats, ref position) => {
                if ats.len() >= 1 {
                    Ok(vec![])
                } else {
                    err_position(position.clone(), "node with zero items".to_string())
                }
            }
            _ => panic!("compile_function_callnot called on a node.")
        }
    }

    pub fn compile_inner(&mut self, tree: &mut AbstractTree) -> Result<IR> {
        {
            let transformation = self.transformations.get(tree.name());
            if transformation.is_some() {
                return transformation.unwrap()(tree);
            }
        }
        self.compile_function_call(tree)
    }
}

/// The AbstractTree is what is given to the `compile`
/// function of the compiler module. It consists of
/// nodes and tokens - nodes simply hold more abstract
/// trees, whereas tokens have a TokenType and a string
/// representation. All AbstractTree's have a position
/// that is used for reporting errors.
#[derive(Debug)]
pub enum AbstractTree<'a> {
    Node(Vec<AbstractTree<'a>>, Position),
    Token(TokenType, &'a str, Position),
}

impl<'a> AbstractTree<'a> {
    /// This is only called in assert_only_top_level() so it can
    /// be baseed to match_symbol()
    fn fail_for_top_leval_call(a: &mut AbstractTree) -> Result<()> {
        a.err(format!("{} was invoked without being on the top level", a.name()))
    }

    /// assert_only_top_level() will return a Result::Err if
    /// a call occurs somewhere that's not the top level.
    pub fn assert_only_top_level(& mut self, s: &'a str) -> Result<()> {
        // Go two nodes deep and assert there are no more after that.
        match self {
            &mut Node(ref mut ats, _) => {
                ats.iter_mut().fold(Ok(()), |acc, at| {
                    acc.and_then(|_| {
                        match at {
                            &mut Node(ref mut ats, _) => {
                                ats.iter_mut().fold(Ok(()), |acc, a| {
                                    acc.and_then(|_| {
                                        a.match_symbol(s, AbstractTree::fail_for_top_leval_call)
                                    })
                                })
                            }
                            _ => Ok(()),
                        }
                    })
                })
            }
            _ => Ok(()),
        }
    }

    pub fn match_symbol(&mut self,
                        s: &'a str,
                        f: fn(&mut AbstractTree) -> Result<()>)
                        -> Result<()> {
        let start = match self {
                        &mut Node(ref ats, _) => {
                            match ats.get(0) {
                                Some(&Token(TokenType::Symbol, a, _)) => {
                                    if s == a {
                                        // this is so I can destructure immutably
                                        // and then call f on the mutable self object
                                        self.err("this is a goto to f(self)".to_string())
                                    } else {
                                        Ok(())
                                    }
                                }
                                _ => Ok(()),
                            }
                        }
                        _ => Ok(()),
                    }
                    .or_else(|_| f(self));

        match self {
            &mut Node(ref mut ats, _) => {
                ats.iter_mut().fold(start,
                                    |results, x| results.and_then(|_| x.match_symbol(s, f)))
            }
            _ => start,
        }
    }

    // Functions for validating ast

    pub fn check_length(&self, i: usize) -> Result<()> {
        match self {
            &Node(ref ats, _) => {
                if ats.len() == i {
                    Ok(())
                } else {
                    self.err(format!("{} takes {} arguments", self.name(), i - 1))
                }
            }
            _ => panic!(format!("check_length called on not a node: {:?}", self)),
        }
    }

    /// check_argument_block will check to make sure the argument_number'th
    /// argument is formatted like a block.
    pub fn check_argument_block(&self, argument_number: usize) -> Result<()> {
        let error = self.err(format!("{} expects a block for its {}th argument",
                                     self.name(),
                                     argument_number));
        let argument = self.argument(argument_number);
        Ok(())
            .and_then(|_| argument.assert_node(error.clone()))
            .and_then(|_| argument.check_length(3))
            .and_then(|_| {
                // make sure the block starts with 'block'
                match argument {
                    &Node(ref ats, _) => {
                        let block_error = self.err("a block takes a \
                                                     list of arguments \
                                                     followed by a list \
                                                     of expressions"
                                                       .to_string());
                        Ok(())
                            .and_then(|_| {
                                match ats.get(0).unwrap() {
                                    &Token(TokenType::Symbol, a, _) => {
                                        if a == BLOCK_IDENTIFIER {
                                            Ok(())
                                        } else {
                                            error.clone()
                                        }
                                    }
                                    _ => error.clone(),
                                }
                            })
                            .and_then(|_| ats[1].assert_node(block_error.clone()))
                            .and_then(|_| ats[2].assert_node(block_error.clone()))
                    }
                    _ => panic!("I already asserted this was a node."),
                }
            })
    }

    /// Given an AbstractTree, assert it's a Node not a Token.
    ///
    /// # Examples:
    ///
    /// ```
    /// let error = Node(vec![], Position(0,0)).err("this is an error")
    ///
    /// Node(vec![], Position(0,0)).assert_node().ok().unwrap()
    /// Token(Symbol, "", Position(0,0)).assert_node().err().unwrap()
    /// ```
    ///
    fn assert_node(&self, error: Result<()>) -> Result<()> {
        match self {
            &Node(_, _) => Ok(()),
            _ => error,
        }
    }

    // Functions for reading the ast

    /// Get an immutable reference to the ith argument of a node.
    pub fn argument(&self, i: usize) -> &AbstractTree<'a> {
        self.arguments().get(i).unwrap()
    }

    /// Get an immutabe reference to the arguments of a node
    ///
    /// This will panic if called on a Token.
    pub fn arguments(&self) -> &Vec<AbstractTree<'a>> {
        match self {
            &Node(ref ats, _) => return ats,
            _ => panic!("fn arguments called on a Token"),
        }
    }

    /// Get the 'name' of a Node - defined to be the
    /// string of the first token if the abstract tree is
    /// a node and has a first token.
    pub fn name(&self) -> &str {
        match self {
            &Node(ref ats, _) => {
                match ats.get(0) {
                    Some(&Token(TokenType::Symbol, a, _)) => a,
                    _ => "unknown",
                }
            }
            _ => "token",
        }
    }

    /// The Position of an abstract tree -
    /// both a Node and a Token have it, but
    /// accessing it requires deconstructing
    /// which is why this method is useful.
    fn position(&self) -> Position {
        match self {
            &Node(_, ref position) => position.clone(),
            &Token(_, _, ref position) => position.clone(),
        }
    }

    /// Generate an utils::Result type from a discription
    /// passed in and this abstract tree's position.
    fn err<T>(&self, description: String) -> Result<T> {
        err_position(self.position(), description)
    }
}

#[cfg(test)]
mod tests {
    use super::AbstractTree;
    use utils::Result;
    use utils::tests::generate_data;

    static mut foo_visitor_count: i64 = 0;
    fn visitor_match_symbol(_: &mut AbstractTree) -> Result<()> {
        unsafe {
            foo_visitor_count += 1;
        };
        Ok(())
    }

    #[test]
    fn test_match_symbol() {
        let mut data = generate_data();
        data.match_symbol("foo", visitor_match_symbol).ok().unwrap();
        assert_eq!(unsafe { foo_visitor_count }, 2);
        unsafe { foo_visitor_count = 0 };
    }

    fn visitor_check_length_2(at: &mut AbstractTree) -> Result<()> {
        at.check_length(2)
    }
    fn visitor_check_length_1(at: &mut AbstractTree) -> Result<()> {
        at.check_length(1)
    }

    #[test]
    fn test_check_length() {
        let mut data = generate_data();
        assert!(data.match_symbol("define", visitor_check_length_2).is_ok());
        assert!(data.match_symbol("define", visitor_check_length_1).is_err());
        }

    #[test]
    fn test_err_and_position() {
        let data = generate_data();
        let error = data.err::<Result<()>>("this is an error".to_string()).err().unwrap();
        assert_eq!(error.description, "this is an error".to_string());
        assert_eq!(error.position, data.position());
    }
}
<commit_msg>starting to implement function calls<commit_after>// compiler/abstract_tree.rs

use std::collections::HashMap;

use utils::{Result, Position, IR, err_position};
use self::AbstractTree::*;

static BLOCK_IDENTIFIER: &'static str = "block";

mod utils {
    pub fn generate_argument_names(i: usize) -> String {
        // make a range and something like "%arg1 %arg2 %arg3"
        format!("arg{}", i)
    }
}

/// TokenType is supposed to relay any information
/// about the Token that would be known from the first
/// pass of parsing. For example, the difference between
/// an int literal `3` or a float literal `4.3` is determined by the
/// string representation.
///
/// A `Symbol` type is the most basic - representing an ident of the language.
#[derive(Debug)]
pub enum TokenType {
    Symbol,
    Str,
    Int,
    Float,
}

pub struct QBEBuilder<'a> {
    abstract_tree: Option<AbstractTree<'a>>,
    transformations: HashMap<&'a str, fn(&mut AbstractTree) -> Result<IR>>,
}

pub mod qbe_backend {
    use super::{QBEBuilder, AbstractTree};
    use std::collections::HashMap;
    pub fn new<'a>(a: AbstractTree<'a>) -> QBEBuilder<'a> {
        QBEBuilder { abstract_tree: Some(a), transformations: HashMap::new() }
    }
}

impl<'a> QBEBuilder<'a> {
    pub fn handle(mut self, key: &'a str, f: fn(&mut AbstractTree) -> Result<IR>) -> QBEBuilder<'a> {
        self.transformations.insert(key, f);
        self
    }

    pub fn compile(&mut self) -> Result<IR> {
        let mut abstract_tree = self.abstract_tree.take().unwrap();
        self.compile_inner(&mut abstract_tree)
    }

    pub fn compile_function_call(&mut self, tree: &mut AbstractTree) -> Result<IR> {
        match tree {
            &mut Node(ref mut ats, ref position) => {
                let length = ats.len();
                let mut iterator = ats.iter_mut();
                let mut first_item = iterator.next().unwrap();
                if length == 1 {
                    match first_item {
                        &mut Node(_, _) => self.compile_inner(first_item),
                        _ => panic!("unimplemented: no support for calling closures yet")
                    }
                } else if length >= 1 {
                    match first_item {
                        &mut Node(_, ref position) => err_position(position.clone(),
                            "unimplemented: no support for calling closures yet implemented".to_string()),
                        &mut Token(TokenType::Symbol, ref mut fuction_name, _) => {
                            let mut i = 0;
                            let mut ir = iterator.fold(Ok(vec![]), |acc, argument| {
                                acc.and_then(|mut vector| {
                                    self.compile_inner(argument).and_then(|mut argument_ir| {
                                        i += 1;
                                        argument_ir.push(format!("%arg{} =l %ret", i));
                                        vector.append(&mut argument_ir);
                                        Ok(vector)
                                    })
                                })
                            });

                            let argument_names = self::utils::generate_argument_names(length);
                            ir.iter_mut()
                            .map(|inner| inner.push(format!("%ret =l call ${} {}", fuction_name, argument_names)))
                            .collect::<Vec<_>>();
                            ir
                        },
                        &mut Token(ref token_type, ref data, ref position) => {
                            err_position(position.clone(), format!("cannot call token {} of type {:?}", data, token_type))
                        }
                    }
                } else {
                    err_position(position.clone(), "node with zero items".to_string())
                }
            }
            _ => panic!("compile_function_call not called on a node.")
        }
    }

    pub fn compile_token(&mut self, tree: &mut AbstractTree) -> Result<IR> {
        match tree {
            &mut Token(TokenType::Symbol, _, ref position) => err_position(position.clone(), "variables are not yet implemented".to_string()),
            &mut Token(TokenType::Int, ref integer_literal, _) =>
                Ok(vec![format!("%ret =l {}", integer_literal)]),
            _ => panic!("compile_token not called on a token.")
        }
    }

    pub fn compile_inner(&mut self, tree: &mut AbstractTree) -> Result<IR> {
        {
            let transformation = self.transformations.get(tree.name());
            if transformation.is_some() {
                return transformation.unwrap()(tree);
            }
        }
        match tree {
            &mut Node(_, _) => self.compile_function_call(tree),
            _ => self.compile_token(tree),
        }
    }
}

/// The AbstractTree is what is given to the `compile`
/// function of the compiler module. It consists of
/// nodes and tokens - nodes simply hold more abstract
/// trees, whereas tokens have a TokenType and a string
/// representation. All AbstractTree's have a position
/// that is used for reporting errors.
#[derive(Debug)]
pub enum AbstractTree<'a> {
    Node(Vec<AbstractTree<'a>>, Position),
    Token(TokenType, &'a str, Position),
}

impl<'a> AbstractTree<'a> {
    /// This is only called in assert_only_top_level() so it can
    /// be baseed to match_symbol()
    fn fail_for_top_leval_call(a: &mut AbstractTree) -> Result<()> {
        a.err(format!("{} was invoked without being on the top level", a.name()))
    }

    /// assert_only_top_level() will return a Result::Err if
    /// a call occurs somewhere that's not the top level.
    pub fn assert_only_top_level(& mut self, s: &'a str) -> Result<()> {
        // Go two nodes deep and assert there are no more after that.
        match self {
            &mut Node(ref mut ats, _) => {
                ats.iter_mut().fold(Ok(()), |acc, at| {
                    acc.and_then(|_| {
                        match at {
                            &mut Node(ref mut ats, _) => {
                                ats.iter_mut().fold(Ok(()), |acc, a| {
                                    acc.and_then(|_| {
                                        a.match_symbol(s, AbstractTree::fail_for_top_leval_call)
                                    })
                                })
                            }
                            _ => Ok(()),
                        }
                    })
                })
            }
            _ => Ok(()),
        }
    }

    pub fn match_symbol(&mut self,
                        s: &'a str,
                        f: fn(&mut AbstractTree) -> Result<()>)
                        -> Result<()> {
        let start = match self {
                        &mut Node(ref ats, _) => {
                            match ats.get(0) {
                                Some(&Token(TokenType::Symbol, a, _)) => {
                                    if s == a {
                                        // this is so I can destructure immutably
                                        // and then call f on the mutable self object
                                        self.err("this is a goto to f(self)".to_string())
                                    } else {
                                        Ok(())
                                    }
                                }
                                _ => Ok(()),
                            }
                        }
                        _ => Ok(()),
                    }
                    .or_else(|_| f(self));

        match self {
            &mut Node(ref mut ats, _) => {
                ats.iter_mut().fold(start,
                                    |results, x| results.and_then(|_| x.match_symbol(s, f)))
            }
            _ => start,
        }
    }

    // Functions for validating ast

    pub fn check_length(&self, i: usize) -> Result<()> {
        match self {
            &Node(ref ats, _) => {
                if ats.len() == i {
                    Ok(())
                } else {
                    self.err(format!("{} takes {} arguments", self.name(), i - 1))
                }
            }
            _ => panic!(format!("check_length called on not a node: {:?}", self)),
        }
    }

    /// check_argument_block will check to make sure the argument_number'th
    /// argument is formatted like a block.
    pub fn check_argument_block(&self, argument_number: usize) -> Result<()> {
        let error = self.err(format!("{} expects a block for its {}th argument",
                                     self.name(),
                                     argument_number));
        let argument = self.argument(argument_number);
        Ok(())
            .and_then(|_| argument.assert_node(error.clone()))
            .and_then(|_| argument.check_length(3))
            .and_then(|_| {
                // make sure the block starts with 'block'
                match argument {
                    &Node(ref ats, _) => {
                        let block_error = self.err("a block takes a \
                                                     list of arguments \
                                                     followed by a list \
                                                     of expressions"
                                                       .to_string());
                        Ok(())
                            .and_then(|_| {
                                match ats.get(0).unwrap() {
                                    &Token(TokenType::Symbol, a, _) => {
                                        if a == BLOCK_IDENTIFIER {
                                            Ok(())
                                        } else {
                                            error.clone()
                                        }
                                    }
                                    _ => error.clone(),
                                }
                            })
                            .and_then(|_| ats[1].assert_node(block_error.clone()))
                            .and_then(|_| ats[2].assert_node(block_error.clone()))
                    }
                    _ => panic!("I already asserted this was a node."),
                }
            })
    }

    /// Given an AbstractTree, assert it's a Node not a Token.
    ///
    /// # Examples:
    ///
    /// ```
    /// let error = Node(vec![], Position(0,0)).err("this is an error")
    ///
    /// Node(vec![], Position(0,0)).assert_node().ok().unwrap()
    /// Token(Symbol, "", Position(0,0)).assert_node().err().unwrap()
    /// ```
    ///
    fn assert_node(&self, error: Result<()>) -> Result<()> {
        match self {
            &Node(_, _) => Ok(()),
            _ => error,
        }
    }

    // Functions for reading the ast

    /// Get an immutable reference to the ith argument of a node.
    pub fn argument(&self, i: usize) -> &AbstractTree<'a> {
        self.arguments().get(i).unwrap()
    }

    /// Get an immutabe reference to the arguments of a node
    ///
    /// This will panic if called on a Token.
    pub fn arguments(&self) -> &Vec<AbstractTree<'a>> {
        match self {
            &Node(ref ats, _) => return ats,
            _ => panic!("fn arguments called on a Token"),
        }
    }

    /// Get the 'name' of a Node - defined to be the
    /// string of the first token if the abstract tree is
    /// a node and has a first token.
    pub fn name(&self) -> &str {
        match self {
            &Node(ref ats, _) => {
                match ats.get(0) {
                    Some(&Token(TokenType::Symbol, a, _)) => a,
                    _ => "unknown",
                }
            }
            _ => "token",
        }
    }

    /// The Position of an abstract tree -
    /// both a Node and a Token have it, but
    /// accessing it requires deconstructing
    /// which is why this method is useful.
    fn position(&self) -> Position {
        match self {
            &Node(_, ref position) => position.clone(),
            &Token(_, _, ref position) => position.clone(),
        }
    }

    /// Generate an utils::Result type from a discription
    /// passed in and this abstract tree's position.
    fn err<T>(&self, description: String) -> Result<T> {
        err_position(self.position(), description)
    }
}

#[cfg(test)]
mod tests {
    use super::AbstractTree;
    use utils::Result;
    use utils::tests::generate_data;

    static mut foo_visitor_count: i64 = 0;
    fn visitor_match_symbol(_: &mut AbstractTree) -> Result<()> {
        unsafe {
            foo_visitor_count += 1;
        };
        Ok(())
    }

    #[test]
    fn test_match_symbol() {
        let mut data = generate_data();
        data.match_symbol("foo", visitor_match_symbol).ok().unwrap();
        assert_eq!(unsafe { foo_visitor_count }, 2);
        unsafe { foo_visitor_count = 0 };
    }

    fn visitor_check_length_2(at: &mut AbstractTree) -> Result<()> {
        at.check_length(2)
    }
    fn visitor_check_length_1(at: &mut AbstractTree) -> Result<()> {
        at.check_length(1)
    }

    #[test]
    fn test_check_length() {
        let mut data = generate_data();
        assert!(data.match_symbol("define", visitor_check_length_2).is_ok());
        assert!(data.match_symbol("define", visitor_check_length_1).is_err());
        }

    #[test]
    fn test_err_and_position() {
        let data = generate_data();
        let error = data.err::<Result<()>>("this is an error".to_string()).err().unwrap();
        assert_eq!(error.description, "this is an error".to_string());
        assert_eq!(error.position, data.position());
    }
}
<|endoftext|>