<commit_before>//! A [Todo-Backend](http://todobackend.com/) implementation with rotor-http.
//!
//! Todo-Backed is a showcase to compare different languages and frameworks
//! for building web services. One has to implement a simple todo list
//! that is checked by automatic tests available on the website.
//!
//! NOTE: This example only works with Nightly Rust for now because
//! of serde macros.
//!
//! To run use `cargo run --example todobackend --features nightly`.
//!
//! Now you can edit your own todo list via
//! http://todobackend.com/client/index.html?http://localhost:3000
//! (The web page is just for the frontend, the backend is this Rust
//! program.)

#![cfg_attr(feature="nightly", feature(custom_derive, plugin))]
#![cfg_attr(feature="nightly", plugin(serde_macros))]
#![allow(warnings)]

extern crate rotor;
extern crate rotor_http;
#[cfg(feature="nightly")] extern crate serde;
#[cfg(feature="nightly")] extern crate serde_json;

use std::borrow::Cow;
use std::collections::HashMap;
use std::str::from_utf8;
use std::time::Duration;

use rotor::{Scope, Time};
use rotor::mio::tcp::TcpListener;
use rotor_http::server::{self, Fsm, Head, RecvMode, Response, Server};

/// Represents a single Todo entry.
///
/// The entry is serialized and deserialized by serde without
/// any handwritten code.
#[cfg(feature="nightly")]
#[derive(Serialize, Deserialize, Debug)]
struct Todo {
    url: Option<String>,
    title: String,
    #[serde(default)]
    completed: bool,
    #[serde(default)]
    order: u64,
}

#[cfg(feature="nightly")]
impl Todo {
    fn set_url(&mut self, id: u64) {
        self.url = Some(format!("http://localhost:3000/todo/{}", id));
    }

    fn patch(&mut self, patch: TodoPatch) {
        if let Some(title) = patch.title {
            self.title = title;
        }
        if let Some(completed) = patch.completed {
            self.completed = completed;
        }
        if let Some(order) = patch.order {
            self.order = order
        }
    }
}

/// One can patch the title and the status of todos. It is also
/// possible to change the ordering.
#[cfg(feature="nightly")]
#[derive(Deserialize, Debug)]
struct TodoPatch {
    title: Option<String>,
    completed: Option<bool>,
    order: Option<u64>,
}

#[cfg(feature="nightly")]
/// Context is used for global storage.
struct Context {
    /// The last id assigned to a todo entry.
    ///
    /// New ids are incremented by one.
    last_id: u64,
    /// The in memory "database" used for the todo-list.
    database: HashMap<u64, Todo>,
}

#[cfg(feature="nightly")]
trait Database {
    /// Get a unique new id.
    fn id(&mut self) -> u64;
    /// List all existing Todos.
    fn list(&self) -> Vec<&Todo>;
    /// Create a new Todo.
    fn create(&mut self, u64, Todo);
    /// Clear the database.
    fn clear(&mut self);
    /// Get one todo by id.
    fn get(&mut self, u64) -> Option<&Todo>;
    /// Get a mutable reference of a todo.
    ///
    /// This is useful for updating or patching an entry.
    fn get_mut(&mut self, u64) -> Option<&mut Todo>;
    /// Delete an entry.
    fn delete(&mut self, id: u64);
}

#[cfg(feature="nightly")]
impl Database for Context {
    fn id(&mut self) -> u64 {
        self.last_id += 1;
        self.last_id
    }

    fn list(&self) -> Vec<&Todo> {
        self.database.values().collect()
    }

    fn create(&mut self, id: u64, todo: Todo) {
        assert!(self.database.insert(id, todo).is_none());
    }

    fn clear(&mut self) {
        self.database.clear();
    }

    fn get(&mut self, id: u64) -> Option<&Todo> {
        self.database.get(&id)
    }

    fn get_mut(&mut self, id: u64) -> Option<&mut Todo> {
        self.database.get_mut(&id)
    }

    fn delete(&mut self, id: u64) {
        self.database.remove(&id);
    }
}

/// The main server state machine. After parsing the header
/// the type of request determined. Each variant of the
/// enum represents a single action. Some are directly related
/// to todo entries others are for error handling or and CORS.
#[derive(Debug, Clone)]
enum TodoBackend {
    List,
    Create,
    Clear,
    Get(u64),
    Patch(u64),
    Delete(u64),
    Preflight,
    MethodNotAllowed(&'static [u8]),
    NotFound,
}

#[cfg(feature="nightly")]
impl Server for TodoBackend {
    type Seed = ();
    type Context = Context;
    fn headers_received(_seed: (), head: Head, _response: &mut Response,
        scope: &mut Scope<Context>)
        -> Option<(Self, RecvMode, Time)>
    {
        use TodoBackend::*;
        Some((if head.method == "OPTIONS" {
                Preflight
            } else if head.path == "/" {
                match head.method {
                    "GET" => List,
                    "POST" => Create,
                    "DELETE" => Clear,
                    _ => MethodNotAllowed(b"GET, POST, DELETE"),
                }
            } else if head.path.starts_with("/todo/") {
                let id = head.path[6..].parse().unwrap();
                match head.method {
                    "GET" => Get(id),
                    "PATCH" => Patch(id),
                    "DELETE" => Delete(id),
                    _ => MethodNotAllowed(b"GET, PATCH, DELETE"),
                }
            } else {
                NotFound
            }, RecvMode::Buffered(1024), scope.now() + Duration::new(10, 0)))

    }

    fn request_received(self, data: &[u8], response: &mut Response,
        scope: &mut Scope<Context>)
        -> Option<Self>
    {
        use self::TodoBackend::*;
        let text_data = from_utf8(data).unwrap();
        let (status, reason, body) = match self {
            List => (200, "OK", Cow::Owned(serde_json::to_string(&scope.list()).unwrap().into_bytes())),
            Create => {
                let mut todo: Todo = serde_json::from_str(text_data).unwrap();
                let id = scope.id();
                todo.set_url(id);
                let body = serde_json::to_string(&todo).unwrap().into_bytes();
                scope.create(id, todo);
                (201, "Created", Cow::Owned(body))
            }
            Clear => {
                scope.clear();
                (200, "OK", Cow::Borrowed(&b"[]"[..]))
            }
            Get(id) => (200, "OK",
                Cow::Owned(serde_json::to_string(&scope.get(id)).unwrap().into_bytes())),
            Patch(id) => {
                let patch: TodoPatch = serde_json::from_str(text_data).unwrap();
                if let Some(todo) = scope.get_mut(id) {
                    todo.patch(patch);
                    let body = serde_json::to_string(&todo).unwrap().into_bytes();
                    (200, "OK", Cow::Owned(body))
                } else {
                    (404, "Not found", Cow::Borrowed(&b"{}"[..]))
                }
            }
            Delete(id) => {
                scope.delete(id);
                (200, "OK", Cow::Borrowed(&b"{}"[..]))
            }
            Preflight => {
                response.status(200, "OK");
                response.add_length(0).unwrap();
                response.add_header("Access-Control-Allow-Origin", b"*").unwrap();
                response.add_header("Access-Control-Allow-Methods",
                                    b"GET, POST, DELETE, PATCH").unwrap();
                response.add_header("Access-Control-Allow-Headers",
                                    b"Content-Type").unwrap();
                response.add_header("Access-Control-Max-Age", b"60").unwrap();
                response.done_headers().unwrap();
                response.done();
                return None;
            }
            MethodNotAllowed(methods) => {
                let reason = "Method Not Allowed";
                response.status(405, reason);
                response.add_length(reason.len() as u64).unwrap();
                response.add_header("Access-Control-Allow-Origin", b"*").unwrap();
                response.add_header("Content-Type", b"text/plain").unwrap();
                response.add_header("Allow", methods).unwrap();
                response.done_headers().unwrap();
                response.write_body(reason.as_bytes());
                response.done();
                return None;
            }
            NotFound => {
                let reason = "Not Found";
                response.status(404, reason);
                response.add_length(reason.len() as u64).unwrap();
                response.add_header("Access-Control-Allow-Origin", b"*").unwrap();
                response.add_header("Content-Type", b"text/plain").unwrap();
                response.done_headers().unwrap();
                response.write_body(reason.as_bytes());
                response.done();
                return None;
            },
        };
        response.status(status, reason);
        response.add_length(body.len() as u64).unwrap();
        response.add_header("Access-Control-Allow-Origin", b"*").unwrap();
        response.add_header("Content-Type", b"application/json").unwrap();
        response.done_headers().unwrap();
        response.write_body(&body[..]);
        response.done();
        None
    }
    // It is save to leave out `request_chunk` and `request_end` since we
    // only use buffered requests in this example.
    fn request_chunk(self, _chunk: &[u8], _response: &mut Response,
        _scope: &mut Scope<Context>)
        -> Option<Self> { unreachable!(); }
    fn request_end(self, _response: &mut Response, _scope: &mut Scope<Context>)
        -> Option<Self> { unreachable!(); }

    fn timeout(self, _response: &mut Response, _scope: &mut Scope<Context>)
        -> Option<(Self, Time)>
    {
        unimplemented!();
    }
    fn wakeup(self, _response: &mut Response, _scope: &mut Scope<Context>)
        -> Option<Self>
    {
        unimplemented!();
    }
}

#[cfg(feature="nightly")]
fn main() {
    let event_loop = rotor::Loop::new(&rotor::Config::new()).unwrap();
    let mut loop_inst = event_loop.instantiate(Context {
        last_id: 0,
        database: HashMap::new(),
    });
    let lst = TcpListener::bind(&"127.0.0.1:3000".parse().unwrap()).unwrap();
    loop_inst.add_machine_with(|scope| {
        Fsm::<TodoBackend, _>::new(lst, scope)
    }).unwrap();
    loop_inst.run().unwrap();
}

#[cfg(not(feature="nightly"))]
fn main() { panic!("See NOTE in source. You must use --features nightly.")}
<commit_msg>Fix todobackend example<commit_after>//! A [Todo-Backend](http://todobackend.com/) implementation with rotor-http.
//!
//! Todo-Backed is a showcase to compare different languages and frameworks
//! for building web services. One has to implement a simple todo list
//! that is checked by automatic tests available on the website.
//!
//! NOTE: This example only works with Nightly Rust for now because
//! of serde macros.
//!
//! To run use `cargo run --example todobackend --features nightly`.
//!
//! Now you can edit your own todo list via
//! http://todobackend.com/client/index.html?http://localhost:3000
//! (The web page is just for the frontend, the backend is this Rust
//! program.)

#![cfg_attr(feature="nightly", feature(custom_derive, plugin))]
#![cfg_attr(feature="nightly", plugin(serde_macros))]
#![allow(warnings)]

extern crate rotor;
extern crate rotor_http;
#[cfg(feature="nightly")] extern crate serde;
#[cfg(feature="nightly")] extern crate serde_json;

use std::borrow::Cow;
use std::collections::HashMap;
use std::str::from_utf8;
use std::time::Duration;

use rotor::{Scope, Time};
use rotor::mio::tcp::TcpListener;
use rotor_http::server::{self, Fsm, Head, RecvMode, Response, Server};

/// Represents a single Todo entry.
///
/// The entry is serialized and deserialized by serde without
/// any handwritten code.
#[cfg(feature="nightly")]
#[derive(Serialize, Deserialize, Debug)]
struct Todo {
    url: Option<String>,
    title: String,
    #[serde(default)]
    completed: bool,
    #[serde(default)]
    order: u64,
}

#[cfg(feature="nightly")]
impl Todo {
    fn set_url(&mut self, id: u64) {
        self.url = Some(format!("http://localhost:3000/todo/{}", id));
    }

    fn patch(&mut self, patch: TodoPatch) {
        if let Some(title) = patch.title {
            self.title = title;
        }
        if let Some(completed) = patch.completed {
            self.completed = completed;
        }
        if let Some(order) = patch.order {
            self.order = order
        }
    }
}

/// One can patch the title and the status of todos. It is also
/// possible to change the ordering.
#[cfg(feature="nightly")]
#[derive(Deserialize, Debug)]
struct TodoPatch {
    title: Option<String>,
    completed: Option<bool>,
    order: Option<u64>,
}

#[cfg(feature="nightly")]
/// Context is used for global storage.
struct Context {
    /// The last id assigned to a todo entry.
    ///
    /// New ids are incremented by one.
    last_id: u64,
    /// The in memory "database" used for the todo-list.
    database: HashMap<u64, Todo>,
}

#[cfg(feature="nightly")]
trait Database {
    /// Get a unique new id.
    fn id(&mut self) -> u64;
    /// List all existing Todos.
    fn list(&self) -> Vec<&Todo>;
    /// Create a new Todo.
    fn create(&mut self, u64, Todo);
    /// Clear the database.
    fn clear(&mut self);
    /// Get one todo by id.
    fn get(&mut self, u64) -> Option<&Todo>;
    /// Get a mutable reference of a todo.
    ///
    /// This is useful for updating or patching an entry.
    fn get_mut(&mut self, u64) -> Option<&mut Todo>;
    /// Delete an entry.
    fn delete(&mut self, id: u64);
}

#[cfg(feature="nightly")]
impl Database for Context {
    fn id(&mut self) -> u64 {
        self.last_id += 1;
        self.last_id
    }

    fn list(&self) -> Vec<&Todo> {
        self.database.values().collect()
    }

    fn create(&mut self, id: u64, todo: Todo) {
        assert!(self.database.insert(id, todo).is_none());
    }

    fn clear(&mut self) {
        self.database.clear();
    }

    fn get(&mut self, id: u64) -> Option<&Todo> {
        self.database.get(&id)
    }

    fn get_mut(&mut self, id: u64) -> Option<&mut Todo> {
        self.database.get_mut(&id)
    }

    fn delete(&mut self, id: u64) {
        self.database.remove(&id);
    }
}

/// The main server state machine. After parsing the header
/// the type of request determined. Each variant of the
/// enum represents a single action. Some are directly related
/// to todo entries others are for error handling or and CORS.
#[derive(Debug, Clone)]
enum TodoBackend {
    List,
    Create,
    Clear,
    Get(u64),
    Patch(u64),
    Delete(u64),
    Preflight,
    MethodNotAllowed(&'static [u8]),
    NotFound,
}

#[cfg(feature="nightly")]
impl Server for TodoBackend {
    type Seed = ();
    type Context = Context;
    fn headers_received(_seed: (), head: Head, _response: &mut Response,
        scope: &mut Scope<Context>)
        -> Option<(Self, RecvMode, Time)>
    {
        use TodoBackend::*;
        Some((if head.method == "OPTIONS" {
                Preflight
            } else if head.path == "/" {
                match head.method {
                    "GET" => List,
                    "POST" => Create,
                    "DELETE" => Clear,
                    _ => MethodNotAllowed(b"GET, POST, DELETE"),
                }
            } else if head.path.starts_with("/todo/") {
                let id = head.path[6..].parse().unwrap();
                match head.method {
                    "GET" => Get(id),
                    "PATCH" => Patch(id),
                    "DELETE" => Delete(id),
                    _ => MethodNotAllowed(b"GET, PATCH, DELETE"),
                }
            } else {
                NotFound
            }, RecvMode::Buffered(1024), scope.now() + Duration::new(10, 0)))

    }

    fn request_received(self, data: &[u8], response: &mut Response,
        scope: &mut Scope<Context>)
        -> Option<Self>
    {
        use self::TodoBackend::*;
        let text_data = from_utf8(data).unwrap();
        let (status, reason, body) = match self {
            List => (200, "OK", Cow::Owned(serde_json::to_string(&scope.list()).unwrap().into_bytes())),
            Create => {
                let mut todo: Todo = serde_json::from_str(text_data).unwrap();
                let id = scope.id();
                todo.set_url(id);
                let body = serde_json::to_string(&todo).unwrap().into_bytes();
                scope.create(id, todo);
                (201, "Created", Cow::Owned(body))
            }
            Clear => {
                scope.clear();
                (200, "OK", Cow::Borrowed(&b"[]"[..]))
            }
            Get(id) => (200, "OK",
                Cow::Owned(serde_json::to_string(&scope.get(id)).unwrap().into_bytes())),
            Patch(id) => {
                let patch: TodoPatch = serde_json::from_str(text_data).unwrap();
                if let Some(todo) = scope.get_mut(id) {
                    todo.patch(patch);
                    let body = serde_json::to_string(&todo).unwrap().into_bytes();
                    (200, "OK", Cow::Owned(body))
                } else {
                    (404, "Not found", Cow::Borrowed(&b"{}"[..]))
                }
            }
            Delete(id) => {
                scope.delete(id);
                (200, "OK", Cow::Borrowed(&b"{}"[..]))
            }
            Preflight => {
                response.status(200, "OK");
                response.add_length(0).unwrap();
                response.add_header("Access-Control-Allow-Origin", b"*").unwrap();
                response.add_header("Access-Control-Allow-Methods",
                                    b"GET, POST, DELETE, PATCH").unwrap();
                response.add_header("Access-Control-Allow-Headers",
                                    b"Content-Type").unwrap();
                response.add_header("Access-Control-Max-Age", b"60").unwrap();
                response.done_headers().unwrap();
                response.done();
                return None;
            }
            MethodNotAllowed(methods) => {
                let reason = "Method Not Allowed";
                response.status(405, reason);
                response.add_length(reason.len() as u64).unwrap();
                response.add_header("Access-Control-Allow-Origin", b"*").unwrap();
                response.add_header("Content-Type", b"text/plain").unwrap();
                response.add_header("Allow", methods).unwrap();
                response.done_headers().unwrap();
                response.write_body(reason.as_bytes());
                response.done();
                return None;
            }
            NotFound => {
                let reason = "Not Found";
                response.status(404, reason);
                response.add_length(reason.len() as u64).unwrap();
                response.add_header("Access-Control-Allow-Origin", b"*").unwrap();
                response.add_header("Content-Type", b"text/plain").unwrap();
                response.done_headers().unwrap();
                response.write_body(reason.as_bytes());
                response.done();
                return None;
            },
        };
        response.status(status, reason);
        response.add_length(body.len() as u64).unwrap();
        response.add_header("Access-Control-Allow-Origin", b"*").unwrap();
        response.add_header("Content-Type", b"application/json").unwrap();
        response.done_headers().unwrap();
        response.write_body(&body[..]);
        response.done();
        None
    }
    // It is save to leave out `request_chunk` and `request_end` since we
    // only use buffered requests in this example.
    fn request_chunk(self, _chunk: &[u8], _response: &mut Response,
        _scope: &mut Scope<Context>)
        -> Option<Self> { unreachable!(); }
    fn request_end(self, _response: &mut Response, _scope: &mut Scope<Context>)
        -> Option<Self> { unreachable!(); }

    fn timeout(self, _response: &mut Response, _scope: &mut Scope<Context>)
        -> Option<(Self, Time)>
    {
        unimplemented!();
    }
    fn wakeup(self, _response: &mut Response, _scope: &mut Scope<Context>)
        -> Option<Self>
    {
        unimplemented!();
    }
}

#[cfg(feature="nightly")]
fn main() {
    let event_loop = rotor::Loop::new(&rotor::Config::new()).unwrap();
    let mut loop_inst = event_loop.instantiate(Context {
        last_id: 0,
        database: HashMap::new(),
    });
    let lst = TcpListener::bind(&"127.0.0.1:3000".parse().unwrap()).unwrap();
    loop_inst.add_machine_with(|scope| {
        Fsm::<TodoBackend, _>::new(lst, (), scope)
    }).unwrap();
    loop_inst.run().unwrap();
}

#[cfg(not(feature="nightly"))]
fn main() { panic!("See NOTE in source. You must use --features nightly.")}
<|endoftext|>


<commit_before>use std::io::{Listener, Acceptor};
use std::io::net::tcp::{TcpListener, TcpStream};

static EXT_PORT: u16 = 8443;
static SSH_PORT: u16 = 22;
static SSL_PORT: u16 = 9443;

fn keep_copying(mut a: TcpStream, mut b: TcpStream) {
	let mut buf = [0u8,..1024];
	loop {
		let read = match a.read(buf) {
			Err(..) => { drop(a); return; },
			Ok(r) => r
		};
		match b.write(buf.slice_to(read)) {
			Err(..) => { drop(a); return; },
			Ok(..) => ()
		};
	}
}

fn start_pipe(front: TcpStream, port: u16, header: u8) {
	let mut back = match TcpStream::connect("127.0.0.1", port) {
		Err(e) => { println!("Error connecting: {}", e); return; },
		Ok(b) => b
	};
	match back.write_u8(header) {
		Err(e) => { println!("Error writing first byte: {}", e); return }
		Ok(..) => ()
	}
	let front_copy = front.clone();
	let back_copy = back.clone();

	spawn(proc() {
		keep_copying(front, back);
	});
	spawn(proc() {
		keep_copying(back_copy, front_copy);
	});
}

fn handle_new_connection(mut stream: TcpStream) {
	//stream.set_read_timeout(2000);
	let header = match stream.read_byte() {
		Err(..) => -1,
		Ok(b) => b
	};
	if header == 22 || header == 128 {
		start_pipe(stream, SSL_PORT, header);
	}
	else {
		start_pipe(stream, SSH_PORT, header);
	}
}

fn main() {
    let mut acceptor = TcpListener::bind("127.0.0.1", EXT_PORT).listen().unwrap();
    println!("listening started, ready to accept");
    for stream in acceptor.incoming() {
		match stream {
			Err(e) => println!("Strange connection broken: {}", e),
			Ok(stream) => spawn(proc() {
					// connection succeeded
					handle_new_connection(stream);
					})
		}
    }
	drop(acceptor);
}
<commit_msg>Made use of the option structure<commit_after>use std::io::{Listener, Acceptor};
use std::io::net::tcp::{TcpListener, TcpStream};

static EXT_PORT: u16 = 8443;
static SSH_PORT: u16 = 22;
static SSL_PORT: u16 = 9443;

fn keep_copying(mut a: TcpStream, mut b: TcpStream) {
	let mut buf = [0u8,..1024];
	loop {
		let read = match a.read(buf) {
			Err(..) => { drop(a); return; },
			Ok(r) => r
		};
		match b.write(buf.slice_to(read)) {
			Err(..) => { drop(a); return; },
			Ok(..) => ()
		};
	}
}

fn start_pipe(front: TcpStream, port: u16, header: Option<u8>) {
	let mut back = match TcpStream::connect("127.0.0.1", port) {
		Err(e) => { println!("Error connecting: {}", e); return; },
		Ok(b) => b
	};
	if header.is_some() {
		match back.write_u8(header.unwrap()) {
			Err(e) => { println!("Error writing first byte: {}", e); return }
			Ok(..) => ()
		}
	}
	let front_copy = front.clone();
	let back_copy = back.clone();

	spawn(proc() {
		keep_copying(front, back);
	});
	spawn(proc() {
		keep_copying(back_copy, front_copy);
	});
}

fn handle_new_connection(mut stream: TcpStream) {
	let header: Option<u8> = match stream.read_byte() {
		Err(..) => None,
		Ok(b) => Some(b)
	};
	if header.is_some() && header.unwrap() == 22 || header.unwrap() == 128 {
		start_pipe(stream, SSL_PORT, header);
	}
	else {
		start_pipe(stream, SSH_PORT, header);
	}
}

fn main() {
    let mut acceptor = TcpListener::bind("127.0.0.1", EXT_PORT).listen().unwrap();
    println!("listening started, ready to accept");
    for stream in acceptor.incoming() {
		match stream {
			Err(e) => println!("Strange connection broken: {}", e),
			Ok(stream) => spawn(proc() {
					// connection succeeded
					handle_new_connection(stream);
					})
		}
    }
	drop(acceptor);
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use dom::bindings::codegen::Bindings::DocumentBinding::DocumentBinding::DocumentMethods;
use dom::bindings::codegen::Bindings::ElementBinding::ElementBinding::ElementMethods;
use dom::bindings::inheritance::Castable;
use dom::bindings::root::DomRoot;
use dom::characterdata::CharacterData;
use dom::document::Document;
use dom::element::Element;
use dom::htmlelement::HTMLElement;
use dom::node::Node;
use dom::text::Text;
use serde_json;
use std::borrow::Cow;
use std::collections::HashMap;

pub struct Microdata {}
#[derive(Clone,Serialize)]
#[serde(untagged)]
enum Data {
    StrValue(String),
    VecValue(Vec<Data>),
    DataValue(Box<Data>),
    HashValue(HashMap<String, Data>)
}

impl Microdata {
    //[Pref="dom.microdata.testing.enabled"]
    pub fn parse(doc: &Document, node: &Node) -> HashMap<String, String> {
        //let dup_doc = Cow::Borrowed(doc);
        let serialized_vcard = Self::parse_vcard(doc);
        let serialized_json = Self::parse_json(node);
        let mut serialized_data: HashMap<String, String> = HashMap::new();
        serialized_data.insert("vcard".to_string(), serialized_vcard);
        serialized_data.insert("json".to_string(), serialized_json);
        return serialized_data;
    }

    pub fn parse_vcard(doc: &Document) -> String {
        let ele = doc.upcast::<Node>();
        let mut start_vcard = false;
        let mut result: String = String::new();
        let mut master_map: HashMap<String, HashMap<String, String>> = HashMap::new();
        let mut master_key: String = String::new();

        result += "BEGIN:VCARD\nPROFILE:VCARD\nVERSION:4.0\nSOURCE:";
        result += doc.url().as_str();

        let title = doc.Title();
        if !title.is_empty() && !title.trim().is_empty() {
            result += "\nNAME:";
            result += title.trim();
        }

        result += "\n";

        for element in ele.traverse_preorder().filter_map(DomRoot::downcast::<Element>) {
            if element.is::<HTMLElement>() {
                if element.has_attribute(&local_name!("itemtype")) {
                    let mut atoms = element.get_tokenlist_attribute(&local_name!("itemtype"), );
                    if !atoms.is_empty() {
                        let val = atoms.remove(0);
                        if val.trim() == "http://microformats.org/profile/hcard" {
                            if !start_vcard {
                                start_vcard = true;
                            } else {
                                break;
                            }
                        }
                    }
                }
                if start_vcard {
                    let mut atoms = element.get_tokenlist_attribute(&local_name!("itemprop"), );
                    if !atoms.is_empty() {
                        let temp_key = atoms.remove(0);
                        if element.has_attribute(&local_name!("itemscope")) {
                            master_key = String::from(temp_key.trim()).to_owned();
                            let dup_master_key = Cow::Borrowed(&master_key);
                            master_map.entry(dup_master_key.to_string()).or_insert(HashMap::new());
                        } else {
                            let temp = String::from(temp_key.trim()).to_owned();
                            let dup_key = Cow::Borrowed(&temp);
                            let data = String::from(element.GetInnerHTML().unwrap());
                            let dup_master_key = Cow::Borrowed(&master_key);
                            let temp_map = master_map.entry(dup_master_key.to_string()).or_insert(HashMap::new());
                            temp_map.insert(dup_key.to_string(), String::from(data));
                        }
                    }
                }
            }
        }
        let vcard_parts = ["n", "org", "tel", "adr"];
        for info_type in vcard_parts.iter() {
            let detail_map_val = master_map.get(*info_type);
            if detail_map_val.is_none() {
                continue;
            }
            let detail_map = detail_map_val.unwrap();
            match *info_type {
                "n" => {
                    let mut n_value: String = String::new();

                    let name_parts = ["family-name", "given-name",
                    "additional-name", "honorific-prefix", "honorific-suffix"];
                    for part in name_parts.iter() {
                        if detail_map.contains_key(*part) {
                            n_value += format!("{};", detail_map.get(*part).unwrap()).as_str();
                        }
                    }
                    n_value.pop();

                    result += format!("{}:{}\n", info_type.to_ascii_uppercase(), n_value).as_str();
                },
                "org" => {
                    let mut org_value: String = String::new();

                    let org_parts = ["organization-name", "organization-unit"];
                    for part in org_parts.iter() {
                        if detail_map.contains_key(*part) {
                            org_value += format!("{};", detail_map.get(*part).unwrap()).as_str();
                        }
                    }
                    org_value.pop();

                    result += format!("{}:{}\n", info_type.to_ascii_uppercase(), org_value).as_str();
                },
                "tel" => {
                    let mut tel_value: String = String::new();

                    let tel_parts = ["value"];
                    for part in tel_parts.iter() {
                        if detail_map.contains_key(*part) {
                            tel_value += format!("{};", detail_map.get(*part).unwrap()).as_str();
                        }
                    }
                    tel_value.pop();

                    result += format!("{}:{}\n", info_type.to_ascii_uppercase(), tel_value).as_str();
                },
                "adr" => {
                    let mut adr_value: String = String::new();

                    let adr_parts = ["street-address", "locality", "region", "postal-code",
                    "country-name", "post-office-box", "extended-address"];
                    for part in adr_parts.iter() {
                        if detail_map.contains_key(*part) {
                            adr_value += format!("{};", detail_map.get(*part).unwrap()).as_str();
                        }
                    }
                    adr_value.pop();

                    result += format!("{}:{}\n", info_type.to_ascii_uppercase(), adr_value).as_str();
                },
                _ => {},
            }
        }
        result += "END:VCARD";
        if start_vcard {
            return result;
        } else {
            return "".to_string();
        }
    }

    pub fn parse_json(node: &Node) -> String {
        let json_data: Data = Self::traverse(node).unwrap();
        let json = serde_json::to_string(&json_data);
        //println!("printing json from microdata {:?}", json);
        //return json.ok().unwrap();

        return "".to_string();
    }

    fn get_attr_value(element: &Element, property: &str) -> Option<String> {
        println!("{:?}", property);
        // let mut atoms =  match property {
        //   "itemprop" => element.get_tokenlist_attribute(&local_name!("itemprop"), ),
        //   "itemtype" =>  element.get_tokenlist_attribute(&local_name!("itemtype"), ),
        //   _ => {},
        // };
        // if !atoms.is_empty() {
        //   let temp_key = atoms.remove(0);
        //   return Some(String::from(temp_key.trim()).to_owned());
        // }
        // else {
        //   return None;
        // }
        Some(String::from("itemprop"))
    }

    fn traverse(node: &Node) -> Option<Data> {
        if !node.is::<Element>() {
            if let Some(ref text) = node.downcast::<Text>() {
                let mut content = String::new();
                content.push_str(&text.upcast::<CharacterData>().data());
                return Some(Data::StrValue(String::from(content)));
            }
            None
        } else {
            let element = node.downcast::<Element>().unwrap();
            let mut head_str = String::from("");
            let mut parent_vec: Vec<Data> = Vec::new();
            let item_type = Self::get_attr_value(element, "itemtype").unwrap();
            // if element.has_attribute(&local_name!("itemscope")) &&
            // element.has_attribute(&local_name!("itemtype")) &&
            // !element.has_attribute(&local_name!("itemprop")) {
            //     head_str = String::from("items");
            //     let mut propMap:HashMap<String,Data> = HashMap::new();
            //     //Data::HashValue(propMap)
            //     let item_type = Self::get_attr_value(element,"item_type").unwrap();

            // }
            // else if element.has_attribute(&local_name!("itemprop")) &&
            // element.has_attribute(&local_name!("item_type")) {

            //     head_str = Self::get_attr_value(element,"itemprop").unwrap();
            //     let item_type = Self::get_attr_value(element,"item_type").unwrap();

            // } else {
            //     return None;
            // }
            let mut inner_map: HashMap<String, Data> = HashMap::new();
            for child in node.children() {
                if let Some(childData) = Self::traverse(child.upcast::<Node>()) {
                    parent_vec.push(childData);
                }
            }
            inner_map.insert(head_str, Data::VecValue(parent_vec));
            Some(Data::HashValue(inner_map))
        }
    }
}
<commit_msg>Removed JSON code and replaced with a stub<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use dom::bindings::codegen::Bindings::DocumentBinding::DocumentBinding::DocumentMethods;
use dom::bindings::codegen::Bindings::ElementBinding::ElementBinding::ElementMethods;
use dom::bindings::inheritance::Castable;
use dom::bindings::root::DomRoot;
use dom::characterdata::CharacterData;
use dom::document::Document;
use dom::element::Element;
use dom::htmlelement::HTMLElement;
use dom::node::Node;
use dom::text::Text;
use serde_json;
use std::borrow::Cow;
use std::collections::HashMap;

pub struct Microdata {}

impl Microdata {
    pub fn parse(doc: &Document, node: &Node) -> HashMap<String, String> {
        let serialized_vcard = Self::parse_vcard(doc);
        let serialized_json = Self::parse_json(node);
        let mut serialized_data: HashMap<String, String> = HashMap::new();
        serialized_data.insert("vcard".to_string(), serialized_vcard);
        serialized_data.insert("json".to_string(), serialized_json);
        return serialized_data;
    }

    pub fn parse_vcard(doc: &Document) -> String {
        let ele = doc.upcast::<Node>();
        let mut start_vcard = false;
        let mut result: String = String::new();
        let mut master_map: HashMap<String, HashMap<String, String>> = HashMap::new();
        let mut master_key: String = String::new();

        result += "BEGIN:VCARD\nPROFILE:VCARD\nVERSION:4.0\nSOURCE:";
        result += doc.url().as_str();

        let title = doc.Title();
        if !title.is_empty() && !title.trim().is_empty() {
            result += "\nNAME:";
            result += title.trim();
        }

        result += "\n";

        for element in ele.traverse_preorder().filter_map(DomRoot::downcast::<Element>) {
            if element.is::<HTMLElement>() {
                if element.has_attribute(&local_name!("itemtype")) {
                    let mut atoms = element.get_tokenlist_attribute(&local_name!("itemtype"), );
                    if !atoms.is_empty() {
                        let val = atoms.remove(0);
                        if val.trim() == "http://microformats.org/profile/hcard" {
                            if !start_vcard {
                                start_vcard = true;
                            } else {
                                break;
                            }
                        }
                    }
                }
                if start_vcard {
                    let mut atoms = element.get_tokenlist_attribute(&local_name!("itemprop"), );
                    if !atoms.is_empty() {
                        let temp_key = atoms.remove(0);
                        if element.has_attribute(&local_name!("itemscope")) {
                            master_key = String::from(temp_key.trim()).to_owned();
                            let dup_master_key = Cow::Borrowed(&master_key);
                            master_map.entry(dup_master_key.to_string()).or_insert(HashMap::new());
                        } else {
                            let temp = String::from(temp_key.trim()).to_owned();
                            let dup_key = Cow::Borrowed(&temp);
                            let data = String::from(element.GetInnerHTML().unwrap());
                            let dup_master_key = Cow::Borrowed(&master_key);
                            let temp_map = master_map.entry(dup_master_key.to_string()).or_insert(HashMap::new());
                            temp_map.insert(dup_key.to_string(), String::from(data));
                        }
                    }
                }
            }
        }
        let vcard_parts = ["n", "org", "tel", "adr"];
        for info_type in vcard_parts.iter() {
            let detail_map_val = master_map.get(*info_type);
            if detail_map_val.is_none() {
                continue;
            }
            let detail_map = detail_map_val.unwrap();
            match *info_type {
                "n" => {
                    let mut n_value: String = String::new();

                    let name_parts = ["family-name", "given-name",
                    "additional-name", "honorific-prefix", "honorific-suffix"];
                    for part in name_parts.iter() {
                        if detail_map.contains_key(*part) {
                            n_value += format!("{};", detail_map.get(*part).unwrap()).as_str();
                        }
                    }
                    n_value.pop();

                    result += format!("{}:{}\n", info_type.to_ascii_uppercase(), n_value).as_str();
                },
                "org" => {
                    let mut org_value: String = String::new();

                    let org_parts = ["organization-name", "organization-unit"];
                    for part in org_parts.iter() {
                        if detail_map.contains_key(*part) {
                            org_value += format!("{};", detail_map.get(*part).unwrap()).as_str();
                        }
                    }
                    org_value.pop();

                    result += format!("{}:{}\n", info_type.to_ascii_uppercase(), org_value).as_str();
                },
                "tel" => {
                    let mut tel_value: String = String::new();

                    let tel_parts = ["value"];
                    for part in tel_parts.iter() {
                        if detail_map.contains_key(*part) {
                            tel_value += format!("{};", detail_map.get(*part).unwrap()).as_str();
                        }
                    }
                    tel_value.pop();

                    result += format!("{}:{}\n", info_type.to_ascii_uppercase(), tel_value).as_str();
                },
                "adr" => {
                    let mut adr_value: String = String::new();

                    let adr_parts = ["street-address", "locality", "region", "postal-code",
                    "country-name", "post-office-box", "extended-address"];
                    for part in adr_parts.iter() {
                        if detail_map.contains_key(*part) {
                            adr_value += format!("{};", detail_map.get(*part).unwrap()).as_str();
                        }
                    }
                    adr_value.pop();

                    result += format!("{}:{}\n", info_type.to_ascii_uppercase(), adr_value).as_str();
                },
                _ => {},
            }
        }
        result += "END:VCARD";
        if start_vcard {
            return result;
        } else {
            return "".to_string();
        }
    }

    pub fn parse_json(node: &Node) -> String {
        // TODO Write the logic for JSON Parsing
        return "".to_string();
    }
}
<|endoftext|>


<commit_before>//!
//! The API for communicating with devices.
//!
//! This API is provided as Traits to be implemented:
//!
//! - by the low-level layers of the FoxBox, including the adapters;
//! - by test suites and tools that need to simulate connected devices.
//!
//! In turn, this API is used to implement:
//!
//! - the public-facing REST and WebSocket API;
//! - the rules API (ThinkerBell).
//!
//!

use devices::*;
use selector::*;
use values::Value;
use util::Id;

/// An error produced by one of the APIs in this module.
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum Error {
    /// There is no such node connected to the Foxbox, even indirectly.
    NoSuchNode(Id<NodeId>),

    /// There is no such input service connected to the Foxbox, even indirectly.
    NoSuchGet(Id<Get>),

    /// There is no such output service connected to the Foxbox, even indirectly.
    NoSuchSet(Id<Set>),

    /// Attempting to set a value with the wrong type
    TypeError,
}

/// An event during watching.
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum WatchEvent {
    /// A new value is available.
    Value {
        /// The service that sent the value.
        from: Id<Get>,

        /// The actual value.
        value: Value
    },

    /// The set of devices being watched has changed, typically either
    /// because a tag was edited or because a device was
    /// removed. Payload is the id of the device that was removed.
    GetRemoved(Id<Get>),

    /// The set of devices being watched has changed, typically either
    /// because a tag was edited or because a device was
    /// added. Payload is the id of the device that was added.
    GetAdded(Id<Get>),
}

/// A handle to the public API.
pub trait API: Send {
    /// Get the metadata on nodes matching some conditions.
    ///
    /// A call to `API::get_nodes(vec![req1, req2, ...])` will return
    /// the metadata on all nodes matching _either_ `req1` or `req2`
    /// or ...
    ///
    /// # REST API
    ///
    /// `GET /api/v1/nodes`
    ///
    /// ## Gets
    ///
    /// Any JSON that can be deserialized to a `Vec<NodeSelector>`. See
    /// the implementation of `NodeSelector` for details.
    ///
    /// ### Example
    ///
    /// Selector all doors in the entrance (tags `door`, `entrance`)
    /// that support output service `OpenClose`
    ///
    /// ```json
    /// [{
    ///   "tags": ["entrance", "door"],
    ///   "inputs": [
    ///     {
    ///       "kind": {
    ///         "Exactly": {
    ///           "OpenClose": []
    ///         }
    ///       }
    ///     }
    ///   ]
    /// }]
    /// ```
    ///
    ///
    /// ## Errors
    ///
    /// In case of syntax error, Error 400, accompanied with a
    /// somewhat human-readable JSON string detailing the error.
    ///
    /// ## Success
    ///
    /// A JSON representing an array of `Node`. See the implementation
    /// of `Node` for details.
    ///
    /// ### Example
    ///
    /// ```json
    /// [{
    ///   "tags": ["entrance", "door", "somevendor"],
    ///   "id: "some-node-id",
    ///   "inputs": [],
    ///   "outputs": [
    ///     "tags": [...],
    ///     "id": "some-service-id",
    ///     "node": "some-node-id",
    ///     "last_seen": "some-date",
    ///     "mechanism": {
    ///       "Set":  {
    ///         "kind": {
    ///           "OnOff": []
    ///         },
    ///         "push": [5000],
    ///         "updated": "some-date",
    ///       }
    ///     }
    ///   ]
    /// }]
    /// ```
    fn get_nodes(&self, &Vec<NodeSelector>) -> Vec<Node>;

    /// Label a set of nodes with a set of tags.
    ///
    /// A call to `API::put_node_tag(vec![req1, req2, ...], vec![tag1,
    /// ...])` will label all the nodes matching _either_ `req1` or
    /// `req2` or ... with `tag1`, ... and return the number of nodes
    /// matching any of the selectors.
    ///
    /// Some of the nodes may already be labelled with `tag1`, or
    /// `tag2`, ... They will not change state. They are counted in
    /// the resulting `usize` nevertheless.
    ///
    /// Note that this call is _not live_. In other words, if nodes
    /// are added after the call, they will not be affected.
    ///
    /// # REST API
    ///
    /// `POST /api/v1/nodes/tag`
    ///
    /// ## Gets
    ///
    /// Any JSON that can be deserialized to
    ///
    /// ```ignore
    /// {
    ///   set: Vec<NodeSelector>,
    ///   tags: Vec<String>,
    /// }
    /// ```
    ///
    /// ## Errors
    ///
    /// In case of syntax error, Error 400, accompanied with a
    /// somewhat human-readable JSON string detailing the error.
    ///
    /// ## Success
    ///
    /// A JSON string representing a number.
    fn put_node_tag(&self, set: &Vec<NodeSelector>, tags: &Vec<String>) -> usize;

    /// Remove a set of tags from a set of nodes.
    ///
    /// A call to `API::delete_node_tag(vec![req1, req2, ...], vec![tag1,
    /// ...])` will remove from all the nodes matching _either_ `req1` or
    /// `req2` or ... all of the tags `tag1`, ... and return the number of nodes
    /// matching any of the selectors.
    ///
    /// Some of the nodes may not be labelled with `tag1`, or `tag2`,
    /// ... They will not change state. They are counted in the
    /// resulting `usize` nevertheless.
    ///
    /// Note that this call is _not live_. In other words, if nodes
    /// are added after the call, they will not be affected.
    ///
    /// # REST API
    ///
    /// `DELETE /api/v1/nodes/tag`
    ///
    /// ## Gets
    ///
    /// Any JSON that can be deserialized to
    ///
    /// ```ignore
    /// {
    ///   set: Vec<NodeSelector>,
    ///   tags: Vec<String>,
    /// }
    /// ```
    ///
    /// ## Errors
    ///
    /// In case of syntax error, Error 400, accompanied with a
    /// somewhat human-readable JSON string detailing the error.
    ///
    /// ## Success
    ///
    /// A JSON representing a number.
    fn delete_node_tag(&self, set: &Vec<NodeSelector>, tags: String) -> usize;

    /// Get a list of inputs matching some conditions
    ///
    /// # REST API
    ///
    /// `GET /api/v1/services`
    fn get_input_services(&self, &Vec<GetSelector>) -> Vec<Channel<Get>>;
    fn get_output_services(&self, &Vec<SetSelector>) -> Vec<Channel<Set>>;

    /// Label a set of services with a set of tags.
    ///
    /// A call to `API::put_{input, output}_tag(vec![req1, req2, ...], vec![tag1,
    /// ...])` will label all the services matching _either_ `req1` or
    /// `req2` or ... with `tag1`, ... and return the number of services
    /// matching any of the selectors.
    ///
    /// Some of the services may already be labelled with `tag1`, or
    /// `tag2`, ... They will not change state. They are counted in
    /// the resulting `usize` nevertheless.
    ///
    /// Note that this call is _not live_. In other words, if services
    /// are added after the call, they will not be affected.
    ///
    /// # REST API
    ///
    /// `POST /api/v1/services/tag`
    ///
    /// ## Gets
    ///
    /// Any JSON that can be deserialized to
    ///
    /// ```ignore
    /// {
    ///   set: Vec<GetSelector>,
    ///   tags: Vec<String>,
    /// }
    /// ```
    /// or
    /// ```ignore
    /// {
    ///   set: Vec<SetSelector>,
    ///   tags: Vec<String>,
    /// }
    /// ```
    ///
    /// ## Errors
    ///
    /// In case of syntax error, Error 400, accompanied with a
    /// somewhat human-readable JSON string detailing the error.
    ///
    /// ## Success
    ///
    /// A JSON representing a number.
    fn put_input_tag(&self, &Vec<GetSelector>, &Vec<String>) -> usize;
    fn put_output_tag(&self, &Vec<SetSelector>, &Vec<String>) -> usize;

    /// Remove a set of tags from a set of services.
    ///
    /// A call to `API::delete_{input, output}_tag(vec![req1, req2, ...], vec![tag1,
    /// ...])` will remove from all the services matching _either_ `req1` or
    /// `req2` or ... all of the tags `tag1`, ... and return the number of services
    /// matching any of the selectors.
    ///
    /// Some of the services may not be labelled with `tag1`, or `tag2`,
    /// ... They will not change state. They are counted in the
    /// resulting `usize` nevertheless.
    ///
    /// Note that this call is _not live_. In other words, if services
    /// are added after the call, they will not be affected.
    ///
    /// # REST API
    ///
    /// `DELETE /api/v1/services/tag`
    ///
    /// ## Gets
    ///
    /// Any JSON that can be deserialized to
    ///
    /// ```ignore
    /// {
    ///   set: Vec<GetSelector>,
    ///   tags: Vec<String>,
    /// }
    /// ```
    /// or
    /// ```ignore
    /// {
    ///   set: Vec<SetSelector>,
    ///   tags: Vec<String>,
    /// }
    /// ```
    ///
    /// ## Errors
    ///
    /// In case of syntax error, Error 400, accompanied with a
    /// somewhat human-readable JSON string detailing the error.
    ///
    /// ## Success
    ///
    /// A JSON representing a number.
    fn delete_input_tag(&self, &Vec<GetSelector>, &Vec<String>) -> usize;
    fn delete_output_tag(&self, &Vec<GetSelector>, &Vec<String>) -> usize;

    /// Read the latest value from a set of services
    ///
    /// # REST API
    ///
    /// `GET /api/v1/services/value`
    fn get_service_value(&self, &Vec<GetSelector>) -> Vec<(Id<Get>, Result<Value, Error>)>;

    /// Send one value to a set of services
    ///
    /// # REST API
    ///
    /// `POST /api/v1/services/value`
    fn put_service_value(&self, &Vec<SetSelector>, Value) -> Vec<(Id<Set>, Result<(), Error>)>;

    /// Watch for any change
    ///
    /// # WebSocket API
    ///
    /// `/api/v1/services/watch`
    fn register_service_watch(&self, Vec<WatchOptions>, cb: Box<Fn(WatchEvent) + Send + 'static>) -> Self::WatchGuard;

    /// A value that causes a disconnection once it is dropped.
    type WatchGuard;
}

/// Options for watching changes in one or more services.
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct WatchOptions {
    /// The set of inputs to watch. Note that the actual inputs in the
    /// set may change over time.
    pub source: GetSelector,

    /// If `true`, watch as new values become available.
    pub should_watch_values: bool,

    /// If `true`, watch as nodes are connected/disconnected.
    pub should_watch_topology: bool,

    /// Make sure that we can't instantiate from another crate.
    #[serde(default, skip_serializing)]
    private: (),
}

impl WatchOptions {
    pub fn new() -> Self {
        WatchOptions {
            source: GetSelector::new(),
            should_watch_values: false,
            should_watch_topology: false,
            private: (),
        }
    }

    /// Restrict to input services in a given set.
    ///
    /// Also note that the actual input services that are part of the
    /// set may change with time, for instance if devices are added
    /// ore removed.  The selector _is live_, i.e. the service watch
    /// will continue watching any input services that match `req`.
    pub fn with_inputs(self, req: GetSelector) -> Self {
        WatchOptions {
            source: self.source.and(req),
            ..self
        }
    }

    pub fn with_watch_values(self, should: bool) -> Self {
        WatchOptions {
            should_watch_values: should,
            ..self
        }
    }

    pub fn with_watch_topology(self, should: bool) -> Self {
        WatchOptions {
            should_watch_topology: should,
            ..self
        }
    }
}
<commit_msg>[fxbox/taxonomy] Renaming Service => Channel, part 2<commit_after>//!
//! The API for communicating with devices.
//!
//! This API is provided as Traits to be implemented:
//!
//! - by the low-level layers of the FoxBox, including the adapters;
//! - by test suites and tools that need to simulate connected devices.
//!
//! In turn, this API is used to implement:
//!
//! - the public-facing REST and WebSocket API;
//! - the rules API (ThinkerBell).
//!
//!

use devices::*;
use selector::*;
use values::Value;
use util::Id;

/// An error produced by one of the APIs in this module.
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum Error {
    /// There is no such node connected to the Foxbox, even indirectly.
    NoSuchNode(Id<NodeId>),

    /// There is no such input channel connected to the Foxbox, even indirectly.
    NoSuchGet(Id<Get>),

    /// There is no such output channel connected to the Foxbox, even indirectly.
    NoSuchSet(Id<Set>),

    /// Attempting to set a value with the wrong type
    TypeError,
}

/// An event during watching.
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum WatchEvent {
    /// A new value is available.
    Value {
        /// The channel that sent the value.
        from: Id<Get>,

        /// The actual value.
        value: Value
    },

    /// The set of devices being watched has changed, typically either
    /// because a tag was edited or because a device was
    /// removed. Payload is the id of the device that was removed.
    GetRemoved(Id<Get>),

    /// The set of devices being watched has changed, typically either
    /// because a tag was edited or because a device was
    /// added. Payload is the id of the device that was added.
    GetAdded(Id<Get>),
}

/// A handle to the public API.
pub trait API: Send {
    /// Get the metadata on nodes matching some conditions.
    ///
    /// A call to `API::get_nodes(vec![req1, req2, ...])` will return
    /// the metadata on all nodes matching _either_ `req1` or `req2`
    /// or ...
    ///
    /// # REST API
    ///
    /// `GET /api/v1/nodes`
    ///
    /// ## Gets
    ///
    /// Any JSON that can be deserialized to a `Vec<NodeSelector>`. See
    /// the implementation of `NodeSelector` for details.
    ///
    /// ### Example
    ///
    /// Selector all doors in the entrance (tags `door`, `entrance`)
    /// that support output channel `OpenClose`
    ///
    /// ```json
    /// [{
    ///   "tags": ["entrance", "door"],
    ///   "inputs": [
    ///     {
    ///       "kind": {
    ///         "Exactly": {
    ///           "OpenClose": []
    ///         }
    ///       }
    ///     }
    ///   ]
    /// }]
    /// ```
    ///
    ///
    /// ## Errors
    ///
    /// In case of syntax error, Error 400, accompanied with a
    /// somewhat human-readable JSON string detailing the error.
    ///
    /// ## Success
    ///
    /// A JSON representing an array of `Node`. See the implementation
    /// of `Node` for details.
    ///
    /// ### Example
    ///
    /// ```json
    /// [{
    ///   "tags": ["entrance", "door", "somevendor"],
    ///   "id: "some-node-id",
    ///   "inputs": [],
    ///   "outputs": [
    ///     "tags": [...],
    ///     "id": "some-channel-id",
    ///     "node": "some-node-id",
    ///     "last_seen": "some-date",
    ///     "mechanism": {
    ///       "Set":  {
    ///         "kind": {
    ///           "OnOff": []
    ///         },
    ///         "push": [5000],
    ///         "updated": "some-date",
    ///       }
    ///     }
    ///   ]
    /// }]
    /// ```
    fn get_nodes(&self, &Vec<NodeSelector>) -> Vec<Node>;

    /// Label a set of nodes with a set of tags.
    ///
    /// A call to `API::put_node_tag(vec![req1, req2, ...], vec![tag1,
    /// ...])` will label all the nodes matching _either_ `req1` or
    /// `req2` or ... with `tag1`, ... and return the number of nodes
    /// matching any of the selectors.
    ///
    /// Some of the nodes may already be labelled with `tag1`, or
    /// `tag2`, ... They will not change state. They are counted in
    /// the resulting `usize` nevertheless.
    ///
    /// Note that this call is _not live_. In other words, if nodes
    /// are added after the call, they will not be affected.
    ///
    /// # REST API
    ///
    /// `POST /api/v1/nodes/tag`
    ///
    /// ## Gets
    ///
    /// Any JSON that can be deserialized to
    ///
    /// ```ignore
    /// {
    ///   set: Vec<NodeSelector>,
    ///   tags: Vec<String>,
    /// }
    /// ```
    ///
    /// ## Errors
    ///
    /// In case of syntax error, Error 400, accompanied with a
    /// somewhat human-readable JSON string detailing the error.
    ///
    /// ## Success
    ///
    /// A JSON string representing a number.
    fn put_node_tag(&self, set: &Vec<NodeSelector>, tags: &Vec<String>) -> usize;

    /// Remove a set of tags from a set of nodes.
    ///
    /// A call to `API::delete_node_tag(vec![req1, req2, ...], vec![tag1,
    /// ...])` will remove from all the nodes matching _either_ `req1` or
    /// `req2` or ... all of the tags `tag1`, ... and return the number of nodes
    /// matching any of the selectors.
    ///
    /// Some of the nodes may not be labelled with `tag1`, or `tag2`,
    /// ... They will not change state. They are counted in the
    /// resulting `usize` nevertheless.
    ///
    /// Note that this call is _not live_. In other words, if nodes
    /// are added after the call, they will not be affected.
    ///
    /// # REST API
    ///
    /// `DELETE /api/v1/nodes/tag`
    ///
    /// ## Gets
    ///
    /// Any JSON that can be deserialized to
    ///
    /// ```ignore
    /// {
    ///   set: Vec<NodeSelector>,
    ///   tags: Vec<String>,
    /// }
    /// ```
    ///
    /// ## Errors
    ///
    /// In case of syntax error, Error 400, accompanied with a
    /// somewhat human-readable JSON string detailing the error.
    ///
    /// ## Success
    ///
    /// A JSON representing a number.
    fn delete_node_tag(&self, set: &Vec<NodeSelector>, tags: String) -> usize;

    /// Get a list of inputs matching some conditions
    ///
    /// # REST API
    ///
    /// `GET /api/v1/channels`
    fn get_input_channels(&self, &Vec<GetSelector>) -> Vec<Channel<Get>>;
    fn get_output_channels(&self, &Vec<SetSelector>) -> Vec<Channel<Set>>;

    /// Label a set of channels with a set of tags.
    ///
    /// A call to `API::put_{input, output}_tag(vec![req1, req2, ...], vec![tag1,
    /// ...])` will label all the channels matching _either_ `req1` or
    /// `req2` or ... with `tag1`, ... and return the number of channels
    /// matching any of the selectors.
    ///
    /// Some of the channels may already be labelled with `tag1`, or
    /// `tag2`, ... They will not change state. They are counted in
    /// the resulting `usize` nevertheless.
    ///
    /// Note that this call is _not live_. In other words, if channels
    /// are added after the call, they will not be affected.
    ///
    /// # REST API
    ///
    /// `POST /api/v1/channels/tag`
    ///
    /// ## Gets
    ///
    /// Any JSON that can be deserialized to
    ///
    /// ```ignore
    /// {
    ///   set: Vec<GetSelector>,
    ///   tags: Vec<String>,
    /// }
    /// ```
    /// or
    /// ```ignore
    /// {
    ///   set: Vec<SetSelector>,
    ///   tags: Vec<String>,
    /// }
    /// ```
    ///
    /// ## Errors
    ///
    /// In case of syntax error, Error 400, accompanied with a
    /// somewhat human-readable JSON string detailing the error.
    ///
    /// ## Success
    ///
    /// A JSON representing a number.
    fn put_input_tag(&self, &Vec<GetSelector>, &Vec<String>) -> usize;
    fn put_output_tag(&self, &Vec<SetSelector>, &Vec<String>) -> usize;

    /// Remove a set of tags from a set of channels.
    ///
    /// A call to `API::delete_{input, output}_tag(vec![req1, req2, ...], vec![tag1,
    /// ...])` will remove from all the channels matching _either_ `req1` or
    /// `req2` or ... all of the tags `tag1`, ... and return the number of channels
    /// matching any of the selectors.
    ///
    /// Some of the channels may not be labelled with `tag1`, or `tag2`,
    /// ... They will not change state. They are counted in the
    /// resulting `usize` nevertheless.
    ///
    /// Note that this call is _not live_. In other words, if channels
    /// are added after the call, they will not be affected.
    ///
    /// # REST API
    ///
    /// `DELETE /api/v1/channels/tag`
    ///
    /// ## Gets
    ///
    /// Any JSON that can be deserialized to
    ///
    /// ```ignore
    /// {
    ///   set: Vec<GetSelector>,
    ///   tags: Vec<String>,
    /// }
    /// ```
    /// or
    /// ```ignore
    /// {
    ///   set: Vec<SetSelector>,
    ///   tags: Vec<String>,
    /// }
    /// ```
    ///
    /// ## Errors
    ///
    /// In case of syntax error, Error 400, accompanied with a
    /// somewhat human-readable JSON string detailing the error.
    ///
    /// ## Success
    ///
    /// A JSON representing a number.
    fn delete_input_tag(&self, &Vec<GetSelector>, &Vec<String>) -> usize;
    fn delete_output_tag(&self, &Vec<GetSelector>, &Vec<String>) -> usize;

    /// Read the latest value from a set of channels
    ///
    /// # REST API
    ///
    /// `GET /api/v1/channels/value`
    fn get_channel_value(&self, &Vec<GetSelector>) -> Vec<(Id<Get>, Result<Value, Error>)>;

    /// Send one value to a set of channels
    ///
    /// # REST API
    ///
    /// `POST /api/v1/channels/value`
    fn put_channel_value(&self, &Vec<SetSelector>, Value) -> Vec<(Id<Set>, Result<(), Error>)>;

    /// Watch for any change
    ///
    /// # WebSocket API
    ///
    /// `/api/v1/channels/watch`
    fn register_channel_watch(&self, Vec<WatchOptions>, cb: Box<Fn(WatchEvent) + Send + 'static>) -> Self::WatchGuard;

    /// A value that causes a disconnection once it is dropped.
    type WatchGuard;
}

/// Options for watching changes in one or more channels.
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct WatchOptions {
    /// The set of inputs to watch. Note that the actual inputs in the
    /// set may change over time.
    pub source: GetSelector,

    /// If `true`, watch as new values become available.
    pub should_watch_values: bool,

    /// If `true`, watch as nodes are connected/disconnected.
    pub should_watch_topology: bool,

    /// Make sure that we can't instantiate from another crate.
    #[serde(default, skip_serializing)]
    private: (),
}

impl WatchOptions {
    pub fn new() -> Self {
        WatchOptions {
            source: GetSelector::new(),
            should_watch_values: false,
            should_watch_topology: false,
            private: (),
        }
    }

    /// Restrict to input channels in a given set.
    ///
    /// Also note that the actual input channels that are part of the
    /// set may change with time, for instance if devices are added
    /// ore removed.  The selector _is live_, i.e. the channel watch
    /// will continue watching any input channels that match `req`.
    pub fn with_inputs(self, req: GetSelector) -> Self {
        WatchOptions {
            source: self.source.and(req),
            ..self
        }
    }

    pub fn with_watch_values(self, should: bool) -> Self {
        WatchOptions {
            should_watch_values: should,
            ..self
        }
    }

    pub fn with_watch_topology(self, should: bool) -> Self {
        WatchOptions {
            should_watch_topology: should,
            ..self
        }
    }
}
<|endoftext|>


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

use std::cell::RefCell;
use std::io::{Read, Write};

type Handle = u64;

// Keep in sync with /oak/server/oak_node.h.
pub const LOGGING_CHANNEL_HANDLE: Handle = 1;
pub const GRPC_CHANNEL_HANDLE: Handle = 2;
pub const GRPC_METHOD_NAME_CHANNEL_HANDLE: Handle = 3;

// TODO: Implement panic handler.

mod wasm {
    // See https://rustwasm.github.io/book/reference/js-ffi.html
    #[link(wasm_import_module = "oak")]
    extern "C" {
        pub fn channel_read(handle: u64, buf: *mut u8, size: usize) -> usize;
        pub fn channel_write(handle: u64, buf: *const u8, size: usize) -> usize;
    }
}

pub struct Channel {
    handle: Handle,
}

impl Channel {
    pub fn new(handle: Handle) -> Channel {
        Channel { handle }
    }
}

pub fn logging_channel() -> impl Write {
    let logging_channel = Channel::new(LOGGING_CHANNEL_HANDLE);
    // Only flush logging channel on newlines.
    std::io::LineWriter::new(logging_channel)
}

impl Read for Channel {
    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
        Ok(unsafe { wasm::channel_read(self.handle, buf.as_mut_ptr(), buf.len()) })
    }
}

impl Write for Channel {
    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
        Ok(unsafe { wasm::channel_write(self.handle, buf.as_ptr(), buf.len()) })
    }
    fn flush(&mut self) -> std::io::Result<()> {
        Ok(())
    }
}

/// Trait encapsulating the operations required for an Oak Node.
pub trait Node {
    fn new() -> Self
    where
        Self: Sized;
    fn invoke(&mut self, grpc_method_name: &str, grpc_channel: &mut Channel);
}

/// No-op implementation of Node, so that we have a placeholder value until the actual one is set
/// via `set_node`.
struct NopNode;

impl Node for NopNode {
    fn new() -> Self {
        NopNode
    }
    fn invoke(&mut self, _grpc_method_name: &str, _grpc_channel: &mut Channel) {}
}

thread_local! {
    static NODE: RefCell<Box<dyn Node>> = RefCell::new(Box::new(NopNode));
}

/// Sets the Oak Node to execute in the current instance.
///
/// This function may only be called once, and only from an exported `oak_initialize` function:
///
/// ```rust
/// struct Node;
///
/// impl oak::Node for Node {
///     fn new() -> Self { Node }
///     fn invoke(&mut self, grpc_method_name: &str, grpc_channel: &mut oak::Channel) { /* ... */ }
/// }
///
/// #[no_mangle]
/// pub extern "C" fn oak_initialize() {
///     oak::set_node::<Node>();
/// }
/// ```
pub fn set_node<T: Node + 'static>() {
    // TODO: Detect multiple invocations.
    NODE.with(|node| {
        *node.borrow_mut() = Box::new(T::new());
    });
}

#[no_mangle]
pub extern "C" fn oak_handle_grpc_call() {
    NODE.with(|node| {
        let mut grpc_method_channel = Channel::new(GRPC_METHOD_NAME_CHANNEL_HANDLE);
        let mut grpc_method_name = String::new();
        grpc_method_channel
            .read_to_string(&mut grpc_method_name)
            .unwrap();
        let mut grpc_channel = Channel::new(GRPC_CHANNEL_HANDLE);
        node.borrow_mut()
            .invoke(&grpc_method_name, &mut grpc_channel);
    });
}
<commit_msg>Use an Option rather than a silent NopNode<commit_after>//
// Copyright 2018 The Project Oak Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

use std::cell::RefCell;
use std::io::{Read, Write};

type Handle = u64;

// Keep in sync with /oak/server/oak_node.h.
pub const LOGGING_CHANNEL_HANDLE: Handle = 1;
pub const GRPC_CHANNEL_HANDLE: Handle = 2;
pub const GRPC_METHOD_NAME_CHANNEL_HANDLE: Handle = 3;

// TODO: Implement panic handler.

mod wasm {
    // See https://rustwasm.github.io/book/reference/js-ffi.html
    #[link(wasm_import_module = "oak")]
    extern "C" {
        pub fn channel_read(handle: u64, buf: *mut u8, size: usize) -> usize;
        pub fn channel_write(handle: u64, buf: *const u8, size: usize) -> usize;
    }
}

pub struct Channel {
    handle: Handle,
}

impl Channel {
    pub fn new(handle: Handle) -> Channel {
        Channel { handle }
    }
}

pub fn logging_channel() -> impl Write {
    let logging_channel = Channel::new(LOGGING_CHANNEL_HANDLE);
    // Only flush logging channel on newlines.
    std::io::LineWriter::new(logging_channel)
}

impl Read for Channel {
    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
        Ok(unsafe { wasm::channel_read(self.handle, buf.as_mut_ptr(), buf.len()) })
    }
}

impl Write for Channel {
    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
        Ok(unsafe { wasm::channel_write(self.handle, buf.as_ptr(), buf.len()) })
    }
    fn flush(&mut self) -> std::io::Result<()> {
        Ok(())
    }
}

/// Trait encapsulating the operations required for an Oak Node.
pub trait Node {
    fn new() -> Self
    where
        Self: Sized;
    fn invoke(&mut self, grpc_method_name: &str, grpc_channel: &mut Channel);
}

thread_local! {
    static NODE: RefCell<Option<Box<dyn Node>>> = RefCell::new(None);
}

/// Sets the Oak Node to execute in the current instance.
///
/// This function may only be called once, and only from an exported `oak_initialize` function:
///
/// ```rust
/// struct Node;
///
/// impl oak::Node for Node {
///     fn new() -> Self { Node }
///     fn invoke(&mut self, grpc_method_name: &str, grpc_channel: &mut oak::Channel) { /* ... */ }
/// }
///
/// #[no_mangle]
/// pub extern "C" fn oak_initialize() {
///     oak::set_node::<Node>();
/// }
/// ```
pub fn set_node<T: Node + 'static>() {
    // TODO: Detect multiple invocations.
    NODE.with(|node| {
        *node.borrow_mut() = Some(Box::new(T::new()));
    });
}

#[no_mangle]
pub extern "C" fn oak_handle_grpc_call() {
    NODE.with(|node| match *node.borrow_mut() {
        Some(ref mut node) => {
            let mut grpc_method_channel = Channel::new(GRPC_METHOD_NAME_CHANNEL_HANDLE);
            let mut grpc_method_name = String::new();
            grpc_method_channel
                .read_to_string(&mut grpc_method_name)
                .unwrap();
            let mut grpc_channel = Channel::new(GRPC_CHANNEL_HANDLE);
            node.invoke(&grpc_method_name, &mut grpc_channel);
        }
        None => {
            writeln!(logging_channel(), "gRPC call with no loaded Node").unwrap();
            panic!("gRPC call with no loaded Node");
        }
    });
}
<|endoftext|>


<commit_before>use ::cairo;
use ::glib_sys;
use ::glib;

use error::*;
use parsers;
use parsers::Parse;
use parsers::{ListLength, ParseError};

use self::glib::translate::*;

/* Keep this in sync with rsvg-private.h:RsvgViewBox */
#[repr(C)]
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct RsvgViewBox {
    pub rect: cairo::Rectangle,
    active:   glib_sys::gboolean
}

#[derive(Debug, Copy, Clone, PartialEq)]
pub struct ViewBox(pub cairo::Rectangle);

impl From<Option<ViewBox>> for RsvgViewBox {
    fn from(v: Option<ViewBox>) -> RsvgViewBox {
        if let Some(vb) = v {
            RsvgViewBox {
                rect: vb.0,
                active: true.to_glib ()
            }
        } else {
            RsvgViewBox {
                rect: cairo::Rectangle { x: 0.0,
                                         y: 0.0,
                                         width: 0.0,
                                         height: 0.0 },
                active: false.to_glib ()
            }
        }
    }
}

impl Parse for ViewBox {
    type Data = ();
    type Err = AttributeError;

    // Parse a viewBox attribute
    // https://www.w3.org/TR/SVG/coords.html#ViewBoxAttribute
    //
    // viewBox: double [,] double [,] double [,] double [,]
    //
    // x, y, w, h
    //
    // Where w and h must be nonnegative.
    fn parse (s: &str, _: ()) -> Result<ViewBox, AttributeError> {
        let v = parsers::number_list (s, ListLength::Exact (4))
            .map_err (|_| ParseError::new ("string does not match 'x [,] y [,] w [,] h'"))?;

        let (x, y, w, h) = (v[0], v[1], v[2], v[3]);

        if w >= 0.0 && h >= 0.0 {
            Ok (ViewBox(cairo::Rectangle { x: x,
                                           y: y,
                                           width: w,
                                           height: h }))
        } else {
            Err (AttributeError::Value ("width and height must not be negative".to_string ()))
        }
    }
}

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

    #[test]
    fn parses_valid_viewboxes () {
        assert_eq! (ViewBox::parse ("  1 2 3 4", ()),
                    Ok (ViewBox (cairo::Rectangle { x: 1.0,
                                                    y: 2.0,
                                                    width: 3.0,
                                                    height: 4.0 })));

        assert_eq! (ViewBox::parse (" -1.5 -2.5e1,34,56e2  ", ()),
                    Ok (ViewBox (cairo::Rectangle { x: -1.5,
                                                    y: -25.0,
                                                    width: 34.0,
                                                    height: 5600.0 })));
    }

    #[test]
    fn parsing_invalid_viewboxes_yields_error () {
        assert! (is_parse_error (&ViewBox::parse ("", ())));

        assert! (is_value_error (&ViewBox::parse (" 1,2,-3,-4 ", ())));

        assert! (is_parse_error (&ViewBox::parse ("qwerasdfzxcv", ())));

        assert! (is_parse_error (&ViewBox::parse (" 1 2 3 4   5", ())));

        assert! (is_parse_error (&ViewBox::parse (" 1 2 foo 3 4", ())));
    }
}
<commit_msg>ViewBox::new() - new constructor<commit_after>use ::cairo;
use ::glib_sys;
use ::glib;

use error::*;
use parsers;
use parsers::Parse;
use parsers::{ListLength, ParseError};

use self::glib::translate::*;

/* Keep this in sync with rsvg-private.h:RsvgViewBox */
#[repr(C)]
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct RsvgViewBox {
    pub rect: cairo::Rectangle,
    active:   glib_sys::gboolean
}

#[derive(Debug, Copy, Clone, PartialEq)]
pub struct ViewBox(pub cairo::Rectangle);

impl ViewBox {
    pub fn new(x: f64, y: f64, w: f64, h: f64) -> ViewBox {
        assert!(w >= 0.0 && h >= 0.0, "width and height must not be negative");

        ViewBox(cairo::Rectangle { x: x, y: y, width: w, height: h })
    }
}

impl From<Option<ViewBox>> for RsvgViewBox {
    fn from(v: Option<ViewBox>) -> RsvgViewBox {
        if let Some(vb) = v {
            RsvgViewBox {
                rect: vb.0,
                active: true.to_glib ()
            }
        } else {
            RsvgViewBox {
                rect: cairo::Rectangle { x: 0.0,
                                         y: 0.0,
                                         width: 0.0,
                                         height: 0.0 },
                active: false.to_glib ()
            }
        }
    }
}

impl Parse for ViewBox {
    type Data = ();
    type Err = AttributeError;

    // Parse a viewBox attribute
    // https://www.w3.org/TR/SVG/coords.html#ViewBoxAttribute
    //
    // viewBox: double [,] double [,] double [,] double [,]
    //
    // x, y, w, h
    //
    // Where w and h must be nonnegative.
    fn parse (s: &str, _: ()) -> Result<ViewBox, AttributeError> {
        let v = parsers::number_list (s, ListLength::Exact (4))
            .map_err (|_| ParseError::new ("string does not match 'x [,] y [,] w [,] h'"))?;

        let (x, y, w, h) = (v[0], v[1], v[2], v[3]);

        if w >= 0.0 && h >= 0.0 {
            Ok (ViewBox(cairo::Rectangle { x: x,
                                           y: y,
                                           width: w,
                                           height: h }))
        } else {
            Err (AttributeError::Value ("width and height must not be negative".to_string ()))
        }
    }
}

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

    #[test]
    fn parses_valid_viewboxes () {
        assert_eq! (ViewBox::parse ("  1 2 3 4", ()),
                    Ok (ViewBox::new(1.0, 2.0, 3.0, 4.0)));

        assert_eq! (ViewBox::parse (" -1.5 -2.5e1,34,56e2  ", ()),
                    Ok (ViewBox::new(-1.5, -25.0, 34.0, 5600.0)));
    }

    #[test]
    fn parsing_invalid_viewboxes_yields_error () {
        assert! (is_parse_error (&ViewBox::parse ("", ())));

        assert! (is_value_error (&ViewBox::parse (" 1,2,-3,-4 ", ())));

        assert! (is_parse_error (&ViewBox::parse ("qwerasdfzxcv", ())));

        assert! (is_parse_error (&ViewBox::parse (" 1 2 3 4   5", ())));

        assert! (is_parse_error (&ViewBox::parse (" 1 2 foo 3 4", ())));
    }
}
<|endoftext|>


<commit_before>//! Wrapper for `std::io::Read`.

use std::fmt;
use std::mem;
use std::io;

macro_rules! impl_read_little_endian_integer {
    ($ty:ident, $name:ident, $size:expr) => {
/// Reads a little-endian value and returns it.
        fn $name(&mut self) -> io::Result<$ty> {
            assert_eq!($size, mem::size_of::<$ty>());

            let mut data: $ty = 0;
            let mut slice = unsafe {
                ::std::slice::from_raw_parts_mut(&mut data as *mut $ty as *mut u8, $size)
            };
            try!(self.read_exact(slice));
            Ok($ty::from_le(data))
        }
    }
}

/// The `ReadLittleEndian` trait allows for reading little-endian primitive type values from a
/// source.
pub trait ReadLittleEndian: io::Read {
    impl_read_little_endian_integer!(u8, read_u8, 1);
    impl_read_little_endian_integer!(u32, read_u32, 4);
    impl_read_little_endian_integer!(u64, read_u64, 8);
    impl_read_little_endian_integer!(i16, read_i16, 2);
    impl_read_little_endian_integer!(i32, read_i32, 4);
    impl_read_little_endian_integer!(i64, read_i64, 8);

    /// Reads a little-endian value and returns it.
    fn read_f32(&mut self) -> io::Result<f32> {
        let integer = try!(self.read_u32());
        let val = unsafe { mem::transmute(integer) };
        Ok(val)
    }

    /// Reads a little-endian value and returns it.
    fn read_f64(&mut self) -> io::Result<f64> {
        let integer = try!(self.read_u64());
        let val = unsafe { mem::transmute(integer) };
        Ok(val)
    }
}

impl<R: io::Read> ReadLittleEndian for R {}

/// Source stream for `BinaryParser`.
pub trait ParserSource: fmt::Debug + io::Read {
    /// Returns the current position from the start of the stream.
    fn position(&self) -> u64;

    /// Skips to the given position.
    ///
    /// # Panics
    /// Panics if a byte at the given position has been already read.
    fn skip_to(&mut self, dest_pos: u64) -> io::Result<()>;
}

/// Reader with position info.
pub struct BasicSource<R> {
    /// Source reader.
    source: R,
    /// Current position from the start of the stream..
    position: u64,
}

impl<R: io::Read> BasicSource<R> {
    /// Creates a new `BasicSource`.
    pub fn new(source: R) -> Self {
        BasicSource {
            source: source,
            position: 0,
        }
    }
}

impl<R: io::Read> io::Read for BasicSource<R> {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        let read_len = try!(self.source.read(buf));
        self.position += read_len as u64;
        Ok(read_len)
    }

    fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
        try!(self.source.read_exact(buf));
        self.position += buf.len() as u64;
        Ok(())
    }
}

impl<R> fmt::Debug for BasicSource<R> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("BasicSource")
            .field("position", &self.position)
            .finish()
    }
}

impl<R: io::Read> ParserSource for BasicSource<R> {
    fn position(&self) -> u64 {
        self.position
    }

    fn skip_to(&mut self, dest_pos: u64) -> io::Result<()> {
        use std::io::Read;

        assert!(dest_pos >= self.position(),
                "Destination position should be after current position");
        const TEMP_BUF_LEN: usize = 256;
        let mut temp_buf = [0u8; TEMP_BUF_LEN];
        let mut rest_len = dest_pos - self.position();
        while rest_len > TEMP_BUF_LEN as u64 {
            try!(self.read_exact(&mut temp_buf));
            rest_len -= TEMP_BUF_LEN as u64;
        }
        try!(self.read_exact(&mut temp_buf[0..rest_len as usize]));

        assert_eq!(self.position(), dest_pos);
        Ok(())
    }
}

/// Reader with position info and seek feature.
///
/// This wrapper doesn't manage offset, i.e. the start of the source stream should be the start of
/// the FBX data.
pub struct SeekableSource<R> {
    /// Source reader.
    source: R,
    /// Current position from the start of the stream..
    position: u64,
}

impl<R: io::Read + io::Seek> SeekableSource<R> {
    /// Creates a new `SeekableSource`.
    pub fn new(source: R) -> Self {
        SeekableSource {
            source: source,
            position: 0,
        }
    }
}

impl<R: io::Read> io::Read for SeekableSource<R> {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        let read_len = try!(self.source.read(buf));
        self.position += read_len as u64;
        Ok(read_len)
    }

    fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
        try!(self.source.read_exact(buf));
        self.position += buf.len() as u64;
        Ok(())
    }
}

impl<R: io::Read + io::Seek> io::Seek for SeekableSource<R> {
    fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
        self.position = try!(self.source.seek(pos));
        Ok(self.position)
    }
}

impl<R> fmt::Debug for SeekableSource<R> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("SeekableSource")
            .field("position", &self.position)
            .finish()
    }
}

impl<R: io::Read + io::Seek> ParserSource for SeekableSource<R> {
    fn position(&self) -> u64 {
        self.position
    }

    fn skip_to(&mut self, dest_pos: u64) -> io::Result<()> {
        use std::io::{Seek, SeekFrom};

        assert!(dest_pos >= self.position(),
                "Destination position should be after current position");
        try!(self.seek(SeekFrom::Start(dest_pos)));

        assert_eq!(self.position(), dest_pos);
        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use std::io::{Cursor, Seek, SeekFrom};
    use super::BasicSource;

    fn do_test_skip_to(buf_size: usize, skip_dest: u64) {
        let mut short_buf = Cursor::new(vec![0; buf_size]);
        let short_count = {
            let mut reader = BasicSource::new(&mut short_buf);
            reader.skip_to(skip_dest).expect("Failed to skip");
            reader.position()
        };
        assert_eq!(short_count, skip_dest);
        assert_eq!(short_count,
                   short_buf.seek(SeekFrom::Current(0)).expect("Failed to seek"));
    }

    #[test]
    fn test_skip_to() {
        do_test_skip_to(0, 0);
        do_test_skip_to(15, 0);
        do_test_skip_to(30, 23);
        do_test_skip_to(512, 401);
        do_test_skip_to(64, 64);
    }
}
<commit_msg>Improve assert messages<commit_after>//! Wrapper for `std::io::Read`.

use std::fmt;
use std::mem;
use std::io;

macro_rules! impl_read_little_endian_integer {
    ($ty:ident, $name:ident, $size:expr) => {
/// Reads a little-endian value and returns it.
        fn $name(&mut self) -> io::Result<$ty> {
            assert_eq!($size, mem::size_of::<$ty>());

            let mut data: $ty = 0;
            let mut slice = unsafe {
                ::std::slice::from_raw_parts_mut(&mut data as *mut $ty as *mut u8, $size)
            };
            try!(self.read_exact(slice));
            Ok($ty::from_le(data))
        }
    }
}

/// The `ReadLittleEndian` trait allows for reading little-endian primitive type values from a
/// source.
pub trait ReadLittleEndian: io::Read {
    impl_read_little_endian_integer!(u8, read_u8, 1);
    impl_read_little_endian_integer!(u32, read_u32, 4);
    impl_read_little_endian_integer!(u64, read_u64, 8);
    impl_read_little_endian_integer!(i16, read_i16, 2);
    impl_read_little_endian_integer!(i32, read_i32, 4);
    impl_read_little_endian_integer!(i64, read_i64, 8);

    /// Reads a little-endian value and returns it.
    fn read_f32(&mut self) -> io::Result<f32> {
        let integer = try!(self.read_u32());
        let val = unsafe { mem::transmute(integer) };
        Ok(val)
    }

    /// Reads a little-endian value and returns it.
    fn read_f64(&mut self) -> io::Result<f64> {
        let integer = try!(self.read_u64());
        let val = unsafe { mem::transmute(integer) };
        Ok(val)
    }
}

impl<R: io::Read> ReadLittleEndian for R {}

/// Source stream for `BinaryParser`.
pub trait ParserSource: fmt::Debug + io::Read {
    /// Returns the current position from the start of the stream.
    fn position(&self) -> u64;

    /// Skips to the given position.
    ///
    /// # Panics
    /// Panics if a byte at the given position has been already read.
    fn skip_to(&mut self, dest_pos: u64) -> io::Result<()>;
}

/// Reader with position info.
pub struct BasicSource<R> {
    /// Source reader.
    source: R,
    /// Current position from the start of the stream..
    position: u64,
}

impl<R: io::Read> BasicSource<R> {
    /// Creates a new `BasicSource`.
    pub fn new(source: R) -> Self {
        BasicSource {
            source: source,
            position: 0,
        }
    }
}

impl<R: io::Read> io::Read for BasicSource<R> {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        let read_len = try!(self.source.read(buf));
        self.position += read_len as u64;
        Ok(read_len)
    }

    fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
        try!(self.source.read_exact(buf));
        self.position += buf.len() as u64;
        Ok(())
    }
}

impl<R> fmt::Debug for BasicSource<R> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("BasicSource")
            .field("position", &self.position)
            .finish()
    }
}

impl<R: io::Read> ParserSource for BasicSource<R> {
    fn position(&self) -> u64 {
        self.position
    }

    fn skip_to(&mut self, dest_pos: u64) -> io::Result<()> {
        use std::io::Read;

        assert!(dest_pos >= self.position(),
                "Destination position should be after current position: dest_pos={}, position={}",
                dest_pos,
                self.position());
        const TEMP_BUF_LEN: usize = 256;
        let mut temp_buf = [0u8; TEMP_BUF_LEN];
        let mut rest_len = dest_pos - self.position();
        while rest_len > TEMP_BUF_LEN as u64 {
            try!(self.read_exact(&mut temp_buf));
            rest_len -= TEMP_BUF_LEN as u64;
        }
        try!(self.read_exact(&mut temp_buf[0..rest_len as usize]));

        assert_eq!(self.position(), dest_pos);
        Ok(())
    }
}

/// Reader with position info and seek feature.
///
/// This wrapper doesn't manage offset, i.e. the start of the source stream should be the start of
/// the FBX data.
pub struct SeekableSource<R> {
    /// Source reader.
    source: R,
    /// Current position from the start of the stream..
    position: u64,
}

impl<R: io::Read + io::Seek> SeekableSource<R> {
    /// Creates a new `SeekableSource`.
    pub fn new(source: R) -> Self {
        SeekableSource {
            source: source,
            position: 0,
        }
    }
}

impl<R: io::Read> io::Read for SeekableSource<R> {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        let read_len = try!(self.source.read(buf));
        self.position += read_len as u64;
        Ok(read_len)
    }

    fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
        try!(self.source.read_exact(buf));
        self.position += buf.len() as u64;
        Ok(())
    }
}

impl<R: io::Read + io::Seek> io::Seek for SeekableSource<R> {
    fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
        self.position = try!(self.source.seek(pos));
        Ok(self.position)
    }
}

impl<R> fmt::Debug for SeekableSource<R> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("SeekableSource")
            .field("position", &self.position)
            .finish()
    }
}

impl<R: io::Read + io::Seek> ParserSource for SeekableSource<R> {
    fn position(&self) -> u64 {
        self.position
    }

    fn skip_to(&mut self, dest_pos: u64) -> io::Result<()> {
        use std::io::{Seek, SeekFrom};

        assert!(dest_pos >= self.position(),
                "Destination position should be after current position: dest_pos={}, position={}",
                dest_pos,
                self.position());
        try!(self.seek(SeekFrom::Start(dest_pos)));

        assert_eq!(self.position(), dest_pos);
        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use std::io::{Cursor, Seek, SeekFrom};
    use super::BasicSource;

    fn do_test_skip_to(buf_size: usize, skip_dest: u64) {
        let mut short_buf = Cursor::new(vec![0; buf_size]);
        let short_count = {
            let mut reader = BasicSource::new(&mut short_buf);
            reader.skip_to(skip_dest).expect("Failed to skip");
            reader.position()
        };
        assert_eq!(short_count, skip_dest);
        assert_eq!(short_count,
                   short_buf.seek(SeekFrom::Current(0)).expect("Failed to seek"));
    }

    #[test]
    fn test_skip_to() {
        do_test_skip_to(0, 0);
        do_test_skip_to(15, 0);
        do_test_skip_to(30, 23);
        do_test_skip_to(512, 401);
        do_test_skip_to(64, 64);
    }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;

fn changeCoin(value: i32, coinL: &[i32], result: &mut HashMap<i32, i32>) -> i32 {
    if coinL.contains(&value) {
        return 1;
    }
    if let Some(a) = result.get(&value) {
        return *a;
    }

    let mut tempresult: Vec<i32> = Vec::new();
    for c in &coinL {
        if *c <= value {
            tempResult.push(1 + changeCoin(value - 1, coinL, result));
        }
    }

    let mut tempmin = tempresult[0];
    for tt in &tempresult {
        if *tt <= tempmin {
            tempmin = *tt;
        }
    }

    result.insert(value, tempmin);
    return tempmin;
}

fn main() {
    let coinList = [1, 5, 10, 25];
    let mut resultM: HashMap<i32, i32>;

    println!("{}", changeCoin(16));
}
<commit_msg>has some bugs<commit_after>use std::collections::HashMap;

fn changeCoin(value: i32, coinL: &[i32], result: &mut HashMap<i32, i32>) -> i32 {
    if coinL.contains(&value) {
        return 1;
    }
    if let Some(a) = result.get(&value) {
        return *a;
    }

    let mut tempresult: Vec<i32> = Vec::new();
    for c in coinL {
        if *c <= value {
            tempresult.push(1 + changeCoin(value - 1, coinL, result));
        }
    }

    let mut tempmin = tempresult[0];
    for tt in &tempresult {
        if *tt < tempmin {
            tempmin = *tt;
        }
    }

    result.insert(value, tempmin);
    println!("{}=>{}, {:?}", value, tempmin, result);
    return tempmin;
}

fn main() {
    let coinList = [1, 5, 12, 25];
    let mut resultM: HashMap<i32, i32> = HashMap::new();

    println!("{}", changeCoin(16, &coinList, &mut resultM));
}
<|endoftext|>