<commit_before>//! This pass checks HIR bodies that may be evaluated at compile-time (e.g., `const`, `static`,
//! `const fn`) for structured control flow (e.g. `if`, `while`), which is forbidden in a const
//! context.
//!
//! By the time the MIR const-checker runs, these high-level constructs have been lowered to
//! control-flow primitives (e.g., `Goto`, `SwitchInt`), making it tough to properly attribute
//! errors. We still look for those primitives in the MIR const-checker to ensure nothing slips
//! through, but errors for structured control flow in a `const` should be emitted here.

use rustc::hir::def_id::DefId;
use rustc::hir::intravisit::{Visitor, NestedVisitorMap};
use rustc::hir::map::Map;
use rustc::hir;
use rustc::ty::TyCtxt;
use rustc::ty::query::Providers;
use rustc::session::config::nightly_options;
use syntax::ast::Mutability;
use syntax::feature_gate::feature_err;
use syntax::span_err;
use syntax_pos::{sym, Span, Symbol};
use rustc_error_codes::*;

use std::fmt;

/// An expression that is not *always* legal in a const context.
#[derive(Clone, Copy)]
enum NonConstExpr {
    Loop(hir::LoopSource),
    Match(hir::MatchSource),
}

impl NonConstExpr {
    fn name(self) -> &'static str {
        match self {
            Self::Loop(src) => src.name(),
            Self::Match(src) => src.name(),
        }
    }

    fn required_feature_gates(self) -> Option<&'static [Symbol]> {
        use hir::MatchSource::*;
        use hir::LoopSource::*;

        let gates: &[_] = match self {
            | Self::Match(Normal)
            | Self::Match(IfDesugar { .. })
            | Self::Match(IfLetDesugar { .. })
            => &[sym::const_if_match],

            | Self::Loop(Loop)
            => &[sym::const_loop],

            | Self::Loop(While)
            | Self::Loop(WhileLet)
            | Self::Match(WhileDesugar)
            | Self::Match(WhileLetDesugar)
            => &[sym::const_loop, sym::const_if_match],

            // `for` loops desugar to a call to `FromIterator::from_iterator`,
            // so they are not yet supported behind a feature flag.
            _ => return None,
        };

        Some(gates)
    }
}

#[derive(Copy, Clone)]
enum ConstKind {
    Static,
    StaticMut,
    ConstFn,
    Const,
    AnonConst,
}

impl ConstKind {
    fn for_body(body: &hir::Body, hir_map: &Map<'_>) -> Option<Self> {
        let is_const_fn = |id| hir_map.fn_sig_by_hir_id(id).unwrap().header.is_const();

        let owner = hir_map.body_owner(body.id());
        let const_kind = match hir_map.body_owner_kind(owner) {
            hir::BodyOwnerKind::Const => Self::Const,
            hir::BodyOwnerKind::Static(Mutability::Mutable) => Self::StaticMut,
            hir::BodyOwnerKind::Static(Mutability::Immutable) => Self::Static,

            hir::BodyOwnerKind::Fn if is_const_fn(owner) => Self::ConstFn,
            hir::BodyOwnerKind::Fn | hir::BodyOwnerKind::Closure => return None,
        };

        Some(const_kind)
    }
}

impl fmt::Display for ConstKind {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let s = match self {
            Self::Static => "static",
            Self::StaticMut => "static mut",
            Self::Const | Self::AnonConst => "const",
            Self::ConstFn => "const fn",
        };

        write!(f, "{}", s)
    }
}

fn check_mod_const_bodies(tcx: TyCtxt<'_>, module_def_id: DefId) {
    let mut vis = CheckConstVisitor::new(tcx);
    tcx.hir().visit_item_likes_in_module(module_def_id, &mut vis.as_deep_visitor());
}

pub(crate) fn provide(providers: &mut Providers<'_>) {
    *providers = Providers {
        check_mod_const_bodies,
        ..*providers
    };
}

#[derive(Copy, Clone)]
struct CheckConstVisitor<'tcx> {
    tcx: TyCtxt<'tcx>,
    const_kind: Option<ConstKind>,
}

impl<'tcx> CheckConstVisitor<'tcx> {
    fn new(tcx: TyCtxt<'tcx>) -> Self {
        CheckConstVisitor {
            tcx,
            const_kind: None,
        }
    }

    /// Emits an error when an unsupported expression is found in a const context.
    fn const_check_violated(&self, expr: NonConstExpr, span: Span) {
        let features = self.tcx.features();
        let gates = expr.required_feature_gates();
        match gates {
            // Don't emit an error if the user has enabled the requisite feature gates.
            Some(gates) if gates.iter().all(|&g| features[g]) => return,

            // `-Zunleash-the-miri-inside-of-you` only works for expressions that don't have a
            // corresponding feature gate. This encourages nightly users to use feature gates when
            // possible.
            None if self.tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you => {
                self.tcx.sess.span_warn(span, "skipping const checks");
                return;
            }

            _ => {}
        }

        let const_kind = self.const_kind
            .expect("`const_check_violated` may only be called inside a const context");
        let msg = format!("`{}` is not allowed in a `{}`", expr.name(), const_kind);

        let gates = gates.unwrap_or(&[]);
        let missing_gates: Vec<_> = gates
            .iter()
            .copied()
            .filter(|&g| !features[g])
            .collect();

        match missing_gates.as_slice() {
            &[] => span_err!(self.tcx.sess, span, E0744, "{}", msg),

            // If the user enabled `#![feature(const_loop)]` but not `#![feature(const_if_match)]`,
            // explain why their `while` loop is being rejected.
            &[gate @ sym::const_if_match] if gates.contains(&sym::const_loop) => {
                let mut err = feature_err(&self.tcx.sess.parse_sess, gate, span, &msg);
                err.note("`#![feature(const_loop)]` alone is not sufficient, \
                          since this loop expression contains an implicit conditional");
                err.emit();
            }

            &[missing_primary, ref missing_secondary @ ..] => {
                let mut err = feature_err(&self.tcx.sess.parse_sess, missing_primary, span, &msg);

                // If multiple feature gates would be required to enable this expression, include
                // them as help messages. Don't emit a separate error for each missing feature gate.
                //
                // FIXME(ecstaticmorse): Maybe this could be incorporated into `feature_err`? This
                // is a pretty narrow case, however.
                if nightly_options::is_nightly_build() {
                    for gate in missing_secondary {
                        let note = format!(
                            "add `#![feature({})]` to the crate attributes to enable",
                            gate,
                        );
                        err.help(&note);
                    }
                }

                err.emit();
            }
        }
    }

    /// Saves the parent `const_kind` before calling `f` and restores it afterwards.
    fn recurse_into(&mut self, kind: Option<ConstKind>, f: impl FnOnce(&mut Self)) {
        let parent_kind = self.const_kind;
        self.const_kind = kind;
        f(self);
        self.const_kind = parent_kind;
    }
}

impl<'tcx> Visitor<'tcx> for CheckConstVisitor<'tcx> {
    fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
        NestedVisitorMap::OnlyBodies(&self.tcx.hir())
    }

    fn visit_anon_const(&mut self, anon: &'tcx hir::AnonConst) {
        let kind = Some(ConstKind::AnonConst);
        self.recurse_into(kind, |this| hir::intravisit::walk_anon_const(this, anon));
    }

    fn visit_body(&mut self, body: &'tcx hir::Body) {
        let kind = ConstKind::for_body(body, self.tcx.hir());
        self.recurse_into(kind, |this| hir::intravisit::walk_body(this, body));
    }

    fn visit_expr(&mut self, e: &'tcx hir::Expr) {
        match &e.kind {
            // Skip the following checks if we are not currently in a const context.
            _ if self.const_kind.is_none() => {}

            hir::ExprKind::Loop(_, _, source) => {
                self.const_check_violated(NonConstExpr::Loop(*source), e.span);
            }

            hir::ExprKind::Match(_, _, source) => {
                let non_const_expr = match source {
                    // These are handled by `ExprKind::Loop` above.
                    | hir::MatchSource::WhileDesugar
                    | hir::MatchSource::WhileLetDesugar
                    | hir::MatchSource::ForLoopDesugar
                    => None,

                    _ => Some(NonConstExpr::Match(*source)),
                };

                if let Some(expr) = non_const_expr {
                    self.const_check_violated(expr, e.span);
                }
            }

            _ => {},
        }

        hir::intravisit::walk_expr(self, e);
    }
}
<commit_msg>Improve comment<commit_after>//! This pass checks HIR bodies that may be evaluated at compile-time (e.g., `const`, `static`,
//! `const fn`) for structured control flow (e.g. `if`, `while`), which is forbidden in a const
//! context.
//!
//! By the time the MIR const-checker runs, these high-level constructs have been lowered to
//! control-flow primitives (e.g., `Goto`, `SwitchInt`), making it tough to properly attribute
//! errors. We still look for those primitives in the MIR const-checker to ensure nothing slips
//! through, but errors for structured control flow in a `const` should be emitted here.

use rustc::hir::def_id::DefId;
use rustc::hir::intravisit::{Visitor, NestedVisitorMap};
use rustc::hir::map::Map;
use rustc::hir;
use rustc::ty::TyCtxt;
use rustc::ty::query::Providers;
use rustc::session::config::nightly_options;
use syntax::ast::Mutability;
use syntax::feature_gate::feature_err;
use syntax::span_err;
use syntax_pos::{sym, Span, Symbol};
use rustc_error_codes::*;

use std::fmt;

/// An expression that is not *always* legal in a const context.
#[derive(Clone, Copy)]
enum NonConstExpr {
    Loop(hir::LoopSource),
    Match(hir::MatchSource),
}

impl NonConstExpr {
    fn name(self) -> &'static str {
        match self {
            Self::Loop(src) => src.name(),
            Self::Match(src) => src.name(),
        }
    }

    fn required_feature_gates(self) -> Option<&'static [Symbol]> {
        use hir::MatchSource::*;
        use hir::LoopSource::*;

        let gates: &[_] = match self {
            | Self::Match(Normal)
            | Self::Match(IfDesugar { .. })
            | Self::Match(IfLetDesugar { .. })
            => &[sym::const_if_match],

            | Self::Loop(Loop)
            => &[sym::const_loop],

            | Self::Loop(While)
            | Self::Loop(WhileLet)
            | Self::Match(WhileDesugar)
            | Self::Match(WhileLetDesugar)
            => &[sym::const_loop, sym::const_if_match],

            // A `for` loop's desugaring contains a call to `FromIterator::from_iter`,
            // so they are not yet allowed with `#![feature(const_loop)]`.
            _ => return None,
        };

        Some(gates)
    }
}

#[derive(Copy, Clone)]
enum ConstKind {
    Static,
    StaticMut,
    ConstFn,
    Const,
    AnonConst,
}

impl ConstKind {
    fn for_body(body: &hir::Body, hir_map: &Map<'_>) -> Option<Self> {
        let is_const_fn = |id| hir_map.fn_sig_by_hir_id(id).unwrap().header.is_const();

        let owner = hir_map.body_owner(body.id());
        let const_kind = match hir_map.body_owner_kind(owner) {
            hir::BodyOwnerKind::Const => Self::Const,
            hir::BodyOwnerKind::Static(Mutability::Mutable) => Self::StaticMut,
            hir::BodyOwnerKind::Static(Mutability::Immutable) => Self::Static,

            hir::BodyOwnerKind::Fn if is_const_fn(owner) => Self::ConstFn,
            hir::BodyOwnerKind::Fn | hir::BodyOwnerKind::Closure => return None,
        };

        Some(const_kind)
    }
}

impl fmt::Display for ConstKind {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let s = match self {
            Self::Static => "static",
            Self::StaticMut => "static mut",
            Self::Const | Self::AnonConst => "const",
            Self::ConstFn => "const fn",
        };

        write!(f, "{}", s)
    }
}

fn check_mod_const_bodies(tcx: TyCtxt<'_>, module_def_id: DefId) {
    let mut vis = CheckConstVisitor::new(tcx);
    tcx.hir().visit_item_likes_in_module(module_def_id, &mut vis.as_deep_visitor());
}

pub(crate) fn provide(providers: &mut Providers<'_>) {
    *providers = Providers {
        check_mod_const_bodies,
        ..*providers
    };
}

#[derive(Copy, Clone)]
struct CheckConstVisitor<'tcx> {
    tcx: TyCtxt<'tcx>,
    const_kind: Option<ConstKind>,
}

impl<'tcx> CheckConstVisitor<'tcx> {
    fn new(tcx: TyCtxt<'tcx>) -> Self {
        CheckConstVisitor {
            tcx,
            const_kind: None,
        }
    }

    /// Emits an error when an unsupported expression is found in a const context.
    fn const_check_violated(&self, expr: NonConstExpr, span: Span) {
        let features = self.tcx.features();
        let gates = expr.required_feature_gates();
        match gates {
            // Don't emit an error if the user has enabled the requisite feature gates.
            Some(gates) if gates.iter().all(|&g| features[g]) => return,

            // `-Zunleash-the-miri-inside-of-you` only works for expressions that don't have a
            // corresponding feature gate. This encourages nightly users to use feature gates when
            // possible.
            None if self.tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you => {
                self.tcx.sess.span_warn(span, "skipping const checks");
                return;
            }

            _ => {}
        }

        let const_kind = self.const_kind
            .expect("`const_check_violated` may only be called inside a const context");
        let msg = format!("`{}` is not allowed in a `{}`", expr.name(), const_kind);

        let gates = gates.unwrap_or(&[]);
        let missing_gates: Vec<_> = gates
            .iter()
            .copied()
            .filter(|&g| !features[g])
            .collect();

        match missing_gates.as_slice() {
            &[] => span_err!(self.tcx.sess, span, E0744, "{}", msg),

            // If the user enabled `#![feature(const_loop)]` but not `#![feature(const_if_match)]`,
            // explain why their `while` loop is being rejected.
            &[gate @ sym::const_if_match] if gates.contains(&sym::const_loop) => {
                let mut err = feature_err(&self.tcx.sess.parse_sess, gate, span, &msg);
                err.note("`#![feature(const_loop)]` alone is not sufficient, \
                          since this loop expression contains an implicit conditional");
                err.emit();
            }

            &[missing_primary, ref missing_secondary @ ..] => {
                let mut err = feature_err(&self.tcx.sess.parse_sess, missing_primary, span, &msg);

                // If multiple feature gates would be required to enable this expression, include
                // them as help messages. Don't emit a separate error for each missing feature gate.
                //
                // FIXME(ecstaticmorse): Maybe this could be incorporated into `feature_err`? This
                // is a pretty narrow case, however.
                if nightly_options::is_nightly_build() {
                    for gate in missing_secondary {
                        let note = format!(
                            "add `#![feature({})]` to the crate attributes to enable",
                            gate,
                        );
                        err.help(&note);
                    }
                }

                err.emit();
            }
        }
    }

    /// Saves the parent `const_kind` before calling `f` and restores it afterwards.
    fn recurse_into(&mut self, kind: Option<ConstKind>, f: impl FnOnce(&mut Self)) {
        let parent_kind = self.const_kind;
        self.const_kind = kind;
        f(self);
        self.const_kind = parent_kind;
    }
}

impl<'tcx> Visitor<'tcx> for CheckConstVisitor<'tcx> {
    fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
        NestedVisitorMap::OnlyBodies(&self.tcx.hir())
    }

    fn visit_anon_const(&mut self, anon: &'tcx hir::AnonConst) {
        let kind = Some(ConstKind::AnonConst);
        self.recurse_into(kind, |this| hir::intravisit::walk_anon_const(this, anon));
    }

    fn visit_body(&mut self, body: &'tcx hir::Body) {
        let kind = ConstKind::for_body(body, self.tcx.hir());
        self.recurse_into(kind, |this| hir::intravisit::walk_body(this, body));
    }

    fn visit_expr(&mut self, e: &'tcx hir::Expr) {
        match &e.kind {
            // Skip the following checks if we are not currently in a const context.
            _ if self.const_kind.is_none() => {}

            hir::ExprKind::Loop(_, _, source) => {
                self.const_check_violated(NonConstExpr::Loop(*source), e.span);
            }

            hir::ExprKind::Match(_, _, source) => {
                let non_const_expr = match source {
                    // These are handled by `ExprKind::Loop` above.
                    | hir::MatchSource::WhileDesugar
                    | hir::MatchSource::WhileLetDesugar
                    | hir::MatchSource::ForLoopDesugar
                    => None,

                    _ => Some(NonConstExpr::Match(*source)),
                };

                if let Some(expr) = non_const_expr {
                    self.const_check_violated(expr, e.span);
                }
            }

            _ => {},
        }

        hir::intravisit::walk_expr(self, e);
    }
}
<|endoftext|>


<commit_before>use std::io::net::ip::{SocketAddr, Ipv4Addr};
use std::time::duration::Duration;
use string_telephone::{Client, ConnectionConfig, ClientConnectionConfig};
use scene::{Scene,BoxedScene};
use gamescene::GameScene;
use gamestate::GameState;
use conrod::{
    label,
    Color,
    WidgetMatrix,
    Button,
    Callable,
    Colorable,
    Drawable,
    Frameable,
    Labelable,
    Positionable,
    Shapeable,
    TextBox,
};

use piston::{graphics, Render, Event, Update};
use piston::graphics::{AddColor, Draw};
use packet;

enum ConnectState {
    Disconnected,
    Connecting(Receiver<Option<Client<packet::Packet>>>)
}

pub struct ConnectScene {
    pub edit_ip: Vec<String>,
    try_connect: ConnectState,
}

impl ConnectScene {

    pub fn new() -> BoxedScene {
       box ConnectScene {
           edit_ip: vec!["127".to_string(), "0".to_string(), "0".to_string(), "1".to_string()],
           try_connect: Disconnected
       }
    }

    pub fn is_connecting(&self) -> bool {
        match self.try_connect { Disconnected => false, _ => true}
    }
}

impl Scene for ConnectScene {
    fn handle_event(&mut self, e: &Event, state: &mut GameState) -> Option<BoxedScene> {
        match e {
            &Update(_) => {
                let mut maybe_scene = None;
                let should_disconnect = match self.try_connect {
                    Connecting(ref socket) => {
                        match socket.try_recv() {
                            Ok(Some(comms)) => {
                                println!("Connected");
                                maybe_scene = Some(GameScene::new());
                                state.set_comms(comms);
                                true
                            },
                            Ok(None) => {
                                println!("Couldn't connect");
                                true
                            },
                            _ => false
                        }
                    },
                    _ => false
                };
                if should_disconnect {
                    self.try_connect = Disconnected;
                }
                maybe_scene
            },
            &Render(args) => {
                let (uic, gl) = state.get_drawables();

                gl.viewport(0, 0, args.width as i32, args.height as i32);

                let c = &graphics::Context::abs(args.width as f64, args.height as f64);

                c.rgb(1.0, 1.0, 1.0).draw(gl);
                label::draw(
                    gl,
                    uic,
                    [0f64, 0f64], // Screen position.
                    48u32, // Font size.
                    Color::new(1.0, 0.0, 0.0, 1.0),
                    "Select a server"
                );

                uic.widget_matrix(4, 1)
                    .position(0.0, 56.0)
                    .dimensions(300.0, 240.0)
                    .each_widget(|uic, num, _, _, pos, dims| {
                        // Now draw the widgets with the given callback.
                        uic.text_box(2 + num as u64, self.edit_ip.get_mut(num))
                            .font_size(24u32)
                            .dimensions(dims[0], 36.0)
                            .position(pos[0], pos[1])
                            .frame(2.0)
                            .color(Color::black())
                            .draw(gl);

                    });

                uic.button(7u64)
                    .dimensions(90.0, 36.0)
                    .position(0.0, 128.0)
                    .color(Color::black())
                    .frame(2.0)
                    .label("Connect")
                    .callback(|| {
                        if self.is_connecting() == false {
                            let parsed = (from_str(self.edit_ip[0].as_slice()), from_str(self.edit_ip[1].as_slice()), from_str(self.edit_ip[2].as_slice()), from_str(self.edit_ip[3].as_slice()));

                            match parsed {
                                (Some(a), Some(b), Some(c), Some(d)) => {
                                    let (tx, rx) = channel();
                                    self.try_connect = Connecting(rx);
                                    spawn(proc() {
                                        let settings = ConnectionConfig {
                                            protocol_id: 88869,
                                            timeout_period: Duration::seconds(10),
                                            packet_deserializer: packet::deserializer,
                                            packet_serializer: packet::serializer
                                        };

                                        let client_settings = ClientConnectionConfig::new(3, Duration::seconds(5));

                                        println!("Connecting");
                                        match Client::connect(SocketAddr {ip: Ipv4Addr(0, 0, 0, 0), port: 0}, SocketAddr {ip: Ipv4Addr(a, b, c, d), port: 8869}, settings, client_settings) {
                                            Ok(client) => {
                                                tx.send(Some(client))
                                            },
                                            Err(_) => tx.send(None)
                                        };
                                    });
                                },
                                _ => ()
                            };
                        }
                    })
                    .draw(gl);
                None
            },
            _ => None,
        }
    }
}
<commit_msg>Improved connection failure reporting<commit_after>use std::io::net::ip::{SocketAddr, Ipv4Addr};
use std::io::IoError;
use std::time::duration::Duration;
use string_telephone::{Client, ConnectionConfig, ClientConnectionConfig};
use scene::{Scene,BoxedScene};
use gamescene::GameScene;
use gamestate::GameState;
use conrod::{
    Label,
    Color,
    WidgetMatrix,
    Button,
    Callable,
    Colorable,
    Drawable,
    Frameable,
    Labelable,
    Positionable,
    Shapeable,
    TextBox,
};

use piston::{graphics, Render, Event, Update};
use piston::graphics::{AddColor, Draw};
use packet;

enum ConnectState {
    Disconnected,
    Connecting(Receiver<Result<Client<packet::Packet>, IoError>>)
}

pub struct ConnectScene {
    pub edit_ip: Vec<String>,
    try_connect: ConnectState,
    error_message: Option<String>
}

impl ConnectScene {

    pub fn new() -> BoxedScene {
       box ConnectScene {
           edit_ip: vec!["127".to_string(), "0".to_string(), "0".to_string(), "1".to_string()],
           try_connect: Disconnected,
           error_message: None
       }
    }

    pub fn is_connecting(&self) -> bool {
        match self.try_connect { Disconnected => false, _ => true}
    }
}

impl Scene for ConnectScene {
    fn handle_event(&mut self, e: &Event, state: &mut GameState) -> Option<BoxedScene> {
        match e {
            &Update(_) => {
                let mut maybe_scene = None;
                let should_disconnect = match self.try_connect {
                    Connecting(ref socket) => {
                        match socket.try_recv() {
                            Ok(Ok(comms)) => {
                                println!("Connected");
                                maybe_scene = Some(GameScene::new());
                                state.set_comms(comms);
                                true
                            },
                            Ok(Err(e)) => {
                                println!("Couldn't connect");
                                self.error_message = Some(e.desc.to_string());
                                true
                            },
                            _ => false
                        }
                    },
                    _ => false
                };
                if should_disconnect {
                    self.try_connect = Disconnected;
                }
                maybe_scene
            },
            &Render(args) => {
                let (uic, gl) = state.get_drawables();

                gl.viewport(0, 0, args.width as i32, args.height as i32);

                let c = &graphics::Context::abs(args.width as f64, args.height as f64);
                c.rgb(1.0, 1.0, 1.0).draw(gl);

                let initial_pos = [((600u32 / 2) - 150) as f64, 0.0];

                //Title label
                uic.label("Select a server")
                    .position(initial_pos[0], initial_pos[1])
                    .size(48u32)
                    .color(Color::black())
                    .draw(gl);

                uic.widget_matrix(4, 1)
                    .position(initial_pos[0], initial_pos[1] + 56.0)
                    .dimensions(300.0, 240.0)
                    .each_widget(|uic, num, _, _, pos, dims| {
                        // Now draw the widgets with the given callback.
                        uic.text_box(2 + num as u64, self.edit_ip.get_mut(num))
                            .font_size(24u32)
                            .dimensions(dims[0], 36.0)
                            .position(pos[0], pos[1])
                            .frame(2.0)
                            .color(Color::black())
                            .draw(gl);

                    });

                if self.is_connecting() {
                    uic.label("Connecting...")
                        .position(initial_pos[0], initial_pos[1] + 128.0)
                        .size(48u32)
                        .color(Color::black())
                        .draw(gl);
                } else {
                    uic.button(7u64)
                        .dimensions(90.0, 36.0)
                        .position(initial_pos[0], initial_pos[1] + 128.0)
                        .color(Color::black())
                        .label_color(Color::white())
                        .frame(2.0)
                        .label("Connect")
                        .callback(|| {
                            if self.is_connecting() == false {
                                let parsed = (from_str(self.edit_ip[0].as_slice()), from_str(self.edit_ip[1].as_slice()), from_str(self.edit_ip[2].as_slice()), from_str(self.edit_ip[3].as_slice()));

                                match parsed {
                                    (Some(a), Some(b), Some(c), Some(d)) => {
                                        self.error_message = None;
                                        let (tx, rx) = channel();
                                        self.try_connect = Connecting(rx);
                                        spawn(proc() {
                                            let settings = ConnectionConfig {
                                                protocol_id: 88869,
                                                timeout_period: Duration::seconds(10),
                                                packet_deserializer: packet::deserializer,
                                                packet_serializer: packet::serializer
                                            };

                                            let client_settings = ClientConnectionConfig::new(3, Duration::seconds(5));

                                            println!("Connecting");
                                            tx.send(Client::connect(SocketAddr {ip: Ipv4Addr(0, 0, 0, 0), port: 0}, SocketAddr {ip: Ipv4Addr(a, b, c, d), port: 8869}, settings, client_settings));
                                        });
                                    },
                                    _ => ()
                                };
                            }
                        })
                        .draw(gl);
                }

                match self.error_message {
                    Some(ref error) => {
                        uic.label(error.as_slice())
                            .position(initial_pos[0], initial_pos[1] + 128.0 + 36.0)
                            .size(48u32)
                            .color(Color::black())
                            .draw(gl);
                        }
                    None => ()
                }
                None
            },
            _ => None,
        }
    }
}
<|endoftext|>


<commit_before>use std::slice;

/// The type of merge vectors.
#[derive(Clone,Debug,Eq,PartialEq)]
pub struct MergeVec<T> where T: Ord + Clone {
    status: Vec<Option<usize>>,
    sorted: Vec<T>,
    new_vals: Vec<T>
}

impl<T> MergeVec<T> where T: Ord + Clone{
    /// The length of the vector.
    pub fn len(&self) -> usize {
        self.sorted.len()
    }

    /// Append an element to the end of the vector.
    pub fn push(&mut self, value: T) {
        //println!("vec push index {:?}", self.len());
        let index = self.new_vals.len();
        self.sorted.push(value.clone()); // TODO: only need size change
        self.status.push(Some(index));
        self.new_vals.push(value);
    }

    /// Set the `i`th element of the vector.
    /// Panics if the vector contains fewer than `i` elements.
    pub fn set(&mut self, index: usize, value: T) {
        //println!("vec set index {:?}", index);
        let status = self.status[index];
        match  status {
            None => {
                let new_index = self.new_vals.len();
                self.new_vals.push(value);
                self.status[index] = Some(new_index);
            },
            Some(new_index) => {
                self.new_vals[new_index] = value;
            }
        }
    }

    /// Truncate this vector and reset the sort if necessary.
    pub fn truncate(&mut self, len: usize) {
        //println!("vec truncate to {:?}", len);
        if len >= self.sorted.len() { return; }
        if len <= 0 { *self = MergeVec::new(); return; }
        let old_new_length = self.new_vals.len();
        if old_new_length > 0 {
            let mut new_new_vals = Vec::with_capacity(old_new_length);
            let mut remaining = old_new_length;
            let mut index = 0;
            while remaining > 0 && index < len {
                let status = self.status[index];
                match status {
                    None => {},
                    Some(new_index) => {
                        self.status[index] = Some(new_new_vals.len());
                        new_new_vals.push(self.new_vals[new_index].clone());
                        remaining -= 1;
                    }
                }
                index += 1;
            }
            self.new_vals = new_new_vals;
        }
        self.status.truncate(len);
        self.sorted.truncate(len);
    }

    /// Create a new, empty vector.
    pub fn new() -> MergeVec<T> {
        MergeVec {
            status: Vec::new(),
            sorted: Vec::new(),
            new_vals: Vec::new(),
        }
    }

    /// Sort the vector, merging the new values in
    pub fn sort(&mut self) {
        // TODO: use iterators to simplify this code

        let mut new_sorted = Vec::with_capacity(self.sorted.len());
        self.new_vals.sort();
        // 2 degenerate cases
        if self.new_vals.len() == 0 {
            let mut s_index = 0;
            while s_index < self.sorted.len() {
                if self.status[s_index] == None{
                    new_sorted.push(self.sorted[s_index].clone())
                }
                s_index += 1;
            }
        } else if self.sorted.len() == 0 {
            new_sorted = self.new_vals.clone();
        } else {
            // general case
            let mut s_index = 0;
            let mut n_index = 0;
            while new_sorted.len() < self.sorted.len() {
                if self.status[s_index] != None {
                    // skip modified values
                    s_index += 1;
                    if s_index == self.sorted.len() {
                        // write the rest of the sorted values to the new vec
                        while n_index < self.new_vals.len() {
                            new_sorted.push(self.new_vals[n_index].clone());
                            n_index += 1;
                        }
                    }
                } else if self.new_vals[n_index] < self.sorted[s_index] {
                    // merge in the new value
                    new_sorted.push(self.new_vals[n_index].clone());
                    n_index += 1;
                    if n_index == self.new_vals.len() {
                        // write the rest of the old values to the new vec
                        while s_index < self.sorted.len() {
                            if self.status[s_index] == None{
                                new_sorted.push(self.sorted[s_index].clone())
                            }
                            s_index += 1;
                        }
                    }
                } else {
                    // merge in the sorted value
                    new_sorted.push(self.sorted[s_index].clone());
                    s_index += 1;
                    if s_index == self.sorted.len() {
                        // write the rest of the sorted values to the new vec
                        while n_index < self.new_vals.len() {
                            new_sorted.push(self.sorted[s_index].clone());
                            n_index += 1;
                        }
                    }
                }
            }
        }
        // reset with the new sorted values
        self.sorted = new_sorted;
        self.status = vec![None; self.sorted.len()];
        self.new_vals = Vec::new();
    }

    pub fn sorted_iter(&mut self) -> slice::Iter<T> {
        self.sort();
        self.sorted.iter()
    }

    /// Get the `i`th element of the vector.
    /// Returns `None` if the vector contains fewer than `i` elements.
    pub fn get(&self, index: usize) -> Option<&T> {
        if index >= self.status.len() { return None; }
        let status = self.status[index];
        match status {
            None => { Some(&self.sorted[index]) },
            Some(new_index) => { Some(&self.new_vals[new_index]) }
        }
    }
}

impl<T> From<Vec<T>> for MergeVec<T> where T: Ord + Clone {
    fn from(vec: Vec<T>) -> MergeVec<T> {
        MergeVec {
            status: (0..vec.len()).map(|v| Some(v)).collect(),
            sorted: vec.clone(),
            new_vals: vec,
        }
    }
}

#[test]
fn test_push() {
    let mut vec = MergeVec::new();
    assert_eq!(vec.len(), 0);
    assert_eq!(vec.get(0), None);

    vec.push(0);
    assert_eq!(vec.len(), 1);
    assert_eq!(vec.get(0), Some(&0));
    assert_eq!(vec.get(1), None);
    assert_eq!(vec.sorted_iter().collect::<Vec<&usize>>(), vec![&0]);

    vec.push(30);
    assert_eq!(vec.len(), 2);
    assert_eq!(vec.get(0), Some(&0));
    assert_eq!(vec.get(1), Some(&30));
    assert_eq!(vec.get(2), None);
    assert_eq!(vec.sorted_iter().collect::<Vec<&usize>>(), vec![&0, &30]);

    vec.push(20);
    assert_eq!(vec.len(), 3);
    assert_eq!(vec.get(0), Some(&0));
    assert_eq!(vec.get(1), Some(&30));
    assert_eq!(vec.get(2), Some(&20));
    assert_eq!(vec.get(3), None);
    assert_eq!(vec.sorted_iter().collect::<Vec<&usize>>(), vec![&0, &20, &30]);

}

#[test]
fn test_set() {
    let mut vec = MergeVec::from(vec![0, 30, 20, 10]);
    assert_eq!(vec.len(), 4);
    assert_eq!(vec.get(0), Some(&0));
    assert_eq!(vec.get(1), Some(&30));
    assert_eq!(vec.get(2), Some(&20));
    assert_eq!(vec.get(3), Some(&10));
    assert_eq!(vec.get(4), None);
    assert_eq!(vec.sorted_iter().collect::<Vec<&usize>>(), vec![&0, &10, &20, &30]);

}
<commit_msg>rewrote MergeVec, need to test<commit_after>
/// The type of merge vectors.
#[derive(Clone,Debug)]
pub struct MergeVec<T> where T: Ord {
    content: Vec<T>,                // the content changed only by user
    sort_index: Vec<usize>,         // where the sort reference is stored
    sorted: Vec<SortTarget>,        //  holes if changed
    unsorted: Vec<Option<usize>>    // new, unsorted content
}

#[derive(Clone,Copy,Debug)]
enum SortTarget {
    Content(usize),
    Unsorted(usize),
    Removed,
}

struct ContentIter<'a> {
    index: usize,
    vec: &'a Vec<SortTarget>,
}

impl<'a> Iterator for ContentIter<'a> {
    type Item = usize;
    fn next(&mut self) -> Option<usize> {
        while self.index < self.vec.len() {
            let i = self.index;
            self.index += 1;
            if let SortTarget::Content(c) = self.vec[i] {
                return Some(c);
            }
        }
        return None
    }
}

struct OpContentIter<'a> {
    index: usize,
    vec: &'a Vec<Option<usize>>,
}

impl<'a> Iterator for OpContentIter<'a> {
    type Item = usize;
    fn next(&mut self) -> Option<usize> {
        while self.index < self.vec.len() {
            let i = self.index;
            self.index += 1;
            if let Some(c) = self.vec[i] {
                return Some(c);
            }
        }
        return None
    }
}

#[derive(Clone,Debug)]
pub struct MergeVecIter<'a, T> where T: 'a {
    index: usize,
    content: &'a[T],
    sort_order: &'a[SortTarget],
}

impl<'a, T> Iterator for MergeVecIter<'a, T> where T: 'a {
    type Item = &'a T;
    fn next(&mut self) -> Option<&'a T> {
        let sorted_index = self.index;
        self.index += 1;
        self.sort_order.get(sorted_index).and_then(|&target| {
            match target {
                SortTarget::Content(i) => self.content.get(i),
                _ => None
            }
        })
    }
}

impl<T> MergeVec<T> where T: Ord {
    /// The length of the vector.
    pub fn len(&self) -> usize {
        self.content.len()
    }

    /// Append an element to the end of the content.
    pub fn push(&mut self, value: T) {
        //println!("vec push index {:?}", self.len());

        let content_index = self.content.len();
        if value >= self.content[content_index] {
            // in order
            let sort_index = self.sorted.len();
            self.sort_index.push(sort_index);
            self.sorted.push(SortTarget::Content(content_index));
        } else {
            // out of order
            let new_index = self.unsorted.len();
            let sort_index = self.sorted.len();
            self.sort_index.push(sort_index);
            self.sorted.push(SortTarget::Unsorted(new_index));
            self.unsorted.push(Some(content_index));
        }
        self.content.push(value);
    }

    /// Set the `i`th element of the vector.
    /// Panics if the vector contains fewer than `i` elements.
    pub fn set(&mut self, index: usize, value: T) {
        //println!("vec set index {:?}", index);
        let sort_index = self.sort_index[index];
        let old_loc = self.sorted[sort_index];
        match old_loc {
            SortTarget::Content(_) => {
                if !self.would_be_sorted(sort_index, &value) {
                    let new_index = self.unsorted.len();
                    self.sorted[sort_index] = SortTarget::Unsorted(new_index);
                    self.unsorted.push(Some(index));
                }
            }
            SortTarget::Unsorted(unsort_index) => {
                if self.would_be_sorted(sort_index, &value) {
                    self.unsorted[unsort_index] = None;
                    self.sorted[sort_index] = SortTarget::Content(index);
                }
            }
            SortTarget::Removed => {
                if self.would_be_sorted(sort_index, &value) {
                    self.sorted[sort_index] = SortTarget::Content(index);
                } else {
                    let new_index = self.unsorted.len();
                    self.sorted[sort_index] = SortTarget::Unsorted(new_index);
                    self.unsorted.push(Some(index));
                }
            }
        }
        self.content[index] = value;
    }

    fn would_be_sorted(&self, sort_index: usize, value: &T) -> bool {
        if sort_index > 0 {
            if let SortTarget::Content(content_index) = self.sorted[sort_index - 1] {
                if self.content[content_index] > *value { return false; }
            } else {
                // TODO: Should we search through unsorted/removed values?
                return false;
            }
        }
        if sort_index < self.sorted.len() - 1 {
            if let SortTarget::Content(content_index) = self.sorted[sort_index + 1] {
                if self.content[content_index] < *value { return false; }
            } else {
                // TODO: Should we search through unsorted/removed values?
                return false;
            }
        }
        return true;
    }

    /// Truncate this vector.
    pub fn truncate(&mut self, len: usize) {
        //println!("vec truncate to {:?}", len);

        // remove all linked values
        for index in len..self.content.len() {
            let sort_index = self.sort_index[index];
            match self.sorted[sort_index] {
                SortTarget::Content(_) => self.sorted[sort_index] = SortTarget::Removed,
                SortTarget::Unsorted(unsort_index) => {
                    self.unsorted[unsort_index] = None;
                    self.sorted[sort_index] = SortTarget::Removed;
                },
                SortTarget::Removed => {}
            }
        }
        //truncate data
        self.content.truncate(len);
        self.sort_index.truncate(len);
    }

    /// Create a new, empty vector.
    pub fn new() -> MergeVec<T> {
        MergeVec {
            content: Vec::new(),
            sort_index: Vec::new(),
            sorted: Vec::new(),
            unsorted: Vec::new(),
        }
    }

    /// Consolidate incremental data, in preparation of producing a sorted iterator
    pub fn sort(&mut self) {
        self.unsorted.sort();
        let mut new_sort = Vec::with_capacity(self.content.len());
        {
            let mut a_iter = ContentIter { index: 0, vec: &self.sorted};
            let mut b_iter = OpContentIter { index: 0, vec: &self.unsorted};
            let mut op_a = a_iter.next();
            let mut op_b = b_iter.next();
            // merge
            loop {
                if let Some(index_a) = op_a {
                if let Some(index_b) = op_b {
                    if self.content[index_a] <= self.content[index_b] {
                        self.sort_index[index_a] = new_sort.len();
                        new_sort.push(SortTarget::Content(index_a));
                        op_a = a_iter.next();
                    } else {
                        self.sort_index[index_b] = new_sort.len();
                        new_sort.push(SortTarget::Content(index_b));
                        op_b = b_iter.next();
                    }
                } else { break; }
                } else { break; }
            }
            // add data from longer iter
            while let Some(index_a) = op_a {
                self.sort_index[index_a] = new_sort.len();
                new_sort.push(SortTarget::Content(index_a));
                op_a = a_iter.next();
            }
            while let Some(index_b) = op_b {
                self.sort_index[index_b] = new_sort.len();
                new_sort.push(SortTarget::Content(index_b));
                op_b = b_iter.next();
            }
        }
        //finalize vecs
        self.sorted = new_sort;
        self.unsorted = Vec::new();
    }

    pub fn sorted_iter(&mut self) -> MergeVecIter<T> {
        self.sort();
        MergeVecIter {
            index: 0,
            content: &self.content,
            sort_order: &self.sorted,
        }
    }

    /// Get the `i`th element of the vector.
    /// Returns `None` if the vector contains fewer than `i` elements.
    pub fn get(&self, index: usize) -> Option<&T> {
        self.content.get(index)
    }
}

impl<T> From<Vec<T>> for MergeVec<T> where T: Ord + Clone {
    fn from(vec: Vec<T>) -> MergeVec<T> {
        let length = vec.len();
        MergeVec {
            content: vec,
            sort_index: (0..length).collect(),
            sorted: (0..length).map(|i| SortTarget::Unsorted(i)).collect(),
            unsorted: (0..length).map(|i| Some(i)).collect(),
        }
    }
}

#[test]
fn test_push() {
    let mut vec = MergeVec::new();
    assert_eq!(vec.len(), 0);
    assert_eq!(vec.get(0), None);

    vec.push(0);
    assert_eq!(vec.len(), 1);
    assert_eq!(vec.get(0), Some(&0));
    assert_eq!(vec.get(1), None);
    assert_eq!(vec.sorted_iter().collect::<Vec<&usize>>(), vec![&0]);

    vec.push(30);
    assert_eq!(vec.len(), 2);
    assert_eq!(vec.get(0), Some(&0));
    assert_eq!(vec.get(1), Some(&30));
    assert_eq!(vec.get(2), None);
    assert_eq!(vec.sorted_iter().collect::<Vec<&usize>>(), vec![&0, &30]);

    vec.push(20);
    assert_eq!(vec.len(), 3);
    assert_eq!(vec.get(0), Some(&0));
    assert_eq!(vec.get(1), Some(&30));
    assert_eq!(vec.get(2), Some(&20));
    assert_eq!(vec.get(3), None);
    assert_eq!(vec.sorted_iter().collect::<Vec<&usize>>(), vec![&0, &20, &30]);

}

#[test]
fn test_set() {
    let mut vec = MergeVec::from(vec![0, 30, 20, 10]);
    assert_eq!(vec.len(), 4);
    assert_eq!(vec.get(0), Some(&0));
    assert_eq!(vec.get(1), Some(&30));
    assert_eq!(vec.get(2), Some(&20));
    assert_eq!(vec.get(3), Some(&10));
    assert_eq!(vec.get(4), None);
    assert_eq!(vec.sorted_iter().collect::<Vec<&usize>>(), vec![&0, &10, &20, &30]);

}
<|endoftext|>


<commit_before>// Copyright 2012 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.

use ast;
use ast::{token_tree, tt_delim, tt_tok, tt_seq, tt_nonterminal,Ident};
use codemap::{Span, DUMMY_SP};
use diagnostic::SpanHandler;
use ext::tt::macro_parser::{named_match, matched_seq, matched_nonterminal};
use parse::token::{EOF, INTERPOLATED, IDENT, Token, nt_ident};
use parse::token::{ident_to_str};
use parse::lexer::TokenAndSpan;

use std::cell::{Cell, RefCell};
use std::hashmap::HashMap;
use std::option;

///an unzipping of `token_tree`s
struct TtFrame {
    forest: @~[ast::token_tree],
    idx: Cell<uint>,
    dotdotdoted: bool,
    sep: Option<Token>,
    up: Option<@mut TtFrame>,
}

pub struct TtReader {
    sp_diag: @SpanHandler,
    // the unzipped tree:
    priv stack: RefCell<@mut TtFrame>,
    /* for MBE-style macro transcription */
    priv interpolations: RefCell<HashMap<Ident, @named_match>>,
    priv repeat_idx: RefCell<~[uint]>,
    priv repeat_len: RefCell<~[uint]>,
    /* cached: */
    cur_tok: RefCell<Token>,
    cur_span: RefCell<Span>,
}

/** This can do Macro-By-Example transcription. On the other hand, if
 *  `src` contains no `tt_seq`s and `tt_nonterminal`s, `interp` can (and
 *  should) be none. */
pub fn new_tt_reader(sp_diag: @SpanHandler,
                     interp: Option<HashMap<Ident,@named_match>>,
                     src: ~[ast::token_tree])
                     -> @TtReader {
    let r = @TtReader {
        sp_diag: sp_diag,
        stack: RefCell::new(@mut TtFrame {
            forest: @src,
            idx: Cell::new(0u),
            dotdotdoted: false,
            sep: None,
            up: option::None
        }),
        interpolations: match interp { /* just a convienience */
            None => RefCell::new(HashMap::new()),
            Some(x) => RefCell::new(x),
        },
        repeat_idx: RefCell::new(~[]),
        repeat_len: RefCell::new(~[]),
        /* dummy values, never read: */
        cur_tok: RefCell::new(EOF),
        cur_span: RefCell::new(DUMMY_SP),
    };
    tt_next_token(r); /* get cur_tok and cur_span set up */
    return r;
}

fn dup_tt_frame(f: @mut TtFrame) -> @mut TtFrame {
    @mut TtFrame {
        forest: @(*f.forest).clone(),
        idx: f.idx.clone(),
        dotdotdoted: f.dotdotdoted,
        sep: f.sep.clone(),
        up: match f.up {
            Some(up_frame) => Some(dup_tt_frame(up_frame)),
            None => None
        }
    }
}

pub fn dup_tt_reader(r: @TtReader) -> @TtReader {
    @TtReader {
        sp_diag: r.sp_diag,
        stack: RefCell::new(dup_tt_frame(r.stack.get())),
        repeat_idx: r.repeat_idx.clone(),
        repeat_len: r.repeat_len.clone(),
        cur_tok: r.cur_tok.clone(),
        cur_span: r.cur_span.clone(),
        interpolations: r.interpolations.clone(),
    }
}

fn lookup_cur_matched_by_matched(r: &TtReader, start: @named_match)
                                 -> @named_match {
    fn red(ad: @named_match, idx: &uint) -> @named_match {
        match *ad {
          matched_nonterminal(_) => {
            // end of the line; duplicate henceforth
            ad
          }
          matched_seq(ref ads, _) => ads[*idx]
        }
    }
    let repeat_idx = r.repeat_idx.borrow();
    repeat_idx.get().iter().fold(start, red)
}

fn lookup_cur_matched(r: &TtReader, name: Ident) -> @named_match {
    let matched_opt = {
        let interpolations = r.interpolations.borrow();
        interpolations.get().find_copy(&name)
    };
    match matched_opt {
        Some(s) => lookup_cur_matched_by_matched(r, s),
        None => {
            r.sp_diag.span_fatal(r.cur_span.get(),
                                 format!("unknown macro variable `{}`",
                                         ident_to_str(&name)));
        }
    }
}

#[deriving(Clone)]
enum lis {
    lis_unconstrained,
    lis_constraint(uint, Ident),
    lis_contradiction(~str),
}

fn lockstep_iter_size(t: &token_tree, r: &TtReader) -> lis {
    fn lis_merge(lhs: lis, rhs: lis) -> lis {
        match lhs {
          lis_unconstrained => rhs.clone(),
          lis_contradiction(_) => lhs.clone(),
          lis_constraint(l_len, ref l_id) => match rhs {
            lis_unconstrained => lhs.clone(),
            lis_contradiction(_) => rhs.clone(),
            lis_constraint(r_len, _) if l_len == r_len => lhs.clone(),
            lis_constraint(r_len, ref r_id) => {
                let l_n = ident_to_str(l_id);
                let r_n = ident_to_str(r_id);
                lis_contradiction(format!("Inconsistent lockstep iteration: \
                                           '{}' has {} items, but '{}' has {}",
                                           l_n, l_len, r_n, r_len))
            }
          }
        }
    }
    match *t {
      tt_delim(ref tts) | tt_seq(_, ref tts, _, _) => {
        tts.iter().fold(lis_unconstrained, |lis, tt| {
            let lis2 = lockstep_iter_size(tt, r);
            lis_merge(lis, lis2)
        })
      }
      tt_tok(..) => lis_unconstrained,
      tt_nonterminal(_, name) => match *lookup_cur_matched(r, name) {
        matched_nonterminal(_) => lis_unconstrained,
        matched_seq(ref ads, _) => lis_constraint(ads.len(), name)
      }
    }
}

// return the next token from the TtReader.
// EFFECT: advances the reader's token field
pub fn tt_next_token(r: &TtReader) -> TokenAndSpan {
    // XXX(pcwalton): Bad copy?
    let ret_val = TokenAndSpan {
        tok: r.cur_tok.get(),
        sp: r.cur_span.get(),
    };
    loop {
        {
            let mut stack = r.stack.borrow_mut();
            if stack.get().idx.get() < stack.get().forest.len() {
                break;
            }
        }

        /* done with this set; pop or repeat? */
        if !r.stack.get().dotdotdoted || {
                let repeat_idx = r.repeat_idx.borrow();
                let repeat_len = r.repeat_len.borrow();
                *repeat_idx.get().last() == *repeat_len.get().last() - 1
            } {

            match r.stack.get().up {
              None => {
                r.cur_tok.set(EOF);
                return ret_val;
              }
              Some(tt_f) => {
                if r.stack.get().dotdotdoted {
                    {
                        let mut repeat_idx = r.repeat_idx.borrow_mut();
                        let mut repeat_len = r.repeat_len.borrow_mut();
                        repeat_idx.get().pop();
                        repeat_len.get().pop();
                    }
                }

                r.stack.set(tt_f);
                r.stack.get().idx.set(r.stack.get().idx.get() + 1u);
              }
            }

        } else { /* repeat */
            r.stack.get().idx.set(0u);
            {
                let mut repeat_idx = r.repeat_idx.borrow_mut();
                repeat_idx.get()[repeat_idx.get().len() - 1u] += 1u;
            }
            match r.stack.get().sep.clone() {
              Some(tk) => {
                r.cur_tok.set(tk); /* repeat same span, I guess */
                return ret_val;
              }
              None => ()
            }
        }
    }
    loop { /* because it's easiest, this handles `tt_delim` not starting
    with a `tt_tok`, even though it won't happen */
        // XXX(pcwalton): Bad copy.
        match r.stack.get().forest[r.stack.get().idx.get()].clone() {
          tt_delim(tts) => {
            r.stack.set(@mut TtFrame {
                forest: tts,
                idx: Cell::new(0u),
                dotdotdoted: false,
                sep: None,
                up: option::Some(r.stack.get())
            });
            // if this could be 0-length, we'd need to potentially recur here
          }
          tt_tok(sp, tok) => {
            r.cur_span.set(sp);
            r.cur_tok.set(tok);
            r.stack.get().idx.set(r.stack.get().idx.get() + 1u);
            return ret_val;
          }
          tt_seq(sp, tts, sep, zerok) => {
            // XXX(pcwalton): Bad copy.
            let t = tt_seq(sp, tts, sep.clone(), zerok);
            match lockstep_iter_size(&t, r) {
              lis_unconstrained => {
                r.sp_diag.span_fatal(
                    sp, /* blame macro writer */
                      "attempted to repeat an expression \
                       containing no syntax \
                       variables matched as repeating at this depth");
                  }
                  lis_contradiction(ref msg) => {
                      /* FIXME #2887 blame macro invoker instead*/
                      r.sp_diag.span_fatal(sp, (*msg));
                  }
                  lis_constraint(len, _) => {
                    if len == 0 {
                      if !zerok {
                        r.sp_diag.span_fatal(sp, /* FIXME #2887 blame invoker
                        */
                                             "this must repeat at least \
                                              once");
                          }

                    r.stack.get().idx.set(r.stack.get().idx.get() + 1u);
                    return tt_next_token(r);
                } else {
                    {
                        let mut repeat_idx = r.repeat_idx.borrow_mut();
                        let mut repeat_len = r.repeat_len.borrow_mut();
                        repeat_len.get().push(len);
                        repeat_idx.get().push(0u);
                        r.stack.set(@mut TtFrame {
                            forest: tts,
                            idx: Cell::new(0u),
                            dotdotdoted: true,
                            sep: sep,
                            up: Some(r.stack.get())
                        });
                    }
                }
              }
            }
          }
          // FIXME #2887: think about span stuff here
          tt_nonterminal(sp, ident) => {
            match *lookup_cur_matched(r, ident) {
              /* sidestep the interpolation tricks for ident because
              (a) idents can be in lots of places, so it'd be a pain
              (b) we actually can, since it's a token. */
              matched_nonterminal(nt_ident(~sn,b)) => {
                r.cur_span.set(sp);
                r.cur_tok.set(IDENT(sn,b));
                r.stack.get().idx.set(r.stack.get().idx.get() + 1u);
                return ret_val;
              }
              matched_nonterminal(ref other_whole_nt) => {
                // XXX(pcwalton): Bad copy.
                r.cur_span.set(sp);
                r.cur_tok.set(INTERPOLATED((*other_whole_nt).clone()));
                r.stack.get().idx.set(r.stack.get().idx.get() + 1u);
                return ret_val;
              }
              matched_seq(..) => {
                r.sp_diag.span_fatal(
                    r.cur_span.get(), /* blame the macro writer */
                    format!("variable '{}' is still repeating at this depth",
                         ident_to_str(&ident)));
              }
            }
          }
        }
    }

}
<commit_msg>libsyntax: De-`@mut` `TtFrame`<commit_after>// Copyright 2012 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.

use ast;
use ast::{token_tree, tt_delim, tt_tok, tt_seq, tt_nonterminal,Ident};
use codemap::{Span, DUMMY_SP};
use diagnostic::SpanHandler;
use ext::tt::macro_parser::{named_match, matched_seq, matched_nonterminal};
use parse::token::{EOF, INTERPOLATED, IDENT, Token, nt_ident};
use parse::token::{ident_to_str};
use parse::lexer::TokenAndSpan;

use std::cell::{Cell, RefCell};
use std::hashmap::HashMap;
use std::option;

///an unzipping of `token_tree`s
struct TtFrame {
    forest: @~[ast::token_tree],
    idx: Cell<uint>,
    dotdotdoted: bool,
    sep: Option<Token>,
    up: Option<@TtFrame>,
}

pub struct TtReader {
    sp_diag: @SpanHandler,
    // the unzipped tree:
    priv stack: RefCell<@TtFrame>,
    /* for MBE-style macro transcription */
    priv interpolations: RefCell<HashMap<Ident, @named_match>>,
    priv repeat_idx: RefCell<~[uint]>,
    priv repeat_len: RefCell<~[uint]>,
    /* cached: */
    cur_tok: RefCell<Token>,
    cur_span: RefCell<Span>,
}

/** This can do Macro-By-Example transcription. On the other hand, if
 *  `src` contains no `tt_seq`s and `tt_nonterminal`s, `interp` can (and
 *  should) be none. */
pub fn new_tt_reader(sp_diag: @SpanHandler,
                     interp: Option<HashMap<Ident,@named_match>>,
                     src: ~[ast::token_tree])
                     -> @TtReader {
    let r = @TtReader {
        sp_diag: sp_diag,
        stack: RefCell::new(@TtFrame {
            forest: @src,
            idx: Cell::new(0u),
            dotdotdoted: false,
            sep: None,
            up: option::None
        }),
        interpolations: match interp { /* just a convienience */
            None => RefCell::new(HashMap::new()),
            Some(x) => RefCell::new(x),
        },
        repeat_idx: RefCell::new(~[]),
        repeat_len: RefCell::new(~[]),
        /* dummy values, never read: */
        cur_tok: RefCell::new(EOF),
        cur_span: RefCell::new(DUMMY_SP),
    };
    tt_next_token(r); /* get cur_tok and cur_span set up */
    return r;
}

fn dup_tt_frame(f: @TtFrame) -> @TtFrame {
    @TtFrame {
        forest: @(*f.forest).clone(),
        idx: f.idx.clone(),
        dotdotdoted: f.dotdotdoted,
        sep: f.sep.clone(),
        up: match f.up {
            Some(up_frame) => Some(dup_tt_frame(up_frame)),
            None => None
        }
    }
}

pub fn dup_tt_reader(r: @TtReader) -> @TtReader {
    @TtReader {
        sp_diag: r.sp_diag,
        stack: RefCell::new(dup_tt_frame(r.stack.get())),
        repeat_idx: r.repeat_idx.clone(),
        repeat_len: r.repeat_len.clone(),
        cur_tok: r.cur_tok.clone(),
        cur_span: r.cur_span.clone(),
        interpolations: r.interpolations.clone(),
    }
}

fn lookup_cur_matched_by_matched(r: &TtReader, start: @named_match)
                                 -> @named_match {
    fn red(ad: @named_match, idx: &uint) -> @named_match {
        match *ad {
          matched_nonterminal(_) => {
            // end of the line; duplicate henceforth
            ad
          }
          matched_seq(ref ads, _) => ads[*idx]
        }
    }
    let repeat_idx = r.repeat_idx.borrow();
    repeat_idx.get().iter().fold(start, red)
}

fn lookup_cur_matched(r: &TtReader, name: Ident) -> @named_match {
    let matched_opt = {
        let interpolations = r.interpolations.borrow();
        interpolations.get().find_copy(&name)
    };
    match matched_opt {
        Some(s) => lookup_cur_matched_by_matched(r, s),
        None => {
            r.sp_diag.span_fatal(r.cur_span.get(),
                                 format!("unknown macro variable `{}`",
                                         ident_to_str(&name)));
        }
    }
}

#[deriving(Clone)]
enum lis {
    lis_unconstrained,
    lis_constraint(uint, Ident),
    lis_contradiction(~str),
}

fn lockstep_iter_size(t: &token_tree, r: &TtReader) -> lis {
    fn lis_merge(lhs: lis, rhs: lis) -> lis {
        match lhs {
          lis_unconstrained => rhs.clone(),
          lis_contradiction(_) => lhs.clone(),
          lis_constraint(l_len, ref l_id) => match rhs {
            lis_unconstrained => lhs.clone(),
            lis_contradiction(_) => rhs.clone(),
            lis_constraint(r_len, _) if l_len == r_len => lhs.clone(),
            lis_constraint(r_len, ref r_id) => {
                let l_n = ident_to_str(l_id);
                let r_n = ident_to_str(r_id);
                lis_contradiction(format!("Inconsistent lockstep iteration: \
                                           '{}' has {} items, but '{}' has {}",
                                           l_n, l_len, r_n, r_len))
            }
          }
        }
    }
    match *t {
      tt_delim(ref tts) | tt_seq(_, ref tts, _, _) => {
        tts.iter().fold(lis_unconstrained, |lis, tt| {
            let lis2 = lockstep_iter_size(tt, r);
            lis_merge(lis, lis2)
        })
      }
      tt_tok(..) => lis_unconstrained,
      tt_nonterminal(_, name) => match *lookup_cur_matched(r, name) {
        matched_nonterminal(_) => lis_unconstrained,
        matched_seq(ref ads, _) => lis_constraint(ads.len(), name)
      }
    }
}

// return the next token from the TtReader.
// EFFECT: advances the reader's token field
pub fn tt_next_token(r: &TtReader) -> TokenAndSpan {
    // XXX(pcwalton): Bad copy?
    let ret_val = TokenAndSpan {
        tok: r.cur_tok.get(),
        sp: r.cur_span.get(),
    };
    loop {
        {
            let mut stack = r.stack.borrow_mut();
            if stack.get().idx.get() < stack.get().forest.len() {
                break;
            }
        }

        /* done with this set; pop or repeat? */
        if !r.stack.get().dotdotdoted || {
                let repeat_idx = r.repeat_idx.borrow();
                let repeat_len = r.repeat_len.borrow();
                *repeat_idx.get().last() == *repeat_len.get().last() - 1
            } {

            match r.stack.get().up {
              None => {
                r.cur_tok.set(EOF);
                return ret_val;
              }
              Some(tt_f) => {
                if r.stack.get().dotdotdoted {
                    {
                        let mut repeat_idx = r.repeat_idx.borrow_mut();
                        let mut repeat_len = r.repeat_len.borrow_mut();
                        repeat_idx.get().pop();
                        repeat_len.get().pop();
                    }
                }

                r.stack.set(tt_f);
                r.stack.get().idx.set(r.stack.get().idx.get() + 1u);
              }
            }

        } else { /* repeat */
            r.stack.get().idx.set(0u);
            {
                let mut repeat_idx = r.repeat_idx.borrow_mut();
                repeat_idx.get()[repeat_idx.get().len() - 1u] += 1u;
            }
            match r.stack.get().sep.clone() {
              Some(tk) => {
                r.cur_tok.set(tk); /* repeat same span, I guess */
                return ret_val;
              }
              None => ()
            }
        }
    }
    loop { /* because it's easiest, this handles `tt_delim` not starting
    with a `tt_tok`, even though it won't happen */
        // XXX(pcwalton): Bad copy.
        match r.stack.get().forest[r.stack.get().idx.get()].clone() {
          tt_delim(tts) => {
            r.stack.set(@TtFrame {
                forest: tts,
                idx: Cell::new(0u),
                dotdotdoted: false,
                sep: None,
                up: option::Some(r.stack.get())
            });
            // if this could be 0-length, we'd need to potentially recur here
          }
          tt_tok(sp, tok) => {
            r.cur_span.set(sp);
            r.cur_tok.set(tok);
            r.stack.get().idx.set(r.stack.get().idx.get() + 1u);
            return ret_val;
          }
          tt_seq(sp, tts, sep, zerok) => {
            // XXX(pcwalton): Bad copy.
            let t = tt_seq(sp, tts, sep.clone(), zerok);
            match lockstep_iter_size(&t, r) {
              lis_unconstrained => {
                r.sp_diag.span_fatal(
                    sp, /* blame macro writer */
                      "attempted to repeat an expression \
                       containing no syntax \
                       variables matched as repeating at this depth");
                  }
                  lis_contradiction(ref msg) => {
                      /* FIXME #2887 blame macro invoker instead*/
                      r.sp_diag.span_fatal(sp, (*msg));
                  }
                  lis_constraint(len, _) => {
                    if len == 0 {
                      if !zerok {
                        r.sp_diag.span_fatal(sp, /* FIXME #2887 blame invoker
                        */
                                             "this must repeat at least \
                                              once");
                          }

                    r.stack.get().idx.set(r.stack.get().idx.get() + 1u);
                    return tt_next_token(r);
                } else {
                    {
                        let mut repeat_idx = r.repeat_idx.borrow_mut();
                        let mut repeat_len = r.repeat_len.borrow_mut();
                        repeat_len.get().push(len);
                        repeat_idx.get().push(0u);
                        r.stack.set(@TtFrame {
                            forest: tts,
                            idx: Cell::new(0u),
                            dotdotdoted: true,
                            sep: sep,
                            up: Some(r.stack.get())
                        });
                    }
                }
              }
            }
          }
          // FIXME #2887: think about span stuff here
          tt_nonterminal(sp, ident) => {
            match *lookup_cur_matched(r, ident) {
              /* sidestep the interpolation tricks for ident because
              (a) idents can be in lots of places, so it'd be a pain
              (b) we actually can, since it's a token. */
              matched_nonterminal(nt_ident(~sn,b)) => {
                r.cur_span.set(sp);
                r.cur_tok.set(IDENT(sn,b));
                r.stack.get().idx.set(r.stack.get().idx.get() + 1u);
                return ret_val;
              }
              matched_nonterminal(ref other_whole_nt) => {
                // XXX(pcwalton): Bad copy.
                r.cur_span.set(sp);
                r.cur_tok.set(INTERPOLATED((*other_whole_nt).clone()));
                r.stack.get().idx.set(r.stack.get().idx.get() + 1u);
                return ret_val;
              }
              matched_seq(..) => {
                r.sp_diag.span_fatal(
                    r.cur_span.get(), /* blame the macro writer */
                    format!("variable '{}' is still repeating at this depth",
                         ident_to_str(&ident)));
              }
            }
          }
        }
    }

}
<|endoftext|>


<commit_before>use chattium_oxide_lib::json::{FromJsonnable, ToJsonnable};
use chattium_oxide_lib::ChatMessage;
use hyper::server::{Handler, Request, Response};
use hyper::header::{ContentLength, ContentType};
use hyper::status::StatusCode;
use hyper::method::Method;
use time::strftime;
use std::sync::RwLock;
use std::io::{Read, Write, stderr};

pub struct ClientHandler {
	messages  : RwLock<Vec<ChatMessage>>,
	message_id: RwLock<u64>,
}

impl ClientHandler {
	pub fn new() -> ClientHandler {
		ClientHandler{
			messages  : RwLock::new(Vec::new()),
			message_id: RwLock::new(1),
		}
	}
}

impl Handler for ClientHandler {
	fn handle(&self, mut req: Request, mut res: Response) {
		let mut body = "".to_string();

		let mut reqbody = String::new();
		*res.status_mut() = match req.read_to_string(&mut reqbody) {
			Ok(_) =>
				match req.method {
					Method::Post =>
						match ChatMessage::from_json_string(&reqbody) {
							Ok(mut message) => {
								message.sender.fill_ip(req.remote_addr);
								message.fill_id(self.message_id.write().unwrap());
								println!("{}@{}: {} @ {} # {}", message.sender.name, req.remote_addr, message.value, strftime("%T", &message.time_posted).unwrap(),
								                                message.id);
								self.messages.write().unwrap().push(message);
								StatusCode::Ok
							},
							Err(error) => {
								let _ = stderr().write_fmt(format_args!("Couldn't process POSTed message from {}: {}\n", req.remote_addr, error));
								StatusCode::UnprocessableEntity
							},
						},
					Method::Get => {  // Web browser, probably
						println!("Serving {} HTML message to connect via client.", req.remote_addr);
						body = format!("{}, use chattium-oxide-client
							              to connect to chat.", req.remote_addr);
						res.headers_mut().set(ContentType::html());
						StatusCode::Ok
					},
					Method::Trace => {
						match u64::from_json_string(&reqbody) {
							Ok(id) => {
								let messages = self.messages.read().unwrap();
								let msgs     = messages.iter().rev().take_while(|&m| m.id != id).collect::<Vec<&_>>()
								                       .iter().rev().map(|&m| m.clone()).collect::<Vec<_>>();
								match msgs.to_json_string() {
									Ok(msgs) => {
										body = msgs;
										StatusCode::Ok
									},
									Err(error) => {
										let _ = stderr().write_fmt(format_args!("Couldn't create a JSON response for {}: {}\n", req.remote_addr, error));
										body = "[]".to_string();  // Empty array
										StatusCode::Accepted
									},
								}
							},
							Err(error) => {
								let _ = stderr().write_fmt(format_args!("Couldn't process a TRACE message id ({:?}) from {}: {}\n", reqbody, req.remote_addr, error));
								StatusCode::UnprocessableEntity
							},
						}
					},
				  _ => StatusCode::ImATeapot,
				},
			Err(error) => {
				let _ = stderr().write_fmt(format_args!("Failed reading request from {}: {}\n", req.remote_addr, error));
				StatusCode::UnsupportedMediaType  // non-UTF-8
			},
		};

		res.headers_mut().set(ContentLength(body.len() as u64));
		if let Err(error) = res.start().unwrap().write_all(body.as_bytes()) {
			let _ = stderr().write_fmt(format_args!("Failed to respond to {}: {}", req.remote_addr, error));
		}
	}
}
<commit_msg>Add a better HTML message<commit_after>use chattium_oxide_lib::json::{FromJsonnable, ToJsonnable};
use chattium_oxide_lib::ChatMessage;
use hyper::server::{Handler, Request, Response};
use hyper::header::{ContentLength, ContentType, CacheControl, CacheDirective, ContentLanguage, Server, qitem};
use hyper::status::StatusCode;
use hyper::method::Method;
use hyper::LanguageTag;
use time::strftime;
use std::collections::btree_map::BTreeMap;
use std::sync::RwLock;
use std::io::{Read, Write, stderr};

pub struct ClientHandler {
	messages  : RwLock<Vec<ChatMessage>>,
	message_id: RwLock<u64>,
}

impl ClientHandler {
	pub fn new() -> ClientHandler {
		ClientHandler{
			messages  : RwLock::new(Vec::new()),
			message_id: RwLock::new(1),
		}
	}
}

impl Handler for ClientHandler {
	fn handle(&self, mut req: Request, mut res: Response) {
		let mut body = "".to_string();

		let mut reqbody = String::new();
		*res.status_mut() = match req.read_to_string(&mut reqbody) {
			Ok(_) =>
				match req.method {
					Method::Post =>
						match ChatMessage::from_json_string(&reqbody) {
							Ok(mut message) => {
								message.sender.fill_ip(req.remote_addr);
								message.fill_id(self.message_id.write().unwrap());
								println!("{}@{}: {} @ {} # {}", message.sender.name, req.remote_addr, message.value, strftime("%T", &message.time_posted).unwrap(),
								                                message.id);
								self.messages.write().unwrap().push(message);
								StatusCode::Ok
							},
							Err(error) => {
								let _ = stderr().write_fmt(format_args!("Couldn't process POSTed message from {}: {}\n", req.remote_addr, error));
								StatusCode::UnprocessableEntity
							},
						},
					Method::Get => {  // Web browser, probably
						println!("Serving {} HTML message to connect via client.", req.remote_addr);
						body = r#"<!DOCTYPE html>

						          	<title>chattium-oxide — please connect with chattium-oxide client</title>
						          	<meta charset="utf-8" />
						          	<meta name="application-name" content="chattium-oxide" />
						          	<meta name="author"           content="chattium-oxide server" />
						          	<meta name="description"      content="Please reconnect with chattium-oxide client" />
						          	<meta name="keywords"         content="chat,open source" />
						          	<meta name="robots"           content="index,follow" />

						          		What is Ч@O2?

						          		Ч@O2 (read: chattium oxide, as in: chemical compound) is a lightweight chat platform written in
						          		Rust with an aim for simplicity. Both the client and the
						          		server reside on GitHub.

						          		How do I connect to a Ч@O2 server?

						          		Just type in the exact URL of this page into the client, as this document is sent by the Ч@O2 server
						          		itself.

						          		Latest Windows and Ubuntu client binaries can be downloaded from the
						          		latest Ч@O2 client
						          		release page to connect to chat.

						          "#.to_string();
						res.headers_mut().set(ContentType::html());
						res.headers_mut().set(Server(concat!("chattium-oxide-server/", env!("CARGO_PKG_VERSION")).to_string()));
						res.headers_mut().set(ContentLanguage(vec![qitem(LanguageTag{
							language  : Some("en-GB".to_string()),
							extlangs  : vec![],
							script    : None,
							region    : None,
							variants  : vec![],
							extensions: BTreeMap::new(),
							privateuse: vec![],
						})]));
						res.headers_mut().set(CacheControl(vec![
							CacheDirective::Public,
							CacheDirective::MaxAge(60 * 60 * 24 * 7),
						]));
						StatusCode::Ok
					},
					Method::Trace => {
						match u64::from_json_string(&reqbody) {
							Ok(id) => {
								let messages = self.messages.read().unwrap();
								let msgs     = messages.iter().rev().take_while(|&m| m.id != id).collect::<Vec<&_>>()
								                       .iter().rev().map(|&m| m.clone()).collect::<Vec<_>>();
								match msgs.to_json_string() {
									Ok(msgs) => {
										body = msgs;
										StatusCode::Ok
									},
									Err(error) => {
										let _ = stderr().write_fmt(format_args!("Couldn't create a JSON response for {}: {}\n", req.remote_addr, error));
										body = "[]".to_string();  // Empty array
										StatusCode::Accepted
									},
								}
							},
							Err(error) => {
								let _ = stderr().write_fmt(format_args!("Couldn't process a TRACE message id ({:?}) from {}: {}\n", reqbody, req.remote_addr, error));
								StatusCode::UnprocessableEntity
							},
						}
					},
				  _ => StatusCode::ImATeapot,
				},
			Err(error) => {
				let _ = stderr().write_fmt(format_args!("Failed reading request from {}: {}\n", req.remote_addr, error));
				StatusCode::UnsupportedMediaType  // non-UTF-8
			},
		};

		res.headers_mut().set(ContentLength(body.len() as u64));
		if let Err(error) = res.start().unwrap().write_all(body.as_bytes()) {
			let _ = stderr().write_fmt(format_args!("Failed to respond to {}: {}", req.remote_addr, error));
		}
	}
}
<|endoftext|>


<commit_before>//! Defines a series of convenience modifiers for editing Responses
//!
//! Modifiers can be used to edit Responses through the owning
//! method `set` or the mutating `set_mut`, both of which are
//! defined in the `Set` trait.
//!
//! Instead of having a combinatorial explosion of Response methods
//! and constructors, this provides a series of modifiers that
//! can be used through the `Set` trait.
//!
//! For instance, instead of `Response::redirect` constructing a
//! redirect response, we provide a `Redirect` modifier, so you
//! can just do:
//!
//! ```
//! # use iron::prelude::*;
//! # use iron::status;
//! # use iron::modifiers::Redirect;
//! # use iron::Url;
//!
//! let url = Url::parse("http://doc.rust-lang.org").unwrap();
//! Response::with((status::Found, Redirect(url)));
//! ```
//!
//! This is more extensible as it allows you to combine
//! arbitrary modifiers without having a massive number of
//! Response constructors.
//!
//! For more information about the modifier system, see
//! [rust-modifier](https://github.com/reem/rust-modifier).

use std::fs::File;
use std::io;
use std::path::{Path, PathBuf};

use modifier::Modifier;

use hyper::mime::Mime;

use {status, headers, Request, Response, Set, Url};

use mime_types;
use response::{WriteBody, BodyReader};

lazy_static! {
    static ref MIME_TYPES: mime_types::Types = mime_types::Types::new().unwrap();
}

impl Modifier<Response> for Mime {
    #[inline]
    fn modify(self, res: &mut Response) {
        res.headers.set(headers::ContentType(self))
    }
}

impl Modifier<Response> for Box<WriteBody> {
    #[inline]
    fn modify(self, res: &mut Response) {
        res.body = Some(self);
    }
}

impl <R: io::Read + Send + 'static> Modifier<Response> for BodyReader<R> {
    #[inline]
    fn modify(self, res: &mut Response) {
        res.body = Some(Box::new(self));
    }
}

impl Modifier<Response> for String {
    #[inline]
    fn modify(self, res: &mut Response) {
        self.into_bytes().modify(res);
    }
}

impl Modifier<Response> for Vec<u8> {
    #[inline]
    fn modify(self, res: &mut Response) {
        res.headers.set(headers::ContentLength(self.len() as u64));
        res.body = Some(Box::new(self));
    }
}

impl<'a> Modifier<Response> for &'a str {
    #[inline]
    fn modify(self, res: &mut Response) {
        self.to_owned().modify(res);
    }
}

impl<'a> Modifier<Response> for &'a [u8] {
    #[inline]
    fn modify(self, res: &mut Response) {
        self.to_vec().modify(res);
    }
}

impl Modifier<Response> for File {
    fn modify(self, res: &mut Response) {
        // Set the content type based on the file extension if a path is available.
        if let Ok(metadata) = self.metadata() {
            res.headers.set(headers::ContentLength(metadata.len()));
        }

        res.body = Some(Box::new(self));
    }
}

impl<'a> Modifier<Response> for &'a Path {
    /// Set the body to the contents of the File at this path.
    ///
    /// ## Panics
    ///
    /// Panics if there is no file at the passed-in Path.
    fn modify(self, res: &mut Response) {
        File::open(self)
            .expect(&format!("No such file: {}", self.display()))
            .modify(res);

        let mime_str = MIME_TYPES.mime_for_path(self);
        let _ = mime_str.parse().map(|mime: Mime| res.set_mut(mime));
    }
}

impl Modifier<Response> for PathBuf {
    /// Set the body to the contents of the File at this path.
    ///
    /// ## Panics
    ///
    /// Panics if there is no file at the passed-in Path.
    fn modify(self, res: &mut Response) {
        File::open(&self)
            .expect(&format!("No such file: {}", self.display()))
            .modify(res);
    }
}

impl Modifier<Response> for status::Status {
    fn modify(self, res: &mut Response) {
        res.status = Some(self);
    }
}

/// A modifier for changing headers on requests and responses.
pub struct Header<H: headers::Header + headers::HeaderFormat>(pub H);

impl<H> Modifier<Response> for Header<H>
where H: headers::Header + headers::HeaderFormat {
    fn modify(self, res: &mut Response) {
        res.headers.set(self.0);
    }
}

impl<'a, 'b, H> Modifier<Request<'a, 'b>> for Header<H>
where H: headers::Header + headers::HeaderFormat {
    fn modify(self, res: &mut Request) {
        res.headers.set(self.0);
    }
}

/// A modifier for creating redirect responses.
pub struct Redirect(pub Url);

impl Modifier<Response> for Redirect {
    fn modify(self, res: &mut Response) {
        let Redirect(url) = self;
        res.headers.set(headers::Location(url.to_string()));
    }
}

/// A modifier for creating redirect responses.
pub struct RedirectRaw(String);

impl Modifier<Response> for RedirectRaw {
    fn modify(self, res: &mut Response) {
        let RedirectRaw(path) = self;
        res.headers.set(headers::Location(path));
    }
}
<commit_msg>Expand docs on modifiers<commit_after>//! This module defines a series of convenience modifiers for changing
//! Responses.
//!
//! Modifiers can be used to edit `Response`s through the owning method `set`
//! or the mutating `set_mut`, both of which are defined in the `Set` trait.
//!
//! For Iron, the `Modifier` interface offers extensible and ergonomic response
//! creation while avoiding the introduction of many highly specific `Response`
//! constructors.
//!
//! The simplest case of a modifier is probably the one used to change the
//! return status code:
//!
//! ```
//! # use iron::prelude::*;
//! # use iron::status;
//! let r = Response::with(status::NotFound);
//! assert_eq!(r.status.unwrap().to_u16(), 404);
//! ```
//!
//! You can also pass in a tuple of modifiers, they will all be applied. Here's
//! an example of a modifier 2-tuple that will change the status code and the
//! body message:
//!
//! ```
//! # use iron::prelude::*;
//! # use iron::status;
//! Response::with((status::ImATeapot, "I am a tea pot!"));
//! ```
//!
//! There is also a `Redirect` modifier:
//!
//! ```
//! # use iron::prelude::*;
//! # use iron::status;
//! # use iron::modifiers;
//! # use iron::Url;
//! let url = Url::parse("http://doc.rust-lang.org").unwrap();
//! Response::with((status::Found, modifiers::Redirect(url)));
//! ```
//!
//! The modifiers are applied depending on their type. Currently the easiest
//! way to see how different types are used as modifiers, take a look at [the
//! source code](https://github.com/iron/iron/blob/master/src/modifiers.rs).
//!
//! For more information about the modifier system, see
//! [rust-modifier](https://github.com/reem/rust-modifier).

use std::fs::File;
use std::io;
use std::path::{Path, PathBuf};

use modifier::Modifier;

use hyper::mime::Mime;

use {status, headers, Request, Response, Set, Url};

use mime_types;
use response::{WriteBody, BodyReader};

lazy_static! {
    static ref MIME_TYPES: mime_types::Types = mime_types::Types::new().unwrap();
}

impl Modifier<Response> for Mime {
    #[inline]
    fn modify(self, res: &mut Response) {
        res.headers.set(headers::ContentType(self))
    }
}

impl Modifier<Response> for Box<WriteBody> {
    #[inline]
    fn modify(self, res: &mut Response) {
        res.body = Some(self);
    }
}

impl <R: io::Read + Send + 'static> Modifier<Response> for BodyReader<R> {
    #[inline]
    fn modify(self, res: &mut Response) {
        res.body = Some(Box::new(self));
    }
}

impl Modifier<Response> for String {
    #[inline]
    fn modify(self, res: &mut Response) {
        self.into_bytes().modify(res);
    }
}

impl Modifier<Response> for Vec<u8> {
    #[inline]
    fn modify(self, res: &mut Response) {
        res.headers.set(headers::ContentLength(self.len() as u64));
        res.body = Some(Box::new(self));
    }
}

impl<'a> Modifier<Response> for &'a str {
    #[inline]
    fn modify(self, res: &mut Response) {
        self.to_owned().modify(res);
    }
}

impl<'a> Modifier<Response> for &'a [u8] {
    #[inline]
    fn modify(self, res: &mut Response) {
        self.to_vec().modify(res);
    }
}

impl Modifier<Response> for File {
    fn modify(self, res: &mut Response) {
        // Set the content type based on the file extension if a path is available.
        if let Ok(metadata) = self.metadata() {
            res.headers.set(headers::ContentLength(metadata.len()));
        }

        res.body = Some(Box::new(self));
    }
}

impl<'a> Modifier<Response> for &'a Path {
    /// Set the body to the contents of the File at this path.
    ///
    /// ## Panics
    ///
    /// Panics if there is no file at the passed-in Path.
    fn modify(self, res: &mut Response) {
        File::open(self)
            .expect(&format!("No such file: {}", self.display()))
            .modify(res);

        let mime_str = MIME_TYPES.mime_for_path(self);
        let _ = mime_str.parse().map(|mime: Mime| res.set_mut(mime));
    }
}

impl Modifier<Response> for PathBuf {
    /// Set the body to the contents of the File at this path.
    ///
    /// ## Panics
    ///
    /// Panics if there is no file at the passed-in Path.
    fn modify(self, res: &mut Response) {
        File::open(&self)
            .expect(&format!("No such file: {}", self.display()))
            .modify(res);
    }
}

impl Modifier<Response> for status::Status {
    fn modify(self, res: &mut Response) {
        res.status = Some(self);
    }
}

/// A modifier for changing headers on requests and responses.
pub struct Header<H: headers::Header + headers::HeaderFormat>(pub H);

impl<H> Modifier<Response> for Header<H>
where H: headers::Header + headers::HeaderFormat {
    fn modify(self, res: &mut Response) {
        res.headers.set(self.0);
    }
}

impl<'a, 'b, H> Modifier<Request<'a, 'b>> for Header<H>
where H: headers::Header + headers::HeaderFormat {
    fn modify(self, res: &mut Request) {
        res.headers.set(self.0);
    }
}

/// A modifier for creating redirect responses.
pub struct Redirect(pub Url);

impl Modifier<Response> for Redirect {
    fn modify(self, res: &mut Response) {
        let Redirect(url) = self;
        res.headers.set(headers::Location(url.to_string()));
    }
}

/// A modifier for creating redirect responses.
pub struct RedirectRaw(String);

impl Modifier<Response> for RedirectRaw {
    fn modify(self, res: &mut Response) {
        let RedirectRaw(path) = self;
        res.headers.set(headers::Location(path));
    }
}
<|endoftext|>