<commit_before>extern crate conduit;
extern crate libc;
extern crate semver;
extern crate civet_sys as ffi;

use std::collections::HashMap;
use std::io::prelude::*;
use std::io::{self, BufWriter};
use std::net::{SocketAddr, IpAddr, Ipv4Addr};
use std::str::FromStr;

use conduit::{Handler, Extensions, TypeMap, Method, Scheme, Host};

use raw::{RequestInfo,Header};
use raw::{get_header,get_headers,get_request_info};
use status::{ToStatusCode};

pub use config::Config;

mod raw;
mod config;
pub mod status;

pub struct Connection<'a> {
    request: CivetRequest<'a>,
    written: bool,
}

pub struct CivetRequest<'a> {
    conn: &'a raw::Connection,
    request_info: RequestInfo<'a>,
    headers: Headers<'a>,
    extensions: Extensions
}

fn ver(major: u64, minor: u64) -> semver::Version {
    semver::Version {
        major: major,
        minor: minor,
        patch: 0,
        pre: vec!(),
        build: vec!()
    }
}

impl<'a> conduit::Request for CivetRequest<'a> {
    fn http_version(&self) -> semver::Version {
        let version = self.request_info.http_version().unwrap();
        match version {
            "1.0" => ver(1, 0),
            "1.1" => ver(1, 1),
            _ => ver(1, 1)
        }
    }

    fn conduit_version(&self) -> semver::Version {
        ver(0, 1)
    }

    fn method(&self) -> Method {
        match self.request_info.method().unwrap() {
            "HEAD" => Method::Head,
            "GET" => Method::Get,
            "POST" => Method::Post,
            "PUT" => Method::Put,
            "DELETE" => Method::Delete,
            "PATCH" => Method::Patch,
            "PURGE" => Method::Purge,
            "CONNECT" => Method::Connect,
            "OPTIONS" => Method::Options,
            "TRACE" => Method::Trace,
            other @ _ => panic!("Civet does not support {} requests", other)
        }
    }

    fn scheme(&self) -> Scheme {
        if self.request_info.is_ssl() {
            Scheme::Https
        } else {
            Scheme::Http
        }
    }

    fn host(&self) -> Host {
        Host::Name(get_header(self.conn, "Host").unwrap())
    }

    fn virtual_root(&self) -> Option<&str> {
        None
    }

    fn path(&self) -> &str {
        self.request_info.url().unwrap()
    }

    fn query_string(&self) -> Option<&str> {
        self.request_info.query_string()
    }

    fn remote_addr(&self) -> SocketAddr {
        let ip = self.request_info.remote_addr();
        let ip = IpAddr::from_str(ip).unwrap_or_else(|_| Ipv4Addr::new(0, 0, 0, 0).into());
        SocketAddr::new(ip, self.request_info.remote_port())
    }

    fn content_length(&self) -> Option<u64> {
        get_header(self.conn, "Content-Length").and_then(|s| s.parse().ok())
    }

    fn headers(&self) -> &conduit::Headers { &self.headers }

    fn body(&mut self) -> &mut Read { self }

    fn extensions(&self) -> &Extensions { &self.extensions }

    fn mut_extensions(&mut self) -> &mut Extensions {
        &mut self.extensions
    }
}

pub fn response<S: ToStatusCode, R: Read + Send + 'static>(status: S,
    headers: HashMap<String, Vec<String>>, body: R) -> conduit::Response
{
    conduit::Response {
        status: status.to_status().ok().unwrap().to_code(),
        headers: headers,
        body: Box::new(body),
    }
}

impl<'a> Connection<'a> {
    fn new(conn: &raw::Connection) -> Result<Connection, String> {
        match request_info(conn) {
            Ok(info) => {
                let request = CivetRequest {
                    conn: conn,
                    request_info: info,
                    headers: Headers { conn: conn },
                    extensions: TypeMap::new()
                };

                Ok(Connection {
                    request: request,
                    written: false,
                })
            },
            Err(err) => Err(err)
        }
    }

}

impl<'a> Write for Connection<'a> {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.written = true;
        match raw::write(self.request.conn, buf) {
            n if n < 0 => Err(io::Error::new(io::ErrorKind::Other,
                                             &format!("write error ({})", n)[..])),
            n => Ok(n as usize)
        }
    }
    fn flush(&mut self) -> io::Result<()> { Ok(()) }
}

impl<'a> Read for CivetRequest<'a> {
    fn read(&mut self, buf: &mut[u8]) -> io::Result<usize> {
        match raw::read(self.conn, buf) {
            n if n < 0 => Err(io::Error::new(io::ErrorKind::Other,
                                             &format!("read error ({})", n)[..])),
            n => Ok(n as usize)
        }
    }
}

impl<'a> Drop for Connection<'a> {
    fn drop(&mut self) {
        if !self.written {
            let _ = writeln!(self, "HTTP/1.1 500 Internal Server Error");
        }
    }
}

pub struct Headers<'a> {
    conn: &'a raw::Connection
}

impl<'a> conduit::Headers for Headers<'a> {
    fn find(&self, string: &str) -> Option<Vec<&str>> {
        get_header(self.conn, string).map(|s| vec!(s))
    }

    fn has(&self, string: &str) -> bool {
        get_header(self.conn, string).is_some()
    }

    fn all(&self) -> Vec<(&str, Vec<&str>)> {
        HeaderIterator::new(self.conn).collect()
    }
}

pub struct HeaderIterator<'a> {
    headers: Vec<Header<'a>>,
    position: usize
}

impl<'a> HeaderIterator<'a> {
    fn new<'b>(conn: &'b raw::Connection) -> HeaderIterator<'b> {
        HeaderIterator { headers: get_headers(conn), position: 0 }
    }
}

impl<'a> Iterator for HeaderIterator<'a> {
    type Item = (&'a str, Vec<&'a str>);
    fn next(&mut self) -> Option<(&'a str, Vec<&'a str>)> {
        let pos = self.position;
        let headers = &self.headers;

        if self.headers.len() <= pos {
            None
        } else {
            let header = &headers[pos];
            self.position += 1;
            header.name().map(|name| (name, vec!(header.value().unwrap())))
        }
    }
}

pub struct Server(raw::Server<Box<Handler + 'static + Sync>>);

impl Server {
    pub fn start<H: Handler + 'static + Sync>(options: Config, handler: H)
        -> io::Result<Server>
    {
        fn internal_handler(conn: &mut raw::Connection,
                            handler: &Box<Handler + 'static + Sync>)
                            -> Result<(), ()> {
            let mut connection = Connection::new(conn).unwrap();
            let response = handler.call(&mut connection.request);
            let mut writer = BufWriter::new(connection);

            fn err<W: Write>(writer: &mut W) {
                let _ = writeln!(writer, "HTTP/1.1 500 Internal Server Error");
            }

            let conduit::Response { status, headers, mut body } = match response {
                Ok(r) => r,
                Err(_) => return Err(err(&mut writer)),
            };
            let (code, string) = status;
            try!(write!(&mut writer, "HTTP/1.1 {} {}\r\n", code, string).map_err(|_| ()));

            for (key, value) in headers.iter() {
                for header in value.iter() {
                    try!(write!(&mut writer, "{}: {}\r\n", *key, *header).map_err(|_| ()));
                }
            }

            try!(write!(&mut writer, "\r\n").map_err(|_| ()));
            try!(body.write_body(&mut writer).map_err(|_| ()));

            Ok(())
        }

        let handler = Box::new(handler);
        let raw_callback = raw::ServerCallback::new(internal_handler, handler);
        Ok(Server(try!(raw::Server::start(options, raw_callback))))
    }
}

fn request_info<'a>(connection: &'a raw::Connection)
    -> Result<RequestInfo<'a>, String>
{
    match get_request_info(connection) {
        Some(info) => Ok(info),
        None => Err("Couldn't get request info for connection".to_string())
    }
}

#[cfg(test)]
mod test {
    use std::collections::HashMap;
    use std::error::Error;
    use std::io::prelude::*;
    use std::io::{self, Cursor};
    use std::net::{SocketAddr, TcpStream, SocketAddrV4, Ipv4Addr};
    use std::sync::Mutex;
    use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering};
    use std::sync::mpsc::{channel, Sender};
    use super::{Server, Config, response};
    use conduit::{Request, Response, Handler};

    fn noop(_: &mut Request) -> Result<Response, io::Error> { unreachable!() }

    fn request(addr: SocketAddr, req: &str) -> String {
        let mut s = TcpStream::connect(&addr).unwrap();
        s.write_all(req.trim_left().as_bytes()).unwrap();
        let mut ret = String::new();
        s.read_to_string(&mut ret).unwrap();
        ret
    }

    fn port() -> u16 {
        static CNT: AtomicUsize = ATOMIC_USIZE_INIT;
        CNT.fetch_add(1, Ordering::SeqCst) as u16 + 13038
    }

    fn cfg(port: u16) -> Config {
        let mut cfg = Config::new();
        cfg.port(port).threads(1);
        return cfg
    }

    #[test]
    fn smoke() {
        Server::start(cfg(port()), noop).unwrap();
    }

    #[test]
    fn dupe_port() {
        let port = port();
        let s1 = Server::start(cfg(port), noop);
        assert!(s1.is_ok());
        let s2 = Server::start(cfg(port), noop);
        assert!(s2.is_err());
    }

    #[test]
    fn drops_handler() {
        static mut DROPPED: bool = false;
        struct Foo;
        impl Handler for Foo {
            fn call(&self, _req: &mut Request) -> Result<Response, Box<Error+Send>> {
                panic!()
            }
        }
        impl Drop for Foo {
            fn drop(&mut self) { unsafe { DROPPED = true; } }
        }

        drop(Server::start(cfg(port()), Foo));
        unsafe { assert!(DROPPED); }
    }

    #[test]
    fn invokes() {
        struct Foo(Mutex<Sender<()>>);
        impl Handler for Foo {
            fn call(&self, _req: &mut Request) -> Result<Response, Box<Error+Send>> {
                let Foo(ref tx) = *self;
                tx.lock().unwrap().send(()).unwrap();
                Ok(response(200, HashMap::new(), Cursor::new(vec![])))
            }
        }

        let (tx, rx) = channel();
        let handler = Foo(Mutex::new(tx));
        let port = port();
        let ip = Ipv4Addr::new(127, 0, 0, 1);
        let addr = SocketAddr::V4(SocketAddrV4::new(ip, port));
        let _s = Server::start(cfg(port), handler);
        request(addr, "\r
GET / HTTP/1.1\r
\r
");
        rx.recv().unwrap();
    }

    #[test]
    fn header_sent() {
        struct Foo(Mutex<Sender<String>>);
        impl Handler for Foo {
            fn call(&self, req: &mut Request) -> Result<Response, Box<Error+Send>> {
                let Foo(ref tx) = *self;
                tx.lock().unwrap()
                  .send(req.headers().find("Foo").unwrap().join("")).unwrap();
                Ok(response(200, HashMap::new(), Cursor::new(vec![])))
            }
        }

        let (tx, rx) = channel();
        let handler = Foo(Mutex::new(tx));
        let port = port();
        let ip = Ipv4Addr::new(127, 0, 0, 1);
        let addr = SocketAddr::V4(SocketAddrV4::new(ip, port));
        let _s = Server::start(cfg(port), handler);
        request(addr, "\r
GET / HTTP/1.1\r
Foo: bar\r
\r
");
        assert_eq!(rx.recv().unwrap(), "bar");
    }

    #[test]
    fn failing_handler() {
        struct Foo;
        impl Handler for Foo {
            fn call(&self, _req: &mut Request) -> Result<Response, Box<Error+Send>> {
                panic!()
            }
        }

        let port = port();
        let ip = Ipv4Addr::new(127, 0, 0, 1);
        let addr = SocketAddr::V4(SocketAddrV4::new(ip, port));
        let _s = Server::start(cfg(port), Foo);
        request(addr, "\r
GET / HTTP/1.1\r
Foo: bar\r
\r
");
    }

    #[test]
    fn failing_handler_is_500() {
        struct Foo;
        impl Handler for Foo {
            fn call(&self, _req: &mut Request) -> Result<Response, Box<Error+Send>> {
                panic!()
            }
        }

        let port = port();
        let ip = Ipv4Addr::new(127, 0, 0, 1);
        let addr = SocketAddr::V4(SocketAddrV4::new(ip, port));
        let _s = Server::start(cfg(port), Foo);
        let response = request(addr, "\r
GET / HTTP/1.1\r
Foo: bar\r
\r
");
        assert!(response.contains("500 Internal"),
                "not a failing response: {}", response);
    }
}
<commit_msg>Make the test `dupe_port` Windows-only<commit_after>extern crate conduit;
extern crate libc;
extern crate semver;
extern crate civet_sys as ffi;

use std::collections::HashMap;
use std::io::prelude::*;
use std::io::{self, BufWriter};
use std::net::{SocketAddr, IpAddr, Ipv4Addr};
use std::str::FromStr;

use conduit::{Handler, Extensions, TypeMap, Method, Scheme, Host};

use raw::{RequestInfo,Header};
use raw::{get_header,get_headers,get_request_info};
use status::{ToStatusCode};

pub use config::Config;

mod raw;
mod config;
pub mod status;

pub struct Connection<'a> {
    request: CivetRequest<'a>,
    written: bool,
}

pub struct CivetRequest<'a> {
    conn: &'a raw::Connection,
    request_info: RequestInfo<'a>,
    headers: Headers<'a>,
    extensions: Extensions
}

fn ver(major: u64, minor: u64) -> semver::Version {
    semver::Version {
        major: major,
        minor: minor,
        patch: 0,
        pre: vec!(),
        build: vec!()
    }
}

impl<'a> conduit::Request for CivetRequest<'a> {
    fn http_version(&self) -> semver::Version {
        let version = self.request_info.http_version().unwrap();
        match version {
            "1.0" => ver(1, 0),
            "1.1" => ver(1, 1),
            _ => ver(1, 1)
        }
    }

    fn conduit_version(&self) -> semver::Version {
        ver(0, 1)
    }

    fn method(&self) -> Method {
        match self.request_info.method().unwrap() {
            "HEAD" => Method::Head,
            "GET" => Method::Get,
            "POST" => Method::Post,
            "PUT" => Method::Put,
            "DELETE" => Method::Delete,
            "PATCH" => Method::Patch,
            "PURGE" => Method::Purge,
            "CONNECT" => Method::Connect,
            "OPTIONS" => Method::Options,
            "TRACE" => Method::Trace,
            other @ _ => panic!("Civet does not support {} requests", other)
        }
    }

    fn scheme(&self) -> Scheme {
        if self.request_info.is_ssl() {
            Scheme::Https
        } else {
            Scheme::Http
        }
    }

    fn host(&self) -> Host {
        Host::Name(get_header(self.conn, "Host").unwrap())
    }

    fn virtual_root(&self) -> Option<&str> {
        None
    }

    fn path(&self) -> &str {
        self.request_info.url().unwrap()
    }

    fn query_string(&self) -> Option<&str> {
        self.request_info.query_string()
    }

    fn remote_addr(&self) -> SocketAddr {
        let ip = self.request_info.remote_addr();
        let ip = IpAddr::from_str(ip).unwrap_or_else(|_| Ipv4Addr::new(0, 0, 0, 0).into());
        SocketAddr::new(ip, self.request_info.remote_port())
    }

    fn content_length(&self) -> Option<u64> {
        get_header(self.conn, "Content-Length").and_then(|s| s.parse().ok())
    }

    fn headers(&self) -> &conduit::Headers { &self.headers }

    fn body(&mut self) -> &mut Read { self }

    fn extensions(&self) -> &Extensions { &self.extensions }

    fn mut_extensions(&mut self) -> &mut Extensions {
        &mut self.extensions
    }
}

pub fn response<S: ToStatusCode, R: Read + Send + 'static>(status: S,
    headers: HashMap<String, Vec<String>>, body: R) -> conduit::Response
{
    conduit::Response {
        status: status.to_status().ok().unwrap().to_code(),
        headers: headers,
        body: Box::new(body),
    }
}

impl<'a> Connection<'a> {
    fn new(conn: &raw::Connection) -> Result<Connection, String> {
        match request_info(conn) {
            Ok(info) => {
                let request = CivetRequest {
                    conn: conn,
                    request_info: info,
                    headers: Headers { conn: conn },
                    extensions: TypeMap::new()
                };

                Ok(Connection {
                    request: request,
                    written: false,
                })
            },
            Err(err) => Err(err)
        }
    }

}

impl<'a> Write for Connection<'a> {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.written = true;
        match raw::write(self.request.conn, buf) {
            n if n < 0 => Err(io::Error::new(io::ErrorKind::Other,
                                             &format!("write error ({})", n)[..])),
            n => Ok(n as usize)
        }
    }
    fn flush(&mut self) -> io::Result<()> { Ok(()) }
}

impl<'a> Read for CivetRequest<'a> {
    fn read(&mut self, buf: &mut[u8]) -> io::Result<usize> {
        match raw::read(self.conn, buf) {
            n if n < 0 => Err(io::Error::new(io::ErrorKind::Other,
                                             &format!("read error ({})", n)[..])),
            n => Ok(n as usize)
        }
    }
}

impl<'a> Drop for Connection<'a> {
    fn drop(&mut self) {
        if !self.written {
            let _ = writeln!(self, "HTTP/1.1 500 Internal Server Error");
        }
    }
}

pub struct Headers<'a> {
    conn: &'a raw::Connection
}

impl<'a> conduit::Headers for Headers<'a> {
    fn find(&self, string: &str) -> Option<Vec<&str>> {
        get_header(self.conn, string).map(|s| vec!(s))
    }

    fn has(&self, string: &str) -> bool {
        get_header(self.conn, string).is_some()
    }

    fn all(&self) -> Vec<(&str, Vec<&str>)> {
        HeaderIterator::new(self.conn).collect()
    }
}

pub struct HeaderIterator<'a> {
    headers: Vec<Header<'a>>,
    position: usize
}

impl<'a> HeaderIterator<'a> {
    fn new<'b>(conn: &'b raw::Connection) -> HeaderIterator<'b> {
        HeaderIterator { headers: get_headers(conn), position: 0 }
    }
}

impl<'a> Iterator for HeaderIterator<'a> {
    type Item = (&'a str, Vec<&'a str>);
    fn next(&mut self) -> Option<(&'a str, Vec<&'a str>)> {
        let pos = self.position;
        let headers = &self.headers;

        if self.headers.len() <= pos {
            None
        } else {
            let header = &headers[pos];
            self.position += 1;
            header.name().map(|name| (name, vec!(header.value().unwrap())))
        }
    }
}

pub struct Server(raw::Server<Box<Handler + 'static + Sync>>);

impl Server {
    pub fn start<H: Handler + 'static + Sync>(options: Config, handler: H)
        -> io::Result<Server>
    {
        fn internal_handler(conn: &mut raw::Connection,
                            handler: &Box<Handler + 'static + Sync>)
                            -> Result<(), ()> {
            let mut connection = Connection::new(conn).unwrap();
            let response = handler.call(&mut connection.request);
            let mut writer = BufWriter::new(connection);

            fn err<W: Write>(writer: &mut W) {
                let _ = writeln!(writer, "HTTP/1.1 500 Internal Server Error");
            }

            let conduit::Response { status, headers, mut body } = match response {
                Ok(r) => r,
                Err(_) => return Err(err(&mut writer)),
            };
            let (code, string) = status;
            try!(write!(&mut writer, "HTTP/1.1 {} {}\r\n", code, string).map_err(|_| ()));

            for (key, value) in headers.iter() {
                for header in value.iter() {
                    try!(write!(&mut writer, "{}: {}\r\n", *key, *header).map_err(|_| ()));
                }
            }

            try!(write!(&mut writer, "\r\n").map_err(|_| ()));
            try!(body.write_body(&mut writer).map_err(|_| ()));

            Ok(())
        }

        let handler = Box::new(handler);
        let raw_callback = raw::ServerCallback::new(internal_handler, handler);
        Ok(Server(try!(raw::Server::start(options, raw_callback))))
    }
}

fn request_info<'a>(connection: &'a raw::Connection)
    -> Result<RequestInfo<'a>, String>
{
    match get_request_info(connection) {
        Some(info) => Ok(info),
        None => Err("Couldn't get request info for connection".to_string())
    }
}

#[cfg(test)]
mod test {
    use std::collections::HashMap;
    use std::error::Error;
    use std::io::prelude::*;
    use std::io::{self, Cursor};
    use std::net::{SocketAddr, TcpStream, SocketAddrV4, Ipv4Addr};
    use std::sync::Mutex;
    use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering};
    use std::sync::mpsc::{channel, Sender};
    use super::{Server, Config, response};
    use conduit::{Request, Response, Handler};

    fn noop(_: &mut Request) -> Result<Response, io::Error> { unreachable!() }

    fn request(addr: SocketAddr, req: &str) -> String {
        let mut s = TcpStream::connect(&addr).unwrap();
        s.write_all(req.trim_left().as_bytes()).unwrap();
        let mut ret = String::new();
        s.read_to_string(&mut ret).unwrap();
        ret
    }

    fn port() -> u16 {
        static CNT: AtomicUsize = ATOMIC_USIZE_INIT;
        CNT.fetch_add(1, Ordering::SeqCst) as u16 + 13038
    }

    fn cfg(port: u16) -> Config {
        let mut cfg = Config::new();
        cfg.port(port).threads(1);
        return cfg
    }

    #[test]
    fn smoke() {
        Server::start(cfg(port()), noop).unwrap();
    }

    #[test]
    #[cfg(target_os = "windows")]
    fn dupe_port() {
        let port = port();
        let s1 = Server::start(cfg(port), noop);
        assert!(s1.is_ok());
        let s2 = Server::start(cfg(port), noop);
        assert!(s2.is_err());
    }

    #[test]
    fn drops_handler() {
        static mut DROPPED: bool = false;
        struct Foo;
        impl Handler for Foo {
            fn call(&self, _req: &mut Request) -> Result<Response, Box<Error+Send>> {
                panic!()
            }
        }
        impl Drop for Foo {
            fn drop(&mut self) { unsafe { DROPPED = true; } }
        }

        drop(Server::start(cfg(port()), Foo));
        unsafe { assert!(DROPPED); }
    }

    #[test]
    fn invokes() {
        struct Foo(Mutex<Sender<()>>);
        impl Handler for Foo {
            fn call(&self, _req: &mut Request) -> Result<Response, Box<Error+Send>> {
                let Foo(ref tx) = *self;
                tx.lock().unwrap().send(()).unwrap();
                Ok(response(200, HashMap::new(), Cursor::new(vec![])))
            }
        }

        let (tx, rx) = channel();
        let handler = Foo(Mutex::new(tx));
        let port = port();
        let ip = Ipv4Addr::new(127, 0, 0, 1);
        let addr = SocketAddr::V4(SocketAddrV4::new(ip, port));
        let _s = Server::start(cfg(port), handler);
        request(addr, "\r
GET / HTTP/1.1\r
\r
");
        rx.recv().unwrap();
    }

    #[test]
    fn header_sent() {
        struct Foo(Mutex<Sender<String>>);
        impl Handler for Foo {
            fn call(&self, req: &mut Request) -> Result<Response, Box<Error+Send>> {
                let Foo(ref tx) = *self;
                tx.lock().unwrap()
                  .send(req.headers().find("Foo").unwrap().join("")).unwrap();
                Ok(response(200, HashMap::new(), Cursor::new(vec![])))
            }
        }

        let (tx, rx) = channel();
        let handler = Foo(Mutex::new(tx));
        let port = port();
        let ip = Ipv4Addr::new(127, 0, 0, 1);
        let addr = SocketAddr::V4(SocketAddrV4::new(ip, port));
        let _s = Server::start(cfg(port), handler);
        request(addr, "\r
GET / HTTP/1.1\r
Foo: bar\r
\r
");
        assert_eq!(rx.recv().unwrap(), "bar");
    }

    #[test]
    fn failing_handler() {
        struct Foo;
        impl Handler for Foo {
            fn call(&self, _req: &mut Request) -> Result<Response, Box<Error+Send>> {
                panic!()
            }
        }

        let port = port();
        let ip = Ipv4Addr::new(127, 0, 0, 1);
        let addr = SocketAddr::V4(SocketAddrV4::new(ip, port));
        let _s = Server::start(cfg(port), Foo);
        request(addr, "\r
GET / HTTP/1.1\r
Foo: bar\r
\r
");
    }

    #[test]
    fn failing_handler_is_500() {
        struct Foo;
        impl Handler for Foo {
            fn call(&self, _req: &mut Request) -> Result<Response, Box<Error+Send>> {
                panic!()
            }
        }

        let port = port();
        let ip = Ipv4Addr::new(127, 0, 0, 1);
        let addr = SocketAddr::V4(SocketAddrV4::new(ip, port));
        let _s = Server::start(cfg(port), Foo);
        let response = request(addr, "\r
GET / HTTP/1.1\r
Foo: bar\r
\r
");
        assert!(response.contains("500 Internal"),
                "not a failing response: {}", response);
    }
}
<|endoftext|>


<commit_before>// Copyright 2013-2016, The Gtk-rs Project Developers.
// See the COPYRIGHT file at the top-level directory of this distribution.
// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>

//! # **glib**, **gobject** and **gio** bindings for Rust
//!
//! This library contains
//!
//! - bindings to some essential GLib, GObject, GIO types and APIs,
//!
//! - common building blocks used in both handmade and machine generated
//! bindings to GTK+ and other GLib-based libraries.
//!
//! It is the foundation for higher level libraries with uniform Rusty (safe
//! and strongly typed) APIs. It avoids exposing GLib-specific data types where
//! possible and is not meant to provide comprehensive GLib bindings, which
//! would often amount to duplicating the Rust Standard Library or other utility
//! crates.
//!
//! The library is a work in progress: expect missing functionality and breaking
//! changes.
//!
//! # Objects
//!
//! Each class and interface has a corresponding smart pointer struct
//! representing an instance of that type (e.g. `Object` for `GObject`,
//! `gtk::Widget` for `GtkWidget`). They are reference counted and feature
//! interior mutability similarly to Rust's `Rc<RefCell<T>>` idiom.
//! Consequently, cloning objects is cheap and their methods never require
//! mutable borrows. Two smart pointers are equal iff they point to the same
//! object.
//!
//! The root of the object hierarchy is [`Object`](object/struct.Object.html).
//! Inheritance and subtyping is denoted with the [`IsA`](object/trait.IsA.html)
//! marker trait. The [`Cast`](object/trait.Cast.html) trait enables upcasting
//! and downcasting.
//!
//! Interfaces and non-leaf classes also have corresponding traits (e.g.
//! `ObjectExt` and `gtk::WidgetExt`), which are blanketly implemented for all
//! their subtypes.

extern crate libc;
extern crate glib_sys as glib_ffi;
extern crate gobject_sys as gobject_ffi;
extern crate gio_sys as gio_ffi;

use libc::c_char;

pub use self::app_info::AppInfo;
pub use self::glib_container::GlibContainer;
pub use self::error::{Error};
pub use self::object::{
    Cast,
    IsA,
    Object,
};
pub use self::permission::Permission;
pub use self::source::{Continue, idle_add, timeout_add, timeout_add_seconds};
pub use self::traits::FFIGObject;
pub use types::Type;
pub use value::Value;
pub use self::date::{TimeVal, Time, Date, Year, Month, Weekday, Day};

#[macro_use]
pub mod wrapper;
#[macro_use]
pub mod boxed;
#[macro_use]
pub mod shared;
#[macro_use]
pub mod object;

mod app_info;
pub mod glib_container;
pub mod error;
mod file_error;
mod permission;
pub mod signal;
pub mod source;
pub mod traits;
pub mod translate;
pub mod value;

pub mod types;
pub mod date;

pub fn to_gboolean(b: bool) -> glib_ffi::gboolean {
    match b {
        true => glib_ffi::GTRUE,
        false => glib_ffi::GFALSE
    }
}

pub fn to_bool(b: glib_ffi::gboolean) -> bool {
    b != glib_ffi::GFALSE
}

// An opaque structure used as the base of all interface types.
pub struct TypeInterface;

// An opaque structure used as the base of all type instances.
pub struct TypeInstance;

// An opaque structure used as the base of all classes.
pub struct TypeClass;

//FIXME: Check if this is actually correct (maybe not since ParamFlags is deprecated)
#[derive(Clone, Copy)]
pub enum ParamFlags{
    Readable,
    Writable,
    ReadWrite,
    Construct,
    ConstructOnly,
    LaxValidation,
    StaticName,
    Private,
    StaticNick,
    StaticBlurb,
    Deprecated
}

#[repr(C)]
pub struct ParamSpec {
    g_type_instance: TypeInstance,
    name: *mut c_char,
    flags: ParamFlags,
    value_type: glib_ffi::GType,
    owner_type: glib_ffi::GType,
}
<commit_msg>Reexport `Value` helper types<commit_after>// Copyright 2013-2016, The Gtk-rs Project Developers.
// See the COPYRIGHT file at the top-level directory of this distribution.
// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>

//! # **glib**, **gobject** and **gio** bindings for Rust
//!
//! This library contains
//!
//! - bindings to some essential GLib, GObject, GIO types and APIs,
//!
//! - common building blocks used in both handmade and machine generated
//! bindings to GTK+ and other GLib-based libraries.
//!
//! It is the foundation for higher level libraries with uniform Rusty (safe
//! and strongly typed) APIs. It avoids exposing GLib-specific data types where
//! possible and is not meant to provide comprehensive GLib bindings, which
//! would often amount to duplicating the Rust Standard Library or other utility
//! crates.
//!
//! The library is a work in progress: expect missing functionality and breaking
//! changes.
//!
//! # Objects
//!
//! Each class and interface has a corresponding smart pointer struct
//! representing an instance of that type (e.g. `Object` for `GObject`,
//! `gtk::Widget` for `GtkWidget`). They are reference counted and feature
//! interior mutability similarly to Rust's `Rc<RefCell<T>>` idiom.
//! Consequently, cloning objects is cheap and their methods never require
//! mutable borrows. Two smart pointers are equal iff they point to the same
//! object.
//!
//! The root of the object hierarchy is [`Object`](object/struct.Object.html).
//! Inheritance and subtyping is denoted with the [`IsA`](object/trait.IsA.html)
//! marker trait. The [`Cast`](object/trait.Cast.html) trait enables upcasting
//! and downcasting.
//!
//! Interfaces and non-leaf classes also have corresponding traits (e.g.
//! `ObjectExt` and `gtk::WidgetExt`), which are blanketly implemented for all
//! their subtypes.

extern crate libc;
extern crate glib_sys as glib_ffi;
extern crate gobject_sys as gobject_ffi;
extern crate gio_sys as gio_ffi;

use libc::c_char;

pub use self::app_info::AppInfo;
pub use self::glib_container::GlibContainer;
pub use self::error::{Error};
pub use self::object::{
    Cast,
    IsA,
    Object,
};
pub use self::permission::Permission;
pub use self::source::{Continue, idle_add, timeout_add, timeout_add_seconds};
pub use self::traits::FFIGObject;
pub use types::Type;
pub use value::{TypedValue, TypedValueMut, Value};
pub use self::date::{TimeVal, Time, Date, Year, Month, Weekday, Day};

#[macro_use]
pub mod wrapper;
#[macro_use]
pub mod boxed;
#[macro_use]
pub mod shared;
#[macro_use]
pub mod object;

mod app_info;
pub mod glib_container;
pub mod error;
mod file_error;
mod permission;
pub mod signal;
pub mod source;
pub mod traits;
pub mod translate;
pub mod value;

pub mod types;
pub mod date;

pub fn to_gboolean(b: bool) -> glib_ffi::gboolean {
    match b {
        true => glib_ffi::GTRUE,
        false => glib_ffi::GFALSE
    }
}

pub fn to_bool(b: glib_ffi::gboolean) -> bool {
    b != glib_ffi::GFALSE
}

// An opaque structure used as the base of all interface types.
pub struct TypeInterface;

// An opaque structure used as the base of all type instances.
pub struct TypeInstance;

// An opaque structure used as the base of all classes.
pub struct TypeClass;

//FIXME: Check if this is actually correct (maybe not since ParamFlags is deprecated)
#[derive(Clone, Copy)]
pub enum ParamFlags{
    Readable,
    Writable,
    ReadWrite,
    Construct,
    ConstructOnly,
    LaxValidation,
    StaticName,
    Private,
    StaticNick,
    StaticBlurb,
    Deprecated
}

#[repr(C)]
pub struct ParamSpec {
    g_type_instance: TypeInstance,
    name: *mut c_char,
    flags: ParamFlags,
    value_type: glib_ffi::GType,
    owner_type: glib_ffi::GType,
}
<|endoftext|>


<commit_before>//! Read memory from another process' address space.
//!
//! This crate provides a trait—[`CopyAddress`](trait.CopyAddress.html),
//! and a helper function—[`copy_address`](fn.copy_address.html) that
//! allow reading memory from another process.
//!
//! Note: you may not always have permission to read memory from another
//! process! This may require `sudo` on some systems, and may fail even with
//! `sudo` on OS X. You are most likely to succeed if you are attempting to
//! read a process that you have spawned yourself.
//!
//! # Examples
//!
//! ```rust,no_run
//! # use std::io;
//! use read_process_memory::*;
//!
//! # fn foo(pid: Pid, address: usize, size: usize) -> io::Result<()> {
//! let handle = try!(pid.try_into_process_handle());
//! let bytes = try!(copy_address(address, size, &handle));
//! # Ok(())
//! # }
//! ```

#[macro_use] extern crate log;
extern crate libc;

use std::io;

/// A trait that provides a method for reading memory from another process.
pub trait CopyAddress {
    /// Try to copy `buf.len()` bytes from `addr` in the process `self`, placing them in `buf`.
    fn copy_address(&self, addr: usize, buf: &mut [u8]) -> io::Result<()>;
}

/// A process ID.
pub use platform::Pid;
/// A handle to a running process. This is not a process ID on all platforms.
pub use platform::ProcessHandle;

/// Attempt to get a process handle for a running process.
///
/// This operation is not guaranteed to succeed. Specifically, on Windows
/// `OpenProcess` may fail, and on OS X `task_for_pid` will generally fail
/// unless run as root, and even then it may fail when called on certain
/// programs.
///
/// This should be dropped in favor of TryInto when that stabilizes:
/// https://github.com/rust-lang/rust/issues/33417
pub trait TryIntoProcessHandle {
    /// Attempt to get a `ProcessHandle` from `self`.
    fn try_into_process_handle(&self) -> io::Result<ProcessHandle>;
}

/// Trivial implementation of `TryIntoProcessHandle`.
///
/// A `ProcessHandle` is always usable.
impl TryIntoProcessHandle for ProcessHandle {
    fn try_into_process_handle(&self) -> io::Result<platform::ProcessHandle> {
        Ok(*self)
    }
}

#[cfg(target_os="linux")]
mod platform {
    use libc::{pid_t, c_void, iovec, process_vm_readv};
    use std::io;
    use std::process::Child;

    use super::{CopyAddress, TryIntoProcessHandle};

    /// On Linux a `Pid` is just a `libc::pid_t`.
    pub type Pid = pid_t;
    /// On Linux a `ProcessHandle` is just a `libc::pid_t`.
    pub type ProcessHandle = pid_t;

    /// A `process::Child` always has a pid, which is all we need on Linux.
    impl TryIntoProcessHandle for Child {
        fn try_into_process_handle(&self) -> io::Result<ProcessHandle> {
            Ok(self.id() as pid_t)
        }
    }

    impl CopyAddress for ProcessHandle {
        fn copy_address(&self, addr: usize, buf: &mut [u8]) -> io::Result<()> {
            let local_iov = iovec {
                iov_base: buf.as_mut_ptr() as *mut c_void,
                iov_len: buf.len(),
            };
            let remote_iov = iovec {
                iov_base: addr as *mut c_void,
                iov_len: buf.len(),
            };
            let result = unsafe {
                process_vm_readv(*self, &local_iov, 1, &remote_iov, 1, 0)
            };
            if result == -1 {
                Err(io::Error::last_os_error())
            } else {
                Ok(())
            }
        }
    }
}

#[cfg(target_os="macos")]
mod platform {
    extern crate mach;

    use libc::{pid_t, c_int};
    use self::mach::kern_return::{kern_return_t, KERN_SUCCESS};
    use self::mach::port::{mach_port_t, mach_port_name_t, MACH_PORT_NULL};
    use self::mach::vm_types::{mach_vm_address_t, mach_vm_size_t};
    use self::mach::message::{mach_msg_type_number_t};
    use std::io;
    use std::process::Child;
    use std::ptr;
    use std::slice;

    use super::{CopyAddress, TryIntoProcessHandle};

    #[allow(non_camel_case_types)] type vm_map_t = mach_port_t;
    #[allow(non_camel_case_types)] type vm_address_t = mach_vm_address_t;
    #[allow(non_camel_case_types)] type vm_size_t = mach_vm_size_t;

    /// On OS X a `Pid` is just a `libc::pid_t`.
    pub type Pid = pid_t;
    /// On OS X a `ProcessHandle` is a mach port.
    pub type ProcessHandle = mach_port_name_t;

    extern "C" {
        fn vm_read(target_task: vm_map_t, address: vm_address_t, size: vm_size_t, data: &*mut u8, data_size: *mut mach_msg_type_number_t) -> kern_return_t;
    }

    /// A small wrapper around `task_for_pid`, which taskes a pid returns the mach port representing its task.
    fn task_for_pid(pid: pid_t) -> io::Result<mach_port_name_t> {
        let mut task: mach_port_name_t = MACH_PORT_NULL;

        unsafe {
            let result = mach::traps::task_for_pid(mach::traps::mach_task_self(), pid as c_int, &mut task);
            if result != KERN_SUCCESS {
                return Err(io::Error::last_os_error())
            }
        }

        Ok(task)
    }

    /// `Pid` can be turned into a `ProcessHandle` with `task_for_pid`.
    impl TryIntoProcessHandle for Pid {
        fn try_into_process_handle(&self) -> io::Result<ProcessHandle> {
            task_for_pid(*self)
        }
    }

    /// This `TryIntoProcessHandle` impl simply calls the `TryIntoProcessHandle` impl for `Pid`.
    ///
    /// Unfortunately spawning a process on OS X does not hand back a mach
    /// port by default (you have to jump through several hoops to get at it),
    /// so there's no simple implementation of `TryIntoProcessHandle` for
    /// `std::process::Child`. This implementation is just provided for symmetry
    /// with other platforms to make writing cross-platform code easier.
    ///
    /// Ideally we would provide an implementation of `std::process::Command::spawn`
    /// that jumped through those hoops and provided the task port.
    impl TryIntoProcessHandle for Child {
        fn try_into_process_handle(&self) -> io::Result<ProcessHandle> {
            self.id().try_into_process_handle()
        }
    }

    /// Use `vm_read` to read memory from another process on OS X.
    impl CopyAddress for ProcessHandle {
        fn copy_address(&self, addr: usize, buf: &mut [u8]) -> io::Result<()> {
            let page_addr      = (addr as i64 & (-4096)) as mach_vm_address_t;
	    let last_page_addr = ((addr as i64 + buf.len() as i64 + 4095) & (-4096)) as mach_vm_address_t;
            let page_size      = last_page_addr as usize - page_addr as usize;

            let read_ptr: *mut u8 = ptr::null_mut();
            let mut read_len: mach_msg_type_number_t = 0;

            let result = unsafe {
                vm_read(*self, page_addr as u64, page_size as vm_size_t, &read_ptr, &mut read_len)
            };

            if result != KERN_SUCCESS {
                return Err(io::Error::last_os_error())
            }

            if read_len != page_size as u32 {
                panic!("Mismatched read sizes for `vm_read` (expected {}, got {})", page_size, read_len)
            }

            let read_buf = unsafe { slice::from_raw_parts(read_ptr, read_len as usize) };

            let offset = addr - page_addr as usize;
            let len = buf.len();
            buf.copy_from_slice(&read_buf[offset..(offset + len)]);

            Ok(())
        }
    }
}

#[cfg(windows)]
mod platform {
    extern crate winapi;
    extern crate kernel32;

    use std::io;
    use std::mem;
    use std::os::windows::io::{AsRawHandle, RawHandle};
    use std::process::Child;
    use std::ptr;

    use super::{CopyAddress, TryIntoProcessHandle};

    /// On Windows a `Pid` is a `DWORD`.
    pub type Pid = winapi::DWORD;
    /// On Windows a `ProcessHandle` is a `HANDLE`.
    pub type ProcessHandle = RawHandle;

    /// A `Pid` can be turned into a `ProcessHandle` with `OpenProcess`.
    impl TryIntoProcessHandle for winapi::DWORD {
        fn try_into_process_handle(&self) -> io::Result<ProcessHandle> {
            let handle = unsafe { kernel32::OpenProcess(winapi::winnt::PROCESS_VM_READ, winapi::FALSE, *self) };
            if handle == (0 as RawHandle) {
                Err(io::Error::last_os_error())
            } else {
                Ok(handle)
            }
        }
    }

    /// A `std::process::Child` has a `HANDLE` from calling `CreateProcess`.
    impl TryIntoProcessHandle for Child {
        fn try_into_process_handle(&self) -> io::Result<ProcessHandle> {
            Ok(self.as_raw_handle())
        }
    }

    /// Use `ReadProcessMemory` to read memory from another process on Windows.
    impl CopyAddress for ProcessHandle {
        fn copy_address(&self, addr: usize, buf: &mut [u8]) -> io::Result<()> {
            if buf.len() == 0 {
                return Ok(());
            }

            if unsafe { kernel32::ReadProcessMemory(*self,
                                                    addr as winapi::LPVOID,
                                                    buf.as_mut_ptr() as winapi::LPVOID,
                                                    mem::size_of_val(buf) as winapi::SIZE_T,
                                                    ptr::null_mut()) } == winapi::FALSE
            {
                Err(io::Error::last_os_error())
            } else {
                Ok(())
            }
        }
    }
}

/// Copy `length` bytes of memory at `addr` from `source`.
///
/// This is just a convenient way to call `CopyAddress::copy_address` without
/// having to provide your own buffer.
pub fn copy_address<T>(addr: usize, length: usize, source: &T) -> io::Result<Vec<u8>>
    where T: CopyAddress
{
    debug!("copy_address: addr: {:x}", addr);

    let mut copy = vec![0; length];

    source.copy_address(addr, &mut copy)
        .map_err(|e| {
            warn!("copy_address failed for {:x}: {:?}", addr, e);
            e
        })
        .and(Ok(copy))
}

#[cfg(test)]
mod test {
    #[cfg(target_os="macos")]
    extern crate spawn_task_port;

    use super::*;
    use std::env;
    use std::io::{self, BufRead, BufReader};
    use std::path::PathBuf;
    use std::process::{Child, Command, Stdio};

    fn test_process_path() -> Option<PathBuf> {
        env::current_exe()
            .ok()
            .and_then(|p| p.parent().map(|p| p.with_file_name("test")
                                         .with_extension(env::consts::EXE_EXTENSION)))
    }

    #[cfg(not(target_os="macos"))]
    fn spawn_with_handle(cmd: &mut Command) -> io::Result<(Child, ProcessHandle)>
    {
        let mut child = try!(cmd
                             .spawn()
                             .map_err(|e| {
                                 println!("Error spawning test process '{:?}': {:?}", path, e);
                                 e
                             }));
        let handle = try!(child.try_into_process_handle());
        Ok((child, handle))
    }

    #[cfg(target_os="macos")]
    fn spawn_with_handle(cmd: &mut Command) -> io::Result<(Child, ProcessHandle)>
    {
        use self::spawn_task_port::CommandSpawnWithTask;
        cmd.spawn_get_task_port()
    }

    fn read_test_process(args: Option<&[&str]>) -> io::Result<Vec<u8>> {
        // Spawn a child process and attempt to read its memory.
        let path = test_process_path().unwrap();
        let mut cmd = Command::new(&path);
        {
            cmd.stdin(Stdio::piped())
                .stdout(Stdio::piped());
        }
        if let Some(a) = args {
            cmd.args(a);
        }
        let (mut child, handle) = try!(spawn_with_handle(&mut cmd));
        // The test program prints the address and size.
        // See `src/bin/test.rs` for its source.
        let reader = BufReader::new(child.stdout.take().unwrap());
        let line = reader.lines().next().unwrap().unwrap();
        let bits = line.split(' ').collect::<Vec<_>>();
        let addr = usize::from_str_radix(&bits[0][2..], 16).unwrap();
        let size = bits[1].parse::<usize>().unwrap();
        let mem = try!(copy_address(addr, size, &handle));
        try!(child.wait());
        Ok(mem)
    }

    #[test]
    fn test_read_small() {
        let mem = read_test_process(None).unwrap();
        assert_eq!(mem, (0..32u8).collect::<Vec<u8>>());
    }

    #[test]
    fn test_read_large() {
        // 5000 should be greater than a single page on most systems.
        const SIZE: usize = 5000;
        let arg = format!("{}", SIZE);
        let mem = read_test_process(Some(&[&arg])).unwrap();
        let expected = (0..SIZE).map(|v| (v % (u8::max_value() as usize + 1)) as u8).collect::<Vec<u8>>();
        assert_eq!(mem, expected);
    }
}
<commit_msg>oops, fix non-mac build<commit_after>//! Read memory from another process' address space.
//!
//! This crate provides a trait—[`CopyAddress`](trait.CopyAddress.html),
//! and a helper function—[`copy_address`](fn.copy_address.html) that
//! allow reading memory from another process.
//!
//! Note: you may not always have permission to read memory from another
//! process! This may require `sudo` on some systems, and may fail even with
//! `sudo` on OS X. You are most likely to succeed if you are attempting to
//! read a process that you have spawned yourself.
//!
//! # Examples
//!
//! ```rust,no_run
//! # use std::io;
//! use read_process_memory::*;
//!
//! # fn foo(pid: Pid, address: usize, size: usize) -> io::Result<()> {
//! let handle = try!(pid.try_into_process_handle());
//! let bytes = try!(copy_address(address, size, &handle));
//! # Ok(())
//! # }
//! ```

#[macro_use] extern crate log;
extern crate libc;

use std::io;

/// A trait that provides a method for reading memory from another process.
pub trait CopyAddress {
    /// Try to copy `buf.len()` bytes from `addr` in the process `self`, placing them in `buf`.
    fn copy_address(&self, addr: usize, buf: &mut [u8]) -> io::Result<()>;
}

/// A process ID.
pub use platform::Pid;
/// A handle to a running process. This is not a process ID on all platforms.
pub use platform::ProcessHandle;

/// Attempt to get a process handle for a running process.
///
/// This operation is not guaranteed to succeed. Specifically, on Windows
/// `OpenProcess` may fail, and on OS X `task_for_pid` will generally fail
/// unless run as root, and even then it may fail when called on certain
/// programs.
///
/// This should be dropped in favor of TryInto when that stabilizes:
/// https://github.com/rust-lang/rust/issues/33417
pub trait TryIntoProcessHandle {
    /// Attempt to get a `ProcessHandle` from `self`.
    fn try_into_process_handle(&self) -> io::Result<ProcessHandle>;
}

/// Trivial implementation of `TryIntoProcessHandle`.
///
/// A `ProcessHandle` is always usable.
impl TryIntoProcessHandle for ProcessHandle {
    fn try_into_process_handle(&self) -> io::Result<platform::ProcessHandle> {
        Ok(*self)
    }
}

#[cfg(target_os="linux")]
mod platform {
    use libc::{pid_t, c_void, iovec, process_vm_readv};
    use std::io;
    use std::process::Child;

    use super::{CopyAddress, TryIntoProcessHandle};

    /// On Linux a `Pid` is just a `libc::pid_t`.
    pub type Pid = pid_t;
    /// On Linux a `ProcessHandle` is just a `libc::pid_t`.
    pub type ProcessHandle = pid_t;

    /// A `process::Child` always has a pid, which is all we need on Linux.
    impl TryIntoProcessHandle for Child {
        fn try_into_process_handle(&self) -> io::Result<ProcessHandle> {
            Ok(self.id() as pid_t)
        }
    }

    impl CopyAddress for ProcessHandle {
        fn copy_address(&self, addr: usize, buf: &mut [u8]) -> io::Result<()> {
            let local_iov = iovec {
                iov_base: buf.as_mut_ptr() as *mut c_void,
                iov_len: buf.len(),
            };
            let remote_iov = iovec {
                iov_base: addr as *mut c_void,
                iov_len: buf.len(),
            };
            let result = unsafe {
                process_vm_readv(*self, &local_iov, 1, &remote_iov, 1, 0)
            };
            if result == -1 {
                Err(io::Error::last_os_error())
            } else {
                Ok(())
            }
        }
    }
}

#[cfg(target_os="macos")]
mod platform {
    extern crate mach;

    use libc::{pid_t, c_int};
    use self::mach::kern_return::{kern_return_t, KERN_SUCCESS};
    use self::mach::port::{mach_port_t, mach_port_name_t, MACH_PORT_NULL};
    use self::mach::vm_types::{mach_vm_address_t, mach_vm_size_t};
    use self::mach::message::{mach_msg_type_number_t};
    use std::io;
    use std::process::Child;
    use std::ptr;
    use std::slice;

    use super::{CopyAddress, TryIntoProcessHandle};

    #[allow(non_camel_case_types)] type vm_map_t = mach_port_t;
    #[allow(non_camel_case_types)] type vm_address_t = mach_vm_address_t;
    #[allow(non_camel_case_types)] type vm_size_t = mach_vm_size_t;

    /// On OS X a `Pid` is just a `libc::pid_t`.
    pub type Pid = pid_t;
    /// On OS X a `ProcessHandle` is a mach port.
    pub type ProcessHandle = mach_port_name_t;

    extern "C" {
        fn vm_read(target_task: vm_map_t, address: vm_address_t, size: vm_size_t, data: &*mut u8, data_size: *mut mach_msg_type_number_t) -> kern_return_t;
    }

    /// A small wrapper around `task_for_pid`, which taskes a pid returns the mach port representing its task.
    fn task_for_pid(pid: pid_t) -> io::Result<mach_port_name_t> {
        let mut task: mach_port_name_t = MACH_PORT_NULL;

        unsafe {
            let result = mach::traps::task_for_pid(mach::traps::mach_task_self(), pid as c_int, &mut task);
            if result != KERN_SUCCESS {
                return Err(io::Error::last_os_error())
            }
        }

        Ok(task)
    }

    /// `Pid` can be turned into a `ProcessHandle` with `task_for_pid`.
    impl TryIntoProcessHandle for Pid {
        fn try_into_process_handle(&self) -> io::Result<ProcessHandle> {
            task_for_pid(*self)
        }
    }

    /// This `TryIntoProcessHandle` impl simply calls the `TryIntoProcessHandle` impl for `Pid`.
    ///
    /// Unfortunately spawning a process on OS X does not hand back a mach
    /// port by default (you have to jump through several hoops to get at it),
    /// so there's no simple implementation of `TryIntoProcessHandle` for
    /// `std::process::Child`. This implementation is just provided for symmetry
    /// with other platforms to make writing cross-platform code easier.
    ///
    /// Ideally we would provide an implementation of `std::process::Command::spawn`
    /// that jumped through those hoops and provided the task port.
    impl TryIntoProcessHandle for Child {
        fn try_into_process_handle(&self) -> io::Result<ProcessHandle> {
            self.id().try_into_process_handle()
        }
    }

    /// Use `vm_read` to read memory from another process on OS X.
    impl CopyAddress for ProcessHandle {
        fn copy_address(&self, addr: usize, buf: &mut [u8]) -> io::Result<()> {
            let page_addr      = (addr as i64 & (-4096)) as mach_vm_address_t;
	    let last_page_addr = ((addr as i64 + buf.len() as i64 + 4095) & (-4096)) as mach_vm_address_t;
            let page_size      = last_page_addr as usize - page_addr as usize;

            let read_ptr: *mut u8 = ptr::null_mut();
            let mut read_len: mach_msg_type_number_t = 0;

            let result = unsafe {
                vm_read(*self, page_addr as u64, page_size as vm_size_t, &read_ptr, &mut read_len)
            };

            if result != KERN_SUCCESS {
                return Err(io::Error::last_os_error())
            }

            if read_len != page_size as u32 {
                panic!("Mismatched read sizes for `vm_read` (expected {}, got {})", page_size, read_len)
            }

            let read_buf = unsafe { slice::from_raw_parts(read_ptr, read_len as usize) };

            let offset = addr - page_addr as usize;
            let len = buf.len();
            buf.copy_from_slice(&read_buf[offset..(offset + len)]);

            Ok(())
        }
    }
}

#[cfg(windows)]
mod platform {
    extern crate winapi;
    extern crate kernel32;

    use std::io;
    use std::mem;
    use std::os::windows::io::{AsRawHandle, RawHandle};
    use std::process::Child;
    use std::ptr;

    use super::{CopyAddress, TryIntoProcessHandle};

    /// On Windows a `Pid` is a `DWORD`.
    pub type Pid = winapi::DWORD;
    /// On Windows a `ProcessHandle` is a `HANDLE`.
    pub type ProcessHandle = RawHandle;

    /// A `Pid` can be turned into a `ProcessHandle` with `OpenProcess`.
    impl TryIntoProcessHandle for winapi::DWORD {
        fn try_into_process_handle(&self) -> io::Result<ProcessHandle> {
            let handle = unsafe { kernel32::OpenProcess(winapi::winnt::PROCESS_VM_READ, winapi::FALSE, *self) };
            if handle == (0 as RawHandle) {
                Err(io::Error::last_os_error())
            } else {
                Ok(handle)
            }
        }
    }

    /// A `std::process::Child` has a `HANDLE` from calling `CreateProcess`.
    impl TryIntoProcessHandle for Child {
        fn try_into_process_handle(&self) -> io::Result<ProcessHandle> {
            Ok(self.as_raw_handle())
        }
    }

    /// Use `ReadProcessMemory` to read memory from another process on Windows.
    impl CopyAddress for ProcessHandle {
        fn copy_address(&self, addr: usize, buf: &mut [u8]) -> io::Result<()> {
            if buf.len() == 0 {
                return Ok(());
            }

            if unsafe { kernel32::ReadProcessMemory(*self,
                                                    addr as winapi::LPVOID,
                                                    buf.as_mut_ptr() as winapi::LPVOID,
                                                    mem::size_of_val(buf) as winapi::SIZE_T,
                                                    ptr::null_mut()) } == winapi::FALSE
            {
                Err(io::Error::last_os_error())
            } else {
                Ok(())
            }
        }
    }
}

/// Copy `length` bytes of memory at `addr` from `source`.
///
/// This is just a convenient way to call `CopyAddress::copy_address` without
/// having to provide your own buffer.
pub fn copy_address<T>(addr: usize, length: usize, source: &T) -> io::Result<Vec<u8>>
    where T: CopyAddress
{
    debug!("copy_address: addr: {:x}", addr);

    let mut copy = vec![0; length];

    source.copy_address(addr, &mut copy)
        .map_err(|e| {
            warn!("copy_address failed for {:x}: {:?}", addr, e);
            e
        })
        .and(Ok(copy))
}

#[cfg(test)]
mod test {
    #[cfg(target_os="macos")]
    extern crate spawn_task_port;

    use super::*;
    use std::env;
    use std::io::{self, BufRead, BufReader};
    use std::path::PathBuf;
    use std::process::{Child, Command, Stdio};

    fn test_process_path() -> Option<PathBuf> {
        env::current_exe()
            .ok()
            .and_then(|p| p.parent().map(|p| p.with_file_name("test")
                                         .with_extension(env::consts::EXE_EXTENSION)))
    }

    #[cfg(not(target_os="macos"))]
    fn spawn_with_handle(cmd: &mut Command) -> io::Result<(Child, ProcessHandle)>
    {
        let mut child = try!(cmd
                             .spawn()
                             .map_err(|e| {
                                 println!("Error spawning test process '{:?}': {:?}", cmd, e);
                                 e
                             }));
        let handle = try!(child.try_into_process_handle());
        Ok((child, handle))
    }

    #[cfg(target_os="macos")]
    fn spawn_with_handle(cmd: &mut Command) -> io::Result<(Child, ProcessHandle)>
    {
        use self::spawn_task_port::CommandSpawnWithTask;
        cmd.spawn_get_task_port()
    }

    fn read_test_process(args: Option<&[&str]>) -> io::Result<Vec<u8>> {
        // Spawn a child process and attempt to read its memory.
        let path = test_process_path().unwrap();
        let mut cmd = Command::new(&path);
        {
            cmd.stdin(Stdio::piped())
                .stdout(Stdio::piped());
        }
        if let Some(a) = args {
            cmd.args(a);
        }
        let (mut child, handle) = try!(spawn_with_handle(&mut cmd));
        // The test program prints the address and size.
        // See `src/bin/test.rs` for its source.
        let reader = BufReader::new(child.stdout.take().unwrap());
        let line = reader.lines().next().unwrap().unwrap();
        let bits = line.split(' ').collect::<Vec<_>>();
        let addr = usize::from_str_radix(&bits[0][2..], 16).unwrap();
        let size = bits[1].parse::<usize>().unwrap();
        let mem = try!(copy_address(addr, size, &handle));
        try!(child.wait());
        Ok(mem)
    }

    #[test]
    fn test_read_small() {
        let mem = read_test_process(None).unwrap();
        assert_eq!(mem, (0..32u8).collect::<Vec<u8>>());
    }

    #[test]
    fn test_read_large() {
        // 5000 should be greater than a single page on most systems.
        const SIZE: usize = 5000;
        let arg = format!("{}", SIZE);
        let mem = read_test_process(Some(&[&arg])).unwrap();
        let expected = (0..SIZE).map(|v| (v % (u8::max_value() as usize + 1)) as u8).collect::<Vec<u8>>();
        assert_eq!(mem, expected);
    }
}
<|endoftext|>


<commit_before>//! This crate is a Rust port of Google's high-performance [SwissTable] hash
//! map, adapted to make it a drop-in replacement for Rust's standard `HashMap`
//! and `HashSet` types.
//!
//! The original C++ version of [SwissTable] can be found [here], and this
//! [CppCon talk] gives an overview of how the algorithm works.
//!
//! [SwissTable]: https://abseil.io/blog/20180927-swisstables
//! [here]: https://github.com/abseil/abseil-cpp/blob/master/absl/container/internal/raw_hash_set.h
//! [CppCon talk]: https://www.youtube.com/watch?v=ncHmEUmJZf4

#![no_std]
#![cfg_attr(
    feature = "nightly",
    feature(
        alloc_layout_extra,
        allocator_api,
        ptr_offset_from,
        test,
        core_intrinsics,
        dropck_eyepatch,
        cfg_doctest,
    )
)]
#![warn(missing_docs)]
#![allow(clippy::module_name_repetitions)]
#![warn(rust_2018_idioms)]

#[cfg(test)]
#[macro_use]
extern crate std;

#[cfg(has_extern_crate_alloc)]
#[cfg_attr(test, macro_use)]
extern crate alloc;
#[cfg(not(has_extern_crate_alloc))]
extern crate std as alloc;

#[cfg(feature = "nightly")]
#[cfg(doctest)]
doc_comment::doctest!("../README.md");

#[macro_use]
mod macros;

#[cfg(feature = "raw")]
/// Experimental and unsafe `RawTable` API. This module is only available if the
/// `raw` feature is enabled.
pub mod raw {
    // The RawTable API is still experimental and is not properly documented yet.
    #[allow(missing_docs)]
    #[path = "mod.rs"]
    mod inner;
    pub use inner::*;

    #[cfg(feature = "rayon")]
    pub mod rayon {
        pub use crate::external_trait_impls::rayon::raw::*;
    }
}
#[cfg(not(feature = "raw"))]
mod raw;

mod external_trait_impls;
mod map;
#[cfg(feature = "rustc-internal-api")]
mod rustc_entry;
mod scopeguard;
mod set;

pub mod hash_map {
    //! A hash map implemented with quadratic probing and SIMD lookup.
    pub use crate::map::*;

    #[cfg(feature = "rustc-internal-api")]
    pub use crate::rustc_entry::*;

    #[cfg(feature = "rayon")]
    /// [rayon]-based parallel iterator types for hash maps.
    /// You will rarely need to interact with it directly unless you have need
    /// to name one of the iterator types.
    ///
    /// [rayon]: https://docs.rs/rayon/1.0/rayon
    pub mod rayon {
        pub use crate::external_trait_impls::rayon::map::*;
    }
}
pub mod hash_set {
    //! A hash set implemented as a `HashMap` where the value is `()`.
    pub use crate::set::*;

    #[cfg(feature = "rayon")]
    /// [rayon]-based parallel iterator types for hash sets.
    /// You will rarely need to interact with it directly unless you have need
    /// to name one of the iterator types.
    ///
    /// [rayon]: https://docs.rs/rayon/1.0/rayon
    pub mod rayon {
        pub use crate::external_trait_impls::rayon::set::*;
    }
}

pub use crate::map::HashMap;
pub use crate::set::HashSet;

/// Augments `AllocErr` with a `CapacityOverflow` variant.
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum CollectionAllocErr {
    /// Error due to the computed capacity exceeding the collection's maximum
    /// (usually `isize::MAX` bytes).
    CapacityOverflow,
    /// Error due to the allocator.
    AllocErr {
        /// The layout of the allocation request that failed.
        layout: alloc::alloc::Layout,
    },
}
<commit_msg>Allow clippy::doc_markdown<commit_after>//! This crate is a Rust port of Google's high-performance [SwissTable] hash
//! map, adapted to make it a drop-in replacement for Rust's standard `HashMap`
//! and `HashSet` types.
//!
//! The original C++ version of [SwissTable] can be found [here], and this
//! [CppCon talk] gives an overview of how the algorithm works.
//!
//! [SwissTable]: https://abseil.io/blog/20180927-swisstables
//! [here]: https://github.com/abseil/abseil-cpp/blob/master/absl/container/internal/raw_hash_set.h
//! [CppCon talk]: https://www.youtube.com/watch?v=ncHmEUmJZf4

#![no_std]
#![cfg_attr(
    feature = "nightly",
    feature(
        alloc_layout_extra,
        allocator_api,
        ptr_offset_from,
        test,
        core_intrinsics,
        dropck_eyepatch,
        cfg_doctest,
    )
)]
#![warn(missing_docs)]
#![allow(clippy::module_name_repetitions, clippy::doc_markdown)]
#![warn(rust_2018_idioms)]

#[cfg(test)]
#[macro_use]
extern crate std;

#[cfg(has_extern_crate_alloc)]
#[cfg_attr(test, macro_use)]
extern crate alloc;
#[cfg(not(has_extern_crate_alloc))]
extern crate std as alloc;

#[cfg(feature = "nightly")]
#[cfg(doctest)]
doc_comment::doctest!("../README.md");

#[macro_use]
mod macros;

#[cfg(feature = "raw")]
/// Experimental and unsafe `RawTable` API. This module is only available if the
/// `raw` feature is enabled.
pub mod raw {
    // The RawTable API is still experimental and is not properly documented yet.
    #[allow(missing_docs)]
    #[path = "mod.rs"]
    mod inner;
    pub use inner::*;

    #[cfg(feature = "rayon")]
    pub mod rayon {
        pub use crate::external_trait_impls::rayon::raw::*;
    }
}
#[cfg(not(feature = "raw"))]
mod raw;

mod external_trait_impls;
mod map;
#[cfg(feature = "rustc-internal-api")]
mod rustc_entry;
mod scopeguard;
mod set;

pub mod hash_map {
    //! A hash map implemented with quadratic probing and SIMD lookup.
    pub use crate::map::*;

    #[cfg(feature = "rustc-internal-api")]
    pub use crate::rustc_entry::*;

    #[cfg(feature = "rayon")]
    /// [rayon]-based parallel iterator types for hash maps.
    /// You will rarely need to interact with it directly unless you have need
    /// to name one of the iterator types.
    ///
    /// [rayon]: https://docs.rs/rayon/1.0/rayon
    pub mod rayon {
        pub use crate::external_trait_impls::rayon::map::*;
    }
}
pub mod hash_set {
    //! A hash set implemented as a `HashMap` where the value is `()`.
    pub use crate::set::*;

    #[cfg(feature = "rayon")]
    /// [rayon]-based parallel iterator types for hash sets.
    /// You will rarely need to interact with it directly unless you have need
    /// to name one of the iterator types.
    ///
    /// [rayon]: https://docs.rs/rayon/1.0/rayon
    pub mod rayon {
        pub use crate::external_trait_impls::rayon::set::*;
    }
}

pub use crate::map::HashMap;
pub use crate::set::HashSet;

/// Augments `AllocErr` with a `CapacityOverflow` variant.
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum CollectionAllocErr {
    /// Error due to the computed capacity exceeding the collection's maximum
    /// (usually `isize::MAX` bytes).
    CapacityOverflow,
    /// Error due to the allocator.
    AllocErr {
        /// The layout of the allocation request that failed.
        layout: alloc::alloc::Layout,
    },
}
<|endoftext|>


<commit_before>#![deny(missing_debug_implementations)]

use janus_plugin_sys as ffi;
use bitflags::bitflags;
pub use debug::LogLevel;
pub use debug::log;
pub use jansson::{JanssonDecodingFlags, JanssonEncodingFlags, JanssonValue, RawJanssonValue};
pub use session::SessionWrapper;
pub use ffi::events::janus_eventhandler as EventHandler;
pub use ffi::plugin::janus_callbacks as PluginCallbacks;
pub use ffi::plugin::janus_plugin as Plugin;
pub use ffi::plugin::janus_plugin_result as RawPluginResult;
pub use ffi::plugin::janus_plugin_session as PluginSession;
use ffi::plugin::janus_plugin_result_type as PluginResultType;
use std::error::Error;
use std::fmt;
use std::ffi::CStr;
use std::mem;
use std::ops::Deref;
use std::os::raw::{c_char, c_int};
use std::ptr;

pub mod debug;
pub mod rtcp;
pub mod sdp;
pub mod session;
pub mod jansson;
pub mod utils;
pub mod refcount;

bitflags! {
    /// Flags that control which events an event handler receives.
    pub struct JanusEventType: u32 {
        const JANUS_EVENT_TYPE_SESSION   = 1 << 0;
        const JANUS_EVENT_TYPE_HANDLE    = 1 << 1;
        const JANUS_EVENT_TYPE_JSEP      = 1 << 3; // yes, really
        const JANUS_EVENT_TYPE_WEBRTC    = 1 << 4;
        const JANUS_EVENT_TYPE_MEDIA     = 1 << 5;
        const JANUS_EVENT_TYPE_PLUGIN    = 1 << 6;
        const JANUS_EVENT_TYPE_TRANSPORT = 1 << 7;
        const JANUS_EVENT_TYPE_CORE      = 1 << 8;
    }
}

/// An error emitted by the Janus core in response to a plugin pushing an event.
#[derive(Debug, Clone, Copy)]
pub struct JanusError {
    pub code: i32
}

/// A result from pushing an event to Janus core.
pub type JanusResult = Result<(), JanusError>;

impl JanusError {
    /// Returns Janus's description text for this error.
    pub fn to_cstr(self) -> &'static CStr {
        unsafe { CStr::from_ptr(ffi::janus_get_api_error(self.code)) }
    }
    /// Converts a Janus result code to either success or a potential error.
    pub fn from(val: i32) -> JanusResult {
        match val {
            0 => Ok(()),
            e => Err(JanusError { code: e })
        }
    }
}

impl Error for JanusError {
    fn description(&self) -> &'static str {
        self.to_cstr().to_str().unwrap()
    }
}

impl fmt::Display for JanusError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{} (code: {})", self.description(), self.code)
    }
}

/// A Janus plugin result; what a plugin returns to the gateway as a direct response to a signalling message.
#[derive(Debug)]
pub struct PluginResult {
    ptr: *mut RawPluginResult,
}

impl PluginResult {
    /// Creates a new plugin result.
    pub unsafe fn new(type_: PluginResultType, text: *const c_char, content: *mut RawJanssonValue) -> Self {
        Self { ptr: ffi::plugin::janus_plugin_result_new(type_, text, content) }
    }

    /// Creates a plugin result indicating a synchronously successful request. The provided response
    /// JSON will be passed back to the client.
    pub fn ok(response: JanssonValue) -> Self {
        unsafe { Self::new(PluginResultType::JANUS_PLUGIN_OK, ptr::null(), response.into_raw()) }
    }

    /// Creates a plugin result indicating an asynchronous request in progress. If provided, the hint text
    /// will be synchronously passed back to the client in the acknowledgement.
    pub fn ok_wait(hint: Option<&'static CStr>) -> Self {
        let hint_ptr = hint.map(|x| x.as_ptr()).unwrap_or_else(ptr::null);
        unsafe { Self::new(PluginResultType::JANUS_PLUGIN_OK_WAIT, hint_ptr, ptr::null_mut()) }
    }

    /// Creates a plugin result indicating an error. The provided error text will be synchronously passed
    /// back to the client.
    pub fn error(msg: &'static CStr) -> Self {
        unsafe { Self::new(PluginResultType::JANUS_PLUGIN_ERROR, msg.as_ptr(), ptr::null_mut()) }
    }

    /// Transfers ownership of this result to the wrapped raw pointer. The consumer is responsible for calling
    /// `janus_plugin_result_destroy` on the pointer when finished.
    pub fn into_raw(self) -> *mut RawPluginResult {
        let ptr = self.ptr;
        mem::forget(self);
        ptr
    }
}

impl Deref for PluginResult {
    type Target = RawPluginResult;

    fn deref(&self) -> &RawPluginResult {
        unsafe { &*self.ptr }
    }
}

impl Drop for PluginResult {
    fn drop(&mut self) {
        unsafe { ffi::plugin::janus_plugin_result_destroy(self.ptr) }
    }
}

unsafe impl Send for PluginResult {}

#[derive(Debug)]
/// Represents metadata about this library which Janus can query at runtime.
pub struct LibraryMetadata<'a> {
    pub api_version: c_int,
    pub version: c_int,
    pub version_str: &'a CStr,
    pub description: &'a CStr,
    pub name: &'a CStr,
    pub author: &'a CStr,
    pub package: &'a CStr,
}

/// Helper macro to produce a Janus plugin instance. Should be called with
/// a `LibraryMetadata` instance and a series of exported plugin callbacks.
#[macro_export]
macro_rules! build_plugin {
    ($md:expr, $($cb:ident),*) => {{
        extern "C" fn get_api_compatibility() -> c_int { $md.api_version }
        extern "C" fn get_version() -> c_int { $md.version }
        extern "C" fn get_version_string() -> *const c_char { $md.version_str.as_ptr() }
        extern "C" fn get_description() -> *const c_char { $md.description.as_ptr() }
        extern "C" fn get_name() -> *const c_char { $md.name.as_ptr() }
        extern "C" fn get_author() -> *const c_char { $md.author.as_ptr() }
        extern "C" fn get_package() -> *const c_char { $md.package.as_ptr() }
        $crate::Plugin {
            get_api_compatibility,
            get_version,
            get_version_string,
            get_description,
            get_name,
            get_author,
            get_package,
            $($cb,)*
        }
    }}
}

/// Macro to export a Janus plugin instance from this module.
#[macro_export]
macro_rules! export_plugin {
    ($pl:expr) => {
        /// Called by Janus to create an instance of this plugin, using the provided callbacks to dispatch events.
        #[no_mangle]
        pub extern "C" fn create() -> *const $crate::Plugin { $pl }
    }
}

/// Helper macro to produce a Janus event handler instance. Should be called with
/// a `LibraryMetadata` instance and a series of exported event handler callbacks.
#[macro_export]
macro_rules! build_eventhandler {
    ($md:expr, $mask:expr, $($cb:ident),*) => {{
        extern "C" fn get_api_compatibility() -> c_int { $md.api_version }
        extern "C" fn get_version() -> c_int { $md.version }
        extern "C" fn get_version_string() -> *const c_char { $md.version_str.as_ptr() }
        extern "C" fn get_description() -> *const c_char { $md.description.as_ptr() }
        extern "C" fn get_name() -> *const c_char { $md.name.as_ptr() }
        extern "C" fn get_author() -> *const c_char { $md.author.as_ptr() }
        extern "C" fn get_package() -> *const c_char { $md.package.as_ptr() }
        $crate::EventHandler {
            events_mask: $mask,
            get_api_compatibility,
            get_version,
            get_version_string,
            get_description,
            get_name,
            get_author,
            get_package,
            $($cb,)*
        }
    }}
}

/// Macro to export a Janus event handler instance from this module.
#[macro_export]
macro_rules! export_eventhandler {
    ($evh:expr) => {
        /// Called by Janus to create an instance of this event handler, using the provided callbacks to dispatch events.
        #[no_mangle]
        pub extern "C" fn create() -> *const $crate::EventHandler { $evh }
    }
}
<commit_msg>Expose new plugin structs at package root<commit_after>#![deny(missing_debug_implementations)]

use janus_plugin_sys as ffi;
use bitflags::bitflags;
pub use debug::LogLevel;
pub use debug::log;
pub use jansson::{JanssonDecodingFlags, JanssonEncodingFlags, JanssonValue, RawJanssonValue};
pub use session::SessionWrapper;
pub use ffi::events::janus_eventhandler as EventHandler;
pub use ffi::plugin::janus_callbacks as PluginCallbacks;
pub use ffi::plugin::janus_plugin as Plugin;
pub use ffi::plugin::janus_plugin_result as RawPluginResult;
pub use ffi::plugin::janus_plugin_session as PluginSession;
pub use ffi::plugin::janus_plugin_rtp as PluginRtpPacket;
pub use ffi::plugin::janus_plugin_rtcp as PluginRtcpPacket;
pub use ffi::plugin::janus_plugin_data as PluginDataPacket;
pub use ffi::plugin::janus_plugin_rtp_extensions as PluginRtpExtensions;
use ffi::plugin::janus_plugin_result_type as PluginResultType;
use std::error::Error;
use std::fmt;
use std::ffi::CStr;
use std::mem;
use std::ops::Deref;
use std::os::raw::{c_char, c_int};
use std::ptr;

pub mod debug;
pub mod rtcp;
pub mod sdp;
pub mod session;
pub mod jansson;
pub mod utils;
pub mod refcount;

bitflags! {
    /// Flags that control which events an event handler receives.
    pub struct JanusEventType: u32 {
        const JANUS_EVENT_TYPE_SESSION   = 1 << 0;
        const JANUS_EVENT_TYPE_HANDLE    = 1 << 1;
        const JANUS_EVENT_TYPE_JSEP      = 1 << 3; // yes, really
        const JANUS_EVENT_TYPE_WEBRTC    = 1 << 4;
        const JANUS_EVENT_TYPE_MEDIA     = 1 << 5;
        const JANUS_EVENT_TYPE_PLUGIN    = 1 << 6;
        const JANUS_EVENT_TYPE_TRANSPORT = 1 << 7;
        const JANUS_EVENT_TYPE_CORE      = 1 << 8;
    }
}

/// An error emitted by the Janus core in response to a plugin pushing an event.
#[derive(Debug, Clone, Copy)]
pub struct JanusError {
    pub code: i32
}

/// A result from pushing an event to Janus core.
pub type JanusResult = Result<(), JanusError>;

impl JanusError {
    /// Returns Janus's description text for this error.
    pub fn to_cstr(self) -> &'static CStr {
        unsafe { CStr::from_ptr(ffi::janus_get_api_error(self.code)) }
    }
    /// Converts a Janus result code to either success or a potential error.
    pub fn from(val: i32) -> JanusResult {
        match val {
            0 => Ok(()),
            e => Err(JanusError { code: e })
        }
    }
}

impl Error for JanusError {
    fn description(&self) -> &'static str {
        self.to_cstr().to_str().unwrap()
    }
}

impl fmt::Display for JanusError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{} (code: {})", self.description(), self.code)
    }
}

/// A Janus plugin result; what a plugin returns to the gateway as a direct response to a signalling message.
#[derive(Debug)]
pub struct PluginResult {
    ptr: *mut RawPluginResult,
}

impl PluginResult {
    /// Creates a new plugin result.
    pub unsafe fn new(type_: PluginResultType, text: *const c_char, content: *mut RawJanssonValue) -> Self {
        Self { ptr: ffi::plugin::janus_plugin_result_new(type_, text, content) }
    }

    /// Creates a plugin result indicating a synchronously successful request. The provided response
    /// JSON will be passed back to the client.
    pub fn ok(response: JanssonValue) -> Self {
        unsafe { Self::new(PluginResultType::JANUS_PLUGIN_OK, ptr::null(), response.into_raw()) }
    }

    /// Creates a plugin result indicating an asynchronous request in progress. If provided, the hint text
    /// will be synchronously passed back to the client in the acknowledgement.
    pub fn ok_wait(hint: Option<&'static CStr>) -> Self {
        let hint_ptr = hint.map(|x| x.as_ptr()).unwrap_or_else(ptr::null);
        unsafe { Self::new(PluginResultType::JANUS_PLUGIN_OK_WAIT, hint_ptr, ptr::null_mut()) }
    }

    /// Creates a plugin result indicating an error. The provided error text will be synchronously passed
    /// back to the client.
    pub fn error(msg: &'static CStr) -> Self {
        unsafe { Self::new(PluginResultType::JANUS_PLUGIN_ERROR, msg.as_ptr(), ptr::null_mut()) }
    }

    /// Transfers ownership of this result to the wrapped raw pointer. The consumer is responsible for calling
    /// `janus_plugin_result_destroy` on the pointer when finished.
    pub fn into_raw(self) -> *mut RawPluginResult {
        let ptr = self.ptr;
        mem::forget(self);
        ptr
    }
}

impl Deref for PluginResult {
    type Target = RawPluginResult;

    fn deref(&self) -> &RawPluginResult {
        unsafe { &*self.ptr }
    }
}

impl Drop for PluginResult {
    fn drop(&mut self) {
        unsafe { ffi::plugin::janus_plugin_result_destroy(self.ptr) }
    }
}

unsafe impl Send for PluginResult {}

#[derive(Debug)]
/// Represents metadata about this library which Janus can query at runtime.
pub struct LibraryMetadata<'a> {
    pub api_version: c_int,
    pub version: c_int,
    pub version_str: &'a CStr,
    pub description: &'a CStr,
    pub name: &'a CStr,
    pub author: &'a CStr,
    pub package: &'a CStr,
}

/// Helper macro to produce a Janus plugin instance. Should be called with
/// a `LibraryMetadata` instance and a series of exported plugin callbacks.
#[macro_export]
macro_rules! build_plugin {
    ($md:expr, $($cb:ident),*) => {{
        extern "C" fn get_api_compatibility() -> c_int { $md.api_version }
        extern "C" fn get_version() -> c_int { $md.version }
        extern "C" fn get_version_string() -> *const c_char { $md.version_str.as_ptr() }
        extern "C" fn get_description() -> *const c_char { $md.description.as_ptr() }
        extern "C" fn get_name() -> *const c_char { $md.name.as_ptr() }
        extern "C" fn get_author() -> *const c_char { $md.author.as_ptr() }
        extern "C" fn get_package() -> *const c_char { $md.package.as_ptr() }
        $crate::Plugin {
            get_api_compatibility,
            get_version,
            get_version_string,
            get_description,
            get_name,
            get_author,
            get_package,
            $($cb,)*
        }
    }}
}

/// Macro to export a Janus plugin instance from this module.
#[macro_export]
macro_rules! export_plugin {
    ($pl:expr) => {
        /// Called by Janus to create an instance of this plugin, using the provided callbacks to dispatch events.
        #[no_mangle]
        pub extern "C" fn create() -> *const $crate::Plugin { $pl }
    }
}

/// Helper macro to produce a Janus event handler instance. Should be called with
/// a `LibraryMetadata` instance and a series of exported event handler callbacks.
#[macro_export]
macro_rules! build_eventhandler {
    ($md:expr, $mask:expr, $($cb:ident),*) => {{
        extern "C" fn get_api_compatibility() -> c_int { $md.api_version }
        extern "C" fn get_version() -> c_int { $md.version }
        extern "C" fn get_version_string() -> *const c_char { $md.version_str.as_ptr() }
        extern "C" fn get_description() -> *const c_char { $md.description.as_ptr() }
        extern "C" fn get_name() -> *const c_char { $md.name.as_ptr() }
        extern "C" fn get_author() -> *const c_char { $md.author.as_ptr() }
        extern "C" fn get_package() -> *const c_char { $md.package.as_ptr() }
        $crate::EventHandler {
            events_mask: $mask,
            get_api_compatibility,
            get_version,
            get_version_string,
            get_description,
            get_name,
            get_author,
            get_package,
            $($cb,)*
        }
    }}
}

/// Macro to export a Janus event handler instance from this module.
#[macro_export]
macro_rules! export_eventhandler {
    ($evh:expr) => {
        /// Called by Janus to create an instance of this event handler, using the provided callbacks to dispatch events.
        #[no_mangle]
        pub extern "C" fn create() -> *const $crate::EventHandler { $evh }
    }
}
<|endoftext|>


<commit_before>//! This library provides extensible asynchronous retry behaviours
//! for use with the popular [`futures`](https://crates.io/crates/futures) crate
//! and the ecosystem of [`tokio`](https://tokio.rs/) libraries.
//!
//! # Installation
//!
//! Add this to your `Cargo.toml`:
//!
//! ```toml
//! [dependencies]
//! tokio-retry = "*"
//! ```
//!
//! By default, `tokio-retry` will work both with the [`Handle`](https://docs.rs/tokio-core/0.1.4/tokio_core/reactor/struct.Handle.html) type from
//! `tokio-core`, and the [`Timer`](https://docs.rs/tokio-timer/0.1.0/tokio_timer/struct.Timer.html) type from `tokio-timer`.
//! Both of these can be disabled or enabled via cargo feature flags:
//!
//! ```toml
//! [dependencies.tokio-retry]
//! version = "*"
//! default-features = false
//! # enable only tokio-core compatibility
//! features = ["tokio_core"]
//! ```
//!
//! # Examples
//!
//! ```rust
//! extern crate futures;
//! extern crate tokio_timer;
//! extern crate tokio_retry;
//!
//! use std::time::Duration;
//! use std::default::Default;
//! use futures::future::Future;
//! use tokio_timer::Timer;
//! use tokio_retry::RetryFuture;
//! use tokio_retry::strategy::{ExponentialBackoff, jitter};
//!
//! fn action() -> Result<u64, ()> {
//!     // do some real-world stuff here...
//!     Ok(42)
//! }
//!
//! pub fn main() {
//!     let retry_strategy = ExponentialBackoff::from_millis(10)
//!         .take(3)
//!         .map(jitter);
//!     let retry_future = RetryFuture::spawn(Timer::default(), retry_strategy, action);
//!     let retry_result = retry_future.wait();
//!
//!     assert_eq!(retry_result, Ok(42));
//! }
//! ```

extern crate either;
extern crate futures;
extern crate rand;
#[cfg(feature = "tokio_core")]
extern crate tokio_core;
#[cfg(feature = "tokio_timer")]
extern crate tokio_timer;
#[cfg(feature = "tokio_service")]
extern crate tokio_service;

mod action;
mod future;
#[cfg(feature = "tokio_service")]
mod middleware;
/// Assorted retry strategies including fixed interval and exponential back-off.
pub mod strategy;

pub use action::Action;
pub use future::{Sleep, RetryError, RetryFuture};
#[cfg(feature = "tokio_service")]
pub use middleware::{RetryService, ServiceRetryFuture, ServiceAction};
<commit_msg>simplify imports in example<commit_after>//! This library provides extensible asynchronous retry behaviours
//! for use with the popular [`futures`](https://crates.io/crates/futures) crate
//! and the ecosystem of [`tokio`](https://tokio.rs/) libraries.
//!
//! # Installation
//!
//! Add this to your `Cargo.toml`:
//!
//! ```toml
//! [dependencies]
//! tokio-retry = "*"
//! ```
//!
//! By default, `tokio-retry` will work both with the [`Handle`](https://docs.rs/tokio-core/0.1.4/tokio_core/reactor/struct.Handle.html) type from
//! `tokio-core`, and the [`Timer`](https://docs.rs/tokio-timer/0.1.0/tokio_timer/struct.Timer.html) type from `tokio-timer`.
//! Both of these can be disabled or enabled via cargo feature flags:
//!
//! ```toml
//! [dependencies.tokio-retry]
//! version = "*"
//! default-features = false
//! # enable only tokio-core compatibility
//! features = ["tokio_core"]
//! ```
//!
//! # Examples
//!
//! ```rust
//! extern crate futures;
//! extern crate tokio_timer;
//! extern crate tokio_retry;
//!
//! use futures::Future;
//! use tokio_timer::Timer;
//! use tokio_retry::RetryFuture;
//! use tokio_retry::strategy::{ExponentialBackoff, jitter};
//!
//! fn action() -> Result<u64, ()> {
//!     // do some real-world stuff here...
//!     Ok(42)
//! }
//!
//! pub fn main() {
//!     let retry_strategy = ExponentialBackoff::from_millis(10)
//!         .take(3)
//!         .map(jitter);
//!     let retry_future = RetryFuture::spawn(Timer::default(), retry_strategy, action);
//!     let retry_result = retry_future.wait();
//!
//!     assert_eq!(retry_result, Ok(42));
//! }
//! ```

extern crate either;
extern crate futures;
extern crate rand;
#[cfg(feature = "tokio_core")]
extern crate tokio_core;
#[cfg(feature = "tokio_timer")]
extern crate tokio_timer;
#[cfg(feature = "tokio_service")]
extern crate tokio_service;

mod action;
mod future;
#[cfg(feature = "tokio_service")]
mod middleware;
/// Assorted retry strategies including fixed interval and exponential back-off.
pub mod strategy;

pub use action::Action;
pub use future::{Sleep, RetryError, RetryFuture};
#[cfg(feature = "tokio_service")]
pub use middleware::{RetryService, ServiceRetryFuture, ServiceAction};
<|endoftext|>


<commit_before>//! Jamal provides a bi-directional interface for transformations between json and yaml documents.
extern crate serde_json;
extern crate serde_yaml;

use serde_json::Value as JsonValue;
use serde_yaml::Value as YamlValue;
use std::error::Error as StdError;
use std::num::ParseFloatError;
use std::collections::BTreeMap;

/// represents potential errors that can happen during the transformation process
#[derive(Debug)]
pub enum Error {
    /// Occurs when an attempt to parse a `serde_yaml::Real` as an `f64` fails
    ParseFloat(ParseFloatError),
    /// Occurs when a transformation is requested for an invalid value
    InvalidValue,
    /// Occurs when an unsupported transformation is requested
    UnsupportedValue(YamlValue)
}

impl StdError for Error {
    fn description(&self) -> &str {
        match *self {
            Error::ParseFloat(ref e) => e.description(),
            Error::InvalidValue => "invalid value",
            Error::UnsupportedValue(_) => "supported value",
        }
    }

    fn cause(&self) -> Option<&StdError> {
        match *self {
            Error::ParseFloat(ref e) => Some(e),
            _ => None,
        }
    }
}

impl ::std::fmt::Display for Error {
    fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
        write!(f, "{:?}", self)
    }
}

/// A result type with a fixed type for `jamal::Errors`
pub type Result<T> = std::result::Result<T, Error>;

impl From<ParseFloatError> for Error {
    fn from(error: ParseFloatError) -> Error {
        Error::ParseFloat(error)
    }
}

/// converts a `serde_json::Value` into a `serde_yaml::Value`
pub fn to_yaml(json: &JsonValue) -> Result<YamlValue> {
    match json {
        &JsonValue::Null => Ok(YamlValue::Null),
        &JsonValue::Bool(ref value) => Ok(YamlValue::Boolean(value.clone())),
        &JsonValue::I64(ref value) => Ok(YamlValue::Integer(value.clone())),
        &JsonValue::U64(ref value) => Ok(YamlValue::Integer(value.clone() as i64)),
        &JsonValue::F64(ref value) => Ok(YamlValue::Real(value.to_string())),
        &JsonValue::String(ref value) => Ok(YamlValue::String(value.clone())),
        &JsonValue::Array(ref values) => {
            let mut yaml_values = vec![];
            for value in values {
                yaml_values.push(try!(to_yaml(&value)));
            }
            Ok(YamlValue::Array(yaml_values))
        }
        &JsonValue::Object(ref value) => {
            let mut yaml_value = BTreeMap::new();
            for (k, v) in value {
                let yaml = try!(to_yaml(&v));
                yaml_value.insert(YamlValue::String(k.clone()), yaml);
            }
            Ok(YamlValue::Hash(yaml_value))
        }
    }
}

/// converts a `serde_yaml::Value` into a `serde_json::Value`
pub fn to_json(yaml: &YamlValue) -> Result<JsonValue> {
    match yaml {
        &YamlValue::Real(ref value) => Ok(JsonValue::F64(try!(value.parse::<f64>()))),
        &YamlValue::Integer(ref value) => Ok(JsonValue::I64(value.clone())),
        &YamlValue::String(ref value) => Ok(JsonValue::String(value.clone())),
        &YamlValue::Boolean(ref value) => Ok(JsonValue::Bool(value.clone())),
        &YamlValue::Array(ref values) => {
            let mut json_values = vec![];
            for value in values {
                let json = try!(to_json(&value));
                json_values.push(json);
            }
            Ok(JsonValue::Array(json_values))
        }
        &YamlValue::Hash(ref value) => {
            let mut json_value = BTreeMap::new();
            for (k, v) in value {
                match k {
                    &YamlValue::String(ref key) => {
                        let json = try!(to_json(&v));
                        json_value.insert(key.clone(), json);
                    }
                    unsupported => return Err(Error::UnsupportedValue(unsupported.clone())),
                }
            }
            Ok(JsonValue::Object(json_value))
        }
        alias @ &YamlValue::Alias(_) => Err(Error::UnsupportedValue(alias.clone())),
        &YamlValue::Null => Ok(JsonValue::Null),
        &YamlValue::BadValue => Err(Error::InvalidValue),
    }
}

#[cfg(test)]
mod tests {
    use std::collections::BTreeMap;
    use super::serde_json;
    use super::serde_yaml;
    use super::{to_json, to_yaml};

    #[test]
    fn json_str_vec_to_yaml() {
        let input = serde_json::to_value(&vec!["foo"]);
        let output = serde_yaml::Value::Array(vec![serde_yaml::Value::String("foo".to_owned())]);
        assert_eq!(to_yaml(&input).unwrap(), output);
    }

    #[test]
    fn yaml_str_vec_to_json() {
        let input = serde_yaml::to_value(&vec!["foo"]);
        let output = serde_json::Value::Array(vec![serde_json::Value::String("foo".to_owned())]);
        assert_eq!(to_json(&input).unwrap(), output);
    }

    #[test]
    fn json_obj_to_yaml() {
        let mut json_obj = BTreeMap::new();
        json_obj.insert("foo", 1);
        let input = serde_json::to_value(&json_obj);

        let mut yaml_obj = BTreeMap::new();
        yaml_obj.insert(serde_yaml::Value::String("foo".to_owned()), serde_yaml::Value::Integer(1));
        let output = serde_yaml::Value::Hash(yaml_obj);
        assert_eq!(to_yaml(&input).unwrap(), output);
    }

    #[test]
    fn yaml_obj_to_json() {
        let mut yaml_obj = BTreeMap::new();
        yaml_obj.insert("foo", 1);
        let input = serde_yaml::to_value(&yaml_obj);

        let mut json_obj = BTreeMap::new();
        json_obj.insert("foo".to_owned(), serde_json::Value::I64(1));
        let output = serde_json::Value::Object(json_obj);
        assert_eq!(to_json(&input).unwrap(), output);
    }

    #[test]
    fn json_bool_to_yaml() {
        let input = serde_json::to_value(&true);
        let output = serde_yaml::Value::Boolean(true);
        assert_eq!(to_yaml(&input).unwrap(), output);
    }

    #[test]
    fn yaml_bool_to_json() {
        let input = serde_yaml::to_value(&true);
        let output = serde_json::Value::Bool(true);
        assert_eq!(to_json(&input).unwrap(), output);
    }
}
<commit_msg>increase test cov<commit_after>//! Jamal provides a bi-directional interface for transformations between json and yaml documents.
extern crate serde_json;
extern crate serde_yaml;

use serde_json::Value as JsonValue;
use serde_yaml::Value as YamlValue;
use std::error::Error as StdError;
use std::num::ParseFloatError;
use std::collections::BTreeMap;

/// represents potential errors that can happen during the transformation process
#[derive(Debug)]
pub enum Error {
    /// Occurs when an attempt to parse a `serde_yaml::Real` as an `f64` fails
    ParseFloat(ParseFloatError),
    /// Occurs when a transformation is requested for an invalid value
    InvalidValue,
    /// Occurs when an unsupported transformation is requested
    UnsupportedValue(YamlValue)
}

impl StdError for Error {
    fn description(&self) -> &str {
        match *self {
            Error::ParseFloat(ref e) => e.description(),
            Error::InvalidValue => "invalid value",
            Error::UnsupportedValue(_) => "supported value",
        }
    }

    fn cause(&self) -> Option<&StdError> {
        match *self {
            Error::ParseFloat(ref e) => Some(e),
            _ => None,
        }
    }
}

impl ::std::fmt::Display for Error {
    fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
        write!(f, "{:?}", self)
    }
}

/// A result type with a fixed type for `jamal::Errors`
pub type Result<T> = std::result::Result<T, Error>;

impl From<ParseFloatError> for Error {
    fn from(error: ParseFloatError) -> Error {
        Error::ParseFloat(error)
    }
}

/// converts a `serde_json::Value` into a `serde_yaml::Value`
pub fn to_yaml(json: &JsonValue) -> Result<YamlValue> {
    match json {
        &JsonValue::Null => Ok(YamlValue::Null),
        &JsonValue::Bool(ref value) => Ok(YamlValue::Boolean(value.clone())),
        &JsonValue::I64(ref value) => Ok(YamlValue::Integer(value.clone())),
        &JsonValue::U64(ref value) => Ok(YamlValue::Integer(value.clone() as i64)),
        &JsonValue::F64(ref value) => Ok(YamlValue::Real(value.to_string())),
        &JsonValue::String(ref value) => Ok(YamlValue::String(value.clone())),
        &JsonValue::Array(ref values) => {
            let mut yaml_values = vec![];
            for value in values {
                yaml_values.push(try!(to_yaml(&value)));
            }
            Ok(YamlValue::Array(yaml_values))
        }
        &JsonValue::Object(ref value) => {
            let mut yaml_value = BTreeMap::new();
            for (k, v) in value {
                let yaml = try!(to_yaml(&v));
                yaml_value.insert(YamlValue::String(k.clone()), yaml);
            }
            Ok(YamlValue::Hash(yaml_value))
        }
    }
}

/// converts a `serde_yaml::Value` into a `serde_json::Value`
pub fn to_json(yaml: &YamlValue) -> Result<JsonValue> {
    match yaml {
        &YamlValue::Real(ref value) => Ok(JsonValue::F64(try!(value.parse::<f64>()))),
        &YamlValue::Integer(ref value) => Ok(JsonValue::I64(value.clone())),
        &YamlValue::String(ref value) => Ok(JsonValue::String(value.clone())),
        &YamlValue::Boolean(ref value) => Ok(JsonValue::Bool(value.clone())),
        &YamlValue::Array(ref values) => {
            let mut json_values = vec![];
            for value in values {
                let json = try!(to_json(&value));
                json_values.push(json);
            }
            Ok(JsonValue::Array(json_values))
        }
        &YamlValue::Hash(ref value) => {
            let mut json_value = BTreeMap::new();
            for (k, v) in value {
                match k {
                    &YamlValue::String(ref key) => {
                        let json = try!(to_json(&v));
                        json_value.insert(key.clone(), json);
                    }
                    unsupported => return Err(Error::UnsupportedValue(unsupported.clone())),
                }
            }
            Ok(JsonValue::Object(json_value))
        }
        alias @ &YamlValue::Alias(_) => Err(Error::UnsupportedValue(alias.clone())),
        &YamlValue::Null => Ok(JsonValue::Null),
        &YamlValue::BadValue => Err(Error::InvalidValue),
    }
}

#[cfg(test)]
mod tests {
    use std::collections::BTreeMap;
    use super::serde_json;
    use super::serde_yaml;
    use super::{to_json, to_yaml};

    #[test]
    fn json_str_vec_to_yaml() {
        let input = serde_json::to_value(&vec!["foo"]);
        let output = serde_yaml::Value::Array(vec![serde_yaml::Value::String("foo".to_owned())]);
        assert_eq!(to_yaml(&input).unwrap(), output);
    }

    #[test]
    fn yaml_str_vec_to_json() {
        let input = serde_yaml::to_value(&vec!["foo"]);
        let output = serde_json::Value::Array(vec![serde_json::Value::String("foo".to_owned())]);
        assert_eq!(to_json(&input).unwrap(), output);
    }

    #[test]
    fn json_obj_to_yaml() {
        let mut json_obj = BTreeMap::new();
        json_obj.insert("foo", 1);
        let input = serde_json::to_value(&json_obj);

        let mut yaml_obj = BTreeMap::new();
        yaml_obj.insert(serde_yaml::Value::String("foo".to_owned()), serde_yaml::Value::Integer(1));
        let output = serde_yaml::Value::Hash(yaml_obj);
        assert_eq!(to_yaml(&input).unwrap(), output);
    }

    #[test]
    fn yaml_obj_to_json() {
        let mut yaml_obj = BTreeMap::new();
        yaml_obj.insert("foo", 1);
        let input = serde_yaml::to_value(&yaml_obj);

        let mut json_obj = BTreeMap::new();
        json_obj.insert("foo".to_owned(), serde_json::Value::I64(1));
        let output = serde_json::Value::Object(json_obj);
        assert_eq!(to_json(&input).unwrap(), output);
    }

    #[test]
    fn json_bool_to_yaml() {
        let input = serde_json::to_value(&true);
        let output = serde_yaml::Value::Boolean(true);
        assert_eq!(to_yaml(&input).unwrap(), output);
    }

    #[test]
    fn yaml_bool_to_json() {
        let input = serde_yaml::to_value(&true);
        let output = serde_json::Value::Bool(true);
        assert_eq!(to_json(&input).unwrap(), output);
    }

    #[test]
    fn json_null_to_yaml() {
        let input = serde_json::Value::Null;
        let output = serde_yaml::Value::Null;
        assert_eq!(to_yaml(&input).unwrap(), output);
    }

    #[test]
    fn yaml_null_to_json() {
        let input = serde_yaml::Value::Null;
        let output = serde_json::Value::Null;
        assert_eq!(to_json(&input).unwrap(), output);
    }
}
<|endoftext|>