<commit_before>// Copyright 2013-2014 The gl-rs developers. For a full listing of the authors,
// refer to the AUTHORS file at the top-level directory of this distribution.
//
// 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.

//! # gl_generator
//!
//! `gl_generator` is an OpenGL bindings generator plugin. It defines a macro
//!  named `generate_gl_bindings!` which can be used to generate all constants
//!  and functions of a given OpenGL version.
//!
//! ## Example
//!
//! ~~~rust
//! #[phase(plugin)]
//! extern crate gl_generator;
//! extern crate libc;
//!
//! use std::mem;
//! use self::types::*;
//!
//! generate_gl_bindings! {
//!     api: gl,
//!     profile: core,
//!     version: 4.5,
//!     generator: global,
//!     extensions: [
//!         GL_EXT_texture_filter_anisotropic,
//!     ],
//! }
//! ~~~
//!
//! ## Field Syntax
//!
//! Each field can be specified at most once, or not at all. If the field is not
//! specified, then a default value will be used. The syntax for each field is
//! as follows (in [EBNF](http://en.wikipedia.org/wiki/Extended_Backus-Naur_Form)):
//!
//! ~~~ebnf
//! api        = "api:",        ("gl" | "gles1" | "gles2" | "wgl" | "glx" | "egl")
//! profile    = "profile:",    ("core" | "compatibility")
//! version    = "version:",    digit, ".", digit
//! generator  = "generator:",  ("static" | "global" | "struct")
//! extensions = "extensions:", "[", [ [ ident ], { ",", ident } ], [ "," ], "]"
//! ~~~
//!
//! ### Notes
//!
//! - `api`: The API to generate. Defaults to `gl`.
//! - `profile`: Defaults to `core`. `core` will only include all functions
//!    supported by the requested version it self, while `compatibility` will
//!    include all the functions from previous versions as well.
//! - `version`: The requested API version. Defaults to `1.0`.
//! - `generator`: The type of loader to generate. Defaults to `static`.
//! - `extensions`: Extra extensions to include in the bindings. Defaults to `[]`.
//!
//! ## About EGL
//!
//! When you generate bindings for EGL, the following platform-specific types must be declared
//!  *at the same level where you call `generate_gl_bindings`*:
//!
//! - `khronos_utime_nanoseconds_t`
//! - `khronos_uint64_t`
//! - `khronos_ssize_t`
//! - `EGLNativeDisplayType`
//! - `EGLNativePixmapType`
//! - `EGLNativeWindowType`
//! - `EGLint`
//! - `NativeDisplayType`
//! - `NativePixmapType`
//! - `NativeWindowType`
//!

#![crate_name = "gl_generator"]
#![comment = "OpenGL function loader generator."]
#![license = "ASL2"]
#![crate_type = "dylib"]

#![feature(advanced_slice_patterns)]
#![feature(macro_rules)]
#![feature(phase)]
#![feature(plugin_registrar)]
#![feature(quote)]

#[phase(plugin, link)]
extern crate log;

#[phase(plugin)]
extern crate regex_macros;

extern crate khronos_api;
extern crate regex;
extern crate rustc;
extern crate syntax;

use generators::Generator;
use registry::{Registry, Filter, Ns};
use syntax::ast::{TokenTree, TTDelim, TTTok};
use syntax::codemap::Span;
use syntax::ext::base::{DummyResult, ExtCtxt, MacResult, MacItems};
use syntax::parse::token;

mod generators;

#[allow(dead_code)]
mod registry;

#[plugin_registrar]
#[doc(hidden)]
pub fn plugin_registrar(reg: &mut ::rustc::plugin::Registry) {
    reg.register_macro("generate_gl_bindings", macro_handler);
}

/// A predicate that is useful for splitting a comma separated list of tokens
fn is_comma(tt: &TokenTree) -> bool {
    match *tt {
        TTTok(_, token::COMMA) => true,
        _ => false,
    }
}

/// Drops a trailing comma if it exists
fn drop_trailing_comma(tts: &[TokenTree]) -> &[TokenTree] {
    match tts {
        [tts.., TTTok(_, token::COMMA)] => tts,
        tts => tts,
    }
}

/// handler for generate_gl_bindings!
fn macro_handler(ecx: &mut ExtCtxt, span: Span, tts: &[TokenTree]) -> Box<MacResult+'static> {
    // Generator options
    let mut api = None::<(Ns, &'static [u8])>;
    let mut profile = None::<String>;
    let mut version = None::<String>;
    let mut generator = None::<Box<Generator>>;
    let mut extensions = None::<Vec<String>>;

    let tts = drop_trailing_comma(tts);

    // Iterate through the comma separated
    for tts in tts.split(is_comma) {
        let mut it = tts.iter();
        let (field, field_span) = match it.next() {
            Some(&TTTok(span, token::IDENT(ref field, _))) => {
                (field.as_str(), span)
            },
            _ => {
                ecx.span_err(span, "Expected a generator attribute, either: \
                                    `api`, `profile`, `version`, `generator`, \
                                    or `extensions`.");
                return DummyResult::any(span);
            },
        };
        match it.next() {
            Some(&TTTok(_, token::COLON)) => {},
            _ => {
                ecx.span_err(span, "Expected a `:`");
                return DummyResult::any(span);
            },
        }
        match (field, it.collect::<Vec<_>>().as_slice()) {
            ("api", tts) => {
                if api.is_some() {
                    ecx.span_err(field_span, "An API was already specified.");
                    return DummyResult::any(span);
                }
                api = Some(match tts {
                    [&TTTok(_, token::IDENT(ref api, _))] if api.as_str() == "gl"
                        => (registry::Gl, khronos_api::GL_XML),
                    [&TTTok(_, token::IDENT(ref api, _))] if api.as_str() == "glx"
                        => (registry::Glx, khronos_api::GLX_XML),
                    [&TTTok(_, token::IDENT(ref api, _))] if api.as_str() == "wgl"
                        => (registry::Wgl, khronos_api::WGL_XML),
                    [&TTTok(_, token::IDENT(ref api, _))] if api.as_str() == "egl"
                        => (registry::Egl, khronos_api::EGL_XML),
                    [&TTTok(_, token::IDENT(ref api, _))] if api.as_str() == "gles1"
                        => (registry::Gles1, khronos_api::GL_XML),
                    [&TTTok(_, token::IDENT(ref api, _))] if api.as_str() == "gles2"
                        => (registry::Gles2, khronos_api::GL_XML),
                    [&TTTok(span, token::IDENT(ref api, _))] => {
                        ecx.span_err(span, format!("Unknown API `{}`", api.as_str()).as_slice());
                        return DummyResult::any(span);
                    },
                    _ => {
                        ecx.span_err(field_span, "Invalid API format, \
                                                  expected identifier");
                        return DummyResult::any(span);
                    }
                })
            }
            ("profile", tts) => {
                if profile.is_some() {
                    ecx.span_err(field_span, "A profile was already specified.");
                    return DummyResult::any(span);
                }
                profile = Some(match tts {
                    [&TTTok(_, token::IDENT(ref profile, _))] if profile.as_str() == "core"
                        => "core".to_string(),
                    [&TTTok(_, token::IDENT(ref profile, _))] if profile.as_str() == "compatibility"
                        => "compatibility".to_string(),
                    [&TTTok(_, token::IDENT(ref profile, _))] => {
                        ecx.span_err(span, format!("Unknown profile `{}`",
                                                   profile.as_str()).as_slice());
                        return DummyResult::any(span);
                    },
                    _ => {
                        ecx.span_err(field_span, "Invalid profile format, \
                                                  expected identifier");
                        return DummyResult::any(span);
                    },
                })
            }
            ("version", tts) => {
                if version.is_some() {
                    ecx.span_err(field_span, "A version was already specified.");
                    return DummyResult::any(span);
                }
                version = Some(match tts {
                    [&TTTok(_, token::LIT_FLOAT(ref version))] => {
                        version.as_str().to_string()
                    },
                    _ => {
                        ecx.span_err(field_span, "Invalid version format, \
                                                  expected `major.minor`");
                        return DummyResult::any(span);
                    },
                });
            }
            ("generator", tts) => {
                if generator.is_some() {
                    ecx.span_err(field_span, "A generator was already specified.");
                    return DummyResult::any(span);
                }
                generator = Some(match tts {
                    [&TTTok(_, token::IDENT(ref gen, _))] if gen.as_str() == "global"
                        => box generators::global_gen::GlobalGenerator as Box<Generator>,
                    [&TTTok(_, token::IDENT(ref gen, _))] if gen.as_str() == "struct"
                        => box generators::struct_gen::StructGenerator as Box<Generator>,
                    [&TTTok(_, token::IDENT(ref gen, _))] if gen.as_str() == "static"
                        => box generators::static_gen::StaticGenerator as Box<Generator>,
                    [&TTTok(span, token::IDENT(ref gen, _))] => {
                        ecx.span_err(span, format!("Unknown generator `{}`",
                                                   gen.as_str()).as_slice());
                        return DummyResult::any(span);
                    },
                    _ => {
                        ecx.span_err(field_span, "Invalid generator format, \
                                                  expected identifier");
                        return DummyResult::any(span);
                    },
                });
            }
            ("extensions", tts) => {
                if extensions.is_some() {
                    ecx.span_err(field_span, "The list of extensions were \
                                              already specified.");
                    return DummyResult::any(span);
                }
                extensions = Some(match tts {
                    [&TTDelim(ref tts)] => match tts.as_slice() {
                        [TTTok(_, token::LBRACKET),
                         tts..,
                         TTTok(_, token::RBRACKET)] => {
                            // Drop the trailing comma if it exists
                            let tts = drop_trailing_comma(tts);

                            // Collect the extensions, breaking early if a parse
                            // error occurs.
                            let mut failed = false;
                            let exts = tts.split(is_comma)
                                          .scan((), |_, tts| match tts {
                                [TTTok(_, token::IDENT(ref ext, _))] => {
                                    Some(ext.as_str().to_string())
                                },
                                _ => {
                                    failed = true;
                                    None
                                },
                            }).collect();

                            // Cause an error if there is still some leftover
                            // tokens.
                            if failed {
                                ecx.span_err(field_span, "Invalid extension \
                                                          format, expected \
                                                          identifier");
                                return DummyResult::any(span);
                            } else {
                                exts
                            }
                        },
                        _ => {
                            ecx.span_err(field_span, "Expected a comma \
                                                      separated list of \
                                                      extension identifiers \
                                                      delimited by square \
                                                      brackets: `[]`");
                            return DummyResult::any(span);
                        }
                    },
                    _ => {
                        ecx.span_err(field_span, "Expected a comma separated \
                                                  list of extension \
                                                  identifiers.");
                        return DummyResult::any(span);
                    },
                });
            }
            (field, _) => {
                ecx.span_err(span, format!("Unknown field `{}`", field).as_slice());
                return DummyResult::any(span);
            }
        }
    }

    // Use default values if the parameters have not been set
    let (ns, source) = api.unwrap_or((registry::Gl, khronos_api::GL_XML));
    let extensions = extensions.unwrap_or(vec![]);
    let version = version.unwrap_or("1.0".to_string());
    let generator = generator.unwrap_or(box generators::static_gen::StaticGenerator);
    let profile = profile.unwrap_or("core".to_string());

    // Get generator field values, using default values if they have not been
    // specified
    let filter = Some(Filter {
        api: ns.to_string(),
        extensions: extensions,
        version: version,
        profile: profile,
    });

    // Generate the registry of all bindings
    let registry = {
        use std::io::BufReader;
        use std::task;

        let result = task::try(proc() {
            let reader = BufReader::new(source);
            Registry::from_xml(reader, ns, filter)
        });

        match result {
            Ok(reg) => reg,
            Err(err) => {
                use std::any::{Any, AnyRefExt};
                let err: &Any = &*err;

                match err {
                    err if err.is::<String>() => {
                        ecx.span_err(span, "error while parsing the registry");
                        ecx.span_err(span, err.downcast_ref::<String>().unwrap().as_slice());
                    },
                    err if err.is::<&'static str>() => {
                        ecx.span_err(span, "error while parsing the registry");
                        ecx.span_err(span, err.downcast_ref::<&'static str>().unwrap().as_slice());
                    },
                    _ => {
                        ecx.span_err(span, "unknown error while parsing the registry");
                    }
                }

                return DummyResult::any(span);
            }
        }
    };

    // generating the Rust bindings as a source code into "buffer"
    let items = generator.write(ecx, &registry, ns);

    MacItems::new(items.into_iter())
}
<commit_msg>Use proper span for error messages<commit_after>// Copyright 2013-2014 The gl-rs developers. For a full listing of the authors,
// refer to the AUTHORS file at the top-level directory of this distribution.
//
// 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.

//! # gl_generator
//!
//! `gl_generator` is an OpenGL bindings generator plugin. It defines a macro
//!  named `generate_gl_bindings!` which can be used to generate all constants
//!  and functions of a given OpenGL version.
//!
//! ## Example
//!
//! ~~~rust
//! #[phase(plugin)]
//! extern crate gl_generator;
//! extern crate libc;
//!
//! use std::mem;
//! use self::types::*;
//!
//! generate_gl_bindings! {
//!     api: gl,
//!     profile: core,
//!     version: 4.5,
//!     generator: global,
//!     extensions: [
//!         GL_EXT_texture_filter_anisotropic,
//!     ],
//! }
//! ~~~
//!
//! ## Field Syntax
//!
//! Each field can be specified at most once, or not at all. If the field is not
//! specified, then a default value will be used. The syntax for each field is
//! as follows (in [EBNF](http://en.wikipedia.org/wiki/Extended_Backus-Naur_Form)):
//!
//! ~~~ebnf
//! api        = "api:",        ("gl" | "gles1" | "gles2" | "wgl" | "glx" | "egl")
//! profile    = "profile:",    ("core" | "compatibility")
//! version    = "version:",    digit, ".", digit
//! generator  = "generator:",  ("static" | "global" | "struct")
//! extensions = "extensions:", "[", [ [ ident ], { ",", ident } ], [ "," ], "]"
//! ~~~
//!
//! ### Notes
//!
//! - `api`: The API to generate. Defaults to `gl`.
//! - `profile`: Defaults to `core`. `core` will only include all functions
//!    supported by the requested version it self, while `compatibility` will
//!    include all the functions from previous versions as well.
//! - `version`: The requested API version. Defaults to `1.0`.
//! - `generator`: The type of loader to generate. Defaults to `static`.
//! - `extensions`: Extra extensions to include in the bindings. Defaults to `[]`.
//!
//! ## About EGL
//!
//! When you generate bindings for EGL, the following platform-specific types must be declared
//!  *at the same level where you call `generate_gl_bindings`*:
//!
//! - `khronos_utime_nanoseconds_t`
//! - `khronos_uint64_t`
//! - `khronos_ssize_t`
//! - `EGLNativeDisplayType`
//! - `EGLNativePixmapType`
//! - `EGLNativeWindowType`
//! - `EGLint`
//! - `NativeDisplayType`
//! - `NativePixmapType`
//! - `NativeWindowType`
//!

#![crate_name = "gl_generator"]
#![comment = "OpenGL function loader generator."]
#![license = "ASL2"]
#![crate_type = "dylib"]

#![feature(advanced_slice_patterns)]
#![feature(macro_rules)]
#![feature(phase)]
#![feature(plugin_registrar)]
#![feature(quote)]

#[phase(plugin, link)]
extern crate log;

#[phase(plugin)]
extern crate regex_macros;

extern crate khronos_api;
extern crate regex;
extern crate rustc;
extern crate syntax;

use generators::Generator;
use registry::{Registry, Filter, Ns};
use syntax::ast::{TokenTree, TTDelim, TTNonterminal, TTSeq, TTTok};
use syntax::codemap::Span;
use syntax::ext::base::{DummyResult, ExtCtxt, MacResult, MacItems};
use syntax::parse::token;

mod generators;

#[allow(dead_code)]
mod registry;

#[plugin_registrar]
#[doc(hidden)]
pub fn plugin_registrar(reg: &mut ::rustc::plugin::Registry) {
    reg.register_macro("generate_gl_bindings", macro_handler);
}

/// A predicate that is useful for splitting a comma separated list of tokens
fn is_comma(tt: &TokenTree) -> bool {
    match *tt {
        TTTok(_, token::COMMA) => true,
        _ => false,
    }
}

/// Drops a trailing comma if it exists
fn drop_trailing_comma(tts: &[TokenTree]) -> &[TokenTree] {
    match tts {
        [tts.., TTTok(_, token::COMMA)] => tts,
        tts => tts,
    }
}

fn get_span_from_tt(tt: &TokenTree) -> Span {
    match *tt {
        TTTok(span, _) => span,
        TTSeq(span, _, _, _) => span,
        TTNonterminal(span, _) => span,
        TTDelim(ref tts) => get_span_from_tt(
            tts.as_slice().head()
               .expect("Delimetered token trees should contain at least \
                        two elements.")
        ),
    }
}

/// handler for generate_gl_bindings!
fn macro_handler(ecx: &mut ExtCtxt, span: Span, tts: &[TokenTree]) -> Box<MacResult+'static> {
    // Generator options
    let mut api = None::<(Ns, &'static [u8])>;
    let mut profile = None::<String>;
    let mut version = None::<String>;
    let mut generator = None::<Box<Generator>>;
    let mut extensions = None::<Vec<String>>;

    let tts = drop_trailing_comma(tts);

    // Iterate through the comma separated
    for tts in tts.split(is_comma) {
        let mut it = tts.iter();
        let field = match it.next() {
            Some(&TTTok(_, token::IDENT(ref field, _))) => field.as_str(),
            tt => {
                let span = tt.map(get_span_from_tt).unwrap_or(span);
                ecx.span_err(span, "Expected a generator argument name, \
                                    either: `api`, `profile`, `version`, \
                                    `generator`, or `extensions`.");
                return DummyResult::any(span);
            },
        };
        match it.next() {
            Some(&TTTok(_, token::COLON)) => {},
            tt => {
                let span = tt.map(get_span_from_tt).unwrap_or(span);
                ecx.span_err(span, "Expected `:`");
                return DummyResult::any(span);
            },
        }
        match (field, it.collect::<Vec<_>>().as_slice()) {
            ("api", tts) => {
                if api.is_some() {
                    let span = tts.head().map_or(span, |tt| get_span_from_tt(*tt));
                    ecx.span_err(span, "An API was already specified.");
                    return DummyResult::any(span);
                }
                api = Some(match tts {
                    [&TTTok(_, token::IDENT(ref api, _))] if api.as_str() == "gl"
                        => (registry::Gl, khronos_api::GL_XML),
                    [&TTTok(_, token::IDENT(ref api, _))] if api.as_str() == "glx"
                        => (registry::Glx, khronos_api::GLX_XML),
                    [&TTTok(_, token::IDENT(ref api, _))] if api.as_str() == "wgl"
                        => (registry::Wgl, khronos_api::WGL_XML),
                    [&TTTok(_, token::IDENT(ref api, _))] if api.as_str() == "egl"
                        => (registry::Egl, khronos_api::EGL_XML),
                    [&TTTok(_, token::IDENT(ref api, _))] if api.as_str() == "gles1"
                        => (registry::Gles1, khronos_api::GL_XML),
                    [&TTTok(_, token::IDENT(ref api, _))] if api.as_str() == "gles2"
                        => (registry::Gles2, khronos_api::GL_XML),
                    [&TTTok(span, token::IDENT(ref api, _))] => {
                        ecx.span_err(span, format!("Unknown API `{}`", api.as_str()).as_slice());
                        return DummyResult::any(span);
                    },
                    _ => {
                        let span = tts.head().map_or(span, |tt| get_span_from_tt(*tt));
                        ecx.span_err(span, "Invalid API format, expected \
                                            identifier");
                        return DummyResult::any(span);
                    }
                })
            }
            ("profile", tts) => {
                if profile.is_some() {
                    ecx.span_err(get_span_from_tt(tts[0]),
                                 "A profile was already specified.");
                    return DummyResult::any(span);
                }
                profile = Some(match tts {
                    [&TTTok(_, token::IDENT(ref profile, _))] if profile.as_str() == "core"
                        => "core".to_string(),
                    [&TTTok(_, token::IDENT(ref profile, _))] if profile.as_str() == "compatibility"
                        => "compatibility".to_string(),
                    [&TTTok(_, token::IDENT(ref profile, _))] => {
                        let span = tts.head().map_or(span, |tt| get_span_from_tt(*tt));
                        ecx.span_err(span, format!("Unknown profile `{}`",
                                                   profile.as_str()).as_slice());
                        return DummyResult::any(span);
                    },
                    _ => {
                        let span = tts.head().map_or(span, |tt| get_span_from_tt(*tt));
                        ecx.span_err(span, "Invalid profile format, expected \
                                            identifier");
                        return DummyResult::any(span);
                    },
                })
            }
            ("version", tts) => {
                if version.is_some() {
                    let span = tts.head().map_or(span, |tt| get_span_from_tt(*tt));
                    ecx.span_err(span, "A version was already specified.");
                    return DummyResult::any(span);
                }
                version = Some(match tts {
                    [&TTTok(_, token::LIT_FLOAT(ref version))] => {
                        version.as_str().to_string()
                    },
                    _ => {
                        let span = tts.head().map_or(span, |tt| get_span_from_tt(*tt));
                        ecx.span_err(span, "Invalid version format, expected \
                                            `major.minor`");
                        return DummyResult::any(span);
                    },
                });
            }
            ("generator", tts) => {
                if generator.is_some() {
                    let span = tts.head().map_or(span, |tt| get_span_from_tt(*tt));
                    ecx.span_err(span, "A generator was already specified.");
                    return DummyResult::any(span);
                }
                generator = Some(match tts {
                    [&TTTok(_, token::IDENT(ref gen, _))] if gen.as_str() == "global"
                        => box generators::global_gen::GlobalGenerator as Box<Generator>,
                    [&TTTok(_, token::IDENT(ref gen, _))] if gen.as_str() == "struct"
                        => box generators::struct_gen::StructGenerator as Box<Generator>,
                    [&TTTok(_, token::IDENT(ref gen, _))] if gen.as_str() == "static"
                        => box generators::static_gen::StaticGenerator as Box<Generator>,
                    [&TTTok(span, token::IDENT(ref gen, _))] => {
                        ecx.span_err(span, format!("Unknown generator `{}`",
                                                   gen.as_str()).as_slice());
                        return DummyResult::any(span);
                    },
                    _ => {
                        let span = tts.head().map_or(span, |tt| get_span_from_tt(*tt));
                        ecx.span_err(span, "Invalid generator format, expected \
                                            identifier");
                        return DummyResult::any(span);
                    },
                });
            }
            ("extensions", tts) => {
                if extensions.is_some() {
                    let span = tts.head().map_or(span, |tt| get_span_from_tt(*tt));
                    ecx.span_err(span, "The list of extensions were already \
                                        specified.");
                    return DummyResult::any(span);
                }
                extensions = Some(match tts {
                    [&TTDelim(ref tts)] => match tts.as_slice() {
                        [TTTok(_, token::LBRACKET),
                         tts..,
                         TTTok(_, token::RBRACKET)] => {
                            // Drop the trailing comma if it exists
                            let tts = drop_trailing_comma(tts);

                            // Collect the extensions, breaking early if a parse
                            // error occurs.
                            let mut failed = false;
                            let exts = tts.split(is_comma)
                                          .scan((), |_, tts| match tts {
                                [TTTok(_, token::IDENT(ref ext, _))] => {
                                    Some(ext.as_str().to_string())
                                },
                                _ => {
                                    failed = true;
                                    None
                                },
                            }).collect();

                            // Cause an error if there is still some leftover
                            // tokens.
                            if failed {
                                let span = tts.head().map_or(span, get_span_from_tt);
                                ecx.span_err(span, "Invalid extension format, \
                                                    expected identifier");
                                return DummyResult::any(span);
                            } else {
                                exts
                            }
                        },
                        _ => {
                            let span = tts.as_slice().head().map_or(span, get_span_from_tt);
                            ecx.span_err(span, "Expected a comma separated \
                                                list of extension identifiers \
                                                delimited by square brackets: \
                                                `[]`");
                            return DummyResult::any(span);
                        }
                    },
                    _ => {
                        let span = tts.head().map_or(span, |tt| get_span_from_tt(*tt));
                        ecx.span_err(span, "Expected a comma separated list of \
                                            extension identifiers.");
                        return DummyResult::any(span);
                    },
                });
            }
            (field, _) => {
                ecx.span_err(span, format!("Unknown field `{}`", field).as_slice());
                return DummyResult::any(span);
            }
        }
    }

    // Use default values if the parameters have not been set
    let (ns, source) = api.unwrap_or((registry::Gl, khronos_api::GL_XML));
    let extensions = extensions.unwrap_or(vec![]);
    let version = version.unwrap_or("1.0".to_string());
    let generator = generator.unwrap_or(box generators::static_gen::StaticGenerator);
    let profile = profile.unwrap_or("core".to_string());

    // Get generator field values, using default values if they have not been
    // specified
    let filter = Some(Filter {
        api: ns.to_string(),
        extensions: extensions,
        version: version,
        profile: profile,
    });

    // Generate the registry of all bindings
    let registry = {
        use std::io::BufReader;
        use std::task;

        let result = task::try(proc() {
            let reader = BufReader::new(source);
            Registry::from_xml(reader, ns, filter)
        });

        match result {
            Ok(reg) => reg,
            Err(err) => {
                use std::any::{Any, AnyRefExt};
                let err: &Any = &*err;

                match err {
                    err if err.is::<String>() => {
                        ecx.span_err(span, "error while parsing the registry");
                        ecx.span_err(span, err.downcast_ref::<String>().unwrap().as_slice());
                    },
                    err if err.is::<&'static str>() => {
                        ecx.span_err(span, "error while parsing the registry");
                        ecx.span_err(span, err.downcast_ref::<&'static str>().unwrap().as_slice());
                    },
                    _ => {
                        ecx.span_err(span, "unknown error while parsing the registry");
                    }
                }

                return DummyResult::any(span);
            }
        }
    };

    // generating the Rust bindings as a source code into "buffer"
    let items = generator.write(ecx, &registry, ns);

    MacItems::new(items.into_iter())
}
<|endoftext|>


<commit_before>// Copyright 2019 The Grin Developers
//
// 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.

//! Provides a connection wrapper that handles the lower level tasks in sending
//! or receiving data from the TCP socket, as well as dealing with timeouts.
//!
//! Because of a few idiosyncracies in the Rust `TcpStream`, this has to use
//! async I/O to be able to both read *and* write on the connection. Which
//! forces us to go through some additional gymnastic to loop over the async
//! stream and make sure we get the right number of bytes out.

use crate::core::ser;
use crate::core::ser::{FixedLength, ProtocolVersion};
use crate::msg::{
	read_body, read_discard, read_header, read_item, write_message, Msg, MsgHeader,
	MsgHeaderWrapper,
};
use crate::types::Error;
use crate::util::{RateCounter, RwLock};
use std::io::{self, Read, Write};
use std::net::{Shutdown, TcpStream};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{mpsc, Arc};
use std::time::Duration;
use std::{
	cmp,
	thread::{self, JoinHandle},
};

const IO_TIMEOUT: Duration = Duration::from_millis(10000);

/// A trait to be implemented in order to receive messages from the
/// connection. Allows providing an optional response.
pub trait MessageHandler: Send + 'static {
	fn consume<'a>(&self, msg: Message<'a>, tracker: Arc<Tracker>) -> Result<Option<Msg>, Error>;
}

// Macro to simplify the boilerplate around async I/O error handling,
// especially with WouldBlock kind of errors.
macro_rules! try_break {
	($inner:expr) => {
		match $inner {
			Ok(v) => Some(v),
			Err(Error::Connection(ref e)) if e.kind() == io::ErrorKind::TimedOut => None,
			Err(Error::Connection(ref e)) if e.kind() == io::ErrorKind::WouldBlock => {
				// to avoid the heavy polling which will consume CPU 100%
				thread::sleep(Duration::from_millis(10));
				None
				}
			Err(Error::Store(_))
			| Err(Error::Chain(_))
			| Err(Error::Internal)
			| Err(Error::NoDandelionRelay) => None,
			Err(ref e) => {
				debug!("try_break: exit the loop: {:?}", e);
				break;
				}
			}
	};
}

/// A message as received by the connection. Provides access to the message
/// header lazily consumes the message body, handling its deserialization.
pub struct Message<'a> {
	pub header: MsgHeader,
	stream: &'a mut dyn Read,
	version: ProtocolVersion,
}

impl<'a> Message<'a> {
	fn from_header(
		header: MsgHeader,
		stream: &'a mut dyn Read,
		version: ProtocolVersion,
	) -> Message<'a> {
		Message {
			header,
			stream,
			version,
		}
	}

	/// Read the message body from the underlying connection
	pub fn body<T: ser::Readable>(&mut self) -> Result<T, Error> {
		read_body(&self.header, self.stream, self.version)
	}

	/// Read a single "thing" from the underlying connection.
	/// Return the thing and the total bytes read.
	pub fn streaming_read<T: ser::Readable>(&mut self) -> Result<(T, u64), Error> {
		read_item(self.stream, self.version)
	}

	pub fn copy_attachment(&mut self, len: usize, writer: &mut dyn Write) -> Result<usize, Error> {
		let mut written = 0;
		while written < len {
			let read_len = cmp::min(8000, len - written);
			let mut buf = vec![0u8; read_len];
			self.stream.read_exact(&mut buf[..])?;
			writer.write_all(&mut buf)?;
			written += read_len;
		}
		Ok(written)
	}
}

pub const SEND_CHANNEL_CAP: usize = 100;

pub struct StopHandle {
	/// Channel to close the connection
	stopped: Arc<AtomicBool>,
	// we need Option to take ownhership of the handle in stop()
	reader_thread: Option<JoinHandle<()>>,
	writer_thread: Option<JoinHandle<()>>,
}

impl StopHandle {
	/// Schedule this connection to safely close via the async close_channel.
	pub fn stop(&self) {
		self.stopped.store(true, Ordering::Relaxed);
	}

	pub fn wait(&mut self) {
		if let Some(reader_thread) = self.reader_thread.take() {
			self.join_thread(reader_thread);
		}
		if let Some(writer_thread) = self.writer_thread.take() {
			self.join_thread(writer_thread);
		}
	}

	fn join_thread(&self, peer_thread: JoinHandle<()>) {
		// wait only if other thread is calling us, eg shutdown
		if thread::current().id() != peer_thread.thread().id() {
			debug!("waiting for thread {:?} exit", peer_thread.thread().id());
			if let Err(e) = peer_thread.join() {
				error!("failed to stop peer thread: {:?}", e);
			}
		} else {
			debug!(
				"attempt to stop thread {:?} from itself",
				peer_thread.thread().id()
			);
		}
	}
}

#[derive(Clone)]
pub struct ConnHandle {
	/// Channel to allow sending data through the connection
	pub send_channel: mpsc::SyncSender<Msg>,
}

impl ConnHandle {
	pub fn send(&self, msg: Msg) -> Result<(), Error> {
		self.send_channel.try_send(msg)?;
		Ok(())
	}
}

pub struct Tracker {
	/// Bytes we've sent.
	pub sent_bytes: Arc<RwLock<RateCounter>>,
	/// Bytes we've received.
	pub received_bytes: Arc<RwLock<RateCounter>>,
}

impl Tracker {
	pub fn new() -> Tracker {
		let received_bytes = Arc::new(RwLock::new(RateCounter::new()));
		let sent_bytes = Arc::new(RwLock::new(RateCounter::new()));
		Tracker {
			received_bytes,
			sent_bytes,
		}
	}

	pub fn inc_received(&self, size: u64) {
		self.received_bytes.write().inc(size);
	}

	pub fn inc_sent(&self, size: u64) {
		self.sent_bytes.write().inc(size);
	}

	pub fn inc_quiet_received(&self, size: u64) {
		self.received_bytes.write().inc_quiet(size);
	}

	pub fn inc_quiet_sent(&self, size: u64) {
		self.sent_bytes.write().inc_quiet(size);
	}
}

/// Start listening on the provided connection and wraps it. Does not hang
/// the current thread, instead just returns a future and the Connection
/// itself.
pub fn listen<H>(
	stream: TcpStream,
	version: ProtocolVersion,
	tracker: Arc<Tracker>,
	handler: H,
) -> io::Result<(ConnHandle, StopHandle)>
where
	H: MessageHandler,
{
	let (send_tx, send_rx) = mpsc::sync_channel(SEND_CHANNEL_CAP);

	stream
		.set_read_timeout(Some(IO_TIMEOUT))
		.expect("can't set read timeout");
	stream
		.set_write_timeout(Some(IO_TIMEOUT))
		.expect("can't set read timeout");

	let stopped = Arc::new(AtomicBool::new(false));

	let conn_handle = ConnHandle {
		send_channel: send_tx,
	};

	let (reader_thread, writer_thread) = poll(
		stream,
		conn_handle.clone(),
		version,
		handler,
		send_rx,
		stopped.clone(),
		tracker,
	)?;

	Ok((
		conn_handle,
		StopHandle {
			stopped,
			reader_thread: Some(reader_thread),
			writer_thread: Some(writer_thread),
		},
	))
}

fn poll<H>(
	conn: TcpStream,
	conn_handle: ConnHandle,
	version: ProtocolVersion,
	handler: H,
	send_rx: mpsc::Receiver<Msg>,
	stopped: Arc<AtomicBool>,
	tracker: Arc<Tracker>,
) -> io::Result<(JoinHandle<()>, JoinHandle<()>)>
where
	H: MessageHandler,
{
	// Split out tcp stream out into separate reader/writer halves.
	let mut reader = conn.try_clone().expect("clone conn for reader failed");
	let mut writer = conn.try_clone().expect("clone conn for writer failed");
	let reader_stopped = stopped.clone();

	let reader_tracker = tracker.clone();
	let writer_tracker = tracker.clone();

	let reader_thread = thread::Builder::new()
		.name("peer_read".to_string())
		.spawn(move || {
			loop {
				// check the read end
				match try_break!(read_header(&mut reader, version)) {
					Some(MsgHeaderWrapper::Known(header)) => {
						let msg = Message::from_header(header, &mut reader, version);

						trace!(
							"Received message header, type {:?}, len {}.",
							msg.header.msg_type,
							msg.header.msg_len
						);

						// Increase received bytes counter
						reader_tracker.inc_received(MsgHeader::LEN as u64 + msg.header.msg_len);

						let resp_msg = try_break!(handler.consume(msg, reader_tracker.clone()));
						if let Some(Some(resp_msg)) = resp_msg {
							try_break!(conn_handle.send(resp_msg));
						}
					}
					Some(MsgHeaderWrapper::Unknown(msg_len, type_byte)) => {
						debug!(
							"Received unknown message header, type {:?}, len {}.",
							type_byte, msg_len
						);
						// Increase received bytes counter
						reader_tracker.inc_received(MsgHeader::LEN as u64 + msg_len);

						try_break!(read_discard(msg_len, &mut reader));
					}
					None => {}
				}

				// check the close channel
				if reader_stopped.load(Ordering::Relaxed) {
					break;
				}
			}

			debug!(
				"Shutting down reader connection with {}",
				reader
					.peer_addr()
					.map(|a| a.to_string())
					.unwrap_or("?".to_owned())
			);
			let _ = reader.shutdown(Shutdown::Both);
		})?;

	let writer_thread = thread::Builder::new()
		.name("peer_write".to_string())
		.spawn(move || {
			let mut retry_send = Err(());
			loop {
				let maybe_data = retry_send.or_else(|_| send_rx.recv_timeout(IO_TIMEOUT));
				retry_send = Err(());
				if let Ok(data) = maybe_data {
					let written =
						try_break!(write_message(&mut writer, &data, writer_tracker.clone()));
					if written.is_none() {
						retry_send = Ok(data);
					}
				}
				// check the close channel
				if stopped.load(Ordering::Relaxed) {
					break;
				}
			}

			debug!(
				"Shutting down writer connection with {}",
				writer
					.peer_addr()
					.map(|a| a.to_string())
					.unwrap_or("?".to_owned())
			);
		})?;
	Ok((reader_thread, writer_thread))
}
<commit_msg>Set longer timeout for msg body read/write operations (#3145)<commit_after>// Copyright 2019 The Grin Developers
//
// 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.

//! Provides a connection wrapper that handles the lower level tasks in sending
//! or receiving data from the TCP socket, as well as dealing with timeouts.
//!
//! Because of a few idiosyncracies in the Rust `TcpStream`, this has to use
//! async I/O to be able to both read *and* write on the connection. Which
//! forces us to go through some additional gymnastic to loop over the async
//! stream and make sure we get the right number of bytes out.

use crate::core::ser;
use crate::core::ser::{FixedLength, ProtocolVersion};
use crate::msg::{
	read_body, read_discard, read_header, read_item, write_message, Msg, MsgHeader,
	MsgHeaderWrapper,
};
use crate::types::Error;
use crate::util::{RateCounter, RwLock};
use std::io::{self, Read, Write};
use std::net::{Shutdown, TcpStream};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{mpsc, Arc};
use std::time::Duration;
use std::{
	cmp,
	thread::{self, JoinHandle},
};

const HEADER_IO_TIMEOUT: Duration = Duration::from_millis(2000);
const CHANNEL_TIMEOUT: Duration = Duration::from_millis(1000);
const BODY_IO_TIMEOUT: Duration = Duration::from_millis(60000);

/// A trait to be implemented in order to receive messages from the
/// connection. Allows providing an optional response.
pub trait MessageHandler: Send + 'static {
	fn consume<'a>(&self, msg: Message<'a>, tracker: Arc<Tracker>) -> Result<Option<Msg>, Error>;
}

// Macro to simplify the boilerplate around I/O and Grin error handling
macro_rules! try_break {
	($inner:expr) => {
		match $inner {
			Ok(v) => Some(v),
			Err(Error::Connection(ref e)) if e.kind() == io::ErrorKind::TimedOut => None,
			Err(Error::Connection(ref e)) if e.kind() == io::ErrorKind::WouldBlock => {
				// to avoid the heavy polling which will consume CPU 100%
				thread::sleep(Duration::from_millis(10));
				None
				}
			Err(Error::Store(_))
			| Err(Error::Chain(_))
			| Err(Error::Internal)
			| Err(Error::NoDandelionRelay) => None,
			Err(ref e) => {
				debug!("try_break: exit the loop: {:?}", e);
				break;
				}
			}
	};
}

macro_rules! try_header {
	($res:expr, $conn: expr) => {{
		$conn
			.set_read_timeout(Some(HEADER_IO_TIMEOUT))
			.expect("set timeout");
		try_break!($res)
		}};
}

/// A message as received by the connection. Provides access to the message
/// header lazily consumes the message body, handling its deserialization.
pub struct Message<'a> {
	pub header: MsgHeader,
	stream: &'a mut dyn Read,
	version: ProtocolVersion,
}

impl<'a> Message<'a> {
	fn from_header(
		header: MsgHeader,
		stream: &'a mut dyn Read,
		version: ProtocolVersion,
	) -> Message<'a> {
		Message {
			header,
			stream,
			version,
		}
	}

	/// Read the message body from the underlying connection
	pub fn body<T: ser::Readable>(&mut self) -> Result<T, Error> {
		read_body(&self.header, self.stream, self.version)
	}

	/// Read a single "thing" from the underlying connection.
	/// Return the thing and the total bytes read.
	pub fn streaming_read<T: ser::Readable>(&mut self) -> Result<(T, u64), Error> {
		read_item(self.stream, self.version)
	}

	pub fn copy_attachment(&mut self, len: usize, writer: &mut dyn Write) -> Result<usize, Error> {
		let mut written = 0;
		while written < len {
			let read_len = cmp::min(8000, len - written);
			let mut buf = vec![0u8; read_len];
			self.stream.read_exact(&mut buf[..])?;
			writer.write_all(&mut buf)?;
			written += read_len;
		}
		Ok(written)
	}
}

pub const SEND_CHANNEL_CAP: usize = 100;

pub struct StopHandle {
	/// Channel to close the connection
	stopped: Arc<AtomicBool>,
	// we need Option to take ownhership of the handle in stop()
	reader_thread: Option<JoinHandle<()>>,
	writer_thread: Option<JoinHandle<()>>,
}

impl StopHandle {
	/// Schedule this connection to safely close via the async close_channel.
	pub fn stop(&self) {
		self.stopped.store(true, Ordering::Relaxed);
	}

	pub fn wait(&mut self) {
		if let Some(reader_thread) = self.reader_thread.take() {
			self.join_thread(reader_thread);
		}
		if let Some(writer_thread) = self.writer_thread.take() {
			self.join_thread(writer_thread);
		}
	}

	fn join_thread(&self, peer_thread: JoinHandle<()>) {
		// wait only if other thread is calling us, eg shutdown
		if thread::current().id() != peer_thread.thread().id() {
			debug!("waiting for thread {:?} exit", peer_thread.thread().id());
			if let Err(e) = peer_thread.join() {
				error!("failed to stop peer thread: {:?}", e);
			}
		} else {
			debug!(
				"attempt to stop thread {:?} from itself",
				peer_thread.thread().id()
			);
		}
	}
}

#[derive(Clone)]
pub struct ConnHandle {
	/// Channel to allow sending data through the connection
	pub send_channel: mpsc::SyncSender<Msg>,
}

impl ConnHandle {
	pub fn send(&self, msg: Msg) -> Result<(), Error> {
		self.send_channel.try_send(msg)?;
		Ok(())
	}
}

pub struct Tracker {
	/// Bytes we've sent.
	pub sent_bytes: Arc<RwLock<RateCounter>>,
	/// Bytes we've received.
	pub received_bytes: Arc<RwLock<RateCounter>>,
}

impl Tracker {
	pub fn new() -> Tracker {
		let received_bytes = Arc::new(RwLock::new(RateCounter::new()));
		let sent_bytes = Arc::new(RwLock::new(RateCounter::new()));
		Tracker {
			received_bytes,
			sent_bytes,
		}
	}

	pub fn inc_received(&self, size: u64) {
		self.received_bytes.write().inc(size);
	}

	pub fn inc_sent(&self, size: u64) {
		self.sent_bytes.write().inc(size);
	}

	pub fn inc_quiet_received(&self, size: u64) {
		self.received_bytes.write().inc_quiet(size);
	}

	pub fn inc_quiet_sent(&self, size: u64) {
		self.sent_bytes.write().inc_quiet(size);
	}
}

/// Start listening on the provided connection and wraps it. Does not hang
/// the current thread, instead just returns a future and the Connection
/// itself.
pub fn listen<H>(
	stream: TcpStream,
	version: ProtocolVersion,
	tracker: Arc<Tracker>,
	handler: H,
) -> io::Result<(ConnHandle, StopHandle)>
where
	H: MessageHandler,
{
	let (send_tx, send_rx) = mpsc::sync_channel(SEND_CHANNEL_CAP);

	let stopped = Arc::new(AtomicBool::new(false));

	let conn_handle = ConnHandle {
		send_channel: send_tx,
	};

	let (reader_thread, writer_thread) = poll(
		stream,
		conn_handle.clone(),
		version,
		handler,
		send_rx,
		stopped.clone(),
		tracker,
	)?;

	Ok((
		conn_handle,
		StopHandle {
			stopped,
			reader_thread: Some(reader_thread),
			writer_thread: Some(writer_thread),
		},
	))
}

fn poll<H>(
	conn: TcpStream,
	conn_handle: ConnHandle,
	version: ProtocolVersion,
	handler: H,
	send_rx: mpsc::Receiver<Msg>,
	stopped: Arc<AtomicBool>,
	tracker: Arc<Tracker>,
) -> io::Result<(JoinHandle<()>, JoinHandle<()>)>
where
	H: MessageHandler,
{
	// Split out tcp stream out into separate reader/writer halves.
	let mut reader = conn.try_clone().expect("clone conn for reader failed");
	let mut writer = conn.try_clone().expect("clone conn for writer failed");
	let reader_stopped = stopped.clone();

	let reader_tracker = tracker.clone();
	let writer_tracker = tracker.clone();

	let reader_thread = thread::Builder::new()
		.name("peer_read".to_string())
		.spawn(move || {
			loop {
				// check the read end
				match try_header!(read_header(&mut reader, version), &mut reader) {
					Some(MsgHeaderWrapper::Known(header)) => {
						reader
							.set_read_timeout(Some(BODY_IO_TIMEOUT))
							.expect("set timeout");
						let msg = Message::from_header(header, &mut reader, version);

						trace!(
							"Received message header, type {:?}, len {}.",
							msg.header.msg_type,
							msg.header.msg_len
						);

						// Increase received bytes counter
						reader_tracker.inc_received(MsgHeader::LEN as u64 + msg.header.msg_len);

						let resp_msg = try_break!(handler.consume(msg, reader_tracker.clone()));
						if let Some(Some(resp_msg)) = resp_msg {
							try_break!(conn_handle.send(resp_msg));
						}
					}
					Some(MsgHeaderWrapper::Unknown(msg_len, type_byte)) => {
						debug!(
							"Received unknown message header, type {:?}, len {}.",
							type_byte, msg_len
						);
						// Increase received bytes counter
						reader_tracker.inc_received(MsgHeader::LEN as u64 + msg_len);

						try_break!(read_discard(msg_len, &mut reader));
					}
					None => {}
				}

				// check the close channel
				if reader_stopped.load(Ordering::Relaxed) {
					break;
				}
			}

			debug!(
				"Shutting down reader connection with {}",
				reader
					.peer_addr()
					.map(|a| a.to_string())
					.unwrap_or("?".to_owned())
			);
			let _ = reader.shutdown(Shutdown::Both);
		})?;

	let writer_thread = thread::Builder::new()
		.name("peer_write".to_string())
		.spawn(move || {
			let mut retry_send = Err(());
			writer
				.set_write_timeout(Some(BODY_IO_TIMEOUT))
				.expect("set timeout");
			loop {
				let maybe_data = retry_send.or_else(|_| send_rx.recv_timeout(CHANNEL_TIMEOUT));
				retry_send = Err(());
				if let Ok(data) = maybe_data {
					let written =
						try_break!(write_message(&mut writer, &data, writer_tracker.clone()));
					if written.is_none() {
						retry_send = Ok(data);
					}
				}
				// check the close channel
				if stopped.load(Ordering::Relaxed) {
					break;
				}
			}

			debug!(
				"Shutting down writer connection with {}",
				writer
					.peer_addr()
					.map(|a| a.to_string())
					.unwrap_or("?".to_owned())
			);
		})?;
	Ok((reader_thread, writer_thread))
}
<|endoftext|>


<commit_before>// Copyright 2018 The Grin Developers
//
// 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::collections::HashMap;
use std::fs::File;
use std::net::{Shutdown, SocketAddr, TcpListener, TcpStream};
use std::sync::Arc;
use std::time::Duration;
use std::{io, thread};

use crate::lmdb;

use crate::core::core;
use crate::core::core::hash::Hash;
use crate::core::global;
use crate::core::pow::Difficulty;
use crate::handshake::Handshake;
use crate::peer::Peer;
use crate::peers::Peers;
use crate::store::PeerStore;
use crate::types::{Capabilities, ChainAdapter, Error, NetAdapter, P2PConfig, TxHashSetRead, ReasonForBan};
use crate::util::{Mutex, StopState};
use chrono::prelude::{DateTime, Utc};

/// P2P server implementation, handling bootstrapping to find and connect to
/// peers, receiving connections from other peers and keep track of all of them.
pub struct Server {
	pub config: P2PConfig,
	capabilities: Capabilities,
	handshake: Arc<Handshake>,
	pub peers: Arc<Peers>,
	stop_state: Arc<Mutex<StopState>>,
}

// TODO TLS
impl Server {
	/// Creates a new idle p2p server with no peers
	pub fn new(
		db_env: Arc<lmdb::Environment>,
		capab: Capabilities,
		config: P2PConfig,
		adapter: Arc<dyn ChainAdapter>,
		genesis: Hash,
		stop_state: Arc<Mutex<StopState>>,
	) -> Result<Server, Error> {
		Ok(Server {
			config: config.clone(),
			capabilities: capab,
			handshake: Arc::new(Handshake::new(genesis, config.clone())),
			peers: Arc::new(Peers::new(PeerStore::new(db_env)?, adapter, config)),
			stop_state,
		})
	}

	/// Starts a new TCP server and listen to incoming connections. This is a
	/// blocking call until the TCP server stops.
	pub fn listen(&self) -> Result<(), Error> {
		// start TCP listener and handle incoming connections
		let addr = SocketAddr::new(self.config.host, self.config.port);
		let listener = TcpListener::bind(addr)?;
		listener.set_nonblocking(true)?;

		let mut connected_sockets: HashMap<SocketAddr, TcpStream> = HashMap::new();

		let sleep_time = Duration::from_millis(1);
		loop {
			// Pause peer ingress connection request. Only for tests.
			if self.stop_state.lock().is_paused() {
				thread::sleep(Duration::from_secs(1));
				continue;
			}

			match listener.accept() {
				Ok((stream, peer_addr)) => {
					if !self.check_banned(&stream) {
						let sc = stream.try_clone();
						if let Err(e) = self.handle_new_peer(stream) {
							warn!("Error accepting peer {}: {:?}", peer_addr.to_string(), e);
							let _ = self.peers.add_banned(peer_addr, ReasonForBan::BadHandshake);
						} else if let Ok(s) = sc {
							connected_sockets.insert(peer_addr, s);
						}
					}
					// if any active socket not in our peers list, close it
					self.clean_lost_sockets(&mut connected_sockets);
				}
				Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
					// nothing to do, will retry in next iteration
				}
				Err(e) => {
					warn!("Couldn't establish new client connection: {:?}", e);
				}
			}
			if self.stop_state.lock().is_stopped() {
				break;
			}
			thread::sleep(sleep_time);
		}
		Ok(())
	}

	/// Asks the server to connect to a new peer. Directly returns the peer if
	/// we're already connected to the provided address.
	pub fn connect(&self, addr: &SocketAddr) -> Result<Arc<Peer>, Error> {
		if Peer::is_denied(&self.config, &addr) {
			debug!("connect_peer: peer {} denied, not connecting.", addr);
			return Err(Error::ConnectionClose);
		}

		if global::is_production_mode() {
			let hs = self.handshake.clone();
			let addrs = hs.addrs.read();
			if addrs.contains(&addr) {
				debug!("connect: ignore connecting to PeerWithSelf, addr: {}", addr);
				return Err(Error::PeerWithSelf);
			}
		}

		if let Some(p) = self.peers.get_connected_peer(addr) {
			// if we're already connected to the addr, just return the peer
			trace!("connect_peer: already connected {}", addr);
			return Ok(p);
		}

		trace!(
			"connect_peer: on {}:{}. connecting to {}",
			self.config.host,
			self.config.port,
			addr
		);
		match TcpStream::connect_timeout(addr, Duration::from_secs(10)) {
			Ok(mut stream) => {
				let addr = SocketAddr::new(self.config.host, self.config.port);
				let total_diff = self.peers.total_difficulty();

				let mut peer = Peer::connect(
					&mut stream,
					self.capabilities,
					total_diff,
					addr,
					&self.handshake,
					self.peers.clone(),
				)?;
				peer.start(stream);
				let peer = Arc::new(peer);
				self.peers.add_connected(peer.clone())?;
				Ok(peer)
			}
			Err(e) => {
				trace!(
					"connect_peer: on {}:{}. Could not connect to {}: {:?}",
					self.config.host,
					self.config.port,
					addr,
					e
				);
				Err(Error::Connection(e))
			}
		}
	}

	fn handle_new_peer(&self, mut stream: TcpStream) -> Result<(), Error> {
		let total_diff = self.peers.total_difficulty();

		// accept the peer and add it to the server map
		let mut peer = Peer::accept(
			&mut stream,
			self.capabilities,
			total_diff,
			&self.handshake,
			self.peers.clone(),
		)?;
		peer.start(stream);
		self.peers.add_connected(Arc::new(peer))?;
		Ok(())
	}

	fn check_banned(&self, stream: &TcpStream) -> bool {
		// peer has been banned, go away!
		if let Ok(peer_addr) = stream.peer_addr() {
			if self.peers.is_banned(peer_addr) {
				debug!("Peer {} banned, refusing connection.", peer_addr);
				if let Err(e) = stream.shutdown(Shutdown::Both) {
					debug!("Error shutting down conn: {:?}", e);
				}
				return true;
			}
		}
		false
	}

	/// For all kinds of exception cases, the node could accepted / initiated a peer connection successfully but
	/// failed on the Handshake protocol communication, or a connected peer was closed but without a successful
	/// clean-up on its socket, that will cause this connected (on TcpStream) peer becomes so-called "invisible" peer!
	/// i.e. a peer not included in the 'self.peers.peers' hashmap. This "invisible" peer will cause some security
	/// concern because it still can send something to this node, but without enough visibility as other connected peers.
	/// Another impact is these connections could never be closed, which make the node fully occupied by all such
	/// kind of connections and become un-connectable.
	/// This function can help to clean the peer connections which is "invisible" for this node.
	fn clean_lost_sockets(&self, sockets: &mut HashMap<SocketAddr, TcpStream>) {
		let mut lost_sockets: Vec<SocketAddr> = vec![];
		for (socket, stream) in sockets.iter() {
			if !self.peers.is_known_ip(&socket) {
				if let Ok(_) = stream.shutdown(Shutdown::Both) {
					debug!(
						"clean_lost_sockets: {} cleaned which's not in peers list",
						socket
					);
				}
				lost_sockets.push(socket.clone());
			}
		}

		for socket in lost_sockets {
			sockets.remove(&socket);
		}
	}

	pub fn stop(&self) {
		self.stop_state.lock().stop();
		self.peers.stop();
	}

	/// Pause means: stop all the current peers connection, only for tests.
	/// Note:
	/// 1. must pause the 'seed' thread also, to avoid the new egress peer connection
	/// 2. must pause the 'p2p-server' thread also, to avoid the new ingress peer connection.
	pub fn pause(&self) {
		self.peers.stop();
	}
}

/// A no-op network adapter used for testing.
pub struct DummyAdapter {}

impl ChainAdapter for DummyAdapter {
	fn total_difficulty(&self) -> Difficulty {
		Difficulty::min()
	}
	fn total_height(&self) -> u64 {
		0
	}
	fn get_transaction(&self, _h: Hash) -> Option<core::Transaction> {
		None
	}
	fn tx_kernel_received(&self, _h: Hash, _addr: SocketAddr) {}
	fn transaction_received(&self, _: core::Transaction, _stem: bool) {}
	fn compact_block_received(&self, _cb: core::CompactBlock, _addr: SocketAddr) -> bool {
		true
	}
	fn header_received(&self, _bh: core::BlockHeader, _addr: SocketAddr) -> bool {
		true
	}
	fn block_received(&self, _: core::Block, _: SocketAddr) -> bool {
		true
	}
	fn headers_received(&self, _: &[core::BlockHeader], _: SocketAddr) -> bool {
		true
	}
	fn locate_headers(&self, _: &[Hash]) -> Vec<core::BlockHeader> {
		vec![]
	}
	fn get_block(&self, _: Hash) -> Option<core::Block> {
		None
	}
	fn txhashset_read(&self, _h: Hash) -> Option<TxHashSetRead> {
		unimplemented!()
	}

	fn txhashset_receive_ready(&self) -> bool {
		false
	}

	fn txhashset_write(&self, _h: Hash, _txhashset_data: File, _peer_addr: SocketAddr) -> bool {
		false
	}

	fn txhashset_download_update(
		&self,
		_start_time: DateTime<Utc>,
		_downloaded_size: u64,
		_total_size: u64,
	) -> bool {
		false
	}
}

impl NetAdapter for DummyAdapter {
	fn find_peer_addrs(&self, _: Capabilities) -> Vec<SocketAddr> {
		vec![]
	}
	fn peer_addrs_received(&self, _: Vec<SocketAddr>) {}
	fn peer_difficulty(&self, _: SocketAddr, _: Difficulty, _: u64) {}
	fn is_banned(&self, _: SocketAddr) -> bool {
		false
	}
}
<commit_msg>rustfmt<commit_after>// Copyright 2018 The Grin Developers
//
// 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::collections::HashMap;
use std::fs::File;
use std::net::{Shutdown, SocketAddr, TcpListener, TcpStream};
use std::sync::Arc;
use std::time::Duration;
use std::{io, thread};

use crate::lmdb;

use crate::core::core;
use crate::core::core::hash::Hash;
use crate::core::global;
use crate::core::pow::Difficulty;
use crate::handshake::Handshake;
use crate::peer::Peer;
use crate::peers::Peers;
use crate::store::PeerStore;
use crate::types::{
	Capabilities, ChainAdapter, Error, NetAdapter, P2PConfig, ReasonForBan, TxHashSetRead,
};
use crate::util::{Mutex, StopState};
use chrono::prelude::{DateTime, Utc};

/// P2P server implementation, handling bootstrapping to find and connect to
/// peers, receiving connections from other peers and keep track of all of them.
pub struct Server {
	pub config: P2PConfig,
	capabilities: Capabilities,
	handshake: Arc<Handshake>,
	pub peers: Arc<Peers>,
	stop_state: Arc<Mutex<StopState>>,
}

// TODO TLS
impl Server {
	/// Creates a new idle p2p server with no peers
	pub fn new(
		db_env: Arc<lmdb::Environment>,
		capab: Capabilities,
		config: P2PConfig,
		adapter: Arc<dyn ChainAdapter>,
		genesis: Hash,
		stop_state: Arc<Mutex<StopState>>,
	) -> Result<Server, Error> {
		Ok(Server {
			config: config.clone(),
			capabilities: capab,
			handshake: Arc::new(Handshake::new(genesis, config.clone())),
			peers: Arc::new(Peers::new(PeerStore::new(db_env)?, adapter, config)),
			stop_state,
		})
	}

	/// Starts a new TCP server and listen to incoming connections. This is a
	/// blocking call until the TCP server stops.
	pub fn listen(&self) -> Result<(), Error> {
		// start TCP listener and handle incoming connections
		let addr = SocketAddr::new(self.config.host, self.config.port);
		let listener = TcpListener::bind(addr)?;
		listener.set_nonblocking(true)?;

		let mut connected_sockets: HashMap<SocketAddr, TcpStream> = HashMap::new();

		let sleep_time = Duration::from_millis(1);
		loop {
			// Pause peer ingress connection request. Only for tests.
			if self.stop_state.lock().is_paused() {
				thread::sleep(Duration::from_secs(1));
				continue;
			}

			match listener.accept() {
				Ok((stream, peer_addr)) => {
					if !self.check_banned(&stream) {
						let sc = stream.try_clone();
						if let Err(e) = self.handle_new_peer(stream) {
							warn!("Error accepting peer {}: {:?}", peer_addr.to_string(), e);
							let _ = self.peers.add_banned(peer_addr, ReasonForBan::BadHandshake);
						} else if let Ok(s) = sc {
							connected_sockets.insert(peer_addr, s);
						}
					}
					// if any active socket not in our peers list, close it
					self.clean_lost_sockets(&mut connected_sockets);
				}
				Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
					// nothing to do, will retry in next iteration
				}
				Err(e) => {
					warn!("Couldn't establish new client connection: {:?}", e);
				}
			}
			if self.stop_state.lock().is_stopped() {
				break;
			}
			thread::sleep(sleep_time);
		}
		Ok(())
	}

	/// Asks the server to connect to a new peer. Directly returns the peer if
	/// we're already connected to the provided address.
	pub fn connect(&self, addr: &SocketAddr) -> Result<Arc<Peer>, Error> {
		if Peer::is_denied(&self.config, &addr) {
			debug!("connect_peer: peer {} denied, not connecting.", addr);
			return Err(Error::ConnectionClose);
		}

		if global::is_production_mode() {
			let hs = self.handshake.clone();
			let addrs = hs.addrs.read();
			if addrs.contains(&addr) {
				debug!("connect: ignore connecting to PeerWithSelf, addr: {}", addr);
				return Err(Error::PeerWithSelf);
			}
		}

		if let Some(p) = self.peers.get_connected_peer(addr) {
			// if we're already connected to the addr, just return the peer
			trace!("connect_peer: already connected {}", addr);
			return Ok(p);
		}

		trace!(
			"connect_peer: on {}:{}. connecting to {}",
			self.config.host,
			self.config.port,
			addr
		);
		match TcpStream::connect_timeout(addr, Duration::from_secs(10)) {
			Ok(mut stream) => {
				let addr = SocketAddr::new(self.config.host, self.config.port);
				let total_diff = self.peers.total_difficulty();

				let mut peer = Peer::connect(
					&mut stream,
					self.capabilities,
					total_diff,
					addr,
					&self.handshake,
					self.peers.clone(),
				)?;
				peer.start(stream);
				let peer = Arc::new(peer);
				self.peers.add_connected(peer.clone())?;
				Ok(peer)
			}
			Err(e) => {
				trace!(
					"connect_peer: on {}:{}. Could not connect to {}: {:?}",
					self.config.host,
					self.config.port,
					addr,
					e
				);
				Err(Error::Connection(e))
			}
		}
	}

	fn handle_new_peer(&self, mut stream: TcpStream) -> Result<(), Error> {
		let total_diff = self.peers.total_difficulty();

		// accept the peer and add it to the server map
		let mut peer = Peer::accept(
			&mut stream,
			self.capabilities,
			total_diff,
			&self.handshake,
			self.peers.clone(),
		)?;
		peer.start(stream);
		self.peers.add_connected(Arc::new(peer))?;
		Ok(())
	}

	fn check_banned(&self, stream: &TcpStream) -> bool {
		// peer has been banned, go away!
		if let Ok(peer_addr) = stream.peer_addr() {
			if self.peers.is_banned(peer_addr) {
				debug!("Peer {} banned, refusing connection.", peer_addr);
				if let Err(e) = stream.shutdown(Shutdown::Both) {
					debug!("Error shutting down conn: {:?}", e);
				}
				return true;
			}
		}
		false
	}

	/// For all kinds of exception cases, the node could accepted / initiated a peer connection successfully but
	/// failed on the Handshake protocol communication, or a connected peer was closed but without a successful
	/// clean-up on its socket, that will cause this connected (on TcpStream) peer becomes so-called "invisible" peer!
	/// i.e. a peer not included in the 'self.peers.peers' hashmap. This "invisible" peer will cause some security
	/// concern because it still can send something to this node, but without enough visibility as other connected peers.
	/// Another impact is these connections could never be closed, which make the node fully occupied by all such
	/// kind of connections and become un-connectable.
	/// This function can help to clean the peer connections which is "invisible" for this node.
	fn clean_lost_sockets(&self, sockets: &mut HashMap<SocketAddr, TcpStream>) {
		let mut lost_sockets: Vec<SocketAddr> = vec![];
		for (socket, stream) in sockets.iter() {
			if !self.peers.is_known_ip(&socket) {
				if let Ok(_) = stream.shutdown(Shutdown::Both) {
					debug!(
						"clean_lost_sockets: {} cleaned which's not in peers list",
						socket
					);
				}
				lost_sockets.push(socket.clone());
			}
		}

		for socket in lost_sockets {
			sockets.remove(&socket);
		}
	}

	pub fn stop(&self) {
		self.stop_state.lock().stop();
		self.peers.stop();
	}

	/// Pause means: stop all the current peers connection, only for tests.
	/// Note:
	/// 1. must pause the 'seed' thread also, to avoid the new egress peer connection
	/// 2. must pause the 'p2p-server' thread also, to avoid the new ingress peer connection.
	pub fn pause(&self) {
		self.peers.stop();
	}
}

/// A no-op network adapter used for testing.
pub struct DummyAdapter {}

impl ChainAdapter for DummyAdapter {
	fn total_difficulty(&self) -> Difficulty {
		Difficulty::min()
	}
	fn total_height(&self) -> u64 {
		0
	}
	fn get_transaction(&self, _h: Hash) -> Option<core::Transaction> {
		None
	}
	fn tx_kernel_received(&self, _h: Hash, _addr: SocketAddr) {}
	fn transaction_received(&self, _: core::Transaction, _stem: bool) {}
	fn compact_block_received(&self, _cb: core::CompactBlock, _addr: SocketAddr) -> bool {
		true
	}
	fn header_received(&self, _bh: core::BlockHeader, _addr: SocketAddr) -> bool {
		true
	}
	fn block_received(&self, _: core::Block, _: SocketAddr) -> bool {
		true
	}
	fn headers_received(&self, _: &[core::BlockHeader], _: SocketAddr) -> bool {
		true
	}
	fn locate_headers(&self, _: &[Hash]) -> Vec<core::BlockHeader> {
		vec![]
	}
	fn get_block(&self, _: Hash) -> Option<core::Block> {
		None
	}
	fn txhashset_read(&self, _h: Hash) -> Option<TxHashSetRead> {
		unimplemented!()
	}

	fn txhashset_receive_ready(&self) -> bool {
		false
	}

	fn txhashset_write(&self, _h: Hash, _txhashset_data: File, _peer_addr: SocketAddr) -> bool {
		false
	}

	fn txhashset_download_update(
		&self,
		_start_time: DateTime<Utc>,
		_downloaded_size: u64,
		_total_size: u64,
	) -> bool {
		false
	}
}

impl NetAdapter for DummyAdapter {
	fn find_peer_addrs(&self, _: Capabilities) -> Vec<SocketAddr> {
		vec![]
	}
	fn peer_addrs_received(&self, _: Vec<SocketAddr>) {}
	fn peer_difficulty(&self, _: SocketAddr, _: Difficulty, _: u64) {}
	fn is_banned(&self, _: SocketAddr) -> bool {
		false
	}
}
<|endoftext|>


<commit_before>use num::{self, Float, Num, NumCast};
use ordered_float::OrderedFloat;

use graph::{AsPosition, Attribute, Cross, Geometry, Normalize};

pub trait Unit: Copy + Num {
    fn unit_radius() -> (Self, Self);
    fn unit_width() -> (Self, Self);
}

macro_rules! unit {
    (integer => $($t:ty),*) => {$(
        impl Unit for $t {
            fn unit_radius() -> (Self, Self) {
                use num::{One, Zero};
                (Self::zero(), Self::one() + Self::one())
            }

            fn unit_width() -> (Self, Self) {
                use num::{One, Zero};
                (Self::zero(), Self::one())
            }
        }
    )*};
    (real => $($t:ty),*) => {$(
        impl Unit for $t {
            fn unit_radius() -> (Self, Self) {
                use num::One;
                (-Self::one(), Self::one())
            }

            fn unit_width() -> (Self, Self) {
                use num::One;
                let half = Self::one() / (Self::one() + Self::one());
                (-half, half)
            }
        }
    )*};
}
unit!(integer => i8, i16, i32, i64, u8, u16, u32, u64);
unit!(real => f32, f64);

impl<T> Attribute for (T, T, T)
where
    T: Default + Unit,
{
}

impl<T> Attribute for (OrderedFloat<T>, OrderedFloat<T>, OrderedFloat<T>)
where
    T: Default + Float + Unit,
{
}

impl<T> Geometry for (T, T, T)
where
    T: Default + Unit,
{
    type Vertex = Self;
    type Edge = ();
    type Face = ();
}

impl<T> Geometry for (OrderedFloat<T>, OrderedFloat<T>, OrderedFloat<T>)
where
    T: Default + Float + Unit,
{
    type Vertex = Self;
    type Edge = ();
    type Face = ();
}

impl<T> AsPosition for (T, T, T)
where
    T: Unit,
{
    type Target = Self;

    fn as_position(&self) -> &Self::Target {
        self
    }

    fn as_position_mut(&mut self) -> &mut Self::Target {
        self
    }
}

impl<T> AsPosition for (OrderedFloat<T>, OrderedFloat<T>, OrderedFloat<T>)
where
    T: Float + Unit,
{
    type Target = Self;

    fn as_position(&self) -> &Self::Target {
        self
    }

    fn as_position_mut(&mut self) -> &mut Self::Target {
        self
    }
}

impl<T> Cross for (T, T, T)
where
    T: Float + Unit,
{
    type Output = Self;

    fn cross(self, other: Self) -> Self::Output {
        (
            (self.1 * other.2) - (self.2 * other.1),
            (self.2 * other.0) - (self.0 * other.2),
            (self.0 * other.1) - (self.1 * other.0),
        )
    }
}

impl<T> Cross for (OrderedFloat<T>, OrderedFloat<T>, OrderedFloat<T>)
where
    T: Float + Unit,
{
    type Output = Self;

    fn cross(self, other: Self) -> Self::Output {
        (
            OrderedFloat((*self.1 * *other.2) - (*self.2 * *other.1)),
            OrderedFloat((*self.2 * *other.0) - (*self.0 * *other.2)),
            OrderedFloat((*self.0 * *other.1) - (*self.1 * *other.0)),
        )
    }
}

impl<T> Normalize for (T, T, T)
where
    T: Float + Unit,
{
    fn normalize(self) -> Self {
        let m = ((self.0 * self.0) + (self.1 * self.1) + (self.2 * self.2)).sqrt();
        (self.0 / m, self.1 / m, self.0 / m)
    }
}

impl<T> Normalize for (OrderedFloat<T>, OrderedFloat<T>, OrderedFloat<T>)
where
    T: Float + Unit,
{
    fn normalize(self) -> Self {
        let m = ((*self.0 * *self.0) + (*self.1 * *self.1) + (*self.2 * *self.2)).sqrt();
        (
            OrderedFloat(*self.0 / m),
            OrderedFloat(*self.1 / m),
            OrderedFloat(*self.0 / m),
        )
    }
}

pub trait Interpolate<T = Self>: Sized {
    type Output;

    fn lerp(self, other: T, f: f64) -> Self::Output;

    fn midpoint(self, other: T) -> Self::Output {
        self.lerp(other, 0.5)
    }
}

impl<T> Interpolate for (T, T)
where
    T: Copy + Num + NumCast,
{
    type Output = Self;

    fn lerp(self, other: Self, f: f64) -> Self::Output {
        (lerp(self.0, other.0, f), lerp(self.1, other.1, f))
    }
}

impl<T> Interpolate for (T, T, T)
where
    T: Copy + Num + NumCast,
{
    type Output = Self;

    fn lerp(self, other: Self, f: f64) -> Self::Output {
        (
            lerp(self.0, other.0, f),
            lerp(self.1, other.1, f),
            lerp(self.2, other.2, f),
        )
    }
}

fn lerp<T>(a: T, b: T, f: f64) -> T
where
    T: Num + NumCast,
{
    let f = num::clamp(f, 0.0, 1.0);
    let af = <f64 as NumCast>::from(a).unwrap() * (1.0 - f);
    let bf = <f64 as NumCast>::from(b).unwrap() * f;
    <T as NumCast>::from(af + bf).unwrap()
}
<commit_msg>Remove geometric trait implementations for 3-tuples.<commit_after>use num::{self, Float, Num, NumCast};
use ordered_float::OrderedFloat;

use graph::{Attribute, Geometry};

pub trait Unit: Copy + Num {
    fn unit_radius() -> (Self, Self);
    fn unit_width() -> (Self, Self);
}

macro_rules! unit {
    (integer => $($t:ty),*) => {$(
        impl Unit for $t {
            fn unit_radius() -> (Self, Self) {
                use num::{One, Zero};
                (Self::zero(), Self::one() + Self::one())
            }

            fn unit_width() -> (Self, Self) {
                use num::{One, Zero};
                (Self::zero(), Self::one())
            }
        }
    )*};
    (real => $($t:ty),*) => {$(
        impl Unit for $t {
            fn unit_radius() -> (Self, Self) {
                use num::One;
                (-Self::one(), Self::one())
            }

            fn unit_width() -> (Self, Self) {
                use num::One;
                let half = Self::one() / (Self::one() + Self::one());
                (-half, half)
            }
        }
    )*};
}
unit!(integer => i8, i16, i32, i64, u8, u16, u32, u64);
unit!(real => f32, f64);

impl<T> Attribute for (T, T, T)
where
    T: Default + Unit,
{
}

impl<T> Attribute for (OrderedFloat<T>, OrderedFloat<T>, OrderedFloat<T>)
where
    T: Default + Float + Unit,
{
}

impl<T> Geometry for (T, T, T)
where
    T: Default + Unit,
{
    type Vertex = Self;
    type Edge = ();
    type Face = ();
}

impl<T> Geometry for (OrderedFloat<T>, OrderedFloat<T>, OrderedFloat<T>)
where
    T: Default + Float + Unit,
{
    type Vertex = Self;
    type Edge = ();
    type Face = ();
}

pub trait Interpolate<T = Self>: Sized {
    type Output;

    fn lerp(self, other: T, f: f64) -> Self::Output;

    fn midpoint(self, other: T) -> Self::Output {
        self.lerp(other, 0.5)
    }
}

impl<T> Interpolate for (T, T)
where
    T: Copy + Num + NumCast,
{
    type Output = Self;

    fn lerp(self, other: Self, f: f64) -> Self::Output {
        (lerp(self.0, other.0, f), lerp(self.1, other.1, f))
    }
}

impl<T> Interpolate for (T, T, T)
where
    T: Copy + Num + NumCast,
{
    type Output = Self;

    fn lerp(self, other: Self, f: f64) -> Self::Output {
        (
            lerp(self.0, other.0, f),
            lerp(self.1, other.1, f),
            lerp(self.2, other.2, f),
        )
    }
}

fn lerp<T>(a: T, b: T, f: f64) -> T
where
    T: Num + NumCast,
{
    let f = num::clamp(f, 0.0, 1.0);
    let af = <f64 as NumCast>::from(a).unwrap() * (1.0 - f);
    let bf = <f64 as NumCast>::from(b).unwrap() * f;
    <T as NumCast>::from(af + bf).unwrap()
}
<|endoftext|>


<commit_before>use luminance::Mode;
use luminance_gl::gl33::Tessellation;

// A unit cube.
//
//     7-----5
//    /|    /|
//   3-+---1 |
//   | 6---+-4
//   |/    |/
//   2-----0
pub fn new_cube() -> Tessellation {
  let vertices = [
    [ 1., -1.,  1.],
    [ 1.,  1.,  1.],
    [-1., -1.,  1.],
    [-1.,  1.,  1.],
    [ 1., -1., -1.],
    [ 1.,  1., -1.],
    [-1., -1., -1.],
    [-1.,  1., -1.],
  ];

  let indices = [
    0, 1, 2, 2, 1, 3, // front face
    1, 5, 3, 3, 5, 7, // top face
    2, 3, 6, 6, 3, 7, // right face
    4, 5, 0, 0, 5, 1, // left face
    4, 0, 6, 6, 0, 2, // bottom face
    4, 5, 6, 6, 5, 7, // back face
  ];

  Tessellation::new(Mode::Triangle, &vertices, Some(&indices))
}

<commit_msg>White.<commit_after>use luminance::Mode;
use luminance_gl::gl33::Tessellation;

// A unit cube.
//
//     7-----5
//    /|    /|
//   3-+---1 |
//   | 6---+-4
//   |/    |/
//   2-----0
pub fn new_cube() -> Tessellation {
  let vertices = [
    [ 1., -1.,  1.],
    [ 1.,  1.,  1.],
    [-1., -1.,  1.],
    [-1.,  1.,  1.],
    [ 1., -1., -1.],
    [ 1.,  1., -1.],
    [-1., -1., -1.],
    [-1.,  1., -1.],
  ];

  let indices = [
    0, 1, 2, 2, 1, 3, // front face
    1, 5, 3, 3, 5, 7, // top face
    2, 3, 6, 6, 3, 7, // right face
    4, 5, 0, 0, 5, 1, // left face
    4, 0, 6, 6, 0, 2, // bottom face
    4, 5, 6, 6, 5, 7, // back face
  ];

  Tessellation::new(Mode::Triangle, &vertices, Some(&indices))
}
<|endoftext|>


<commit_before>use std::rc::Rc;
use game_map::{RoomId, GameMap};
use combat::CombatState;
use platform;

const PAUSE_MS: u64 = 2500;
const CHEATING_FOOD_DIVISOR: i32 = 4;
const INITIAL_STRENGTH: i32 = 100;
const INITIAL_WEALTH: i32 = 75;
const STRENGTH_PER_FOOD: i32 = 5;
const TALLY_PER_MOVE: i32 = 1;
const STRENGTH_LOSS_PER_MOVE: i32 = 5;

#[derive(Debug, PartialEq, Copy, Clone)]
pub enum GameMode {
  AskName,
  Primary,
  Inventory,
  EatFood,
  Combat(CombatState),
  Finished,

  #[cfg(debug_assertions)]
  Debug,
}

// We have our input callbacks have a GameState explicitly passed
// into them instead of expecting 'self' to be captured into their
// closure, because the latter leads to all kinds of lifetime
// headaches.
type InputCallback = Fn(&mut GameState, String);

pub struct GameState {
  pub map: GameMap,
  pub curr_mode: GameMode,
  pub shown_hint: bool,
  pub player_name: String,
  pub strength: i32,
  pub wealth: i32,
  pub food: i32,
  pub tally: i32,
  pub monsters_killed: i32,
  pub sword: bool,
  pub amulet: bool,
  pub axe: bool,
  pub suit: bool,
  pub light: bool,
  pub curr_room: RoomId,
  pub show_desc: bool,
  input_callback: Option<Rc<InputCallback>>,
}

impl GameState {
  pub fn new() -> Self {
    Self {
      map: GameMap::new(),
      player_name: String::from(""),
      curr_mode: GameMode::AskName,
      curr_room: RoomId::Entrance,
      shown_hint: false,
      strength: INITIAL_STRENGTH,
      wealth: INITIAL_WEALTH,
      food: 0,
      tally: 0,
      monsters_killed: 0,
      sword: false,
      amulet: false,
      axe: false,
      suit: false,
      light: false,
      show_desc: true,
      input_callback: None,
    }
  }

  pub fn read_input<F>(&mut self, cb: F)
      where F: 'static + Fn(&mut GameState, String) {
    assert!(self.input_callback.is_none(),
            "Program must not already be waiting for input");
    self.input_callback = Some(Rc::new(cb));
  }

  pub fn can_player_see(&self) -> bool {
    self.curr_room == RoomId::Entrance || self.light
  }

  pub fn set_mode(&mut self, mode: GameMode) {
    self.show_desc = true;
    self.curr_mode = mode;
  }

  pub fn print_wealth(&self) {
    print!("You have ");
    if self.wealth > 0 {
      println!("${}.", self.wealth);
    } else {
      println!("no money.");
    }
  }

  pub fn print_food(&self) {
    println!("Your provisions sack holds {} unit{} of food.",
             self.food, if self.food == 1 { "" } else { "s" });
  }

  fn get_score(&self) -> i32 {
    3  * self.tally +
    5  * self.strength +
    2  * self.wealth +
    1  * self.food +
    30 * self.monsters_killed
  }

  pub fn finish_game(&mut self) {
    self.curr_mode = GameMode::Finished;
    println!("Your score is {}.\n", self.get_score());
    println!("Farewell.");
  }

  pub fn is_finished(&self) -> bool {
    self.curr_mode == GameMode::Finished
  }

  pub fn accuse_player_of_cheating(&mut self) {
    println!("YOU HAVE TRIED TO CHEAT ME!");
    self.wealth = 0;
    self.light = false;
    self.axe = false;
    self.sword = false;
    self.food = self.food / CHEATING_FOOD_DIVISOR;
    self.amulet = false;
    self.suit = false;
    Self::pause();
  }

  pub fn pause() {
    platform::sleep(PAUSE_MS);
  }

  fn die(&mut self) {
    println!("You have died.........");
    Self::pause();
    self.finish_game();
  }

  fn tick_ask_name_mode(&mut self) {
    platform::show_prompt("What is your name, explorer? ");

    self.read_input(|state, input| {
      if input.len() == 0 {
        println!("Pardon me?");
      } else {
        state.player_name = input;
        state.set_mode(GameMode::Primary);
      }
    });
  }

  fn tick_eat_food_mode(&mut self) {
    if self.show_desc {
      self.print_food();
      println!("");
      self.show_desc = false;
    }

    platform::show_prompt("How many do you want to eat? ");

    self.read_input(|state, input| {
      match input.parse::<i32>() {
        Ok(amount) => {
          if amount < 0 {
            println!("GIVE ME A POSITIVE INTEGER.");
          } else if amount == 0 {
            println!("Fine, be that way.");
            Self::pause();
            state.set_mode(GameMode::Primary);
          } else if amount > state.food {
            state.accuse_player_of_cheating();
            state.set_mode(GameMode::Primary);
          } else {
            println!("After some munching, you feel stronger.");
            state.food -= amount;
            state.strength += amount * STRENGTH_PER_FOOD;
            state.set_mode(GameMode::Primary);
            Self::pause();
          }
        },
        Err(_) => {
          println!("That does not even look like a number, {}.",
                   state.player_name);
        }
      }
    });
  }

  pub fn process_move(&mut self) {
    self.tally += TALLY_PER_MOVE;
    self.strength -= STRENGTH_LOSS_PER_MOVE;
  }

  pub fn tick(&mut self) {
    let mut input_cb: Option<Rc<InputCallback>> = None;

    if let Some(ref cb) = self.input_callback {
      input_cb = Some(cb.clone());
    }

    if let Some(ref cb) = input_cb {
      match platform::read_input() {
        Some(input) => {
          platform::hide_prompt();
          self.input_callback = None;
          cb(self, input);
        },
        None => {
          // We're probably running in the browser and there's currently
          // no input to process, so just return.
          return;
        }
      }
    }

    if self.strength < 1 { self.die() }

    match self.curr_mode {
      GameMode::AskName => { self.tick_ask_name_mode() },
      GameMode::Primary => { self.tick_primary_mode() },
      GameMode::Inventory => { self.tick_inventory_mode() },
      GameMode::EatFood => { self.tick_eat_food_mode() },
      GameMode::Combat(state) => { self.tick_combat_mode(&state) },
      GameMode::Finished => {},

      #[cfg(debug_assertions)]
      GameMode::Debug => { self.tick_debug_mode() },
    }
  }
}
<commit_msg>allow read_input() callbacks to ask for mroe input.<commit_after>use std::rc::Rc;
use game_map::{RoomId, GameMap};
use combat::CombatState;
use platform;

const PAUSE_MS: u64 = 2500;
const CHEATING_FOOD_DIVISOR: i32 = 4;
const INITIAL_STRENGTH: i32 = 100;
const INITIAL_WEALTH: i32 = 75;
const STRENGTH_PER_FOOD: i32 = 5;
const TALLY_PER_MOVE: i32 = 1;
const STRENGTH_LOSS_PER_MOVE: i32 = 5;

#[derive(Debug, PartialEq, Copy, Clone)]
pub enum GameMode {
  AskName,
  Primary,
  Inventory,
  EatFood,
  Combat(CombatState),
  Finished,

  #[cfg(debug_assertions)]
  Debug,
}

// We have our input callbacks have a GameState explicitly passed
// into them instead of expecting 'self' to be captured into their
// closure, because the latter leads to all kinds of lifetime
// headaches.
type InputCallback = Fn(&mut GameState, String);

pub struct GameState {
  pub map: GameMap,
  pub curr_mode: GameMode,
  pub shown_hint: bool,
  pub player_name: String,
  pub strength: i32,
  pub wealth: i32,
  pub food: i32,
  pub tally: i32,
  pub monsters_killed: i32,
  pub sword: bool,
  pub amulet: bool,
  pub axe: bool,
  pub suit: bool,
  pub light: bool,
  pub curr_room: RoomId,
  pub show_desc: bool,
  input_callback: Option<Rc<InputCallback>>,
}

impl GameState {
  pub fn new() -> Self {
    Self {
      map: GameMap::new(),
      player_name: String::from(""),
      curr_mode: GameMode::AskName,
      curr_room: RoomId::Entrance,
      shown_hint: false,
      strength: INITIAL_STRENGTH,
      wealth: INITIAL_WEALTH,
      food: 0,
      tally: 0,
      monsters_killed: 0,
      sword: false,
      amulet: false,
      axe: false,
      suit: false,
      light: false,
      show_desc: true,
      input_callback: None,
    }
  }

  pub fn read_input<F>(&mut self, cb: F)
      where F: 'static + Fn(&mut GameState, String) {
    assert!(self.input_callback.is_none(),
            "Program must not already be waiting for input");
    self.input_callback = Some(Rc::new(cb));
  }

  pub fn can_player_see(&self) -> bool {
    self.curr_room == RoomId::Entrance || self.light
  }

  pub fn set_mode(&mut self, mode: GameMode) {
    self.show_desc = true;
    self.curr_mode = mode;
  }

  pub fn print_wealth(&self) {
    print!("You have ");
    if self.wealth > 0 {
      println!("${}.", self.wealth);
    } else {
      println!("no money.");
    }
  }

  pub fn print_food(&self) {
    println!("Your provisions sack holds {} unit{} of food.",
             self.food, if self.food == 1 { "" } else { "s" });
  }

  fn get_score(&self) -> i32 {
    3  * self.tally +
    5  * self.strength +
    2  * self.wealth +
    1  * self.food +
    30 * self.monsters_killed
  }

  pub fn finish_game(&mut self) {
    self.curr_mode = GameMode::Finished;
    println!("Your score is {}.\n", self.get_score());
    println!("Farewell.");
  }

  pub fn is_finished(&self) -> bool {
    self.curr_mode == GameMode::Finished
  }

  pub fn accuse_player_of_cheating(&mut self) {
    println!("YOU HAVE TRIED TO CHEAT ME!");
    self.wealth = 0;
    self.light = false;
    self.axe = false;
    self.sword = false;
    self.food = self.food / CHEATING_FOOD_DIVISOR;
    self.amulet = false;
    self.suit = false;
    Self::pause();
  }

  pub fn pause() {
    platform::sleep(PAUSE_MS);
  }

  fn die(&mut self) {
    println!("You have died.........");
    Self::pause();
    self.finish_game();
  }

  fn tick_ask_name_mode(&mut self) {
    platform::show_prompt("What is your name, explorer? ");

    self.read_input(|state, input| {
      if input.len() == 0 {
        println!("Pardon me?");
      } else {
        state.player_name = input;
        state.set_mode(GameMode::Primary);
      }
    });
  }

  fn tick_eat_food_mode(&mut self) {
    if self.show_desc {
      self.print_food();
      println!("");
      self.show_desc = false;
    }

    platform::show_prompt("How many do you want to eat? ");

    self.read_input(|state, input| {
      match input.parse::<i32>() {
        Ok(amount) => {
          if amount < 0 {
            println!("GIVE ME A POSITIVE INTEGER.");
          } else if amount == 0 {
            println!("Fine, be that way.");
            Self::pause();
            state.set_mode(GameMode::Primary);
          } else if amount > state.food {
            state.accuse_player_of_cheating();
            state.set_mode(GameMode::Primary);
          } else {
            println!("After some munching, you feel stronger.");
            state.food -= amount;
            state.strength += amount * STRENGTH_PER_FOOD;
            state.set_mode(GameMode::Primary);
            Self::pause();
          }
        },
        Err(_) => {
          println!("That does not even look like a number, {}.",
                   state.player_name);
        }
      }
    });
  }

  pub fn process_move(&mut self) {
    self.tally += TALLY_PER_MOVE;
    self.strength -= STRENGTH_LOSS_PER_MOVE;
  }

  pub fn tick(&mut self) {
    let mut input_cb: Option<Rc<InputCallback>> = None;

    if let Some(ref cb) = self.input_callback {
      input_cb = Some(cb.clone());
    }

    if let Some(ref cb) = input_cb {
      match platform::read_input() {
        Some(input) => {
          platform::hide_prompt();
          self.input_callback = None;
          cb(self, input);
          // Note that at this point, self.input_callback may be
          // set again, if the callback asked for input again.
        },
        None => {
          // We're probably running in the browser and there's currently
          // no input to process.
        }
      }
      return;
    }

    if self.strength < 1 { self.die() }

    match self.curr_mode {
      GameMode::AskName => { self.tick_ask_name_mode() },
      GameMode::Primary => { self.tick_primary_mode() },
      GameMode::Inventory => { self.tick_inventory_mode() },
      GameMode::EatFood => { self.tick_eat_food_mode() },
      GameMode::Combat(state) => { self.tick_combat_mode(&state) },
      GameMode::Finished => {},

      #[cfg(debug_assertions)]
      GameMode::Debug => { self.tick_debug_mode() },
    }
  }
}
<|endoftext|>