<commit_before>//! HTTP Proxy client server

use std::{
    collections::HashMap,
    convert::Infallible,
    future::Future,
    io,
    net::{IpAddr, SocketAddr},
    pin::Pin,
    sync::Arc,
    task::{self, Poll},
};

use futures::{
    future,
    future::{BoxFuture, Either},
    FutureExt,
};
use hyper::{
    client::connect::{Connected, Connection},
    server::conn::AddrStream,
    service::{make_service_fn, service_fn},
    upgrade::Upgraded,
    Body,
    Client,
    Method,
    Request,
    Response,
    Server,
    StatusCode,
    Uri,
};
use log::{debug, error, info, trace};
use pin_project::pin_project;
use tokio;
use tower;

use super::{CryptoStream, STcpStream};
use crate::{
    config::ServerConfig,
    context::SharedContext,
    relay::{
        loadbalancing::server::{ping, LoadBalancer, PingBalancer},
        socks5::Address,
    },
};

#[derive(Clone)]
struct ShadowSocksConnector {
    context: SharedContext,
    svr_cfg: Arc<ServerConfig>,
}

impl ShadowSocksConnector {
    fn new(context: SharedContext, svr_cfg: Arc<ServerConfig>) -> ShadowSocksConnector {
        ShadowSocksConnector { context, svr_cfg }
    }
}

impl tower::Service<Address> for ShadowSocksConnector {
    type Error = io::Error;
    type Future = ShadowSocksConnecting;
    type Response = CryptoStream<STcpStream>;

    fn poll_ready(&mut self, _cx: &mut task::Context<'_>) -> Poll<Result<(), Self::Error>> {
        Poll::Ready(Ok(()))
    }

    fn call(&mut self, addr: Address) -> Self::Future {
        let svr_cfg = self.svr_cfg.clone();
        let context = self.context.clone();

        ShadowSocksConnecting {
            fut: async move {
                let stream = super::connect_proxy_server(&*context, &*svr_cfg).await?;
                super::proxy_server_handshake(stream, svr_cfg.clone(), &addr).await
            }
            .boxed(),
        }
    }
}

impl tower::Service<Uri> for ShadowSocksConnector {
    type Error = io::Error;
    type Future = ShadowSocksConnecting;
    type Response = CryptoStream<STcpStream>;

    fn poll_ready(&mut self, _cx: &mut task::Context<'_>) -> Poll<Result<(), Self::Error>> {
        Poll::Ready(Ok(()))
    }

    fn call(&mut self, dst: Uri) -> Self::Future {
        let svr_cfg = self.svr_cfg.clone();
        let context = self.context.clone();

        ShadowSocksConnecting {
            fut: async move {
                match host_addr(&dst) {
                    None => {
                        use std::io::{Error, ErrorKind};

                        error!("HTTP target URI must be a valid address, but found: {}", dst);

                        let err = Error::new(ErrorKind::Other, "URI must be a valid Address");
                        Err(err)
                    }
                    Some(addr) => {
                        let stream = super::connect_proxy_server(&*context, &*svr_cfg).await?;
                        super::proxy_server_handshake(stream, svr_cfg.clone(), &addr).await
                    }
                }
            }
            .boxed(),
        }
    }
}

#[pin_project]
struct ShadowSocksConnecting {
    #[pin]
    fut: BoxFuture<'static, io::Result<CryptoStream<STcpStream>>>,
}

impl Future for ShadowSocksConnecting {
    type Output = io::Result<CryptoStream<STcpStream>>;

    fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
        self.project().fut.poll(cx)
    }
}

fn host_addr(uri: &Uri) -> Option<Address> {
    let port = match uri.port_u16() {
        Some(p) => p,
        None => {
            match uri.scheme_str() {
                None => 80, // Assume it is http
                Some("http") => 80,
                Some("https") => 443,
                _ => return None, // Not supported
            }
        }
    };

    match uri.host() {
        None => None,
        Some(host_str) => match host_str.parse::<IpAddr>() {
            Ok(ip) => Some(Address::from(SocketAddr::new(ip, port))),
            Err(..) => Some(Address::DomainNameAddress(host_str.to_owned(), port)),
        },
    }
}

impl Connection for CryptoStream<STcpStream> {
    fn connected(&self) -> Connected {
        Connected::new()
    }
}

async fn establish_connect_tunnel(
    upgraded: Upgraded,
    mut stream: CryptoStream<STcpStream>,
    svr_cfg: Arc<ServerConfig>,
    client_addr: SocketAddr,
    addr: Address,
) {
    use tokio::io::{copy, split};

    let (mut r, mut w) = split(upgraded);
    let (mut svr_r, mut svr_w) = stream.split();

    let rhalf = copy(&mut r, &mut svr_w);
    let whalf = copy(&mut svr_r, &mut w);

    debug!(
        "CONNECT relay established {} <-> {} ({})",
        client_addr,
        svr_cfg.addr(),
        addr
    );

    match future::select(rhalf, whalf).await {
        Either::Left((Ok(..), _)) => trace!("CONNECT relay {} -> {} ({}) closed", client_addr, svr_cfg.addr(), addr),
        Either::Left((Err(err), _)) => trace!(
            "CONNECT relay {} -> {} ({}) closed with error {:?}",
            client_addr,
            svr_cfg.addr(),
            addr,
            err,
        ),
        Either::Right((Ok(..), _)) => trace!("CONNECT relay {} <- {} ({}) closed", client_addr, svr_cfg.addr(), addr),
        Either::Right((Err(err), _)) => trace!(
            "CONNECT relay {} <- {} ({}) closed with error {:?}",
            client_addr,
            svr_cfg.addr(),
            addr,
            err,
        ),
    }

    debug!("CONNECT relay {} <-> {} ({}) closed", client_addr, svr_cfg.addr(), addr);
}

type ShadowSocksClient = Client<ShadowSocksConnector>;

async fn server_dispatch(
    context: SharedContext,
    req: Request<Body>,
    svr_cfg: Arc<ServerConfig>,
    client_addr: SocketAddr,
    client: ShadowSocksClient,
) -> Result<Response<Body>, io::Error> {
    if Method::CONNECT == req.method() {
        // Establish a TCP tunnel
        // https://tools.ietf.org/html/draft-luotonen-web-proxy-tunneling-01

        match host_addr(req.uri()) {
            Some(addr) => {
                debug!("CONNECT {}", addr);

                // Connect to Shadowsocks' remote
                //
                // FIXME: What STATUS should I return for connection error?
                let stream = super::connect_proxy_server(&*context, &*svr_cfg).await?;
                let stream = super::proxy_server_handshake(stream, svr_cfg.clone(), &addr).await?;

                debug!(
                    "CONNECT relay connected {} <-> {} ({})",
                    client_addr,
                    svr_cfg.addr(),
                    addr
                );

                // Upgrade to a TCP tunnel
                //
                // Note: only after client received an empty body with STATUS_OK can the
                // connection be upgraded, so we can't return a response inside
                // `on_upgrade` future.
                tokio::spawn(async move {
                    match req.into_body().on_upgrade().await {
                        Ok(upgraded) => {
                            trace!(
                                "CONNECT tunnel upgrade success, {} <-> {} ({})",
                                client_addr,
                                svr_cfg.addr(),
                                addr
                            );

                            establish_connect_tunnel(upgraded, stream, svr_cfg, client_addr, addr).await
                        }
                        Err(e) => {
                            error!(
                                "Failed to upgrade TCP tunnel {} <-> {} ({}), error: {}",
                                client_addr,
                                svr_cfg.addr(),
                                addr,
                                e
                            );
                        }
                    }
                });

                let resp = Response::builder()
                    .header("Proxy-Agent", format!("ShadowSocks/{}", crate::VERSION))
                    .body(Body::empty())
                    .unwrap();

                Ok(resp)
            }
            None => {
                error!("HTTP CONNECT URI is not a valid address. URI: {}", req.uri());

                let mut resp = Response::new(Body::from("CONNECT URI must be a valid Address"));
                *resp.status_mut() = StatusCode::BAD_REQUEST;

                Ok(resp)
            }
        }
    } else {
        let method = req.method().clone();

        let host = match host_addr(req.uri()) {
            None => {
                error!("HTTP {} URI is not a valid address. URI: {}", method, req.uri());

                let mut resp = Response::new(Body::from("URI must be a valid Address"));
                *resp.status_mut() = StatusCode::BAD_REQUEST;

                return Ok(resp);
            }
            Some(h) => h,
        };

        debug!("HTTP {} {}", method, host);

        let res = match client.request(req).await {
            Ok(res) => res,
            Err(err) => {
                error!(
                    "HTTP {} {} <-> {} ({}) relay failed, error: {}",
                    method,
                    client_addr,
                    svr_cfg.addr(),
                    host,
                    err
                );

                let mut resp = Response::new(Body::from(format!("Relay failed to {}", host)));
                *resp.status_mut() = StatusCode::INTERNAL_SERVER_ERROR;

                return Ok(resp);
            }
        };

        debug!(
            "HTTP {} relay {} <-> {} ({}) finished",
            method,
            client_addr,
            svr_cfg.addr(),
            host
        );

        Ok(res)
    }
}

/// Starts a TCP local server with HTTP proxy protocol
pub async fn run(context: SharedContext) -> io::Result<()> {
    let local_addr = *context.config().local.as_ref().expect("Missing local config");

    let mut servers = PingBalancer::new(context.clone(), ping::ServerType::Tcp).await;

    let mut server_clients = HashMap::new();

    // Create HTTP clients for each remote servers
    for svr_cfg in servers.servers() {
        let addr_str = svr_cfg.addr().to_string();
        let client = Client::builder().build::<_, Body>(ShadowSocksConnector::new(context.clone(), svr_cfg));
        server_clients.insert(addr_str, client);
    }

    let make_service = make_service_fn(|socket: &AddrStream| {
        let client_addr = socket.remote_addr();
        let svr_cfg = servers.pick_server();
        let context = context.clone();

        // Keep connections for clients
        let addr_str = svr_cfg.addr().to_string();
        let client = server_clients.get(&addr_str).unwrap().clone();

        async move {
            Ok::<_, Infallible>(service_fn(move |req: Request<Body>| {
                server_dispatch(context.clone(), req, svr_cfg.clone(), client_addr, client.clone())
            }))
        }
    });

    let server = Server::bind(&local_addr).serve(make_service);

    let actual_local_addr = server.local_addr();

    info!("ShadowSocks HTTP Listening on {}", actual_local_addr);

    if let Err(err) = server.await {
        use std::io::{Error, ErrorKind};

        error!("Hyper Server error: {}", err);
        return Err(Error::new(ErrorKind::Other, err));
    }

    Ok(())
}
<commit_msg>Optimized http local code<commit_after>//! HTTP Proxy client server

use std::{
    collections::HashMap,
    convert::Infallible,
    future::Future,
    io,
    net::{IpAddr, SocketAddr},
    pin::Pin,
    sync::Arc,
    task::{self, Poll},
};

use futures::{
    future,
    future::{BoxFuture, Either},
    FutureExt,
};
use hyper::{
    client::connect::{Connected, Connection},
    server::conn::AddrStream,
    service::{make_service_fn, service_fn},
    upgrade::Upgraded,
    Body,
    Client,
    Method,
    Request,
    Response,
    Server,
    StatusCode,
    Uri,
};
use log::{debug, error, info, trace};
use pin_project::pin_project;
use tokio;
use tower;

use super::{CryptoStream, STcpStream};
use crate::{
    config::ServerConfig,
    context::SharedContext,
    relay::{
        loadbalancing::server::{ping, LoadBalancer, PingBalancer},
        socks5::Address,
    },
};

#[derive(Clone)]
struct ShadowSocksConnector {
    context: SharedContext,
    svr_cfg: Arc<ServerConfig>,
}

impl ShadowSocksConnector {
    fn new(context: SharedContext, svr_cfg: Arc<ServerConfig>) -> ShadowSocksConnector {
        ShadowSocksConnector { context, svr_cfg }
    }
}

impl tower::Service<Address> for ShadowSocksConnector {
    type Error = io::Error;
    type Future = ShadowSocksConnecting;
    type Response = CryptoStream<STcpStream>;

    fn poll_ready(&mut self, _cx: &mut task::Context<'_>) -> Poll<Result<(), Self::Error>> {
        Poll::Ready(Ok(()))
    }

    fn call(&mut self, addr: Address) -> Self::Future {
        let svr_cfg = self.svr_cfg.clone();
        let context = self.context.clone();

        ShadowSocksConnecting {
            fut: async move {
                let stream = super::connect_proxy_server(&*context, &*svr_cfg).await?;
                super::proxy_server_handshake(stream, svr_cfg.clone(), &addr).await
            }
            .boxed(),
        }
    }
}

impl tower::Service<Uri> for ShadowSocksConnector {
    type Error = io::Error;
    type Future = ShadowSocksConnecting;
    type Response = CryptoStream<STcpStream>;

    fn poll_ready(&mut self, _cx: &mut task::Context<'_>) -> Poll<Result<(), Self::Error>> {
        Poll::Ready(Ok(()))
    }

    fn call(&mut self, dst: Uri) -> Self::Future {
        let svr_cfg = self.svr_cfg.clone();
        let context = self.context.clone();

        ShadowSocksConnecting {
            fut: async move {
                match host_addr(&dst) {
                    None => {
                        use std::io::{Error, ErrorKind};

                        error!("HTTP target URI must be a valid address, but found: {}", dst);

                        let err = Error::new(ErrorKind::Other, "URI must be a valid Address");
                        Err(err)
                    }
                    Some(addr) => {
                        let stream = super::connect_proxy_server(&*context, &*svr_cfg).await?;
                        super::proxy_server_handshake(stream, svr_cfg.clone(), &addr).await
                    }
                }
            }
            .boxed(),
        }
    }
}

#[pin_project]
struct ShadowSocksConnecting {
    #[pin]
    fut: BoxFuture<'static, io::Result<CryptoStream<STcpStream>>>,
}

impl Future for ShadowSocksConnecting {
    type Output = io::Result<CryptoStream<STcpStream>>;

    fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
        self.project().fut.poll(cx)
    }
}

fn host_addr(uri: &Uri) -> Option<Address> {
    let port = match uri.port_u16() {
        Some(p) => p,
        None => {
            match uri.scheme_str() {
                None => 80, // Assume it is http
                Some("http") => 80,
                Some("https") => 443,
                _ => return None, // Not supported
            }
        }
    };

    match uri.host() {
        None => None,
        Some(host_str) => match host_str.parse::<IpAddr>() {
            Ok(ip) => Some(Address::from(SocketAddr::new(ip, port))),
            Err(..) => Some(Address::DomainNameAddress(host_str.to_owned(), port)),
        },
    }
}

impl Connection for CryptoStream<STcpStream> {
    fn connected(&self) -> Connected {
        Connected::new()
    }
}

async fn establish_connect_tunnel(
    upgraded: Upgraded,
    mut stream: CryptoStream<STcpStream>,
    svr_cfg: Arc<ServerConfig>,
    client_addr: SocketAddr,
    addr: Address,
) {
    use tokio::io::{copy, split};

    let (mut r, mut w) = split(upgraded);
    let (mut svr_r, mut svr_w) = stream.split();

    let rhalf = copy(&mut r, &mut svr_w);
    let whalf = copy(&mut svr_r, &mut w);

    debug!(
        "CONNECT relay established {} <-> {} ({})",
        client_addr,
        svr_cfg.addr(),
        addr
    );

    match future::select(rhalf, whalf).await {
        Either::Left((Ok(..), _)) => trace!("CONNECT relay {} -> {} ({}) closed", client_addr, svr_cfg.addr(), addr),
        Either::Left((Err(err), _)) => trace!(
            "CONNECT relay {} -> {} ({}) closed with error {:?}",
            client_addr,
            svr_cfg.addr(),
            addr,
            err,
        ),
        Either::Right((Ok(..), _)) => trace!("CONNECT relay {} <- {} ({}) closed", client_addr, svr_cfg.addr(), addr),
        Either::Right((Err(err), _)) => trace!(
            "CONNECT relay {} <- {} ({}) closed with error {:?}",
            client_addr,
            svr_cfg.addr(),
            addr,
            err,
        ),
    }

    debug!("CONNECT relay {} <-> {} ({}) closed", client_addr, svr_cfg.addr(), addr);
}

type ShadowSocksClient = Client<ShadowSocksConnector>;

async fn server_dispatch(
    context: SharedContext,
    req: Request<Body>,
    svr_cfg: Arc<ServerConfig>,
    client_addr: SocketAddr,
    client: ShadowSocksClient,
) -> Result<Response<Body>, io::Error> {
    // Parse URI
    //
    // Proxy request URI must contains a host
    let host = match host_addr(req.uri()) {
        None => {
            error!("HTTP {} URI is not a valid address. URI: {}", req.method(), req.uri());

            let mut resp = Response::new(Body::from("URI must be a valid Address"));
            *resp.status_mut() = StatusCode::BAD_REQUEST;

            return Ok(resp);
        }
        Some(h) => h,
    };

    if Method::CONNECT == req.method() {
        // Establish a TCP tunnel
        // https://tools.ietf.org/html/draft-luotonen-web-proxy-tunneling-01

        debug!("HTTP CONNECT {}", host);

        // Connect to Shadowsocks' remote
        //
        // FIXME: What STATUS should I return for connection error?
        let stream = super::connect_proxy_server(&*context, &*svr_cfg).await?;
        let stream = super::proxy_server_handshake(stream, svr_cfg.clone(), &host).await?;

        debug!(
            "CONNECT relay connected {} <-> {} ({})",
            client_addr,
            svr_cfg.addr(),
            host
        );

        // Upgrade to a TCP tunnel
        //
        // Note: only after client received an empty body with STATUS_OK can the
        // connection be upgraded, so we can't return a response inside
        // `on_upgrade` future.
        tokio::spawn(async move {
            match req.into_body().on_upgrade().await {
                Ok(upgraded) => {
                    trace!(
                        "CONNECT tunnel upgrade success, {} <-> {} ({})",
                        client_addr,
                        svr_cfg.addr(),
                        host
                    );

                    establish_connect_tunnel(upgraded, stream, svr_cfg, client_addr, host).await
                }
                Err(e) => {
                    error!(
                        "Failed to upgrade TCP tunnel {} <-> {} ({}), error: {}",
                        client_addr,
                        svr_cfg.addr(),
                        host,
                        e
                    );
                }
            }
        });

        let resp = Response::builder()
            .header("Proxy-Agent", format!("ShadowSocks/{}", crate::VERSION))
            .body(Body::empty())
            .unwrap();

        Ok(resp)
    } else {
        let method = req.method().clone();

        debug!("HTTP {} {}", method, host);

        let res = match client.request(req).await {
            Ok(res) => res,
            Err(err) => {
                error!(
                    "HTTP {} {} <-> {} ({}) relay failed, error: {}",
                    method,
                    client_addr,
                    svr_cfg.addr(),
                    host,
                    err
                );

                let mut resp = Response::new(Body::from(format!("Relay failed to {}", host)));
                *resp.status_mut() = StatusCode::INTERNAL_SERVER_ERROR;

                return Ok(resp);
            }
        };

        debug!(
            "HTTP {} relay {} <-> {} ({}) finished",
            method,
            client_addr,
            svr_cfg.addr(),
            host
        );

        Ok(res)
    }
}

/// Starts a TCP local server with HTTP proxy protocol
pub async fn run(context: SharedContext) -> io::Result<()> {
    let local_addr = *context.config().local.as_ref().expect("Missing local config");

    let mut servers = PingBalancer::new(context.clone(), ping::ServerType::Tcp).await;

    let mut server_clients = HashMap::new();

    // Create HTTP clients for each remote servers
    for svr_cfg in servers.servers() {
        let addr_str = svr_cfg.addr().to_string();
        let client = Client::builder().build::<_, Body>(ShadowSocksConnector::new(context.clone(), svr_cfg));
        server_clients.insert(addr_str, client);
    }

    let make_service = make_service_fn(|socket: &AddrStream| {
        let client_addr = socket.remote_addr();
        let svr_cfg = servers.pick_server();
        let context = context.clone();

        // Keep connections for clients
        let addr_str = svr_cfg.addr().to_string();
        let client = server_clients.get(&addr_str).unwrap().clone();

        async move {
            Ok::<_, Infallible>(service_fn(move |req: Request<Body>| {
                server_dispatch(context.clone(), req, svr_cfg.clone(), client_addr, client.clone())
            }))
        }
    });

    let server = Server::bind(&local_addr).serve(make_service);

    let actual_local_addr = server.local_addr();

    info!("ShadowSocks HTTP Listening on {}", actual_local_addr);

    if let Err(err) = server.await {
        use std::io::{Error, ErrorKind};

        error!("Hyper Server error: {}", err);
        return Err(Error::new(ErrorKind::Other, err));
    }

    Ok(())
}
<|endoftext|>


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

extern crate serde;

use ::resource::config::ConfigLoader;
use ::resource::plugin::PluginLoader;

use self::serde::{Deserialize, Serialize};

use std::cell::RefCell;
use std::rc::Rc;

/*===============================================================================================*/
/*------RESOURCE MANAGER STRUCT------------------------------------------------------------------*/
/*===============================================================================================*/

/// Interface for resource loading and management.
pub struct ResourceManager {

    // Private
    _cfg_dir: String,
    _res_dir: String,
    _bin_dir: String,

    _config_loader: Rc<RefCell<ConfigLoader>>,
    _plugin_loader: Rc<RefCell<PluginLoader>>,
}

/*===============================================================================================*/
/*------RESOURCE MANAGER PUBLIC METHODS----------------------------------------------------------*/
/*===============================================================================================*/

impl ResourceManager {

    /// Initializes the Resource Manager.
    pub fn init (&mut self) {
        self._plugin_loader.borrow_mut ().init (self);
    }

/*-----------------------------------------------------------------------------------------------*/

    /// Loads a config file.
    pub fn load_config<T: Deserialize> (&self, config_name: &str) -> Result<T, ()> {
        self._config_loader.borrow ().load_config::<T> (&self._cfg_dir, config_name)
    }

/*-----------------------------------------------------------------------------------------------*/

    /// Saves a config file.
    pub fn save_config<T: Serialize> (&self, config_name: &str, config_data: &T) -> Result<(), ()> {
        self._config_loader.borrow ().save_config::<T> (&self._cfg_dir, config_name, config_data)
    }

/*-----------------------------------------------------------------------------------------------*/

    /// Returns a pointer to the config loader instance.
    pub unsafe fn get_config_loader_raw (&self) -> Rc<RefCell<ConfigLoader>> {
        self._config_loader.clone ()
    }

/*-----------------------------------------------------------------------------------------------*/

    /// Returns a pointer to the plugin loader instance.
    pub unsafe fn get_plugin_loader_raw (&self) -> Rc<RefCell<PluginLoader>> {
        self._plugin_loader.clone ()
    }

/*===============================================================================================*/
/*------RESOURCE MANAGER PUBLIC STATIC METHODS---------------------------------------------------*/
/*===============================================================================================*/

    /// Create a new instance of the Resource Manager.
    pub fn new () -> ResourceManager {

        ResourceManager {

            _cfg_dir: "cfg/".to_string (),
            _res_dir: "res/".to_string (),
            _bin_dir: "bin/".to_string (),
            _config_loader: Rc::new (RefCell::new (ConfigLoader {})),
            _plugin_loader: Rc::new (RefCell::new (PluginLoader::new ()))
        }
    }
}

/*-----------------------------------------------------------------------------------------------*/

impl Default for ResourceManager {

    fn default () -> ResourceManager {
        ResourceManager::new ()
    }
}
<commit_msg>Made directory variables public<commit_after>/*===============================================================================================*/
// Copyright 2016 Kyle Finlay
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/*===============================================================================================*/

extern crate serde;

use ::resource::config::ConfigLoader;
use ::resource::plugin::PluginLoader;

use self::serde::{Deserialize, Serialize};

use std::cell::RefCell;
use std::rc::Rc;

/*===============================================================================================*/
/*------RESOURCE MANAGER STRUCT------------------------------------------------------------------*/
/*===============================================================================================*/

/// Interface for resource loading and management.
pub struct ResourceManager {

    // Public
    /// The config directory.
    pub cfg_dir: String,
    /// The resource direcotry.
    pub res_dir: String,
    /// The binary directory.
    pub bin_dir: String,
    /// The plugin directory.
    pub plug_dir: String,

    _config_loader: Rc<RefCell<ConfigLoader>>,
    _plugin_loader: Rc<RefCell<PluginLoader>>,
}

/*===============================================================================================*/
/*------RESOURCE MANAGER PUBLIC METHODS----------------------------------------------------------*/
/*===============================================================================================*/

impl ResourceManager {

    /// Initializes the Resource Manager.
    pub fn init (&mut self) {
        self._plugin_loader.borrow_mut ().init (self);
    }

/*-----------------------------------------------------------------------------------------------*/

    /// Loads a config file.
    pub fn load_config<T: Deserialize> (&self, config_name: &str) -> Result<T, ()> {
        self._config_loader.borrow ().load_config::<T> (&self.cfg_dir, config_name)
    }

/*-----------------------------------------------------------------------------------------------*/

    /// Saves a config file.
    pub fn save_config<T: Serialize> (&self, config_name: &str, config_data: &T) -> Result<(), ()> {
        self._config_loader.borrow ().save_config::<T> (&self.cfg_dir, config_name, config_data)
    }

/*-----------------------------------------------------------------------------------------------*/

    /// Returns a pointer to the config loader instance.
    pub unsafe fn get_config_loader_raw (&self) -> Rc<RefCell<ConfigLoader>> {
        self._config_loader.clone ()
    }

/*-----------------------------------------------------------------------------------------------*/

    /// Returns a pointer to the plugin loader instance.
    pub unsafe fn get_plugin_loader_raw (&self) -> Rc<RefCell<PluginLoader>> {
        self._plugin_loader.clone ()
    }

/*===============================================================================================*/
/*------RESOURCE MANAGER PUBLIC STATIC METHODS---------------------------------------------------*/
/*===============================================================================================*/

    /// Create a new instance of the Resource Manager.
    pub fn new () -> ResourceManager {

        ResourceManager {

            cfg_dir: "cfg/".to_string (),
            res_dir: "res/".to_string (),
            bin_dir: "bin/".to_string (),
            plug_dir: "bin/plugins/".to_string (),
            _config_loader: Rc::new (RefCell::new (ConfigLoader {})),
            _plugin_loader: Rc::new (RefCell::new (PluginLoader::new ()))
        }
    }
}

/*-----------------------------------------------------------------------------------------------*/

impl Default for ResourceManager {

    fn default () -> ResourceManager {
        ResourceManager::new ()
    }
}
<|endoftext|>


<commit_before>use symbols::*;
use support::*;

//TODO change String to Symbol
///start, end, name, namespace
pub struct MemberBlock (pub usize, pub usize, pub String, pub String, pub Option<Vec<String>>);

pub struct ClassStructure
{
    //TODO add constructor vector
    pub static_variables: Vec<MemberBlock>,
    pub static_methods: Vec<MemberBlock>,
    pub static_classes: Vec<MemberBlock>,

    pub non_static_variables: Vec<MemberBlock>,
    pub non_static_methods: Vec<MemberBlock>,
    pub non_static_classes: Vec<MemberBlock>,
}

impl ClassStructure
{
    pub fn new() -> ClassStructure
    {
        ClassStructure
        {
            static_variables: Vec::new(),
            static_methods: Vec::new(),
            static_classes: Vec::new(),

            non_static_variables: Vec::new(),
            non_static_methods: Vec::new(),
            non_static_classes: Vec::new(),
        }
    }
}

pub struct SymbolTable
{
    pub children_scopes: Vec<Symbol>,
}

impl<'a> SymbolTable
{
    pub fn new() -> SymbolTable
    {
        SymbolTable
        {
            children_scopes: Vec::new(),
        }
    }
}

impl StaticSymbolTable for SymbolTable
{
    /// Return all symbols in this table with the specified name (in any namespace)
    fn lookup_by_name(&self, name: &str) -> Vec<&Symbol>
    {
        let mut symbols: Vec<&Symbol> = Vec::new();
        for symbol in self.children_scopes.iter()
        {
            if symbol.name == name
            {
                symbols.push(symbol.clone());
            }
        }
        symbols
    }

    /// Return all symbols in this table with the specified namespace
	fn lookup_by_namespace(&self, namespace: &str) -> Vec<&Symbol>
    {
        let mut symbols: Vec<&Symbol> = Vec::new();
        for symbol in self.children_scopes.iter()
        {
            if symbol.namespace == namespace
            {
                symbols.push(symbol.clone());
            }
        }
        symbols
    }

    /// Lookup a variable by its name and namespace. Duplicate symbols are not allowed, so the result will be unique
    /// @return the specified symbol or None if the specified symbol is not in this namespace
	fn lookup_variable(&self, namespace: &str, name: &str) -> Option<&Symbol>
    {
        let mut namespaces: Vec<&str> = namespace.split_terminator('_').collect();
        let mut length;
        let mut current_namespace;

        loop
        {
            current_namespace = namespaces.connect("_");

            if namespaces.is_empty()
            {
                break;
            }
            else
            {
                for symbol in self.lookup_by_namespace(&*current_namespace).iter()
                {
                    if symbol.namespace == current_namespace
                    {
                        if symbol.name == name
                        {
                            match symbol.symbol_class
                            {
                                SymbolClass::Variable(ref variable_type) => return Some((symbol).clone()),
                                _ => continue,
                            };
                        }
                    }
                }
                length = namespaces.len();
                namespaces.remove(length - 1);
            }
        }
        None
    }

    /// Lookup a function by its name and namespace. Functions with the same signature are not allowed, so the result will be unique
    /// If no result is found in the direct namespace, the parent namespaces will be searched
    /// @return the specified symbol or None if the specified symbol is not in this namespace or a parent namespace
	fn lookup_function(&self, namespace: &str, name: &str, argument_types: &Vec<String>) -> Option<&Symbol>
    {
        //TODO use argument_types
        let mut namespaces: Vec<&str> = namespace.split_terminator('_').collect();
        let mut length;
        let mut current_namespace;

        loop
        {
            current_namespace = namespaces.connect("_");

            if namespaces.is_empty()
            {
                break;
            }
            else
            {
                for symbol in self.lookup_by_namespace(&*current_namespace).iter()
                {
                    if symbol.namespace == current_namespace
                    {
                        if symbol.name == name
                        {
                            match symbol.symbol_class
                            {
                                SymbolClass::Function(ref return_type, ref arguments, ref static_label, static_length) => return Some((symbol).clone()),
                                _ => continue,
                            };
                        }
                    }
                }
                length = namespaces.len();
                namespaces.remove(length - 1);
            }
        }
        None
    }

    /// Lookup a structure (class, enum) by its name and namespace.
    /// Duplicate classes in the same namespace are not allowed, so the result will be unique
    /// If no result is found in the direct namespace, the parent namespaces will be searched
    /// @return the specified symbol or None if the specified symbol is not in this namespace or a parent namespace
	fn lookup_structure(&self, namespace: &str, name: &str) -> Option<(&Symbol)>
    {
        let mut namespaces: Vec<&str> = namespace.split_terminator('_').collect();
        let mut length;
        let mut current_namespace;

        loop
        {
            current_namespace = namespaces.connect("_");

            if namespaces.is_empty()
            {
                break;
            }
            else
            {
                for symbol in self.lookup_by_namespace(&*current_namespace).iter()
                {
                    if symbol.namespace == current_namespace
                    {
                        if symbol.name == name
                        {
                            match symbol.symbol_class
                            {
                                SymbolClass::Structure(ref sub_type) => return Some((symbol).clone()),
                                _ => continue,
                            };
                        }
                    }
                }
                length = namespaces.len();
                namespaces.remove(length - 1);
            }
        }
        None
    }

    /// Adds a symbol to this table and allocates it's location
    /// Returns true if the symbol could be added; false otherwise
    /// Duplicate symbols are not allowed
    /// TODO: support overloaded methods
	fn add(&mut self, class: SymbolClass, namespace: String, name: String, is_static: bool, in_method: bool, is_parameter: bool, local_variable_count: u16, static_variable_count: u16, parameter_offset: u16) -> bool
    {
        let mut static_label = namespace.replace(".", "_").clone();
        static_label.push_str("_static");
        let mut method_namespace = namespace.replace(".", "_").clone();
        method_namespace.push_str("_");
        method_namespace.push_str(&*name);

        //TODO add return false
        let mut location: SymbolLocation = match class
        {
            //TODO replace with storing logic
            SymbolClass::Structure(ref sub_type) => SymbolLocation::Structured,
            SymbolClass::Variable(ref variable_type) => match in_method
                {
                    true => match is_parameter
                        {
                            true => SymbolLocation::MethodArgument(parameter_offset),
                            false =>SymbolLocation::Memory(MemoryAddress {label_name: static_label, offset: static_variable_count * 4}),
                        },
                    false => match is_static
                        {
                            true => SymbolLocation::Memory(MemoryAddress {label_name: static_label, offset: static_variable_count * 4}),
                            false => SymbolLocation::InstancedMemory(local_variable_count * 4),
                        },
                },
            SymbolClass::Function(ref return_type, ref arguments, ref static_label, static_length) => SymbolLocation::Memory(MemoryAddress {label_name: method_namespace, offset: 0}),
        };

        let mut symbol: Symbol =  Symbol
        {
            name: name,
            namespace: namespace,
            is_static: is_static,
            symbol_class: class,
            location: location,
        };

        self.children_scopes.push(symbol);
        return true;
    }
}
<commit_msg>Update documentation<commit_after>use symbols::*;
use support::*;

//TODO change String to Symbol
///start, end, name, namespace, argument types (if applicable)
pub struct MemberBlock (pub usize, pub usize, pub String, pub String, pub Option<Vec<String>>);

pub struct ClassStructure
{
    //TODO add constructor vector
    pub static_variables: Vec<MemberBlock>,
    pub static_methods: Vec<MemberBlock>,
    pub static_classes: Vec<MemberBlock>,

    pub non_static_variables: Vec<MemberBlock>,
    pub non_static_methods: Vec<MemberBlock>,
    pub non_static_classes: Vec<MemberBlock>,
}

impl ClassStructure
{
    pub fn new() -> ClassStructure
    {
        ClassStructure
        {
            static_variables: Vec::new(),
            static_methods: Vec::new(),
            static_classes: Vec::new(),

            non_static_variables: Vec::new(),
            non_static_methods: Vec::new(),
            non_static_classes: Vec::new(),
        }
    }
}

pub struct SymbolTable
{
    pub children_scopes: Vec<Symbol>,
}

impl<'a> SymbolTable
{
    pub fn new() -> SymbolTable
    {
        SymbolTable
        {
            children_scopes: Vec::new(),
        }
    }
}

impl StaticSymbolTable for SymbolTable
{
    /// Return all symbols in this table with the specified name (in any namespace)
    fn lookup_by_name(&self, name: &str) -> Vec<&Symbol>
    {
        let mut symbols: Vec<&Symbol> = Vec::new();
        for symbol in self.children_scopes.iter()
        {
            if symbol.name == name
            {
                symbols.push(symbol.clone());
            }
        }
        symbols
    }

    /// Return all symbols in this table with the specified namespace
	fn lookup_by_namespace(&self, namespace: &str) -> Vec<&Symbol>
    {
        let mut symbols: Vec<&Symbol> = Vec::new();
        for symbol in self.children_scopes.iter()
        {
            if symbol.namespace == namespace
            {
                symbols.push(symbol.clone());
            }
        }
        symbols
    }

    /// Lookup a variable by its name and namespace. Duplicate symbols are not allowed, so the result will be unique
    /// @return the specified symbol or None if the specified symbol is not in this namespace
	fn lookup_variable(&self, namespace: &str, name: &str) -> Option<&Symbol>
    {
        let mut namespaces: Vec<&str> = namespace.split_terminator('_').collect();
        let mut length;
        let mut current_namespace;

        loop
        {
            current_namespace = namespaces.connect("_");

            if namespaces.is_empty()
            {
                break;
            }
            else
            {
                for symbol in self.lookup_by_namespace(&*current_namespace).iter()
                {
                    if symbol.namespace == current_namespace
                    {
                        if symbol.name == name
                        {
                            match symbol.symbol_class
                            {
                                SymbolClass::Variable(ref variable_type) => return Some((symbol).clone()),
                                _ => continue,
                            };
                        }
                    }
                }
                length = namespaces.len();
                namespaces.remove(length - 1);
            }
        }
        None
    }

    /// Lookup a function by its name and namespace. Functions with the same signature are not allowed, so the result will be unique
    /// If no result is found in the direct namespace, the parent namespaces will be searched
    /// @return the specified symbol or None if the specified symbol is not in this namespace or a parent namespace
	fn lookup_function(&self, namespace: &str, name: &str, argument_types: &Vec<String>) -> Option<&Symbol>
    {
        //TODO use argument_types
        let mut namespaces: Vec<&str> = namespace.split_terminator('_').collect();
        let mut length;
        let mut current_namespace;

        loop
        {
            current_namespace = namespaces.connect("_");

            if namespaces.is_empty()
            {
                break;
            }
            else
            {
                for symbol in self.lookup_by_namespace(&*current_namespace).iter()
                {
                    if symbol.namespace == current_namespace
                    {
                        if symbol.name == name
                        {
                            match symbol.symbol_class
                            {
                                SymbolClass::Function(ref return_type, ref arguments, ref static_label, static_length) => return Some((symbol).clone()),
                                _ => continue,
                            };
                        }
                    }
                }
                length = namespaces.len();
                namespaces.remove(length - 1);
            }
        }
        None
    }

    /// Lookup a structure (class, enum) by its name and namespace.
    /// Duplicate classes in the same namespace are not allowed, so the result will be unique
    /// If no result is found in the direct namespace, the parent namespaces will be searched
    /// @return the specified symbol or None if the specified symbol is not in this namespace or a parent namespace
	fn lookup_structure(&self, namespace: &str, name: &str) -> Option<(&Symbol)>
    {
        let mut namespaces: Vec<&str> = namespace.split_terminator('_').collect();
        let mut length;
        let mut current_namespace;

        loop
        {
            current_namespace = namespaces.connect("_");

            if namespaces.is_empty()
            {
                break;
            }
            else
            {
                for symbol in self.lookup_by_namespace(&*current_namespace).iter()
                {
                    if symbol.namespace == current_namespace
                    {
                        if symbol.name == name
                        {
                            match symbol.symbol_class
                            {
                                SymbolClass::Structure(ref sub_type) => return Some((symbol).clone()),
                                _ => continue,
                            };
                        }
                    }
                }
                length = namespaces.len();
                namespaces.remove(length - 1);
            }
        }
        None
    }

    /// Adds a symbol to this table and allocates it's location
    /// Returns true if the symbol could be added; false otherwise
    /// Duplicate symbols are not allowed
    /// TODO: support overloaded methods
	fn add(&mut self, class: SymbolClass, namespace: String, name: String, is_static: bool, in_method: bool, is_parameter: bool, local_variable_count: u16, static_variable_count: u16, parameter_offset: u16) -> bool
    {
        let mut static_label = namespace.replace(".", "_").clone();
        static_label.push_str("_static");
        let mut method_namespace = namespace.replace(".", "_").clone();
        method_namespace.push_str("_");
        method_namespace.push_str(&*name);

        //TODO add return false
        let mut location: SymbolLocation = match class
        {
            //TODO replace with storing logic
            SymbolClass::Structure(ref sub_type) => SymbolLocation::Structured,
            SymbolClass::Variable(ref variable_type) => match in_method
                {
                    true => match is_parameter
                        {
                            true => SymbolLocation::MethodArgument(parameter_offset),
                            false =>SymbolLocation::Memory(MemoryAddress {label_name: static_label, offset: static_variable_count * 4}),
                        },
                    false => match is_static
                        {
                            true => SymbolLocation::Memory(MemoryAddress {label_name: static_label, offset: static_variable_count * 4}),
                            false => SymbolLocation::InstancedMemory(local_variable_count * 4),
                        },
                },
            SymbolClass::Function(ref return_type, ref arguments, ref static_label, static_length) => SymbolLocation::Memory(MemoryAddress {label_name: method_namespace, offset: 0}),
        };

        let mut symbol: Symbol =  Symbol
        {
            name: name,
            namespace: namespace,
            is_static: is_static,
            symbol_class: class,
            location: location,
        };

        self.children_scopes.push(symbol);
        return true;
    }
}
<|endoftext|>


<commit_before>use std::cell::RefCell;
use std::thread::sleep;
use std::time::{Duration, Instant};
use super::scheduler::*;

/// Schedule a func to be executed on the current thread. The action is not
/// executed immediately but is queued and execute when calling one of the
/// methods [`run_pending_actions()`](#method.run_pending_actions),
/// [`run_until_empty()`](#method.run_until_empty).
#[derive(Copy, Clone)]
pub struct CurrentThread;

impl CurrentThread {
    pub fn current() -> CurrentThread {
        CurrentThread
    }

    /// Returns true if the schedule queue contains funcs that are due
    /// to be executed.
    pub fn has_pending_actions(&self) -> bool {
        CURRENT_THREAD.with(|f| f.borrow().has_pending_actions())
    }

    /// Returns true if the schedule queue is empty
    pub fn is_empty(&self) -> bool {
        CURRENT_THREAD.with(|f| f.borrow().is_empty())
    }

    /// Block and execute only the actions that are due to be executed.
    pub fn run_pending_actions(&mut self) {
        CURRENT_THREAD.with(|f| f.borrow_mut().run_pending_actions())
    }

    /// Block and execute all items in the schedule queue until it is completed.
    pub fn run_until_empty(&mut self) {
        CURRENT_THREAD.with(|f| f.borrow_mut().run_until_empty())
    }
}

impl Scheduler for CurrentThread {
    fn schedule<F>(&mut self, func: F, delay: Duration)
        where F: FnOnce() + 'static
    {
        CURRENT_THREAD.with(|f| f.borrow_mut().schedule(func, delay))
    }
}

struct RecordItem<F> {
    func: Option<F>,
    instant: Instant,
}

impl<F> RecordItem<F>
    where F: FnOnce() + 'static
{
    fn new(f: F, instant: Instant) -> Self {
        RecordItem {
            func: Some(f),
            instant: instant,
        }
    }
}

trait Record {
    fn get_instant(&self) -> Instant;
    fn invoke(&mut self);
}

impl<F> Record for RecordItem<F>
    where F: FnOnce()
{
    fn get_instant(&self) -> Instant {
        self.instant
    }

    fn invoke(&mut self) {
        if let Some(func) = self.func.take() {
            func();
        }
    }
}

struct ThreadCell {
    due: Option<Instant>,
    records: Vec<Box<Record>>,
}

impl ThreadCell {
    fn has_pending_actions(&self) -> bool {
        if let Some(due) = self.due {
            due <= Instant::now()
        } else {
            false
        }
    }

    fn is_empty(&self) -> bool {
        self.records.is_empty()
    }

    fn new() -> Self {
        ThreadCell {
            due: None,
            records: Vec::new(),
        }
    }

    fn run_once(&mut self) {
        self.records.sort_by_key(|r| r.get_instant());
        self.due = None;

        let mut i = self.records.len();

        while i > 0 {
            i -= 1;

            let due = self.records[i].get_instant();

            if due <= Instant::now() {
                self.records.remove(i).invoke();

            } else {
                self.set_due(due);
                break;
            }
        }
    }

    fn run_pending_actions(&mut self) {
        while self.due.map_or(false, |v| v <= Instant::now()) {
            self.run_once();
        }
    }

    fn run_until_empty(&mut self) {
        while let Some(due) = self.due.take() {
            let now = Instant::now();
            if due > now {
                sleep(due - now);
            }
            self.run_once();
        }
    }

    fn schedule<F>(&mut self, func: F, delay: Duration)
        where F: FnOnce() + 'static
    {
        let due = Instant::now() + delay;
        self.records.push(Box::new(RecordItem::new(func, due)));
        self.set_due(due);
    }

    fn set_due(&mut self, instant: Instant) {
        if let Some(due) = self.due {
            if due > instant {
                self.due = Some(instant);
            }
        } else {
            self.due = Some(instant);
        }
    }
}

thread_local!(static CURRENT_THREAD: RefCell<ThreadCell> = RefCell::new(ThreadCell::new()));
<commit_msg>Fix CurrentThread documentation link<commit_after>use std::cell::RefCell;
use std::thread::sleep;
use std::time::{Duration, Instant};
use super::scheduler::*;

/// Schedule a func to be executed on the current thread. The action is not
/// executed immediately but is queued and execute when calling one of the
/// methods [`run_pending_actions()`](./struct.CurrentThread.html#method.run_pending_actions),
/// [`run_until_empty()`](./struct.CurrentThread.html#method.run_until_empty).
#[derive(Copy, Clone)]
pub struct CurrentThread;

impl CurrentThread {
    pub fn current() -> CurrentThread {
        CurrentThread
    }

    /// Returns true if the schedule queue contains funcs that are due
    /// to be executed.
    pub fn has_pending_actions(&self) -> bool {
        CURRENT_THREAD.with(|f| f.borrow().has_pending_actions())
    }

    /// Returns true if the schedule queue is empty
    pub fn is_empty(&self) -> bool {
        CURRENT_THREAD.with(|f| f.borrow().is_empty())
    }

    /// Block and execute only the actions that are due to be executed.
    pub fn run_pending_actions(&mut self) {
        CURRENT_THREAD.with(|f| f.borrow_mut().run_pending_actions())
    }

    /// Block and execute all items in the schedule queue until it is completed.
    pub fn run_until_empty(&mut self) {
        CURRENT_THREAD.with(|f| f.borrow_mut().run_until_empty())
    }
}

impl Scheduler for CurrentThread {
    fn schedule<F>(&mut self, func: F, delay: Duration)
        where F: FnOnce() + 'static
    {
        CURRENT_THREAD.with(|f| f.borrow_mut().schedule(func, delay))
    }
}

struct RecordItem<F> {
    func: Option<F>,
    instant: Instant,
}

impl<F> RecordItem<F>
    where F: FnOnce() + 'static
{
    fn new(f: F, instant: Instant) -> Self {
        RecordItem {
            func: Some(f),
            instant: instant,
        }
    }
}

trait Record {
    fn get_instant(&self) -> Instant;
    fn invoke(&mut self);
}

impl<F> Record for RecordItem<F>
    where F: FnOnce()
{
    fn get_instant(&self) -> Instant {
        self.instant
    }

    fn invoke(&mut self) {
        if let Some(func) = self.func.take() {
            func();
        }
    }
}

struct ThreadCell {
    due: Option<Instant>,
    records: Vec<Box<Record>>,
}

impl ThreadCell {
    fn has_pending_actions(&self) -> bool {
        if let Some(due) = self.due {
            due <= Instant::now()
        } else {
            false
        }
    }

    fn is_empty(&self) -> bool {
        self.records.is_empty()
    }

    fn new() -> Self {
        ThreadCell {
            due: None,
            records: Vec::new(),
        }
    }

    fn run_once(&mut self) {
        self.records.sort_by_key(|r| r.get_instant());
        self.due = None;

        let mut i = self.records.len();

        while i > 0 {
            i -= 1;

            let due = self.records[i].get_instant();

            if due <= Instant::now() {
                self.records.remove(i).invoke();

            } else {
                self.set_due(due);
                break;
            }
        }
    }

    fn run_pending_actions(&mut self) {
        while self.due.map_or(false, |v| v <= Instant::now()) {
            self.run_once();
        }
    }

    fn run_until_empty(&mut self) {
        while let Some(due) = self.due.take() {
            let now = Instant::now();
            if due > now {
                sleep(due - now);
            }
            self.run_once();
        }
    }

    fn schedule<F>(&mut self, func: F, delay: Duration)
        where F: FnOnce() + 'static
    {
        let due = Instant::now() + delay;
        self.records.push(Box::new(RecordItem::new(func, due)));
        self.set_due(due);
    }

    fn set_due(&mut self, instant: Instant) {
        if let Some(due) = self.due {
            if due > instant {
                self.due = Some(instant);
            }
        } else {
            self.due = Some(instant);
        }
    }
}

thread_local!(static CURRENT_THREAD: RefCell<ThreadCell> = RefCell::new(ThreadCell::new()));
<|endoftext|>


<commit_before>#![feature(old_io, std_misc)]
extern crate zookeeper;

use std::time::Duration;
use std::thread;
use std::old_io as io;
use zookeeper::{Acl, CreateMode, Watcher, WatchedEvent, ZkResult, ZooKeeper};
use zookeeper::perms;

struct LoggingWatcher;
impl Watcher for LoggingWatcher {
    fn handle(&self, e: &WatchedEvent) {
        println!("{:?}", e)
    }
}

fn zk_example() -> ZkResult<()> {
    let zk = try!(ZooKeeper::connect("127.0.0.1:2181", Duration::seconds(5), LoggingWatcher));

    println!("connecting... press enter to continue");
    io::stdin().read_line().unwrap();

    let acl1 = vec![Acl{perms: perms::ALL, scheme: "world".to_string(), id: "anyone".to_string()}];
    let acl2 = vec![Acl{perms: perms::ALL, scheme: "world".to_string(), id: "anyone".to_string()}];

    let auth = zk.add_auth("digest", vec![1,2,3,4]);

    println!("authenticated -> {:?}", auth);

    let path = zk.create("/test", vec![1,2], acl1, CreateMode::Ephemeral);

    println!("created -> {:?}", path);

    let exists = zk.exists("/test", true);

    println!("exists -> {:?}", exists);

    let dont_exists = zk.exists("/blabla", true);

    println!("don't exists path -> {:?}", dont_exists);

    let get_acl = zk.get_acl("/test");

    println!("get_acl -> {:?}", get_acl);

    let set_acl = zk.set_acl("/test", acl2, -1);

    println!("set_acl -> {:?}", set_acl);

    let children = zk.get_children("/", true);

    println!("children of / -> {:?}", children);

    let set_data = zk.set_data("/test", vec![6,5,4,3], -1);

    println!("set_data -> {:?}", set_data);

    let get_data = zk.get_data("/test", true);

    println!("get_data -> {:?}", get_data);

    let delete = zk.delete("/test", -1);

    println!("deleted /test -> {:?}", delete);

    println!("press enter to close client");
    io::stdin().read_line().unwrap();

    // The client can be shared between tasks
    let zk2 = zk.clone();
    thread::spawn(move || {
        zk2.close();

        // And operations return error after closed
        match zk.exists("/test", false) {
            Err(err) => println!("Usage after closed: {:?}", err),
            Ok(_) => panic!("Shouldn't happen")
        }
    });

    println!("press enter to exit example");
    io::stdin().read_line().unwrap();

    Ok(())
}

fn main() {
    zk_example().unwrap();
}
<commit_msg>example warning removed<commit_after>#![feature(std_misc)]
extern crate zookeeper;

use std::time::Duration;
use std::thread;
use std::io;
use zookeeper::{Acl, CreateMode, Watcher, WatchedEvent, ZkResult, ZooKeeper};
use zookeeper::perms;

struct LoggingWatcher;
impl Watcher for LoggingWatcher {
    fn handle(&self, e: &WatchedEvent) {
        println!("{:?}", e)
    }
}

fn zk_example() -> ZkResult<()> {
    let zk = try!(ZooKeeper::connect("127.0.0.1:2181", Duration::seconds(5), LoggingWatcher));

    let mut tmp = String::new();
    println!("connecting... press any to continue");
    io::stdin().read_line(&mut tmp).unwrap();

    let acl1 = vec![Acl{perms: perms::ALL, scheme: "world".to_string(), id: "anyone".to_string()}];
    let acl2 = vec![Acl{perms: perms::ALL, scheme: "world".to_string(), id: "anyone".to_string()}];

    let auth = zk.add_auth("digest", vec![1,2,3,4]);

    println!("authenticated -> {:?}", auth);

    let path = zk.create("/test", vec![1,2], acl1, CreateMode::Ephemeral);

    println!("created -> {:?}", path);

    let exists = zk.exists("/test", true);

    println!("exists -> {:?}", exists);

    let dont_exists = zk.exists("/blabla", true);

    println!("don't exists path -> {:?}", dont_exists);

    let get_acl = zk.get_acl("/test");

    println!("get_acl -> {:?}", get_acl);

    let set_acl = zk.set_acl("/test", acl2, -1);

    println!("set_acl -> {:?}", set_acl);

    let children = zk.get_children("/", true);

    println!("children of / -> {:?}", children);

    let set_data = zk.set_data("/test", vec![6,5,4,3], -1);

    println!("set_data -> {:?}", set_data);

    let get_data = zk.get_data("/test", true);

    println!("get_data -> {:?}", get_data);

    let delete = zk.delete("/test", -1);

    println!("deleted /test -> {:?}", delete);

    println!("press enter to close client");
    io::stdin().read_line(&mut tmp).unwrap();

    // The client can be shared between tasks
    let zk2 = zk.clone();
    thread::spawn(move || {
        zk2.close();

        // And operations return error after closed
        match zk.exists("/test", false) {
            Err(err) => println!("Usage after closed: {:?}", err),
            Ok(_) => panic!("Shouldn't happen")
        }
    });

    println!("press enter to exit example");
    io::stdin().read_line(&mut tmp).unwrap();

    Ok(())
}

fn main() {
    zk_example().unwrap();
}
<|endoftext|>


<commit_before>/// Traits required for parsing
pub use crate::types::parsing::traits;
use crate::{
    frame::*,
    protocol::{basic::parse_properties, *},
    types::parsing::*,
};
use nom::{
    branch::alt,
    bytes::streaming::{tag, take},
    combinator::{all_consuming, cut, flat_map, map, map_opt, map_res},
    error::context,
    sequence::{pair, tuple},
};
use traits::ParsableInput;

/// Parse a channel id
pub fn parse_channel<I: ParsableInput>(i: I) -> ParserResult<I, AMQPChannel> {
    context("parse_channel", map(parse_id, From::from))(i)
}

/// Parse the protocol header
pub fn parse_protocol_header<I: ParsableInput>(i: I) -> ParserResult<I, ProtocolVersion> {
    context(
        "parse_protocol_header",
        map(
            tuple((
                tag(metadata::NAME.as_bytes()),
                tag(&[0][..]),
                parse_short_short_uint,
                parse_short_short_uint,
                parse_short_short_uint,
            )),
            |(_, _, major, minor, revision)| ProtocolVersion {
                major,
                minor,
                revision,
            },
        ),
    )(i)
}

/// Parse the frame type
pub fn parse_frame_type<I: ParsableInput>(i: I) -> ParserResult<I, AMQPFrameType> {
    context(
        "parse_frame_type",
        map_opt(parse_short_short_uint, |method| match method {
            constants::FRAME_METHOD => Some(AMQPFrameType::Method),
            constants::FRAME_HEADER => Some(AMQPFrameType::Header),
            constants::FRAME_BODY => Some(AMQPFrameType::Body),
            constants::FRAME_HEARTBEAT => Some(AMQPFrameType::Heartbeat),
            _ => None,
        }),
    )(i)
}

/// Parse a full AMQP Frame (with contents)
pub fn parse_frame<I: ParsableInput>(i: I) -> ParserResult<I, AMQPFrame> {
    context(
        "parse_frame",
        alt((
            map_res(
                parse_raw_frame,
                |AMQPRawFrame {
                     channel_id,
                     frame_type,
                     payload,
                 }: AMQPRawFrame<I>| match frame_type {
                    AMQPFrameType::Method => all_consuming(parse_class)(payload)
                        .map(|(_, m)| AMQPFrame::Method(channel_id, m)),
                    AMQPFrameType::Header => all_consuming(parse_content_header)(payload)
                        .map(|(_, h)| AMQPFrame::Header(channel_id, h.class_id, Box::new(h))),
                    AMQPFrameType::Body => Ok(AMQPFrame::Body(
                        channel_id,
                        payload.iter_elements().collect(),
                    )),
                    AMQPFrameType::Heartbeat => Ok(AMQPFrame::Heartbeat(channel_id)),
                },
            ),
            map(parse_protocol_header, AMQPFrame::ProtocolHeader),
        )),
    )(i)
}

/// Parse a raw AMQP frame
pub fn parse_raw_frame<I: ParsableInput>(i: I) -> ParserResult<I, AMQPRawFrame<I>> {
    context(
        "parse_raw_frame",
        flat_map(parse_frame_type, move |frame_type| {
            cut(flat_map(
                pair(parse_id, parse_long_uint),
                move |(channel_id, size)| {
                    map(
                        pair(take(size), tag(&[constants::FRAME_END][..])),
                        move |(payload, _)| AMQPRawFrame {
                            frame_type,
                            channel_id,
                            payload,
                        },
                    )
                },
            ))
        }),
    )(i)
}

/// Parse a content header frame
pub fn parse_content_header<I: ParsableInput>(i: I) -> ParserResult<I, AMQPContentHeader> {
    context(
        "parse_content_header",
        map(
            tuple((
                parse_id,
                parse_short_uint,
                parse_long_long_uint,
                context("parse_properties", parse_properties),
            )),
            |(class_id, weight, body_size, properties)| AMQPContentHeader {
                class_id,
                weight,
                body_size,
                properties,
            },
        ),
    )(i)
}

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

    #[test]
    fn test_protocol_header() {
        assert_eq!(
            parse_frame(&['A' as u8, 'M' as u8, 'Q' as u8, 'P' as u8, 0, 0, 9, 1][..]),
            Ok((
                &[][..],
                AMQPFrame::ProtocolHeader(ProtocolVersion::amqp_0_9_1())
            ))
        );
    }

    #[test]
    fn test_heartbeat() {
        assert_eq!(
            parse_frame(&[8, 0, 1, 0, 0, 0, 0, 206][..]),
            Ok((&[][..], AMQPFrame::Heartbeat(1)))
        );
    }
}
<commit_msg>parse frame: reorder<commit_after>/// Traits required for parsing
pub use crate::types::parsing::traits;
use crate::{
    frame::*,
    protocol::{basic::parse_properties, *},
    types::parsing::*,
};
use nom::{
    branch::alt,
    bytes::streaming::{tag, take},
    combinator::{all_consuming, cut, flat_map, map, map_opt, map_res},
    error::context,
    sequence::{pair, tuple},
};
use traits::ParsableInput;

/// Parse a channel id
pub fn parse_channel<I: ParsableInput>(i: I) -> ParserResult<I, AMQPChannel> {
    context("parse_channel", map(parse_id, From::from))(i)
}

/// Parse the protocol header
pub fn parse_protocol_header<I: ParsableInput>(i: I) -> ParserResult<I, ProtocolVersion> {
    context(
        "parse_protocol_header",
        map(
            tuple((
                tag(metadata::NAME.as_bytes()),
                tag(&[0][..]),
                parse_short_short_uint,
                parse_short_short_uint,
                parse_short_short_uint,
            )),
            |(_, _, major, minor, revision)| ProtocolVersion {
                major,
                minor,
                revision,
            },
        ),
    )(i)
}

/// Parse the frame type
pub fn parse_frame_type<I: ParsableInput>(i: I) -> ParserResult<I, AMQPFrameType> {
    context(
        "parse_frame_type",
        map_opt(parse_short_short_uint, |method| match method {
            constants::FRAME_METHOD => Some(AMQPFrameType::Method),
            constants::FRAME_HEADER => Some(AMQPFrameType::Header),
            constants::FRAME_BODY => Some(AMQPFrameType::Body),
            constants::FRAME_HEARTBEAT => Some(AMQPFrameType::Heartbeat),
            _ => None,
        }),
    )(i)
}

/// Parse a full AMQP Frame (with contents)
pub fn parse_frame<I: ParsableInput>(i: I) -> ParserResult<I, AMQPFrame> {
    context(
        "parse_frame",
        alt((
            map(parse_protocol_header, AMQPFrame::ProtocolHeader),
            map_res(
                parse_raw_frame,
                |AMQPRawFrame {
                     channel_id,
                     frame_type,
                     payload,
                 }: AMQPRawFrame<I>| match frame_type {
                    AMQPFrameType::Method => all_consuming(parse_class)(payload)
                        .map(|(_, m)| AMQPFrame::Method(channel_id, m)),
                    AMQPFrameType::Header => all_consuming(parse_content_header)(payload)
                        .map(|(_, h)| AMQPFrame::Header(channel_id, h.class_id, Box::new(h))),
                    AMQPFrameType::Body => Ok(AMQPFrame::Body(
                        channel_id,
                        payload.iter_elements().collect(),
                    )),
                    AMQPFrameType::Heartbeat => Ok(AMQPFrame::Heartbeat(channel_id)),
                },
            ),
        )),
    )(i)
}

/// Parse a raw AMQP frame
pub fn parse_raw_frame<I: ParsableInput>(i: I) -> ParserResult<I, AMQPRawFrame<I>> {
    context(
        "parse_raw_frame",
        flat_map(parse_frame_type, move |frame_type| {
            cut(flat_map(
                pair(parse_id, parse_long_uint),
                move |(channel_id, size)| {
                    map(
                        pair(take(size), tag(&[constants::FRAME_END][..])),
                        move |(payload, _)| AMQPRawFrame {
                            frame_type,
                            channel_id,
                            payload,
                        },
                    )
                },
            ))
        }),
    )(i)
}

/// Parse a content header frame
pub fn parse_content_header<I: ParsableInput>(i: I) -> ParserResult<I, AMQPContentHeader> {
    context(
        "parse_content_header",
        map(
            tuple((
                parse_id,
                parse_short_uint,
                parse_long_long_uint,
                context("parse_properties", parse_properties),
            )),
            |(class_id, weight, body_size, properties)| AMQPContentHeader {
                class_id,
                weight,
                body_size,
                properties,
            },
        ),
    )(i)
}

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

    #[test]
    fn test_protocol_header() {
        assert_eq!(
            parse_frame(&['A' as u8, 'M' as u8, 'Q' as u8, 'P' as u8, 0, 0, 9, 1][..]),
            Ok((
                &[][..],
                AMQPFrame::ProtocolHeader(ProtocolVersion::amqp_0_9_1())
            ))
        );
    }

    #[test]
    fn test_heartbeat() {
        assert_eq!(
            parse_frame(&[8, 0, 1, 0, 0, 0, 0, 206][..]),
            Ok((&[][..], AMQPFrame::Heartbeat(1)))
        );
    }
}
<|endoftext|>


<commit_before>#![feature(allow_internal_unstable)]
#![feature(bench_black_box)]
#![feature(extend_one)]
#![feature(iter_zip)]
#![feature(unboxed_closures)]
#![feature(test)]
#![feature(fn_traits)]

pub mod bit_set;
pub mod vec;

// FIXME(#56935): Work around ICEs during cross-compilation.
#[allow(unused)]
extern crate rustc_macros;
<commit_msg>Swap out unboxed_closures feature gate for min_specialization<commit_after>#![feature(allow_internal_unstable)]
#![feature(bench_black_box)]
#![feature(extend_one)]
#![feature(iter_zip)]
#![feature(min_specialization)]
#![feature(test)]
#![feature(fn_traits)]

pub mod bit_set;
pub mod vec;

// FIXME(#56935): Work around ICEs during cross-compilation.
#[allow(unused)]
extern crate rustc_macros;
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2014 Maciej Piechotka
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

use std::libc::{c_char, c_int, c_uchar, c_ushort, c_void};

pub type xmlChar = c_uchar;

#[deriving(ToStr)]
pub enum xmlElementType {
    ElementNode = 1,
    AttributeNode = 2,
    TextNode = 3,
    CDATASectionNode = 4,
    EntityRefNode = 5,
    EntityNode = 6,
    PINode = 7,
    CommentNode = 8,
    DocumentNode = 9,
    DocumentTypeNode = 10,
    DocumentFragNode = 11,
    NotationNode = 12,
    HtmlDocumentNode = 13,
    DTDNode = 14,
    ElementDecl = 15,
    AttributeDecl = 16,
    EntityDecl = 17,
    NamespaceDecl = 18,
    XIncludeStart = 19,
    XIncludeEnd = 20,
    DOCBDocumentNode = 21
}

pub struct xmlAttr {
    _private: *c_void,
    _type: xmlElementType, // AttributeNode
    name: *c_char,
    children: *xmlNode,
    last: *xmlNode,
    parent: *xmlNode,
    next: *xmlAttr,
    prev: *xmlAttr,
    doc: *xmlDoc,
    ns: *xmlNs,
    atype: xmlAttributeType,
    psvi: *c_void
}

#[allow(dead_code)]
pub struct xmlAttribute {
    _private: *c_void,
    _type: xmlElementType, // AttributeDecl
    name: *c_char,
    children: *xmlNode, // NULL
    last: *xmlNode, // NULL
    parent: *xmlDtd, // NULL
    next: *xmlNode,
    prev: *xmlNode,
    doc: *xmlDoc,
    nexth: *xmlAttribute,
    atype: xmlAttributeType,
    def: xmlAttributeDefault,
    defaultValue: *xmlChar,
    tree: *xmlEnumeration,
    prefix: *xmlChar,
    elem: *xmlChar
}

#[allow(dead_code)]
pub enum xmlAttributeDefault {
    None = 1,
    Required = 2,
    Implied = 3,
    Fixed = 4
}

pub enum xmlAttributeType {
    CDATA = 1,
    ID = 2,
    IDRef = 3,
    IDRefs = 4,
    Entity = 5,
    Entities = 6,
    NMToken = 7,
    NMTokens = 8,
    Enumeration = 9,
    Notation = 10
}

pub struct xmlDoc {
    _private: *c_void,
    _type: xmlElementType, // DocumentNode
    name: *c_char,
    children: *xmlNode,
    last: *xmlNode,
    parent: *xmlNode,
    next: *xmlNode,
    prev: *xmlNode,
    doc: *xmlDoc,
    compression: c_int,
    standalone: c_int,
    intSubset: *xmlDtd,
    extSubset: *xmlDtd,
    oldNs: *xmlNs,
    version: *c_char,
    encoding: *c_char,
    ids: *c_void,
    refs: *c_void,
    url: *c_char,
    charset: c_int,
    dict: *c_void,
    psvi: *c_void,
    parseFlags: c_int,
    properties: c_int
}

pub struct xmlDtd {
    _private: *c_void,
    _type: xmlElementType, // DTDNode
    name: *c_char,
    children: *xmlNode,
    last: *xmlNode,
    parent: *xmlDoc,
    next: *xmlNode,
    prev: *xmlNode,
    doc: *xmlDoc,
    notations: *c_void,
    elements: *c_void,
    attributes: *c_void,
    entities: *c_void,
    externalId: *xmlChar,
    systemId: *xmlChar,
    pentities: *c_void
}

#[allow(dead_code)]
pub struct xmlElement {
    _private: *c_void,
    _type: xmlElementType, // ElementDecl
    name: *c_char,
    children: *xmlNode, // NULL
    last: *xmlNode, // NULL
    parent: *xmlDtd,
    next: *xmlNode,
    prev: *xmlNode,
    doc: *xmlDoc,
    etype: xmlElementTypeVal,
    content: xmlElementContent,
    attributes: *xmlAttribute,
    prefix: *xmlChar,
    contModel: *c_void
}

#[allow(dead_code)]
pub enum xmlElementTypeVal {
    Undefined,
    Empty,
    Any,
    Mixed,
    ElementType
}

#[allow(dead_code)]
pub struct xmlElementContent {
    _type: xmlElementContentType,
    ocur: xmlElementContentOccur,
    name: *xmlChar,
    first: *xmlElementContent,
    second: *xmlElementContent,
    parent: *xmlElementContent,
    prefix: *xmlChar
}

#[allow(dead_code)]
pub enum xmlElementContentType {
    PCData,
    ElementContent,
    Seq,
    Or
}

#[allow(dead_code)]
pub enum xmlElementContentOccur {
    Once,
    Opt,
    Mult,
    Plus
}

#[allow(dead_code)]
pub struct xmlEntity {
    _private: *c_void,
    _type: xmlElementType, // EntityDecl
    name: *c_char,
    children: *xmlNode,
    last: *xmlNode,
    parent: *xmlDtd,
    next: *xmlNode,
    prev: *xmlNode,
    doc: *xmlDoc,
    orig: *xmlChar,
    content: *xmlChar,
    length: xmlEntityType,
    externalID: *xmlChar,
    systemID: *xmlChar,
    nexte: *c_void,
    uri: *xmlChar,
    owner: c_int,
    checked: c_int
}

#[allow(dead_code)]
pub enum xmlEntityType {
    InternalGeneralEntity = 1,
    ExternalGeneralParsedEntity = 2,
    ExternalGeneralUnparsedEntity = 3,
    InternalParameterEntity = 4,
    ExternalParameterEntity = 5,
    InternalPredefinedEntity = 6
}

#[allow(dead_code)]
pub struct xmlEnumeration {
    next: *xmlEnumeration,
    name: *xmlChar
}

pub struct xmlNode {
    _private: *c_void,
    _type: xmlElementType,
    name: *xmlChar,
    children: *xmlNode,
    last: *xmlNode,
    parent: *xmlNode,
    next: *xmlNode,
    prev: *xmlNode,
    doc: *xmlDoc,
    ns: *xmlNs,
    content: *xmlChar,
    properties: *xmlAttr,
    nsDef: *xmlNs,
    psvi: *c_void,
    line: c_ushort,
    extra: c_ushort
}

pub struct xmlNs {
    next: *xmlNs,
    _type: xmlElementType,
    href: *xmlChar,
    prefix: xmlChar,
    private: *c_void,
    context: *xmlDoc
}

#[link(name = "xml2")]
extern "C" {
    pub fn xmlCheckVersion(version: c_int);

    // Parser API
    pub fn xmlReadMemory(buffer: *c_uchar, size: c_int, name: *c_char, encoding: *c_char, options: c_int) -> *xmlDoc;

    // Tree API
    pub fn xmlDocGetRootElement(doc: *xmlDoc) -> *xmlNode;
    pub fn xmlFreeDoc(doc: *xmlDoc);
}

pub static xmlVersion : c_int = 20901;
<commit_msg>Add #[repr(C)] to C enums<commit_after>/*
 * Copyright (C) 2014 Maciej Piechotka
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

use std::libc::{c_char, c_int, c_uchar, c_ushort, c_void};

pub type xmlChar = c_uchar;

#[deriving(ToStr)]
#[repr(C)]
pub enum xmlElementType {
    ElementNode = 1,
    AttributeNode = 2,
    TextNode = 3,
    CDATASectionNode = 4,
    EntityRefNode = 5,
    EntityNode = 6,
    PINode = 7,
    CommentNode = 8,
    DocumentNode = 9,
    DocumentTypeNode = 10,
    DocumentFragNode = 11,
    NotationNode = 12,
    HtmlDocumentNode = 13,
    DTDNode = 14,
    ElementDecl = 15,
    AttributeDecl = 16,
    EntityDecl = 17,
    NamespaceDecl = 18,
    XIncludeStart = 19,
    XIncludeEnd = 20,
    DOCBDocumentNode = 21
}

pub struct xmlAttr {
    _private: *c_void,
    _type: xmlElementType, // AttributeNode
    name: *c_char,
    children: *xmlNode,
    last: *xmlNode,
    parent: *xmlNode,
    next: *xmlAttr,
    prev: *xmlAttr,
    doc: *xmlDoc,
    ns: *xmlNs,
    atype: xmlAttributeType,
    psvi: *c_void
}

#[allow(dead_code)]
pub struct xmlAttribute {
    _private: *c_void,
    _type: xmlElementType, // AttributeDecl
    name: *c_char,
    children: *xmlNode, // NULL
    last: *xmlNode, // NULL
    parent: *xmlDtd, // NULL
    next: *xmlNode,
    prev: *xmlNode,
    doc: *xmlDoc,
    nexth: *xmlAttribute,
    atype: xmlAttributeType,
    def: xmlAttributeDefault,
    defaultValue: *xmlChar,
    tree: *xmlEnumeration,
    prefix: *xmlChar,
    elem: *xmlChar
}

#[allow(dead_code)]
#[repr(C)]
pub enum xmlAttributeDefault {
    None = 1,
    Required = 2,
    Implied = 3,
    Fixed = 4
}

#[repr(C)]
pub enum xmlAttributeType {
    CDATA = 1,
    ID = 2,
    IDRef = 3,
    IDRefs = 4,
    Entity = 5,
    Entities = 6,
    NMToken = 7,
    NMTokens = 8,
    Enumeration = 9,
    Notation = 10
}

pub struct xmlDoc {
    _private: *c_void,
    _type: xmlElementType, // DocumentNode
    name: *c_char,
    children: *xmlNode,
    last: *xmlNode,
    parent: *xmlNode,
    next: *xmlNode,
    prev: *xmlNode,
    doc: *xmlDoc,
    compression: c_int,
    standalone: c_int,
    intSubset: *xmlDtd,
    extSubset: *xmlDtd,
    oldNs: *xmlNs,
    version: *c_char,
    encoding: *c_char,
    ids: *c_void,
    refs: *c_void,
    url: *c_char,
    charset: c_int,
    dict: *c_void,
    psvi: *c_void,
    parseFlags: c_int,
    properties: c_int
}

pub struct xmlDtd {
    _private: *c_void,
    _type: xmlElementType, // DTDNode
    name: *c_char,
    children: *xmlNode,
    last: *xmlNode,
    parent: *xmlDoc,
    next: *xmlNode,
    prev: *xmlNode,
    doc: *xmlDoc,
    notations: *c_void,
    elements: *c_void,
    attributes: *c_void,
    entities: *c_void,
    externalId: *xmlChar,
    systemId: *xmlChar,
    pentities: *c_void
}

#[allow(dead_code)]
pub struct xmlElement {
    _private: *c_void,
    _type: xmlElementType, // ElementDecl
    name: *c_char,
    children: *xmlNode, // NULL
    last: *xmlNode, // NULL
    parent: *xmlDtd,
    next: *xmlNode,
    prev: *xmlNode,
    doc: *xmlDoc,
    etype: xmlElementTypeVal,
    content: xmlElementContent,
    attributes: *xmlAttribute,
    prefix: *xmlChar,
    contModel: *c_void
}

#[allow(dead_code)]
#[repr(C)]
pub enum xmlElementTypeVal {
    Undefined,
    Empty,
    Any,
    Mixed,
    ElementType
}

#[allow(dead_code)]
pub struct xmlElementContent {
    _type: xmlElementContentType,
    ocur: xmlElementContentOccur,
    name: *xmlChar,
    first: *xmlElementContent,
    second: *xmlElementContent,
    parent: *xmlElementContent,
    prefix: *xmlChar
}

#[allow(dead_code)]
#[repr(C)]
pub enum xmlElementContentType {
    PCData,
    ElementContent,
    Seq,
    Or
}

#[allow(dead_code)]
#[repr(C)]
pub enum xmlElementContentOccur {
    Once,
    Opt,
    Mult,
    Plus
}

#[allow(dead_code)]
pub struct xmlEntity {
    _private: *c_void,
    _type: xmlElementType, // EntityDecl
    name: *c_char,
    children: *xmlNode,
    last: *xmlNode,
    parent: *xmlDtd,
    next: *xmlNode,
    prev: *xmlNode,
    doc: *xmlDoc,
    orig: *xmlChar,
    content: *xmlChar,
    length: xmlEntityType,
    externalID: *xmlChar,
    systemID: *xmlChar,
    nexte: *c_void,
    uri: *xmlChar,
    owner: c_int,
    checked: c_int
}

#[allow(dead_code)]
#[repr(C)]
pub enum xmlEntityType {
    InternalGeneralEntity = 1,
    ExternalGeneralParsedEntity = 2,
    ExternalGeneralUnparsedEntity = 3,
    InternalParameterEntity = 4,
    ExternalParameterEntity = 5,
    InternalPredefinedEntity = 6
}

#[allow(dead_code)]
pub struct xmlEnumeration {
    next: *xmlEnumeration,
    name: *xmlChar
}

pub struct xmlNode {
    _private: *c_void,
    _type: xmlElementType,
    name: *xmlChar,
    children: *xmlNode,
    last: *xmlNode,
    parent: *xmlNode,
    next: *xmlNode,
    prev: *xmlNode,
    doc: *xmlDoc,
    ns: *xmlNs,
    content: *xmlChar,
    properties: *xmlAttr,
    nsDef: *xmlNs,
    psvi: *c_void,
    line: c_ushort,
    extra: c_ushort
}

pub struct xmlNs {
    next: *xmlNs,
    _type: xmlElementType,
    href: *xmlChar,
    prefix: xmlChar,
    private: *c_void,
    context: *xmlDoc
}

#[link(name = "xml2")]
extern "C" {
    pub fn xmlCheckVersion(version: c_int);

    // Parser API
    pub fn xmlReadMemory(buffer: *c_uchar, size: c_int, name: *c_char, encoding: *c_char, options: c_int) -> *xmlDoc;

    // Tree API
    pub fn xmlDocGetRootElement(doc: *xmlDoc) -> *xmlNode;
    pub fn xmlFreeDoc(doc: *xmlDoc);
}

pub static xmlVersion : c_int = 20901;
<|endoftext|>


<commit_before>//! The `ztext` module contains encoding functions to encode text in z-ascii characters.
//!

use super::zbytes::Bytes;

pub static ALPHABET: [char; 78] = [
    'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
    'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',

    'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
    'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',

    '\0', '^', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.',
    ',', '!', '?', '_', '#', '\'','"', '/', '\\','-', ':', '(', ')'];

/// encodes an string to z-characters
/// and returns the length of the used bytes
/// TODO: Only works with lower case letters
///
/// # Examples
///
/// ```ignore
/// let data = Bytes{bytes: Vec::new()};
/// let byteLength = data.encode("hello");
/// ```
pub fn encode(data: &mut Bytes, content: &str) -> u16 {
    let string_bytes = content.to_string().into_bytes();
    let mut temp: Vec<i8> = Vec::new();
    for i in string_bytes{
        let t_index = pos_in_alpha(i as u8);
        if i == 0x0A {
            println!("newline");
        } else if i == 0x20 {
            temp.push(0x05 as i8);
            temp.push(0);
        }
        else {
            if t_index >52 {
                temp.push(0x05 as i8);
                temp.push(t_index % 26 + 6);
            } else if t_index <27 {
                temp.push(t_index % 26 + 6);
            }
             else {
                temp.push(0x04 as i8);
                temp.push(t_index % 26 + 6);
            }
        }
    }

    let mut two_bytes: u16 = 0;
    let len = temp.len();
    for i in 0..len {
        let zasci_id =temp[i];

        //two_bytes |= (zasci_id as u16) << shift(i as u8);
        two_bytes |= shift(zasci_id as u16, i as u8);

        if i % 3 == 2 {
            data.write_u16(two_bytes, pos_to_index(i));
            two_bytes = 0;
        }

        // end of string
        if i == len -1 {
            if i % 3 != 2 {
                for j in (i % 3) + 1..3 {
                    //two_bytes |= (0x05 as u16) << shift(j as u8);
                    two_bytes |= shift(0x05 as u16, j as u8);
                }

                data.write_u16(two_bytes, pos_to_index(i));
            }

            // end bit is written to the first bit of the next to last byte
            let end_index: usize = data.bytes.len() - 2 as usize;
            let mut end_byte: u8 = data.bytes[end_index];

            end_byte |= 0x80;
            data.write_byte(end_byte, end_index as usize);
        }
    }

    data.bytes.len() as u16
}

/// shifts the z-char in a 2 bytes-array to the right position
/// shift_length has 3 possibilities: 10, 5, 0
/// an z-char use 5 bytes
/// look into two bytes:
/// ____byte one____ ____byte two____
/// 1 2 3 4 5 6  7 8 1 2 3  4 5 6 7 8
///   ^          ^          1
///   1 2 3 4 5  1 2 3 4 5  1 2 3 4 5
///   1. zchar   2. zchar   3. zchar
///   10         5          0
fn shift(zchar: u16, position: u8) -> u16 {
    let shift_length = 10 - (position % 3) * 5;
    zchar << shift_length
}

/// Returns the location of the character of the specified index in the zcode character array
///
/// # Examples
///
/// ```ignore
/// assert_eq!(pos_in_alpha('c'), 2);
/// ```
fn pos_in_alpha(letter: u8) -> i8 {
    for i in 0..ALPHABET.len() {
        if ALPHABET[i] as u8 == letter {
            return i as i8
        }
    }

    return -1
}

/// position in the vector from the position of an character in the string
/// every 3 zchars encode() writes 2 bytes
/// for example "helloworld"
/// "h": nothing
/// "e": nothing
/// "l": write 2 bytes to position 0   (2 * 2/3))
/// "l": nothing
/// "o": nothing
/// "w": write 2 bytes to position 2   (2 * 5/3))
/// ...
///
/// # Examples
///
/// ```ignore
/// assert_eq!(pos_to_index(5), 2);
/// ```
fn pos_to_index(position: usize) -> usize {
    2 * (position / 3)
}
<commit_msg>refactored, better style, kitten approved<commit_after>//! The `ztext` module contains encoding functions to encode text in z-ascii characters.
//!

use super::zbytes::Bytes;

pub static ALPHABET: [char; 78] = [
    'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
    'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',

    'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
    'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',

    '\0', '^', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.',
    ',', '!', '?', '_', '#', '\'','"', '/', '\\','-', ':', '(', ')'];

/// encodes an string to z-characters
/// and returns the length of the used bytes
/// TODO: Only works with lower case letters
///
/// # Examples
///
/// ```ignore
/// let data = Bytes{bytes: Vec::new()};
/// let byteLength = data.encode("hello");
/// ```
pub fn encode(data: &mut Bytes, content: &str) -> u16 {
    let temp: Vec<i8> = stringtozchar(content);

    let mut two_bytes: u16 = 0;
    let len = temp.len();
    for i in 0..len {
        let zasci_id =temp[i];

        two_bytes |= shift(zasci_id as u16, i as u8);

        if i % 3 == 2 {
            data.write_u16(two_bytes, pos_to_index(i));
            two_bytes = 0;
        }

        // end of string
        if i == len -1 {
            if i % 3 != 2 {
                for j in (i % 3) + 1..3 {
                    two_bytes |= shift(0x05 as u16, j as u8);
                }

                data.write_u16(two_bytes, pos_to_index(i));
            }

            // end bit is written to the first bit of the next to last byte
            let end_index: usize = data.bytes.len() - 2 as usize;
            let mut end_byte: u8 = data.bytes[end_index];

            end_byte |= 0x80;
            data.write_byte(end_byte, end_index as usize);
        }
    }

    data.bytes.len() as u16
}

/// reads the content and converts it to a zasci vector
fn stringtozchar(content: &str) -> Vec<i8> {
    let string_bytes = content.to_string().into_bytes();
        let mut temp: Vec<i8> = Vec::new();
        for i in string_bytes{
            let t_index = pos_in_alpha(i as u8);
            if i == 0x0A {
                println!("newline");
            } else if i == 0x20 {
                temp.push(0x05 as i8);
                temp.push(0);
            }
            else {
                if t_index >52 {
                    temp.push(0x05 as i8);
                    temp.push(t_index % 26 + 6);
                } else if t_index <27 {
                    temp.push(t_index % 26 + 6);
                }
                 else {
                    temp.push(0x04 as i8);
                    temp.push(t_index % 26 + 6);
                }
            }
        }
    temp
}

/// shifts the z-char in a 2 bytes-array to the right position
/// shift_length has 3 possibilities: 10, 5, 0
/// an z-char use 5 bytes
/// look into two bytes:
/// ____byte one____ ____byte two____
/// 1 2 3 4 5 6  7 8 1 2 3  4 5 6 7 8
///   ^          ^          1
///   1 2 3 4 5  1 2 3 4 5  1 2 3 4 5
///   1. zchar   2. zchar   3. zchar
///   10         5          0
fn shift(zchar: u16, position: u8) -> u16 {
    let shift_length = 10 - (position % 3) * 5;
    zchar << shift_length
}

/// Returns the location of the character of the specified index in the zcode character array
///
/// # Examples
///
/// ```ignore
/// assert_eq!(pos_in_alpha('c'), 2);
/// ```
fn pos_in_alpha(letter: u8) -> i8 {
    for i in 0..ALPHABET.len() {
        if ALPHABET[i] as u8 == letter {
            return i as i8
        }
    }

    return -1
}

/// position in the vector from the position of an character in the string
/// every 3 zchars encode() writes 2 bytes
/// for example "helloworld"
/// "h": nothing
/// "e": nothing
/// "l": write 2 bytes to position 0   (2 * 2/3))
/// "l": nothing
/// "o": nothing
/// "w": write 2 bytes to position 2   (2 * 5/3))
/// ...
///
/// # Examples
///
/// ```ignore
/// assert_eq!(pos_to_index(5), 2);
/// ```
fn pos_to_index(position: usize) -> usize {
    2 * (position / 3)
}
<|endoftext|>


<commit_before>
use std::env;
use std::process::{Command, Stdio};
use std::io::Write;

macro_rules! try_log {
    ($exp:expr) => {
        match $exp {
            Ok(v) => v,
            Err(e) => { println!("<3>{}", e); return; }
        }
    }
}

fn get_systemd_unit_property(unit: &str, prop: &str) -> String {
    String::from_utf8_lossy(&try_log!(Command::new("systemctl")
                            .arg("show")
                            .arg(unit)
                            .arg("--property")
                            .arg(prop)
                            .output())
                            .stdout[prop.len() + 1..])
        .trim_right_matches('\n')
        .to_owned()
}

fn main() {
    kernlog::init().unwrap();

    let unit = match env::args().nth(1) {
        Some(unit) => unit,
        None => {
            println!("Usage: mail-on-failure <unit>");
            return;
        }
    };

    let mut user = get_systemd_unit_property(&*unit, "User");
    if user.len() == 0 {
        user = "root".to_owned();
    }

    let job_env = get_systemd_unit_property(&*unit, "Environment");
    for pair in job_env.split(' ') {
        let mut p = pair.splitn(2, '=');
        if let (Some(name), Some(value)) = (p.next(), p.next()) {
            if name == "MAILTO" {
                user = value.to_owned();
                break;
            }
        }
    }

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

    let mut hostname = String::from_utf8_lossy(&try_log!(Command::new("uname")
        .arg("-n")
        .output())
        .stdout[..])
        .trim_right_matches('\n')
        .to_owned();

    if hostname.len() == 0 {
        hostname = "localhost".to_owned();
    }

    let mut head = String::new();
    head.push_str("From: root (systemd-cron)\nTo: ");
    head.push_str(&*user);
    head.push_str("\nSubject: [");
    head.push_str(&*hostname);
    head.push_str("] job ");
    head.push_str(&*unit);
    head.push_str(r###" failed
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Auto-Submitted: auto-generated

"###);

    let status = Command::new("systemctl")
        .arg("status")
        .arg(&*unit)
        .output()
        .unwrap();

    let mut mailer = try_log!(Command::new("sendmail")
        .arg("-i")
        .arg("-B8BITMIME")
        .arg(&*user)
        .stdin(Stdio::piped())
        .spawn());

    if let Some(ref mut stdin) = mailer.stdin {
        try_log!(stdin.write_all(head.as_bytes()).and_then(|_|
                 stdin.write_all(&*status.stdout)));
    }

    mailer.wait().unwrap();
}
<commit_msg>escalate errors from unit property getter<commit_after>
use std::env;
use std::process::{Command, Stdio};
use std::io::{Write, Result};

macro_rules! try_log {
    ($exp:expr) => {
        match $exp {
            Ok(v) => v,
            Err(e) => { println!("<3>{}", e); return; }
        }
    }
}

fn get_systemd_unit_property(unit: &str, prop: &str) -> Result<String> {
    Command::new("systemctl")
        .arg("show")
        .arg(unit)
        .arg("--property")
        .arg(prop)
        .output()
        .map(|out|
             String::from_utf8_lossy(&out.stdout[prop.len() + 1..])
             .trim_right_matches('\n')
             .to_owned())
}

fn main() {
    let unit = match env::args().nth(1) {
        Some(unit) => unit,
        None => {
            println!("Usage: mail-on-failure <unit>");
            return;
        }
    };

    let mut user = try_log!(get_systemd_unit_property(&*unit, "User"));
    if user.len() == 0 {
        user = "root".to_owned();
    }

    let job_env = try_log!(get_systemd_unit_property(&*unit, "Environment"));
    for pair in job_env.split(' ') {
        let mut p = pair.splitn(2, '=');
        if let (Some(name), Some(value)) = (p.next(), p.next()) {
            if name == "MAILTO" {
                user = value.to_owned();
                break;
            }
        }
    }

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

    let mut hostname = String::from_utf8_lossy(&try_log!(Command::new("uname")
        .arg("-n")
        .output())
        .stdout[..])
        .trim_right_matches('\n')
        .to_owned();

    if hostname.len() == 0 {
        hostname = "localhost".to_owned();
    }

    let mut head = String::new();
    head.push_str("From: root (systemd-cron)\nTo: ");
    head.push_str(&*user);
    head.push_str("\nSubject: [");
    head.push_str(&*hostname);
    head.push_str("] job ");
    head.push_str(&*unit);
    head.push_str(r###" failed
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Auto-Submitted: auto-generated

"###);

    let status = Command::new("systemctl")
        .arg("status")
        .arg(&*unit)
        .output()
        .unwrap();

    let mut mailer = try_log!(Command::new("sendmail")
        .arg("-i")
        .arg("-B8BITMIME")
        .arg(&*user)
        .stdin(Stdio::piped())
        .spawn());

    if let Some(ref mut stdin) = mailer.stdin {
        try_log!(stdin.write_all(head.as_bytes()).and_then(|_|
                 stdin.write_all(&*status.stdout)));
    }

    mailer.wait().unwrap();
}
<|endoftext|>