<commit_before>// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! # Node Builder
//!
//! ```
//! use commands::parser::builder::*;
//!
//! let mut tree = CommandTree::new();
//! tree.command(Command::new("again")
//!                  .hidden(false)
//!                  .parameter(Parameter::new("test")
//!                                 .required(false)
//!                                 .help("This is just a test parameter.")
//!                                 .finalize())
//!                  .finalize());
//! ```

use std::rc::Rc;
use super::nodes::*;

/// Indicate the type of parameter, so that the correct class and node
/// structures are created.
#[derive(Clone, Copy, PartialEq)]
pub enum ParameterKind {
    /// This parameter is a `FlagParameter`.
    Flag,
    /// This parameter is a `NamedParameter`.
    Named,
    /// This parameter is a `SimpleParameter`.
    Simple,
}

/// Store a command tree while populating it. This can be used
/// to construct a `RootNode` to be used with the `Parser`.
pub struct CommandTree {
    commands: Vec<Command>,
}

impl CommandTree {
    /// Create a new `CommandTree`.
    pub fn new() -> Self {
        CommandTree { commands: vec![] }
    }

    /// Add a `Command` to the `CommandTree`.
    pub fn command(&mut self, command: Command) {
        self.commands.push(command);
    }

    /// Construct the `CommandTree` and produce a `RootNode`.
    pub fn finalize(&self) -> Rc<RootNode> {
        let mut successors: Vec<Rc<Node>> = vec![];
        for c in &self.commands {
            successors.push(self.build_command(c));
        }
        RootNode::new(successors)
    }

    fn build_command(&self, command: &Command) -> Rc<Node> {
        let mut parameters: Vec<Rc<ParameterNode>> = vec![];
        let mut successors: Vec<Rc<Node>> = vec![];
        for parameter in &command.parameters {
            match parameter.parameter_kind {
                ParameterKind::Flag => {
                    self.build_flag_parameter(parameter, &mut parameters, &mut successors);
                }
                ParameterKind::Named => {
                    self.build_named_parameter(parameter, &mut parameters, &mut successors);
                }
                ParameterKind::Simple => {
                    self.build_simple_parameter(parameter, &mut parameters, &mut successors);
                }
            };
        }
        let c = CommandNode::new(&*command.name,
                                 command.help_text.clone(),
                                 command.hidden,
                                 command.priority,
                                 successors,
                                 None,
                                 parameters);
        Rc::new(c)
    }

    fn build_flag_parameter(&self,
                            parameter: &Parameter,
                            parameters: &mut Vec<Rc<ParameterNode>>,
                            successors: &mut Vec<Rc<Node>>) {
        let p = FlagParameterNode::new(&*parameter.name,
                                       parameter.help_text.clone(),
                                       parameter.hidden,
                                       parameter.priority.unwrap_or(PRIORITY_DEFAULT),
                                       vec![],
                                       parameter.repeatable,
                                       None,
                                       parameter.required);
        let fp = Rc::new(p);
        parameters.push(fp.clone());
        successors.push(fp);
    }

    fn build_named_parameter(&self,
                             parameter: &Parameter,
                             parameters: &mut Vec<Rc<ParameterNode>>,
                             successors: &mut Vec<Rc<Node>>) {
        let p = Rc::new(NamedParameterNode::new(&*parameter.name,
                                                parameter.help_text.clone(),
                                                parameter.hidden,
                                                parameter.priority.unwrap_or(PRIORITY_PARAMETER),
                                                vec![],
                                                parameter.repeatable,
                                                None,
                                                parameter.required));
        parameters.push(p.clone());
        let n = Rc::new(ParameterNameNode::new(&*parameter.name,
                                               parameter.hidden,
                                               PRIORITY_DEFAULT,
                                               vec![p.clone()],
                                               parameter.repeatable,
                                               Some(p.clone()),
                                               p.clone()));
        successors.push(n);
        for alias in &parameter.aliases {
            let a = Rc::new(ParameterNameNode::new(&*alias,
                                                   parameter.hidden,
                                                   PRIORITY_DEFAULT,
                                                   vec![p.clone()],
                                                   parameter.repeatable,
                                                   Some(p.clone()),
                                                   p.clone()));
            successors.push(a);
        }
    }

    fn build_simple_parameter(&self,
                              parameter: &Parameter,
                              parameters: &mut Vec<Rc<ParameterNode>>,
                              successors: &mut Vec<Rc<Node>>) {
        let p = SimpleParameterNode::new(&*parameter.name,
                                         parameter.help_text.clone(),
                                         parameter.hidden,
                                         parameter.priority.unwrap_or(PRIORITY_PARAMETER),
                                         vec![],
                                         parameter.repeatable,
                                         None,
                                         parameter.required);
        let sp = Rc::new(p);
        parameters.push(sp.clone());
        successors.push(sp);
    }
}

/// Description of a command to be added to the `CommandTree`.
#[derive(Clone)]
pub struct Command {
    hidden: bool,
    priority: i32,
    name: String,
    help_text: Option<String>,
    parameters: Vec<Parameter>,
    wrapped_root: Option<String>,
}

impl Command {
    /// Construct a default (blank) command with the given `name`.
    pub fn new(name: &str) -> Self {
        Command {
            hidden: false,
            priority: PRIORITY_DEFAULT,
            name: name.to_string(),
            help_text: None,
            parameters: vec![],
            wrapped_root: None,
        }
    }

    /// Mark the command as hidden. Hidden commands will match
    /// within the parser, but are not listed during completion.
    pub fn hidden(&mut self, hidden: bool) -> &mut Self {
        self.hidden = hidden;
        self
    }

    /// Give the command a priority. This is used when sorting
    /// out conflicts during matching and completion.
    pub fn priority(&mut self, priority: i32) -> &mut Self {
        self.priority = priority;
        self
    }

    /// Supply help text for the command.
    pub fn help(&mut self, help_text: &str) -> &mut Self {
        self.help_text = Some(help_text.to_string());
        self
    }

    /// Add a `Parameter` to the command.
    pub fn parameter(&mut self, parameter: Parameter) -> &mut Self {
        self.parameters.push(parameter);
        self
    }

    /// Create a `WrapperNode` instead of a `CommandNode`. The
    /// `wrapped_root` signifies the path to the command that should
    /// be wrapped by this command.
    pub fn wraps(&mut self, wrapped_root: String) -> &mut Self {
        self.wrapped_root = Some(wrapped_root);
        self
    }

    /// Return an instance of `Command` that can be passed to the
    /// `CommandTree`. This is used to terminate the series of construction
    /// methods used to initialize and configure the command.
    pub fn finalize(&self) -> Self {
        self.clone()
    }
}

/// Description of a parameter to be added to the `Command`.
#[derive(Clone)]
pub struct Parameter {
    hidden: bool,
    priority: Option<i32>,
    name: String,
    repeatable: bool,
    aliases: Vec<String>,
    help_text: Option<String>,
    required: bool,
    parameter_kind: ParameterKind,
}

impl Parameter {
    /// Construct a default (blank) parameter with the given `name`.
    pub fn new(name: &str) -> Self {
        Parameter {
            hidden: false,
            priority: None,
            name: name.to_string(),
            repeatable: false,
            aliases: vec![],
            help_text: None,
            required: false,
            parameter_kind: ParameterKind::Simple,
        }
    }

    /// Mark the parameter as hidden. Hidden parameters will match
    /// within the parser, but are not listed during completion.
    pub fn hidden(&mut self, hidden: bool) -> &mut Self {
        self.hidden = hidden;
        self
    }

    /// Give the parameter a priority. This is used when sorting
    /// out conflicts during matching and completion.
    ///
    /// The `priority` of a `Parameter` defaults to `PRIORITY_PARAMETER`
    /// except for when the `kind` is `ParameterKind::Flag` in which
    /// case, the default will be `PRIORITY_DEFAULT`.
    pub fn priority(&mut self, priority: i32) -> &mut Self {
        self.priority = Some(priority);
        self
    }

    /// Establish whether or not this parameter is repeatable.
    /// Repeated parameters produce a vector of values and can
    /// be given multiple times within a single command invocation.
    pub fn repeatable(&mut self, repeatable: bool) -> &mut Self {
        self.repeatable = repeatable;
        self
    }

    /// Add an alias that this parameter can use.
    ///
    /// Aliases are currently only valid for parameters of `kind`
    /// `ParameterKind::Named`.
    pub fn alias(&mut self, alias: &str) -> &mut Self {
        self.aliases.push(alias.to_string());
        self
    }

    /// Supply the help text for the parameter.
    pub fn help(&mut self, help_text: &str) -> &mut Self {
        self.help_text = Some(help_text.to_string());
        self
    }

    /// Establish whether or not this parameter is required.
    pub fn required(&mut self, required: bool) -> &mut Self {
        self.required = required;
        self
    }

    /// Set which type of `ParameterNode` is supposed to be created
    /// to represent this parameter.
    pub fn kind(&mut self, kind: ParameterKind) -> &mut Self {
        self.parameter_kind = kind;
        self
    }

    /// Return an instance of `Parameter` that can be passed to the
    /// `Command`. This is used to terminate the series of construction
    /// methods used to initialize and configure the parameter.
    pub fn finalize(&self) -> Self {
        self.clone()
    }
}
<commit_msg>Add unimplemented section for WrapperNode creation.<commit_after>// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! # Node Builder
//!
//! ```
//! use commands::parser::builder::*;
//!
//! let mut tree = CommandTree::new();
//! tree.command(Command::new("again")
//!                  .hidden(false)
//!                  .parameter(Parameter::new("test")
//!                                 .required(false)
//!                                 .help("This is just a test parameter.")
//!                                 .finalize())
//!                  .finalize());
//! ```

use std::rc::Rc;
use super::nodes::*;

/// Indicate the type of parameter, so that the correct class and node
/// structures are created.
#[derive(Clone, Copy, PartialEq)]
pub enum ParameterKind {
    /// This parameter is a `FlagParameter`.
    Flag,
    /// This parameter is a `NamedParameter`.
    Named,
    /// This parameter is a `SimpleParameter`.
    Simple,
}

/// Store a command tree while populating it. This can be used
/// to construct a `RootNode` to be used with the `Parser`.
pub struct CommandTree {
    commands: Vec<Command>,
}

impl CommandTree {
    /// Create a new `CommandTree`.
    pub fn new() -> Self {
        CommandTree { commands: vec![] }
    }

    /// Add a `Command` to the `CommandTree`.
    pub fn command(&mut self, command: Command) {
        self.commands.push(command);
    }

    /// Construct the `CommandTree` and produce a `RootNode`.
    pub fn finalize(&self) -> Rc<RootNode> {
        let mut successors: Vec<Rc<Node>> = vec![];
        for c in &self.commands {
            successors.push(self.build_command(c));
        }
        RootNode::new(successors)
    }

    fn build_command(&self, command: &Command) -> Rc<Node> {
        let mut parameters: Vec<Rc<ParameterNode>> = vec![];
        let mut successors: Vec<Rc<Node>> = vec![];
        for parameter in &command.parameters {
            match parameter.parameter_kind {
                ParameterKind::Flag => {
                    self.build_flag_parameter(parameter, &mut parameters, &mut successors);
                }
                ParameterKind::Named => {
                    self.build_named_parameter(parameter, &mut parameters, &mut successors);
                }
                ParameterKind::Simple => {
                    self.build_simple_parameter(parameter, &mut parameters, &mut successors);
                }
            };
        }
        if let Some(_) = command.wrapped_root {
            // XXX: This should be a WrapperNode.
            unimplemented!()
        } else {
            Rc::new(CommandNode::new(&*command.name,
                                     command.help_text.clone(),
                                     command.hidden,
                                     command.priority,
                                     successors,
                                     None,
                                     parameters))
        }
    }

    fn build_flag_parameter(&self,
                            parameter: &Parameter,
                            parameters: &mut Vec<Rc<ParameterNode>>,
                            successors: &mut Vec<Rc<Node>>) {
        let p = FlagParameterNode::new(&*parameter.name,
                                       parameter.help_text.clone(),
                                       parameter.hidden,
                                       parameter.priority.unwrap_or(PRIORITY_DEFAULT),
                                       vec![],
                                       parameter.repeatable,
                                       None,
                                       parameter.required);
        let fp = Rc::new(p);
        parameters.push(fp.clone());
        successors.push(fp);
    }

    fn build_named_parameter(&self,
                             parameter: &Parameter,
                             parameters: &mut Vec<Rc<ParameterNode>>,
                             successors: &mut Vec<Rc<Node>>) {
        let p = Rc::new(NamedParameterNode::new(&*parameter.name,
                                                parameter.help_text.clone(),
                                                parameter.hidden,
                                                parameter.priority.unwrap_or(PRIORITY_PARAMETER),
                                                vec![],
                                                parameter.repeatable,
                                                None,
                                                parameter.required));
        parameters.push(p.clone());
        let n = Rc::new(ParameterNameNode::new(&*parameter.name,
                                               parameter.hidden,
                                               PRIORITY_DEFAULT,
                                               vec![p.clone()],
                                               parameter.repeatable,
                                               Some(p.clone()),
                                               p.clone()));
        successors.push(n);
        for alias in &parameter.aliases {
            let a = Rc::new(ParameterNameNode::new(&*alias,
                                                   parameter.hidden,
                                                   PRIORITY_DEFAULT,
                                                   vec![p.clone()],
                                                   parameter.repeatable,
                                                   Some(p.clone()),
                                                   p.clone()));
            successors.push(a);
        }
    }

    fn build_simple_parameter(&self,
                              parameter: &Parameter,
                              parameters: &mut Vec<Rc<ParameterNode>>,
                              successors: &mut Vec<Rc<Node>>) {
        let p = SimpleParameterNode::new(&*parameter.name,
                                         parameter.help_text.clone(),
                                         parameter.hidden,
                                         parameter.priority.unwrap_or(PRIORITY_PARAMETER),
                                         vec![],
                                         parameter.repeatable,
                                         None,
                                         parameter.required);
        let sp = Rc::new(p);
        parameters.push(sp.clone());
        successors.push(sp);
    }
}

/// Description of a command to be added to the `CommandTree`.
#[derive(Clone)]
pub struct Command {
    hidden: bool,
    priority: i32,
    name: String,
    help_text: Option<String>,
    parameters: Vec<Parameter>,
    wrapped_root: Option<String>,
}

impl Command {
    /// Construct a default (blank) command with the given `name`.
    pub fn new(name: &str) -> Self {
        Command {
            hidden: false,
            priority: PRIORITY_DEFAULT,
            name: name.to_string(),
            help_text: None,
            parameters: vec![],
            wrapped_root: None,
        }
    }

    /// Mark the command as hidden. Hidden commands will match
    /// within the parser, but are not listed during completion.
    pub fn hidden(&mut self, hidden: bool) -> &mut Self {
        self.hidden = hidden;
        self
    }

    /// Give the command a priority. This is used when sorting
    /// out conflicts during matching and completion.
    pub fn priority(&mut self, priority: i32) -> &mut Self {
        self.priority = priority;
        self
    }

    /// Supply help text for the command.
    pub fn help(&mut self, help_text: &str) -> &mut Self {
        self.help_text = Some(help_text.to_string());
        self
    }

    /// Add a `Parameter` to the command.
    pub fn parameter(&mut self, parameter: Parameter) -> &mut Self {
        self.parameters.push(parameter);
        self
    }

    /// Create a `WrapperNode` instead of a `CommandNode`. The
    /// `wrapped_root` signifies the path to the command that should
    /// be wrapped by this command.
    pub fn wraps(&mut self, wrapped_root: String) -> &mut Self {
        self.wrapped_root = Some(wrapped_root);
        self
    }

    /// Return an instance of `Command` that can be passed to the
    /// `CommandTree`. This is used to terminate the series of construction
    /// methods used to initialize and configure the command.
    pub fn finalize(&self) -> Self {
        self.clone()
    }
}

/// Description of a parameter to be added to the `Command`.
#[derive(Clone)]
pub struct Parameter {
    hidden: bool,
    priority: Option<i32>,
    name: String,
    repeatable: bool,
    aliases: Vec<String>,
    help_text: Option<String>,
    required: bool,
    parameter_kind: ParameterKind,
}

impl Parameter {
    /// Construct a default (blank) parameter with the given `name`.
    pub fn new(name: &str) -> Self {
        Parameter {
            hidden: false,
            priority: None,
            name: name.to_string(),
            repeatable: false,
            aliases: vec![],
            help_text: None,
            required: false,
            parameter_kind: ParameterKind::Simple,
        }
    }

    /// Mark the parameter as hidden. Hidden parameters will match
    /// within the parser, but are not listed during completion.
    pub fn hidden(&mut self, hidden: bool) -> &mut Self {
        self.hidden = hidden;
        self
    }

    /// Give the parameter a priority. This is used when sorting
    /// out conflicts during matching and completion.
    ///
    /// The `priority` of a `Parameter` defaults to `PRIORITY_PARAMETER`
    /// except for when the `kind` is `ParameterKind::Flag` in which
    /// case, the default will be `PRIORITY_DEFAULT`.
    pub fn priority(&mut self, priority: i32) -> &mut Self {
        self.priority = Some(priority);
        self
    }

    /// Establish whether or not this parameter is repeatable.
    /// Repeated parameters produce a vector of values and can
    /// be given multiple times within a single command invocation.
    pub fn repeatable(&mut self, repeatable: bool) -> &mut Self {
        self.repeatable = repeatable;
        self
    }

    /// Add an alias that this parameter can use.
    ///
    /// Aliases are currently only valid for parameters of `kind`
    /// `ParameterKind::Named`.
    pub fn alias(&mut self, alias: &str) -> &mut Self {
        self.aliases.push(alias.to_string());
        self
    }

    /// Supply the help text for the parameter.
    pub fn help(&mut self, help_text: &str) -> &mut Self {
        self.help_text = Some(help_text.to_string());
        self
    }

    /// Establish whether or not this parameter is required.
    pub fn required(&mut self, required: bool) -> &mut Self {
        self.required = required;
        self
    }

    /// Set which type of `ParameterNode` is supposed to be created
    /// to represent this parameter.
    pub fn kind(&mut self, kind: ParameterKind) -> &mut Self {
        self.parameter_kind = kind;
        self
    }

    /// Return an instance of `Parameter` that can be passed to the
    /// `Command`. This is used to terminate the series of construction
    /// methods used to initialize and configure the parameter.
    pub fn finalize(&self) -> Self {
        self.clone()
    }
}
<|endoftext|>


<commit_before>// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Feature gating
//!
//! This modules implements the gating necessary for preventing certain compiler
//! features from being used by default. This module will crawl a pre-expanded
//! AST to ensure that there are no features which are used that are not
//! enabled.
//!
//! Features are enabled in programs via the crate-level attributes of
//! #[feature(...)] with a comma-separated list of features.

use syntax::ast;
use syntax::attr::AttrMetaMethods;
use syntax::codemap::Span;
use syntax::visit;
use syntax::visit::Visitor;

use driver::session::Session;

/// This is a list of all known features since the beginning of time. This list
/// can never shrink, it may only be expanded (in order to prevent old programs
/// from failing to compile). The status of each feature may change, however.
static KNOWN_FEATURES: &'static [(&'static str, Status)] = &[
    ("globs", Active),
    ("macro_rules", Active),
    ("struct_variant", Active),
    ("once_fns", Active),
    ("asm", Active),

    // These are used to test this portion of the compiler, they don't actually
    // mean anything
    ("test_accepted_feature", Accepted),
    ("test_removed_feature", Removed),
];

enum Status {
    /// Represents an active feature that is currently being implemented or
    /// currently being considered for addition/removal.
    Active,

    /// Represents a feature which has since been removed (it was once Active)
    Removed,

    /// This language feature has since been Accepted (it was once Active)
    Accepted,
}

struct Context {
    features: ~[&'static str],
    sess: Session,
}

impl Context {
    fn gate_feature(&self, feature: &str, span: Span, explain: &str) {
        if !self.has_feature(feature) {
            self.sess.span_err(span, explain);
            self.sess.span_note(span, format!("add \\#[feature({})] to the \
                                                  crate attributes to enable",
                                                 feature));
        }
    }

    fn has_feature(&self, feature: &str) -> bool {
        self.features.iter().any(|n| n.as_slice() == feature)
    }
}

impl Visitor<()> for Context {
    fn visit_view_item(&mut self, i: &ast::view_item, _: ()) {
        match i.node {
            ast::view_item_use(ref paths) => {
                for path in paths.iter() {
                    match path.node {
                        ast::view_path_glob(*) => {
                            self.gate_feature("globs", path.span,
                                              "glob import statements are \
                                               experimental and possibly buggy");
                        }
                        _ => {}
                    }
                }
            }
            _ => {}
        }
        visit::walk_view_item(self, i, ())
    }

    fn visit_item(&mut self, i: @ast::item, _:()) {
        match i.node {
            ast::item_enum(ref def, _) => {
                for variant in def.variants.iter() {
                    match variant.node.kind {
                        ast::struct_variant_kind(*) => {
                            self.gate_feature("struct_variant", variant.span,
                                              "enum struct variants are \
                                               experimental and possibly buggy");
                        }
                        _ => {}
                    }
                }
            }

            _ => {}
        }

        visit::walk_item(self, i, ());
    }

    fn visit_mac(&mut self, macro: &ast::mac, _: ()) {
        let ast::mac_invoc_tt(ref path, _, _) = macro.node;

        if path.segments.last().identifier == self.sess.ident_of("macro_rules") {
            self.gate_feature("macro_rules", path.span, "macro definitions are \
                not stable enough for use and are subject to change");
        }

        else if path.segments.last().identifier == self.sess.ident_of("asm") {
            self.gate_feature("asm", path.span, "inline assembly is not \
                stable enough for use and is subject to change");
        }
    }

    fn visit_ty(&mut self, t: &ast::Ty, _: ()) {
        match t.node {
            ast::ty_closure(closure) if closure.onceness == ast::Once => {
                self.gate_feature("once_fns", t.span,
                                  "once functions are \
                                   experimental and likely to be removed");

            },
            _ => {}
        }

        visit::walk_ty(self, t, ());
    }
}

pub fn check_crate(sess: Session, crate: &ast::Crate) {
    let mut cx = Context {
        features: ~[],
        sess: sess,
    };

    for attr in crate.attrs.iter() {
        if "feature" != attr.name() { continue }

        match attr.meta_item_list() {
            None => {
                sess.span_err(attr.span, "malformed feature attribute, \
                                          expected #[feature(...)]");
            }
            Some(list) => {
                for &mi in list.iter() {
                    let name = match mi.node {
                        ast::MetaWord(word) => word,
                        _ => {
                            sess.span_err(mi.span, "malformed feature, expected \
                                                    just one word");
                            continue
                        }
                    };
                    match KNOWN_FEATURES.iter().find(|& &(n, _)| n == name) {
                        Some(&(name, Active)) => { cx.features.push(name); }
                        Some(&(_, Removed)) => {
                            sess.span_err(mi.span, "feature has been removed");
                        }
                        Some(&(_, Accepted)) => {
                            sess.span_warn(mi.span, "feature has added to rust, \
                                                     directive not necessary");
                        }
                        None => {
                            sess.span_err(mi.span, "unknown feature");
                        }
                    }
                }
            }
        }
    }

    visit::walk_crate(&mut cx, crate, ());

    sess.abort_if_errors();
}
<commit_msg>add feature gate for managed boxes<commit_after>// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Feature gating
//!
//! This modules implements the gating necessary for preventing certain compiler
//! features from being used by default. This module will crawl a pre-expanded
//! AST to ensure that there are no features which are used that are not
//! enabled.
//!
//! Features are enabled in programs via the crate-level attributes of
//! #[feature(...)] with a comma-separated list of features.

use syntax::ast;
use syntax::attr::AttrMetaMethods;
use syntax::codemap::Span;
use syntax::visit;
use syntax::visit::Visitor;

use driver::session::Session;

/// This is a list of all known features since the beginning of time. This list
/// can never shrink, it may only be expanded (in order to prevent old programs
/// from failing to compile). The status of each feature may change, however.
static KNOWN_FEATURES: &'static [(&'static str, Status)] = &[
    ("globs", Active),
    ("macro_rules", Active),
    ("struct_variant", Active),
    ("once_fns", Active),
    ("asm", Active),
    ("managed_boxes", Active),

    // These are used to test this portion of the compiler, they don't actually
    // mean anything
    ("test_accepted_feature", Accepted),
    ("test_removed_feature", Removed),
];

enum Status {
    /// Represents an active feature that is currently being implemented or
    /// currently being considered for addition/removal.
    Active,

    /// Represents a feature which has since been removed (it was once Active)
    Removed,

    /// This language feature has since been Accepted (it was once Active)
    Accepted,
}

struct Context {
    features: ~[&'static str],
    sess: Session,
}

impl Context {
    fn gate_feature(&self, feature: &str, span: Span, explain: &str) {
        if !self.has_feature(feature) {
            self.sess.span_err(span, explain);
            self.sess.span_note(span, format!("add \\#[feature({})] to the \
                                                  crate attributes to enable",
                                                 feature));
        }
    }

    fn has_feature(&self, feature: &str) -> bool {
        self.features.iter().any(|n| n.as_slice() == feature)
    }
}

impl Visitor<()> for Context {
    fn visit_view_item(&mut self, i: &ast::view_item, _: ()) {
        match i.node {
            ast::view_item_use(ref paths) => {
                for path in paths.iter() {
                    match path.node {
                        ast::view_path_glob(*) => {
                            self.gate_feature("globs", path.span,
                                              "glob import statements are \
                                               experimental and possibly buggy");
                        }
                        _ => {}
                    }
                }
            }
            _ => {}
        }
        visit::walk_view_item(self, i, ())
    }

    fn visit_item(&mut self, i: @ast::item, _:()) {
        match i.node {
            ast::item_enum(ref def, _) => {
                for variant in def.variants.iter() {
                    match variant.node.kind {
                        ast::struct_variant_kind(*) => {
                            self.gate_feature("struct_variant", variant.span,
                                              "enum struct variants are \
                                               experimental and possibly buggy");
                        }
                        _ => {}
                    }
                }
            }

            _ => {}
        }

        visit::walk_item(self, i, ());
    }

    fn visit_mac(&mut self, macro: &ast::mac, _: ()) {
        let ast::mac_invoc_tt(ref path, _, _) = macro.node;

        if path.segments.last().identifier == self.sess.ident_of("macro_rules") {
            self.gate_feature("macro_rules", path.span, "macro definitions are \
                not stable enough for use and are subject to change");
        }

        else if path.segments.last().identifier == self.sess.ident_of("asm") {
            self.gate_feature("asm", path.span, "inline assembly is not \
                stable enough for use and is subject to change");
        }
    }

    fn visit_ty(&mut self, t: &ast::Ty, _: ()) {
        match t.node {
            ast::ty_closure(closure) if closure.onceness == ast::Once => {
                self.gate_feature("once_fns", t.span,
                                  "once functions are \
                                   experimental and likely to be removed");

            },
            // NOTE: enable after snapshot
            ast::ty_box(_) if false => {
                self.gate_feature("managed_boxes", t.span, "The managed box syntax may be replaced \
                                                            by a library type, and a garbage \
                                                            collector is not yet implemented. \
                                                            Consider using the `std::rc` module \
                                                            as it performs much better as a \
                                                            reference counting implementation.");
            }
            _ => {}
        }

        visit::walk_ty(self, t, ());
    }
}

pub fn check_crate(sess: Session, crate: &ast::Crate) {
    let mut cx = Context {
        features: ~[],
        sess: sess,
    };

    for attr in crate.attrs.iter() {
        if "feature" != attr.name() { continue }

        match attr.meta_item_list() {
            None => {
                sess.span_err(attr.span, "malformed feature attribute, \
                                          expected #[feature(...)]");
            }
            Some(list) => {
                for &mi in list.iter() {
                    let name = match mi.node {
                        ast::MetaWord(word) => word,
                        _ => {
                            sess.span_err(mi.span, "malformed feature, expected \
                                                    just one word");
                            continue
                        }
                    };
                    match KNOWN_FEATURES.iter().find(|& &(n, _)| n == name) {
                        Some(&(name, Active)) => { cx.features.push(name); }
                        Some(&(_, Removed)) => {
                            sess.span_err(mi.span, "feature has been removed");
                        }
                        Some(&(_, Accepted)) => {
                            sess.span_warn(mi.span, "feature has added to rust, \
                                                     directive not necessary");
                        }
                        None => {
                            sess.span_err(mi.span, "unknown feature");
                        }
                    }
                }
            }
        }
    }

    visit::walk_crate(&mut cx, crate, ());

    sess.abort_if_errors();
}
<|endoftext|>


<commit_before>// extern crate libc;

// use libc::types;
use std::io::File;
use std::mem;
use std::os;

// https://stackoverflow.com/questions/24633784/is-there-a-gzip-library-available-for-rust

// #[link(name = "z")]
// extern {
//     fn gzopen(path: *const types::os::arch::c95::c_char,
//               mode: *const types::os::arch::c95::c_char)
//               -> types::common::c95::c_void;
// }

// #[link(name = "z")]
// extern {
//     fn gzread(file: types::common::c95::c_void,
//               buf:  types::common::c95::c_void,
//               len:  uint)
//               -> int;
// }

// #[link(name = "z")]
// extern {
//     fn gzclose(file: types::common::c95::c_void) -> int;
// }

fn pointer_size() -> uint {
    let tmp = 0u8;
    let boxed = box tmp;
    mem::size_of_val(&boxed)
}

fn main() {
    let args = os::args();
    if args.len() != 2 {
        println!("usage: read_blend_file /path/to/file.blend");
    } else {
        let slice: &str = args[1].as_slice();
        println!("open \"{}\" ...", slice);
        {
            // read 12 bytes from the Blender file
            let path = Path::new(slice);
            let display = path.display();
            let mut file = match File::open(&path) {
                Err(why) => fail!("couldn't open {}: {}", display, why.desc),
                Ok(file) => file,
            };
            let mut buf = [0u8, ..12];
            match file.read(buf) {
                Err(why) => fail!("couldn't open {}: {}", display, why.desc),
                Ok(_) => (),
            }
            // pack those 12 bytes into a string ...
            let mut header = String::new();
            for n in range(0u, 12) {
                header.push(buf[n] as char);
            }
            // ... to be able to compare them
            let slice = header.as_slice();
            let blender: &str = slice.slice(0, 7); // first 7 chars
            if blender == "BLENDER" {
                println!("INFO: a Blender file.");
                let ptr_size = pointer_size();
                println!("size of pointer in bytes: {}", ptr_size);
                println!("Header (12 chars): {}", header);
                let mut ptr_size_differs = false;
                if buf[7] as char == '_' {
                    // 32-bit pointers expected
                    if ptr_size != 4 {
                        ptr_size_differs = true;
                    }
                    println!("32-bit pointers in file, pointer size differs? {}", ptr_size_differs);
                } else {
                    // 64-bit pointers expected
                    if ptr_size != 8 {
                        ptr_size_differs = true;
                    }
                    println!("64-bit pointers in file, pointer size differs? {}", ptr_size_differs);
                }
                // little endian or big endian?
                let mut l_endian;
                let endian_test = 1u16; // value one stored in two bytes
                if endian_test.to_le() != endian_test {
                    l_endian = false;
                } else {
                    // if we are on a little endian machine to_le() is a no-op
                    l_endian = true;
                }
                println!("l_endian = {}", l_endian);
                let mut switch_endian;
                if buf[8] as char == 'v' {
                    // file stores little endian
                    if !l_endian {
                        switch_endian = true;
                    } else {
                        switch_endian = false;
                    }
                } else {
                    // file stores big endian
                    if l_endian {
                        switch_endian = true;
                    } else {
                        switch_endian = false;
                    }
                }
                println!("switch_endian = {}", switch_endian);
                // get the version number
                let mut num = String::new();
                for n in range(9u, 12) {
                    num.push(buf[n] as char);
                }
                println!("num = {}", num);
                // assumes 64-bit pointers (in file as well as on platform)
                loop {
                    // read_file_dna
                    let mut bhead8 = [0u8, ..24]; // 4 * int + 64-bit pointer
                    match file.read(bhead8) {
                        Err(why) => fail!("couldn't open {}: {}", display, why.desc),
                        Ok(_) => (),
                    }
                    // pack those 24 bytes into a string ...
                    let mut bhead = String::new();
                    for n in range(0u, 24) {
                        bhead.push(bhead8[n] as char);
                    }
                    let slice = bhead.as_slice();
                    let code: &str = slice.slice(0, 4); // first 4 chars
                    println!("code = {}", code);
                    let mut len: uint = 0;
                    len += (bhead8[4] as uint) <<  0;
                    len += (bhead8[5] as uint) <<  8;
                    len += (bhead8[6] as uint) << 16;
                    len += (bhead8[7] as uint) << 24;
                    println!("len = {}", len);
                    file.read_exact(len);
                    if code == "ENDB" && len == 0 {
                        break;
                    }
                }
            } else {
                println!("ERROR: not a Blender file.");
            }
        } // file gets closed here
    }
}
<commit_msg>Typo.<commit_after>// extern crate libc;

// use libc::types;
use std::io::File;
use std::mem;
use std::os;

// https://stackoverflow.com/questions/24633784/is-there-a-gzip-library-available-for-rust

// #[link(name = "z")]
// extern {
//     fn gzopen(path: *const types::os::arch::c95::c_char,
//               mode: *const types::os::arch::c95::c_char)
//               -> types::common::c95::c_void;
// }

// #[link(name = "z")]
// extern {
//     fn gzread(file: types::common::c95::c_void,
//               buf:  types::common::c95::c_void,
//               len:  uint)
//               -> int;
// }

// #[link(name = "z")]
// extern {
//     fn gzclose(file: types::common::c95::c_void) -> int;
// }

fn pointer_size() -> uint {
    let tmp = 0u8;
    let boxed = box tmp;
    mem::size_of_val(&boxed)
}

fn main() {
    let args = os::args();
    if args.len() != 2 {
        println!("usage: read_blend_file /path/to/file.blend");
    } else {
        let slice: &str = args[1].as_slice();
        println!("open \"{}\" ...", slice);
        {
            // read 12 bytes from the Blender file
            let path = Path::new(slice);
            let display = path.display();
            let mut file = match File::open(&path) {
                Err(why) => fail!("couldn't open {}: {}", display, why.desc),
                Ok(file) => file,
            };
            let mut buf = [0u8, ..12];
            match file.read(buf) {
                Err(why) => fail!("couldn't read {}: {}", display, why.desc),
                Ok(_) => (),
            }
            // pack those 12 bytes into a string ...
            let mut header = String::new();
            for n in range(0u, 12) {
                header.push(buf[n] as char);
            }
            // ... to be able to compare them
            let slice = header.as_slice();
            let blender: &str = slice.slice(0, 7); // first 7 chars
            if blender == "BLENDER" {
                println!("INFO: a Blender file.");
                let ptr_size = pointer_size();
                println!("size of pointer in bytes: {}", ptr_size);
                println!("Header (12 chars): {}", header);
                let mut ptr_size_differs = false;
                if buf[7] as char == '_' {
                    // 32-bit pointers expected
                    if ptr_size != 4 {
                        ptr_size_differs = true;
                    }
                    println!("32-bit pointers in file, pointer size differs? {}", ptr_size_differs);
                } else {
                    // 64-bit pointers expected
                    if ptr_size != 8 {
                        ptr_size_differs = true;
                    }
                    println!("64-bit pointers in file, pointer size differs? {}", ptr_size_differs);
                }
                // little endian or big endian?
                let mut l_endian;
                let endian_test = 1u16; // value one stored in two bytes
                if endian_test.to_le() != endian_test {
                    l_endian = false;
                } else {
                    // if we are on a little endian machine to_le() is a no-op
                    l_endian = true;
                }
                println!("l_endian = {}", l_endian);
                let mut switch_endian;
                if buf[8] as char == 'v' {
                    // file stores little endian
                    if !l_endian {
                        switch_endian = true;
                    } else {
                        switch_endian = false;
                    }
                } else {
                    // file stores big endian
                    if l_endian {
                        switch_endian = true;
                    } else {
                        switch_endian = false;
                    }
                }
                println!("switch_endian = {}", switch_endian);
                // get the version number
                let mut num = String::new();
                for n in range(9u, 12) {
                    num.push(buf[n] as char);
                }
                println!("num = {}", num);
                // assumes 64-bit pointers (in file as well as on platform)
                loop {
                    // read_file_dna
                    let mut bhead8 = [0u8, ..24]; // 4 * int + 64-bit pointer
                    match file.read(bhead8) {
                        Err(why) => fail!("couldn't open {}: {}", display, why.desc),
                        Ok(_) => (),
                    }
                    // pack those 24 bytes into a string ...
                    let mut bhead = String::new();
                    for n in range(0u, 24) {
                        bhead.push(bhead8[n] as char);
                    }
                    let slice = bhead.as_slice();
                    let code: &str = slice.slice(0, 4); // first 4 chars
                    println!("code = {}", code);
                    let mut len: uint = 0;
                    len += (bhead8[4] as uint) <<  0;
                    len += (bhead8[5] as uint) <<  8;
                    len += (bhead8[6] as uint) << 16;
                    len += (bhead8[7] as uint) << 24;
                    println!("len = {}", len);
                    file.read_exact(len);
                    if code == "ENDB" && len == 0 {
                        break;
                    }
                }
            } else {
                println!("ERROR: not a Blender file.");
            }
        } // file gets closed here
    }
}
<|endoftext|>


<commit_before>use cgmath::Point2;
use gfx;
use specs;
use specs::prelude::{Read, ReadStorage, WriteStorage};

use crate::bullet::{BulletDrawable, bullets::Bullets};
use crate::character::controls::CharacterInputState;
use crate::critter::CritterData;
use crate::data;
use crate::game::constants::{ASPECT_RATIO, NORMAL_DEATH_SPRITE_OFFSET, SPRITE_OFFSET, VIEW_DISTANCE, ZOMBIE_SHEET_TOTAL_WIDTH, ZOMBIE_STILL_SPRITE_OFFSET};
use crate::game::get_random_bool;
use crate::gfx_app::{ColorFormat, DepthFormat};
use crate::graphics::{get_nearest_random_tile_position,
                      distance,
                      camera::CameraInputState,
                      can_move_to_tile,
                      direction,
                      direction_movement,
                      direction_movement_180,
                      GameTime,
                      orientation::{Orientation, Stance},
                      orientation_to_direction,
                      overlaps};
use crate::graphics::dimensions::{Dimensions, get_projection, get_view_matrix};
use crate::graphics::mesh::{RectangularTexturedMesh, Geometry};
use crate::graphics::texture::{load_texture, Texture};
use crate::shaders::{CharacterSheet, critter_pipeline, Position, Projection};
use crate::terrain::path_finding::calc_next_movement;
use crate::zombie::zombies::Zombies;

pub mod zombies;

const SHADER_VERT: &[u8] = include_bytes!("../shaders/character.v.glsl");
const SHADER_FRAG: &[u8] = include_bytes!("../shaders/character.f.glsl");

pub struct ZombieDrawable {
  projection: Projection,
  pub position: Position,
  previous_position: Position,
  orientation: Orientation,
  pub stance: Stance,
  direction: Orientation,
  last_decision: i64,
  pub movement_direction: Point2<f32>,
  zombie_idx: usize,
  zombie_death_idx: usize,
  movement_speed: f32,
  health: f32,
}

impl ZombieDrawable {
  pub fn new(position: Position) -> ZombieDrawable {
    let view = get_view_matrix(VIEW_DISTANCE);
    let projection = get_projection(view, ASPECT_RATIO);
    ZombieDrawable {
      projection,
      position,
      previous_position: Position::origin(),
      orientation: Orientation::Left,
      stance: Stance::Still,
      direction: Orientation::Left,
      last_decision: -2,
      movement_direction: Point2::new(0.0, 0.0),
      zombie_idx: 0,
      zombie_death_idx: 0,
      movement_speed: 0.0,
      health: 1.0,
    }
  }

  pub fn update(&mut self, world_to_clip: &Projection, ci: &CharacterInputState, game_time: u64) {
    self.projection = *world_to_clip;

    let offset_delta = ci.movement - self.previous_position;
    self.previous_position = ci.movement;

    let x_y_distance_to_player = self.position - offset_delta;

    let distance_to_player = distance(x_y_distance_to_player.x().abs(), x_y_distance_to_player.y().abs());

    let is_alive = self.health > 0.0 && self.stance != Stance::NormalDeath && self.stance != Stance::CriticalDeath;

    if is_alive {
      let zombie_pos = ci.movement - self.position;

      if distance_to_player < 400.0 {
        let dir = calc_next_movement(zombie_pos, self.previous_position) as f32;
        self.direction = orientation_to_direction(dir);
        self.movement_direction = direction_movement(dir);
        self.stance = Stance::Running;
        self.movement_speed = 2.4 * self.health;
      } else {
        self.idle_direction_movement(zombie_pos, game_time as i64);
        self.movement_speed = 1.2 * self.health;
      }
    } else {
      self.movement_direction = Point2::new(0.0, 0.0);
    }

    self.position = Position::new(self.movement_direction.x * self.movement_speed,
                                  self.movement_direction.y * self.movement_speed) + self.position + offset_delta;
  }

  fn idle_direction_movement(&mut self, zombie_pos: Position, game_time: i64) {
    if !can_move_to_tile(zombie_pos) {
      let dir = direction(self.movement_direction, Point2::new(0.0, 0.0));
      self.movement_direction = direction_movement_180(self.movement_direction);
      self.orientation = orientation_to_direction(dir);
      self.direction = orientation_to_direction(dir);
    }

    if self.last_decision + 2 < game_time {
      self.stance = Stance::Walking;
      self.last_decision = game_time;
      let end_point = get_nearest_random_tile_position(zombie_pos);
      let dir = calc_next_movement(zombie_pos, end_point) as f32;
      self.movement_direction = direction_movement(dir);
      self.direction = orientation_to_direction(dir);
    }
  }

  fn handle_bullet_hit(&mut self) {
    self.health -= 0.5;
    if self.health <= 0.0 {
      self.stance =
        if get_random_bool() {
          Stance::NormalDeath
        } else {
          Stance::CriticalDeath
        };
    }
  }

  fn check_bullet_hits(&mut self, bullets: &[BulletDrawable]) {
    bullets.iter().for_each(|bullet| {
      if overlaps(self.position, bullet.position, 15.0, 15.0) && self.stance != Stance::NormalDeath && self.stance != Stance::CriticalDeath {
        self.handle_bullet_hit()
      }
    });
  }

  pub fn update_alive_idx(&mut self, max_idx: usize) {
    if self.zombie_idx < max_idx {
      self.zombie_idx += 1;
    } else {
      self.zombie_idx = 0;
    }
  }

  pub fn update_death_idx(&mut self, max_idx: usize) {
    if self.zombie_death_idx < max_idx {
      self.zombie_death_idx += 1;
    }
  }
}

pub struct ZombieDrawSystem<R: gfx::Resources> {
  bundle: gfx::pso::bundle::Bundle<R, critter_pipeline::Data<R>>,
  data: Vec<CritterData>,
}

impl<R: gfx::Resources> ZombieDrawSystem<R> {
  pub fn new<F>(factory: &mut F,
                rtv: gfx::handle::RenderTargetView<R, ColorFormat>,
                dsv: gfx::handle::DepthStencilView<R, DepthFormat>) -> ZombieDrawSystem<R>
    where F: gfx::Factory<R> {
    use gfx::traits::FactoryExt;

    let zombie_bytes = include_bytes!("../../assets/zombie.png");
    let char_texture = load_texture(factory, zombie_bytes);

    let rect_mesh =
      RectangularTexturedMesh::new(factory, Texture::new(char_texture, None), Geometry::Rectangle, Point2::new(25.0, 35.0), None, None, None);

    let pso =
      factory.create_pipeline_simple(SHADER_VERT, SHADER_FRAG, critter_pipeline::new())
        .expect("Zombie shader loading error");

    let pipeline_data = critter_pipeline::Data {
      vbuf: rect_mesh.mesh.vertex_buffer,
      projection_cb: factory.create_constant_buffer(1),
      position_cb: factory.create_constant_buffer(1),
      character_sprite_cb: factory.create_constant_buffer(1),
      charactersheet: (rect_mesh.mesh.texture.raw, factory.create_sampler_linear()),
      out_color: rtv,
      out_depth: dsv,
    };

    let data = data::load_zombie();

    ZombieDrawSystem {
      bundle: gfx::Bundle::new(rect_mesh.mesh.slice, pso, pipeline_data),
      data,
    }
  }

  fn get_next_sprite(&self, drawable: &mut ZombieDrawable) -> CharacterSheet {
    let sprite_idx = match drawable.stance {
      Stance::Still => {
        (drawable.direction as usize * 4 + drawable.zombie_idx)
      }
      Stance::Walking if drawable.orientation != Orientation::Normal => {
        (drawable.direction as usize * 8 + drawable.zombie_idx + ZOMBIE_STILL_SPRITE_OFFSET)
      }
      Stance::Running if drawable.orientation != Orientation::Normal => {
        (drawable.direction as usize * 8 + drawable.zombie_idx + ZOMBIE_STILL_SPRITE_OFFSET)
      }
      Stance::NormalDeath if drawable.orientation != Orientation::Normal => {
        (drawable.direction as usize * 6 + drawable.zombie_death_idx + NORMAL_DEATH_SPRITE_OFFSET)
      }
      Stance::CriticalDeath if drawable.orientation != Orientation::Normal => {
        (drawable.direction as usize * 8 + drawable.zombie_death_idx)
      }
      _ => {
        drawable.direction = drawable.orientation;
        (drawable.orientation as usize * 8 + drawable.zombie_idx + ZOMBIE_STILL_SPRITE_OFFSET)
      }
    } as usize;

    let (y_div, row_idx) =
      if drawable.stance == Stance::NormalDeath || drawable.stance == Stance::CriticalDeath {
        (0.0, 2)
      } else {
        (1.0, 2)
      };

    let elements_x = ZOMBIE_SHEET_TOTAL_WIDTH / (self.data[sprite_idx].data[2] + SPRITE_OFFSET);
    CharacterSheet {
      x_div: elements_x,
      y_div,
      row_idx,
      index: sprite_idx as f32,
    }
  }

  pub fn draw<C>(&mut self,
                 mut drawable: &mut ZombieDrawable,
                 encoder: &mut gfx::Encoder<R, C>)
    where C: gfx::CommandBuffer<R> {
    encoder.update_constant_buffer(&self.bundle.data.projection_cb, &drawable.projection);
    encoder.update_constant_buffer(&self.bundle.data.position_cb, &drawable.position);
    encoder.update_constant_buffer(&self.bundle.data.character_sprite_cb,
                                   &self.get_next_sprite(&mut drawable));
    self.bundle.encode(encoder);
  }
}

pub struct PreDrawSystem;

impl<'a> specs::prelude::System<'a> for PreDrawSystem {
  type SystemData = (WriteStorage<'a, Zombies>,
                     ReadStorage<'a, CameraInputState>,
                     ReadStorage<'a, CharacterInputState>,
                     ReadStorage<'a, Bullets>,
                     Read<'a, Dimensions>,
                     Read<'a, GameTime>);

  fn run(&mut self, (mut zombies, camera_input, character_input, bullets, dim, gt): Self::SystemData) {
    use specs::join::Join;

    for (zs, camera, ci, bs) in (&mut zombies, &camera_input, &character_input, &bullets).join() {
      let world_to_clip = dim.world_to_projection(camera);

      for z in &mut zs.zombies {
        z.update(&world_to_clip, ci, gt.0);
        z.check_bullet_hits(&bs.bullets);
      }
    }
  }
}
<commit_msg>Add zombie elevation when crossing terrain slope<commit_after>use cgmath::Point2;
use gfx;
use specs;
use specs::prelude::{Read, ReadStorage, WriteStorage};

use crate::bullet::{BulletDrawable, bullets::Bullets};
use crate::character::controls::CharacterInputState;
use crate::critter::CritterData;
use crate::data;
use crate::game::constants::{ASPECT_RATIO, NORMAL_DEATH_SPRITE_OFFSET, SMALL_HILLS, SPRITE_OFFSET, VIEW_DISTANCE, ZOMBIE_SHEET_TOTAL_WIDTH, ZOMBIE_STILL_SPRITE_OFFSET};
use crate::game::get_random_bool;
use crate::gfx_app::{ColorFormat, DepthFormat};
use crate::graphics::{camera::CameraInputState, can_move_to_tile, check_terrain_elevation, direction, direction_movement, direction_movement_180, distance, GameTime, get_nearest_random_tile_position, orientation::{Orientation, Stance}, orientation_to_direction, overlaps};
use crate::graphics::dimensions::{Dimensions, get_projection, get_view_matrix};
use crate::graphics::mesh::{Geometry, RectangularTexturedMesh};
use crate::graphics::texture::{load_texture, Texture};
use crate::shaders::{CharacterSheet, critter_pipeline, Position, Projection};
use crate::terrain::path_finding::calc_next_movement;
use crate::zombie::zombies::Zombies;

pub mod zombies;

const SHADER_VERT: &[u8] = include_bytes!("../shaders/character.v.glsl");
const SHADER_FRAG: &[u8] = include_bytes!("../shaders/character.f.glsl");

pub struct ZombieDrawable {
  projection: Projection,
  pub position: Position,
  previous_position: Position,
  previous_elevation: f32,
  orientation: Orientation,
  pub stance: Stance,
  direction: Orientation,
  last_decision: i64,
  pub movement_direction: Point2<f32>,
  zombie_idx: usize,
  zombie_death_idx: usize,
  movement_speed: f32,
  health: f32,
}

impl ZombieDrawable {
  pub fn new(position: Position) -> ZombieDrawable {
    let view = get_view_matrix(VIEW_DISTANCE);
    let projection = get_projection(view, ASPECT_RATIO);
    ZombieDrawable {
      projection,
      position,
      previous_position: Position::origin(),
      orientation: Orientation::Left,
      stance: Stance::Still,
      direction: Orientation::Left,
      last_decision: -2,
      movement_direction: Point2::new(0.0, 0.0),
      previous_elevation: 0.0,
      zombie_idx: 0,
      zombie_death_idx: 0,
      movement_speed: 0.0,
      health: 1.0,
    }
  }

  pub fn update(&mut self, world_to_clip: &Projection, ci: &CharacterInputState, game_time: u64) {
    self.projection = *world_to_clip;

    let elevated_pos_y = check_terrain_elevation(ci.movement - self.position, &SMALL_HILLS);

    let offset_delta = ci.movement - self.previous_position;
    self.previous_position = ci.movement;

    let x_y_distance_to_player = self.position - offset_delta;

    let distance_to_player = distance(x_y_distance_to_player.x().abs(), x_y_distance_to_player.y().abs());

    let is_alive = self.health > 0.0 && self.stance != Stance::NormalDeath && self.stance != Stance::CriticalDeath;

    if is_alive {
      let zombie_pos = ci.movement - self.position;

      if distance_to_player < 400.0 {
        let dir = calc_next_movement(zombie_pos, self.previous_position) as f32;
        self.direction = orientation_to_direction(dir);
        self.movement_direction = direction_movement(dir);
        self.stance = Stance::Running;
        self.movement_speed = 2.4 * self.health;
      } else {
        self.idle_direction_movement(zombie_pos, game_time as i64);
        self.movement_speed = 1.2 * self.health;
      }
    } else {
      self.movement_direction = Point2::new(0.0, 0.0);
    }

    self.position = Position::new(self.position.position[0] + self.movement_direction.x * self.movement_speed,
                                  self.position.position[1] + (elevated_pos_y - self.previous_elevation) + self.movement_direction.y * self.movement_speed) + offset_delta;
    self.previous_elevation = elevated_pos_y;

  }

  fn idle_direction_movement(&mut self, zombie_pos: Position, game_time: i64) {
    if !can_move_to_tile(zombie_pos) {
      let dir = direction(self.movement_direction, Point2::new(0.0, 0.0));
      self.movement_direction = direction_movement_180(self.movement_direction);
      self.orientation = orientation_to_direction(dir);
      self.direction = orientation_to_direction(dir);
    }

    if self.last_decision + 2 < game_time {
      self.stance = Stance::Walking;
      self.last_decision = game_time;
      let end_point = get_nearest_random_tile_position(zombie_pos);
      let dir = calc_next_movement(zombie_pos, end_point) as f32;
      self.movement_direction = direction_movement(dir);
      self.direction = orientation_to_direction(dir);
    }
  }

  fn handle_bullet_hit(&mut self) {
    self.health -= 0.5;
    if self.health <= 0.0 {
      self.stance =
        if get_random_bool() {
          Stance::NormalDeath
        } else {
          Stance::CriticalDeath
        };
    }
  }

  fn check_bullet_hits(&mut self, bullets: &[BulletDrawable]) {
    bullets.iter().for_each(|bullet| {
      if overlaps(self.position, bullet.position, 15.0, 15.0) && self.stance != Stance::NormalDeath && self.stance != Stance::CriticalDeath {
        self.handle_bullet_hit()
      }
    });
  }

  pub fn update_alive_idx(&mut self, max_idx: usize) {
    if self.zombie_idx < max_idx {
      self.zombie_idx += 1;
    } else {
      self.zombie_idx = 0;
    }
  }

  pub fn update_death_idx(&mut self, max_idx: usize) {
    if self.zombie_death_idx < max_idx {
      self.zombie_death_idx += 1;
    }
  }
}

pub struct ZombieDrawSystem<R: gfx::Resources> {
  bundle: gfx::pso::bundle::Bundle<R, critter_pipeline::Data<R>>,
  data: Vec<CritterData>,
}

impl<R: gfx::Resources> ZombieDrawSystem<R> {
  pub fn new<F>(factory: &mut F,
                rtv: gfx::handle::RenderTargetView<R, ColorFormat>,
                dsv: gfx::handle::DepthStencilView<R, DepthFormat>) -> ZombieDrawSystem<R>
    where F: gfx::Factory<R> {
    use gfx::traits::FactoryExt;

    let zombie_bytes = include_bytes!("../../assets/zombie.png");
    let char_texture = load_texture(factory, zombie_bytes);

    let rect_mesh =
      RectangularTexturedMesh::new(factory, Texture::new(char_texture, None), Geometry::Rectangle, Point2::new(25.0, 35.0), None, None, None);

    let pso =
      factory.create_pipeline_simple(SHADER_VERT, SHADER_FRAG, critter_pipeline::new())
        .expect("Zombie shader loading error");

    let pipeline_data = critter_pipeline::Data {
      vbuf: rect_mesh.mesh.vertex_buffer,
      projection_cb: factory.create_constant_buffer(1),
      position_cb: factory.create_constant_buffer(1),
      character_sprite_cb: factory.create_constant_buffer(1),
      charactersheet: (rect_mesh.mesh.texture.raw, factory.create_sampler_linear()),
      out_color: rtv,
      out_depth: dsv,
    };

    let data = data::load_zombie();

    ZombieDrawSystem {
      bundle: gfx::Bundle::new(rect_mesh.mesh.slice, pso, pipeline_data),
      data,
    }
  }

  fn get_next_sprite(&self, drawable: &mut ZombieDrawable) -> CharacterSheet {
    let sprite_idx = match drawable.stance {
      Stance::Still => {
        (drawable.direction as usize * 4 + drawable.zombie_idx)
      }
      Stance::Walking if drawable.orientation != Orientation::Normal => {
        (drawable.direction as usize * 8 + drawable.zombie_idx + ZOMBIE_STILL_SPRITE_OFFSET)
      }
      Stance::Running if drawable.orientation != Orientation::Normal => {
        (drawable.direction as usize * 8 + drawable.zombie_idx + ZOMBIE_STILL_SPRITE_OFFSET)
      }
      Stance::NormalDeath if drawable.orientation != Orientation::Normal => {
        (drawable.direction as usize * 6 + drawable.zombie_death_idx + NORMAL_DEATH_SPRITE_OFFSET)
      }
      Stance::CriticalDeath if drawable.orientation != Orientation::Normal => {
        (drawable.direction as usize * 8 + drawable.zombie_death_idx)
      }
      _ => {
        drawable.direction = drawable.orientation;
        (drawable.orientation as usize * 8 + drawable.zombie_idx + ZOMBIE_STILL_SPRITE_OFFSET)
      }
    } as usize;

    let (y_div, row_idx) =
      if drawable.stance == Stance::NormalDeath || drawable.stance == Stance::CriticalDeath {
        (0.0, 2)
      } else {
        (1.0, 2)
      };

    let elements_x = ZOMBIE_SHEET_TOTAL_WIDTH / (self.data[sprite_idx].data[2] + SPRITE_OFFSET);
    CharacterSheet {
      x_div: elements_x,
      y_div,
      row_idx,
      index: sprite_idx as f32,
    }
  }

  pub fn draw<C>(&mut self,
                 mut drawable: &mut ZombieDrawable,
                 encoder: &mut gfx::Encoder<R, C>)
    where C: gfx::CommandBuffer<R> {
    encoder.update_constant_buffer(&self.bundle.data.projection_cb, &drawable.projection);
    encoder.update_constant_buffer(&self.bundle.data.position_cb, &drawable.position);
    encoder.update_constant_buffer(&self.bundle.data.character_sprite_cb,
                                   &self.get_next_sprite(&mut drawable));
    self.bundle.encode(encoder);
  }
}

pub struct PreDrawSystem;

impl<'a> specs::prelude::System<'a> for PreDrawSystem {
  type SystemData = (WriteStorage<'a, Zombies>,
                     ReadStorage<'a, CameraInputState>,
                     ReadStorage<'a, CharacterInputState>,
                     ReadStorage<'a, Bullets>,
                     Read<'a, Dimensions>,
                     Read<'a, GameTime>);

  fn run(&mut self, (mut zombies, camera_input, character_input, bullets, dim, gt): Self::SystemData) {
    use specs::join::Join;

    for (zs, camera, ci, bs) in (&mut zombies, &camera_input, &character_input, &bullets).join() {
      let world_to_clip = dim.world_to_projection(camera);

      for z in &mut zs.zombies {
        z.update(&world_to_clip, ci, gt.0);
        z.check_bullet_hits(&bs.bullets);
      }
    }
  }
}
<|endoftext|>


<commit_before>use super::{
    LENGTH_BYTES,
    MAX_LIST_SIZE,
};

#[derive(Debug)]
pub enum EncodeError {
    ListTooLong,
}

pub trait Encodable {
    fn ssz_append(&self, s: &mut SszStream);
}

/// Provides a buffer for appending ssz-encodable values.
///
/// Use the `append()` fn to add a value to a list, then use
/// the `drain()` method to consume the struct and return the
/// ssz encoded bytes.
pub struct SszStream {
    buffer: Vec<u8>
}

impl SszStream {
    /// Create a new, empty stream for writing ssz values.
    pub fn new() -> Self {
        SszStream {
            buffer: Vec::new()
        }
    }

    /// Append some ssz encodable value to the stream.
    pub fn append<E>(&mut self, value: &E) -> &mut Self
        where E: Encodable
    {
        value.ssz_append(self);
        self
    }

    /// Append some ssz encoded bytes to the stream.
    ///
    /// The length of the supplied bytes will be concatenated
    /// to the stream before the supplied bytes.
    pub fn append_encoded_val(&mut self, vec: &Vec<u8>) {
        self.buffer.extend_from_slice(
            &encode_length(vec.len(),
            LENGTH_BYTES));
        self.buffer.extend_from_slice(&vec);
    }

    /// Append some ssz encoded bytes to the stream without calculating length
    ///
    /// The raw bytes will be concatenated to the stream.
    pub fn append_encoded_raw(&mut self, vec: &Vec<u8>) {
        self.buffer.extend_from_slice(&vec);
    }

    /// Append some vector (list) of encodable values to the stream.
    ///
    /// The length of the list will be concatenated to the stream, then
    /// each item in the vector will be encoded and concatenated.
    pub fn append_vec<E>(&mut self, vec: &Vec<E>)
        -> Result<(), EncodeError>
        where E: Encodable
    {
        let mut list_stream = SszStream::new();
        for item in vec {
            item.ssz_append(&mut list_stream);
        }
        let list_ssz = list_stream.drain();
        if list_ssz.len() <= MAX_LIST_SIZE {
            self.append_encoded_val(&list_ssz);
            Ok(())
        } else {
            Err(EncodeError::ListTooLong)
        }
    }

    /// Consume the stream and return the underlying bytes.
    pub fn drain(self) -> Vec<u8> {
        self.buffer
    }
}

/// Encode some length into a ssz size prefix.
///
/// The ssz size prefix is 4 bytes, which is treated as a continuious
/// 32bit big-endian integer.
pub fn encode_length(len: usize, length_bytes: usize) -> Vec<u8> {
    assert!(length_bytes > 0);  // For sanity
    assert!((len as usize) < 2usize.pow(length_bytes as u32 * 8));
    let mut header: Vec<u8> = vec![0; length_bytes];
    for i in 0..length_bytes {
        let offset = (length_bytes - i - 1) * 8;
        header[i] = ((len >> offset) & 0xff) as u8;
    };
    header
}

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

    #[test]
    #[should_panic]
    fn test_encode_length_0_bytes_panic() {
        encode_length(0, 0);
    }

    #[test]
    fn test_encode_length_4_bytes() {
        assert_eq!(
            encode_length(0, LENGTH_BYTES),
            vec![0; 4]
        );
        assert_eq!(
            encode_length(1, LENGTH_BYTES),
            vec![0, 0, 0, 1]
        );
        assert_eq!(
            encode_length(255, LENGTH_BYTES),
            vec![0, 0, 0, 255]
        );
        assert_eq!(
            encode_length(256, LENGTH_BYTES),
            vec![0, 0, 1, 0]
        );
        assert_eq!(
            encode_length(4294967295, LENGTH_BYTES),  // 2^(3*8) - 1
            vec![255, 255, 255, 255]
        );
    }

    #[test]
    #[should_panic]
    fn test_encode_length_4_bytes_panic() {
        encode_length(4294967296, LENGTH_BYTES);  // 2^(3*8)
    }

    #[test]
    fn test_encode_list() {
        let test_vec: Vec<u16> = vec![256; 12];
        let mut stream = SszStream::new();
        stream.append_vec(&test_vec).unwrap();
        let ssz = stream.drain();

        assert_eq!(ssz.len(), 4 + (12 * 2));
        assert_eq!(ssz[0..4], *vec![0, 0, 0, 24]);
        assert_eq!(ssz[4..6], *vec![1, 0]);
    }
}
<commit_msg>Remove Result from list encoding, panic instead<commit_after>use super::{
    LENGTH_BYTES,
    MAX_LIST_SIZE,
};

#[derive(Debug)]
pub enum EncodeError {
    ListTooLong,
}

pub trait Encodable {
    fn ssz_append(&self, s: &mut SszStream);
}

/// Provides a buffer for appending ssz-encodable values.
///
/// Use the `append()` fn to add a value to a list, then use
/// the `drain()` method to consume the struct and return the
/// ssz encoded bytes.
pub struct SszStream {
    buffer: Vec<u8>
}

impl SszStream {
    /// Create a new, empty stream for writing ssz values.
    pub fn new() -> Self {
        SszStream {
            buffer: Vec::new()
        }
    }

    /// Append some ssz encodable value to the stream.
    pub fn append<E>(&mut self, value: &E) -> &mut Self
        where E: Encodable
    {
        value.ssz_append(self);
        self
    }

    /// Append some ssz encoded bytes to the stream.
    ///
    /// The length of the supplied bytes will be concatenated
    /// to the stream before the supplied bytes.
    pub fn append_encoded_val(&mut self, vec: &Vec<u8>) {
        self.buffer.extend_from_slice(
            &encode_length(vec.len(),
            LENGTH_BYTES));
        self.buffer.extend_from_slice(&vec);
    }

    /// Append some ssz encoded bytes to the stream without calculating length
    ///
    /// The raw bytes will be concatenated to the stream.
    pub fn append_encoded_raw(&mut self, vec: &Vec<u8>) {
        self.buffer.extend_from_slice(&vec);
    }

    /// Append some vector (list) of encodable values to the stream.
    ///
    /// The length of the list will be concatenated to the stream, then
    /// each item in the vector will be encoded and concatenated.
    pub fn append_vec<E>(&mut self, vec: &Vec<E>)
        where E: Encodable
    {
        let mut list_stream = SszStream::new();
        for item in vec {
            item.ssz_append(&mut list_stream);
        }
        self.append_encoded_val(&list_stream.drain());
    }

    /// Consume the stream and return the underlying bytes.
    pub fn drain(self) -> Vec<u8> {
        self.buffer
    }
}

/// Encode some length into a ssz size prefix.
///
/// The ssz size prefix is 4 bytes, which is treated as a continuious
/// 32bit big-endian integer.
pub fn encode_length(len: usize, length_bytes: usize) -> Vec<u8> {
    assert!(length_bytes > 0);  // For sanity
    assert!((len as usize) < 2usize.pow(length_bytes as u32 * 8));
    let mut header: Vec<u8> = vec![0; length_bytes];
    for i in 0..length_bytes {
        let offset = (length_bytes - i - 1) * 8;
        header[i] = ((len >> offset) & 0xff) as u8;
    };
    header
}

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

    #[test]
    #[should_panic]
    fn test_encode_length_0_bytes_panic() {
        encode_length(0, 0);
    }

    #[test]
    fn test_encode_length_4_bytes() {
        assert_eq!(
            encode_length(0, LENGTH_BYTES),
            vec![0; 4]
        );
        assert_eq!(
            encode_length(1, LENGTH_BYTES),
            vec![0, 0, 0, 1]
        );
        assert_eq!(
            encode_length(255, LENGTH_BYTES),
            vec![0, 0, 0, 255]
        );
        assert_eq!(
            encode_length(256, LENGTH_BYTES),
            vec![0, 0, 1, 0]
        );
        assert_eq!(
            encode_length(4294967295, LENGTH_BYTES),  // 2^(3*8) - 1
            vec![255, 255, 255, 255]
        );
    }

    #[test]
    #[should_panic]
    fn test_encode_length_4_bytes_panic() {
        encode_length(4294967296, LENGTH_BYTES);  // 2^(3*8)
    }

    #[test]
    fn test_encode_list() {
        let test_vec: Vec<u16> = vec![256; 12];
        let mut stream = SszStream::new();
        stream.append_vec(&test_vec);
        let ssz = stream.drain();

        assert_eq!(ssz.len(), 4 + (12 * 2));
        assert_eq!(ssz[0..4], *vec![0, 0, 0, 24]);
        assert_eq!(ssz[4..6], *vec![1, 0]);
    }
}
<|endoftext|>


<commit_before>extern mod extra;
use extra::sort;
use std::hashmap::HashMap;

struct School ( HashMap<uint, ~[~str]> );

fn sorted<T: Clone + Ord>(array: &[T]) -> ~[T] {
    let mut res = array.iter().map(|v| v.clone()).to_owned_vec();
    sort::tim_sort(res);
    res
}

impl School {
    pub fn new() -> School {
        School(HashMap::new())
    }

    pub fn add(self, grade: uint, student: &str) -> School {
        let mut m = *self;
        m.mangle(
            grade,
            student,
            |_, x| ~[x.into_owned()],
            |_, xs, x| xs.push(x.into_owned()));
        School(m)
    }

    pub fn sorted(self) -> ~[(uint, ~[~str])] {
        sorted((*self).iter().map(|(&grade, students)| {
            (grade, sorted(students.clone()))
        }).to_owned_vec())
    }

    pub fn grade(self, grade: uint) -> ~[~str] {
        let default: ~[~str] = ~[];
        sorted(*(*self).find(&grade).unwrap_or(&default))
    }
}
<commit_msg>rust grade-school - use a named field in School, clean up grade(uint)<commit_after>extern mod extra;
use extra::sort;
use std::hashmap::HashMap;

struct School {
    priv grades: HashMap<uint, ~[~str]>
}

fn sorted<T: Clone + Ord>(array: &[T]) -> ~[T] {
    let mut res = array.iter().map(|v| v.clone()).to_owned_vec();
    sort::tim_sort(res);
    res
}

impl School {
    pub fn new() -> School {
        School { grades: HashMap::new() }
    }

    pub fn add(self, grade: uint, student: &str) -> School {
        let mut s = self;
        s.grades.mangle(
            grade,
            student,
            |_, x| ~[x.into_owned()],
            |_, xs, x| xs.push(x.into_owned()));
        s
    }

    pub fn sorted(self) -> ~[(uint, ~[~str])] {
        sorted(self.grades.iter().map(|(&grade, students)| {
            (grade, sorted(students.clone()))
        }).to_owned_vec())
    }

    pub fn grade(self, grade: uint) -> ~[~str] {
        self.grades.find(&grade).map(|&v| sorted(v.to_owned())).unwrap_or(~[])
    }
}
<|endoftext|>


<commit_before>//! Utilities for working with the WTF-8 encoding.
//!
//! WTF-8 is a hack that allows to represent potentially ill-formed UTF-16 byte
//! strings in a way somewhat compatible with UTF-8. Such bugs UTF-16 strings
//! can be found for example in Windows paths for legacy reasons.
//!
//! See the official [WTF-8][wtf8] specification for more information.
//!
//! [wtf8]: https://simonsapin.github.io/wtf-8

/// Converts the given potentially ill-formed UTF-16 into a WTF-8 byte sequence.
///
/// If the given byte sequence is a well-formed UTF-16 string, then the result
/// is guaranteed to be a valid UTF-8string.
pub fn from_ill_formed_utf16<I>(units: I) -> Vec<u8>
where
    I: Iterator<Item = u16>,
{
    let mut res = Vec::new();

    let mut iter = units.peekable();
    loop {
        let unit = match iter.next() {
            Some(unit) => unit,
            None => return res,
        };

        // Potentially ill-formed UTF-16 to code point conversion based on [1].
        //
        // [1]: https://simonsapin.github.io/wtf-8/#decoding-ill-formed-utf-16
        let mut point = unit as u32;
        if is_lead_surrogate(&unit) {
            if let Some(next) = iter.next_if(is_trail_surrogate) {
                let lead = (unit as u32 - 0xD800) << 10;
                let trail = next as u32 - 0xDC00;
                point = 0x10000 + lead + trail;
            }
        }

        // Code point to WTF-8 encoding procedure based on [1].
        //
        // [1]: https://simonsapin.github.io/wtf-8/#encoding-wtf-8
        match point {
            0x0000..=0x007F => {
                res.push(point as u8);
            }
            0x0080..=0x07FF => {
                res.push(0xC0 | (point >> 6) as u8);
                res.push(0x80 | (point & 0x3F) as u8);
            }
            0x0800..=0xFFFF => {
                res.push(0xE0 | (point >> 12) as u8);
                res.push(0x80 | ((point >> 6) as u8 & 0x3F));
                res.push(0x80 | (point & 0x3F) as u8);
            }
            0x10000..=0x10FFFF => {
                res.push(0xF0 | (point >> 18) as u8);
                res.push(0x80 | ((point >> 12) as u8 & 0x3F));
                res.push(0x80 | ((point >> 6) as u8 & 0x3F));
                res.push(0x80 | (point & 0x3F) as u8);
            }
            _ => unreachable!(), // Not possible by construction.
        }
    }
}

/// Converts the given WTF-8 byte sequence into potentially ill-formed UTF-16.
///
/// If the given byte sequence is a valid UTF-8 string, then the result is
/// guaranteed to be a well-formed UTF-16 string.
///
/// If the input is not a valid WTF-8 byte sequence, an error is returned.
pub fn into_ill_formed_utf16<I>(units: I) -> Result<Vec<u16>, ParseError>
where
    I: Iterator<Item = u8>
{
    let mut res = Vec::new();

    let mut iter = units.peekable();
    loop {
        let byte1 = match iter.next() {
            Some(byte1) => byte1,
            None => return Ok(res),
        };

        // WTF-8 to a code point decoding procedure based on [1].
        //
        // [1]: https://simonsapin.github.io/wtf-8/#decoding-wtf-8
        let mut point = 0;
        match byte1 {
            0x00..=0x7F => {
                point += byte1 as u32;
            }
            0xC2..=0xDF => {
                let byte2 = iter.next().ok_or(ParseError::UnexpectedEnd)?;
                point += ((byte1 & 0x1F) as u32) << 6;
                point += ((byte2 & 0x3F) as u32) << 0;
            }
            0xE0..=0xEF => {
                let byte2 = iter.next().ok_or(ParseError::UnexpectedEnd)?;
                let byte3 = iter.next().ok_or(ParseError::UnexpectedEnd)?;
                point += ((byte1 & 0x0F) as u32) << 12;
                point += ((byte2 & 0x3F) as u32) << 6;
                point += ((byte3 & 0x3F) as u32) << 0;
            }
            0xF0..=0xF4 => {
                let byte2 = iter.next().ok_or(ParseError::UnexpectedEnd)?;
                let byte3 = iter.next().ok_or(ParseError::UnexpectedEnd)?;
                let byte4 = iter.next().ok_or(ParseError::UnexpectedEnd)?;
                point += ((byte1 & 0x07) as u32) << 18;
                point += ((byte2 & 0x3F) as u32) << 12;
                point += ((byte3 & 0x3F) as u32) << 6;
                point += ((byte4 & 0x3F) as u32) << 0;
            }
            _ => return Err(ParseError::IllegalByte(byte1)),
        }

        // Code point to potentially ill-formed UTF-16 coversion based on [1].
        //
        // [1]: https://simonsapin.github.io/wtf-8/#encoding-ill-formed-utf-16
        if is_supplementary(&point) {
            res.push(((point - 0x10000) >> 10) as u16 + 0xD800);
            res.push(((point - 0x10000) as u16 & 0x3FF) + 0xDC00);
        } else {
            res.push(point as u16);
        }
    }
}

/// Determines whether the given UTF-16 code unit is a lead surrogate.
#[inline]
fn is_lead_surrogate(unit: &u16) -> bool {
    matches!(unit, 0xD800..=0xDBFF)
}

/// Determines whether the given UTF-16 code unit is a trail surrogate.
#[inline]
fn is_trail_surrogate(unit: &u16) -> bool {
    matches!(unit, 0xDC00..=0xDFFF)
}

/// Determines whether the given code point is in the supplementary.
#[inline]
fn is_supplementary(point: &u32) -> bool {
    matches!(point, 0x10000..=0x10FFFF)
}

/// An error type for failures related to WTF-8 parsing.
#[derive(Debug, PartialEq, Eq)]
pub enum ParseError {
    /// The input ended but more bytes were expected.
    UnexpectedEnd,
    /// There was an illegal byte in the input.
    IllegalByte(u8),
}

impl std::fmt::Display for ParseError {

    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(fmt, "invalid wtf-8: ")?;

        use ParseError::*;
        match self {
            UnexpectedEnd => write!(fmt, "unexpected end"),
            IllegalByte(byte) => write!(fmt, "illegal byte: {:#02x}", byte),
        }
    }
}

impl std::error::Error for ParseError {

    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        None
    }
}

#[cfg(test)]
mod tests {

    use super::*;

    use quickcheck_macros::quickcheck;

    #[test]
    fn from_empty() {
        assert_eq!(from_ill_formed_utf16(std::iter::empty()), b"");
    }

    #[test]
    fn from_small_bytes() {
        let bytes_utf16 = vec![0x04, 0x08, 0x16, 0x23, 0x42];
        let bytes_wtf8 = from_ill_formed_utf16(bytes_utf16.into_iter());
        assert_eq!(bytes_wtf8, b"\x04\x08\x16\x23\x42");
    }

    #[test]
    fn from_bmp_bytes() {
        let bytes_utf16 = vec![0xABCD, 0xBEEF, 0xFFFF];
        let bytes_wtf8 = from_ill_formed_utf16(bytes_utf16.into_iter());
        assert_eq!(bytes_wtf8, b"\xEA\xAF\x8D\xEB\xBB\xAF\xEF\xBF\xBF");
    }

    #[test]
    fn from_sup_bytes() {
        let bytes_utf16 = vec![0xD800, 0xDC00, 0xDBFF, 0xDFFF];
        let bytes_wtf8 = from_ill_formed_utf16(bytes_utf16.into_iter());
        assert_eq!(bytes_wtf8, b"\xF0\x90\x80\x80\xF4\x8F\xBF\xBF");
    }

    #[test]
    fn from_ascii_string() {
        let string = "foo bar baz";
        let string_wtf8 = from_ill_formed_utf16(string.encode_utf16());
        let string_utf8 = string.as_bytes();
        assert_eq!(string_wtf8, string_utf8);
    }

    #[test]
    fn from_unicode_string() {
        let string = "zażółć gęślą jaźń";
        let string_wtf8 = from_ill_formed_utf16(string.encode_utf16());
        let string_utf8 = string.as_bytes();
        assert_eq!(string_wtf8, string_utf8);
    }

    #[test]
    fn into_empty() {
        assert_eq!(into_ill_formed_utf16(std::iter::empty()), Ok(vec![]));
    }

    #[test]
    fn into_ascii_string() {
        let string = "foo bar baz";
        let string_wtf8 = into_ill_formed_utf16(string.bytes());
        let string_utf16 = string.encode_utf16().collect::<Vec<_>>();
        assert_eq!(string_wtf8, Ok(string_utf16));
    }

    #[test]
    fn into_unicode_string() {
        let string = "zażółć gęślą jaźń";
        let string_wtf8 = into_ill_formed_utf16(string.bytes());
        let string_utf16 = string.encode_utf16().collect::<Vec<_>>();
        assert_eq!(string_wtf8, Ok(string_utf16));
    }

    // TODO: Write `into` tests that cover failure scanarios.

    #[quickcheck]
    fn from_any_string(input: String) {
        let units = input.encode_utf16();

        let string_wtf8 = from_ill_formed_utf16(units);
        let string_utf8 = input.as_bytes();
        assert_eq!(string_wtf8, string_utf8);
    }

    #[quickcheck]
    fn into_from_any_string(input: String) {
        let units = input.bytes();

        let string_utf16 = into_ill_formed_utf16(units);
        let string_wtf8 = from_ill_formed_utf16(string_utf16.into_iter());
        assert_eq!(String::from_utf8(string_wtf8).unwrap(), input);
    }

    #[quickcheck]
    fn from_into_any_units(input: Vec<u16>) {
        let units = input.iter().map(|unit| *unit);

        let string_wtf8 = from_ill_formed_utf16(units);
        let string_utf16 = into_ill_formed_utf16(string_wtf8.into_iter());
        assert_eq!(string_utf16, input);
    }
}
<commit_msg>Fix compilation errors in WTF-8 tests.<commit_after>//! Utilities for working with the WTF-8 encoding.
//!
//! WTF-8 is a hack that allows to represent potentially ill-formed UTF-16 byte
//! strings in a way somewhat compatible with UTF-8. Such bugs UTF-16 strings
//! can be found for example in Windows paths for legacy reasons.
//!
//! See the official [WTF-8][wtf8] specification for more information.
//!
//! [wtf8]: https://simonsapin.github.io/wtf-8

/// Converts the given potentially ill-formed UTF-16 into a WTF-8 byte sequence.
///
/// If the given byte sequence is a well-formed UTF-16 string, then the result
/// is guaranteed to be a valid UTF-8string.
pub fn from_ill_formed_utf16<I>(units: I) -> Vec<u8>
where
    I: Iterator<Item = u16>,
{
    let mut res = Vec::new();

    let mut iter = units.peekable();
    loop {
        let unit = match iter.next() {
            Some(unit) => unit,
            None => return res,
        };

        // Potentially ill-formed UTF-16 to code point conversion based on [1].
        //
        // [1]: https://simonsapin.github.io/wtf-8/#decoding-ill-formed-utf-16
        let mut point = unit as u32;
        if is_lead_surrogate(&unit) {
            if let Some(next) = iter.next_if(is_trail_surrogate) {
                let lead = (unit as u32 - 0xD800) << 10;
                let trail = next as u32 - 0xDC00;
                point = 0x10000 + lead + trail;
            }
        }

        // Code point to WTF-8 encoding procedure based on [1].
        //
        // [1]: https://simonsapin.github.io/wtf-8/#encoding-wtf-8
        match point {
            0x0000..=0x007F => {
                res.push(point as u8);
            }
            0x0080..=0x07FF => {
                res.push(0xC0 | (point >> 6) as u8);
                res.push(0x80 | (point & 0x3F) as u8);
            }
            0x0800..=0xFFFF => {
                res.push(0xE0 | (point >> 12) as u8);
                res.push(0x80 | ((point >> 6) as u8 & 0x3F));
                res.push(0x80 | (point & 0x3F) as u8);
            }
            0x10000..=0x10FFFF => {
                res.push(0xF0 | (point >> 18) as u8);
                res.push(0x80 | ((point >> 12) as u8 & 0x3F));
                res.push(0x80 | ((point >> 6) as u8 & 0x3F));
                res.push(0x80 | (point & 0x3F) as u8);
            }
            _ => unreachable!(), // Not possible by construction.
        }
    }
}

/// Converts the given WTF-8 byte sequence into potentially ill-formed UTF-16.
///
/// If the given byte sequence is a valid UTF-8 string, then the result is
/// guaranteed to be a well-formed UTF-16 string.
///
/// If the input is not a valid WTF-8 byte sequence, an error is returned.
pub fn into_ill_formed_utf16<I>(units: I) -> Result<Vec<u16>, ParseError>
where
    I: Iterator<Item = u8>
{
    let mut res = Vec::new();

    let mut iter = units.peekable();
    loop {
        let byte1 = match iter.next() {
            Some(byte1) => byte1,
            None => return Ok(res),
        };

        // WTF-8 to a code point decoding procedure based on [1].
        //
        // [1]: https://simonsapin.github.io/wtf-8/#decoding-wtf-8
        let mut point = 0;
        match byte1 {
            0x00..=0x7F => {
                point += byte1 as u32;
            }
            0xC2..=0xDF => {
                let byte2 = iter.next().ok_or(ParseError::UnexpectedEnd)?;
                point += ((byte1 & 0x1F) as u32) << 6;
                point += ((byte2 & 0x3F) as u32) << 0;
            }
            0xE0..=0xEF => {
                let byte2 = iter.next().ok_or(ParseError::UnexpectedEnd)?;
                let byte3 = iter.next().ok_or(ParseError::UnexpectedEnd)?;
                point += ((byte1 & 0x0F) as u32) << 12;
                point += ((byte2 & 0x3F) as u32) << 6;
                point += ((byte3 & 0x3F) as u32) << 0;
            }
            0xF0..=0xF4 => {
                let byte2 = iter.next().ok_or(ParseError::UnexpectedEnd)?;
                let byte3 = iter.next().ok_or(ParseError::UnexpectedEnd)?;
                let byte4 = iter.next().ok_or(ParseError::UnexpectedEnd)?;
                point += ((byte1 & 0x07) as u32) << 18;
                point += ((byte2 & 0x3F) as u32) << 12;
                point += ((byte3 & 0x3F) as u32) << 6;
                point += ((byte4 & 0x3F) as u32) << 0;
            }
            _ => return Err(ParseError::IllegalByte(byte1)),
        }

        // Code point to potentially ill-formed UTF-16 coversion based on [1].
        //
        // [1]: https://simonsapin.github.io/wtf-8/#encoding-ill-formed-utf-16
        if is_supplementary(&point) {
            res.push(((point - 0x10000) >> 10) as u16 + 0xD800);
            res.push(((point - 0x10000) as u16 & 0x3FF) + 0xDC00);
        } else {
            res.push(point as u16);
        }
    }
}

/// Determines whether the given UTF-16 code unit is a lead surrogate.
#[inline]
fn is_lead_surrogate(unit: &u16) -> bool {
    matches!(unit, 0xD800..=0xDBFF)
}

/// Determines whether the given UTF-16 code unit is a trail surrogate.
#[inline]
fn is_trail_surrogate(unit: &u16) -> bool {
    matches!(unit, 0xDC00..=0xDFFF)
}

/// Determines whether the given code point is in the supplementary.
#[inline]
fn is_supplementary(point: &u32) -> bool {
    matches!(point, 0x10000..=0x10FFFF)
}

/// An error type for failures related to WTF-8 parsing.
#[derive(Debug, PartialEq, Eq)]
pub enum ParseError {
    /// The input ended but more bytes were expected.
    UnexpectedEnd,
    /// There was an illegal byte in the input.
    IllegalByte(u8),
}

impl std::fmt::Display for ParseError {

    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(fmt, "invalid wtf-8: ")?;

        use ParseError::*;
        match self {
            UnexpectedEnd => write!(fmt, "unexpected end"),
            IllegalByte(byte) => write!(fmt, "illegal byte: {:#02x}", byte),
        }
    }
}

impl std::error::Error for ParseError {

    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        None
    }
}

#[cfg(test)]
mod tests {

    use super::*;

    use quickcheck_macros::quickcheck;

    #[test]
    fn from_empty() {
        assert_eq!(from_ill_formed_utf16(std::iter::empty()), b"");
    }

    #[test]
    fn from_small_bytes() {
        let bytes_utf16 = vec![0x04, 0x08, 0x16, 0x23, 0x42];
        let bytes_wtf8 = from_ill_formed_utf16(bytes_utf16.into_iter());
        assert_eq!(bytes_wtf8, b"\x04\x08\x16\x23\x42");
    }

    #[test]
    fn from_bmp_bytes() {
        let bytes_utf16 = vec![0xABCD, 0xBEEF, 0xFFFF];
        let bytes_wtf8 = from_ill_formed_utf16(bytes_utf16.into_iter());
        assert_eq!(bytes_wtf8, b"\xEA\xAF\x8D\xEB\xBB\xAF\xEF\xBF\xBF");
    }

    #[test]
    fn from_sup_bytes() {
        let bytes_utf16 = vec![0xD800, 0xDC00, 0xDBFF, 0xDFFF];
        let bytes_wtf8 = from_ill_formed_utf16(bytes_utf16.into_iter());
        assert_eq!(bytes_wtf8, b"\xF0\x90\x80\x80\xF4\x8F\xBF\xBF");
    }

    #[test]
    fn from_ascii_string() {
        let string = "foo bar baz";
        let string_wtf8 = from_ill_formed_utf16(string.encode_utf16());
        let string_utf8 = string.as_bytes();
        assert_eq!(string_wtf8, string_utf8);
    }

    #[test]
    fn from_unicode_string() {
        let string = "zażółć gęślą jaźń";
        let string_wtf8 = from_ill_formed_utf16(string.encode_utf16());
        let string_utf8 = string.as_bytes();
        assert_eq!(string_wtf8, string_utf8);
    }

    #[test]
    fn into_empty() {
        assert_eq!(into_ill_formed_utf16(std::iter::empty()), Ok(vec![]));
    }

    #[test]
    fn into_ascii_string() {
        let string = "foo bar baz";
        let string_wtf8 = into_ill_formed_utf16(string.bytes());
        let string_utf16 = string.encode_utf16().collect::<Vec<_>>();
        assert_eq!(string_wtf8, Ok(string_utf16));
    }

    #[test]
    fn into_unicode_string() {
        let string = "zażółć gęślą jaźń";
        let string_wtf8 = into_ill_formed_utf16(string.bytes());
        let string_utf16 = string.encode_utf16().collect::<Vec<_>>();
        assert_eq!(string_wtf8, Ok(string_utf16));
    }

    // TODO: Write `into` tests that cover failure scanarios.

    #[quickcheck]
    fn from_any_string(input: String) {
        let units = input.encode_utf16();

        let string_wtf8 = from_ill_formed_utf16(units);
        let string_utf8 = input.as_bytes();
        assert_eq!(string_wtf8, string_utf8);
    }

    #[quickcheck]
    fn into_from_any_string(input: String) {
        let units = input.bytes();

        let string_utf16 = into_ill_formed_utf16(units).unwrap();
        let string_wtf8 = from_ill_formed_utf16(string_utf16.into_iter());
        assert_eq!(String::from_utf8(string_wtf8).unwrap(), input);
    }

    #[quickcheck]
    fn from_into_any_units(input: Vec<u16>) {
        let units = input.iter().map(|unit| *unit);

        let string_wtf8 = from_ill_formed_utf16(units);
        let string_utf16 = into_ill_formed_utf16(string_wtf8.into_iter()).unwrap();
        assert_eq!(string_utf16, input);
    }
}
<|endoftext|>