<commit_before>// Copyright (c) 2013-2015 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

use schema_capnp::{brand, node, type_};
use codegen;
use codegen::{GeneratorContext};
use std::collections::hash_map::HashMap;

#[derive(Copy,Clone,PartialEq)]
pub enum Leaf {
    Reader(&'static str),
    Builder(&'static str),
    Owned,
    Client,
    Pipeline
}

impl ::std::fmt::Display for Leaf {
    fn fmt(&self, fmt:&mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> {
        let display_string = match self {
            &Leaf::Reader(lt) => format!("Reader<{}>", lt),
            &Leaf::Builder(lt) => format!("Builder<{}>", lt),
            &Leaf::Owned => "Owned".to_string(),
            &Leaf::Client => "Client".to_string(),
            &Leaf::Pipeline => "Pipeline".to_string(),
        };
        ::std::fmt::Display::fmt(&display_string, fmt)
    }
}

impl Leaf {
    fn bare_name(&self) -> &'static str {
        match self {
            &Leaf::Reader(_) => "Reader",
            &Leaf::Builder(_) => "Builder",
            &Leaf::Owned => "Owned",
            &Leaf::Client => "Client",
            &Leaf::Pipeline => "Pipeline",
        }
    }

    fn _have_lifetime(&self) -> bool {
        match self {
            &Leaf::Reader(_) | &Leaf::Builder(_) => true,
            &Leaf::Owned | &Leaf::Client | &Leaf::Pipeline => false,
        }
    }
}

pub struct TypeParameterTexts {
    pub expanded_list: Vec<String>,
    pub params: String,
    pub where_clause: String,
    pub where_clause_with_send: String,
    pub phantom_data: String
}

// this is a collection of helpers acting on a "Node" (most of them are Type definitions)
pub trait RustNodeInfo {
    fn parameters_texts(&self, gen: &::codegen::GeneratorContext,
                        parent_node_id: Option<u64>) -> TypeParameterTexts;
}

// this is a collection of helpers acting on a "Type" (someplace where a Type is used, not defined)
pub trait RustTypeInfo {

    fn is_prim(&self) -> bool;
    fn is_parameter(&self) -> bool;
    fn is_branded(&self) -> bool;
    fn type_string(&self, gen:&codegen::GeneratorContext, module:Leaf) -> String;
}

impl <'a> RustNodeInfo for node::Reader<'a> {
    fn parameters_texts(&self, gen:&::codegen::GeneratorContext,
                        parent_node_id: Option<u64>) -> TypeParameterTexts {
        if self.get_is_generic() {
            let params = get_type_parameters(&gen, self.get_id(), parent_node_id);
            let type_parameters = params.iter().map(|param| {
                format!("{}",param)
            }).collect::<Vec<String>>().join(",");
            let where_clause = "where ".to_string() + &*(params.iter().map(|param| {
                format!("{}: for<'c> ::capnp::traits::Owned<'c>", param)
            }).collect::<Vec<String>>().join(", ") + " ");
            let where_clause_with_send = "where ".to_string() + &*(params.iter().map(|param| {
                //format!("{}Reader:Send+FromPointerReader<'a>", param)
                format!("{}:Send+'static", param)
            }).collect::<Vec<String>>().join(", ") + " ") + ", "
                + &*(params.iter().map(|param| {
                //format!("{}Builder:Send+FromPointerBuilder<'a>", param)
                format!("{}:Send+'static", param)
            }).collect::<Vec<String>>().join(", ") + " ");
            let phantom_data = "_phantom: PhantomData,".to_string();

            TypeParameterTexts {
                expanded_list: params,
                params: type_parameters,
                where_clause: where_clause,
                where_clause_with_send: where_clause_with_send,
                phantom_data: phantom_data
            }
        } else {
            TypeParameterTexts {
                expanded_list: vec!(),
                params: "".to_string(),
                where_clause: "".to_string(),
                where_clause_with_send: "".to_string(),
                phantom_data: "".to_string(),
            }
        }
    }
}

impl <'a> RustTypeInfo for type_::Reader<'a> {

    fn type_string(&self, gen:&codegen::GeneratorContext, module:Leaf) -> String {
        use codegen_types::RustTypeInfo;

        let local_lifetime = match module {
            Leaf::Reader(lt) => lt,
            Leaf::Builder(lt) => lt,
            _ => "",
        };

        let lifetime_coma = if local_lifetime == "" { "".to_string() } else {
            format!("{},", local_lifetime)
        };

        match self.which().unwrap() {
            type_::Void(()) => "()".to_string(),
            type_::Bool(()) => "bool".to_string(),
            type_::Int8(()) => "i8".to_string(),
            type_::Int16(()) => "i16".to_string(),
            type_::Int32(()) => "i32".to_string(),
            type_::Int64(()) => "i64".to_string(),
            type_::Uint8(()) => "u8".to_string(),
            type_::Uint16(()) => "u16".to_string(),
            type_::Uint32(()) => "u32".to_string(),
            type_::Uint64(()) => "u64".to_string(),
            type_::Float32(()) => "f32".to_string(),
            type_::Float64(()) => "f64".to_string(),
            type_::Text(()) => format!("text::{}", module),
            type_::Data(()) => format!("data::{}", module),
            type_::Struct(st) => {
                do_branding(gen, st.get_type_id(), st.get_brand().unwrap(), module,
                            gen.scope_map[&st.get_type_id()].join("::"), None)
            }
            type_::Interface(interface) => {
                do_branding(gen, interface.get_type_id(), interface.get_brand().unwrap(), module,
                            gen.scope_map[&interface.get_type_id()].join("::"), None)
            }
            type_::List(ot1) => {
                match ot1.get_element_type().unwrap().which() {
                    Err(_) => { panic!("unsupported type") },
                    Ok(type_::Struct(_)) => {
                        let inner = ot1.get_element_type().unwrap().type_string(gen, Leaf::Owned);
                        format!("struct_list::{}<{}{}>", module.bare_name(), lifetime_coma, inner)
                    },
                    Ok(type_::Enum(_)) => {
                        let inner = ot1.get_element_type().unwrap().type_string(gen, Leaf::Owned);
                        format!("enum_list::{}<{}{}>", module.bare_name(), lifetime_coma, inner)
                    },
                    Ok(type_::List(_)) => {
                        let inner = ot1.get_element_type().unwrap().type_string(gen, Leaf::Owned);
                        format!("list_list::{}<{}{}>", module.bare_name(), lifetime_coma, inner)
                    },
                    Ok(type_::Text(())) => {
                        format!("text_list::{}", module)
                    },
                    Ok(type_::Data(())) => {
                        format!("data_list::{}", module)
                    },
                    Ok(type_::Interface(_)) => {panic!("unimplemented") },
                    Ok(type_::AnyPointer(_)) => {panic!("List(AnyPointer) is unsupported")},
                    Ok(_) => {
                        let inner = ot1.get_element_type().unwrap().type_string(gen, Leaf::Owned);
                        format!("primitive_list::{}<{}{}>", module.bare_name(), lifetime_coma, inner)
                    },
                }
            },
            type_::Enum(en) => {
                let scope = &gen.scope_map[&en.get_type_id()];
                scope.join("::").to_string()
            },
            type_::AnyPointer(pointer) => {
                match pointer.which().unwrap() {
                    type_::any_pointer::Parameter(def) => {
                        let the_struct = &gen.node_map[&def.get_scope_id()];
                        let parameters = the_struct.get_parameters().unwrap();
                        let parameter = parameters.get(def.get_parameter_index() as u32);
                        let parameter_name = parameter.get_name().unwrap();
                        match module {
                            Leaf::Owned => parameter_name.to_string(),
                            Leaf::Reader(lifetime) => {
                                format!("<{} as ::capnp::traits::Owned<{}>>::Reader",
                                        parameter_name, lifetime)
                            }
                            Leaf::Builder(lifetime) => {
                                format!("<{} as ::capnp::traits::Owned<{}>>::Builder",
                                        parameter_name, lifetime)
                            }
                            Leaf::Pipeline => {
                                format!("<{} as ::capnp::traits::Owned<'static>>::Pipeline", parameter_name)
                            }
                            _ => { unimplemented!() }
                        }
                    },
                    _ => {
                        match module {
                            Leaf::Reader(lifetime) => {
                                format!("::capnp::any_pointer::Reader<{}>", lifetime)
                            }
                            Leaf::Builder(lifetime) => {
                                format!("::capnp::any_pointer::Builder<{}>", lifetime)
                            }
                            _ => {
                                format!("::capnp::any_pointer::{}", module)
                            }
                        }
                    }
                }
            }
        }
    }

    fn is_parameter(&self) -> bool {
        match self.which().unwrap() {
            type_::AnyPointer(pointer) => {
                match pointer.which().unwrap() {
                    type_::any_pointer::Parameter(_) => true,
                    _ => false
                }
            }
            _ => false
        }
    }

    fn is_branded(&self) -> bool {
        match self.which().unwrap() {
            type_::Struct(st) => {
                let brand = st.get_brand().unwrap();
                let scopes = brand.get_scopes().unwrap();
                scopes.len() > 0
            }
            _ => false
        }
    }

    #[inline(always)]
    fn is_prim(&self) -> bool {
        match self.which().unwrap() {
            type_::Int8(()) | type_::Int16(()) | type_::Int32(()) | type_::Int64(()) |
            type_::Uint8(()) | type_::Uint16(()) | type_::Uint32(()) | type_::Uint64(()) |
            type_::Float32(()) | type_::Float64(()) | type_::Void(()) | type_::Bool(()) => true,
            _ => false
        }
    }
}

///
///
pub fn do_branding(gen: &GeneratorContext,
                   node_id: u64,
                   brand: brand::Reader,
                   leaf: Leaf,
                   the_mod: String,
                   mut parent_scope_id: Option<u64>) -> String {
    let scopes = brand.get_scopes().unwrap();
    let mut brand_scopes = HashMap::new();
    for scope in scopes.iter() {
        brand_scopes.insert(scope.get_scope_id(), scope);
    }
    let brand_scopes = brand_scopes; // freeze
    let mut current_node_id = node_id;
    let mut accumulator: Vec<Vec<String>> = Vec::new();
    loop {
        let current_node = match gen.node_map.get(&current_node_id) {
            None => break,
            Some(node) => node,
        };
        let params = current_node.get_parameters().unwrap();
        let mut arguments: Vec<String> = Vec::new();
        match brand_scopes.get(&current_node_id) {
            None => {
                for _ in params.iter() {
                    arguments.push("::capnp::any_pointer::Owned".to_string());
                }
            },
            Some(scope) => {
                match scope.which().unwrap() {
                    brand::scope::Inherit(()) => {
                        for param in params.iter() {
                            arguments.push(param.get_name().unwrap().to_string());
                        }
                    }
                    brand::scope::Bind(bindings_list_opt) => {
                        let bindings_list = bindings_list_opt.unwrap();
                        assert_eq!(bindings_list.len(), params.len());
                        for binding in bindings_list.iter() {
                            match binding.which().unwrap() {
                                brand::binding::Unbound(()) => {
                                    arguments.push("::capnp::any_pointer::Owned".to_string());
                                }
                                brand::binding::Type(t) => {
                                    arguments.push(t.unwrap().type_string(gen, Leaf::Owned));
                                }
                            }
                        }
                    }
                }
            }
        }
        accumulator.push(arguments);
        current_node_id = current_node.get_scope_id();
        match (current_node_id, parent_scope_id) {
            (0, Some(id)) => current_node_id = id,
            _ => (),
        }
        parent_scope_id = None; // Only consider on the first time around.
    }

    // Now add a lifetime parameter if the leaf has one.
    match leaf {
        Leaf::Reader(lt) => accumulator.push(vec!(lt.to_string())),
        Leaf::Builder(lt) => accumulator.push(vec!(lt.to_string())),
        _ => (),
    }

    accumulator.reverse();

    let arguments = if accumulator.len() > 0 {
        format!("<{}>", accumulator.concat().join(","))
    } else {
        "".to_string()
    };

    return format!("{}::{}{}", the_mod,
                   leaf.bare_name().to_string(), arguments);
}

pub fn get_type_parameters(gen: &GeneratorContext,
                           node_id: u64,
                           mut parent_scope_id: Option<u64>) -> Vec<String> {
    let mut current_node_id = node_id;
    let mut accumulator: Vec<Vec<String>> = Vec::new();
    loop {
        let current_node = match gen.node_map.get(&current_node_id) {
            None => break,
            Some(node) => node,
        };
        let mut params = Vec::new();
        for param in current_node.get_parameters().unwrap().iter() {
            params.push(param.get_name().unwrap().to_string());
        }

        accumulator.push(params);
        current_node_id = current_node.get_scope_id();
        match (current_node_id, parent_scope_id) {
            (0, Some(id)) => current_node_id = id,
            _ => (),
        }
        parent_scope_id = None; // Only consider on the first time around.
    }

    accumulator.reverse();
    return accumulator.concat();
}
<commit_msg>remove unused import<commit_after>// Copyright (c) 2013-2015 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

use schema_capnp::{brand, node, type_};
use codegen;
use codegen::{GeneratorContext};
use std::collections::hash_map::HashMap;

#[derive(Copy,Clone,PartialEq)]
pub enum Leaf {
    Reader(&'static str),
    Builder(&'static str),
    Owned,
    Client,
    Pipeline
}

impl ::std::fmt::Display for Leaf {
    fn fmt(&self, fmt:&mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> {
        let display_string = match self {
            &Leaf::Reader(lt) => format!("Reader<{}>", lt),
            &Leaf::Builder(lt) => format!("Builder<{}>", lt),
            &Leaf::Owned => "Owned".to_string(),
            &Leaf::Client => "Client".to_string(),
            &Leaf::Pipeline => "Pipeline".to_string(),
        };
        ::std::fmt::Display::fmt(&display_string, fmt)
    }
}

impl Leaf {
    fn bare_name(&self) -> &'static str {
        match self {
            &Leaf::Reader(_) => "Reader",
            &Leaf::Builder(_) => "Builder",
            &Leaf::Owned => "Owned",
            &Leaf::Client => "Client",
            &Leaf::Pipeline => "Pipeline",
        }
    }

    fn _have_lifetime(&self) -> bool {
        match self {
            &Leaf::Reader(_) | &Leaf::Builder(_) => true,
            &Leaf::Owned | &Leaf::Client | &Leaf::Pipeline => false,
        }
    }
}

pub struct TypeParameterTexts {
    pub expanded_list: Vec<String>,
    pub params: String,
    pub where_clause: String,
    pub where_clause_with_send: String,
    pub phantom_data: String
}

// this is a collection of helpers acting on a "Node" (most of them are Type definitions)
pub trait RustNodeInfo {
    fn parameters_texts(&self, gen: &::codegen::GeneratorContext,
                        parent_node_id: Option<u64>) -> TypeParameterTexts;
}

// this is a collection of helpers acting on a "Type" (someplace where a Type is used, not defined)
pub trait RustTypeInfo {

    fn is_prim(&self) -> bool;
    fn is_parameter(&self) -> bool;
    fn is_branded(&self) -> bool;
    fn type_string(&self, gen:&codegen::GeneratorContext, module:Leaf) -> String;
}

impl <'a> RustNodeInfo for node::Reader<'a> {
    fn parameters_texts(&self, gen:&::codegen::GeneratorContext,
                        parent_node_id: Option<u64>) -> TypeParameterTexts {
        if self.get_is_generic() {
            let params = get_type_parameters(&gen, self.get_id(), parent_node_id);
            let type_parameters = params.iter().map(|param| {
                format!("{}",param)
            }).collect::<Vec<String>>().join(",");
            let where_clause = "where ".to_string() + &*(params.iter().map(|param| {
                format!("{}: for<'c> ::capnp::traits::Owned<'c>", param)
            }).collect::<Vec<String>>().join(", ") + " ");
            let where_clause_with_send = "where ".to_string() + &*(params.iter().map(|param| {
                //format!("{}Reader:Send+FromPointerReader<'a>", param)
                format!("{}:Send+'static", param)
            }).collect::<Vec<String>>().join(", ") + " ") + ", "
                + &*(params.iter().map(|param| {
                //format!("{}Builder:Send+FromPointerBuilder<'a>", param)
                format!("{}:Send+'static", param)
            }).collect::<Vec<String>>().join(", ") + " ");
            let phantom_data = "_phantom: PhantomData,".to_string();

            TypeParameterTexts {
                expanded_list: params,
                params: type_parameters,
                where_clause: where_clause,
                where_clause_with_send: where_clause_with_send,
                phantom_data: phantom_data
            }
        } else {
            TypeParameterTexts {
                expanded_list: vec!(),
                params: "".to_string(),
                where_clause: "".to_string(),
                where_clause_with_send: "".to_string(),
                phantom_data: "".to_string(),
            }
        }
    }
}

impl <'a> RustTypeInfo for type_::Reader<'a> {

    fn type_string(&self, gen:&codegen::GeneratorContext, module:Leaf) -> String {

        let local_lifetime = match module {
            Leaf::Reader(lt) => lt,
            Leaf::Builder(lt) => lt,
            _ => "",
        };

        let lifetime_coma = if local_lifetime == "" { "".to_string() } else {
            format!("{},", local_lifetime)
        };

        match self.which().unwrap() {
            type_::Void(()) => "()".to_string(),
            type_::Bool(()) => "bool".to_string(),
            type_::Int8(()) => "i8".to_string(),
            type_::Int16(()) => "i16".to_string(),
            type_::Int32(()) => "i32".to_string(),
            type_::Int64(()) => "i64".to_string(),
            type_::Uint8(()) => "u8".to_string(),
            type_::Uint16(()) => "u16".to_string(),
            type_::Uint32(()) => "u32".to_string(),
            type_::Uint64(()) => "u64".to_string(),
            type_::Float32(()) => "f32".to_string(),
            type_::Float64(()) => "f64".to_string(),
            type_::Text(()) => format!("text::{}", module),
            type_::Data(()) => format!("data::{}", module),
            type_::Struct(st) => {
                do_branding(gen, st.get_type_id(), st.get_brand().unwrap(), module,
                            gen.scope_map[&st.get_type_id()].join("::"), None)
            }
            type_::Interface(interface) => {
                do_branding(gen, interface.get_type_id(), interface.get_brand().unwrap(), module,
                            gen.scope_map[&interface.get_type_id()].join("::"), None)
            }
            type_::List(ot1) => {
                match ot1.get_element_type().unwrap().which() {
                    Err(_) => { panic!("unsupported type") },
                    Ok(type_::Struct(_)) => {
                        let inner = ot1.get_element_type().unwrap().type_string(gen, Leaf::Owned);
                        format!("struct_list::{}<{}{}>", module.bare_name(), lifetime_coma, inner)
                    },
                    Ok(type_::Enum(_)) => {
                        let inner = ot1.get_element_type().unwrap().type_string(gen, Leaf::Owned);
                        format!("enum_list::{}<{}{}>", module.bare_name(), lifetime_coma, inner)
                    },
                    Ok(type_::List(_)) => {
                        let inner = ot1.get_element_type().unwrap().type_string(gen, Leaf::Owned);
                        format!("list_list::{}<{}{}>", module.bare_name(), lifetime_coma, inner)
                    },
                    Ok(type_::Text(())) => {
                        format!("text_list::{}", module)
                    },
                    Ok(type_::Data(())) => {
                        format!("data_list::{}", module)
                    },
                    Ok(type_::Interface(_)) => {panic!("unimplemented") },
                    Ok(type_::AnyPointer(_)) => {panic!("List(AnyPointer) is unsupported")},
                    Ok(_) => {
                        let inner = ot1.get_element_type().unwrap().type_string(gen, Leaf::Owned);
                        format!("primitive_list::{}<{}{}>", module.bare_name(), lifetime_coma, inner)
                    },
                }
            },
            type_::Enum(en) => {
                let scope = &gen.scope_map[&en.get_type_id()];
                scope.join("::").to_string()
            },
            type_::AnyPointer(pointer) => {
                match pointer.which().unwrap() {
                    type_::any_pointer::Parameter(def) => {
                        let the_struct = &gen.node_map[&def.get_scope_id()];
                        let parameters = the_struct.get_parameters().unwrap();
                        let parameter = parameters.get(def.get_parameter_index() as u32);
                        let parameter_name = parameter.get_name().unwrap();
                        match module {
                            Leaf::Owned => parameter_name.to_string(),
                            Leaf::Reader(lifetime) => {
                                format!("<{} as ::capnp::traits::Owned<{}>>::Reader",
                                        parameter_name, lifetime)
                            }
                            Leaf::Builder(lifetime) => {
                                format!("<{} as ::capnp::traits::Owned<{}>>::Builder",
                                        parameter_name, lifetime)
                            }
                            Leaf::Pipeline => {
                                format!("<{} as ::capnp::traits::Owned<'static>>::Pipeline", parameter_name)
                            }
                            _ => { unimplemented!() }
                        }
                    },
                    _ => {
                        match module {
                            Leaf::Reader(lifetime) => {
                                format!("::capnp::any_pointer::Reader<{}>", lifetime)
                            }
                            Leaf::Builder(lifetime) => {
                                format!("::capnp::any_pointer::Builder<{}>", lifetime)
                            }
                            _ => {
                                format!("::capnp::any_pointer::{}", module)
                            }
                        }
                    }
                }
            }
        }
    }

    fn is_parameter(&self) -> bool {
        match self.which().unwrap() {
            type_::AnyPointer(pointer) => {
                match pointer.which().unwrap() {
                    type_::any_pointer::Parameter(_) => true,
                    _ => false
                }
            }
            _ => false
        }
    }

    fn is_branded(&self) -> bool {
        match self.which().unwrap() {
            type_::Struct(st) => {
                let brand = st.get_brand().unwrap();
                let scopes = brand.get_scopes().unwrap();
                scopes.len() > 0
            }
            _ => false
        }
    }

    #[inline(always)]
    fn is_prim(&self) -> bool {
        match self.which().unwrap() {
            type_::Int8(()) | type_::Int16(()) | type_::Int32(()) | type_::Int64(()) |
            type_::Uint8(()) | type_::Uint16(()) | type_::Uint32(()) | type_::Uint64(()) |
            type_::Float32(()) | type_::Float64(()) | type_::Void(()) | type_::Bool(()) => true,
            _ => false
        }
    }
}

///
///
pub fn do_branding(gen: &GeneratorContext,
                   node_id: u64,
                   brand: brand::Reader,
                   leaf: Leaf,
                   the_mod: String,
                   mut parent_scope_id: Option<u64>) -> String {
    let scopes = brand.get_scopes().unwrap();
    let mut brand_scopes = HashMap::new();
    for scope in scopes.iter() {
        brand_scopes.insert(scope.get_scope_id(), scope);
    }
    let brand_scopes = brand_scopes; // freeze
    let mut current_node_id = node_id;
    let mut accumulator: Vec<Vec<String>> = Vec::new();
    loop {
        let current_node = match gen.node_map.get(&current_node_id) {
            None => break,
            Some(node) => node,
        };
        let params = current_node.get_parameters().unwrap();
        let mut arguments: Vec<String> = Vec::new();
        match brand_scopes.get(&current_node_id) {
            None => {
                for _ in params.iter() {
                    arguments.push("::capnp::any_pointer::Owned".to_string());
                }
            },
            Some(scope) => {
                match scope.which().unwrap() {
                    brand::scope::Inherit(()) => {
                        for param in params.iter() {
                            arguments.push(param.get_name().unwrap().to_string());
                        }
                    }
                    brand::scope::Bind(bindings_list_opt) => {
                        let bindings_list = bindings_list_opt.unwrap();
                        assert_eq!(bindings_list.len(), params.len());
                        for binding in bindings_list.iter() {
                            match binding.which().unwrap() {
                                brand::binding::Unbound(()) => {
                                    arguments.push("::capnp::any_pointer::Owned".to_string());
                                }
                                brand::binding::Type(t) => {
                                    arguments.push(t.unwrap().type_string(gen, Leaf::Owned));
                                }
                            }
                        }
                    }
                }
            }
        }
        accumulator.push(arguments);
        current_node_id = current_node.get_scope_id();
        match (current_node_id, parent_scope_id) {
            (0, Some(id)) => current_node_id = id,
            _ => (),
        }
        parent_scope_id = None; // Only consider on the first time around.
    }

    // Now add a lifetime parameter if the leaf has one.
    match leaf {
        Leaf::Reader(lt) => accumulator.push(vec!(lt.to_string())),
        Leaf::Builder(lt) => accumulator.push(vec!(lt.to_string())),
        _ => (),
    }

    accumulator.reverse();

    let arguments = if accumulator.len() > 0 {
        format!("<{}>", accumulator.concat().join(","))
    } else {
        "".to_string()
    };

    return format!("{}::{}{}", the_mod,
                   leaf.bare_name().to_string(), arguments);
}

pub fn get_type_parameters(gen: &GeneratorContext,
                           node_id: u64,
                           mut parent_scope_id: Option<u64>) -> Vec<String> {
    let mut current_node_id = node_id;
    let mut accumulator: Vec<Vec<String>> = Vec::new();
    loop {
        let current_node = match gen.node_map.get(&current_node_id) {
            None => break,
            Some(node) => node,
        };
        let mut params = Vec::new();
        for param in current_node.get_parameters().unwrap().iter() {
            params.push(param.get_name().unwrap().to_string());
        }

        accumulator.push(params);
        current_node_id = current_node.get_scope_id();
        match (current_node_id, parent_scope_id) {
            (0, Some(id)) => current_node_id = id,
            _ => (),
        }
        parent_scope_id = None; // Only consider on the first time around.
    }

    accumulator.reverse();
    return accumulator.concat();
}
<|endoftext|>


<commit_before>use rustc::lint::*;
use rustc::middle::const_val::ConstVal;
use rustc::ty;
use rustc_const_eval::ConstContext;
use rustc_const_math::ConstInt;
use rustc::hir;
use syntax::ast::RangeLimits;
use utils::{self, higher};

/// **What it does:** Checks for out of bounds array indexing with a constant index.
///
/// **Why is this bad?** This will always panic at runtime.
///
/// **Known problems:** Hopefully none.
///
/// **Example:**
/// ```rust
/// let x = [1,2,3,4];
/// ...
/// x[9];
/// &x[2..9];
/// ```
declare_lint! {
    pub OUT_OF_BOUNDS_INDEXING,
    Deny,
    "out of bounds constant indexing"
}

/// **What it does:** Checks for usage of indexing or slicing.
///
/// **Why is this bad?** Usually, this can be safely allowed. However, in some
/// domains such as kernel development, a panic can cause the whole operating
/// system to crash.
///
/// **Known problems:** Hopefully none.
///
/// **Example:**
/// ```rust
/// ...
/// x[2];
/// &x[0..2];
/// ```
declare_restriction_lint! {
    pub INDEXING_SLICING,
    "indexing/slicing usage"
}

#[derive(Copy,Clone)]
pub struct ArrayIndexing;

impl LintPass for ArrayIndexing {
    fn get_lints(&self) -> LintArray {
        lint_array!(INDEXING_SLICING, OUT_OF_BOUNDS_INDEXING)
    }
}

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ArrayIndexing {
    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx hir::Expr) {
        if let hir::ExprIndex(ref array, ref index) = e.node {
            // Array with known size can be checked statically
            let ty = cx.tables.expr_ty(array);
            if let ty::TyArray(_, size) = ty.sty {
                let size = ConstInt::U128(size as u128);
                let constcx = ConstContext::with_tables(cx.tcx, cx.tables);

                // Index is a constant uint
                let const_index = constcx.eval(index);
                if let Ok(ConstVal::Integral(const_index)) = const_index {
                    if size <= const_index {
                        utils::span_lint(cx, OUT_OF_BOUNDS_INDEXING, e.span, "const index is out of bounds");
                    }

                    return;
                }

                // Index is a constant range
                if let Some(range) = higher::range(index) {
                    let start = range.start
                        .map(|start| constcx.eval(start))
                        .map(|v| v.ok());
                    let end = range.end
                        .map(|end| constcx.eval(end))
                        .map(|v| v.ok());

                    if let Some((start, end)) = to_const_range(&start, &end, range.limits, size) {
                        if start > size || end > size {
                            utils::span_lint(cx, OUT_OF_BOUNDS_INDEXING, e.span, "range is out of bounds");
                        }
                        return;
                    }
                }
            }

            if let Some(range) = higher::range(index) {
                // Full ranges are always valid
                if range.start.is_none() && range.end.is_none() {
                    return;
                }

                // Impossible to know if indexing or slicing is correct
                utils::span_lint(cx, INDEXING_SLICING, e.span, "slicing may panic");
            } else {
                utils::span_lint(cx, INDEXING_SLICING, e.span, "indexing may panic");
            }
        }
    }
}

/// Returns an option containing a tuple with the start and end (exclusive) of the range.
fn to_const_range(
    start: &Option<Option<ConstVal>>,
    end: &Option<Option<ConstVal>>,
    limits: RangeLimits,
    array_size: ConstInt
) -> Option<(ConstInt, ConstInt)> {
    let start = match *start {
        Some(Some(ConstVal::Integral(x))) => x,
        Some(_) => return None,
        None => ConstInt::U8(0),
    };

    let end = match *end {
        Some(Some(ConstVal::Integral(x))) => {
            if limits == RangeLimits::Closed {
                (x + ConstInt::U8(1)).expect("such a big array is not realistic")
            } else {
                x
            }
        },
        Some(_) => return None,
        None => array_size,
    };

    Some((start, end))
}
<commit_msg>Array lengths are usize, so you need ConstInt::Usize(...)<commit_after>use rustc::lint::*;
use rustc::middle::const_val::ConstVal;
use rustc::ty;
use rustc_const_eval::ConstContext;
use rustc_const_math::{ConstUsize,ConstInt};
use rustc::hir;
use syntax::ast::RangeLimits;
use utils::{self, higher};

/// **What it does:** Checks for out of bounds array indexing with a constant index.
///
/// **Why is this bad?** This will always panic at runtime.
///
/// **Known problems:** Hopefully none.
///
/// **Example:**
/// ```rust
/// let x = [1,2,3,4];
/// ...
/// x[9];
/// &x[2..9];
/// ```
declare_lint! {
    pub OUT_OF_BOUNDS_INDEXING,
    Deny,
    "out of bounds constant indexing"
}

/// **What it does:** Checks for usage of indexing or slicing.
///
/// **Why is this bad?** Usually, this can be safely allowed. However, in some
/// domains such as kernel development, a panic can cause the whole operating
/// system to crash.
///
/// **Known problems:** Hopefully none.
///
/// **Example:**
/// ```rust
/// ...
/// x[2];
/// &x[0..2];
/// ```
declare_restriction_lint! {
    pub INDEXING_SLICING,
    "indexing/slicing usage"
}

#[derive(Copy,Clone)]
pub struct ArrayIndexing;

impl LintPass for ArrayIndexing {
    fn get_lints(&self) -> LintArray {
        lint_array!(INDEXING_SLICING, OUT_OF_BOUNDS_INDEXING)
    }
}

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ArrayIndexing {
    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx hir::Expr) {
        if let hir::ExprIndex(ref array, ref index) = e.node {
            // Array with known size can be checked statically
            let ty = cx.tables.expr_ty(array);
            if let ty::TyArray(_, size) = ty.sty {
                let size = ConstInt::Usize(ConstUsize::new(size as u64, cx.sess().target.uint_type).unwrap());
                let constcx = ConstContext::with_tables(cx.tcx, cx.tables);

                // Index is a constant uint
                let const_index = constcx.eval(index);
                if let Ok(ConstVal::Integral(const_index)) = const_index {
                    if size <= const_index {
                        utils::span_lint(cx, OUT_OF_BOUNDS_INDEXING, e.span, "const index is out of bounds");
                    }

                    return;
                }

                // Index is a constant range
                if let Some(range) = higher::range(index) {
                    let start = range.start
                        .map(|start| constcx.eval(start))
                        .map(|v| v.ok());
                    let end = range.end
                        .map(|end| constcx.eval(end))
                        .map(|v| v.ok());

                    if let Some((start, end)) = to_const_range(&start, &end, range.limits, size) {
                        if start > size || end > size {
                            utils::span_lint(cx, OUT_OF_BOUNDS_INDEXING, e.span, "range is out of bounds");
                        }
                        return;
                    }
                }
            }

            if let Some(range) = higher::range(index) {
                // Full ranges are always valid
                if range.start.is_none() && range.end.is_none() {
                    return;
                }

                // Impossible to know if indexing or slicing is correct
                utils::span_lint(cx, INDEXING_SLICING, e.span, "slicing may panic");
            } else {
                utils::span_lint(cx, INDEXING_SLICING, e.span, "indexing may panic");
            }
        }
    }
}

/// Returns an option containing a tuple with the start and end (exclusive) of the range.
fn to_const_range(
    start: &Option<Option<ConstVal>>,
    end: &Option<Option<ConstVal>>,
    limits: RangeLimits,
    array_size: ConstInt
) -> Option<(ConstInt, ConstInt)> {
    let start = match *start {
        Some(Some(ConstVal::Integral(x))) => x,
        Some(_) => return None,
        None => ConstInt::U8(0),
    };

    let end = match *end {
        Some(Some(ConstVal::Integral(x))) => {
            if limits == RangeLimits::Closed {
                (x + ConstInt::U8(1)).expect("such a big array is not realistic")
            } else {
                x
            }
        },
        Some(_) => return None,
        None => array_size,
    };

    Some((start, end))
}
<|endoftext|>


<commit_before>//! The component module provides all the different components available. A
//! Component allows querying different kinds of information from a Timer. This
//! information is provided as state objects in a way that can easily be
//! visualized by any kind of User Interface.

pub mod blank_space;
pub mod current_comparison;
pub mod current_pace;
pub mod delta;
pub mod detailed_timer;
pub mod graph;
pub mod possible_time_save;
pub mod previous_segment;
pub mod separator;
pub mod splits;
pub mod sum_of_best;
pub mod text;
pub mod timer;
pub mod title;
pub mod total_playtime;

use crate::settings::{Color, Gradient};
use palette::rgb::Rgb;
use palette::Alpha;
use std::marker::PhantomData;

const DEFAULT_INFO_TEXT_GRADIENT: Gradient = Gradient::Vertical(
    Color {
        rgba: Alpha {
            alpha: 0.06,
            color: Rgb {
                red: 1.0,
                green: 1.0,
                blue: 1.0,
                standard: PhantomData,
            },
        },
    },
    Color {
        rgba: Alpha {
            alpha: 0.005,
            color: Rgb {
                red: 1.0,
                green: 1.0,
                blue: 1.0,
                standard: PhantomData,
            },
        },
    },
);
<commit_msg>Reexport components in component module<commit_after>//! The component module provides all the different components available. A
//! Component allows querying different kinds of information from a Timer. This
//! information is provided as state objects in a way that can easily be
//! visualized by any kind of User Interface.

pub mod blank_space;
pub mod current_comparison;
pub mod current_pace;
pub mod delta;
pub mod detailed_timer;
pub mod graph;
pub mod possible_time_save;
pub mod previous_segment;
pub mod separator;
pub mod splits;
pub mod sum_of_best;
pub mod text;
pub mod timer;
pub mod title;
pub mod total_playtime;

pub use blank_space::Component as BlankSpace;
pub use current_comparison::Component as CurrentComparison;
pub use current_pace::Component as CurrentPace;
pub use delta::Component as Delta;
pub use detailed_timer::Component as DetailedTimer;
pub use graph::Component as Graph;
pub use possible_time_save::Component as PossibleTimeSave;
pub use previous_segment::Component as PreviousSegment;
pub use separator::Component as Separator;
pub use splits::Component as Splits;
pub use sum_of_best::Component as SumOfBest;
pub use text::Component as Text;
pub use timer::Component as Timer;
pub use title::Component as Title;
pub use total_playtime::Component as TotalPlaytime;

use crate::settings::{Color, Gradient};
use palette::rgb::Rgb;
use palette::Alpha;
use std::marker::PhantomData;

const DEFAULT_INFO_TEXT_GRADIENT: Gradient = Gradient::Vertical(
    Color {
        rgba: Alpha {
            alpha: 0.06,
            color: Rgb {
                red: 1.0,
                green: 1.0,
                blue: 1.0,
                standard: PhantomData,
            },
        },
    },
    Color {
        rgba: Alpha {
            alpha: 0.005,
            color: Rgb {
                red: 1.0,
                green: 1.0,
                blue: 1.0,
                standard: PhantomData,
            },
        },
    },
);
<|endoftext|>


<commit_before>use serialize::json;

use channel::{self, Channel};
use user::{self, User};
use message::{self, Message};

pub struct CurrentState {
    me: User,
    channels: Vec<Channel>,
    users: Vec<User>
    // current_channel: Channel
}

pub fn new_from_str(json: &str) -> CurrentState {
    CurrentState {
        me: extract_me(&json),
        channels: extract_channels(&json),
        users: extract_users(&json)
    }
}

fn extract_me(json: &str) -> User {
    // Hideous, maybe better to just manually implement decodable?
    // also use try! instead.
    let json = json::from_str(json).unwrap();
    let val = json.find("self").unwrap();
    user::new_from_str(json::encode(&val.as_object()).unwrap().as_ref()).unwrap()
}

fn extract_users(json: &str) -> Vec<User> {
    // Hideous, maybe better to just manually implement decodable?
    // also use try! instead.
    let json = json::from_str(json).unwrap();
    json.find("users").unwrap().as_array().unwrap().iter().map(|user| {
        user::new_from_str(json::encode(user.as_object().unwrap()).unwrap().as_ref()).unwrap()
    }).collect()
}

fn extract_channels(json: &str) -> Vec<Channel> {
    // Hideous, maybe better to just manually implement decodable?
    // also use try! instead.
    let json = json::from_str(json).unwrap();
    json.find("channels").unwrap().as_array().unwrap().iter().map(|channel| {
        channel::new_from_str(json::encode(channel.as_object().unwrap()).unwrap().as_ref()).unwrap()
    }).collect()
}

impl CurrentState {
    pub fn id_to_user(&self, id: &str) -> Option<&User> {
        self.users.iter().find(|user| user.id == id)
    }

    pub fn id_to_channel(&self, id: &str) -> Option<&Channel> {
        self.channels.iter().find(|channel| channel.id == id)
    }

    pub fn parse_incoming_message(&self, raw: &str) -> json::DecodeResult<Message> {
        let mut message = try!(message::new_from_str(&raw));

        match message.channel_id {
            // I'm not sure I fully understand why the ref is needed.
            // The value is borrowed, but isn't it returned after going out of scope?
            Some(ref id) => message.channel = self.id_to_channel(&id).map(|chan| chan.clone()),
            None => ()
        }

        match message.user_id {
            Some(ref id) => message.user = self.id_to_user(&id).map(|user| user.clone()),
            None => ()
        }

        Ok(message)
    }
}

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

    #[test]
    fn test_new_current_state_from_str() {
        let state = new_from_str(&generate_json());
        assert_eq!(state.me.name, "bobby");
        assert_eq!(state.users[0].name, "Matijs");
        assert_eq!(state.channels[0].name, "General");
    }

    #[test]
    fn test_id_to_user() {
        let state = new_from_str(&generate_json());
        let user = state.id_to_user("xyz");
        assert_eq!(user.unwrap().name, "Matijs");
    }

    #[test]
    fn test_id_to_channel() {
        let state = new_from_str(&generate_json());
        let channel = state.id_to_channel("zyx");
        assert_eq!(channel.unwrap().name, "General");
    }

    #[test]
    fn test_parse_incoming_message() {
        let state = new_from_str(&generate_json());
        let json = "{
            \"type\": \"message\",
            \"user\": \"xyz\",
            \"text\": \"Bananas!\",
            \"ts\": \"today\",
            \"channel\": \"zyx\"
        }";

        let message = state.parse_incoming_message(&json).unwrap();
        assert_eq!(message.channel.unwrap().name, "General");
        assert_eq!(message.user.unwrap().name, "Matijs");
    }

    fn generate_json() -> String {
        "{
            \"ok\": true,
            \"url\": \"wss://ms9.slack-msgs.com/websocket/7I5yBpcvk\",
            \"self\": {
                \"id\": \"U023BECGF\",
                \"name\": \"bobby\",
                \"prefs\": {
                },
                \"created\": 1402463766,
                \"manual_presence\": \"active\"
            },
            \"team\": {
                \"id\": \"T024BE7LD\",
                \"name\": \"Example Team\",
                \"email_domain\": \"\",
                \"domain\": \"example\",
                \"msg_edit_window_mins\": -1,
                \"over_storage_limit\": false,
                \"prefs\": {
                },
                \"plan\": \"std\"
            },
            \"users\": [
                {
                    \"id\": \"xyz\",
                    \"name\": \"Matijs\"
                }
            ],
            \"channels\": [
                {
                    \"id\": \"zyx\",
                    \"name\": \"General\",
                    \"members\": [],
                    \"is_member\": false
                }
            ],
            \"groups\": [ ],
            \"ims\": [ ],
            \"bots\": [ ]
        }".to_string()
    }
}
<commit_msg>Remove superfluous &<commit_after>use serialize::json;

use channel::{self, Channel};
use user::{self, User};
use message::{self, Message};

pub struct CurrentState {
    me: User,
    channels: Vec<Channel>,
    users: Vec<User>
    // current_channel: Channel
}

pub fn new_from_str(json: &str) -> CurrentState {
    CurrentState {
        me: extract_me(&json),
        channels: extract_channels(&json),
        users: extract_users(&json)
    }
}

fn extract_me(json: &str) -> User {
    // Hideous, maybe better to just manually implement decodable?
    // also use try! instead.
    let json = json::from_str(json).unwrap();
    let val = json.find("self").unwrap();
    user::new_from_str(json::encode(&val.as_object()).unwrap().as_ref()).unwrap()
}

fn extract_users(json: &str) -> Vec<User> {
    // Hideous, maybe better to just manually implement decodable?
    // also use try! instead.
    let json = json::from_str(json).unwrap();
    json.find("users").unwrap().as_array().unwrap().iter().map(|user| {
        user::new_from_str(json::encode(user.as_object().unwrap()).unwrap().as_ref()).unwrap()
    }).collect()
}

fn extract_channels(json: &str) -> Vec<Channel> {
    // Hideous, maybe better to just manually implement decodable?
    // also use try! instead.
    let json = json::from_str(json).unwrap();
    json.find("channels").unwrap().as_array().unwrap().iter().map(|channel| {
        channel::new_from_str(json::encode(channel.as_object().unwrap()).unwrap().as_ref()).unwrap()
    }).collect()
}

impl CurrentState {
    pub fn id_to_user(&self, id: &str) -> Option<&User> {
        self.users.iter().find(|user| user.id == id)
    }

    pub fn id_to_channel(&self, id: &str) -> Option<&Channel> {
        self.channels.iter().find(|channel| channel.id == id)
    }

    pub fn parse_incoming_message(&self, raw: &str) -> json::DecodeResult<Message> {
        let mut message = try!(message::new_from_str(&raw));

        match message.channel_id {
            // I'm not sure I fully understand why the ref is needed.
            // The value is borrowed, but isn't it returned after going out of scope?
            Some(ref id) => message.channel = self.id_to_channel(id).map(|chan| chan.clone()),
            None => ()
        }

        match message.user_id {
            Some(ref id) => message.user = self.id_to_user(id).map(|user| user.clone()),
            None => ()
        }

        Ok(message)
    }
}

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

    #[test]
    fn test_new_current_state_from_str() {
        let state = new_from_str(&generate_json());
        assert_eq!(state.me.name, "bobby");
        assert_eq!(state.users[0].name, "Matijs");
        assert_eq!(state.channels[0].name, "General");
    }

    #[test]
    fn test_id_to_user() {
        let state = new_from_str(&generate_json());
        let user = state.id_to_user("xyz");
        assert_eq!(user.unwrap().name, "Matijs");
    }

    #[test]
    fn test_id_to_channel() {
        let state = new_from_str(&generate_json());
        let channel = state.id_to_channel("zyx");
        assert_eq!(channel.unwrap().name, "General");
    }

    #[test]
    fn test_parse_incoming_message() {
        let state = new_from_str(&generate_json());
        let json = "{
            \"type\": \"message\",
            \"user\": \"xyz\",
            \"text\": \"Bananas!\",
            \"ts\": \"today\",
            \"channel\": \"zyx\"
        }";

        let message = state.parse_incoming_message(&json).unwrap();
        assert_eq!(message.channel.unwrap().name, "General");
        assert_eq!(message.user.unwrap().name, "Matijs");
    }

    fn generate_json() -> String {
        "{
            \"ok\": true,
            \"url\": \"wss://ms9.slack-msgs.com/websocket/7I5yBpcvk\",
            \"self\": {
                \"id\": \"U023BECGF\",
                \"name\": \"bobby\",
                \"prefs\": {
                },
                \"created\": 1402463766,
                \"manual_presence\": \"active\"
            },
            \"team\": {
                \"id\": \"T024BE7LD\",
                \"name\": \"Example Team\",
                \"email_domain\": \"\",
                \"domain\": \"example\",
                \"msg_edit_window_mins\": -1,
                \"over_storage_limit\": false,
                \"prefs\": {
                },
                \"plan\": \"std\"
            },
            \"users\": [
                {
                    \"id\": \"xyz\",
                    \"name\": \"Matijs\"
                }
            ],
            \"channels\": [
                {
                    \"id\": \"zyx\",
                    \"name\": \"General\",
                    \"members\": [],
                    \"is_member\": false
                }
            ],
            \"groups\": [ ],
            \"ims\": [ ],
            \"bots\": [ ]
        }".to_string()
    }
}
<|endoftext|>


<commit_before>use lazy_static::lazy_static;
use reqwest::header::{HeaderMap, ACCEPT};
use reqwest::{blocking::Client, StatusCode};

use config::LinkChecker;
use errors::Result;

use std::collections::HashMap;
use std::error::Error;
use std::sync::{Arc, RwLock};

#[derive(Clone, Debug, PartialEq)]
pub struct LinkResult {
    pub code: Option<StatusCode>,
    /// Whether the HTTP request didn't make it to getting a HTTP code
    pub error: Option<String>,
}

impl LinkResult {
    pub fn is_valid(&self) -> bool {
        if self.error.is_some() {
            return false;
        }

        if let Some(c) = self.code {
            return c.is_success() || c == StatusCode::NOT_MODIFIED;
        }

        true
    }

    pub fn message(&self) -> String {
        if let Some(ref e) = self.error {
            return e.clone();
        }

        if let Some(c) = self.code {
            return format!("{}", c);
        }

        "Unknown error".to_string()
    }
}

lazy_static! {
    // Keep history of link checks so a rebuild doesn't have to check again
    static ref LINKS: Arc<RwLock<HashMap<String, LinkResult>>> = Arc::new(RwLock::new(HashMap::new()));
}

pub fn check_url(url: &str, config: &LinkChecker) -> LinkResult {
    {
        let guard = LINKS.read().unwrap();
        if let Some(res) = guard.get(url) {
            return res.clone();
        }
    }

    let mut headers = HeaderMap::new();
    headers.insert(ACCEPT, "text/html".parse().unwrap());
    headers.append(ACCEPT, "*/*".parse().unwrap());

    let client = Client::new();

    let check_anchor = !config.skip_anchor_prefixes.iter().any(|prefix| url.starts_with(prefix));

    // Need to actually do the link checking
    let res = match client.get(url).headers(headers).send() {
        Ok(ref mut response) if check_anchor && has_anchor(url) => {
            let body = {
                let mut buf: Vec<u8> = vec![];
                response.copy_to(&mut buf).unwrap();
                String::from_utf8(buf).unwrap()
            };

            match check_page_for_anchor(url, body) {
                Ok(_) => LinkResult { code: Some(response.status()), error: None },
                Err(e) => LinkResult { code: None, error: Some(e.to_string()) },
            }
        }
        Ok(response) => {
            if response.status().is_success() || response.status() == StatusCode::NOT_MODIFIED {
                LinkResult { code: Some(response.status()), error: None }
            } else {
                let error_string = if response.status().is_informational() {
                    format!("Informational status code ({}) received", response.status())
                } else if response.status().is_redirection() {
                    format!("Redirection status code ({}) received", response.status())
                } else if response.status().is_client_error() {
                    format!("Client error status code ({}) received", response.status())
                } else if response.status().is_server_error() {
                    format!("Server error status code ({}) received", response.status())
                } else {
                    format!("Non-success status code ({}) received", response.status())
                };

                LinkResult { code: None, error: Some(error_string) }
            }
        }
        Err(e) => LinkResult { code: None, error: Some(e.description().to_string()) },
    };

    LINKS.write().unwrap().insert(url.to_string(), res.clone());
    res
}

fn has_anchor(url: &str) -> bool {
    match url.find('#') {
        Some(index) => match url.get(index..=index + 1) {
            Some("#/") | Some("#!") | None => false,
            Some(_) => true,
        },
        None => false,
    }
}

fn check_page_for_anchor(url: &str, body: String) -> Result<()> {
    let index = url.find('#').unwrap();
    let anchor = url.get(index + 1..).unwrap();
    let checks: [String; 4] = [
        format!(" id='{}'", anchor),
        format!(r#" id="{}""#, anchor),
        format!(" name='{}'", anchor),
        format!(r#" name="{}""#, anchor),
    ];

    if checks.iter().any(|check| body[..].contains(&check[..])) {
        Ok(())
    } else {
        Err(errors::Error::from(format!("Anchor `#{}` not found on page", anchor)))
    }
}

#[cfg(test)]
mod tests {
    use super::{check_page_for_anchor, check_url, has_anchor, LinkChecker, LINKS};
    use mockito::mock;

    // NOTE: HTTP mock paths below are randomly generated to avoid name
    // collisions. Mocks with the same path can sometimes bleed between tests
    // and cause them to randomly pass/fail. Please make sure to use unique
    // paths when adding or modifying tests that use Mockito.

    #[test]
    fn can_validate_ok_links() {
        let url = format!("{}{}", mockito::server_url(), "/ekbtwxfhjw");
        let _m = mock("GET", "/ekbtwxfhjw")
            .with_header("Content-Type", "text/html")
            .with_body(format!(
                r#"<!DOCTYPE html>

  <title>Test</title>

  Mock URL

"#,
                url
            ))
            .create();

        let res = check_url(&url, &LinkChecker::default());
        assert!(res.is_valid());
        assert!(LINKS.read().unwrap().get(&url).is_some());
    }

    #[test]
    fn can_follow_301_links() {
        let _m1 = mock("GET", "/c7qrtrv3zz")
            .with_status(301)
            .with_header("Content-Type", "text/plain")
            .with_header("Location", format!("{}/rbs5avjs8e", mockito::server_url()).as_str())
            .with_body("Redirecting...")
            .create();

        let _m2 = mock("GET", "/rbs5avjs8e")
            .with_header("Content-Type", "text/plain")
            .with_body("Test")
            .create();

        let url = format!("{}{}", mockito::server_url(), "/c7qrtrv3zz");
        let res = check_url(&url, &LinkChecker::default());
        assert!(res.is_valid());
        assert!(res.code.is_some());
        assert!(res.error.is_none());
    }

    #[test]
    fn can_fail_301_to_404_links() {
        let _m1 = mock("GET", "/cav9vibhsc")
            .with_status(301)
            .with_header("Content-Type", "text/plain")
            .with_header("Location", format!("{}/72zmfg4smd", mockito::server_url()).as_str())
            .with_body("Redirecting...")
            .create();

        let _m2 = mock("GET", "/72zmfg4smd")
            .with_status(404)
            .with_header("Content-Type", "text/plain")
            .with_body("Not Found")
            .create();

        let url = format!("{}{}", mockito::server_url(), "/cav9vibhsc");
        let res = check_url(&url, &LinkChecker::default());
        assert_eq!(res.is_valid(), false);
        assert!(res.code.is_none());
        assert!(res.error.is_some());
    }

    #[test]
    fn can_fail_404_links() {
        let _m = mock("GET", "/nlhab9c1vc")
            .with_status(404)
            .with_header("Content-Type", "text/plain")
            .with_body("Not Found")
            .create();

        let url = format!("{}{}", mockito::server_url(), "/nlhab9c1vc");
        let res = check_url(&url, &LinkChecker::default());
        assert_eq!(res.is_valid(), false);
        assert!(res.code.is_none());
        assert!(res.error.is_some());
    }

    #[test]
    fn can_fail_500_links() {
        let _m = mock("GET", "/qdbrssazes")
            .with_status(500)
            .with_header("Content-Type", "text/plain")
            .with_body("Internal Server Error")
            .create();

        let url = format!("{}{}", mockito::server_url(), "/qdbrssazes");
        let res = check_url(&url, &LinkChecker::default());
        assert_eq!(res.is_valid(), false);
        assert!(res.code.is_none());
        assert!(res.error.is_some());
    }

    #[test]
    fn can_fail_unresolved_links() {
        let res = check_url("https://t6l5cn9lpm.lxizfnzckd", &LinkChecker::default());
        assert_eq!(res.is_valid(), false);
        assert!(res.code.is_none());
        assert!(res.error.is_some());
    }

    #[test]
    fn can_validate_anchors() {
        let url = "https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.collect";
        let body = r#"
collect
"#.to_string();
        let res = check_page_for_anchor(url, body);
        assert!(res.is_ok());
    }

    #[test]
    fn can_validate_anchors_with_other_quotes() {
        let url = "https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.collect";
        let body = r#"
collect
"#.to_string();
        let res = check_page_for_anchor(url, body);
        assert!(res.is_ok());
    }

    #[test]
    fn can_validate_anchors_with_name_attr() {
        let url = "https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.collect";
        let body = r#"
collect
"#.to_string();
        let res = check_page_for_anchor(url, body);
        assert!(res.is_ok());
    }

    #[test]
    fn can_fail_when_anchor_not_found() {
        let url = "https://doc.rust-lang.org/std/iter/trait.Iterator.html#me";
        let body = r#"
collect
"#.to_string();
        let res = check_page_for_anchor(url, body);
        assert!(res.is_err());
    }

    #[test]
    fn can_check_url_for_anchor() {
        let url = "https://doc.rust-lang.org/std/index.html#the-rust-standard-library";
        let res = has_anchor(url);
        assert_eq!(res, true);
    }

    #[test]
    fn will_return_false_when_no_anchor() {
        let url = "https://doc.rust-lang.org/std/index.html";
        let res = has_anchor(url);
        assert_eq!(res, false);
    }

    #[test]
    fn will_return_false_when_has_router_url() {
        let url = "https://doc.rust-lang.org/#/std";
        let res = has_anchor(url);
        assert_eq!(res, false);
    }

    #[test]
    fn will_return_false_when_has_router_url_alt() {
        let url = "https://doc.rust-lang.org/#!/std";
        let res = has_anchor(url);
        assert_eq!(res, false);
    }

    #[test]
    fn skip_anchor_prefixes() {
        let ignore_url = format!("{}{}", mockito::server_url(), "/ignore/");
        let config = LinkChecker { skip_prefixes: vec![], skip_anchor_prefixes: vec![ignore_url] };

        let _m1 = mock("GET", "/ignore/i30hobj1cy")
            .with_header("Content-Type", "text/html")
            .with_body(
                r#"<!DOCTYPE html>

  <title>Ignore</title>

"#,
            )
            .create();

        // anchor check is ignored because the url matches the prefix
        let ignore = format!("{}{}", mockito::server_url(), "/ignore/i30hobj1cy#nonexistent");
        assert!(check_url(&ignore, &config).is_valid());

        let _m2 = mock("GET", "/guvqcqwmth")
            .with_header("Content-Type", "text/html")
            .with_body(
                r#"<!DOCTYPE html>

  <title>Test</title>

"#,
            )
            .create();

        // other anchors are checked
        let existent = format!("{}{}", mockito::server_url(), "/guvqcqwmth#existent");
        assert!(check_url(&existent, &config).is_valid());

        let nonexistent = format!("{}{}", mockito::server_url(), "/guvqcqwmth#nonexistent");
        assert_eq!(check_url(&nonexistent, &config).is_valid(), false);
    }
}
<commit_msg>fixed warning (#912)<commit_after>use lazy_static::lazy_static;
use reqwest::header::{HeaderMap, ACCEPT};
use reqwest::{blocking::Client, StatusCode};

use config::LinkChecker;
use errors::Result;

use std::collections::HashMap;
use std::sync::{Arc, RwLock};

#[derive(Clone, Debug, PartialEq)]
pub struct LinkResult {
    pub code: Option<StatusCode>,
    /// Whether the HTTP request didn't make it to getting a HTTP code
    pub error: Option<String>,
}

impl LinkResult {
    pub fn is_valid(&self) -> bool {
        if self.error.is_some() {
            return false;
        }

        if let Some(c) = self.code {
            return c.is_success() || c == StatusCode::NOT_MODIFIED;
        }

        true
    }

    pub fn message(&self) -> String {
        if let Some(ref e) = self.error {
            return e.clone();
        }

        if let Some(c) = self.code {
            return format!("{}", c);
        }

        "Unknown error".to_string()
    }
}

lazy_static! {
    // Keep history of link checks so a rebuild doesn't have to check again
    static ref LINKS: Arc<RwLock<HashMap<String, LinkResult>>> = Arc::new(RwLock::new(HashMap::new()));
}

pub fn check_url(url: &str, config: &LinkChecker) -> LinkResult {
    {
        let guard = LINKS.read().unwrap();
        if let Some(res) = guard.get(url) {
            return res.clone();
        }
    }

    let mut headers = HeaderMap::new();
    headers.insert(ACCEPT, "text/html".parse().unwrap());
    headers.append(ACCEPT, "*/*".parse().unwrap());

    let client = Client::new();

    let check_anchor = !config.skip_anchor_prefixes.iter().any(|prefix| url.starts_with(prefix));

    // Need to actually do the link checking
    let res = match client.get(url).headers(headers).send() {
        Ok(ref mut response) if check_anchor && has_anchor(url) => {
            let body = {
                let mut buf: Vec<u8> = vec![];
                response.copy_to(&mut buf).unwrap();
                String::from_utf8(buf).unwrap()
            };

            match check_page_for_anchor(url, body) {
                Ok(_) => LinkResult { code: Some(response.status()), error: None },
                Err(e) => LinkResult { code: None, error: Some(e.to_string()) },
            }
        }
        Ok(response) => {
            if response.status().is_success() || response.status() == StatusCode::NOT_MODIFIED {
                LinkResult { code: Some(response.status()), error: None }
            } else {
                let error_string = if response.status().is_informational() {
                    format!("Informational status code ({}) received", response.status())
                } else if response.status().is_redirection() {
                    format!("Redirection status code ({}) received", response.status())
                } else if response.status().is_client_error() {
                    format!("Client error status code ({}) received", response.status())
                } else if response.status().is_server_error() {
                    format!("Server error status code ({}) received", response.status())
                } else {
                    format!("Non-success status code ({}) received", response.status())
                };

                LinkResult { code: None, error: Some(error_string) }
            }
        }
        Err(e) => LinkResult { code: None, error: Some(e.to_string()) },
    };

    LINKS.write().unwrap().insert(url.to_string(), res.clone());
    res
}

fn has_anchor(url: &str) -> bool {
    match url.find('#') {
        Some(index) => match url.get(index..=index + 1) {
            Some("#/") | Some("#!") | None => false,
            Some(_) => true,
        },
        None => false,
    }
}

fn check_page_for_anchor(url: &str, body: String) -> Result<()> {
    let index = url.find('#').unwrap();
    let anchor = url.get(index + 1..).unwrap();
    let checks: [String; 4] = [
        format!(" id='{}'", anchor),
        format!(r#" id="{}""#, anchor),
        format!(" name='{}'", anchor),
        format!(r#" name="{}""#, anchor),
    ];

    if checks.iter().any(|check| body[..].contains(&check[..])) {
        Ok(())
    } else {
        Err(errors::Error::from(format!("Anchor `#{}` not found on page", anchor)))
    }
}

#[cfg(test)]
mod tests {
    use super::{check_page_for_anchor, check_url, has_anchor, LinkChecker, LINKS};
    use mockito::mock;

    // NOTE: HTTP mock paths below are randomly generated to avoid name
    // collisions. Mocks with the same path can sometimes bleed between tests
    // and cause them to randomly pass/fail. Please make sure to use unique
    // paths when adding or modifying tests that use Mockito.

    #[test]
    fn can_validate_ok_links() {
        let url = format!("{}{}", mockito::server_url(), "/ekbtwxfhjw");
        let _m = mock("GET", "/ekbtwxfhjw")
            .with_header("Content-Type", "text/html")
            .with_body(format!(
                r#"<!DOCTYPE html>

  <title>Test</title>

  Mock URL

"#,
                url
            ))
            .create();

        let res = check_url(&url, &LinkChecker::default());
        assert!(res.is_valid());
        assert!(LINKS.read().unwrap().get(&url).is_some());
    }

    #[test]
    fn can_follow_301_links() {
        let _m1 = mock("GET", "/c7qrtrv3zz")
            .with_status(301)
            .with_header("Content-Type", "text/plain")
            .with_header("Location", format!("{}/rbs5avjs8e", mockito::server_url()).as_str())
            .with_body("Redirecting...")
            .create();

        let _m2 = mock("GET", "/rbs5avjs8e")
            .with_header("Content-Type", "text/plain")
            .with_body("Test")
            .create();

        let url = format!("{}{}", mockito::server_url(), "/c7qrtrv3zz");
        let res = check_url(&url, &LinkChecker::default());
        assert!(res.is_valid());
        assert!(res.code.is_some());
        assert!(res.error.is_none());
    }

    #[test]
    fn can_fail_301_to_404_links() {
        let _m1 = mock("GET", "/cav9vibhsc")
            .with_status(301)
            .with_header("Content-Type", "text/plain")
            .with_header("Location", format!("{}/72zmfg4smd", mockito::server_url()).as_str())
            .with_body("Redirecting...")
            .create();

        let _m2 = mock("GET", "/72zmfg4smd")
            .with_status(404)
            .with_header("Content-Type", "text/plain")
            .with_body("Not Found")
            .create();

        let url = format!("{}{}", mockito::server_url(), "/cav9vibhsc");
        let res = check_url(&url, &LinkChecker::default());
        assert_eq!(res.is_valid(), false);
        assert!(res.code.is_none());
        assert!(res.error.is_some());
    }

    #[test]
    fn can_fail_404_links() {
        let _m = mock("GET", "/nlhab9c1vc")
            .with_status(404)
            .with_header("Content-Type", "text/plain")
            .with_body("Not Found")
            .create();

        let url = format!("{}{}", mockito::server_url(), "/nlhab9c1vc");
        let res = check_url(&url, &LinkChecker::default());
        assert_eq!(res.is_valid(), false);
        assert!(res.code.is_none());
        assert!(res.error.is_some());
    }

    #[test]
    fn can_fail_500_links() {
        let _m = mock("GET", "/qdbrssazes")
            .with_status(500)
            .with_header("Content-Type", "text/plain")
            .with_body("Internal Server Error")
            .create();

        let url = format!("{}{}", mockito::server_url(), "/qdbrssazes");
        let res = check_url(&url, &LinkChecker::default());
        assert_eq!(res.is_valid(), false);
        assert!(res.code.is_none());
        assert!(res.error.is_some());
    }

    #[test]
    fn can_fail_unresolved_links() {
        let res = check_url("https://t6l5cn9lpm.lxizfnzckd", &LinkChecker::default());
        assert_eq!(res.is_valid(), false);
        assert!(res.code.is_none());
        assert!(res.error.is_some());
    }

    #[test]
    fn can_validate_anchors() {
        let url = "https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.collect";
        let body = r#"
collect
"#.to_string();
        let res = check_page_for_anchor(url, body);
        assert!(res.is_ok());
    }

    #[test]
    fn can_validate_anchors_with_other_quotes() {
        let url = "https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.collect";
        let body = r#"
collect
"#.to_string();
        let res = check_page_for_anchor(url, body);
        assert!(res.is_ok());
    }

    #[test]
    fn can_validate_anchors_with_name_attr() {
        let url = "https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.collect";
        let body = r#"
collect
"#.to_string();
        let res = check_page_for_anchor(url, body);
        assert!(res.is_ok());
    }

    #[test]
    fn can_fail_when_anchor_not_found() {
        let url = "https://doc.rust-lang.org/std/iter/trait.Iterator.html#me";
        let body = r#"
collect
"#.to_string();
        let res = check_page_for_anchor(url, body);
        assert!(res.is_err());
    }

    #[test]
    fn can_check_url_for_anchor() {
        let url = "https://doc.rust-lang.org/std/index.html#the-rust-standard-library";
        let res = has_anchor(url);
        assert_eq!(res, true);
    }

    #[test]
    fn will_return_false_when_no_anchor() {
        let url = "https://doc.rust-lang.org/std/index.html";
        let res = has_anchor(url);
        assert_eq!(res, false);
    }

    #[test]
    fn will_return_false_when_has_router_url() {
        let url = "https://doc.rust-lang.org/#/std";
        let res = has_anchor(url);
        assert_eq!(res, false);
    }

    #[test]
    fn will_return_false_when_has_router_url_alt() {
        let url = "https://doc.rust-lang.org/#!/std";
        let res = has_anchor(url);
        assert_eq!(res, false);
    }

    #[test]
    fn skip_anchor_prefixes() {
        let ignore_url = format!("{}{}", mockito::server_url(), "/ignore/");
        let config = LinkChecker { skip_prefixes: vec![], skip_anchor_prefixes: vec![ignore_url] };

        let _m1 = mock("GET", "/ignore/i30hobj1cy")
            .with_header("Content-Type", "text/html")
            .with_body(
                r#"<!DOCTYPE html>

  <title>Ignore</title>

"#,
            )
            .create();

        // anchor check is ignored because the url matches the prefix
        let ignore = format!("{}{}", mockito::server_url(), "/ignore/i30hobj1cy#nonexistent");
        assert!(check_url(&ignore, &config).is_valid());

        let _m2 = mock("GET", "/guvqcqwmth")
            .with_header("Content-Type", "text/html")
            .with_body(
                r#"<!DOCTYPE html>

  <title>Test</title>

"#,
            )
            .create();

        // other anchors are checked
        let existent = format!("{}{}", mockito::server_url(), "/guvqcqwmth#existent");
        assert!(check_url(&existent, &config).is_valid());

        let nonexistent = format!("{}{}", mockito::server_url(), "/guvqcqwmth#nonexistent");
        assert_eq!(check_url(&nonexistent, &config).is_valid(), false);
    }
}
<|endoftext|>


<commit_before>use std::ops::Range;

use byteorder::{ByteOrder, LittleEndian};

use gameboy::{CPU_FREQUENCY, MAX_CPU_CYCLES, MAX_DIV_REG_CYCLES, Gpu, Irq, Memory, Timer};
use gameboy::cartridge::{Cartridge, CartridgeDetails};
use gameboy::frame::Frame;
use super::memory_map::{self, Address};

const MAIN_MEM_SIZE: usize = 0x2000;
const ZRAM_SIZE: usize = 0x80;
const MMAP_SIZE: usize = 0x80;

pub struct Interconnect {
    pub gpu: Gpu,
    pub ram: Memory,
    pub zram: Memory,
    pub mmap_io: Memory,
    pub timer: Timer,
    pub irq: Irq,
    pub cart: Option<Cartridge>,
    pub interrupt: u8,
}

impl Interconnect {
    pub fn new() -> Interconnect {
        Interconnect {
            gpu: Gpu::new(),
            ram: Memory::new(MAIN_MEM_SIZE),
            zram: Memory::new(ZRAM_SIZE),
            mmap_io: Memory::new(MMAP_SIZE),
            timer: Timer::new(),
            irq: Irq::new(),
            cart: None,
            interrupt: 0x00,
        }
    }

    pub fn with_cart(cart: Cartridge) -> Interconnect {
        Interconnect {
            gpu: Gpu::new(),
            ram: Memory::new(MAIN_MEM_SIZE),
            zram: Memory::new(ZRAM_SIZE),
            mmap_io: Memory::new(MMAP_SIZE),
            timer: Timer::new(),
            irq: Irq::new(),
            cart: Some(cart),
            interrupt: 0x00,
        }
    }

    pub fn step(&mut self, cycles: usize) -> Result<(), String> {
        self.gpu.step(&mut self.irq, cycles)?;
        self.timer.step(&mut self.irq, cycles)?;

        Ok(())
    }

    pub fn reset(&mut self) {
        self.gpu.reset();
        self.timer.reset();
        self.irq.reset();
    }

    pub fn cart_details(&self) -> CartridgeDetails {
        let cart = self.cart.as_ref().expect("Cartridge is empty");

        cart.details(&self)
    }

    pub fn request_frame(&self) -> &Frame {
        &self.gpu.frame
    }

    pub fn write_u8(&mut self, addr: u16, byte: u8) {
        // Special case - DMA transfer
        if addr == 0xFF46 {
            self.dma_transfer(byte);
            return;
        }
        let cart = self.cart.as_mut().expect("Cartridge is empty");

        match memory_map::map_address(addr) {
            Address::Ram(a) => self.ram.write_u8(a, byte),
            Address::RamShadow(a) => self.ram.write_u8(a, byte),
            Address::Gfx(a) => self.gpu.ram.write_u8(a, byte),
            Address::CartRam(a) => cart.ram.write_u8(a, byte),
            Address::CartRom(a) => cart.rom.write_u8(a, byte),
            Address::ZRam(a) => {
                // if a != 0x00 {
                self.zram.write_u8(a, byte);
                // }
            }
            Address::Oam(a) => self.gpu.sprite_data.write_u8(a, byte),
            Address::Unused(_) => (),
            Address::Io(a) => {
                match a {
                    0x00 => println!("err: write to joypad not supported"),
                    0x01...0x02 => println!("err: write to serial driver not supported"),
                    0x04...0x07 => self.timer.write_u8(a, byte),
                    0x0F => self.irq.request_flag = byte,
                    0x10...0x26 => println!("err: write to sound driver not supported"),
                    0x30...0x3F => println!("err: write to wave pattern RAM not supported"),
                    0x40...0x45 => self.gpu.write_u8(a, byte),
                    0x47...0x49 => self.gpu.write_u8(a, byte),
                    0x4A...0x4B => self.gpu.write_u8(a, byte),
                    0x7F => self.mmap_io.write_u8(a, byte),
                    _ => panic!("write memory mapped I/O in unsupported range: {:04X}", a),
                }
            }
            Address::InterruptEnableRegister(a) => self.irq.enable_flag = byte,
            _ => {
                panic!("Unable to write byte to: {:#X}, invalid memory region.",
                       addr)
            }
        }
    }

    pub fn read_u8(&self, addr: u16) -> u8 {
        let cart = self.cart.as_ref().expect("Cartridge is empty");

        match memory_map::map_address(addr) {
            Address::Ram(addr) => self.ram.read_u8(addr),
            Address::RamShadow(addr) => self.ram.read_u8(addr),
            Address::CartRom(addr) |
            Address::CartRomOtherBank(addr) => cart.rom.read_u8(addr),
            Address::Gfx(value) => self.gpu.ram.read_u8(value),
            Address::CartRam(a) => cart.ram.read_u8(a),
            Address::ZRam(a) => self.zram.read_u8(a),
            Address::Oam(a) => self.gpu.sprite_data.read_u8(a),
            Address::Unused(_) => 0xFF, // Always return high
            Address::Io(a) => {
                match a {
                    0x00 => {
                        println!("err: read from joypad not supported");
                        0xFF
                    }
                    0x01...0x02 => {
                        println!("err: read from serial driver not supported");
                        0
                    }
                    0x04...0x07 => self.timer.read_u8(a),
                    0x0F => self.irq.request_flag,
                    0x10...0x26 => {
                        println!("err: read from sound driver not supported");
                        0
                    }
                    0x30...0x3F => {
                        println!("err: write to wave pattern RAM not supported");
                        0
                    }
                    0x40...0x45 => self.gpu.read_u8(a),
                    0x47...0x49 => self.gpu.read_u8(a),
                    0x4A...0x4B => self.gpu.read_u8(a),
                    0x7F => self.mmap_io.read_u8(a),
                    _ => panic!("read memory mapped I/O in unsupported range"),
                }
            }
            Address::InterruptEnableRegister(a) => self.irq.enable_flag,
            _ => panic!("Unable to read address: {:#X}", addr),
        }
    }

    pub fn read_bytes(&self, r: Range<u16>) -> &[u8] {
        let cart = self.cart.as_ref().expect("Cartridge is empty");

        match memory_map::map_address(r.start) {
            Address::Ram(_) |
            Address::RamShadow(_) => self.ram.read_bytes(r),
            Address::CartRom(_) |
            Address::CartRomOtherBank(_) => cart.rom.read_bytes(r),
            Address::Gfx(_) => self.gpu.ram.read_bytes(r),
            Address::CartRam(_) => cart.ram.read_bytes(r),
            Address::ZRam(a) => self.zram.read_bytes(r),
            Address::Io(a) => self.mmap_io.read_bytes(r),
            _ => panic!("Unable to read address range: {:?}", r),
        }
    }

    pub fn write_u16(&mut self, addr: u16, val: u16) {
        let cart = self.cart.as_ref().expect("Cartridge is empty");

        match memory_map::map_address(addr) {
            Address::Ram(a) => self.ram.write_u16(a, val),
            Address::RamShadow(a) => self.ram.write_u16(a, val),
            Address::ZRam(a) => self.zram.write_u16(a, val),
            _ => panic!("Unable to write address: {:#X}", addr),
        }
    }

    pub fn read_u16(&self, addr: u16) -> u16 {
        let cart = self.cart.as_ref().expect("Cartridge is empty");

        match memory_map::map_address(addr) {
            Address::Ram(addr) => self.ram.read_u16(addr),
            Address::RamShadow(addr) => self.ram.read_u16(addr),
            Address::CartRom(addr) |
            Address::CartRomOtherBank(addr) => cart.rom.read_u16(addr),
            Address::Gfx(value) => self.gpu.ram.read_u16(value),
            Address::CartRam(a) => cart.ram.read_u16(a),
            Address::ZRam(a) => self.zram.read_u16(a),
            Address::Io(a) => self.mmap_io.read_u16(a),
            _ => panic!("Unable to read address: {:#X}", addr),
        }
    }

    fn dma_transfer(&mut self, byte: u8) {
        let addr = ((byte as u16) << 0x08); // "The written value specifies the transfer source address divided by 0x100"
        for x in 0..0xA0 {
            let val = self.read_u8(addr + x);
            self.write_u8(0xFE00 + x, val);
        }
    }
}
<commit_msg>Small unused import cleanups<commit_after>use std::ops::Range;

use gameboy::{Gpu, Irq, Memory, Timer};
use gameboy::cartridge::{Cartridge, CartridgeDetails};
use gameboy::frame::Frame;
use super::memory_map::{self, Address};

const MAIN_MEM_SIZE: usize = 0x2000;
const ZRAM_SIZE: usize = 0x80;
const MMAP_SIZE: usize = 0x80;

pub struct Interconnect {
    pub gpu: Gpu,
    pub ram: Memory,
    pub zram: Memory,
    pub mmap_io: Memory,
    pub timer: Timer,
    pub irq: Irq,
    pub cart: Option<Cartridge>,
    pub interrupt: u8,
}

impl Interconnect {
    pub fn new() -> Interconnect {
        Interconnect {
            gpu: Gpu::new(),
            ram: Memory::new(MAIN_MEM_SIZE),
            zram: Memory::new(ZRAM_SIZE),
            mmap_io: Memory::new(MMAP_SIZE),
            timer: Timer::new(),
            irq: Irq::new(),
            cart: None,
            interrupt: 0x00,
        }
    }

    pub fn with_cart(cart: Cartridge) -> Interconnect {
        Interconnect {
            gpu: Gpu::new(),
            ram: Memory::new(MAIN_MEM_SIZE),
            zram: Memory::new(ZRAM_SIZE),
            mmap_io: Memory::new(MMAP_SIZE),
            timer: Timer::new(),
            irq: Irq::new(),
            cart: Some(cart),
            interrupt: 0x00,
        }
    }

    pub fn step(&mut self, cycles: usize) -> Result<(), String> {
        self.gpu.step(&mut self.irq, cycles)?;
        self.timer.step(&mut self.irq, cycles)?;

        Ok(())
    }

    pub fn reset(&mut self) {
        self.gpu.reset();
        self.timer.reset();
        self.irq.reset();
    }

    pub fn cart_details(&self) -> CartridgeDetails {
        let cart = self.cart.as_ref().expect("Cartridge is empty");

        cart.details(&self)
    }

    pub fn request_frame(&self) -> &Frame {
        &self.gpu.frame
    }

    pub fn write_u8(&mut self, addr: u16, byte: u8) {
        // Special case - DMA transfer
        if addr == 0xFF46 {
            self.dma_transfer(byte);
            return;
        }
        let cart = self.cart.as_mut().expect("Cartridge is empty");

        match memory_map::map_address(addr) {
            Address::Ram(a) => self.ram.write_u8(a, byte),
            Address::RamShadow(a) => self.ram.write_u8(a, byte),
            Address::Gfx(a) => self.gpu.ram.write_u8(a, byte),
            Address::CartRam(a) => cart.ram.write_u8(a, byte),
            Address::CartRom(a) => cart.rom.write_u8(a, byte),
            Address::ZRam(a) => {
                // if a != 0x00 {
                self.zram.write_u8(a, byte);
                // }
            }
            Address::Oam(a) => self.gpu.sprite_data.write_u8(a, byte),
            Address::Unused(_) => (),
            Address::Io(a) => {
                match a {
                    0x00 => println!("err: write to joypad not supported"),
                    0x01...0x02 => println!("err: write to serial driver not supported"),
                    0x04...0x07 => self.timer.write_u8(a, byte),
                    0x0F => self.irq.request_flag = byte,
                    0x10...0x26 => println!("err: write to sound driver not supported"),
                    0x30...0x3F => println!("err: write to wave pattern RAM not supported"),
                    0x40...0x45 => self.gpu.write_u8(a, byte),
                    0x47...0x49 => self.gpu.write_u8(a, byte),
                    0x4A...0x4B => self.gpu.write_u8(a, byte),
                    0x7F => self.mmap_io.write_u8(a, byte),
                    _ => panic!("write memory mapped I/O in unsupported range: {:04X}", a),
                }
            }
            Address::InterruptEnableRegister(a) => self.irq.enable_flag = byte,
            _ => {
                panic!("Unable to write byte to: {:#X}, invalid memory region.",
                       addr)
            }
        }
    }

    pub fn read_u8(&self, addr: u16) -> u8 {
        let cart = self.cart.as_ref().expect("Cartridge is empty");

        match memory_map::map_address(addr) {
            Address::Ram(addr) => self.ram.read_u8(addr),
            Address::RamShadow(addr) => self.ram.read_u8(addr),
            Address::CartRom(addr) |
            Address::CartRomOtherBank(addr) => cart.rom.read_u8(addr),
            Address::Gfx(value) => self.gpu.ram.read_u8(value),
            Address::CartRam(a) => cart.ram.read_u8(a),
            Address::ZRam(a) => self.zram.read_u8(a),
            Address::Oam(a) => self.gpu.sprite_data.read_u8(a),
            Address::Unused(_) => 0xFF, // Always return high
            Address::Io(a) => {
                match a {
                    0x00 => {
                        println!("err: read from joypad not supported");
                        0xFF
                    }
                    0x01...0x02 => {
                        println!("err: read from serial driver not supported");
                        0
                    }
                    0x04...0x07 => self.timer.read_u8(a),
                    0x0F => self.irq.request_flag,
                    0x10...0x26 => {
                        println!("err: read from sound driver not supported");
                        0
                    }
                    0x30...0x3F => {
                        println!("err: write to wave pattern RAM not supported");
                        0
                    }
                    0x40...0x45 => self.gpu.read_u8(a),
                    0x47...0x49 => self.gpu.read_u8(a),
                    0x4A...0x4B => self.gpu.read_u8(a),
                    0x7F => self.mmap_io.read_u8(a),
                    _ => panic!("read memory mapped I/O in unsupported range"),
                }
            }
            Address::InterruptEnableRegister(a) => self.irq.enable_flag,
            _ => panic!("Unable to read address: {:#X}", addr),
        }
    }

    pub fn read_bytes(&self, r: Range<u16>) -> &[u8] {
        let cart = self.cart.as_ref().expect("Cartridge is empty");

        match memory_map::map_address(r.start) {
            Address::Ram(_) |
            Address::RamShadow(_) => self.ram.read_bytes(r),
            Address::CartRom(_) |
            Address::CartRomOtherBank(_) => cart.rom.read_bytes(r),
            Address::Gfx(_) => self.gpu.ram.read_bytes(r),
            Address::CartRam(_) => cart.ram.read_bytes(r),
            Address::ZRam(a) => self.zram.read_bytes(r),
            Address::Io(a) => self.mmap_io.read_bytes(r),
            _ => panic!("Unable to read address range: {:?}", r),
        }
    }

    pub fn write_u16(&mut self, addr: u16, val: u16) {
        let cart = self.cart.as_ref().expect("Cartridge is empty");

        match memory_map::map_address(addr) {
            Address::Ram(a) => self.ram.write_u16(a, val),
            Address::RamShadow(a) => self.ram.write_u16(a, val),
            Address::ZRam(a) => self.zram.write_u16(a, val),
            _ => panic!("Unable to write address: {:#X}", addr),
        }
    }

    pub fn read_u16(&self, addr: u16) -> u16 {
        let cart = self.cart.as_ref().expect("Cartridge is empty");

        match memory_map::map_address(addr) {
            Address::Ram(addr) => self.ram.read_u16(addr),
            Address::RamShadow(addr) => self.ram.read_u16(addr),
            Address::CartRom(addr) |
            Address::CartRomOtherBank(addr) => cart.rom.read_u16(addr),
            Address::Gfx(value) => self.gpu.ram.read_u16(value),
            Address::CartRam(a) => cart.ram.read_u16(a),
            Address::ZRam(a) => self.zram.read_u16(a),
            Address::Io(a) => self.mmap_io.read_u16(a),
            _ => panic!("Unable to read address: {:#X}", addr),
        }
    }

    fn dma_transfer(&mut self, byte: u8) {
        let addr = ((byte as u16) << 0x08); // "The written value specifies the transfer source address divided by 0x100"
        for x in 0..0xA0 {
            let val = self.read_u8(addr + x);
            self.write_u8(0xFE00 + x, val);
        }
    }
}
<|endoftext|>