<commit_before>use ide_db::{base_db::Upcast, helpers::pick_best_token, RootDatabase};
use rustc_hash::FxHashSet;
use syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, T};

use crate::{display::TryToNav, FilePosition, NavigationTarget, RangeInfo};

// Feature: Go to Type Definition
//
// Navigates to the type of an identifier.
//
// |===
// | Editor  | Action Name
//
// | VS Code | **Go to Type Definition*
// |===
//
// image::https://user-images.githubusercontent.com/48062697/113020657-b560f500-917a-11eb-9007-0f809733a338.gif[]
pub(crate) fn goto_type_definition(
    db: &RootDatabase,
    position: FilePosition,
) -> Option<RangeInfo<Vec<NavigationTarget>>> {
    let sema = hir::Semantics::new(db);

    let file: ast::SourceFile = sema.parse(position.file_id);
    let token: SyntaxToken =
        pick_best_token(file.syntax().token_at_offset(position.offset), |kind| match kind {
            IDENT | INT_NUMBER | T![self] => 2,
            kind if kind.is_trivia() => 0,
            _ => 1,
        })?;
    let token: SyntaxToken = sema.descend_into_macros(token);

    let (ty, node) = sema.token_ancestors_with_macros(token).find_map(|node| {
        let ty = match_ast! {
            match node {
                ast::Expr(it) => sema.type_of_expr(&it)?,
                ast::Pat(it) => sema.type_of_pat(&it)?,
                ast::SelfParam(it) => sema.type_of_self(&it)?,
                ast::Type(it) => sema.resolve_type(&it)?,
                ast::RecordField(it) => sema.to_def(&it).map(|d| d.ty(db.upcast()))?,
                // can't match on RecordExprField directly as `ast::Expr` will match an iteration too early otherwise
                ast::NameRef(it) => {
                    if let Some(record_field) = ast::RecordExprField::for_name_ref(&it) {
                        let (_, _, ty) = sema.resolve_record_field(&record_field)?;
                        ty
                    } else {
                        let record_field = ast::RecordPatField::for_field_name_ref(&it)?;
                        sema.resolve_record_pat_field(&record_field)?.ty(db)
                    }
                },
                _ => return None,
            }
        };

        Some((ty, node))
    })?;

    let mut res = FxHashSet::default();
    let mut workload = vec![ty.strip_references()];
    while let Some(ty) = workload.pop() {
        if let Some(adt) = ty.as_adt() {
            res.insert(adt);
        }
        workload.extend(ty.strip_references().type_arguments());
    }

    Some(RangeInfo::new(
        node.text_range(),
        res.into_iter().flat_map(|adt| adt.try_to_nav(db)).collect(),
    ))
}

#[cfg(test)]
mod tests {
    use ide_db::base_db::FileRange;
    use itertools::Itertools;

    use crate::fixture;

    fn check(ra_fixture: &str) {
        let (analysis, position, expected) = fixture::annotations(ra_fixture);
        let navs = analysis.goto_type_definition(position).unwrap().unwrap().info;
        assert_ne!(navs.len(), 0);

        let cmp = |&FileRange { file_id, range }: &_| (file_id, range.start());
        let navs = navs
            .into_iter()
            .map(|nav| FileRange { file_id: nav.file_id, range: nav.focus_or_full_range() })
            .sorted_by_key(cmp)
            .collect::<Vec<_>>();
        let expected = expected
            .into_iter()
            .map(|(FileRange { file_id, range }, _)| FileRange { file_id, range })
            .sorted_by_key(cmp)
            .collect::<Vec<_>>();
        assert_eq!(expected, navs);
    }

    #[test]
    fn goto_type_definition_works_simple() {
        check(
            r#"
struct Foo;
     //^^^
fn foo() {
    let f: Foo; f$0
}
"#,
        );
    }

    #[test]
    fn goto_type_definition_record_expr_field() {
        check(
            r#"
struct Bar;
    // ^^^
struct Foo { foo: Bar }
fn foo() {
    Foo { foo$0 }
}
"#,
        );
        check(
            r#"
struct Bar;
    // ^^^
struct Foo { foo: Bar }
fn foo() {
    Foo { foo$0: Bar }
}
"#,
        );
    }

    #[test]
    fn goto_type_definition_record_pat_field() {
        check(
            r#"
struct Bar;
    // ^^^
struct Foo { foo: Bar }
fn foo() {
    let Foo { foo$0 };
}
"#,
        );
        check(
            r#"
struct Bar;
    // ^^^
struct Foo { foo: Bar }
fn foo() {
    let Foo { foo$0: bar };
}
"#,
        );
    }

    #[test]
    fn goto_type_definition_works_simple_ref() {
        check(
            r#"
struct Foo;
     //^^^
fn foo() {
    let f: &Foo; f$0
}
"#,
        );
    }

    #[test]
    fn goto_type_definition_works_through_macro() {
        check(
            r#"
macro_rules! id { ($($tt:tt)*) => { $($tt)* } }
struct Foo {}
     //^^^
id! {
    fn bar() { let f$0 = Foo {}; }
}
"#,
        );
    }

    #[test]
    fn goto_type_definition_for_param() {
        check(
            r#"
struct Foo;
     //^^^
fn foo($0f: Foo) {}
"#,
        );
    }

    #[test]
    fn goto_type_definition_for_tuple_field() {
        check(
            r#"
struct Foo;
     //^^^
struct Bar(Foo);
fn foo() {
    let bar = Bar(Foo);
    bar.$00;
}
"#,
        );
    }

    #[test]
    fn goto_def_for_self_param() {
        check(
            r#"
struct Foo;
     //^^^
impl Foo {
    fn f(&self$0) {}
}
"#,
        )
    }

    #[test]
    fn goto_def_for_type_fallback() {
        check(
            r#"
struct Foo;
     //^^^
impl Foo$0 {}
"#,
        )
    }

    #[test]
    fn goto_def_for_struct_field() {
        check(
            r#"
struct Bar;
     //^^^

struct Foo {
    bar$0: Bar,
}
"#,
        );
    }

    #[test]
    fn goto_def_for_enum_struct_field() {
        check(
            r#"
struct Bar;
     //^^^

enum Foo {
    Bar {
        bar$0: Bar
    },
}
"#,
        );
    }

    #[test]
    fn goto_def_considers_generics() {
        check(
            r#"
struct Foo;
     //^^^
struct Bar<T, U>(T, U);
     //^^^
struct Baz<T>(T);
     //^^^

fn foo(x$0: Bar<Baz<Foo>, Baz<usize>) {}
"#,
        );
    }
}
<commit_msg>Use Type::walk for goto_type_definition<commit_after>use ide_db::{base_db::Upcast, helpers::pick_best_token, RootDatabase};
use syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, T};

use crate::{display::TryToNav, FilePosition, NavigationTarget, RangeInfo};

// Feature: Go to Type Definition
//
// Navigates to the type of an identifier.
//
// |===
// | Editor  | Action Name
//
// | VS Code | **Go to Type Definition*
// |===
//
// image::https://user-images.githubusercontent.com/48062697/113020657-b560f500-917a-11eb-9007-0f809733a338.gif[]
pub(crate) fn goto_type_definition(
    db: &RootDatabase,
    position: FilePosition,
) -> Option<RangeInfo<Vec<NavigationTarget>>> {
    let sema = hir::Semantics::new(db);

    let file: ast::SourceFile = sema.parse(position.file_id);
    let token: SyntaxToken =
        pick_best_token(file.syntax().token_at_offset(position.offset), |kind| match kind {
            IDENT | INT_NUMBER | T![self] => 2,
            kind if kind.is_trivia() => 0,
            _ => 1,
        })?;
    let token: SyntaxToken = sema.descend_into_macros(token);

    let (ty, node) = sema.token_ancestors_with_macros(token).find_map(|node| {
        let ty = match_ast! {
            match node {
                ast::Expr(it) => sema.type_of_expr(&it)?,
                ast::Pat(it) => sema.type_of_pat(&it)?,
                ast::SelfParam(it) => sema.type_of_self(&it)?,
                ast::Type(it) => sema.resolve_type(&it)?,
                ast::RecordField(it) => sema.to_def(&it).map(|d| d.ty(db.upcast()))?,
                // can't match on RecordExprField directly as `ast::Expr` will match an iteration too early otherwise
                ast::NameRef(it) => {
                    if let Some(record_field) = ast::RecordExprField::for_name_ref(&it) {
                        let (_, _, ty) = sema.resolve_record_field(&record_field)?;
                        ty
                    } else {
                        let record_field = ast::RecordPatField::for_field_name_ref(&it)?;
                        sema.resolve_record_pat_field(&record_field)?.ty(db)
                    }
                },
                _ => return None,
            }
        };

        Some((ty, node))
    })?;

    let mut res = Vec::new();
    let mut push = |def: hir::ModuleDef| {
        if let Some(nav) = def.try_to_nav(db) {
            if !res.contains(&nav) {
                res.push(nav);
            }
        }
    };

    let ty = ty.strip_references();
    ty.walk(db, |t| {
        if let Some(adt) = t.as_adt() {
            push(adt.into());
        } else if let Some(trait_) = t.as_dyn_trait() {
            push(trait_.into());
        } else if let Some(traits) = t.as_impl_traits(db) {
            traits.into_iter().for_each(|it| push(it.into()));
        } else if let Some(trait_) = t.as_associated_type_parent_trait(db) {
            push(trait_.into());
        }
    });

    Some(RangeInfo::new(node.text_range(), res))
}

#[cfg(test)]
mod tests {
    use ide_db::base_db::FileRange;
    use itertools::Itertools;

    use crate::fixture;

    fn check(ra_fixture: &str) {
        let (analysis, position, expected) = fixture::annotations(ra_fixture);
        let navs = analysis.goto_type_definition(position).unwrap().unwrap().info;
        assert_ne!(navs.len(), 0);

        let cmp = |&FileRange { file_id, range }: &_| (file_id, range.start());
        let navs = navs
            .into_iter()
            .map(|nav| FileRange { file_id: nav.file_id, range: nav.focus_or_full_range() })
            .sorted_by_key(cmp)
            .collect::<Vec<_>>();
        let expected = expected
            .into_iter()
            .map(|(FileRange { file_id, range }, _)| FileRange { file_id, range })
            .sorted_by_key(cmp)
            .collect::<Vec<_>>();
        assert_eq!(expected, navs);
    }

    #[test]
    fn goto_type_definition_works_simple() {
        check(
            r#"
struct Foo;
     //^^^
fn foo() {
    let f: Foo; f$0
}
"#,
        );
    }

    #[test]
    fn goto_type_definition_record_expr_field() {
        check(
            r#"
struct Bar;
    // ^^^
struct Foo { foo: Bar }
fn foo() {
    Foo { foo$0 }
}
"#,
        );
        check(
            r#"
struct Bar;
    // ^^^
struct Foo { foo: Bar }
fn foo() {
    Foo { foo$0: Bar }
}
"#,
        );
    }

    #[test]
    fn goto_type_definition_record_pat_field() {
        check(
            r#"
struct Bar;
    // ^^^
struct Foo { foo: Bar }
fn foo() {
    let Foo { foo$0 };
}
"#,
        );
        check(
            r#"
struct Bar;
    // ^^^
struct Foo { foo: Bar }
fn foo() {
    let Foo { foo$0: bar };
}
"#,
        );
    }

    #[test]
    fn goto_type_definition_works_simple_ref() {
        check(
            r#"
struct Foo;
     //^^^
fn foo() {
    let f: &Foo; f$0
}
"#,
        );
    }

    #[test]
    fn goto_type_definition_works_through_macro() {
        check(
            r#"
macro_rules! id { ($($tt:tt)*) => { $($tt)* } }
struct Foo {}
     //^^^
id! {
    fn bar() { let f$0 = Foo {}; }
}
"#,
        );
    }

    #[test]
    fn goto_type_definition_for_param() {
        check(
            r#"
struct Foo;
     //^^^
fn foo($0f: Foo) {}
"#,
        );
    }

    #[test]
    fn goto_type_definition_for_tuple_field() {
        check(
            r#"
struct Foo;
     //^^^
struct Bar(Foo);
fn foo() {
    let bar = Bar(Foo);
    bar.$00;
}
"#,
        );
    }

    #[test]
    fn goto_def_for_self_param() {
        check(
            r#"
struct Foo;
     //^^^
impl Foo {
    fn f(&self$0) {}
}
"#,
        )
    }

    #[test]
    fn goto_def_for_type_fallback() {
        check(
            r#"
struct Foo;
     //^^^
impl Foo$0 {}
"#,
        )
    }

    #[test]
    fn goto_def_for_struct_field() {
        check(
            r#"
struct Bar;
     //^^^

struct Foo {
    bar$0: Bar,
}
"#,
        );
    }

    #[test]
    fn goto_def_for_enum_struct_field() {
        check(
            r#"
struct Bar;
     //^^^

enum Foo {
    Bar {
        bar$0: Bar
    },
}
"#,
        );
    }

    #[test]
    fn goto_def_considers_generics() {
        check(
            r#"
struct Foo;
     //^^^
struct Bar<T, U>(T, U);
     //^^^
struct Baz<T>(T);
     //^^^

fn foo(x$0: Bar<Baz<Foo>, Baz<usize>) {}
"#,
        );
    }
}
<|endoftext|>


<commit_before>use models::*;

pub fn print_headline_with_author(item: &HnItem, index: &i32) {
    let s = create_headline_with_author(item, index).unwrap(); // Not handling errs
    println!("{}", s);
}

fn create_headline_with_author(item: &HnItem, index: &i32) -> Result<String, String> {
    match item.title {
        Some(_) => {
            let s = format!("[{:3}] {:80} ~ {}",
                            index,
                            item.title.as_ref().unwrap(),
                            item.by);
            Ok(s)

        }
        None => Err(String::from("Not headline")),
    }
}

pub fn print_filename_of_loaded_page(filen:&str, title:&str){
    println!("{} {} {} {}", "Downloaded page", title, "into file", filen);
}
pub fn could_not_load_page(title:&str){
    println!("Could not download to file with title {}", title);
}

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

    #[test]
    fn create_headline_with_author_test() {
        use std::fs::File;
        use std::io::prelude::*;
        use serde_json;
        let mut contents = String::new();
        File::open("res/test/item.json")
            .and_then(|mut file| file.read_to_string(&mut contents))
            .unwrap();
        let deserialized: HnItem = serde_json::from_str(&contents).unwrap();
        let index = 1;
        let s:String = create_headline_with_author(&deserialized, &index).unwrap();
        assert!(s.len() != 0);
        assert!(s.contains("1"));
        assert!(s.contains("dhouston"));
        assert!(s.contains("My YC app: Dropbox - Throw away your USB drive"));
        assert!(deserialized.by.len() != 0);
        assert!(deserialized.title.unwrap().len() != 0);
    }
}<commit_msg>Comment printing proto cli<commit_after>use models::*;
use std::iter::repeat;

pub fn print_headline_with_author(item: &HnItem, index: &i32) {
    let s = create_headline_with_author(item, index).unwrap(); // Not handling errs
    println!("{}", s);
}

fn create_headline_with_author(item: &HnItem, index: &i32) -> Result<String, String> {
    match item.title {
        Some(_) => {
            let s = format!("[{:3}] {:80} ~ {}",
                            index,
                            item.title.as_ref().unwrap(),
                            item.by);
            Ok(s)

        }
        None => Err(String::from("Not headline")),
    }
}

pub fn print_comments(items: &Vec<HnItem>) {
    println!("Comments: \n");
    for item in items {
        println!("{}", create_comment_string(item, &1).unwrap()); // todo depth and error handling
    }
}

fn create_comment_string(item:&HnItem, depth:&usize) -> Result<String, String> {
    let depthStr = repeat("-").take(*depth).collect::<String>();
    if(item.text.is_some()) {
        return Ok(format!("{} {} ~ {} on {}", depthStr, item.text.as_ref().unwrap(), &item.by, &item.time))
    }
    else {
        return Err(String::from("Could not get text for comment"))
    }
}

pub fn no_comments_for(numb:&usize) {
    println!("No comments for [{}]", numb);
}

pub fn print_filename_of_loaded_page(filen:&str, title:&str){
    println!("{} {} {} {}", "Downloaded page", title, "into file", filen);
}
pub fn could_not_load_page(title:&str){
    println!("Could not download to file with title {}", title);
}

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

    #[test]
    fn create_headline_with_author_test() {
        use std::fs::File;
        use std::io::prelude::*;
        use serde_json;
        let mut contents = String::new();
        File::open("res/test/item.json")
            .and_then(|mut file| file.read_to_string(&mut contents))
            .unwrap();
        let deserialized: HnItem = serde_json::from_str(&contents).unwrap();
        let index = 1;
        let s:String = create_headline_with_author(&deserialized, &index).unwrap();
        assert!(s.len() != 0);
        assert!(s.contains("1"));
        assert!(s.contains("dhouston"));
        assert!(s.contains("My YC app: Dropbox - Throw away your USB drive"));
        assert!(deserialized.by.len() != 0);
        assert!(deserialized.title.unwrap().len() != 0);
    }

    #[test]
    fn create_comment_string_test() {
        use std::fs::File;
        use std::io::prelude::*;
        use serde_json;
        let mut contents = String::new();
        File::open("res/test/children-item.json")
            .and_then(|mut file| file.read_to_string(&mut contents))
            .unwrap();
        let deserialized: HnItem = serde_json::from_str(&contents).unwrap();
        let commentStr = create_comment_string(&deserialized, &2).unwrap();
        assert!(commentStr.contains("is not a valid concern. Unless you are planning"));
        assert!(commentStr.contains("cholantesh"));
        assert!(commentStr.contains("-- "));

    }
}<|endoftext|>


<commit_before>// Copyright 2013-2016, The Gtk-rs Project Developers.
// See the COPYRIGHT file at the top-level directory of this distribution.
// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>

use libc::c_char;
use ffi;
use glib::translate::*;
use glib::object::{Downcast, IsA};
use std::ptr;
use ButtonsType;
use DialogFlags;
use MessageDialog;
use MessageType;
use Widget;
use Window;

impl MessageDialog {
    pub fn new<T: IsA<Window>>(parent: Option<&T>, flags: DialogFlags, type_: MessageType,
            buttons: ButtonsType, message: &str) -> MessageDialog {
        assert_initialized_main_thread!();
        unsafe {
            let message: Stash<*const c_char, _> = message.to_glib_none();
            Widget::from_glib_none(
                ffi::gtk_message_dialog_new(parent.to_glib_none().0, flags.to_glib(), type_.to_glib(), buttons.to_glib(),
                    b"%s\0".as_ptr() as *const c_char, message.0, ptr::null_mut::<*const c_char>()))
                .downcast_unchecked()
        }
    }
}
<commit_msg>Provide gtk::MessageDialog manual setter for secondary markup and text<commit_after>// Copyright 2013-2016, The Gtk-rs Project Developers.
// See the COPYRIGHT file at the top-level directory of this distribution.
// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>

use libc::c_char;
use ffi;
use glib::translate::*;
use glib::object::{Downcast, IsA};
use std::ptr;
use ButtonsType;
use DialogFlags;
use MessageDialog;
use MessageType;
use Widget;
use Window;

impl MessageDialog {
    pub fn new<T: IsA<Window>>(parent: Option<&T>, flags: DialogFlags, type_: MessageType,
            buttons: ButtonsType, message: &str) -> MessageDialog {
        assert_initialized_main_thread!();
        unsafe {
            let message: Stash<*const c_char, _> = message.to_glib_none();
            Widget::from_glib_none(
                ffi::gtk_message_dialog_new(parent.to_glib_none().0, flags.to_glib(), type_.to_glib(), buttons.to_glib(),
                    b"%s\0".as_ptr() as *const c_char, message.0, ptr::null::<c_char>()))
                .downcast_unchecked()
        }
    }

    pub fn set_secondary_markup(&self, message: Option<&str>) {
        match message {
            Some(m) => unsafe {
                let message: Stash<*const c_char, _> = m.to_glib_none();
                ffi::gtk_message_dialog_format_secondary_markup(
                    self.to_glib_none().0, b"%s\0".as_ptr() as *const c_char, message.0,
                    ptr::null::<c_char>())
            },
            None => unsafe {
                ffi::gtk_message_dialog_format_secondary_markup(
                    self.to_glib_none().0,
                    ptr::null::<c_char>())
            },
        }
    }

    pub fn set_secondary_text(&self, message: Option<&str>) {
        match message {
            Some(m) => unsafe {
                let message: Stash<*const c_char, _> = m.to_glib_none();
                ffi::gtk_message_dialog_format_secondary_text(
                    self.to_glib_none().0,
                    b"%s\0".as_ptr() as *const c_char, message.0, ptr::null::<c_char>())
            },
            None => unsafe {
                ffi::gtk_message_dialog_format_secondary_text(
                    self.to_glib_none().0,
                    ptr::null::<c_char>())
            },
        }
    }

}
<|endoftext|>


<commit_before>// Copyright 2015 Nicholas Bishop
//
// 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 std::f32;
use vector::Vec3f;

pub struct Aabb3f {
    pub min: Vec3f,
    pub max: Vec3f
}

impl Aabb3f {
    /// Create an empty Aabb3f with min initialized to +infinity and
    /// max is initialized to -infinity.
    pub fn new() -> Aabb3f {
        Aabb3f {
            min: Vec3f::from_scalar(f32::INFINITY),
            max: Vec3f::from_scalar(f32::NEG_INFINITY)
        }
    }

    /// True if the point intersects the box
    pub fn contains_point(&self, point: Vec3f) -> bool {
        (self.min.x <= point.x &&
         self.min.y <= point.y &&
         self.min.z <= point.z &&

         self.max.x >= point.x &&
         self.max.y >= point.y &&
         self.max.z >= point.z)
    }
}

#[test]
fn test_aabb3f_contains_point() {
    use vector::ZERO_3F;
    assert!(!Aabb3f::new().contains_point(ZERO_3F));
}
<commit_msg>Aabb updates<commit_after>// Copyright 2015 Nicholas Bishop
//
// 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 std::f32;
use vector::Vec3f;

pub struct Aabb3f {
    pub min: Vec3f,
    pub max: Vec3f
}

impl Aabb3f {
    /// Create an empty Aabb3f with min initialized to +infinity and
    /// max is initialized to -infinity.
    pub fn new() -> Aabb3f {
        Aabb3f {
            min: Vec3f::from_scalar(f32::INFINITY),
            max: Vec3f::from_scalar(f32::NEG_INFINITY)
        }
    }

    pub fn from_point(point: Vec3f) -> Aabb3f {
        Aabb3f {
            min: point,
            max: point
        }
    }

    /// True if the point intersects the box
    pub fn contains_point(&self, point: Vec3f) -> bool {
        (self.min.x <= point.x &&
         self.min.y <= point.y &&
         self.min.z <= point.z &&

         self.max.x >= point.x &&
         self.max.y >= point.y &&
         self.max.z >= point.z)
    }
}

#[test]
fn test_aabb3f_contains_point() {
    use vector::ZERO_3F;
    assert!(!Aabb3f::new().contains_point(ZERO_3F));
    assert!(Aabb3f::from_point(ZERO_3F).contains_point(ZERO_3F));
}
<|endoftext|>


<commit_before>//! This module provides the data structures that represent system information.
//!
//! They're always the same across all platforms.

use std::io;
pub use std::time::Duration;
pub use std::net::{Ipv4Addr, Ipv6Addr};
pub use std::collections::BTreeMap;
pub use chrono::{DateTime, Utc, NaiveDateTime};
pub use bytesize::ByteSize;
use std::ops::Sub;

/// A wrapper for a measurement that takes time.
///
/// Time should pass between getting the object and calling .done() on it.
pub struct DelayedMeasurement<T> {
    res: Box<Fn() -> io::Result<T>>,
}

impl<T> DelayedMeasurement<T> {
    #[inline(always)]
    pub fn new(f: Box<Fn() -> io::Result<T>>) -> DelayedMeasurement<T> {
        DelayedMeasurement { res: f }
    }

    #[inline(always)]
    pub fn done(&self) -> io::Result<T> {
        (self.res)()
    }
}

#[derive(Debug, Clone)]
pub struct CPULoad {
    pub user: f32,
    pub nice: f32,
    pub system: f32,
    pub interrupt: f32,
    pub idle: f32,
}

impl CPULoad {
    #[inline(always)]
    pub fn avg_add(self, rhs: &Self) -> Self {
        CPULoad {
            user: (self.user + rhs.user) / 2.0,
            nice: (self.nice + rhs.nice) / 2.0,
            system: (self.system + rhs.system) / 2.0,
            interrupt: (self.interrupt + rhs.interrupt) / 2.0,
            idle: (self.idle + rhs.idle) / 2.0,
        }
    }
}

#[derive(Debug, Clone, Copy)]
pub struct CpuTime {
    pub user: usize,
    pub nice: usize,
    pub system: usize,
    pub interrupt: usize,
    pub idle: usize,
    pub other: usize,
}

impl<'a> Sub<&'a CpuTime> for CpuTime {
    type Output = CpuTime;

    #[inline(always)]
    fn sub(self, rhs: &CpuTime) -> CpuTime {
        CpuTime {
            user: self.user.saturating_sub(rhs.user),
            nice: self.nice.saturating_sub(rhs.nice),
            system: self.system.saturating_sub(rhs.system),
            interrupt: self.interrupt.saturating_sub(rhs.interrupt),
            idle: self.idle.saturating_sub(rhs.idle),
            other: self.other.saturating_sub(rhs.other),
        }
    }
}

impl CpuTime {
    pub fn to_cpuload(&self) -> CPULoad {
        let total = self.user + self.nice + self.system + self.interrupt + self.idle + self.other;
        if total == 0 {
            CPULoad {
                user: 0.0,
                nice: 0.0,
                system: 0.0,
                interrupt: 0.0,
                idle: 0.0,
            }
        } else {
            CPULoad {
                user: self.user as f32 / total as f32,
                nice: self.nice as f32 / total as f32,
                system: self.system as f32 / total as f32,
                interrupt: self.interrupt as f32 / total as f32,
                idle: self.idle as f32 / total as f32,
            }
        }
    }
}

#[derive(Debug, Clone)]
pub struct LoadAverage {
    pub one: f32,
    pub five: f32,
    pub fifteen: f32,
}

#[cfg(target_os = "windows")]
#[derive(Debug, Clone)]
pub struct PlatformMemory {
    pub load: u32,
    pub total_phys: ByteSize,
    pub avail_phys: ByteSize,
    pub total_pagefile: ByteSize,
    pub avail_pagefile: ByteSize,
    pub total_virt: ByteSize,
    pub avail_virt: ByteSize,
    pub avail_ext: ByteSize,
}

#[cfg(target_os = "freebsd")]
#[derive(Debug, Clone)]
pub struct PlatformMemory {
    pub active: ByteSize,
    pub inactive: ByteSize,
    pub wired: ByteSize,
    pub cache: ByteSize,
    pub zfs_arc: ByteSize,
    pub free: ByteSize,
}

#[cfg(target_os = "openbsd")]
#[derive(Debug, Clone)]
pub struct PlatformMemory {
    pub active: ByteSize,
    pub inactive: ByteSize,
    pub wired: ByteSize,
    pub cache: ByteSize,
    pub free: ByteSize,
    pub paging: ByteSize,
}

#[cfg(target_os = "linux")]
#[derive(Debug, Clone)]
pub struct PlatformMemory {
    pub meminfo: BTreeMap<String, ByteSize>,
}

#[derive(Debug, Clone)]
pub struct Memory {
    pub total: ByteSize,
    pub free: ByteSize,
    pub platform_memory: PlatformMemory,
}

#[derive(Debug, Clone)]
pub struct BatteryLife {
    pub remaining_capacity: f32,
    pub remaining_time: Duration,
}

#[derive(Debug, Clone)]
pub struct Filesystem {
    pub files: usize,
    pub free: ByteSize,
    pub avail: ByteSize,
    pub total: ByteSize,
    pub name_max: usize,
    pub fs_type: String,
    pub fs_mounted_from: String,
    pub fs_mounted_on: String,
}

#[derive(Debug, Clone, PartialEq)]
pub enum IpAddr {
    Empty,
    Unsupported,
    V4(Ipv4Addr),
    V6(Ipv6Addr),
}

#[derive(Debug, Clone)]
pub struct NetworkAddrs {
    pub addr: IpAddr,
    pub netmask: IpAddr,
}

#[derive(Debug, Clone)]
pub struct Network {
    pub name: String,
    pub addrs: Vec<NetworkAddrs>,
}

#[derive(Debug, Clone)]
pub struct BlockDeviceStats {
    pub name: String,
    pub read_ios: u64,
    pub read_merges: u64,
    pub read_sectors: u64,
    pub read_ticks: u64,
    pub write_ios: u64,
    pub write_merges: u64,
    pub write_sectors: u64,
    pub write_ticks: u64,
    pub in_flight: u64,
    pub io_ticks: u64,
    pub time_in_queue: u64
}
<commit_msg>Move block stats struct<commit_after>//! This module provides the data structures that represent system information.
//!
//! They're always the same across all platforms.

use std::io;
pub use std::time::Duration;
pub use std::net::{Ipv4Addr, Ipv6Addr};
pub use std::collections::BTreeMap;
pub use chrono::{DateTime, Utc, NaiveDateTime};
pub use bytesize::ByteSize;
use std::ops::Sub;

/// A wrapper for a measurement that takes time.
///
/// Time should pass between getting the object and calling .done() on it.
pub struct DelayedMeasurement<T> {
    res: Box<Fn() -> io::Result<T>>,
}

impl<T> DelayedMeasurement<T> {
    #[inline(always)]
    pub fn new(f: Box<Fn() -> io::Result<T>>) -> DelayedMeasurement<T> {
        DelayedMeasurement { res: f }
    }

    #[inline(always)]
    pub fn done(&self) -> io::Result<T> {
        (self.res)()
    }
}

#[derive(Debug, Clone)]
pub struct CPULoad {
    pub user: f32,
    pub nice: f32,
    pub system: f32,
    pub interrupt: f32,
    pub idle: f32,
}

impl CPULoad {
    #[inline(always)]
    pub fn avg_add(self, rhs: &Self) -> Self {
        CPULoad {
            user: (self.user + rhs.user) / 2.0,
            nice: (self.nice + rhs.nice) / 2.0,
            system: (self.system + rhs.system) / 2.0,
            interrupt: (self.interrupt + rhs.interrupt) / 2.0,
            idle: (self.idle + rhs.idle) / 2.0,
        }
    }
}

#[derive(Debug, Clone, Copy)]
pub struct CpuTime {
    pub user: usize,
    pub nice: usize,
    pub system: usize,
    pub interrupt: usize,
    pub idle: usize,
    pub other: usize,
}

impl<'a> Sub<&'a CpuTime> for CpuTime {
    type Output = CpuTime;

    #[inline(always)]
    fn sub(self, rhs: &CpuTime) -> CpuTime {
        CpuTime {
            user: self.user.saturating_sub(rhs.user),
            nice: self.nice.saturating_sub(rhs.nice),
            system: self.system.saturating_sub(rhs.system),
            interrupt: self.interrupt.saturating_sub(rhs.interrupt),
            idle: self.idle.saturating_sub(rhs.idle),
            other: self.other.saturating_sub(rhs.other),
        }
    }
}

impl CpuTime {
    pub fn to_cpuload(&self) -> CPULoad {
        let total = self.user + self.nice + self.system + self.interrupt + self.idle + self.other;
        if total == 0 {
            CPULoad {
                user: 0.0,
                nice: 0.0,
                system: 0.0,
                interrupt: 0.0,
                idle: 0.0,
            }
        } else {
            CPULoad {
                user: self.user as f32 / total as f32,
                nice: self.nice as f32 / total as f32,
                system: self.system as f32 / total as f32,
                interrupt: self.interrupt as f32 / total as f32,
                idle: self.idle as f32 / total as f32,
            }
        }
    }
}

#[derive(Debug, Clone)]
pub struct LoadAverage {
    pub one: f32,
    pub five: f32,
    pub fifteen: f32,
}

#[cfg(target_os = "windows")]
#[derive(Debug, Clone)]
pub struct PlatformMemory {
    pub load: u32,
    pub total_phys: ByteSize,
    pub avail_phys: ByteSize,
    pub total_pagefile: ByteSize,
    pub avail_pagefile: ByteSize,
    pub total_virt: ByteSize,
    pub avail_virt: ByteSize,
    pub avail_ext: ByteSize,
}

#[cfg(target_os = "freebsd")]
#[derive(Debug, Clone)]
pub struct PlatformMemory {
    pub active: ByteSize,
    pub inactive: ByteSize,
    pub wired: ByteSize,
    pub cache: ByteSize,
    pub zfs_arc: ByteSize,
    pub free: ByteSize,
}

#[cfg(target_os = "openbsd")]
#[derive(Debug, Clone)]
pub struct PlatformMemory {
    pub active: ByteSize,
    pub inactive: ByteSize,
    pub wired: ByteSize,
    pub cache: ByteSize,
    pub free: ByteSize,
    pub paging: ByteSize,
}

#[cfg(target_os = "linux")]
#[derive(Debug, Clone)]
pub struct PlatformMemory {
    pub meminfo: BTreeMap<String, ByteSize>,
}

#[derive(Debug, Clone)]
pub struct Memory {
    pub total: ByteSize,
    pub free: ByteSize,
    pub platform_memory: PlatformMemory,
}

#[derive(Debug, Clone)]
pub struct BatteryLife {
    pub remaining_capacity: f32,
    pub remaining_time: Duration,
}

#[derive(Debug, Clone)]
pub struct Filesystem {
    pub files: usize,
    pub free: ByteSize,
    pub avail: ByteSize,
    pub total: ByteSize,
    pub name_max: usize,
    pub fs_type: String,
    pub fs_mounted_from: String,
    pub fs_mounted_on: String,
}

#[derive(Debug, Clone)]
pub struct BlockDeviceStats {
    pub name: String,
    pub read_ios: u64,
    pub read_merges: u64,
    pub read_sectors: u64,
    pub read_ticks: u64,
    pub write_ios: u64,
    pub write_merges: u64,
    pub write_sectors: u64,
    pub write_ticks: u64,
    pub in_flight: u64,
    pub io_ticks: u64,
    pub time_in_queue: u64
}

#[derive(Debug, Clone, PartialEq)]
pub enum IpAddr {
    Empty,
    Unsupported,
    V4(Ipv4Addr),
    V6(Ipv6Addr),
}

#[derive(Debug, Clone)]
pub struct NetworkAddrs {
    pub addr: IpAddr,
    pub netmask: IpAddr,
}

#[derive(Debug, Clone)]
pub struct Network {
    pub name: String,
    pub addrs: Vec<NetworkAddrs>,
}
<|endoftext|>


<commit_before>extern crate hyper;
extern crate url;

use hyper::{HttpError, HttpResult, Url};
use hyper::client::Request;
use hyper::header::ContentLength;
use url::form_urlencoded;

fn get_content(url: &str) -> HttpResult<String> {
    let url = match Url::parse(url) {
        Ok(url) => url,
        Err(_) => return Err(HttpError::HttpUriError),
    };
    let fresh_request = try!(Request::get(url));
    let streaming_request = try!(fresh_request.start());
    let mut response = try!(streaming_request.send());
    Ok(try!(response.read_to_string()))
}

type Query<'a> = Vec<(&'a str, &'a str)>;

fn post_query(url: &str, query: Query) -> HttpResult<String> {
    let url = match Url::parse(url) {
        Ok(url) => url,
        Err(_) => return Err(HttpError::HttpUriError),
    };
    let body = form_urlencoded::serialize(query.into_iter());
    let mut fresh_request = try!(Request::post(url));
    fresh_request.headers_mut().set(ContentLength(body.len()));
    let mut streaming_request = try!(fresh_request.start());
    try!(streaming_request.write_str(body[]));
    let mut response = try!(streaming_request.send());
    Ok(try!(response.read_to_string()))
}

fn main() {
    println!("24 days of Rust - hyper (day 5)");
    println!("{}", get_content("http://httpbin.org/status/200"));
    let query = vec![("key", "value"), ("foo", "bar")];
    println!("{}", post_query("http://httpbin.org/post", query));
}
<commit_msg>Posting JSON with automatic serialization.<commit_after>extern crate hyper;
extern crate serialize;
extern crate url;

use hyper::{HttpError, HttpResult, Url};
use hyper::client::Request;
use hyper::header::ContentLength;
use serialize::{Encodable, json};
use std::io::IoError;
use url::form_urlencoded;

fn get_content(url: &str) -> HttpResult<String> {
    let url = match Url::parse(url) {
        Ok(url) => url,
        Err(_) => return Err(HttpError::HttpUriError),
    };
    let fresh_request = try!(Request::get(url));
    let streaming_request = try!(fresh_request.start());
    let mut response = try!(streaming_request.send());
    Ok(try!(response.read_to_string()))
}

type Query<'a> = Vec<(&'a str, &'a str)>;

fn post_query(url: &str, query: Query) -> HttpResult<String> {
    let url = match Url::parse(url) {
        Ok(url) => url,
        Err(_) => return Err(HttpError::HttpUriError),
    };
    let body = form_urlencoded::serialize(query.into_iter());
    let mut fresh_request = try!(Request::post(url));
    fresh_request.headers_mut().set(ContentLength(body.len()));
    let mut streaming_request = try!(fresh_request.start());
    try!(streaming_request.write_str(body[]));
    let mut response = try!(streaming_request.send());
    Ok(try!(response.read_to_string()))
}

fn post_json<'a, T: Encodable<json::Encoder<'a>, IoError>>(url: &str, payload: &T) -> HttpResult<String> {
    let body = json::encode(payload);
    let url = match Url::parse(url) {
        Ok(url) => url,
        Err(_) => return Err(HttpError::HttpUriError),
    };
    let mut fresh_request = try!(Request::post(url));
    fresh_request.headers_mut().set(ContentLength(body.len()));
    let mut streaming_request = try!(fresh_request.start());
    try!(streaming_request.write_str(body[]));
    let mut response = try!(streaming_request.send());
    Ok(try!(response.read_to_string()))
}

#[deriving(Decodable, Encodable)]
struct Movie {
    title: String,
    bad_guy: String,
    pub_year: uint,
}

fn main() {
    println!("24 days of Rust - hyper (day 5)");
    println!("{}", get_content("http://httpbin.org/status/200"));
    let query = vec![("key", "value"), ("foo", "bar")];
    println!("{}", post_query("http://httpbin.org/post", query));
    let movie = Movie {
        title: "You Only Live Twice".to_string(),
        bad_guy: "Blofeld".to_string(),
        pub_year: 1967,
    };
    println!("{}", post_json("http://httpbin.org/post", &movie));
}
<|endoftext|>


<commit_before>use super::ConcurrentDevice;

extern crate memmap;
use z80e_core_rust::{ IoDevice };
use self::memmap::{ Mmap };
pub use self::memmap::{ MmapView, Protection };

use std::sync::{ Arc, Condvar, Mutex };
use std::sync::atomic::{ AtomicBool, AtomicUsize, Ordering };
use std::io::{ self, ErrorKind, Write };
use std::path::Path;
use std::{ str, mem, ptr };

// Sector size must be a power of two
const SECTOR_SIZE: u16 = 128;

// Max disk must be a power of two <= 16. If changed, you must insert/delete Nones in DiskController::run()
const MAX_DISK: u8 = 16;

// Status port bitflags.
//     8-bit values, but "must" be usize to avoid overly-verbose casts for AtomicUsize calls.
const COMMAND_READY: usize = 1 << 0;
const DATA_READY: usize = 1 << 1;
const ERROR: usize = 1 << 7;

// Commands.
const NOP: u8 = 0;
const SEL_DSK: u8 = 1;
const SEL_TRK: u8 = 2;
const SEL_SEC: u8 = 3;
const READ: u8 = 4;
const WRITE: u8 = 5;
const RESET: u8 = 6;
const OPEN: u8 = 7;
const CLOSE: u8 = 8;
const DPB: u8 = 9;

#[derive(Clone)]
pub struct DiskController {
    pub status: Arc<AtomicUsize>,
    command_cond: Arc<Condvar>,
    buffer: Arc<Mutex<Buffer>>,
    parameters: Arc<Mutex<Parameters>>,
}

impl DiskController {
    pub fn new() -> DiskController {
        DiskController {
            status: Arc::new(AtomicUsize::new(DATA_READY)),
            command_cond: Arc::new(Condvar::new()),
            buffer: Arc::new(Mutex::new(Buffer::new())),
            parameters: Arc::new(Mutex::new(Parameters::new())),
        }
    }
    pub fn status_port(&self) -> StatusPort {
        StatusPort::new(self.clone())
    }
    pub fn data_port(&self) -> DataPort {
        DataPort::new(self.clone())
    }
}

struct Buffer {
    bytes: [u8; SECTOR_SIZE as usize],
    i: u16,
}

impl Buffer {
    fn new() -> Buffer {
        Buffer {
            bytes: [0; SECTOR_SIZE as usize],
            i: 0,
        }
    }
}

struct Parameters {
    disk: u8,
    track: u16,
    sector: u16,
    command: u8,
    do_command: bool,
}

impl Parameters {
    fn new() -> Parameters {
        Parameters {
            disk: 0,
            track: 0,
            sector: 0,
            command: NOP,
            do_command: false,
        }
    }
}

struct StatusPort {
    controller: DiskController,
}

impl StatusPort {
    fn new(controller: DiskController) -> StatusPort {
        StatusPort {
            controller: controller,
        }
    }
}

impl IoDevice for StatusPort {
    fn read_in(&self) -> u8 {
        (self.controller.status.load(Ordering::SeqCst) & 0xff) as u8
    }
    fn write_out(&mut self, value: u8) {
        if (self.controller.status.fetch_and(!COMMAND_READY, Ordering::SeqCst) & COMMAND_READY) != 0 {
            let mut params = self.controller.parameters.lock().unwrap();
            params.command = value;
            params.do_command = true;
            self.controller.command_cond.notify_one();
        } else {
            self.controller.status.fetch_or(COMMAND_READY | ERROR, Ordering::SeqCst);
            let _ = writeln!(io::stderr(), "disk: Attempted to write command register when not ready.");
        }
    }
}

struct DataPort {
    controller: DiskController,
}

impl DataPort {
    fn new(controller: DiskController) -> DataPort {
        DataPort {
            controller: controller,
        }
    }
}

impl IoDevice for DataPort {
    fn read_in (&self) -> u8 {
        if (self.controller.status.fetch_and(!DATA_READY, Ordering::SeqCst) & DATA_READY) != 0 {
            let byte;
            {
                let mut buffer = self.controller.buffer.lock().unwrap();
                buffer.i = ((buffer.i as usize + 1) & (SECTOR_SIZE - 1) as usize) as u16;
                byte = buffer.bytes[buffer.i as usize];
            }
            self.controller.status.fetch_or(DATA_READY, Ordering::SeqCst);
            byte
        } else {
            self.controller.status.fetch_or(ERROR, Ordering::SeqCst);
            let _ = writeln!(io::stderr(), "disk: Attempted to read data register when not ready.");
            0
        }
    }
    fn write_out(&mut self, value: u8) {
        if (self.controller.status.fetch_and(!DATA_READY, Ordering::SeqCst) & DATA_READY) != 0 {
            {
                let mut buffer = self.controller.buffer.lock().unwrap();
                buffer.bytes[buffer.i as usize] = value;
                buffer.i = ((buffer.i as usize + 1) & (SECTOR_SIZE - 1) as usize) as u16;
            }
            self.controller.status.fetch_or(DATA_READY, Ordering::SeqCst);
        } else {
            self.controller.status.fetch_or(ERROR, Ordering::SeqCst);
            let _ = writeln!(io::stderr(), "disk: Attempted to write data register when not ready.");
        }
    }
}

pub struct Disk {
    view: MmapView,
    pub tracks: u16,
    pub spt: u16,
    dpb: [u8; 16],
}

impl Disk {
    pub fn open<T: AsRef<Path>>(path: &T, protection: Protection) -> io::Result<Disk> {
        let mut file = try!(Mmap::open_path(path, protection)).into_view();
        let (header, image) = try!(file.split_at(128));
        let header = unsafe { header.as_slice() };
        if match str::from_utf8(&header[0..10]) {
            Ok(x) => x,
            Err(err) => return Err(io::Error::new(ErrorKind::InvalidData, "Invalid image header encoding.")),
        }!= "<CPM_Disk>" {
            return Err(io::Error::new(ErrorKind::InvalidData, "Not a valid disk image."));
        }
        let mut dpb: [u8; 16] = [0; 16];
        for i in 0..16 {
            dpb[i] = header[32 + i];
        }
        let spt: u16 = (dpb[0] as u16) | ((dpb[1] as u16) << 8);
        let bsh: u16 = (dpb[2] as u16);
        let dsm: u16 = (dpb[5] as u16) | ((dpb[6] as u16) << 8);
        let off: u16 = (dpb[13] as u16) | ((dpb[14] as u16) << 8);
        let tracks: u16 = (dsm + 1) * (1 << bsh) / spt + off;
        Ok(Disk {
            view: image,
            tracks: tracks,
            spt: spt,
            dpb: dpb
        })
    }
}

impl ConcurrentDevice for DiskController {
    fn run(&mut self, die: Arc<AtomicBool>) {
        let mut disks = unsafe {
            let mut disks: [Option<Disk>; MAX_DISK as usize] = mem::uninitialized();
            for disk in disks.iter_mut() {
                ptr::write(disk, None);
            };
            disks
        };
        let mut parameters = self.parameters.lock().unwrap();
        loop {
            self.status.fetch_or(COMMAND_READY, Ordering::SeqCst);
            while !(*parameters).do_command {
                parameters = self.command_cond.wait(parameters).unwrap();
            }
            if die.load(Ordering::Acquire) { break; }
            let status = self.status.fetch_and(!DATA_READY, Ordering::SeqCst);
            if (status & ERROR) != 0 && parameters.command != RESET {
                self.status.fetch_or(DATA_READY, Ordering::SeqCst);
                continue;
            };
            {
                let mut buffer = self.buffer.lock().unwrap();
                buffer.i = 0;
                match parameters.command {
                    NOP => (),
                    SEL_DSK => {
                        if buffer.bytes[0] < MAX_DISK {
                            parameters.disk = buffer.bytes[0];
                        } else {
                            self.status.fetch_or(ERROR, Ordering::SeqCst);
                        }
                    },
                    SEL_TRK => {
                        let track = buffer.bytes[0] as u16 & ((buffer.bytes[1] as u16) << 8);
                        match disks[parameters.disk as usize] {
                            Some(ref disk) => {
                                if track < disk.tracks {
                                    parameters.track = track;
                                } else {
                                    self.status.fetch_or(ERROR, Ordering::SeqCst);
                                }
                            },
                            None => {
                                self.status.fetch_or(ERROR, Ordering::SeqCst);
                            }
                        }
                    },
                    SEL_SEC => {
                        match disks[parameters.disk as usize] {
                            Some(ref disk) => {
                                let sector = buffer.bytes[0] as u16 & ((buffer.bytes[1] as u16) << 8);
                                if sector < disk.spt {
                                    parameters.sector = sector;
                                } else {
                                    self.status.fetch_or(ERROR, Ordering::SeqCst);
                                }
                            },
                            None => {
                                self.status.fetch_or(ERROR, Ordering::SeqCst);
                            },
                        }
                    },
                    READ => {
                        match disks[parameters.disk as usize] {
                            Some(ref disk) => {
                                let off = ((parameters.track as usize * disk.spt as usize)
                                           + parameters.sector as usize) * SECTOR_SIZE as usize;
                                let bytes = unsafe { disk.view.as_slice() };
                                for (i, byte) in buffer.bytes.iter_mut().enumerate() {
                                    *byte = bytes[off + i];
                                }
                            },
                            None => {
                                self.status.fetch_or(ERROR, Ordering::SeqCst);
                            },
                        }
                    },
                    WRITE => {
                        match disks[parameters.disk as usize] {
                            Some(ref mut disk) => {
                                let off = ((parameters.track as usize * disk.spt as usize)
                                           + parameters.sector as usize) * SECTOR_SIZE as usize;
                                let bytes = unsafe { disk.view.as_mut_slice() };
                                for (i, byte) in buffer.bytes.iter().enumerate() {
                                    bytes[i] = *byte;
                                };
                            },
                            None => {
                                self.status.fetch_or(ERROR, Ordering::SeqCst);
                            },
                        }

                    }
                    RESET => {
                        parameters.disk = 0;
                        parameters.track = 0;
                        parameters.sector = 0;
                        parameters.command = NOP;
                        self.status.fetch_and(!ERROR, Ordering::SeqCst);
                    },
                    OPEN => {
                        match str::from_utf8(buffer.bytes.split(|a| *a == 0).next().unwrap()) {
                            Ok(file_name) => {
                                match Disk::open(&file_name, Protection::ReadWrite) {
                                    Ok(disk) => {
                                        disks[parameters.disk as usize] = Some(disk);
                                    },
                                    Err(err) => {
                                        let mut stderr = io::stderr();
                                        let _ = writeln!(stderr, "disk: Failed to open file: {}", file_name);
                                        let _ = writeln!(stderr, "Error:\n\t{}", err);
                                        self.status.fetch_or(ERROR, Ordering::SeqCst);
                                    },
                                }
                            },
                            Err(err) => {
                                let _ = write!(io::stderr(), "disk: Bad UTF-8 in file name.\nError:\n\t{}\n", err);
                                self.status.fetch_or(ERROR, Ordering::SeqCst);
                            }
                        }
                    },
                    CLOSE => {
                        disks[parameters.disk as usize] = None;
                    },
                    DPB => {
                        match disks[parameters.disk as usize] {
                            Some(ref disk) => {
                                for (a, b) in disk.dpb.iter().zip(buffer.bytes.iter_mut()) {
                                    *b = *a;
                                }
                            },
                            None => {
                                self.status.fetch_or(ERROR, Ordering::SeqCst);
                            }
                        }
                    },
                    _ => {
                        self.status.fetch_or(ERROR, Ordering::SeqCst);
                        let _ = write!(io::stderr(), "disk: System sent bad command: {:02X}\n", parameters.command);
                    },
                }
            }
            self.status.fetch_or(DATA_READY, Ordering::SeqCst);
        }
    }
}
<commit_msg>Bugs!<commit_after>use super::ConcurrentDevice;

extern crate memmap;
use z80e_core_rust::{ IoDevice };
use self::memmap::{ Mmap };
pub use self::memmap::{ MmapView, Protection };

use std::sync::{ Arc, Condvar, Mutex };
use std::sync::atomic::{ AtomicBool, AtomicUsize, Ordering };
use std::io::{ self, ErrorKind, Write };
use std::path::Path;
use std::{ str, mem, ptr };

// Sector size must be a power of two
const SECTOR_SIZE: u16 = 128;

// Max disk must be a power of two <= 16. If changed, you must insert/delete Nones in DiskController::run()
const MAX_DISK: u8 = 16;

// Status port bitflags.
//     8-bit values, but "must" be usize to avoid overly-verbose casts for AtomicUsize calls.
const COMMAND_READY: usize = 1 << 0;
const DATA_READY: usize = 1 << 1;
const ERROR: usize = 1 << 7;

// Commands.
const NOP: u8 = 0;
const SEL_DSK: u8 = 1;
const SEL_TRK: u8 = 2;
const SEL_SEC: u8 = 3;
const READ: u8 = 4;
const WRITE: u8 = 5;
const RESET: u8 = 6;
const OPEN: u8 = 7;
const CLOSE: u8 = 8;
const DPB: u8 = 9;

#[derive(Clone)]
pub struct DiskController {
    pub status: Arc<AtomicUsize>,
    command_cond: Arc<Condvar>,
    buffer: Arc<Mutex<Buffer>>,
    parameters: Arc<Mutex<Parameters>>,
}

impl DiskController {
    pub fn new() -> DiskController {
        DiskController {
            status: Arc::new(AtomicUsize::new(DATA_READY)),
            command_cond: Arc::new(Condvar::new()),
            buffer: Arc::new(Mutex::new(Buffer::new())),
            parameters: Arc::new(Mutex::new(Parameters::new())),
        }
    }
    pub fn status_port(&self) -> StatusPort {
        StatusPort::new(self.clone())
    }
    pub fn data_port(&self) -> DataPort {
        DataPort::new(self.clone())
    }
}

struct Buffer {
    bytes: [u8; SECTOR_SIZE as usize],
    i: u16,
}

impl Buffer {
    fn new() -> Buffer {
        Buffer {
            bytes: [0; SECTOR_SIZE as usize],
            i: 0,
        }
    }
}

struct Parameters {
    disk: u8,
    track: u16,
    sector: u16,
    command: u8,
    do_command: bool,
}

impl Parameters {
    fn new() -> Parameters {
        Parameters {
            disk: 0,
            track: 0,
            sector: 0,
            command: NOP,
            do_command: false,
        }
    }
}

struct StatusPort {
    controller: DiskController,
}

impl StatusPort {
    fn new(controller: DiskController) -> StatusPort {
        StatusPort {
            controller: controller,
        }
    }
}

impl IoDevice for StatusPort {
    fn read_in(&self) -> u8 {
        (self.controller.status.load(Ordering::SeqCst) & 0xff) as u8
    }
    fn write_out(&mut self, value: u8) {
        if (self.controller.status.fetch_and(!COMMAND_READY, Ordering::SeqCst) & COMMAND_READY) != 0 {
            let mut params = self.controller.parameters.lock().unwrap();
            params.command = value;
            params.do_command = true;
            self.controller.command_cond.notify_one();
        } else {
            self.controller.status.fetch_or(COMMAND_READY | ERROR, Ordering::SeqCst);
            let _ = writeln!(io::stderr(), "disk: Attempted to write command register when not ready.");
        }
    }
}

struct DataPort {
    controller: DiskController,
}

impl DataPort {
    fn new(controller: DiskController) -> DataPort {
        DataPort {
            controller: controller,
        }
    }
}

impl IoDevice for DataPort {
    fn read_in (&self) -> u8 {
        if (self.controller.status.fetch_and(!DATA_READY, Ordering::SeqCst) & DATA_READY) != 0 {
            let byte;
            {
                let mut buffer = self.controller.buffer.lock().unwrap();
                buffer.i = ((buffer.i as usize + 1) & (SECTOR_SIZE - 1) as usize) as u16;
                byte = buffer.bytes[buffer.i as usize];
            }
            self.controller.status.fetch_or(DATA_READY, Ordering::SeqCst);
            byte
        } else {
            self.controller.status.fetch_or(ERROR, Ordering::SeqCst);
            let _ = writeln!(io::stderr(), "disk: Attempted to read data register when not ready.");
            0
        }
    }
    fn write_out(&mut self, value: u8) {
        if (self.controller.status.fetch_and(!DATA_READY, Ordering::SeqCst) & DATA_READY) != 0 {
            {
                let mut buffer = self.controller.buffer.lock().unwrap();
                buffer.bytes[buffer.i as usize] = value;
                buffer.i = ((buffer.i as usize + 1) & (SECTOR_SIZE - 1) as usize) as u16;
            }
            self.controller.status.fetch_or(DATA_READY, Ordering::SeqCst);
        } else {
            self.controller.status.fetch_or(ERROR, Ordering::SeqCst);
            let _ = writeln!(io::stderr(), "disk: Attempted to write data register when not ready.");
        }
    }
}

pub struct Disk {
    view: MmapView,
    pub tracks: u16,
    pub spt: u16,
    dpb: [u8; 16],
}

impl Disk {
    pub fn open<T: AsRef<Path>>(path: &T, protection: Protection) -> io::Result<Disk> {
        let mut file = try!(Mmap::open_path(path, protection)).into_view();
        let (header, image) = try!(file.split_at(128));
        let header = unsafe { header.as_slice() };
        if match str::from_utf8(&header[0..10]) {
            Ok(x) => x,
            Err(err) => return Err(io::Error::new(ErrorKind::InvalidData, "Invalid image header encoding.")),
        }!= "<CPM_Disk>" {
            return Err(io::Error::new(ErrorKind::InvalidData, "Not a valid disk image."));
        }
        let mut dpb: [u8; 16] = [0; 16];
        for i in 0..16 {
            dpb[i] = header[32 + i];
        }
        let spt: u16 = (dpb[0] as u16) | ((dpb[1] as u16) << 8);
        let bsh: u16 = (dpb[2] as u16);
        let dsm: u16 = (dpb[5] as u16) | ((dpb[6] as u16) << 8);
        let off: u16 = (dpb[13] as u16) | ((dpb[14] as u16) << 8);
        let tracks: u16 = (dsm + 1) * (1 << bsh) / spt + off;
        Ok(Disk {
            view: image,
            tracks: tracks,
            spt: spt,
            dpb: dpb
        })
    }
}

impl ConcurrentDevice for DiskController {
    fn run(&mut self, die: Arc<AtomicBool>) {
        let mut disks = unsafe {
            let mut disks: [Option<Disk>; MAX_DISK as usize] = mem::uninitialized();
            for disk in disks.iter_mut() {
                ptr::write(disk, None);
            };
            disks
        };
        let mut parameters = self.parameters.lock().unwrap();
        loop {
            self.status.fetch_or(COMMAND_READY, Ordering::SeqCst);
            while !(*parameters).do_command {
                parameters = self.command_cond.wait(parameters).unwrap();
            }
            if die.load(Ordering::Acquire) { break; }
            let status = self.status.fetch_and(!DATA_READY, Ordering::SeqCst);
            if (status & ERROR) != 0 && parameters.command != RESET {
                self.status.fetch_or(DATA_READY, Ordering::SeqCst);
                continue;
            };
            {
                let mut buffer = self.buffer.lock().unwrap();
                buffer.i = 0;
                match parameters.command {
                    NOP => (),
                    SEL_DSK => {
                        if buffer.bytes[0] < MAX_DISK {
                            parameters.disk = buffer.bytes[0];
                        } else {
                            self.status.fetch_or(ERROR, Ordering::SeqCst);
                        }
                    },
                    SEL_TRK => {
                        let track = buffer.bytes[0] as u16 | ((buffer.bytes[1] as u16) << 8);
                        match disks[parameters.disk as usize] {
                            Some(ref disk) => {
                                if track < disk.tracks {
                                    parameters.track = track;
                                } else {
                                    self.status.fetch_or(ERROR, Ordering::SeqCst);
                                }
                            },
                            None => {
                                self.status.fetch_or(ERROR, Ordering::SeqCst);
                            }
                        }
                    },
                    SEL_SEC => {
                        match disks[parameters.disk as usize] {
                            Some(ref disk) => {
                                let sector = buffer.bytes[0] as u16 | ((buffer.bytes[1] as u16) << 8);
                                if sector < disk.spt {
                                    parameters.sector = sector;
                                } else {
                                    self.status.fetch_or(ERROR, Ordering::SeqCst);
                                }
                            },
                            None => {
                                self.status.fetch_or(ERROR, Ordering::SeqCst);
                            },
                        }
                    },
                    READ => {
                        match disks[parameters.disk as usize] {
                            Some(ref disk) => {
                                let off = ((parameters.track as usize * disk.spt as usize)
                                           + parameters.sector as usize) * SECTOR_SIZE as usize;
                                let bytes = unsafe { disk.view.as_slice() };
                                for (i, byte) in buffer.bytes.iter_mut().enumerate() {
                                    *byte = bytes[off + i];
                                }
                            },
                            None => {
                                self.status.fetch_or(ERROR, Ordering::SeqCst);
                            },
                        }
                    },
                    WRITE => {
                        match disks[parameters.disk as usize] {
                            Some(ref mut disk) => {
                                let off = ((parameters.track as usize * disk.spt as usize)
                                           + parameters.sector as usize) * SECTOR_SIZE as usize;
                                let bytes = unsafe { disk.view.as_mut_slice() };
                                for (i, byte) in buffer.bytes.iter().enumerate() {
                                    bytes[i] = *byte;
                                };
                            },
                            None => {
                                self.status.fetch_or(ERROR, Ordering::SeqCst);
                            },
                        }

                    }
                    RESET => {
                        parameters.disk = 0;
                        parameters.track = 0;
                        parameters.sector = 0;
                        parameters.command = NOP;
                        self.status.fetch_and(!ERROR, Ordering::SeqCst);
                    },
                    OPEN => {
                        match str::from_utf8(buffer.bytes.split(|a| *a == 0).next().unwrap()) {
                            Ok(file_name) => {
                                match Disk::open(&file_name, Protection::ReadWrite) {
                                    Ok(disk) => {
                                        disks[parameters.disk as usize] = Some(disk);
                                    },
                                    Err(err) => {
                                        let mut stderr = io::stderr();
                                        let _ = writeln!(stderr, "disk: Failed to open file: {}", file_name);
                                        let _ = writeln!(stderr, "Error:\n\t{}", err);
                                        self.status.fetch_or(ERROR, Ordering::SeqCst);
                                    },
                                }
                            },
                            Err(err) => {
                                let _ = write!(io::stderr(), "disk: Bad UTF-8 in file name.\nError:\n\t{}\n", err);
                                self.status.fetch_or(ERROR, Ordering::SeqCst);
                            }
                        }
                    },
                    CLOSE => {
                        disks[parameters.disk as usize] = None;
                    },
                    DPB => {
                        match disks[parameters.disk as usize] {
                            Some(ref disk) => {
                                for (a, b) in disk.dpb.iter().zip(buffer.bytes.iter_mut()) {
                                    *b = *a;
                                }
                            },
                            None => {
                                self.status.fetch_or(ERROR, Ordering::SeqCst);
                            }
                        }
                    },
                    _ => {
                        self.status.fetch_or(ERROR, Ordering::SeqCst);
                        let _ = write!(io::stderr(), "disk: System sent bad command: {:02X}\n", parameters.command);
                    },
                }
            }
            self.status.fetch_or(DATA_READY, Ordering::SeqCst);
            parameters.do_command = false;
        }
    }
}
<|endoftext|>


<commit_before>use super::object::Object;
use super::object::Env;
use super::object::NULL;
use super::object::TRUE;
use super::object::FALSE;
use super::ast::Program;
use super::ast::Statement;
use super::ast::Expression;

fn to_boolean_object(val: bool) -> Object {
    if val {
        TRUE
    } else {
        FALSE
    }
}

pub struct Eval {
    env: Env,
}

impl Eval {
    pub fn new(env: Env) -> Self {
        Eval { env: env }
    }

    pub fn env(self) -> Env {
        self.env
    }

    pub fn eval(&mut self, program: Program) -> Object {
        self.eval_statements(&program.statements, false)
    }

    fn eval_statements(&mut self, stmts: &Vec<Statement>, nested: bool) -> Object {
        let mut result = NULL;

        for stmt in stmts {
            result = self.eval_statement(&stmt);

            match result {
                Object::ReturnValue(val) => {
                    if nested {
                        return Object::ReturnValue(val);
                    } else {
                        return *val;
                    }
                }
                r @ Object::Error(..) => {
                    return r;
                }
                _ => {
                    continue;
                }
            }
        }

        result
    }

    fn eval_statement(&mut self, stmt: &Statement) -> Object {
        match *stmt {
            Statement::Expression { ref expression, .. } => self.eval_expr(expression),
            Statement::Block { ref statements, .. } => self.eval_statements(statements, true),
            Statement::Return { ref value, .. } => {
                let mut return_val = NULL;
                if let Some(ref v) = *value {
                    return_val = self.eval_expr(v);
                }

                if return_val.is_error() {
                    return return_val;
                } else {
                    return Object::ReturnValue(Box::new(return_val));
                }
            }
            Statement::Let { ref value, ref name, .. } => {
                let val = self.eval_expr(value);
                if val.is_error() {
                    return val;
                }
                if let Expression::Identifier { ref value, .. } = *name {
                    self.env.set(value, val);
                    return NULL;
                } else {
                    return Object::Error("Internal interpreter error. Not an \
                                          Expression::Identifier in Statement::Let::expression."
                        .to_string());
                }
            }
        }
    }

    fn eval_expr(&mut self, expr: &Expression) -> Object {
        match *expr {
            Expression::IntegerLiteral { value, .. } => Object::Integer(value),
            Expression::Boolean { value, .. } => to_boolean_object(value),
            Expression::Prefix { ref operator, ref right, .. } => {
                let right = self.eval_expr(right);

                if right.is_error() {
                    return right;
                }

                self.eval_prefix_expr(operator, right)
            }
            Expression::Infix { ref left, ref right, ref operator, .. } => {
                let left = self.eval_expr(left);

                if left.is_error() {
                    return left;
                }

                let right = self.eval_expr(right);

                if right.is_error() {
                    return right;
                }

                self.eval_infix_expr(operator, left, right)
            }
            Expression::If { ref condition, ref consequence, ref alternative, .. } => {
                let cond = self.eval_expr(condition);

                if cond.is_error() {
                    return cond;
                }

                if cond.is_truthy() {
                    self.eval_statement(consequence)
                } else if let Some(ref alt) = *alternative {
                    self.eval_statement(alt)
                } else {
                    NULL
                }
            }
            Expression::Identifier { ref value, .. } => self.env.get(value),
            Expression::FunctionLiteral { ref parameters, ref body, .. } => {
                Object::Function {
                    parameters: parameters.clone(),
                    body: *body.clone(),
                    env: self.env.clone(),
                }
            }
            Expression::Call { ref function, ref arguments, .. } => {
                let func = self.eval_expr(function);
                if func.is_error() {
                    return func;
                }

                let mut args = self.eval_expressions(arguments);
                let fail = args.len() == 1 && args[0].is_error();
                if fail {
                    return args.remove(0);
                }
                self.apply_function(func, args)
            }
        }
    }

    fn eval_expressions(&mut self, expressions: &[Expression]) -> Vec<Object> {
        let mut result = Vec::new();

        for expr in expressions {
            let evaluated = self.eval_expr(expr);
            if evaluated.is_error() {
                return vec![evaluated];
            } else {
                result.push(evaluated);
            }
        }

        result
    }

    fn eval_prefix_expr(&self, op: &str, right: Object) -> Object {
        match op {
            "!" => self.eval_bang_operator_expr(right),
            "-" => self.eval_minus_prefix_operator_expr(right),
            _ => Object::Error(format!("Unknown operator: {}{}", op, right)),
        }
    }

    fn eval_bang_operator_expr(&self, right: Object) -> Object {
        match right {
            Object::Boolean(true) => FALSE,
            Object::Boolean(false) => TRUE,
            Object::Null => TRUE,
            _ => FALSE,
        }
    }

    fn eval_minus_prefix_operator_expr(&self, right: Object) -> Object {
        match right {
            Object::Integer(val) => Object::Integer(-val),
            r @ _ => Object::Error(format!("Unknown operator: -{}", r)),
        }
    }

    fn eval_infix_expr(&self, op: &str, left: Object, right: Object) -> Object {
        match (op, left, right) {
            (_, Object::Integer(lval), Object::Integer(rval)) => {
                match op {
                    "+" => Object::Integer(lval + rval),
                    "-" => Object::Integer(lval - rval),
                    "*" => Object::Integer(lval * rval),
                    "/" => Object::Integer(lval / rval),
                    "<" => to_boolean_object(lval < rval),
                    ">" => to_boolean_object(lval > rval),
                    "==" => to_boolean_object(lval == rval),
                    "!=" => to_boolean_object(lval != rval),
                    _ => {
                        Object::Error(format!("Unknown operator: {} {} {}",
                                              Object::Integer(lval),
                                              op,
                                              Object::Integer(rval)))
                    }
                }
            }
            ("==", Object::Boolean(lval), Object::Boolean(rval)) => to_boolean_object(lval == rval),
            ("!=", Object::Boolean(lval), Object::Boolean(rval)) => to_boolean_object(lval != rval),
            (_, l @ Object::Integer(..), r @ Object::Boolean(..)) |
            (_, l @ Object::Boolean(..), r @ Object::Integer(..)) => {
                Object::Error(format!("Type mismatch: {} {} {}", l, op, r))
            }
            (_, l, r) => Object::Error(format!("Unknown operator: {} {} {}", l, op, r)),
        }
    }

    fn apply_function(&self, mut f: Object, args: Vec<Object>) -> Object {
        if let Object::Function { ref mut env, ref parameters, ref body, .. } = f {
            for (param, arg) in parameters.iter().zip(args) {
                if let Expression::Identifier { ref value, .. } = *param {
                    env.set(value, arg);
                } else {
                    return Object::Error("Function param is not Identifier.".to_string());
                }
            }

            let mut eval = Eval::new(env.clone());
            let evaluated = eval.eval_statement(body);

            if let Object::ReturnValue(obj) = evaluated {
                return *obj;
            } else {
                return evaluated;
            }

        } else {
            Object::Error(format!("Not a function: {}", f))
        }
    }
}

#[cfg(test)]
use super::lexer::Lexer;
#[cfg(test)]
use super::parser::Parser;
#[cfg(test)]
use super::ast::Node;

#[cfg(test)]
fn test_eval(input: &str) -> Object {
    let mut l = Lexer::new(input.to_string());
    let mut p = Parser::new(&mut l);
    let program = p.parse_program();
    let mut eval = Eval::new(Env::new());

    eval.eval(program)
}

#[cfg(test)]
fn check_integer_object(obj: Object, expected: i64) {
    match obj {
        Object::Integer(val) => {
            assert!(val == expected, "Object has wrong value. Got {}, want {}", val, expected);
        }
        _ => panic!("Object is not an Integer. Got {:?}", obj),
    }
}

#[cfg(test)]
fn check_boolean_object(obj: Object, expected: bool) {
    match obj {
        Object::Boolean(val) => {
            assert!(val == expected, "Object has wrong value. Got {}, want {}", val, expected);
        }
        _ => panic!("Object is not an Boolean. Got {:?}", obj),
    }
}

#[cfg(test)]
fn check_null_object(obj: Object) {
    assert!(obj.is_null(), "obj is not NULL. Got {:?}", obj);
}

#[test]
fn test_eval_integer_expression() {
    let tests = vec![
        ("5", 5),
        ("10", 10),
        ("-5", -5),
        ("-10", -10),
        ("5 + 5 + 5 + 5 - 10", 10),
        ("-50 + 100 + -50", 0),
        ("5 * 2 + 10", 20),
        ("5 + 2 * 10", 25),
        ("2 * 2 * 2 * 2 * 2", 32),
        ("20 + 2 * -10", 0),
        ("50 / 2 * 2 + 10", 60),
        ("2 * (5 + 10)", 30),
        ("3 * 3 * 3 + 10", 37),
        ("3 * (3 * 3) + 10", 37),
        ("(5 + 10 * 2 + 15 / 3) * 2 + -10", 50),
    ];

    for (input, expected) in tests {
        let evaluated = test_eval(input);
        check_integer_object(evaluated, expected);
    }
}

#[test]
fn test_eval_boolean_expression() {
    let tests = vec![
        ("true", true),
        ("false", false),
        ("1 < 2", true),
        ("1 > 2", false),
        ("1 < 1", false),
        ("1 > 1", false),
        ("1 == 1", true),
        ("1 != 1", false),
        ("1 == 2", false),
        ("1 != 2", true),
        ("true == true", true),
        ("false == false", true),
        ("true == false", false),
        ("true != false", true),
        ("false!= true", true),
        ("(1 < 2) == true", true),
        ("(1 < 2) == false", false),
        ("(1 > 2) == true", false),
        ("(1 > 2) == false", true),
    ];

    for (input, expected) in tests {
        let evaluated = test_eval(input);
        check_boolean_object(evaluated, expected);
    }
}

#[test]
fn test_bang_operator() {
    let tests = vec![
        ("!true", false),
        ("!false", true),
        ("!5", false),
        ("!!true", true),
        ("!!false", false),
        ("!!5", true),
    ];

    for (input, expected) in tests {
        let evaluated = test_eval(input);
        check_boolean_object(evaluated, expected);
    }
}

#[test]
fn test_if_else_expressions() {
    let tests = vec![
        ("if (true) { 10 }", Object::Integer(10)),
        ("if (false) { 10 }", NULL),
        ("if (1) { 10 }", Object::Integer(10)),
        ("if (1 < 2) { 10 }", Object::Integer(10)),
        ("if (1 > 2) { 10 }", NULL),
        ("if (1 > 2) { 10 } else { 20 }", Object::Integer(20)),
        ("if (1 < 2) { 10 } else { 20 }", Object::Integer(10)),
    ];

    for (input, expected) in tests {
        let evaluated = test_eval(input);
        match expected {
            Object::Integer(val) => check_integer_object(evaluated, val),
            Object::Null => check_null_object(evaluated),
            _ => panic!("Fix the test."),
        }
    }
}

#[test]
fn test_return_statements() {
    let tests = vec![
        ("return 10;", 10),
        ("return 10; 9;", 10),
        ("return 2 * 5; 9;", 10),
        ("9; return 2 * 5; 9;", 10),
        ("if (10 > 1) {
            if (10 > 1) {
              return 10;
            }

            return 1;
         }", 10),
    ];

    for (input, expected) in tests {
        let evaluated = test_eval(input);
        check_integer_object(evaluated, expected);
    }
}

#[test]
fn test_error_handling() {
    let tests = vec![("5 + true;", "Type mismatch: Integer + Boolean"),
                     ("5 + true; 5;", "Type mismatch: Integer + Boolean"),
                     ("-true", "Unknown operator: -Boolean"),
                     ("true + false;", "Unknown operator: Boolean + Boolean"),
                     ("5; true + false; 5", "Unknown operator: Boolean + Boolean"),
                     ("if (10 > 1) { true + false; }", "Unknown operator: Boolean + Boolean"),
                     ("if (10 > 1) {
                if (10 > 1) {
                    return \
                       true + false;
                }
                return 1;
            }",
                      "Unknown operator: Boolean + Boolean"),
                     ("foobar", "Identifier not found: foobar")];

    for (input, expected) in tests {
        let evaluated = test_eval(input);

        if let Object::Error(ref msg) = evaluated {
            assert!(msg == expected, "Wrong error message. Expected\n{}\nGot\n{}", expected, msg);
        } else {
            panic!("Evaluated object is not Object::Error. Got {:?}", evaluated);
        }
    }
}

#[test]
fn test_let_statements() {
    let tests = vec![
        ("let a = 5; a;", 5),
        ("let a = 5 * 5; a;", 25),
        ("let a = 5; let b = a; b;", 5),
        ("let a = 5; let b = a; let c = a + b + 5; c;", 15),
    ];

    for (input, expected) in tests {
        check_integer_object(test_eval(input), expected);
    }
}

#[test]
fn test_function_object() {
    let input = "fn(x) { x + 2; };";

    let evaluated = test_eval(input);

    if let Object::Function { parameters, body, .. } = evaluated {
        assert!(parameters.len() == 1, "Function has wrong parameters - {:?}", parameters);
        assert!(parameters[0].string() == "x", "parameter is not 'x'. Got {:?}", parameters[0]);

        let expected_body = "(x + 2)";
        assert!(body.string() == expected_body,
                "Body is not {}. Got {}",
                expected_body,
                body.string());
    } else {
        panic!("Object is not a Function. Got {:?}", evaluated);
    }
}

#[test]
fn test_function_application() {
    let tests = vec![("let identity = fn(x) { x; }; identity(5);", 5),
                     ("let identity = fn(x) { return x; }; identity(5);", 5),
                     ("let double = fn(x) { x * 2; }; double(5);", 10),
                     ("let add = fn(x, y) { x + y; }; add(5, 5);", 10),
                     ("let add = fn(x, y) { x + y; }; add(5 + 5, add(5, 5));", 20),
                     ("fn (x) { x; }(5)", 5)];

    for (input, expected) in tests {
        check_integer_object(test_eval(input), expected);
    }
}
<commit_msg>Test for closures too.<commit_after>use super::object::Object;
use super::object::Env;
use super::object::NULL;
use super::object::TRUE;
use super::object::FALSE;
use super::ast::Program;
use super::ast::Statement;
use super::ast::Expression;

fn to_boolean_object(val: bool) -> Object {
    if val {
        TRUE
    } else {
        FALSE
    }
}

pub struct Eval {
    env: Env,
}

impl Eval {
    pub fn new(env: Env) -> Self {
        Eval { env: env }
    }

    pub fn env(self) -> Env {
        self.env
    }

    pub fn eval(&mut self, program: Program) -> Object {
        self.eval_statements(&program.statements, false)
    }

    fn eval_statements(&mut self, stmts: &Vec<Statement>, nested: bool) -> Object {
        let mut result = NULL;

        for stmt in stmts {
            result = self.eval_statement(&stmt);

            match result {
                Object::ReturnValue(val) => {
                    if nested {
                        return Object::ReturnValue(val);
                    } else {
                        return *val;
                    }
                }
                r @ Object::Error(..) => {
                    return r;
                }
                _ => {
                    continue;
                }
            }
        }

        result
    }

    fn eval_statement(&mut self, stmt: &Statement) -> Object {
        match *stmt {
            Statement::Expression { ref expression, .. } => self.eval_expr(expression),
            Statement::Block { ref statements, .. } => self.eval_statements(statements, true),
            Statement::Return { ref value, .. } => {
                let mut return_val = NULL;
                if let Some(ref v) = *value {
                    return_val = self.eval_expr(v);
                }

                if return_val.is_error() {
                    return return_val;
                } else {
                    return Object::ReturnValue(Box::new(return_val));
                }
            }
            Statement::Let { ref value, ref name, .. } => {
                let val = self.eval_expr(value);
                if val.is_error() {
                    return val;
                }
                if let Expression::Identifier { ref value, .. } = *name {
                    self.env.set(value, val);
                    return NULL;
                } else {
                    return Object::Error("Internal interpreter error. Not an \
                                          Expression::Identifier in Statement::Let::expression."
                        .to_string());
                }
            }
        }
    }

    fn eval_expr(&mut self, expr: &Expression) -> Object {
        match *expr {
            Expression::IntegerLiteral { value, .. } => Object::Integer(value),
            Expression::Boolean { value, .. } => to_boolean_object(value),
            Expression::Prefix { ref operator, ref right, .. } => {
                let right = self.eval_expr(right);

                if right.is_error() {
                    return right;
                }

                self.eval_prefix_expr(operator, right)
            }
            Expression::Infix { ref left, ref right, ref operator, .. } => {
                let left = self.eval_expr(left);

                if left.is_error() {
                    return left;
                }

                let right = self.eval_expr(right);

                if right.is_error() {
                    return right;
                }

                self.eval_infix_expr(operator, left, right)
            }
            Expression::If { ref condition, ref consequence, ref alternative, .. } => {
                let cond = self.eval_expr(condition);

                if cond.is_error() {
                    return cond;
                }

                if cond.is_truthy() {
                    self.eval_statement(consequence)
                } else if let Some(ref alt) = *alternative {
                    self.eval_statement(alt)
                } else {
                    NULL
                }
            }
            Expression::Identifier { ref value, .. } => self.env.get(value),
            Expression::FunctionLiteral { ref parameters, ref body, .. } => {
                Object::Function {
                    parameters: parameters.clone(),
                    body: *body.clone(),
                    env: self.env.clone(),
                }
            }
            Expression::Call { ref function, ref arguments, .. } => {
                let func = self.eval_expr(function);
                if func.is_error() {
                    return func;
                }

                let mut args = self.eval_expressions(arguments);
                let fail = args.len() == 1 && args[0].is_error();
                if fail {
                    return args.remove(0);
                }
                self.apply_function(func, args)
            }
        }
    }

    fn eval_expressions(&mut self, expressions: &[Expression]) -> Vec<Object> {
        let mut result = Vec::new();

        for expr in expressions {
            let evaluated = self.eval_expr(expr);
            if evaluated.is_error() {
                return vec![evaluated];
            } else {
                result.push(evaluated);
            }
        }

        result
    }

    fn eval_prefix_expr(&self, op: &str, right: Object) -> Object {
        match op {
            "!" => self.eval_bang_operator_expr(right),
            "-" => self.eval_minus_prefix_operator_expr(right),
            _ => Object::Error(format!("Unknown operator: {}{}", op, right)),
        }
    }

    fn eval_bang_operator_expr(&self, right: Object) -> Object {
        match right {
            Object::Boolean(true) => FALSE,
            Object::Boolean(false) => TRUE,
            Object::Null => TRUE,
            _ => FALSE,
        }
    }

    fn eval_minus_prefix_operator_expr(&self, right: Object) -> Object {
        match right {
            Object::Integer(val) => Object::Integer(-val),
            r @ _ => Object::Error(format!("Unknown operator: -{}", r)),
        }
    }

    fn eval_infix_expr(&self, op: &str, left: Object, right: Object) -> Object {
        match (op, left, right) {
            (_, Object::Integer(lval), Object::Integer(rval)) => {
                match op {
                    "+" => Object::Integer(lval + rval),
                    "-" => Object::Integer(lval - rval),
                    "*" => Object::Integer(lval * rval),
                    "/" => Object::Integer(lval / rval),
                    "<" => to_boolean_object(lval < rval),
                    ">" => to_boolean_object(lval > rval),
                    "==" => to_boolean_object(lval == rval),
                    "!=" => to_boolean_object(lval != rval),
                    _ => {
                        Object::Error(format!("Unknown operator: {} {} {}",
                                              Object::Integer(lval),
                                              op,
                                              Object::Integer(rval)))
                    }
                }
            }
            ("==", Object::Boolean(lval), Object::Boolean(rval)) => to_boolean_object(lval == rval),
            ("!=", Object::Boolean(lval), Object::Boolean(rval)) => to_boolean_object(lval != rval),
            (_, l @ Object::Integer(..), r @ Object::Boolean(..)) |
            (_, l @ Object::Boolean(..), r @ Object::Integer(..)) => {
                Object::Error(format!("Type mismatch: {} {} {}", l, op, r))
            }
            (_, l, r) => Object::Error(format!("Unknown operator: {} {} {}", l, op, r)),
        }
    }

    fn apply_function(&self, mut f: Object, args: Vec<Object>) -> Object {
        if let Object::Function { ref mut env, ref parameters, ref body, .. } = f {
            for (param, arg) in parameters.iter().zip(args) {
                if let Expression::Identifier { ref value, .. } = *param {
                    env.set(value, arg);
                } else {
                    return Object::Error("Function param is not Identifier.".to_string());
                }
            }

            let mut eval = Eval::new(env.clone());
            let evaluated = eval.eval_statement(body);

            if let Object::ReturnValue(obj) = evaluated {
                return *obj;
            } else {
                return evaluated;
            }

        } else {
            Object::Error(format!("Not a function: {}", f))
        }
    }
}

#[cfg(test)]
use super::lexer::Lexer;
#[cfg(test)]
use super::parser::Parser;
#[cfg(test)]
use super::ast::Node;

#[cfg(test)]
fn test_eval(input: &str) -> Object {
    let mut l = Lexer::new(input.to_string());
    let mut p = Parser::new(&mut l);
    let program = p.parse_program();
    let mut eval = Eval::new(Env::new());

    eval.eval(program)
}

#[cfg(test)]
fn check_integer_object(obj: Object, expected: i64) {
    match obj {
        Object::Integer(val) => {
            assert!(val == expected, "Object has wrong value. Got {}, want {}", val, expected);
        }
        _ => panic!("Object is not an Integer. Got {:?}", obj),
    }
}

#[cfg(test)]
fn check_boolean_object(obj: Object, expected: bool) {
    match obj {
        Object::Boolean(val) => {
            assert!(val == expected, "Object has wrong value. Got {}, want {}", val, expected);
        }
        _ => panic!("Object is not an Boolean. Got {:?}", obj),
    }
}

#[cfg(test)]
fn check_null_object(obj: Object) {
    assert!(obj.is_null(), "obj is not NULL. Got {:?}", obj);
}

#[test]
fn test_eval_integer_expression() {
    let tests = vec![
        ("5", 5),
        ("10", 10),
        ("-5", -5),
        ("-10", -10),
        ("5 + 5 + 5 + 5 - 10", 10),
        ("-50 + 100 + -50", 0),
        ("5 * 2 + 10", 20),
        ("5 + 2 * 10", 25),
        ("2 * 2 * 2 * 2 * 2", 32),
        ("20 + 2 * -10", 0),
        ("50 / 2 * 2 + 10", 60),
        ("2 * (5 + 10)", 30),
        ("3 * 3 * 3 + 10", 37),
        ("3 * (3 * 3) + 10", 37),
        ("(5 + 10 * 2 + 15 / 3) * 2 + -10", 50),
    ];

    for (input, expected) in tests {
        let evaluated = test_eval(input);
        check_integer_object(evaluated, expected);
    }
}

#[test]
fn test_eval_boolean_expression() {
    let tests = vec![
        ("true", true),
        ("false", false),
        ("1 < 2", true),
        ("1 > 2", false),
        ("1 < 1", false),
        ("1 > 1", false),
        ("1 == 1", true),
        ("1 != 1", false),
        ("1 == 2", false),
        ("1 != 2", true),
        ("true == true", true),
        ("false == false", true),
        ("true == false", false),
        ("true != false", true),
        ("false!= true", true),
        ("(1 < 2) == true", true),
        ("(1 < 2) == false", false),
        ("(1 > 2) == true", false),
        ("(1 > 2) == false", true),
    ];

    for (input, expected) in tests {
        let evaluated = test_eval(input);
        check_boolean_object(evaluated, expected);
    }
}

#[test]
fn test_bang_operator() {
    let tests = vec![
        ("!true", false),
        ("!false", true),
        ("!5", false),
        ("!!true", true),
        ("!!false", false),
        ("!!5", true),
    ];

    for (input, expected) in tests {
        let evaluated = test_eval(input);
        check_boolean_object(evaluated, expected);
    }
}

#[test]
fn test_if_else_expressions() {
    let tests = vec![
        ("if (true) { 10 }", Object::Integer(10)),
        ("if (false) { 10 }", NULL),
        ("if (1) { 10 }", Object::Integer(10)),
        ("if (1 < 2) { 10 }", Object::Integer(10)),
        ("if (1 > 2) { 10 }", NULL),
        ("if (1 > 2) { 10 } else { 20 }", Object::Integer(20)),
        ("if (1 < 2) { 10 } else { 20 }", Object::Integer(10)),
    ];

    for (input, expected) in tests {
        let evaluated = test_eval(input);
        match expected {
            Object::Integer(val) => check_integer_object(evaluated, val),
            Object::Null => check_null_object(evaluated),
            _ => panic!("Fix the test."),
        }
    }
}

#[test]
fn test_return_statements() {
    let tests = vec![
        ("return 10;", 10),
        ("return 10; 9;", 10),
        ("return 2 * 5; 9;", 10),
        ("9; return 2 * 5; 9;", 10),
        ("if (10 > 1) {
            if (10 > 1) {
              return 10;
            }

            return 1;
         }", 10),
    ];

    for (input, expected) in tests {
        let evaluated = test_eval(input);
        check_integer_object(evaluated, expected);
    }
}

#[test]
fn test_error_handling() {
    let tests = vec![("5 + true;", "Type mismatch: Integer + Boolean"),
                     ("5 + true; 5;", "Type mismatch: Integer + Boolean"),
                     ("-true", "Unknown operator: -Boolean"),
                     ("true + false;", "Unknown operator: Boolean + Boolean"),
                     ("5; true + false; 5", "Unknown operator: Boolean + Boolean"),
                     ("if (10 > 1) { true + false; }", "Unknown operator: Boolean + Boolean"),
                     ("if (10 > 1) {
                if (10 > 1) {
                    return \
                       true + false;
                }
                return 1;
            }",
                      "Unknown operator: Boolean + Boolean"),
                     ("foobar", "Identifier not found: foobar")];

    for (input, expected) in tests {
        let evaluated = test_eval(input);

        if let Object::Error(ref msg) = evaluated {
            assert!(msg == expected, "Wrong error message. Expected\n{}\nGot\n{}", expected, msg);
        } else {
            panic!("Evaluated object is not Object::Error. Got {:?}", evaluated);
        }
    }
}

#[test]
fn test_let_statements() {
    let tests = vec![
        ("let a = 5; a;", 5),
        ("let a = 5 * 5; a;", 25),
        ("let a = 5; let b = a; b;", 5),
        ("let a = 5; let b = a; let c = a + b + 5; c;", 15),
    ];

    for (input, expected) in tests {
        check_integer_object(test_eval(input), expected);
    }
}

#[test]
fn test_function_object() {
    let input = "fn(x) { x + 2; };";

    let evaluated = test_eval(input);

    if let Object::Function { parameters, body, .. } = evaluated {
        assert!(parameters.len() == 1, "Function has wrong parameters - {:?}", parameters);
        assert!(parameters[0].string() == "x", "parameter is not 'x'. Got {:?}", parameters[0]);

        let expected_body = "(x + 2)";
        assert!(body.string() == expected_body,
                "Body is not {}. Got {}",
                expected_body,
                body.string());
    } else {
        panic!("Object is not a Function. Got {:?}", evaluated);
    }
}

#[test]
fn test_function_application() {
    let tests = vec![("let identity = fn(x) { x; }; identity(5);", 5),
                     ("let identity = fn(x) { return x; }; identity(5);", 5),
                     ("let double = fn(x) { x * 2; }; double(5);", 10),
                     ("let add = fn(x, y) { x + y; }; add(5, 5);", 10),
                     ("let add = fn(x, y) { x + y; }; add(5 + 5, add(5, 5));", 20),
                     ("fn (x) { x; }(5)", 5)];

    for (input, expected) in tests {
        check_integer_object(test_eval(input), expected);
    }
}

#[test]
fn test_closures() {
    let input = "let newAdder = fn(x) {
                   fn(y) { x + y };
                 };

                 let addTwo = newAdder(2);
                 addTwo(2);";

    check_integer_object(test_eval(input), 4);
}
<|endoftext|>