<commit_before>use libc;
use std;
use std::cell::RefCell;
use std::ptr;
use std::rc::Rc;
use std::str;

use attributes::Attribute;
use css;
use handle::{self, RsvgHandle};
use load::rsvg_load_new_node;
use node::{node_new, Node, NodeType};
use property_bag::PropertyBag;
use structure::NodeSvg;
use text::NodeChars;
use tree::{RsvgTree, Tree};
use util::utf8_cstr;

/// A trait for processing a certain kind of XML subtree
///
/// In the "normal" state of processing, an `XmlHandler` may create an RsvgNode
/// for each SVG element it finds, and create NodeChars inside those nodes when it
/// encounters character data.
///
/// There may be other, special contexts for different subtrees, for example,
/// for the `<style>` element.
trait XmlHandler {
    /// Called when the XML parser sees the beginning of an element
    fn start_element(
        &self,
        parent: Option<&Rc<Node>>,
        handle: *mut RsvgHandle,
        name: &str,
        pbag: &PropertyBag,
    ) -> Box<XmlHandler>;

    /// Called when the XML parser sees the end of an element.
    fn end_element(&self, handle: *mut RsvgHandle, name: &str) -> Option<Rc<Node>>;

    /// Called when the XML parser sees character data or CDATA
    fn characters(&self, text: &str);

    fn get_node(&self) -> Option<Rc<Node>> {
        None
    }
}

struct NodeCreationContext {
    node: Option<Rc<Node>>,
}

impl XmlHandler for NodeCreationContext {
    fn start_element(
        &self,
        parent: Option<&Rc<Node>>,
        handle: *mut RsvgHandle,
        name: &str,
        pbag: &PropertyBag,
    ) -> Box<XmlHandler> {
        if name == "style" {
            let ctx = StyleContext::empty();
            StyleContext::start_element(&ctx, parent, handle, name, pbag)
        } else {
            let node = self.create_node(parent, handle, name, pbag);

            Box::new(NodeCreationContext { node: Some(node) })
        }
    }

    fn end_element(&self, handle: *mut RsvgHandle, _name: &str) -> Option<Rc<Node>> {
        let node = self.node.as_ref().unwrap().clone();

        // The "svg" node is special; it parses its style attributes
        // here, not during element creation.
        if node.get_type() == NodeType::Svg {
            node.with_impl(|svg: &NodeSvg| {
                svg.parse_style_attributes(&node, handle);
            });
        }

        Some(node)
    }

    fn characters(&self, text: &str) {
        let node = self.node.as_ref().unwrap();

        if text.len() == 0 {
            return;
        }

        if node.accept_chars() {
            let chars_node = if let Some(child) = node.find_last_chars_child() {
                child
            } else {
                let child = node_new(
                    NodeType::Chars,
                    Some(&node),
                    None,
                    None,
                    Box::new(NodeChars::new()),
                );
                node.add_child(&child);
                child
            };

            chars_node.with_impl(|chars: &NodeChars| {
                chars.append(text);
            });
        }
    }

    fn get_node(&self) -> Option<Rc<Node>> {
        Some(self.node.as_ref().unwrap().clone())
    }
}

impl NodeCreationContext {
    fn empty() -> NodeCreationContext {
        NodeCreationContext { node: None }
    }

    fn create_node(
        &self,
        parent: Option<&Rc<Node>>,
        handle: *mut RsvgHandle,
        name: &str,
        pbag: &PropertyBag,
    ) -> Rc<Node> {
        let mut defs = handle::get_defs(handle);

        let new_node = rsvg_load_new_node(name, parent, pbag, &mut defs);

        if let Some(parent) = parent {
            parent.add_child(&new_node);
        }

        new_node.set_atts(&new_node, handle, pbag);

        // The "svg" node is special; it will parse its style attributes
        // until the end, in standard_element_end().
        if new_node.get_type() != NodeType::Svg {
            new_node.parse_style_attributes(handle, name, pbag);
        }

        new_node.set_overridden_properties();

        new_node
    }
}

/// Handles the `<style>` element by parsing its character contents as CSS
struct StyleContext {
    is_text_css: bool,
    text: RefCell<String>,
}

impl XmlHandler for StyleContext {
    fn start_element(
        &self,
        _parent: Option<&Rc<Node>>,
        _handle: *mut RsvgHandle,
        _name: &str,
        pbag: &PropertyBag,
    ) -> Box<XmlHandler> {
        // FIXME: See these:
        //
        // https://www.w3.org/TR/SVG/styling.html#StyleElementTypeAttribute
        // https://www.w3.org/TR/SVG/styling.html#ContentStyleTypeAttribute
        //
        // If the "type" attribute is not present, we should fallback to the
        // "contentStyleType" attribute of the svg element, which in turn
        // defaults to "text/css".
        //
        // See where is_text_css is used to see where we parse the contents
        // of the style element.

        let mut is_text_css = true;

        for (_key, attr, value) in pbag.iter() {
            if attr == Attribute::Type {
                is_text_css = value == "text/css";
            }
        }

        Box::new(StyleContext {
            is_text_css,
            text: RefCell::new(String::new()),
        })
    }

    fn end_element(&self, handle: *mut RsvgHandle, _name: &str) -> Option<Rc<Node>> {
        if self.is_text_css {
            let text = self.text.borrow();
            css::parse_into_handle(handle, &text);
        }

        None
    }

    fn characters(&self, text: &str) {
        self.text.borrow_mut().push_str(text);
    }
}

impl StyleContext {
    fn empty() -> StyleContext {
        StyleContext {
            is_text_css: false,
            text: RefCell::new(String::new()),
        }
    }
}

/// A concrete parsing context for a surrounding `element_name` and its XML event handlers
struct Context {
    element_name: String,
    handler: Box<XmlHandler>,
}

// A *const RsvgXmlState is just the type that we export to C
pub enum RsvgXmlState {}

/// Holds the state used for XML processing
///
/// These methods are called when an XML event is parsed out of the XML stream: `start_element`,
/// `end_element`, `characters`.
///
/// When an element starts, we push a corresponding `Context` into the `context_stack`.  Within
/// that context, all XML events will be forwarded to it, and processed in one of the `XmlHandler`
/// trait objects. Normally the context refers to a `NodeCreationContext` implementation which is
/// what creates normal graphical elements.
///
/// When we get to a `<style>` element, we push a `StyleContext`, which processes its contents
/// specially.
struct XmlState {
    tree: Option<Box<Tree>>,

    context_stack: Vec<Context>,
}

impl XmlState {
    fn new() -> XmlState {
        XmlState {
            tree: None,
            context_stack: Vec::new(),
        }
    }

    pub fn set_root(&mut self, root: &Rc<Node>) {
        if self.tree.is_some() {
            panic!("The tree root has already been set");
        }

        self.tree = Some(Box::new(Tree::new(root)));
    }

    pub fn steal_tree(&mut self) -> Option<Box<Tree>> {
        self.tree.take()
    }

    pub fn start_element(&mut self, handle: *mut RsvgHandle, name: &str, pbag: &PropertyBag) {
        let next_context = if let Some(top) = self.context_stack.last() {
            top.handler
                .start_element(top.handler.get_node().as_ref(), handle, name, pbag)
        } else {
            let default_context = NodeCreationContext::empty();

            default_context.start_element(None, handle, name, pbag)
        };

        let context = Context {
            element_name: name.to_string(),
            handler: next_context,
        };

        self.context_stack.push(context);
    }

    pub fn end_element(&mut self, handle: *mut RsvgHandle, name: &str) {
        if let Some(top) = self.context_stack.pop() {
            assert!(name == top.element_name);

            if let Some(node) = top.handler.end_element(handle, name) {
                if self.context_stack.is_empty() {
                    self.set_root(&node);
                }
            }
        } else {
            panic!("end_element: XML handler stack is empty!?");
        }
    }

    pub fn characters(&mut self, text: &str) {
        if let Some(top) = self.context_stack.last() {
            top.handler.characters(text);
        } else {
            panic!("characters: XML handler stack is empty!?");
        }
    }
}

#[no_mangle]
pub extern "C" fn rsvg_xml_state_new() -> *mut RsvgXmlState {
    Box::into_raw(Box::new(XmlState::new())) as *mut RsvgXmlState
}

#[no_mangle]
pub extern "C" fn rsvg_xml_state_free(xml: *mut RsvgXmlState) {
    assert!(!xml.is_null());
    let xml = unsafe { &mut *(xml as *mut XmlState) };
    unsafe {
        Box::from_raw(xml);
    }
}

#[no_mangle]
pub extern "C" fn rsvg_xml_state_steal_tree(xml: *mut RsvgXmlState) -> *mut RsvgTree {
    assert!(!xml.is_null());
    let xml = unsafe { &mut *(xml as *mut XmlState) };

    if let Some(tree) = xml.steal_tree() {
        Box::into_raw(tree) as *mut RsvgTree
    } else {
        ptr::null_mut()
    }
}

#[no_mangle]
pub extern "C" fn rsvg_xml_state_start_element(
    xml: *mut RsvgXmlState,
    handle: *mut RsvgHandle,
    name: *const libc::c_char,
    pbag: *const PropertyBag,
) {
    assert!(!xml.is_null());
    let xml = unsafe { &mut *(xml as *mut XmlState) };

    assert!(!name.is_null());
    let name = unsafe { utf8_cstr(name) };

    assert!(!pbag.is_null());
    let pbag = unsafe { &*pbag };

    xml.start_element(handle, name, pbag);
}

#[no_mangle]
pub extern "C" fn rsvg_xml_state_end_element(
    xml: *mut RsvgXmlState,
    handle: *mut RsvgHandle,
    name: *const libc::c_char,
) {
    assert!(!xml.is_null());
    let xml = unsafe { &mut *(xml as *mut XmlState) };

    assert!(!name.is_null());
    let name = unsafe { utf8_cstr(name) };

    xml.end_element(handle, name);
}

#[no_mangle]
pub extern "C" fn rsvg_xml_state_characters(
    xml: *mut RsvgXmlState,
    unterminated_text: *const libc::c_char,
    len: usize,
) {
    assert!(!xml.is_null());
    let xml = unsafe { &mut *(xml as *mut XmlState) };

    assert!(!unterminated_text.is_null());

    // libxml2 already validated the incoming string as UTF-8.  Note that
    // it is *not* nul-terminated; this is why we create a byte slice first.
    let bytes = unsafe { std::slice::from_raw_parts(unterminated_text as *const u8, len) };
    let utf8 = unsafe { str::from_utf8_unchecked(bytes) };

    xml.characters(utf8);
}
<commit_msg>end_element: rename svg.set_delayed_style()<commit_after>use libc;
use std;
use std::cell::RefCell;
use std::ptr;
use std::rc::Rc;
use std::str;

use attributes::Attribute;
use css;
use handle::{self, RsvgHandle};
use load::rsvg_load_new_node;
use node::{node_new, Node, NodeType};
use property_bag::PropertyBag;
use structure::NodeSvg;
use text::NodeChars;
use tree::{RsvgTree, Tree};
use util::utf8_cstr;

/// A trait for processing a certain kind of XML subtree
///
/// In the "normal" state of processing, an `XmlHandler` may create an RsvgNode
/// for each SVG element it finds, and create NodeChars inside those nodes when it
/// encounters character data.
///
/// There may be other, special contexts for different subtrees, for example,
/// for the `<style>` element.
trait XmlHandler {
    /// Called when the XML parser sees the beginning of an element
    fn start_element(
        &self,
        parent: Option<&Rc<Node>>,
        handle: *mut RsvgHandle,
        name: &str,
        pbag: &PropertyBag,
    ) -> Box<XmlHandler>;

    /// Called when the XML parser sees the end of an element.
    fn end_element(&self, handle: *mut RsvgHandle, name: &str) -> Option<Rc<Node>>;

    /// Called when the XML parser sees character data or CDATA
    fn characters(&self, text: &str);

    fn get_node(&self) -> Option<Rc<Node>> {
        None
    }
}

struct NodeCreationContext {
    node: Option<Rc<Node>>,
}

impl XmlHandler for NodeCreationContext {
    fn start_element(
        &self,
        parent: Option<&Rc<Node>>,
        handle: *mut RsvgHandle,
        name: &str,
        pbag: &PropertyBag,
    ) -> Box<XmlHandler> {
        if name == "style" {
            let ctx = StyleContext::empty();
            StyleContext::start_element(&ctx, parent, handle, name, pbag)
        } else {
            let node = self.create_node(parent, handle, name, pbag);

            Box::new(NodeCreationContext { node: Some(node) })
        }
    }

    fn end_element(&self, handle: *mut RsvgHandle, _name: &str) -> Option<Rc<Node>> {
        let node = self.node.as_ref().unwrap().clone();

        // The "svg" node is special; it parses its style attributes
        // here, not during element creation.
        if node.get_type() == NodeType::Svg {
            node.with_impl(|svg: &NodeSvg| {
                svg.set_delayed_style(&node, handle);
            });
        }

        Some(node)
    }

    fn characters(&self, text: &str) {
        let node = self.node.as_ref().unwrap();

        if text.len() == 0 {
            return;
        }

        if node.accept_chars() {
            let chars_node = if let Some(child) = node.find_last_chars_child() {
                child
            } else {
                let child = node_new(
                    NodeType::Chars,
                    Some(&node),
                    None,
                    None,
                    Box::new(NodeChars::new()),
                );
                node.add_child(&child);
                child
            };

            chars_node.with_impl(|chars: &NodeChars| {
                chars.append(text);
            });
        }
    }

    fn get_node(&self) -> Option<Rc<Node>> {
        Some(self.node.as_ref().unwrap().clone())
    }
}

impl NodeCreationContext {
    fn empty() -> NodeCreationContext {
        NodeCreationContext { node: None }
    }

    fn create_node(
        &self,
        parent: Option<&Rc<Node>>,
        handle: *mut RsvgHandle,
        name: &str,
        pbag: &PropertyBag,
    ) -> Rc<Node> {
        let mut defs = handle::get_defs(handle);

        let new_node = rsvg_load_new_node(name, parent, pbag, &mut defs);

        if let Some(parent) = parent {
            parent.add_child(&new_node);
        }

        new_node.set_atts(&new_node, handle, pbag);

        // The "svg" node is special; it will parse its style attributes
        // until the end, in standard_element_end().
        if new_node.get_type() != NodeType::Svg {
            new_node.parse_style_attributes(handle, name, pbag);
        }

        new_node.set_overridden_properties();

        new_node
    }
}

/// Handles the `<style>` element by parsing its character contents as CSS
struct StyleContext {
    is_text_css: bool,
    text: RefCell<String>,
}

impl XmlHandler for StyleContext {
    fn start_element(
        &self,
        _parent: Option<&Rc<Node>>,
        _handle: *mut RsvgHandle,
        _name: &str,
        pbag: &PropertyBag,
    ) -> Box<XmlHandler> {
        // FIXME: See these:
        //
        // https://www.w3.org/TR/SVG/styling.html#StyleElementTypeAttribute
        // https://www.w3.org/TR/SVG/styling.html#ContentStyleTypeAttribute
        //
        // If the "type" attribute is not present, we should fallback to the
        // "contentStyleType" attribute of the svg element, which in turn
        // defaults to "text/css".
        //
        // See where is_text_css is used to see where we parse the contents
        // of the style element.

        let mut is_text_css = true;

        for (_key, attr, value) in pbag.iter() {
            if attr == Attribute::Type {
                is_text_css = value == "text/css";
            }
        }

        Box::new(StyleContext {
            is_text_css,
            text: RefCell::new(String::new()),
        })
    }

    fn end_element(&self, handle: *mut RsvgHandle, _name: &str) -> Option<Rc<Node>> {
        if self.is_text_css {
            let text = self.text.borrow();
            css::parse_into_handle(handle, &text);
        }

        None
    }

    fn characters(&self, text: &str) {
        self.text.borrow_mut().push_str(text);
    }
}

impl StyleContext {
    fn empty() -> StyleContext {
        StyleContext {
            is_text_css: false,
            text: RefCell::new(String::new()),
        }
    }
}

/// A concrete parsing context for a surrounding `element_name` and its XML event handlers
struct Context {
    element_name: String,
    handler: Box<XmlHandler>,
}

// A *const RsvgXmlState is just the type that we export to C
pub enum RsvgXmlState {}

/// Holds the state used for XML processing
///
/// These methods are called when an XML event is parsed out of the XML stream: `start_element`,
/// `end_element`, `characters`.
///
/// When an element starts, we push a corresponding `Context` into the `context_stack`.  Within
/// that context, all XML events will be forwarded to it, and processed in one of the `XmlHandler`
/// trait objects. Normally the context refers to a `NodeCreationContext` implementation which is
/// what creates normal graphical elements.
///
/// When we get to a `<style>` element, we push a `StyleContext`, which processes its contents
/// specially.
struct XmlState {
    tree: Option<Box<Tree>>,

    context_stack: Vec<Context>,
}

impl XmlState {
    fn new() -> XmlState {
        XmlState {
            tree: None,
            context_stack: Vec::new(),
        }
    }

    pub fn set_root(&mut self, root: &Rc<Node>) {
        if self.tree.is_some() {
            panic!("The tree root has already been set");
        }

        self.tree = Some(Box::new(Tree::new(root)));
    }

    pub fn steal_tree(&mut self) -> Option<Box<Tree>> {
        self.tree.take()
    }

    pub fn start_element(&mut self, handle: *mut RsvgHandle, name: &str, pbag: &PropertyBag) {
        let next_context = if let Some(top) = self.context_stack.last() {
            top.handler
                .start_element(top.handler.get_node().as_ref(), handle, name, pbag)
        } else {
            let default_context = NodeCreationContext::empty();

            default_context.start_element(None, handle, name, pbag)
        };

        let context = Context {
            element_name: name.to_string(),
            handler: next_context,
        };

        self.context_stack.push(context);
    }

    pub fn end_element(&mut self, handle: *mut RsvgHandle, name: &str) {
        if let Some(top) = self.context_stack.pop() {
            assert!(name == top.element_name);

            if let Some(node) = top.handler.end_element(handle, name) {
                if self.context_stack.is_empty() {
                    self.set_root(&node);
                }
            }
        } else {
            panic!("end_element: XML handler stack is empty!?");
        }
    }

    pub fn characters(&mut self, text: &str) {
        if let Some(top) = self.context_stack.last() {
            top.handler.characters(text);
        } else {
            panic!("characters: XML handler stack is empty!?");
        }
    }
}

#[no_mangle]
pub extern "C" fn rsvg_xml_state_new() -> *mut RsvgXmlState {
    Box::into_raw(Box::new(XmlState::new())) as *mut RsvgXmlState
}

#[no_mangle]
pub extern "C" fn rsvg_xml_state_free(xml: *mut RsvgXmlState) {
    assert!(!xml.is_null());
    let xml = unsafe { &mut *(xml as *mut XmlState) };
    unsafe {
        Box::from_raw(xml);
    }
}

#[no_mangle]
pub extern "C" fn rsvg_xml_state_steal_tree(xml: *mut RsvgXmlState) -> *mut RsvgTree {
    assert!(!xml.is_null());
    let xml = unsafe { &mut *(xml as *mut XmlState) };

    if let Some(tree) = xml.steal_tree() {
        Box::into_raw(tree) as *mut RsvgTree
    } else {
        ptr::null_mut()
    }
}

#[no_mangle]
pub extern "C" fn rsvg_xml_state_start_element(
    xml: *mut RsvgXmlState,
    handle: *mut RsvgHandle,
    name: *const libc::c_char,
    pbag: *const PropertyBag,
) {
    assert!(!xml.is_null());
    let xml = unsafe { &mut *(xml as *mut XmlState) };

    assert!(!name.is_null());
    let name = unsafe { utf8_cstr(name) };

    assert!(!pbag.is_null());
    let pbag = unsafe { &*pbag };

    xml.start_element(handle, name, pbag);
}

#[no_mangle]
pub extern "C" fn rsvg_xml_state_end_element(
    xml: *mut RsvgXmlState,
    handle: *mut RsvgHandle,
    name: *const libc::c_char,
) {
    assert!(!xml.is_null());
    let xml = unsafe { &mut *(xml as *mut XmlState) };

    assert!(!name.is_null());
    let name = unsafe { utf8_cstr(name) };

    xml.end_element(handle, name);
}

#[no_mangle]
pub extern "C" fn rsvg_xml_state_characters(
    xml: *mut RsvgXmlState,
    unterminated_text: *const libc::c_char,
    len: usize,
) {
    assert!(!xml.is_null());
    let xml = unsafe { &mut *(xml as *mut XmlState) };

    assert!(!unterminated_text.is_null());

    // libxml2 already validated the incoming string as UTF-8.  Note that
    // it is *not* nul-terminated; this is why we create a byte slice first.
    let bytes = unsafe { std::slice::from_raw_parts(unterminated_text as *const u8, len) };
    let utf8 = unsafe { str::from_utf8_unchecked(bytes) };

    xml.characters(utf8);
}
<|endoftext|>


<commit_before>#![feature(macro_rules)]

extern crate civet;
extern crate conduit;
extern crate green;
extern crate rustuv;

use std::io::{IoResult, MemReader, MemWriter};
use std::collections::HashMap;

use civet::{Config, Server, response};
use conduit::{Request, Response};

macro_rules! http_write(
    ($dst:expr, $fmt:expr $($arg:tt)*) => (
        try!(write!($dst, concat!($fmt, "\r\n") $($arg)*))
    )
)

fn main() {
    let _a = Server::start(Config { port: 8888, threads: 50 }, handler);
    wait_for_sigint();
}

// libnative doesn't have signal handling yet
fn wait_for_sigint() {
    use green::{SchedPool, PoolConfig, GreenTaskBuilder};
    use std::io::signal::{Listener, Interrupt};
    use std::task::TaskBuilder;

    let mut config = PoolConfig::new();
    config.event_loop_factory = rustuv::event_loop;

    let mut pool = SchedPool::new(config);
    TaskBuilder::new().green(&mut pool).spawn(proc() {
        let mut l = Listener::new();
        l.register(Interrupt).unwrap();
        l.rx.recv();
    });
    pool.shutdown();
}

fn handler(req: &mut Request) -> IoResult<Response> {
    let mut res = MemWriter::with_capacity(10000);

    http_write!(res, "");
    http_write!(res, "
HTTP {}
", req.http_version());
    http_write!(res, "
Method: {}
", req.method());
    http_write!(res, "
Scheme: {}
", req.scheme());
    http_write!(res, "
Host: {}
", req.host());
    http_write!(res, "
Path: {}
", req.path());
    http_write!(res, "
Query String: {}
", req.query_string());
    http_write!(res, "
Remote IP: {}
", req.remote_ip());
    http_write!(res, "
Content Length: {}
", req.content_length());

    http_write!(res, "
Input: {}", req.body().read_to_string());

    http_write!(res, "
Headers

");

    for (key, value) in req.headers().iter() {
        http_write!(res, "
{} = {}
", key, value);
    }

    http_write!(res, "
");

    let mut headers = HashMap::new();
    headers.insert("Content-Type".to_string(), vec!("text/html".to_string()));

    let body = MemReader::new(res.unwrap());

    Ok(response(200i, headers, body))
}
<commit_msg>Comment out a bit of a test for now<commit_after>#![feature(macro_rules)]

extern crate civet;
extern crate conduit;
extern crate green;
extern crate rustuv;

use std::io::{IoResult, MemReader, MemWriter};
use std::collections::HashMap;

use civet::{Config, Server, response};
use conduit::{Request, Response};

macro_rules! http_write(
    ($dst:expr, $fmt:expr $($arg:tt)*) => (
        try!(write!($dst, concat!($fmt, "\r\n") $($arg)*))
    )
)

fn main() {
    let _a = Server::start(Config { port: 8888, threads: 50 }, handler);
    wait_for_sigint();
}

// libnative doesn't have signal handling yet
fn wait_for_sigint() {
    use green::{SchedPool, PoolConfig, GreenTaskBuilder};
    use std::io::signal::{Listener, Interrupt};
    use std::task::TaskBuilder;

    let mut config = PoolConfig::new();
    config.event_loop_factory = rustuv::event_loop;

    let mut pool = SchedPool::new(config);
    TaskBuilder::new().green(&mut pool).spawn(proc() {
        let mut l = Listener::new();
        l.register(Interrupt).unwrap();
        l.rx.recv();
    });
    pool.shutdown();
}

fn handler(req: &mut Request) -> IoResult<Response> {
    let mut res = MemWriter::with_capacity(10000);

    http_write!(res, "");
    http_write!(res, "
HTTP {}
", req.http_version());
    http_write!(res, "
Method: {}
", req.method());
    http_write!(res, "
Scheme: {}
", req.scheme());
    http_write!(res, "
Host: {}
", req.host());
    http_write!(res, "
Path: {}
", req.path());
    http_write!(res, "
Query String: {}
", req.query_string());
    http_write!(res, "
Remote IP: {}
", req.remote_ip());
    http_write!(res, "
Content Length: {}
", req.content_length());

    http_write!(res, "
Input: {}", req.body().read_to_string());

    http_write!(res, "
Headers

");

    // for (key, value) in req.headers().iter() {
    //     http_write!(res, "
{} = {}
", key, value);
    // }

    http_write!(res, "
");

    let mut headers = HashMap::new();
    headers.insert("Content-Type".to_string(), vec!("text/html".to_string()));

    let body = MemReader::new(res.unwrap());

    Ok(response(200i, headers, body))
}
<|endoftext|>


<commit_before>extern crate syntect;
use std::path::Path;
use syntect::parsing::SyntaxSet;
use syntect::highlighting::{Theme, ThemeSet, Style};
use syntect::util::as_24_bit_terminal_escaped;
use syntect::easy::HighlightFile;
use syntect::dumps::{from_dump_file, dump_to_file};

use std::io::BufRead;

fn load_theme(tm_file: &String) -> Theme {
    let tm_path = Path::new(tm_file);
    let tm_cache = tm_path.with_extension("themedump");

    if tm_cache.exists() {
        from_dump_file(tm_cache).unwrap()
    } else {
        let theme = ThemeSet::get_theme(tm_path).unwrap();
        dump_to_file(&theme, tm_cache).unwrap();
        theme

    }
}

fn main() {
    let ss = SyntaxSet::load_defaults_newlines(); // note we load the version with newlines
    let ts = ThemeSet::load_defaults();

    let args: Vec<String> = std::env::args().collect();
    let src;
    let theme;
    let theme_ref;

    if args.len() < 2 {
        println!("USAGE: ./syncat [THEME_FILE] SRC_FILE");
        println!("       ./syncat --list-file-types");
        return;

    } else if args.len() > 1 && args[1] == "--list-file-types" {
        println!("Supported file types:");

        for sd in ss.syntaxes() {
            println!("- {} (.{})", sd.name, sd.file_extensions.join(", ."));
        }

        return;

    } else if args.len() == 2 {
        theme_ref = &ts.themes["base16-ocean.dark"];
        src = &args[1];

    } else {
        theme = load_theme(&args[1]);
        theme_ref = &theme;
        src = &args[2];
    }

    let mut highlighter = HighlightFile::new(src, &ss, theme_ref).unwrap();

    // We use read_line instead of `for line in highlighter.reader.lines()` because that
    // doesn't return strings with a `\n`, and including the `\n` gets us more robust highlighting.
    // See the documentation for `SyntaxSet::load_syntaxes`.
    // It also allows re-using the line buffer, which should be a tiny bit faster.
    let mut line = String::new();
    while highlighter.reader.read_line(&mut line).unwrap() > 0 {
        {
            let regions: Vec<(Style, &str)> = highlighter.highlight_lines.highlight(&line);
            print!("{}", as_24_bit_terminal_escaped(&regions[..], true));
        }
        line.clear();
    }
}
<commit_msg>syncat: Change extension for Theme to not conflict with ThemeSet<commit_after>extern crate syntect;
use std::path::Path;
use syntect::parsing::SyntaxSet;
use syntect::highlighting::{Theme, ThemeSet, Style};
use syntect::util::as_24_bit_terminal_escaped;
use syntect::easy::HighlightFile;
use syntect::dumps::{from_dump_file, dump_to_file};

use std::io::BufRead;

fn load_theme(tm_file: &String) -> Theme {
    let tm_path = Path::new(tm_file);
    let tm_cache = tm_path.with_extension("tmdump");

    if tm_cache.exists() {
        from_dump_file(tm_cache).unwrap()
    } else {
        let theme = ThemeSet::get_theme(tm_path).unwrap();
        dump_to_file(&theme, tm_cache).unwrap();
        theme

    }
}

fn main() {
    let ss = SyntaxSet::load_defaults_newlines(); // note we load the version with newlines
    let ts = ThemeSet::load_defaults();

    let args: Vec<String> = std::env::args().collect();
    let src;
    let theme;
    let theme_ref;

    if args.len() < 2 {
        println!("USAGE: ./syncat [THEME_FILE] SRC_FILE");
        println!("       ./syncat --list-file-types");
        return;

    } else if args.len() > 1 && args[1] == "--list-file-types" {
        println!("Supported file types:");

        for sd in ss.syntaxes() {
            println!("- {} (.{})", sd.name, sd.file_extensions.join(", ."));
        }

        return;

    } else if args.len() == 2 {
        theme_ref = &ts.themes["base16-ocean.dark"];
        src = &args[1];

    } else {
        theme = load_theme(&args[1]);
        theme_ref = &theme;
        src = &args[2];
    }

    let mut highlighter = HighlightFile::new(src, &ss, theme_ref).unwrap();

    // We use read_line instead of `for line in highlighter.reader.lines()` because that
    // doesn't return strings with a `\n`, and including the `\n` gets us more robust highlighting.
    // See the documentation for `SyntaxSet::load_syntaxes`.
    // It also allows re-using the line buffer, which should be a tiny bit faster.
    let mut line = String::new();
    while highlighter.reader.read_line(&mut line).unwrap() > 0 {
        {
            let regions: Vec<(Style, &str)> = highlighter.highlight_lines.highlight(&line);
            print!("{}", as_24_bit_terminal_escaped(&regions[..], true));
        }
        line.clear();
    }
}
<|endoftext|>


<commit_before>// Zinc, the bare metal stack for rust.
// Copyright 2014 Ben Gamari <bgamari@gmail.com>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use syntax::ast;
use syntax::ast::P;
use syntax::ext::base::ExtCtxt;
use syntax::codemap::DUMMY_SP;
use syntax::ext::build::AstBuilder;
use syntax::ext::quote::rt::ToTokens;
use syntax::parse::token;

use super::Builder;
use super::super::node;
use super::utils;

/// A visitor to build the field setters for primitive registers
pub struct BuildSetters<'a, 'b, 'c> {
  builder: &'a mut Builder,
  cx: &'b ExtCtxt<'c>,
}

impl<'a, 'b, 'c> BuildSetters<'a, 'b, 'c> {
  pub fn new(builder: &'a mut Builder, cx: &'b ExtCtxt<'c>)
             -> BuildSetters<'a, 'b, 'c> {
    BuildSetters { builder: builder, cx: cx }
  }
}

impl<'a, 'b, 'c> node::RegVisitor for BuildSetters<'a, 'b, 'c> {
  fn visit_prim_reg<'a>(&'a mut self, path: &Vec<String>,
      reg: &'a node::Reg, _width: node::RegWidth, fields: &Vec<node::Field>)
  {
    if fields.iter().any(|f| f.access != node::ReadOnly) {
      let it = build_type(self.cx, path, reg, fields);
      self.builder.push_item(it);

      let it = build_drop(self.cx, path, reg, fields);
      self.builder.push_item(it);

      let it = build_impl(self.cx, path, reg, fields);
      self.builder.push_item(it);
    }
  }
}

fn build_type<'a>(cx: &'a ExtCtxt, path: &Vec<String>,
    reg: &node::Reg, _fields: &Vec<node::Field>) -> P<ast::Item>
{
  let packed_ty = utils::reg_primitive_type(cx, reg)
    .expect("Unexpected non-primitive register");
  let name = utils::setter_name(cx, path);
  let reg_ty = cx.ty_ident(DUMMY_SP, utils::path_ident(cx, path));

  let reg_doc = match reg.docstring {
    Some(d) => d.node,
    None => cx.ident_of("no documentation"),
  };
  let docstring = format!("Update value of `{}` register: {}",
                          reg.name.node,
                          reg_doc);
  let doc_attr = utils::doc_attribute(cx, utils::intern_string(cx, docstring));

  let item = quote_item!(cx,
    $doc_attr
    #[allow(non_camel_case_types)]
    pub struct $name {
      value: $packed_ty,
      mask: $packed_ty,
      reg: &'static $reg_ty,
    }
  );
  item.unwrap()
}

fn build_new<'a>(cx: &'a ExtCtxt, path: &Vec<String>)
                 -> P<ast::Item> {
  let reg_ty: P<ast::Ty> =
    cx.ty_ident(DUMMY_SP, utils::path_ident(cx, path));
  let setter_ty: P<ast::Ty> = cx.ty_ident(DUMMY_SP,
                                          utils::setter_name(cx, path));
  let item = quote_item!(cx,
    #[doc="Create a new updater"]
    pub fn new(reg: &'static $reg_ty) -> $setter_ty {
      $setter_ty {
        value: 0,
        mask: 0,
        reg: reg,
      }
    });
  item.unwrap()
}

fn build_drop<'a>(cx: &'a ExtCtxt, path: &Vec<String>,
    reg: &node::Reg, fields: &Vec<node::Field>) -> P<ast::Item>
{
  let setter_ty: P<ast::Ty> = cx.ty_ident(DUMMY_SP,
                                          utils::setter_name(cx, path));
  let unpacked_ty = utils::reg_primitive_type(cx, reg)
    .expect("Unexpected non-primitive register");

  // ensure we don't unintentionally clear a set-to-clear flag
  let mut clear: u32 = 0;
  for f in fields.iter() {
    match f.access {
      node::SetToClear => {
        let mask = 1 << (f.count.node * f.width) - 1;
        clear |= mask;
      },
      _ => {},
    }
  }

  let item = quote_item!(cx,
    #[unsafe_destructor]
    #[doc = "This performs the register update"]
    impl Drop for $setter_ty {
      fn drop(&mut self) {
        let clear_mask: $unpacked_ty = $clear as $unpacked_ty;
        if self.mask != 0 {
          let v: $unpacked_ty = self.reg.value.get() & ! clear_mask & ! self.mask;
          self.reg.value.set(self.value | v);
        }
      }
    }
  );
  item.unwrap()
}

fn build_done<'a>(cx: &'a ExtCtxt) -> P<ast::Method>
{
  quote_method!(cx,
    #[doc="Commit changes to register. This is to allow chains of `set_*` \
           invocations to be used as a statement."]
    pub fn done(self) {}
  )
}

fn build_impl<'a>(cx: &'a ExtCtxt, path: &Vec<String>, reg: &node::Reg,
                  fields: &Vec<node::Field>) -> P<ast::Item>
{
  let new = build_new(cx, path);
  let setter_ty: P<ast::Ty> = cx.ty_ident(
    DUMMY_SP,
    utils::setter_name(cx, path));
  let methods: Vec<P<ast::Method>> =
    FromIterator::from_iter(
      fields.iter()
        .filter_map(|field| build_field_fn(cx, path, reg, field)));
  let done: P<ast::Method> = build_done(cx);
  let impl_ = quote_item!(cx,
    #[allow(dead_code)]
    impl $setter_ty {
      $new
      $methods
      $done
    }
    );
  impl_.unwrap()
}

fn build_field_fn<'a>(cx: &'a ExtCtxt, path: &Vec<String>, reg: &node::Reg,
                      field: &node::Field) -> Option<P<ast::Method>>
{
  match field.access {
    node::ReadOnly => None,
    node::SetToClear => Some(build_field_clear_fn(cx, path, reg, field)),
    _ => Some(build_field_set_fn(cx, path, reg, field)),
  }
}

/// Build a setter for a field
fn build_field_set_fn<'a>(cx: &'a ExtCtxt, path: &Vec<String>, reg: &node::Reg,
                          field: &node::Field) -> P<ast::Method>
{
  let setter_ty = utils::setter_name(cx, path);
  let unpacked_ty = utils::reg_primitive_type(cx, reg)
    .expect("Unexpected non-primitive register");
  let fn_name =
    cx.ident_of((String::from_str("set_")+field.name.node).as_slice());
  let field_ty: P<ast::Ty> =
    cx.ty_path(utils::field_type_path(cx, path, reg, field), None);
  let mask = utils::mask(cx, field);

  let field_doc = match field.docstring {
    Some(d) => token::get_ident(d.node).get().into_string(),
    None => "no documentation".into_string(),
  };
  let docstring = format!("Set value of `{}` field: {}",
                          field.name.node,
                          field_doc);
  let doc_attr = utils::doc_attribute(cx, utils::intern_string(cx, docstring));

  if field.count.node == 1 {
    let shift = utils::shift(cx, None, field);
    quote_method!(cx,
      $doc_attr
      pub fn $fn_name<'a>(&'a mut self, new_value: $field_ty)
          -> &'a mut $setter_ty {
        self.value |= (self.value & ! $mask) | ((new_value as $unpacked_ty) & $mask) << $shift;
        self.mask |= $mask << $shift;
        self
      }
    )
  } else {
    let shift = utils::shift(cx, Some(quote_expr!(cx, idx)), field);
    quote_method!(cx,
      $doc_attr
      pub fn $fn_name<'a>(&'a mut self, idx: uint, new_value: $field_ty)
        -> &'a mut $setter_ty {
          self.value |= (self.value & ! $mask) | ((new_value as $unpacked_ty) & $mask) << $shift;
          self.mask |= $mask << $shift;
          self
      }
    )
  }
}

fn build_field_clear_fn<'a>(cx: &'a ExtCtxt, path: &Vec<String>,
    _: &node::Reg, field: &node::Field) -> P<ast::Method>
{
  let setter_ty = utils::setter_name(cx, path);
  let fn_name =
    cx.ident_of((String::from_str("clear_")+field.name.node).as_slice());
  let mask = utils::mask(cx, field);
  let docstring = match field.docstring {
    Some(d) => utils::doc_attribute(cx, token::get_ident(d.node)).node.to_tokens(cx),
    None => Vec::new(),
  };

  if field.count.node == 1 {
    let shift = utils::shift(cx, None, field);
    quote_method!(cx,
      $docstring
      pub fn $fn_name<'a>(&'a mut self) -> &'a mut $setter_ty {
          self.value |= $mask << $shift;
          self.mask |= $mask << $shift;
          self
      }
    )
  } else {
    let shift = utils::shift(cx, Some(quote_expr!(cx, idx)), field);
    quote_method!(cx,
      $docstring
      pub fn $fn_name<'a>(&'a mut self, idx: uint)
                          -> &'a mut $setter_ty {
          self.value |= $mask << $shift;
          self.mask |= $mask << $shift;
          self
      }
    )
  }
}
<commit_msg>ioreg::setter: Fix clear flag docstring<commit_after>// Zinc, the bare metal stack for rust.
// Copyright 2014 Ben Gamari <bgamari@gmail.com>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use syntax::ast;
use syntax::ast::P;
use syntax::ext::base::ExtCtxt;
use syntax::codemap::DUMMY_SP;
use syntax::ext::build::AstBuilder;
use syntax::ext::quote::rt::ToTokens;
use syntax::parse::token;

use super::Builder;
use super::super::node;
use super::utils;

/// A visitor to build the field setters for primitive registers
pub struct BuildSetters<'a, 'b, 'c> {
  builder: &'a mut Builder,
  cx: &'b ExtCtxt<'c>,
}

impl<'a, 'b, 'c> BuildSetters<'a, 'b, 'c> {
  pub fn new(builder: &'a mut Builder, cx: &'b ExtCtxt<'c>)
             -> BuildSetters<'a, 'b, 'c> {
    BuildSetters { builder: builder, cx: cx }
  }
}

impl<'a, 'b, 'c> node::RegVisitor for BuildSetters<'a, 'b, 'c> {
  fn visit_prim_reg<'a>(&'a mut self, path: &Vec<String>,
      reg: &'a node::Reg, _width: node::RegWidth, fields: &Vec<node::Field>)
  {
    if fields.iter().any(|f| f.access != node::ReadOnly) {
      let it = build_type(self.cx, path, reg, fields);
      self.builder.push_item(it);

      let it = build_drop(self.cx, path, reg, fields);
      self.builder.push_item(it);

      let it = build_impl(self.cx, path, reg, fields);
      self.builder.push_item(it);
    }
  }
}

fn build_type<'a>(cx: &'a ExtCtxt, path: &Vec<String>,
    reg: &node::Reg, _fields: &Vec<node::Field>) -> P<ast::Item>
{
  let packed_ty = utils::reg_primitive_type(cx, reg)
    .expect("Unexpected non-primitive register");
  let name = utils::setter_name(cx, path);
  let reg_ty = cx.ty_ident(DUMMY_SP, utils::path_ident(cx, path));

  let reg_doc = match reg.docstring {
    Some(d) => d.node,
    None => cx.ident_of("no documentation"),
  };
  let docstring = format!("Update value of `{}` register: {}",
                          reg.name.node,
                          reg_doc);
  let doc_attr = utils::doc_attribute(cx, utils::intern_string(cx, docstring));

  let item = quote_item!(cx,
    $doc_attr
    #[allow(non_camel_case_types)]
    pub struct $name {
      value: $packed_ty,
      mask: $packed_ty,
      reg: &'static $reg_ty,
    }
  );
  item.unwrap()
}

fn build_new<'a>(cx: &'a ExtCtxt, path: &Vec<String>)
                 -> P<ast::Item> {
  let reg_ty: P<ast::Ty> =
    cx.ty_ident(DUMMY_SP, utils::path_ident(cx, path));
  let setter_ty: P<ast::Ty> = cx.ty_ident(DUMMY_SP,
                                          utils::setter_name(cx, path));
  let item = quote_item!(cx,
    #[doc="Create a new updater"]
    pub fn new(reg: &'static $reg_ty) -> $setter_ty {
      $setter_ty {
        value: 0,
        mask: 0,
        reg: reg,
      }
    });
  item.unwrap()
}

fn build_drop<'a>(cx: &'a ExtCtxt, path: &Vec<String>,
    reg: &node::Reg, fields: &Vec<node::Field>) -> P<ast::Item>
{
  let setter_ty: P<ast::Ty> = cx.ty_ident(DUMMY_SP,
                                          utils::setter_name(cx, path));
  let unpacked_ty = utils::reg_primitive_type(cx, reg)
    .expect("Unexpected non-primitive register");

  // ensure we don't unintentionally clear a set-to-clear flag
  let mut clear: u32 = 0;
  for f in fields.iter() {
    match f.access {
      node::SetToClear => {
        let mask = 1 << (f.count.node * f.width) - 1;
        clear |= mask;
      },
      _ => {},
    }
  }

  let item = quote_item!(cx,
    #[unsafe_destructor]
    #[doc = "This performs the register update"]
    impl Drop for $setter_ty {
      fn drop(&mut self) {
        let clear_mask: $unpacked_ty = $clear as $unpacked_ty;
        if self.mask != 0 {
          let v: $unpacked_ty = self.reg.value.get() & ! clear_mask & ! self.mask;
          self.reg.value.set(self.value | v);
        }
      }
    }
  );
  item.unwrap()
}

fn build_done<'a>(cx: &'a ExtCtxt) -> P<ast::Method>
{
  quote_method!(cx,
    #[doc="Commit changes to register. This is to allow chains of `set_*` \
           invocations to be used as a statement."]
    pub fn done(self) {}
  )
}

fn build_impl<'a>(cx: &'a ExtCtxt, path: &Vec<String>, reg: &node::Reg,
                  fields: &Vec<node::Field>) -> P<ast::Item>
{
  let new = build_new(cx, path);
  let setter_ty: P<ast::Ty> = cx.ty_ident(
    DUMMY_SP,
    utils::setter_name(cx, path));
  let methods: Vec<P<ast::Method>> =
    FromIterator::from_iter(
      fields.iter()
        .filter_map(|field| build_field_fn(cx, path, reg, field)));
  let done: P<ast::Method> = build_done(cx);
  let impl_ = quote_item!(cx,
    #[allow(dead_code)]
    impl $setter_ty {
      $new
      $methods
      $done
    }
    );
  impl_.unwrap()
}

fn build_field_fn<'a>(cx: &'a ExtCtxt, path: &Vec<String>, reg: &node::Reg,
                      field: &node::Field) -> Option<P<ast::Method>>
{
  match field.access {
    node::ReadOnly => None,
    node::SetToClear => Some(build_field_clear_fn(cx, path, reg, field)),
    _ => Some(build_field_set_fn(cx, path, reg, field)),
  }
}

/// Build a setter for a field
fn build_field_set_fn<'a>(cx: &'a ExtCtxt, path: &Vec<String>, reg: &node::Reg,
                          field: &node::Field) -> P<ast::Method>
{
  let setter_ty = utils::setter_name(cx, path);
  let unpacked_ty = utils::reg_primitive_type(cx, reg)
    .expect("Unexpected non-primitive register");
  let fn_name =
    cx.ident_of((String::from_str("set_")+field.name.node).as_slice());
  let field_ty: P<ast::Ty> =
    cx.ty_path(utils::field_type_path(cx, path, reg, field), None);
  let mask = utils::mask(cx, field);

  let field_doc = match field.docstring {
    Some(d) => token::get_ident(d.node).get().into_string(),
    None => "no documentation".into_string(),
  };
  let docstring = format!("Set value of `{}` field: {}",
                          field.name.node,
                          field_doc);
  let doc_attr = utils::doc_attribute(cx, utils::intern_string(cx, docstring));

  if field.count.node == 1 {
    let shift = utils::shift(cx, None, field);
    quote_method!(cx,
      $doc_attr
      pub fn $fn_name<'a>(&'a mut self, new_value: $field_ty)
          -> &'a mut $setter_ty {
        self.value |= (self.value & ! $mask) | ((new_value as $unpacked_ty) & $mask) << $shift;
        self.mask |= $mask << $shift;
        self
      }
    )
  } else {
    let shift = utils::shift(cx, Some(quote_expr!(cx, idx)), field);
    quote_method!(cx,
      $doc_attr
      pub fn $fn_name<'a>(&'a mut self, idx: uint, new_value: $field_ty)
        -> &'a mut $setter_ty {
          self.value |= (self.value & ! $mask) | ((new_value as $unpacked_ty) & $mask) << $shift;
          self.mask |= $mask << $shift;
          self
      }
    )
  }
}

fn build_field_clear_fn<'a>(cx: &'a ExtCtxt, path: &Vec<String>,
    _: &node::Reg, field: &node::Field) -> P<ast::Method>
{
  let setter_ty = utils::setter_name(cx, path);
  let fn_name =
    cx.ident_of((String::from_str("clear_")+field.name.node).as_slice());
  let mask = utils::mask(cx, field);

  let field_doc = match field.docstring {
    Some(d) => token::get_ident(d.node).get().into_string(),
    None => "no documentation".into_string(),
  };
  let docstring = format!("Clear `{}` flag: {}",
                          field.name.node,
                          field_doc);
  let doc_attr = utils::doc_attribute(cx, utils::intern_string(cx, docstring));

  if field.count.node == 1 {
    let shift = utils::shift(cx, None, field);
    quote_method!(cx,
      $doc_attr
      pub fn $fn_name<'a>(&'a mut self) -> &'a mut $setter_ty {
          self.value |= $mask << $shift;
          self.mask |= $mask << $shift;
          self
      }
    )
  } else {
    let shift = utils::shift(cx, Some(quote_expr!(cx, idx)), field);
    quote_method!(cx,
      $doc_attr
      pub fn $fn_name<'a>(&'a mut self, idx: uint)
                          -> &'a mut $setter_ty {
          self.value |= $mask << $shift;
          self.mask |= $mask << $shift;
          self
      }
    )
  }
}
<|endoftext|>


<commit_before>//
// zhtta.rs
//
// Running on Rust 0.8
//
// Starting code for PS3
//
// Note: it would be very unwise to run this server on a machine that is
// on the Internet and contains any sensitive files!
//
// University of Virginia - cs4414 Fall 2013
// Weilin Xu and David Evans
// Version 0.3

extern mod extra;

use std::rt::io::*;
use std::rt::io::net::ip::SocketAddr;
use std::io::println;
use std::cell::Cell;
use std::{os, str, io};
use extra::arc;
use std::comm::*;
use extra::priority_queue;
use extra::md4;
use extra::sort;
use std::path;

static PORT:    int = 4414;
static IP: &'static str = "127.0.0.1";

static MAX_CACHE_SIZE_BYTES: u64 = 50000000;

struct sched_msg {
    stream: Option<std::rt::io::net::tcp::TcpStream>,
    filepath: ~std::path::PosixPath,
    topPriority: int,
    fileSize: u64

}

impl Ord for sched_msg {
    fn lt(&self, other: &sched_msg) -> bool {

        let mut retVal:bool = false;

        //If self has a lower geographic priority, then it is less than
        if (self.topPriority < other.topPriority) {
            retVal = true;
        }
        else if (self.topPriority == other.topPriority) {

            //If the two have equal priorities, then we will sort by file size
            //Smaller files get a higher priority
            if (self.fileSize > other.fileSize) {
                retVal = true;
            }
        }

        retVal
    }
}

struct cache_item {
    name: ~str,
    in_use_flag: bool,
    ssi_flag: bool,
    hash: ~str,
    data: ~[u8],
    count: uint,
    size: u64

}

impl cache_item {
    fn le(&self, other: &cache_item) -> bool {
        let mut retVal:bool = false;

        if(self.count <= other.count) {
            retVal = true;
        }

        retVal
    }
}

fn le(this: &cache_item, other: &cache_item) -> bool {
        let mut retVal:bool = false;

        if(this.count <= other.count) {
            retVal = true;
        }

        retVal
    }

fn main() {
    //let req_vec: ~[sched_msg] = ~[];
    let req_vec: ~priority_queue::PriorityQueue<sched_msg> = ~priority_queue::PriorityQueue::new();
    let shared_req_vec = arc::RWArc::new(req_vec);
    let add_vec = shared_req_vec.clone();
    let take_vec = shared_req_vec.clone();

    let (port, chan) = stream();
    let chan = SharedChan::new(chan);

    let safe_count: ~[uint] = ~[0];
    let shared_visit = arc::RWArc::new(safe_count);

    let cache_list: ~[cache_item] = ~[];
    let shared_cache_list = arc::RWArc::new(cache_list);

    let clone_cache = shared_cache_list.clone();

    //CACHE MANAGER
    do spawn {

        loop {
            do clone_cache.write |vec| {

                sort::quick_sort((*vec), le);

                let mut cache_remaining = MAX_CACHE_SIZE_BYTES;

                for i in range(0, (*vec).len()) {

                    if((*vec)[i].size <= cache_remaining && !(*vec)[i].in_use_flag) {

                        let curr_path = ~path::Path((*vec)[i].name);

                        if os::path_exists(curr_path) {

                            let fileInfo = match std::rt::io::file::stat(curr_path) {
                                Some(s) => s,
                                None => fail!("Could not access file stats for cache")
                            };

                            if(fileInfo.size <= cache_remaining) {

                                match io::read_whole_file(curr_path) {
                                    Ok(file_data) => {

                                        //NEED TO ADD CHECK IF IT HAS SSI
                                        //PERHAPS EXCLUDE ALL SSI FROM CACHE?
                                        //ADD TO IF: size < remaining, !in use, !ssi?

                                        (*vec)[i].data = file_data.to_owned();
                                        (*vec)[i].size = fileInfo.size;
                                        (*vec)[i].hash = md4::md4_str(file_data);
                                        (*vec)[i].in_use_flag = true;

                                        cache_remaining = cache_remaining - fileInfo.size;

                                    }
                                    Err(err) => {
                                        println("ERROR IN UPDATE CACHE");
                                        println(err);
                                    }

                                }

                            }
                        }
                    }
                    else if((*vec)[i].size <= cache_remaining && (*vec)[i].in_use_flag) {
                        cache_remaining = cache_remaining - (*vec)[i].size;
                    }
                    else if((*vec)[i].size > cache_remaining) {
                        (*vec)[i].in_use_flag = false;
                        (*vec)[i].data = ~[];
                    }

                }

            }

        }
    }

    // dequeue file requests, and send responses.
    // FIFO
    do spawn {
        let (sm_port, sm_chan) = stream();

        // a task for sending responses.
        do spawn {
            loop {
                let mut tf: sched_msg = sm_port.recv(); // wait for the dequeued request to handle

                //Check if file is in cache
                //Will do so using RWArc

                let mut serve_from_cache: bool = false;

                do shared_cache_list.write |vec| {

                    //if((*vec).len() > 0) {

                        let mut found: bool = false;

                        for i in range(0, (*vec).len()) {

                            if( (*vec)[i].name == tf.filepath.to_str() && (*vec)[i].in_use_flag) {
                                serve_from_cache = true;
                                found = true;

                                println(fmt!("===== SERVING FROM CACHE: %?", tf.filepath.to_str()));

                                tf.stream.write((*vec)[i].data);

                                (*vec)[i].count += 1;
                            }
                            else if( (*vec)[i].name == tf.filepath.to_str() && !(*vec)[i].in_use_flag) {
                                (*vec)[i].count += 1;
                                found = true;
                            }
                        }

                        //If it isn't found, we will create a blank entry and enter basic info, and add the
                        //data and md4 later
                        if(!found) {

                            println(fmt!("===== ADDING ITEM %?", tf.filepath.to_str()));

                            let new_cache_item: cache_item = cache_item{name: tf.filepath.to_str(), in_use_flag: false,
                                ssi_flag: false, hash: ~"", data: ~[], count: 1, size: tf.fileSize};

                            (*vec).push(new_cache_item);

                        }
                    //}
                }

                if(!serve_from_cache) {

                    match io::read_whole_file(tf.filepath) { // killed if file size is larger than memory size.
                        Ok(file_data) => {
                            println(fmt!("begin serving file [%?]", tf.filepath));

                            tf.stream.write(file_data);
                            println(fmt!("finish file [%?]", tf.filepath));

                            println(fmt!("===== SERVING FROM DISK: %?", tf.filepath.to_str()));

                            /*

                            let md4_val = md4::md4_str(file_data);

                            do shared_cache_list.write |vec| {

                                let mut found_flag: bool = false;

                                for i in range(0, (*vec).len()) {

                                    if( (*vec)[i].name == tf.filepath.to_str() ) {

                                        println("=====FOUND NAME");

                                        found_flag = true;

                                        (*vec)[i].in_use_flag = true;
                                        (*vec)[i].data = file_data.to_owned();
                                        (*vec)[i].hash = md4_val.to_owned();
                                    }
                                }

                                if(!found_flag) {
                                    println(fmt!("===== DID NOT FIND NAME FOR %?", tf.filepath.to_str()));
                                }
                            }

                            */

                        }
                        Err(err) => {
                            println("ERROR IN SEND");
                            println(err);
                        }
                    }
                }
            }
        }

        loop {
            port.recv(); // wait for arrving notification
            do take_vec.write |vec| {
                if ((*vec).len() > 0) {

                    //Since we are using a priority queue, we will use pop()
                    let tf = (*vec).pop();
                    println(fmt!("shift from queue, size: %ud", (*vec).len()));
                    sm_chan.send(tf); // send the request to send-response-task to serve.
                }
            }
        }
    }

    let ip = match FromStr::from_str(IP) { Some(ip) => ip,
                                           None => { println(fmt!("Error: Invalid IP address <%s>", IP));
                                                     return;},
                                         };

    let socket = net::tcp::TcpListener::bind(SocketAddr {ip: ip, port: PORT as u16});

    println(fmt!("Listening on %s:%d ...", ip.to_str(), PORT));
    let mut acceptor = socket.listen().unwrap();

    loop {
        let stream = acceptor.accept();
        let stream = Cell::new(stream);

        // Start a new task to handle the each connection
        let child_chan = chan.clone();
        let child_add_vec = add_vec.clone();
        let child_arc = shared_visit.clone();
        do spawn {

            let mut actual_count:uint = 0;

            //We will use the cloned RWARC
            do child_arc.write |count_vec| {

                let prev_count = (*count_vec).pop();
                let new_count: uint = prev_count + 1;

                actual_count = new_count;

                (*count_vec).push(new_count);
            }

            let stream = stream.take();

            match stream {
                Some(s) => {

                    let mut stream = s;

                    let mut buf = [0, ..500];
                    stream.read(buf);
                    let request_str = str::from_utf8(buf);

                    let req_group : ~[&str]= request_str.splitn_iter(' ', 3).collect();
                    if req_group.len() > 2 {
                        let path = req_group[1];
                        println(fmt!("Request for path: \n%?", path));

                        let file_path = ~os::getcwd().push(path.replace("/../", ""));
                        if !os::path_exists(file_path) || os::path_is_dir(file_path) {
                            println(fmt!("Request received:\n%s", request_str));
                            let response: ~str = fmt!(
                                "HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=UTF-8\r\n\r\n
                                 <doctype !html><title>Hello, Rust!</title>

Greetings, Krusty!

Visitor count: %u

                                 \r\n", actual_count);

                            stream.write(response.as_bytes());
                        }
                        else {
                            // Requests scheduling

                            let mut streamPriority: int = 0;

                            //Retrieving the requesting IP address
                            let ipStr: ~str = match (stream).peer_name() {
                                Some(pr) => pr.ip.to_str(),
                                None => ~"0.0.0.0"
                            };

                            //Split the IP address by '.' so that we can compare
                            let ipSplit: ~[~str] = ipStr.split_iter('.').filter(|&x| x != "")
                                 .map(|x| x.to_owned()).collect();

                            //Assign priority based on geography or if localhost
                            if ( (ipSplit[0] == ~"127" && ipSplit[1] == ~"0") || (ipSplit[0] == ~"128" && ipSplit[1] == ~"143")
                                || (ipSplit[0] == ~"137" && ipSplit[1] == ~"54") ) {

                                streamPriority = 1;
                            }

                            //Get filetype in order to determine HTTP header type
                            let fileType = match file_path.filetype() {
                                Some(s) => s,
                                None => &""
                            };

                            //In order to optimize for the benchmark, we will send the HTTP header quickly before adding to the queue
                            let httpHeader: ~str = match fileType {
                                ".html" | ".htm" | ".php" => ~"HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=UTF-8\r\n\r\n",
                                _ => ~"HTTP/1.1 200 OK\r\nContent-Type: application/octet-stream; charset=UTF-8\r\n\r\n"
                            };

                            stream.write(httpHeader.as_bytes());
                            stream.flush();

                            //Retrieve file info for additional latency fixes
                            let fileInfo = match std::rt::io::file::stat(file_path) {
                                Some(s) => s,
                                None => fail!("Could not access file stats")
                            };

                            let msg: sched_msg = sched_msg{stream: Some(stream), filepath: file_path.clone(), topPriority: streamPriority, fileSize: fileInfo.size};
                            let (sm_port, sm_chan) = std::comm::stream();
                            sm_chan.send(msg);

                            do child_add_vec.write |vec| {
                                let msg = sm_port.recv();
                                (*vec).push(msg); // enqueue new request.
                                println("add to queue");
                            }
                            child_chan.send(""); //notify the new arriving request.
                            println(fmt!("get file request: %?", file_path));
                        }
                    }
                    println!("connection terminates")

                },
                None => ()
            }

        }
    }
}<commit_msg>Finished implementing caching<commit_after>//
// zhtta.rs
//
// Running on Rust 0.8
//
// Starting code for PS3
//
// Note: it would be very unwise to run this server on a machine that is
// on the Internet and contains any sensitive files!
//
// University of Virginia - cs4414 Fall 2013
// Weilin Xu and David Evans
// Version 0.3

extern mod extra;

use std::rt::io::*;
use std::rt::io::net::ip::SocketAddr;
use std::io::println;
use std::cell::Cell;
use std::{os, str, io};
use extra::arc;
use std::comm::*;
use extra::priority_queue;
use extra::md4;
use extra::sort;
use std::path;

static PORT:    int = 4414;
static IP: &'static str = "127.0.0.1";

static MAX_CACHE_SIZE_BYTES: u64 = 50000000;
static CACHE_MANAGER_A_RATE: u64 = 2000;
static CACHE_MANAGER_B_RATE: u64 = 6000;

struct sched_msg {
    stream: Option<std::rt::io::net::tcp::TcpStream>,
    filepath: ~std::path::PosixPath,
    topPriority: int,
    fileSize: u64

}

impl Ord for sched_msg {
    fn lt(&self, other: &sched_msg) -> bool {

        let mut retVal:bool = false;

        //If self has a lower geographic priority, then it is less than
        if (self.topPriority < other.topPriority) {
            retVal = true;
        }
        else if (self.topPriority == other.topPriority) {

            //If the two have equal priorities, then we will sort by file size
            //Smaller files get a higher priority
            if (self.fileSize > other.fileSize) {
                retVal = true;
            }
        }

        retVal
    }
}

struct cache_item {
    name: ~str,
    in_use_flag: bool,
    ssi_flag: bool,
    hash: ~str,
    data: ~[u8],
    count: uint,
    size: u64

}

impl cache_item {
    fn le(&self, other: &cache_item) -> bool {
        let mut retVal:bool = false;

        if(self.count <= other.count) {
            retVal = true;
        }

        retVal
    }
}

fn le(this: &cache_item, other: &cache_item) -> bool {
        let mut retVal:bool = false;

        if(this.count <= other.count) {
            retVal = true;
        }

        retVal
    }

fn main() {
    let req_vec: ~priority_queue::PriorityQueue<sched_msg> = ~priority_queue::PriorityQueue::new();
    let shared_req_vec = arc::RWArc::new(req_vec);
    let add_vec = shared_req_vec.clone();
    let take_vec = shared_req_vec.clone();

    let (port, chan) = stream();
    let chan = SharedChan::new(chan);

    let safe_count: ~[uint] = ~[0];
    let shared_visit = arc::RWArc::new(safe_count);

    let cache_list: ~[cache_item] = ~[];
    let shared_cache_list = arc::RWArc::new(cache_list);

    let cache_manager_a = shared_cache_list.clone();
    let cache_manager_b = shared_cache_list.clone();

    //CACHE UPDATE MANAGER (Manager B)
    //This will handle making sure that items in the cache are up-to-date in case they are changed
    do spawn {

        loop {

            do cache_manager_b.write |vec| {

                for i in range(0, (*vec).len()) {

                    //If it is in the cache and in use, we will check to see if the file has been updated
                    if((*vec)[i].in_use_flag) {

                        let curr_path = ~path::Path((*vec)[i].name);
                        if os::path_exists(curr_path) {

                            match io::read_whole_file(curr_path) {
                                    Ok(file_data) => {

                                        let temp_md4 = md4::md4_str(file_data.to_owned());

                                        if(temp_md4 != (*vec)[i].hash)
                                        {
                                            let fileInfo = match std::rt::io::file::stat(curr_path) {
                                                Some(s) => s,
                                                None => fail!("Could not access file stats for cache")
                                            };

                                            println(fmt!("===== UPDATING FILE: %?", (*vec)[i].name));

                                            (*vec)[i].data = file_data;
                                            (*vec)[i].size = fileInfo.size;
                                            (*vec)[i].hash = temp_md4;
                                        }
                                    }
                                    Err(err) => {
                                        println("ERROR IN UPDATE CACHE");
                                        println(err);
                                    }
                                }
                        }
                    }
                }
            }

            timer::sleep(CACHE_MANAGER_B_RATE);
        }
    }

    //MAIN CACHE MANAGER (Manager A)
    do spawn {

        loop {
            do cache_manager_a.write |vec| {

                sort::quick_sort((*vec), le);

                let mut cache_remaining = MAX_CACHE_SIZE_BYTES;

                for i in range(0, (*vec).len()) {

                    if((*vec)[i].size <= cache_remaining && !(*vec)[i].in_use_flag) {

                        let curr_path = ~path::Path((*vec)[i].name);

                        if os::path_exists(curr_path) {

                            let fileInfo = match std::rt::io::file::stat(curr_path) {
                                Some(s) => s,
                                None => fail!("Could not access file stats for cache")
                            };

                            if(fileInfo.size <= cache_remaining) {

                                match io::read_whole_file(curr_path) {
                                    Ok(file_data) => {

                                        //NEED TO ADD CHECK IF IT HAS SSI
                                        //PERHAPS EXCLUDE ALL SSI FROM CACHE?
                                        //ADD TO IF: size < remaining, !in use, !ssi?

                                        (*vec)[i].data = file_data.to_owned();
                                        (*vec)[i].size = fileInfo.size;
                                        (*vec)[i].hash = md4::md4_str(file_data);
                                        (*vec)[i].in_use_flag = true;

                                        cache_remaining = cache_remaining - fileInfo.size;

                                    }
                                    Err(err) => {
                                        println("ERROR IN UPDATE CACHE");
                                        println(err);
                                    }

                                }

                            }
                        }
                    }
                    else if((*vec)[i].size <= cache_remaining && (*vec)[i].in_use_flag) {
                        cache_remaining = cache_remaining - (*vec)[i].size;
                    }
                    else if((*vec)[i].size > cache_remaining) {
                        (*vec)[i].in_use_flag = false;
                        (*vec)[i].data = ~[];
                    }

                }

            }

            timer::sleep(CACHE_MANAGER_A_RATE);

        }
    }

    // dequeue file requests, and send responses.
    // FIFO
    do spawn {
        let (sm_port, sm_chan) = stream();

        // a task for sending responses.
        do spawn {
            loop {
                let mut tf: sched_msg = sm_port.recv(); // wait for the dequeued request to handle

                //Check if file is in cache
                //Will do so using RWArc

                let mut serve_from_cache: bool = false;

                do shared_cache_list.write |vec| {

                    //if((*vec).len() > 0) {

                        let mut found: bool = false;

                        for i in range(0, (*vec).len()) {

                            if( (*vec)[i].name == tf.filepath.to_str() && (*vec)[i].in_use_flag) {
                                serve_from_cache = true;
                                found = true;

                                println(fmt!("===== SERVING FROM CACHE: %?", tf.filepath.to_str()));

                                tf.stream.write((*vec)[i].data);

                                (*vec)[i].count += 1;
                            }
                            else if( (*vec)[i].name == tf.filepath.to_str() && !(*vec)[i].in_use_flag) {
                                (*vec)[i].count += 1;
                                found = true;
                            }
                        }

                        //If it isn't found, we will create a blank entry and enter basic info, and add the
                        //data and md4 later
                        if(!found) {

                            println(fmt!("===== ADDING ITEM %?", tf.filepath.to_str()));

                            let new_cache_item: cache_item = cache_item{name: tf.filepath.to_str(), in_use_flag: false,
                                ssi_flag: false, hash: ~"", data: ~[], count: 1, size: tf.fileSize};

                            (*vec).push(new_cache_item);

                        }
                    //}
                }

                if(!serve_from_cache) {

                    match io::read_whole_file(tf.filepath) { // killed if file size is larger than memory size.
                        Ok(file_data) => {
                            //println(fmt!("begin serving file [%?]", tf.filepath));

                            tf.stream.write(file_data);
                            //println(fmt!("finish file [%?]", tf.filepath));

                            println(fmt!("===== SERVING FROM DISK: %?", tf.filepath.to_str()));

                            /*

                            let md4_val = md4::md4_str(file_data);

                            do shared_cache_list.write |vec| {

                                let mut found_flag: bool = false;

                                for i in range(0, (*vec).len()) {

                                    if( (*vec)[i].name == tf.filepath.to_str() ) {

                                        println("=====FOUND NAME");

                                        found_flag = true;

                                        (*vec)[i].in_use_flag = true;
                                        (*vec)[i].data = file_data.to_owned();
                                        (*vec)[i].hash = md4_val.to_owned();
                                    }
                                }

                                if(!found_flag) {
                                    println(fmt!("===== DID NOT FIND NAME FOR %?", tf.filepath.to_str()));
                                }
                            }

                            */

                        }
                        Err(err) => {
                            println("ERROR IN SEND");
                            println(err);
                        }
                    }
                }
            }
        }

        loop {
            port.recv(); // wait for arrving notification
            do take_vec.write |vec| {
                if ((*vec).len() > 0) {

                    //Since we are using a priority queue, we will use pop()
                    let tf = (*vec).pop();
                    //println(fmt!("shift from queue, size: %ud", (*vec).len()));
                    sm_chan.send(tf); // send the request to send-response-task to serve.
                }
            }
        }
    }

    let ip = match FromStr::from_str(IP) { Some(ip) => ip,
                                           None => { println(fmt!("Error: Invalid IP address <%s>", IP));
                                                     return;},
                                         };

    let socket = net::tcp::TcpListener::bind(SocketAddr {ip: ip, port: PORT as u16});

    println(fmt!("Listening on %s:%d ...", ip.to_str(), PORT));
    let mut acceptor = socket.listen().unwrap();

    loop {
        let stream = acceptor.accept();
        let stream = Cell::new(stream);

        // Start a new task to handle the each connection
        let child_chan = chan.clone();
        let child_add_vec = add_vec.clone();
        let child_arc = shared_visit.clone();
        do spawn {

            let mut actual_count:uint = 0;

            //We will use the cloned RWARC
            do child_arc.write |count_vec| {

                let prev_count = (*count_vec).pop();
                let new_count: uint = prev_count + 1;

                actual_count = new_count;

                (*count_vec).push(new_count);
            }

            let stream = stream.take();

            match stream {
                Some(s) => {

                    let mut stream = s;

                    let mut buf = [0, ..500];
                    stream.read(buf);
                    let request_str = str::from_utf8(buf);

                    let req_group : ~[&str]= request_str.splitn_iter(' ', 3).collect();
                    if req_group.len() > 2 {
                        let path = req_group[1];
                        //println(fmt!("Request for path: \n%?", path));

                        let file_path = ~os::getcwd().push(path.replace("/../", ""));
                        if !os::path_exists(file_path) || os::path_is_dir(file_path) {
                            //println(fmt!("Request received:\n%s", request_str));
                            let response: ~str = fmt!(
                                "HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=UTF-8\r\n\r\n
                                 <doctype !html><title>Hello, Rust!</title>

Greetings, Krusty!

Visitor count: %u

                                 \r\n", actual_count);

                            stream.write(response.as_bytes());
                        }
                        else {
                            // Requests scheduling

                            let mut streamPriority: int = 0;

                            //Retrieving the requesting IP address
                            let ipStr: ~str = match (stream).peer_name() {
                                Some(pr) => pr.ip.to_str(),
                                None => ~"0.0.0.0"
                            };

                            //Split the IP address by '.' so that we can compare
                            let ipSplit: ~[~str] = ipStr.split_iter('.').filter(|&x| x != "")
                                 .map(|x| x.to_owned()).collect();

                            //Assign priority based on geography or if localhost
                            if ( (ipSplit[0] == ~"127" && ipSplit[1] == ~"0") || (ipSplit[0] == ~"128" && ipSplit[1] == ~"143")
                                || (ipSplit[0] == ~"137" && ipSplit[1] == ~"54") ) {

                                streamPriority = 1;
                            }

                            //Get filetype in order to determine HTTP header type
                            let fileType = match file_path.filetype() {
                                Some(s) => s,
                                None => &""
                            };

                            //In order to optimize for the benchmark, we will send the HTTP header quickly before adding to the queue
                            let httpHeader: ~str = match fileType {
                                ".html" | ".htm" | ".php" => ~"HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=UTF-8\r\n\r\n",
                                _ => ~"HTTP/1.1 200 OK\r\nContent-Type: application/octet-stream; charset=UTF-8\r\n\r\n"
                            };

                            stream.write(httpHeader.as_bytes());
                            stream.flush();

                            //Retrieve file info for additional latency fixes
                            let fileInfo = match std::rt::io::file::stat(file_path) {
                                Some(s) => s,
                                None => fail!("Could not access file stats")
                            };

                            let msg: sched_msg = sched_msg{stream: Some(stream), filepath: file_path.clone(), topPriority: streamPriority, fileSize: fileInfo.size};
                            let (sm_port, sm_chan) = std::comm::stream();
                            sm_chan.send(msg);

                            do child_add_vec.write |vec| {
                                let msg = sm_port.recv();
                                (*vec).push(msg); // enqueue new request.
                                //println("add to queue");
                            }
                            child_chan.send(""); //notify the new arriving request.
                            //println(fmt!("get file request: %?", file_path));
                        }
                    }
                    println!("connection terminates")

                },
                None => ()
            }

        }
    }
}<|endoftext|>


<commit_before>use lazy_static::lazy_static;
use maplit::hashmap;
use serde_json::json;
use std::collections::HashMap;

use crate::game_object::GameObject;
use crate::objects::{Player, Realm};
use crate::sessions::SessionOutput as Output;
use crate::text_utils::is_letter;

use super::sign_up::{
    enter_sign_up_step, exit_sign_up_step, process_sign_up_input, prompt_sign_up_step, SignUpData,
    SignUpState,
};

#[derive(Clone, Debug, PartialEq)]
pub struct SignInState {
    step: SignInStep,
    data: SignInData,
}

impl SignInState {
    pub fn get_sign_in_user_name(&self) -> Option<&str> {
        if self.step == SignInStep::SignedIn {
            if let Some(player) = &self.data.player {
                Some(player.name())
            } else if let Some(sign_up_state) = &self.data.sign_up_state {
                Some(&sign_up_state.data.user_name)
            } else {
                None
            }
        } else {
            None
        }
    }

    pub fn get_sign_up_data(&self) -> Option<&SignUpData> {
        if self.step == SignInStep::SignedIn {
            self.data.sign_up_state.as_ref().map(|state| &state.data)
        } else {
            None
        }
    }

    pub fn is_session_closed(&self) -> bool {
        self.step == SignInStep::SessionClosed
    }

    pub fn new() -> Self {
        new_state(
            SignInStep::AskingUserName,
            SignInData {
                player: None,
                sign_up_state: None,
            },
        )
    }
}

#[derive(Clone, Debug, Eq, Hash, PartialEq)]
enum SignInStep {
    AskingPassword,
    AskingSignUpData,
    AskingUserName,
    SessionClosed,
    SignedIn,
}

#[derive(Clone, Debug, PartialEq)]
struct SignInData {
    player: Option<Player>,
    sign_up_state: Option<SignUpState>,
}

struct StepImpl {
    enter: fn(&SignInData, &Realm) -> Output,
    exit: fn(&SignInData) -> Output,
    prompt: fn(&SignInData) -> Output,
    process_input: fn(&SignInState, &Realm, &String, String) -> (SignInState, Output, Vec<String>),
}

pub fn process_input(
    state: &SignInState,
    realm: &Realm,
    source: &String,
    input: String,
) -> (SignInState, Output, Vec<String>) {
    lazy_static! {
        static ref SIGN_IN_STEPS: HashMap<SignInStep, StepImpl> = get_sign_in_steps();
    }

    match SIGN_IN_STEPS.get(&state.step) {
        Some(step) => {
            let (new_state, output, log_messages) =
                (step.process_input)(state, realm, source, input);
            if let Some(new_step) = SIGN_IN_STEPS.get(&new_state.step) {
                let prompt_output = (new_step.prompt)(&new_state.data);
                let outputs = if new_state == *state {
                    vec![output, prompt_output]
                } else {
                    let exit_output = (step.exit)(&new_state.data);
                    let enter_output = (new_step.enter)(&new_state.data, realm);
                    vec![output, exit_output, enter_output, prompt_output]
                };

                (
                    new_state,
                    Output::combine(outputs).process_highlights(),
                    log_messages,
                )
            } else {
                (new_state, output.process_highlights(), log_messages)
            }
        }
        None => (state.clone(), Output::None, vec![]),
    }
}

fn get_sign_in_steps() -> HashMap<SignInStep, StepImpl> {
    hashmap! {
        SignInStep::AskingUserName => StepImpl {
            enter: |_, _| Output::None,
            exit: |_| Output::None,
            prompt: |_| Output::Str("What is your name? "),
            process_input: process_asking_user_name_input,
        },

        SignInStep::AskingPassword => StepImpl {
            enter: |_, _| Output::JSON(json!({ "inputType": "password" })),
            exit: |_| Output::JSON(json!({ "inputType": "text" })),
            prompt: |_| Output::Str("Please enter your password: "),
            process_input: process_asking_password_input,
        },

        SignInStep::AskingSignUpData => StepImpl {
            enter: |data, realm| enter_sign_up_step(&data.sign_up_state.as_ref().unwrap(), realm),
            exit: |data| exit_sign_up_step(&data.sign_up_state.as_ref().unwrap()),
            prompt: |data| prompt_sign_up_step(&data.sign_up_state.as_ref().unwrap()),
            process_input: |state, realm, _, input| {
                let (sign_up_state, output, log_messages) = process_sign_up_input(
                    &state.data.sign_up_state.as_ref().unwrap(),
                    realm,
                    input
                );
                (
                    new_state(
                        if sign_up_state.is_session_closed() {
                            SignInStep::SessionClosed
                        } else if sign_up_state.is_signed_up() {
                            SignInStep::SignedIn
                        } else {
                            SignInStep::AskingSignUpData
                        },
                        SignInData { sign_up_state: Some(sign_up_state), ..state.data.clone() }
                    ),
                    output,
                    log_messages
                )
            }
        }
    }
}

fn new_state(step: SignInStep, data: SignInData) -> SignInState {
    SignInState { step, data }
}

fn process_asking_user_name_input(
    state: &SignInState,
    realm: &Realm,
    _: &String,
    input: String,
) -> (SignInState, Output, Vec<String>) {
    lazy_static! {
        static ref RESERVED_NAMES: Vec<String> = vec![
            "Admin".to_owned(),
            "Administrator".to_owned(),
            "Root".to_owned()
        ];
    }
    let user_name = validate_user_name(input.to_lowercase());
    if user_name.len() < 3 {
        (
            state.clone(),
            if user_name.len() == 0 {
                Output::None
            } else {
                Output::Str("I'm sorry, but your name should consist of at least 3 letters.\n")
            },
            vec![],
        )
    } else if let Some(player) = realm.player_by_name(&user_name) {
        (
            new_state(
                SignInStep::AskingPassword,
                SignInData {
                    player: Some(player.clone()),
                    ..state.data.clone()
                },
            ),
            Output::None,
            vec![],
        )
    } else if RESERVED_NAMES.contains(&user_name) {
        (
            state.clone(),
            Output::Str("Yeah right, like I believe that...\n"),
            vec![],
        )
    } else {
        (
            new_state(
                SignInStep::AskingSignUpData,
                SignInData {
                    sign_up_state: Some(SignUpState::new(user_name)),
                    ..state.data.clone()
                },
            ),
            Output::None,
            vec![],
        )
    }
}

fn process_asking_password_input(
    state: &SignInState,
    _: &Realm,
    _: &String,
    input: String,
) -> (SignInState, Output, Vec<String>) {
    if let Some(player) = &state.data.player {
        let name = player.name();
        if player.matches_password(&input) {
            let log_messages = vec![format!("Authentication success for player \"{}\"", name)];

            if player.session_id().is_some() {
                (
                    new_state(SignInStep::SessionClosed, state.data.clone()),
                    Output::Str(
                        "Cannot sign you in because you're already signed in from another \
                        location.\n",
                    ),
                    log_messages,
                )
            } else {
                (
                    new_state(SignInStep::SignedIn, state.data.clone()),
                    Output::String(format!(
                        "Welcome back, {}. Type *help* if you're feeling lost.\n",
                        name
                    )),
                    log_messages,
                )
            }
        } else {
            (
                state.clone(),
                Output::Str("Password incorrect.\n"),
                vec![format!("Authentication failed for player \"{}\"", name)],
            )
        }
    } else {
        (state.clone(), Output::None, vec![])
    }
}

fn validate_user_name(input: String) -> String {
    let mut user_name = String::new();
    let mut num_special_chars = 0;
    for character in input.chars() {
        if is_letter(character) {
            user_name.push(if user_name.len() == 0 {
                character.to_ascii_uppercase()
            } else {
                character
            });
        } else if user_name.len() > 0
            && num_special_chars == 0
            && (character == '\'' || character == '-')
        {
            user_name.push(character);
            num_special_chars += 1;
        }
        if user_name.len() >= 12 {
            break;
        }
    }
    if user_name.len() > 1 && !is_letter(user_name.chars().rev().next().unwrap_or_default()) {
        user_name = user_name.chars().take(user_name.len() - 1).collect();
    }
    user_name
}
<commit_msg>Fix input type after sign in<commit_after>use lazy_static::lazy_static;
use maplit::hashmap;
use serde_json::json;
use std::collections::HashMap;

use crate::game_object::GameObject;
use crate::objects::{Player, Realm};
use crate::sessions::SessionOutput as Output;
use crate::text_utils::is_letter;

use super::sign_up::{
    enter_sign_up_step, exit_sign_up_step, process_sign_up_input, prompt_sign_up_step, SignUpData,
    SignUpState,
};

#[derive(Clone, Debug, PartialEq)]
pub struct SignInState {
    step: SignInStep,
    data: SignInData,
}

impl SignInState {
    pub fn get_sign_in_user_name(&self) -> Option<&str> {
        if self.step == SignInStep::SignedIn {
            if let Some(player) = &self.data.player {
                Some(player.name())
            } else if let Some(sign_up_state) = &self.data.sign_up_state {
                Some(&sign_up_state.data.user_name)
            } else {
                None
            }
        } else {
            None
        }
    }

    pub fn get_sign_up_data(&self) -> Option<&SignUpData> {
        if self.step == SignInStep::SignedIn {
            self.data.sign_up_state.as_ref().map(|state| &state.data)
        } else {
            None
        }
    }

    pub fn is_session_closed(&self) -> bool {
        self.step == SignInStep::SessionClosed
    }

    pub fn new() -> Self {
        new_state(
            SignInStep::AskingUserName,
            SignInData {
                player: None,
                sign_up_state: None,
            },
        )
    }
}

#[derive(Clone, Debug, Eq, Hash, PartialEq)]
enum SignInStep {
    AskingPassword,
    AskingSignUpData,
    AskingUserName,
    SessionClosed,
    SignedIn,
}

#[derive(Clone, Debug, PartialEq)]
struct SignInData {
    player: Option<Player>,
    sign_up_state: Option<SignUpState>,
}

struct StepImpl {
    enter: fn(&SignInData, &Realm) -> Output,
    exit: fn(&SignInData) -> Output,
    prompt: fn(&SignInData) -> Output,
    process_input: fn(&SignInState, &Realm, &String, String) -> (SignInState, Output, Vec<String>),
}

pub fn process_input(
    state: &SignInState,
    realm: &Realm,
    source: &String,
    input: String,
) -> (SignInState, Output, Vec<String>) {
    lazy_static! {
        static ref SIGN_IN_STEPS: HashMap<SignInStep, StepImpl> = get_sign_in_steps();
    }

    match SIGN_IN_STEPS.get(&state.step) {
        Some(step) => {
            let (new_state, output, log_messages) =
                (step.process_input)(state, realm, source, input);
            if let Some(new_step) = SIGN_IN_STEPS.get(&new_state.step) {
                let prompt_output = (new_step.prompt)(&new_state.data);
                let outputs = if new_state == *state {
                    vec![output, prompt_output]
                } else {
                    let exit_output = (step.exit)(&new_state.data);
                    let enter_output = (new_step.enter)(&new_state.data, realm);
                    vec![output, exit_output, enter_output, prompt_output]
                };

                (
                    new_state,
                    Output::combine(outputs).process_highlights(),
                    log_messages,
                )
            } else {
                let exit_output = (step.exit)(&new_state.data);
                (
                    new_state,
                    Output::combine(vec![output, exit_output]).process_highlights(),
                    log_messages,
                )
            }
        }
        None => (state.clone(), Output::None, vec![]),
    }
}

fn get_sign_in_steps() -> HashMap<SignInStep, StepImpl> {
    hashmap! {
        SignInStep::AskingUserName => StepImpl {
            enter: |_, _| Output::None,
            exit: |_| Output::None,
            prompt: |_| Output::Str("What is your name? "),
            process_input: process_asking_user_name_input,
        },

        SignInStep::AskingPassword => StepImpl {
            enter: |_, _| Output::JSON(json!({ "inputType": "password" })),
            exit: |_| Output::JSON(json!({ "inputType": "text" })),
            prompt: |_| Output::Str("Please enter your password: "),
            process_input: process_asking_password_input,
        },

        SignInStep::AskingSignUpData => StepImpl {
            enter: |data, realm| enter_sign_up_step(&data.sign_up_state.as_ref().unwrap(), realm),
            exit: |data| exit_sign_up_step(&data.sign_up_state.as_ref().unwrap()),
            prompt: |data| prompt_sign_up_step(&data.sign_up_state.as_ref().unwrap()),
            process_input: |state, realm, _, input| {
                let (sign_up_state, output, log_messages) = process_sign_up_input(
                    &state.data.sign_up_state.as_ref().unwrap(),
                    realm,
                    input
                );
                (
                    new_state(
                        if sign_up_state.is_session_closed() {
                            SignInStep::SessionClosed
                        } else if sign_up_state.is_signed_up() {
                            SignInStep::SignedIn
                        } else {
                            SignInStep::AskingSignUpData
                        },
                        SignInData { sign_up_state: Some(sign_up_state), ..state.data.clone() }
                    ),
                    output,
                    log_messages
                )
            }
        }
    }
}

fn new_state(step: SignInStep, data: SignInData) -> SignInState {
    SignInState { step, data }
}

fn process_asking_user_name_input(
    state: &SignInState,
    realm: &Realm,
    _: &String,
    input: String,
) -> (SignInState, Output, Vec<String>) {
    lazy_static! {
        static ref RESERVED_NAMES: Vec<String> = vec![
            "Admin".to_owned(),
            "Administrator".to_owned(),
            "Root".to_owned()
        ];
    }
    let user_name = validate_user_name(input.to_lowercase());
    if user_name.len() < 3 {
        (
            state.clone(),
            if user_name.len() == 0 {
                Output::None
            } else {
                Output::Str("I'm sorry, but your name should consist of at least 3 letters.\n")
            },
            vec![],
        )
    } else if let Some(player) = realm.player_by_name(&user_name) {
        (
            new_state(
                SignInStep::AskingPassword,
                SignInData {
                    player: Some(player.clone()),
                    ..state.data.clone()
                },
            ),
            Output::None,
            vec![],
        )
    } else if RESERVED_NAMES.contains(&user_name) {
        (
            state.clone(),
            Output::Str("Yeah right, like I believe that...\n"),
            vec![],
        )
    } else {
        (
            new_state(
                SignInStep::AskingSignUpData,
                SignInData {
                    sign_up_state: Some(SignUpState::new(user_name)),
                    ..state.data.clone()
                },
            ),
            Output::None,
            vec![],
        )
    }
}

fn process_asking_password_input(
    state: &SignInState,
    _: &Realm,
    _: &String,
    input: String,
) -> (SignInState, Output, Vec<String>) {
    if let Some(player) = &state.data.player {
        let name = player.name();
        if player.matches_password(&input) {
            let log_messages = vec![format!("Authentication success for player \"{}\"", name)];

            if player.session_id().is_some() {
                (
                    new_state(SignInStep::SessionClosed, state.data.clone()),
                    Output::Str(
                        "Cannot sign you in because you're already signed in from another \
                        location.\n",
                    ),
                    log_messages,
                )
            } else {
                (
                    new_state(SignInStep::SignedIn, state.data.clone()),
                    Output::String(format!(
                        "Welcome back, {}. Type *help* if you're feeling lost.\n",
                        name
                    )),
                    log_messages,
                )
            }
        } else {
            (
                state.clone(),
                Output::Str("Password incorrect.\n"),
                vec![format!("Authentication failed for player \"{}\"", name)],
            )
        }
    } else {
        (state.clone(), Output::None, vec![])
    }
}

fn validate_user_name(input: String) -> String {
    let mut user_name = String::new();
    let mut num_special_chars = 0;
    for character in input.chars() {
        if is_letter(character) {
            user_name.push(if user_name.len() == 0 {
                character.to_ascii_uppercase()
            } else {
                character
            });
        } else if user_name.len() > 0
            && num_special_chars == 0
            && (character == '\'' || character == '-')
        {
            user_name.push(character);
            num_special_chars += 1;
        }
        if user_name.len() >= 12 {
            break;
        }
    }
    if user_name.len() > 1 && !is_letter(user_name.chars().rev().next().unwrap_or_default()) {
        user_name = user_name.chars().take(user_name.len() - 1).collect();
    }
    user_name
}
<|endoftext|>