<commit_before>use std::path::{Path, PathBuf};
use std::sync::mpsc::channel;
use std::sync::{Mutex, Arc};
use std::time::Duration;
use std::thread;
use std::net::{self, ToSocketAddrs};

use notify::{
    RecommendedWatcher,
    Watcher,
    RecursiveMode,
    DebouncedEvent
};

use clap;
use rouille;

use cargo_shim::{
    Profile,
    CargoPackage,
    CargoProject,
    TargetKind,
    CargoTarget,
    BuildConfig
};

use build::{
    BuildArgsMatcher,
    set_rust_flags,
    run_with_broken_first_build_hack
};
use config::Config;
use emscripten::check_for_emcc;
use error::Error;
use utils::{
    CommandExt,
    read,
    read_bytes
};
use wasm;

const DEFAULT_INDEX_HTML: &'static str = r#"
<!DOCTYPE html>
<head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=1" name="viewport" />
    <script>
        var Module = {};
        var __cargo_web = {};
        Object.defineProperty( Module, 'canvas', {
            get: function() {
                if( __cargo_web.canvas ) {
                    return __cargo_web.canvas;
                }

                var canvas = document.createElement( 'canvas' );
                document.querySelector( 'body' ).appendChild( canvas );
                __cargo_web.canvas = canvas;

                return canvas;
            }
        });
    </script>
</head>
<body>
    <script src="js/app.js"></script>
</body>
</html>
"#;

struct Output {
    path: PathBuf,
    data: Vec< u8 >
}

impl AsRef< Path > for Output {
    fn as_ref( &self ) -> &Path {
        &self.path
    }
}

impl Output {
    fn has_extension( &self, extension: &str ) -> bool {
        self.path.extension().map( |ext| ext == extension ).unwrap_or( false )
    }

    fn is_js( &self ) -> bool {
        self.has_extension( "js" )
    }

    fn is_wasm( &self ) -> bool {
        self.has_extension( "wasm" )
    }
}

fn monitor_for_changes_and_rebuild(
    package: &CargoPackage,
    target: &CargoTarget,
    build: BuildConfig,
    extra_path: Option< &Path >,
    outputs: Arc< Mutex< Vec< Output > > >
) -> RecommendedWatcher {
    let (tx, rx) = channel();
    let mut watcher: RecommendedWatcher = Watcher::new( tx, Duration::from_millis( 500 ) ).unwrap();

    // TODO: Support local dependencies.
    // TODO: Support Cargo.toml reloading.
    watcher.watch( &target.source_directory, RecursiveMode::Recursive ).unwrap();
    watcher.watch( &package.manifest_path, RecursiveMode::NonRecursive ).unwrap();

    let extra_path = extra_path.map( |path| path.to_owned() );
    thread::spawn( move || {
        let rx = rx;
        while let Ok( event ) = rx.recv() {
            match event {
                DebouncedEvent::Create( _ ) |
                DebouncedEvent::Remove( _ ) |
                DebouncedEvent::Rename( _, _ ) |
                DebouncedEvent::Write( _ ) => {},
                _ => continue
            };

            println_err!( "==== Triggering `cargo build` ====" );

            let mut command = build.as_command();
            if let Some( ref extra_path ) = extra_path {
                command.append_to_path( extra_path );
            }

            if command.run().is_ok() {
                let mut outputs = outputs.lock().unwrap();
                wasm::process_wasm_files( &build, &outputs );

                for output in outputs.iter_mut() {
                    if let Ok( data ) = read_bytes( &output.path ) {
                        output.data = data;
                    }
                }
            }
        }
    });

    watcher
}

fn address_or_default< 'a >( matches: &clap::ArgMatches< 'a > ) -> net::SocketAddr {
    let host = matches.value_of( "host" ).unwrap_or( "localhost" );
    let port = matches.value_of( "port" ).unwrap_or( "8000" );
    format!( "{}:{}", host, port ).to_socket_addrs().unwrap().next().unwrap()
}

pub fn command_start< 'a >( matches: &clap::ArgMatches< 'a >, project: &CargoProject ) -> Result< (), Error > {
    let build_matcher = BuildArgsMatcher {
        matches: matches,
        project: project
    };

    let use_system_emscripten = matches.is_present( "use-system-emscripten" );
    let targeting_native_wasm = build_matcher.targeting_native_wasm();
    let targeting_webasm = build_matcher.targeting_wasm();
    let extra_path = if !build_matcher.targeting_emscripten() { None } else { check_for_emcc( use_system_emscripten, targeting_webasm ) };

    let package = build_matcher.package_or_default()?;
    let config = Config::load_for_package_printing_warnings( &package ).unwrap().unwrap_or_default();
    set_rust_flags( &config, &build_matcher );

    let targets = build_matcher.target_or_select( package, |target| {
        target.kind == TargetKind::Bin
    })?;

    if targets.is_empty() {
        return Err(
            Error::ConfigurationError( format!( "cannot start a webserver for a crate which is a library!" ) )
        );
    }

    let target = &targets[ 0 ];
    let build_config = build_matcher.build_config( package, target, Profile::Main );

    let mut command = build_config.as_command();
    if let Some( ref extra_path ) = extra_path {
        command.append_to_path( extra_path );
    }

    run_with_broken_first_build_hack( package, &build_config, &mut command )?;

    let artifacts = build_config.potential_artifacts( &package.crate_root );

    let output_path = &artifacts[ 0 ];
    let mut wasm_path = output_path.with_extension( "wasm" );
    if build_matcher.targeting_emscripten_wasm() {
        wasm_path =
            wasm_path.parent().unwrap().join(
                wasm_path.file_name().unwrap().to_string_lossy().into_owned().replace( '-', "_" )
            );
    }
    let wasm_url = format!( "/{}", wasm_path.file_name().unwrap().to_str().unwrap() );
    let mut outputs = vec![
        Output {
            path: output_path.to_owned(),
            data: read_bytes( output_path ).unwrap()
        }
    ];

    if targeting_webasm {
        outputs.push( Output {
            path: wasm_path.clone(),
            data: read_bytes( wasm_path ).unwrap(),
        });
    }

    if targeting_native_wasm {
        let js_path = output_path.with_extension( "js" );
        outputs.push( Output {
            path: js_path.clone(),
            data: read_bytes( js_path ).unwrap()
        });
    }

    let outputs = Arc::new( Mutex::new( outputs ) );

    #[allow(unused_variables)]
    let watcher = monitor_for_changes_and_rebuild( &package, &target, build_config, extra_path.as_ref().map( |path| path.as_path() ), outputs.clone() );

    let crate_static_path = package.crate_root.join( "static" );
    let target_static_path = match target.kind {
        TargetKind::Example => Some( target.source_directory.join( format!( "{}-static", target.name ) ) ),
        TargetKind::Bin => Some( target.source_directory.join( "static" ) ),
        _ => None
    };

    let address = address_or_default( matches );
    let server = rouille::Server::new( &address, move |request| {
        let mut response;

        if let Some( ref target_static_path ) = target_static_path {
            response = rouille::match_assets( &request, target_static_path );
            if response.is_success() {
                return response;
            }
        }

        response = rouille::match_assets( &request, &crate_static_path );
        if response.is_success() {
            return response;
        }

        let url = request.url();
        response = if url == "/" || url == "index.html" {
            let data = target_static_path.as_ref().and_then( |path| {
                read( path.join( "index.html" ) ).ok()
            }).or_else( || {
                read( crate_static_path.join( "index.html" ) ).ok()
            });

            if let Some( data ) = data {
                rouille::Response::html( data )
            } else {
                rouille::Response::html( DEFAULT_INDEX_HTML )
            }
        } else if url == "/js/app.js" {
            let data = outputs.lock().unwrap().iter().find( |output| output.is_js() ).unwrap().data.clone();
            rouille::Response::from_data( "application/javascript", data )
        } else if url == wasm_url {
            let data = outputs.lock().unwrap().iter().find( |output| output.is_wasm() ).unwrap().data.clone();
            rouille::Response::from_data( "application/wasm", data )
        } else {
            rouille::Response::empty_404()
        };

        response.with_no_cache()
    }).unwrap();

    println_err!( "" );
    println_err!( "If you need to serve any extra files put them in the 'static' directory" );
    println_err!( "in the root of your crate; they will be served alongside your application." );
    match target.kind {
        TargetKind::Example => println_err!( "You can also put a '{}-static' directory in your 'examples' directory.", target.name ),
        TargetKind::Bin => println_err!( "You can also put a 'static' directory in your 'src' directory." ),
        _ => unreachable!()
    };
    println_err!( "" );
    println_err!( "Your application is being served at '/js/app.js'. It will be automatically" );
    println_err!( "rebuilt if you make any changes in your code." );
    println_err!( "" );
    println_err!( "You can access the web server at `http://{}`.", &address );

    server.run();
    Ok(())
}
<commit_msg>Turn off caching for static assets when running `cargo web start`<commit_after>use std::path::{Path, PathBuf};
use std::sync::mpsc::channel;
use std::sync::{Mutex, Arc};
use std::time::Duration;
use std::thread;
use std::net::{self, ToSocketAddrs};

use notify::{
    RecommendedWatcher,
    Watcher,
    RecursiveMode,
    DebouncedEvent
};

use clap;
use rouille;

use cargo_shim::{
    Profile,
    CargoPackage,
    CargoProject,
    TargetKind,
    CargoTarget,
    BuildConfig
};

use build::{
    BuildArgsMatcher,
    set_rust_flags,
    run_with_broken_first_build_hack
};
use config::Config;
use emscripten::check_for_emcc;
use error::Error;
use utils::{
    CommandExt,
    read,
    read_bytes
};
use wasm;

const DEFAULT_INDEX_HTML: &'static str = r#"
<!DOCTYPE html>
<head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=1" name="viewport" />
    <script>
        var Module = {};
        var __cargo_web = {};
        Object.defineProperty( Module, 'canvas', {
            get: function() {
                if( __cargo_web.canvas ) {
                    return __cargo_web.canvas;
                }

                var canvas = document.createElement( 'canvas' );
                document.querySelector( 'body' ).appendChild( canvas );
                __cargo_web.canvas = canvas;

                return canvas;
            }
        });
    </script>
</head>
<body>
    <script src="js/app.js"></script>
</body>
</html>
"#;

struct Output {
    path: PathBuf,
    data: Vec< u8 >
}

impl AsRef< Path > for Output {
    fn as_ref( &self ) -> &Path {
        &self.path
    }
}

impl Output {
    fn has_extension( &self, extension: &str ) -> bool {
        self.path.extension().map( |ext| ext == extension ).unwrap_or( false )
    }

    fn is_js( &self ) -> bool {
        self.has_extension( "js" )
    }

    fn is_wasm( &self ) -> bool {
        self.has_extension( "wasm" )
    }
}

fn monitor_for_changes_and_rebuild(
    package: &CargoPackage,
    target: &CargoTarget,
    build: BuildConfig,
    extra_path: Option< &Path >,
    outputs: Arc< Mutex< Vec< Output > > >
) -> RecommendedWatcher {
    let (tx, rx) = channel();
    let mut watcher: RecommendedWatcher = Watcher::new( tx, Duration::from_millis( 500 ) ).unwrap();

    // TODO: Support local dependencies.
    // TODO: Support Cargo.toml reloading.
    watcher.watch( &target.source_directory, RecursiveMode::Recursive ).unwrap();
    watcher.watch( &package.manifest_path, RecursiveMode::NonRecursive ).unwrap();

    let extra_path = extra_path.map( |path| path.to_owned() );
    thread::spawn( move || {
        let rx = rx;
        while let Ok( event ) = rx.recv() {
            match event {
                DebouncedEvent::Create( _ ) |
                DebouncedEvent::Remove( _ ) |
                DebouncedEvent::Rename( _, _ ) |
                DebouncedEvent::Write( _ ) => {},
                _ => continue
            };

            println_err!( "==== Triggering `cargo build` ====" );

            let mut command = build.as_command();
            if let Some( ref extra_path ) = extra_path {
                command.append_to_path( extra_path );
            }

            if command.run().is_ok() {
                let mut outputs = outputs.lock().unwrap();
                wasm::process_wasm_files( &build, &outputs );

                for output in outputs.iter_mut() {
                    if let Ok( data ) = read_bytes( &output.path ) {
                        output.data = data;
                    }
                }
            }
        }
    });

    watcher
}

fn address_or_default< 'a >( matches: &clap::ArgMatches< 'a > ) -> net::SocketAddr {
    let host = matches.value_of( "host" ).unwrap_or( "localhost" );
    let port = matches.value_of( "port" ).unwrap_or( "8000" );
    format!( "{}:{}", host, port ).to_socket_addrs().unwrap().next().unwrap()
}

pub fn command_start< 'a >( matches: &clap::ArgMatches< 'a >, project: &CargoProject ) -> Result< (), Error > {
    let build_matcher = BuildArgsMatcher {
        matches: matches,
        project: project
    };

    let use_system_emscripten = matches.is_present( "use-system-emscripten" );
    let targeting_native_wasm = build_matcher.targeting_native_wasm();
    let targeting_webasm = build_matcher.targeting_wasm();
    let extra_path = if !build_matcher.targeting_emscripten() { None } else { check_for_emcc( use_system_emscripten, targeting_webasm ) };

    let package = build_matcher.package_or_default()?;
    let config = Config::load_for_package_printing_warnings( &package ).unwrap().unwrap_or_default();
    set_rust_flags( &config, &build_matcher );

    let targets = build_matcher.target_or_select( package, |target| {
        target.kind == TargetKind::Bin
    })?;

    if targets.is_empty() {
        return Err(
            Error::ConfigurationError( format!( "cannot start a webserver for a crate which is a library!" ) )
        );
    }

    let target = &targets[ 0 ];
    let build_config = build_matcher.build_config( package, target, Profile::Main );

    let mut command = build_config.as_command();
    if let Some( ref extra_path ) = extra_path {
        command.append_to_path( extra_path );
    }

    run_with_broken_first_build_hack( package, &build_config, &mut command )?;

    let artifacts = build_config.potential_artifacts( &package.crate_root );

    let output_path = &artifacts[ 0 ];
    let mut wasm_path = output_path.with_extension( "wasm" );
    if build_matcher.targeting_emscripten_wasm() {
        wasm_path =
            wasm_path.parent().unwrap().join(
                wasm_path.file_name().unwrap().to_string_lossy().into_owned().replace( '-', "_" )
            );
    }
    let wasm_url = format!( "/{}", wasm_path.file_name().unwrap().to_str().unwrap() );
    let mut outputs = vec![
        Output {
            path: output_path.to_owned(),
            data: read_bytes( output_path ).unwrap()
        }
    ];

    if targeting_webasm {
        outputs.push( Output {
            path: wasm_path.clone(),
            data: read_bytes( wasm_path ).unwrap(),
        });
    }

    if targeting_native_wasm {
        let js_path = output_path.with_extension( "js" );
        outputs.push( Output {
            path: js_path.clone(),
            data: read_bytes( js_path ).unwrap()
        });
    }

    let outputs = Arc::new( Mutex::new( outputs ) );

    #[allow(unused_variables)]
    let watcher = monitor_for_changes_and_rebuild( &package, &target, build_config, extra_path.as_ref().map( |path| path.as_path() ), outputs.clone() );

    let crate_static_path = package.crate_root.join( "static" );
    let target_static_path = match target.kind {
        TargetKind::Example => Some( target.source_directory.join( format!( "{}-static", target.name ) ) ),
        TargetKind::Bin => Some( target.source_directory.join( "static" ) ),
        _ => None
    };

    let address = address_or_default( matches );
    let server = rouille::Server::new( &address, move |request| {
        let mut response;

        if let Some( ref target_static_path ) = target_static_path {
            response = rouille::match_assets( &request, target_static_path );
            if response.is_success() {
                return response.with_no_cache();
            }
        }

        response = rouille::match_assets( &request, &crate_static_path );
        if response.is_success() {
            return response.with_no_cache();
        }

        let url = request.url();
        response = if url == "/" || url == "index.html" {
            let data = target_static_path.as_ref().and_then( |path| {
                read( path.join( "index.html" ) ).ok()
            }).or_else( || {
                read( crate_static_path.join( "index.html" ) ).ok()
            });

            if let Some( data ) = data {
                rouille::Response::html( data )
            } else {
                rouille::Response::html( DEFAULT_INDEX_HTML )
            }
        } else if url == "/js/app.js" {
            let data = outputs.lock().unwrap().iter().find( |output| output.is_js() ).unwrap().data.clone();
            rouille::Response::from_data( "application/javascript", data )
        } else if url == wasm_url {
            let data = outputs.lock().unwrap().iter().find( |output| output.is_wasm() ).unwrap().data.clone();
            rouille::Response::from_data( "application/wasm", data )
        } else {
            rouille::Response::empty_404()
        };

        response.with_no_cache()
    }).unwrap();

    println_err!( "" );
    println_err!( "If you need to serve any extra files put them in the 'static' directory" );
    println_err!( "in the root of your crate; they will be served alongside your application." );
    match target.kind {
        TargetKind::Example => println_err!( "You can also put a '{}-static' directory in your 'examples' directory.", target.name ),
        TargetKind::Bin => println_err!( "You can also put a 'static' directory in your 'src' directory." ),
        _ => unreachable!()
    };
    println_err!( "" );
    println_err!( "Your application is being served at '/js/app.js'. It will be automatically" );
    println_err!( "rebuilt if you make any changes in your code." );
    println_err!( "" );
    println_err!( "You can access the web server at `http://{}`.", &address );

    server.run();
    Ok(())
}
<|endoftext|>


<commit_before>// Copyright (c) 2015, Ben Boeckel
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
//     * Redistributions of source code must retain the above copyright notice,
//       this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright notice,
//       this list of conditions and the following disclaimer in the documentation
//       and/or other materials provided with the distribution.
//     * Neither the name of this project nor the names of its contributors
//       may be used to endorse or promote products derived from this software
//       without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

use crates::libkeyutils_sys::*;

/// Special key types
pub enum KeyType {
    /// Keys which can be created, updated and read from userspace
    /// but are not intended for use by the kernel.
    User,
    /// Keys which can only be created and updated from
    /// userspace but not read back. They are intended to be
    /// only accessible from kernel space.
    Logon,
}

impl KeyType {
    /// Retrieve the constant value for the key type
    pub fn value(self) -> &str {
        match self {
            KeyType::User => KEY_TYPE_USER,
            KeyType::Logon => KEY_TYPE_LOGON,
        }
    }
}

/// Special keyrings predefined for a process.
pub enum SpecialKeyring {
    /// A thread-specific keyring.
    ThreadKeyring,
    /// A process-specific keyring.
    ProcessKeyring,
    /// A session-specific keyring.
    SessionKeyring,
    /// TODO
    UserKeyring,
    /// TODO
    UserSessionKeyring,
    /// TODO
    GroupKeyring,
}

/// The kernel type for representing a keyring (or key).
pub type KeyringSerial = i32;

impl SpecialKeyring {
    /// Retrieve the serial number for the special keyring.
    pub fn serial(self) -> KeyringSerial {
        match self {
            SpecialKeyring::ThreadKeyring => KEY_SPEC_THREAD_KEYRING,
            SpecialKeyring::ProcessKeyring => KEY_SPEC_PROCESS_KEYRING,
            SpecialKeyring::SessionKeyring => KEY_SPEC_SESSION_KEYRING,
            SpecialKeyring::UserKeyring => KEY_SPEC_USER_KEYRING,
            SpecialKeyring::UserSessionKeyring => KEY_SPEC_USER_SESSION_KEYRING,
            SpecialKeyring::GroupKeyring => KEY_SPEC_GROUP_KEYRING,
        }
    }
}

/// An enumeration for the keyrings which may be set as the default.
pub enum DefaultKeyring {
    /// TODO
    NoChange,
    /// TODO
    ThreadKeyring,
    /// TODO
    ProcessKeyring,
    /// TODO
    SessionKeyring,
    /// TODO
    UserKeyring,
    /// TODO
    UserSessionKeyring,
    /// TODO
    GroupKeyring,
    /// TODO
    DefaultKeyring,
}

/// The kernel type for representing a default keyring.
pub type KeyringDefaultSerial = i32;

impl DefaultKeyring {
    /// Retrieve the serial number for the default keyring.
    pub fn serial(self) -> KeyringDefaultSerial {
        match self {
            DefaultKeyring::NoChange => KEY_REQKEY_DEFL_NO_CHANGE,
            DefaultKeyring::ThreadKeyring => KEY_REQKEY_DEFL_THREAD_KEYRING,
            DefaultKeyring::ProcessKeyring => KEY_REQKEY_DEFL_PROCESS_KEYRING,
            DefaultKeyring::SessionKeyring => KEY_REQKEY_DEFL_SESSION_KEYRING,
            DefaultKeyring::UserKeyring => KEY_REQKEY_DEFL_USER_KEYRING,
            DefaultKeyring::UserSessionKeyring => KEY_REQKEY_DEFL_USER_SESSION_KEYRING,
            DefaultKeyring::GroupKeyring => KEY_REQKEY_DEFL_GROUP_KEYRING,
            DefaultKeyring::DefaultKeyring => KEY_REQKEY_DEFL_DEFAULT,
        }
    }
}

impl From<i32> for DefaultKeyring {
    fn from(id: i32) -> DefaultKeyring {
        match id {
            KEY_REQKEY_DEFL_NO_CHANGE => DefaultKeyring::NoChange,
            KEY_REQKEY_DEFL_THREAD_KEYRING => DefaultKeyring::ThreadKeyring,
            KEY_REQKEY_DEFL_PROCESS_KEYRING => DefaultKeyring::ProcessKeyring,
            KEY_REQKEY_DEFL_SESSION_KEYRING => DefaultKeyring::SessionKeyring,
            KEY_REQKEY_DEFL_USER_KEYRING => DefaultKeyring::UserKeyring,
            KEY_REQKEY_DEFL_USER_SESSION_KEYRING => DefaultKeyring::UserSessionKeyring,
            KEY_REQKEY_DEFL_GROUP_KEYRING => DefaultKeyring::GroupKeyring,
            KEY_REQKEY_DEFL_DEFAULT => DefaultKeyring::DefaultKeyring,
            _ => panic!("Invalid value for a default keyring: {}", id),
        }
    }
}

/// The kernel type for representing a keyring's (or key's) permission.
///
/// TODO: explain more
pub type KeyPermissions = u32;

bitflags! {
    /// Permission bits for keyring objects.
    ///
    /// Keyrings and keys contain four sets of permissions. First, there are three permission sets
    /// used is based on which of the owning user's ID, the group ID associated with the key or
    /// keyring, and a third set which is used when neither of the other two match.
    ///
    /// The fourth set is combined with the permission set used above (priority to user, then
    /// group, finaly other) where either set granting a permission allows it. This set is,
    /// however, only used if the caller is a "possessor" of they key or keyring. Generally,
    /// "possession" requires the `search` permission, association from the calling thread
    /// (the session, process, and thread keyrings), or is linked to from a possessed keyring. See
    /// `keyrings(7)` for complete details.
    struct KeyringPermission: key_perm_t {
        /// Possession allows viewing attributes about the key or keyring.
        const POSSESSOR_VIEW            = KEY_POS_VIEW;
        /// Possession allows reading a key's contents or a keyring's subkeys.
        const POSSESSOR_READ            = KEY_POS_READ;
        /// Possession allows writing a key's content, revoking a key, or adding and removing a
        /// keyring's links.
        const POSSESSOR_WRITE           = KEY_POS_WRITE;
        /// Possession allows searching within a keyring and the key or keyring may be discovered
        /// during a search.
        const POSSESSOR_SEARCH          = KEY_POS_SEARCH;
        /// Possession allows linking to the key from a keyring.
        const POSSESSOR_LINK            = KEY_POS_LINK;
        /// Possession allows changing ownership details, security labels, and the expiration
        /// time of a key.
        const POSSESSOR_SET_ATTRIBUTE   = KEY_POS_SETATTR;
        /// Possession grants all permissions.
        const POSSESSOR_ALL             = KEY_POS_ALL;

        /// A user ID match allows viewing attributes about the key or keyring.
        const USER_VIEW             = KEY_USR_VIEW;
        /// A user ID match allows reading a key's contents or a keyring's subkeys.
        const USER_READ             = KEY_USR_READ;
        /// A user ID match allows writing a key's content, revoking a key, or adding and removing
        /// a keyring's links.
        const USER_WRITE            = KEY_USR_WRITE;
        /// A user ID match allows searching within a keyring and the key or keyring may be
        /// discovered during a search.
        const USER_SEARCH           = KEY_USR_SEARCH;
        /// A user ID match allows linking to the key from a keyring.
        const USER_LINK             = KEY_USR_LINK;
        /// A user ID match allows changing ownership details, security labels, and the expiration
        /// time of a key.
        const USER_SET_ATTRIBUTE    = KEY_USR_SETATTR;
        /// The user is granted all permissions.
        const USER_ALL              = KEY_USR_ALL;

        /// A group ID match allows viewing attributes about the key or keyring.
        const GROUP_VIEW            = KEY_GRP_VIEW;
        /// A group ID match allows reading a key's contents or a keyring's subkeys.
        const GROUP_READ            = KEY_GRP_READ;
        /// A group ID match allows writing a key's content, revoking a key, or adding and removing
        /// a keyring's links.
        const GROUP_WRITE           = KEY_GRP_WRITE;
        /// A group ID match allows searching within a keyring and the key or keyring may be
        /// discovered during a search.
        const GROUP_SEARCH          = KEY_GRP_SEARCH;
        /// A group ID match allows linking to the key from a keyring.
        const GROUP_LINK            = KEY_GRP_LINK;
        /// A group ID match allows changing ownership details, security labels, and the expiration
        /// time of a key.
        const GROUP_SET_ATTRIBUTE   = KEY_GRP_SETATTR;
        /// The group is granted all permissions.
        const GROUP_ALL             = KEY_GRP_ALL;

        /// Allows viewing attributes about the key or keyring.
        const OTHER_VIEW            = KEY_OTH_VIEW;
        /// Allows reading a key's contents or a keyring's subkeys.
        const OTHER_READ            = KEY_OTH_READ;
        /// Allows writing a key's content, revoking a key, or adding and removing a keyring's
        /// links.
        const OTHER_WRITE           = KEY_OTH_WRITE;
        /// Allows searching within a keyring and the key or keyring may be discovered during a
        /// search.
        const OTHER_SEARCH          = KEY_OTH_SEARCH;
        /// Allows linking to the key from a keyring.
        const OTHER_LINK            = KEY_OTH_LINK;
        /// Allows changing ownership details, security labels, and the expiration time of a key.
        const OTHER_SET_ATTRIBUTE   = KEY_OTH_SETATTR;
        /// All permissions.
        const OTHER_ALL             = KEY_OTH_ALL;
    }
}

#[test]
fn test_keyring_ids() {
    assert_eq!(SpecialKeyring::ThreadKeyring.serial(),
               KEY_SPEC_THREAD_KEYRING);
    assert_eq!(SpecialKeyring::ProcessKeyring.serial(),
               KEY_SPEC_PROCESS_KEYRING);
    assert_eq!(SpecialKeyring::SessionKeyring.serial(),
               KEY_SPEC_SESSION_KEYRING);
    assert_eq!(SpecialKeyring::UserKeyring.serial(), KEY_SPEC_USER_KEYRING);
    assert_eq!(SpecialKeyring::UserSessionKeyring.serial(),
               KEY_SPEC_USER_SESSION_KEYRING);
    assert_eq!(SpecialKeyring::GroupKeyring.serial(),
               KEY_SPEC_GROUP_KEYRING);
}

#[test]
fn test_default_keyring_ids() {
    assert_eq!(DefaultKeyring::NoChange.serial(), KEY_REQKEY_DEFL_NO_CHANGE);
    assert_eq!(DefaultKeyring::ThreadKeyring.serial(),
               KEY_REQKEY_DEFL_THREAD_KEYRING);
    assert_eq!(DefaultKeyring::ProcessKeyring.serial(),
               KEY_REQKEY_DEFL_PROCESS_KEYRING);
    assert_eq!(DefaultKeyring::SessionKeyring.serial(),
               KEY_REQKEY_DEFL_SESSION_KEYRING);
    assert_eq!(DefaultKeyring::UserKeyring.serial(),
               KEY_REQKEY_DEFL_USER_KEYRING);
    assert_eq!(DefaultKeyring::UserSessionKeyring.serial(),
               KEY_REQKEY_DEFL_USER_SESSION_KEYRING);
    assert_eq!(DefaultKeyring::GroupKeyring.serial(),
               KEY_REQKEY_DEFL_GROUP_KEYRING);
    assert_eq!(DefaultKeyring::DefaultKeyring.serial(),
               KEY_REQKEY_DEFL_DEFAULT);
}

#[test]
fn test_permission_bits() {
    assert_eq!(POSSESSOR_VIEW.bits, KEY_POS_VIEW);
    assert_eq!(POSSESSOR_READ.bits, KEY_POS_READ);
    assert_eq!(POSSESSOR_WRITE.bits, KEY_POS_WRITE);
    assert_eq!(POSSESSOR_SEARCH.bits, KEY_POS_SEARCH);
    assert_eq!(POSSESSOR_LINK.bits, KEY_POS_LINK);
    assert_eq!(POSSESSOR_SET_ATTRIBUTE.bits, KEY_POS_SETATTR);
    assert_eq!(POSSESSOR_ALL.bits, KEY_POS_ALL);

    assert_eq!(USER_VIEW.bits, KEY_USR_VIEW);
    assert_eq!(USER_READ.bits, KEY_USR_READ);
    assert_eq!(USER_WRITE.bits, KEY_USR_WRITE);
    assert_eq!(USER_SEARCH.bits, KEY_USR_SEARCH);
    assert_eq!(USER_LINK.bits, KEY_USR_LINK);
    assert_eq!(USER_SET_ATTRIBUTE.bits, KEY_USR_SETATTR);
    assert_eq!(USER_ALL.bits, KEY_USR_ALL);

    assert_eq!(GROUP_VIEW.bits, KEY_GRP_VIEW);
    assert_eq!(GROUP_READ.bits, KEY_GRP_READ);
    assert_eq!(GROUP_WRITE.bits, KEY_GRP_WRITE);
    assert_eq!(GROUP_SEARCH.bits, KEY_GRP_SEARCH);
    assert_eq!(GROUP_LINK.bits, KEY_GRP_LINK);
    assert_eq!(GROUP_SET_ATTRIBUTE.bits, KEY_GRP_SETATTR);
    assert_eq!(GROUP_ALL.bits, KEY_GRP_ALL);

    assert_eq!(OTHER_VIEW.bits, KEY_OTH_VIEW);
    assert_eq!(OTHER_READ.bits, KEY_OTH_READ);
    assert_eq!(OTHER_WRITE.bits, KEY_OTH_WRITE);
    assert_eq!(OTHER_SEARCH.bits, KEY_OTH_SEARCH);
    assert_eq!(OTHER_LINK.bits, KEY_OTH_LINK);
    assert_eq!(OTHER_SET_ATTRIBUTE.bits, KEY_OTH_SETATTR);
    assert_eq!(OTHER_ALL.bits, KEY_OTH_ALL);
}
<commit_msg>constants: rewrap doc comments<commit_after>// Copyright (c) 2015, Ben Boeckel
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
//     * Redistributions of source code must retain the above copyright notice,
//       this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright notice,
//       this list of conditions and the following disclaimer in the documentation
//       and/or other materials provided with the distribution.
//     * Neither the name of this project nor the names of its contributors
//       may be used to endorse or promote products derived from this software
//       without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

use crates::libkeyutils_sys::*;

/// Special key types
pub enum KeyType {
    /// Keys which can be created, updated, and read from userspace but are not intended for use by
    /// the kernel.
    User,
    /// Keys which can only be created and updated from userspace but not read back.
    ///
    /// These are intended to be only accessible from kernel space.
    Logon,
}

impl KeyType {
    /// Retrieve the constant value for the key type.
    pub fn value(self) -> &str {
        match self {
            KeyType::User => KEY_TYPE_USER,
            KeyType::Logon => KEY_TYPE_LOGON,
        }
    }
}

/// Special keyrings predefined for a process.
pub enum SpecialKeyring {
    /// A thread-specific keyring.
    ThreadKeyring,
    /// A process-specific keyring.
    ProcessKeyring,
    /// A session-specific keyring.
    SessionKeyring,
    /// TODO
    UserKeyring,
    /// TODO
    UserSessionKeyring,
    /// TODO
    GroupKeyring,
}

/// The kernel type for representing a keyring (or key).
pub type KeyringSerial = i32;

impl SpecialKeyring {
    /// Retrieve the serial number for the special keyring.
    pub fn serial(self) -> KeyringSerial {
        match self {
            SpecialKeyring::ThreadKeyring => KEY_SPEC_THREAD_KEYRING,
            SpecialKeyring::ProcessKeyring => KEY_SPEC_PROCESS_KEYRING,
            SpecialKeyring::SessionKeyring => KEY_SPEC_SESSION_KEYRING,
            SpecialKeyring::UserKeyring => KEY_SPEC_USER_KEYRING,
            SpecialKeyring::UserSessionKeyring => KEY_SPEC_USER_SESSION_KEYRING,
            SpecialKeyring::GroupKeyring => KEY_SPEC_GROUP_KEYRING,
        }
    }
}

/// An enumeration for the keyrings which may be set as the default.
pub enum DefaultKeyring {
    /// TODO
    NoChange,
    /// TODO
    ThreadKeyring,
    /// TODO
    ProcessKeyring,
    /// TODO
    SessionKeyring,
    /// TODO
    UserKeyring,
    /// TODO
    UserSessionKeyring,
    /// TODO
    GroupKeyring,
    /// TODO
    DefaultKeyring,
}

/// The kernel type for representing a default keyring.
pub type KeyringDefaultSerial = i32;

impl DefaultKeyring {
    /// Retrieve the serial number for the default keyring.
    pub fn serial(self) -> KeyringDefaultSerial {
        match self {
            DefaultKeyring::NoChange => KEY_REQKEY_DEFL_NO_CHANGE,
            DefaultKeyring::ThreadKeyring => KEY_REQKEY_DEFL_THREAD_KEYRING,
            DefaultKeyring::ProcessKeyring => KEY_REQKEY_DEFL_PROCESS_KEYRING,
            DefaultKeyring::SessionKeyring => KEY_REQKEY_DEFL_SESSION_KEYRING,
            DefaultKeyring::UserKeyring => KEY_REQKEY_DEFL_USER_KEYRING,
            DefaultKeyring::UserSessionKeyring => KEY_REQKEY_DEFL_USER_SESSION_KEYRING,
            DefaultKeyring::GroupKeyring => KEY_REQKEY_DEFL_GROUP_KEYRING,
            DefaultKeyring::DefaultKeyring => KEY_REQKEY_DEFL_DEFAULT,
        }
    }
}

impl From<i32> for DefaultKeyring {
    fn from(id: i32) -> DefaultKeyring {
        match id {
            KEY_REQKEY_DEFL_NO_CHANGE => DefaultKeyring::NoChange,
            KEY_REQKEY_DEFL_THREAD_KEYRING => DefaultKeyring::ThreadKeyring,
            KEY_REQKEY_DEFL_PROCESS_KEYRING => DefaultKeyring::ProcessKeyring,
            KEY_REQKEY_DEFL_SESSION_KEYRING => DefaultKeyring::SessionKeyring,
            KEY_REQKEY_DEFL_USER_KEYRING => DefaultKeyring::UserKeyring,
            KEY_REQKEY_DEFL_USER_SESSION_KEYRING => DefaultKeyring::UserSessionKeyring,
            KEY_REQKEY_DEFL_GROUP_KEYRING => DefaultKeyring::GroupKeyring,
            KEY_REQKEY_DEFL_DEFAULT => DefaultKeyring::DefaultKeyring,
            _ => panic!("Invalid value for a default keyring: {}", id),
        }
    }
}

/// The kernel type for representing a keyring's (or key's) permission.
///
/// TODO: explain more
pub type KeyPermissions = u32;

bitflags! {
    /// Permission bits for keyring objects.
    ///
    /// Keyrings and keys contain four sets of permissions. First, there are three permission sets
    /// used is based on which of the owning user's ID, the group ID associated with the key or
    /// keyring, and a third set which is used when neither of the other two match.
    ///
    /// The fourth set is combined with the permission set used above (priority to user, then
    /// group, finaly other) where either set granting a permission allows it. This set is,
    /// however, only used if the caller is a "possessor" of they key or keyring. Generally,
    /// "possession" requires the `search` permission, association from the calling thread
    /// (the session, process, and thread keyrings), or is linked to from a possessed keyring. See
    /// `keyrings(7)` for complete details.
    struct KeyringPermission: key_perm_t {
        /// Possession allows viewing attributes about the key or keyring.
        const POSSESSOR_VIEW            = KEY_POS_VIEW;
        /// Possession allows reading a key's contents or a keyring's subkeys.
        const POSSESSOR_READ            = KEY_POS_READ;
        /// Possession allows writing a key's content, revoking a key, or adding and removing a
        /// keyring's links.
        const POSSESSOR_WRITE           = KEY_POS_WRITE;
        /// Possession allows searching within a keyring and the key or keyring may be discovered
        /// during a search.
        const POSSESSOR_SEARCH          = KEY_POS_SEARCH;
        /// Possession allows linking to the key from a keyring.
        const POSSESSOR_LINK            = KEY_POS_LINK;
        /// Possession allows changing ownership details, security labels, and the expiration
        /// time of a key.
        const POSSESSOR_SET_ATTRIBUTE   = KEY_POS_SETATTR;
        /// Possession grants all permissions.
        const POSSESSOR_ALL             = KEY_POS_ALL;

        /// A user ID match allows viewing attributes about the key or keyring.
        const USER_VIEW             = KEY_USR_VIEW;
        /// A user ID match allows reading a key's contents or a keyring's subkeys.
        const USER_READ             = KEY_USR_READ;
        /// A user ID match allows writing a key's content, revoking a key, or adding and removing
        /// a keyring's links.
        const USER_WRITE            = KEY_USR_WRITE;
        /// A user ID match allows searching within a keyring and the key or keyring may be
        /// discovered during a search.
        const USER_SEARCH           = KEY_USR_SEARCH;
        /// A user ID match allows linking to the key from a keyring.
        const USER_LINK             = KEY_USR_LINK;
        /// A user ID match allows changing ownership details, security labels, and the expiration
        /// time of a key.
        const USER_SET_ATTRIBUTE    = KEY_USR_SETATTR;
        /// The user is granted all permissions.
        const USER_ALL              = KEY_USR_ALL;

        /// A group ID match allows viewing attributes about the key or keyring.
        const GROUP_VIEW            = KEY_GRP_VIEW;
        /// A group ID match allows reading a key's contents or a keyring's subkeys.
        const GROUP_READ            = KEY_GRP_READ;
        /// A group ID match allows writing a key's content, revoking a key, or adding and removing
        /// a keyring's links.
        const GROUP_WRITE           = KEY_GRP_WRITE;
        /// A group ID match allows searching within a keyring and the key or keyring may be
        /// discovered during a search.
        const GROUP_SEARCH          = KEY_GRP_SEARCH;
        /// A group ID match allows linking to the key from a keyring.
        const GROUP_LINK            = KEY_GRP_LINK;
        /// A group ID match allows changing ownership details, security labels, and the expiration
        /// time of a key.
        const GROUP_SET_ATTRIBUTE   = KEY_GRP_SETATTR;
        /// The group is granted all permissions.
        const GROUP_ALL             = KEY_GRP_ALL;

        /// Allows viewing attributes about the key or keyring.
        const OTHER_VIEW            = KEY_OTH_VIEW;
        /// Allows reading a key's contents or a keyring's subkeys.
        const OTHER_READ            = KEY_OTH_READ;
        /// Allows writing a key's content, revoking a key, or adding and removing a keyring's
        /// links.
        const OTHER_WRITE           = KEY_OTH_WRITE;
        /// Allows searching within a keyring and the key or keyring may be discovered during a
        /// search.
        const OTHER_SEARCH          = KEY_OTH_SEARCH;
        /// Allows linking to the key from a keyring.
        const OTHER_LINK            = KEY_OTH_LINK;
        /// Allows changing ownership details, security labels, and the expiration time of a key.
        const OTHER_SET_ATTRIBUTE   = KEY_OTH_SETATTR;
        /// All permissions.
        const OTHER_ALL             = KEY_OTH_ALL;
    }
}

#[test]
fn test_keyring_ids() {
    assert_eq!(SpecialKeyring::ThreadKeyring.serial(),
               KEY_SPEC_THREAD_KEYRING);
    assert_eq!(SpecialKeyring::ProcessKeyring.serial(),
               KEY_SPEC_PROCESS_KEYRING);
    assert_eq!(SpecialKeyring::SessionKeyring.serial(),
               KEY_SPEC_SESSION_KEYRING);
    assert_eq!(SpecialKeyring::UserKeyring.serial(), KEY_SPEC_USER_KEYRING);
    assert_eq!(SpecialKeyring::UserSessionKeyring.serial(),
               KEY_SPEC_USER_SESSION_KEYRING);
    assert_eq!(SpecialKeyring::GroupKeyring.serial(),
               KEY_SPEC_GROUP_KEYRING);
}

#[test]
fn test_default_keyring_ids() {
    assert_eq!(DefaultKeyring::NoChange.serial(), KEY_REQKEY_DEFL_NO_CHANGE);
    assert_eq!(DefaultKeyring::ThreadKeyring.serial(),
               KEY_REQKEY_DEFL_THREAD_KEYRING);
    assert_eq!(DefaultKeyring::ProcessKeyring.serial(),
               KEY_REQKEY_DEFL_PROCESS_KEYRING);
    assert_eq!(DefaultKeyring::SessionKeyring.serial(),
               KEY_REQKEY_DEFL_SESSION_KEYRING);
    assert_eq!(DefaultKeyring::UserKeyring.serial(),
               KEY_REQKEY_DEFL_USER_KEYRING);
    assert_eq!(DefaultKeyring::UserSessionKeyring.serial(),
               KEY_REQKEY_DEFL_USER_SESSION_KEYRING);
    assert_eq!(DefaultKeyring::GroupKeyring.serial(),
               KEY_REQKEY_DEFL_GROUP_KEYRING);
    assert_eq!(DefaultKeyring::DefaultKeyring.serial(),
               KEY_REQKEY_DEFL_DEFAULT);
}

#[test]
fn test_permission_bits() {
    assert_eq!(POSSESSOR_VIEW.bits, KEY_POS_VIEW);
    assert_eq!(POSSESSOR_READ.bits, KEY_POS_READ);
    assert_eq!(POSSESSOR_WRITE.bits, KEY_POS_WRITE);
    assert_eq!(POSSESSOR_SEARCH.bits, KEY_POS_SEARCH);
    assert_eq!(POSSESSOR_LINK.bits, KEY_POS_LINK);
    assert_eq!(POSSESSOR_SET_ATTRIBUTE.bits, KEY_POS_SETATTR);
    assert_eq!(POSSESSOR_ALL.bits, KEY_POS_ALL);

    assert_eq!(USER_VIEW.bits, KEY_USR_VIEW);
    assert_eq!(USER_READ.bits, KEY_USR_READ);
    assert_eq!(USER_WRITE.bits, KEY_USR_WRITE);
    assert_eq!(USER_SEARCH.bits, KEY_USR_SEARCH);
    assert_eq!(USER_LINK.bits, KEY_USR_LINK);
    assert_eq!(USER_SET_ATTRIBUTE.bits, KEY_USR_SETATTR);
    assert_eq!(USER_ALL.bits, KEY_USR_ALL);

    assert_eq!(GROUP_VIEW.bits, KEY_GRP_VIEW);
    assert_eq!(GROUP_READ.bits, KEY_GRP_READ);
    assert_eq!(GROUP_WRITE.bits, KEY_GRP_WRITE);
    assert_eq!(GROUP_SEARCH.bits, KEY_GRP_SEARCH);
    assert_eq!(GROUP_LINK.bits, KEY_GRP_LINK);
    assert_eq!(GROUP_SET_ATTRIBUTE.bits, KEY_GRP_SETATTR);
    assert_eq!(GROUP_ALL.bits, KEY_GRP_ALL);

    assert_eq!(OTHER_VIEW.bits, KEY_OTH_VIEW);
    assert_eq!(OTHER_READ.bits, KEY_OTH_READ);
    assert_eq!(OTHER_WRITE.bits, KEY_OTH_WRITE);
    assert_eq!(OTHER_SEARCH.bits, KEY_OTH_SEARCH);
    assert_eq!(OTHER_LINK.bits, KEY_OTH_LINK);
    assert_eq!(OTHER_SET_ATTRIBUTE.bits, KEY_OTH_SETATTR);
    assert_eq!(OTHER_ALL.bits, KEY_OTH_ALL);
}
<|endoftext|>


<commit_before>extern crate fuse;
extern crate time;
extern crate libc;
extern crate serialize;

use std::io::{FileType, USER_FILE, USER_DIR};
use std::mem;
use std::os;
use libc::{ENOENT, ENOSYS};
use time::Timespec;
use fuse::{FileAttr, Filesystem, Request, ReplyAttr, ReplyEntry, ReplyDirectory};
use serialize::json;

struct JsonFilesystem {
    tree: json::Object,
}

impl Filesystem for JsonFilesystem {
    fn getattr(&mut self, _req: &Request, ino: u64, reply: ReplyAttr) {
        println!("getattr(ino={})", ino);
        let mut attr: FileAttr = unsafe { mem::zeroed() };
        let ttl = Timespec::new(1, 0);
        if ino == 1 {
            attr.ino = 1;
            attr.kind = FileType::Directory;
            attr.perm = USER_DIR;
            reply.attr(&ttl, &attr);
        } else {
            let tree_index = (ino - 2) as uint;
            println!("\ttree_index={}", tree_index);
            if tree_index < self.tree.len() {
                attr.ino = ino;
                attr.kind = FileType::RegularFile;
                attr.perm = USER_FILE;
                reply.attr(&ttl, &attr);
            } else {
                reply.error(ENOSYS);
            }
        }
    }

    fn lookup(&mut self, _req: &Request, parent: u64, name: &PosixPath, reply: ReplyEntry) {
        println!("lookup(parent={}, name={})", parent, name.display());
        let mut attr: FileAttr = unsafe { mem::zeroed() };
        let ttl = Timespec::new(1, 0);
        let name = name.as_str().unwrap();
        if name == "/" {
            attr.ino = 1;
            attr.kind = FileType::Directory;
            attr.perm = USER_DIR;
            reply.entry(&ttl, &attr, 0);
        } else {
            match self.tree.get(name) {
                Some(value) => {
                    println!("\t name={}, value={}", name, value);
                    attr.kind = FileType::RegularFile;
                    attr.perm = USER_FILE;
                    // TODO: correct attributes!
                    attr.ino = 2;
                    reply.entry(&ttl, &attr, 0);
                },
                None => reply.error(ENOENT)
            };
        }
    }

    fn readdir(&mut self, _req: &Request, ino: u64, fh: u64, offset: u64, mut reply: ReplyDirectory) {
        println!("readdir(ino={}, fh={}, offset={})", ino, fh, offset);
        if ino == 1 {
            if offset == 0 {
                reply.add(1, 0, FileType::Directory, &PosixPath::new("."));
                reply.add(1, 1, FileType::Directory, &PosixPath::new(".."));
                for (i, key) in self.tree.keys().enumerate() {
                    let inode: u64 = 2 + i as u64;
                    let offset: u64 = 2 + i as u64;
                    println!("\tkey={}, inode={}, offset={}", key, inode, offset);
                    reply.add(inode, offset, FileType::RegularFile, &PosixPath::new(key));
                }
            }
            reply.ok();
        } else {
            reply.error(ENOENT);
        }
    }
}

fn main() {
    println!("24 days of Rust - fuse (day N)");
    let data = json::from_str("{\"foo\": \"bar\", \"answer\": 42}").unwrap();
    let tree = data.as_object().unwrap();
    let fs = JsonFilesystem { tree: tree.clone() };
    let mountpoint = Path::new(os::args()[1].as_slice());
    fuse::mount(fs, &mountpoint, &[]);
}
<commit_msg>Use json_macros for readability.<commit_after>#![feature(phase)]

extern crate fuse;
#[phase(plugin)]
extern crate json_macros;
extern crate time;
extern crate libc;
extern crate serialize;

use std::io::{FileType, USER_FILE, USER_DIR};
use std::mem;
use std::os;
use libc::{ENOENT, ENOSYS};
use time::Timespec;
use fuse::{FileAttr, Filesystem, Request, ReplyAttr, ReplyEntry, ReplyDirectory};
use serialize::json;

struct JsonFilesystem {
    tree: json::Object,
}

impl Filesystem for JsonFilesystem {
    fn getattr(&mut self, _req: &Request, ino: u64, reply: ReplyAttr) {
        println!("getattr(ino={})", ino);
        let mut attr: FileAttr = unsafe { mem::zeroed() };
        let ttl = Timespec::new(1, 0);
        if ino == 1 {
            attr.ino = 1;
            attr.kind = FileType::Directory;
            attr.perm = USER_DIR;
            reply.attr(&ttl, &attr);
        } else {
            let tree_index = (ino - 2) as uint;
            println!("\ttree_index={}", tree_index);
            if tree_index < self.tree.len() {
                attr.ino = ino;
                attr.kind = FileType::RegularFile;
                attr.perm = USER_FILE;
                reply.attr(&ttl, &attr);
            } else {
                reply.error(ENOSYS);
            }
        }
    }

    fn lookup(&mut self, _req: &Request, parent: u64, name: &PosixPath, reply: ReplyEntry) {
        println!("lookup(parent={}, name={})", parent, name.display());
        let mut attr: FileAttr = unsafe { mem::zeroed() };
        let ttl = Timespec::new(1, 0);
        let name = name.as_str().unwrap();
        if name == "/" {
            attr.ino = 1;
            attr.kind = FileType::Directory;
            attr.perm = USER_DIR;
            reply.entry(&ttl, &attr, 0);
        } else {
            match self.tree.get(name) {
                Some(value) => {
                    println!("\t name={}, value={}", name, value);
                    attr.kind = FileType::RegularFile;
                    attr.perm = USER_FILE;
                    // TODO: correct attributes!
                    attr.ino = 2;
                    reply.entry(&ttl, &attr, 0);
                },
                None => reply.error(ENOENT)
            };
        }
    }

    fn readdir(&mut self, _req: &Request, ino: u64, fh: u64, offset: u64, mut reply: ReplyDirectory) {
        println!("readdir(ino={}, fh={}, offset={})", ino, fh, offset);
        if ino == 1 {
            if offset == 0 {
                reply.add(1, 0, FileType::Directory, &PosixPath::new("."));
                reply.add(1, 1, FileType::Directory, &PosixPath::new(".."));
                for (i, key) in self.tree.keys().enumerate() {
                    let inode: u64 = 2 + i as u64;
                    let offset: u64 = 2 + i as u64;
                    println!("\tkey={}, inode={}, offset={}", key, inode, offset);
                    reply.add(inode, offset, FileType::RegularFile, &PosixPath::new(key));
                }
            }
            reply.ok();
        } else {
            reply.error(ENOENT);
        }
    }
}

fn main() {
    println!("24 days of Rust - fuse (day N)");
    let data = json!({
        "foo": "bar",
        "answer": 42,
    });
    let tree = data.as_object().unwrap();
    let fs = JsonFilesystem { tree: tree.clone() };
    let mountpoint = Path::new(os::args()[1].as_slice());
    fuse::mount(fs, &mountpoint, &[]);
}
<|endoftext|>


<commit_before>extern crate sdl2;
extern crate sdl2_ttf;
use sdl2::render::{Renderer, TextureQuery, BlendMode};
use sdl2::rect::Rect;
use sdl2::pixels::Color;
use std::env;
use std::vec::Vec;
use std::path::Path;
use font::*;
use std::ops::DerefMut;
use display::{self,Display};
use subtitles::{self,Subtitles};
use utils::*;
use std::f32::consts;

pub struct Displayer<'a> {
    fonts: FontList,
    renderer: Renderer<'a>,
    #[allow(dead_code)]
    ttf_context: sdl2_ttf::Sdl2TtfContext,
    subtitles:Option<Subtitles>
}

impl<'a> Displayer<'a> {
    pub fn new(mut renderer: Renderer<'a>,subtitles:Option<Subtitles>) -> Result<Displayer<'a>, ()> {
        renderer.set_blend_mode(BlendMode::Blend);
        let ttf_context = sdl2_ttf::init().unwrap();
        let mut font_path = env::current_exe().unwrap().parent().unwrap().to_path_buf();
        font_path.push("res/DejaVuSansMono-Bold.ttf");
        let font_path = font_path.as_path();
        let font_list = FontList::new(font_path,
                                      &ttf_context)
                            .unwrap();
        let displayer = Displayer {
            fonts: font_list,
            ttf_context: ttf_context,
            renderer: renderer,
            subtitles: subtitles
        };
        Ok(displayer)
    }

    pub fn render_subtitles(&mut self,frame_number:u32){
        const FADE_INTERVAL : u32 = 10;
        const TRANSITION_INTERVAL : u32 = FADE_INTERVAL + 5 ;
        let mut sub_colors = (Color::RGB(0,0xFF,0xFF),Color::RGB(0xFF,0,0),Color::RGB(0xFF,0xFF,0));
        let mut text2d_vec = vec![];
        match self.subtitles {
            Some(ref subtitles) => {
                let subtitles = subtitles.get_subtitles();
                let iter = subtitles.iter().enumerate().filter(|&(i,line)|{
                    match (line.first(),line.last())  {
                        (Some(&(_,(frame_begin,_))),
                         Some(&(_,(_,frame_end)))) => {
                            frame_begin.saturating_sub(TRANSITION_INTERVAL) < frame_number &&
                            frame_end.saturating_add(TRANSITION_INTERVAL) > frame_number
                         },
                         _ => {false}
                    }
                });
                for (i,line) in iter {
                    let text_pos_y = match i%2 {
                        0 => {display::PosY::FromTopPercent(0.02)},
                        1 => {display::PosY::FromTopPercent(0.12)},
                        _ => unreachable!()
                    };
                    let text_elts = line.iter().fold(vec![],|mut accu_vec,&(ref syllabus,(frame_begin,frame_end))|{
                        if (frame_number < frame_begin){ // not yet

                                let text_2d = display::TextElement {
                                    text:syllabus.clone(),
                                    color:sub_colors.2,
                                    outline:Some(Color::RGB(0,0,0)),
                                    shadow:None
                                };
                                accu_vec.push(text_2d);

                        } else if (frame_begin <= frame_number) && (frame_number <= frame_end) {
                            let percent = (frame_number - frame_begin) as f32 / (frame_end - frame_begin) as f32  ;
                            // lets ease the percent a lil bits
                            let percent = 1.0 - (1.0 - percent).sqrt();
                            //let percent = (percent * consts::PI / 2.0).sin();
                            let transition_color = mix_colors(sub_colors.1, sub_colors.0, percent);
                            let text_2d = display::TextElement {
                                text:syllabus.clone(),
                                color:transition_color,
                                outline:Some(Color::RGB(0,0,0)),
                                shadow:None
                            };
                            accu_vec.push(text_2d);
                        } else {
                            if (accu_vec.is_empty()){
                                let text_2d = display::TextElement {
                                    text:syllabus.clone(),
                                    color:sub_colors.0,
                                    outline:Some(Color::RGB(0,0,0)),
                                    shadow:None
                                };
                                accu_vec.push(text_2d);
                            } else {
                                let mut text_2d = accu_vec.last_mut().unwrap();
                                text_2d.text.push_str(&syllabus) ;
                            }
                        }
                        accu_vec
                    });
                    let text_2d = display::Text2D {
                        text:text_elts,
                        size:display::Size::FitPercent(Some(0.95),Some(0.1)),
                        pos:(display::PosX::Centered,text_pos_y),
                        anchor:(0.5,0.0)
                    };
                    text2d_vec.push(text_2d);
                }
            },
            None => {}
        };
        for text2D in text2d_vec.into_iter() {
            text2D.draw(self);
        }
    }

    pub fn display(&mut self, text: &str) {
        let size: f32 = 0.039;
        let window_width = self.renderer.window().unwrap().size().0 ;
        let font_set = self.fonts.get_fittest_font_set(text, (Some(window_width),None),true).unwrap();
        let font = font_set.get_regular_font();
        let font_outline = font_set.get_outline_font();
        let surface = font.render(text)
                          .blended(Color::RGB(180, 180, 180))
                          .unwrap();
        let mut surface_outline = font_outline.render(text)
                                              .blended(Color::RGB(0, 0, 0))
                                              .unwrap();
        let outline_width: u32 = 2;
        let (width, height) = surface_outline.size();
        surface.blit(None,
                     surface_outline.deref_mut(),
                     Some(Rect::new(outline_width as i32,
                                    outline_width as i32,
                                    (width - outline_width),
                                    (height - outline_width)))).expect("Failed to blit texture");
        let mut texture = self.renderer.create_texture_from_surface(&surface_outline).unwrap();
        texture.set_blend_mode(BlendMode::Blend);
        texture.set_alpha_mod(128);
        let TextureQuery { width:texture_width, height:texture_height, format:texture_format,.. } = texture.query();
        self.renderer.copy(&mut texture,
                           None,
                           Some(Rect::new(3, 3, texture_width, texture_height)));
    }

    pub fn example(&mut self) {
        let text_element_1 = display::TextElement {
            text:"S".to_string(),
            color:Color::RGBA(0,0,0,255),
            outline:Some(Color::RGB(255,255,255)),
            shadow:None
        };
        let text_element_2 = display::TextElement {
            text:"L".to_string(),
            color:Color::RGBA(255,255,255,255),
            outline:Some(Color::RGB(0,0,0)),
            shadow:None
        };
        let text_element_3 = display::TextElement {
            text:"T".to_string(),
            color:Color::RGBA(255,0,0,200),
            outline:None,
            shadow:None
        };
        let text_2d : display::Text2D = display::Text2D {
            text:vec![text_element_1,text_element_2,text_element_3],
            size:display::Size::FitPercent(Some(0.9),Some(0.1)),
            pos:(display::PosX::Centered,display::PosY::FromTopPercent(0.50)),
            anchor:(0.5,0.5)
        };
        text_2d.draw(self);
    }

    // width and height must be between 0 and 1
    pub fn sub_screen_dims(&self,width:Option<f32>,height:Option<f32>) -> (Option<u32>,Option<u32>){
        let dims : (u32,u32)= self.renderer.window().unwrap().size();
        (
            width.and_then(|width|{
                Some((width *  (dims.0 as f32)) as u32)
            }) ,
            height.and_then(|height|{
                Some((height *  (dims.1 as f32)) as u32)
            })
        )
    }

    pub fn render(&mut self) {
        self.sdl_renderer_mut().window().unwrap().gl_swap_window();
    }

    pub fn sdl_renderer_mut(&mut self) -> &mut Renderer<'a> {
        &mut self.renderer
    }

    pub fn sdl_renderer(&self) -> &Renderer<'a> {
        &self.renderer
    }

    pub fn fonts(&self) -> &FontList {
        &self.fonts
    }
}
<commit_msg>transition before / after text appearance<commit_after>extern crate sdl2;
extern crate sdl2_ttf;
use sdl2::render::{Renderer, TextureQuery, BlendMode};
use sdl2::rect::Rect;
use sdl2::pixels::Color;
use std::env;
use std::vec::Vec;
use std::path::Path;
use font::*;
use std::ops::DerefMut;
use display::{self,Display};
use subtitles::{self,Subtitles};
use utils::*;
use std::f32::consts;

pub struct Displayer<'a> {
    fonts: FontList,
    renderer: Renderer<'a>,
    #[allow(dead_code)]
    ttf_context: sdl2_ttf::Sdl2TtfContext,
    subtitles:Option<Subtitles>
}

impl<'a> Displayer<'a> {
    pub fn new(mut renderer: Renderer<'a>,subtitles:Option<Subtitles>) -> Result<Displayer<'a>, ()> {
        renderer.set_blend_mode(BlendMode::Blend);
        let ttf_context = sdl2_ttf::init().unwrap();
        let mut font_path = env::current_exe().unwrap().parent().unwrap().to_path_buf();
        font_path.push("res/DejaVuSansMono-Bold.ttf");
        let font_path = font_path.as_path();
        let font_list = FontList::new(font_path,
                                      &ttf_context)
                            .unwrap();
        let displayer = Displayer {
            fonts: font_list,
            ttf_context: ttf_context,
            renderer: renderer,
            subtitles: subtitles
        };
        Ok(displayer)
    }

    pub fn render_subtitles(&mut self,frame_number:u32){
        const FADE_INTERVAL : u32 = 10;
        const TRANSITION_INTERVAL : u32 = FADE_INTERVAL + 5 ;
        let mut sub_colors = (Color::RGB(0,0xFF,0xFF),Color::RGB(0xFF,0,0),Color::RGB(0xFF,0xFF,0));
        let mut text2d_vec = vec![];
        match self.subtitles {
            Some(ref subtitles) => {
                let subtitles = subtitles.get_subtitles();
                let iter = subtitles.iter().enumerate().filter(|&(i,line)|{
                    match (line.first(),line.last())  {
                        (Some(&(_,(frame_begin,_))),
                         Some(&(_,(_,frame_end)))) => {
                            frame_begin.saturating_sub(TRANSITION_INTERVAL) < frame_number &&
                            frame_end.saturating_add(TRANSITION_INTERVAL) > frame_number
                         },
                         _ => {false}
                    }
                });
                for (i,line) in iter {
                    let text_pos_y = match i%2 {
                        0 => {display::PosY::FromTopPercent(0.02)},
                        1 => {display::PosY::FromTopPercent(0.12)},
                        _ => unreachable!()
                    };
                    let alpha : f32 = match (line.first(),line.last())  {
                        (Some(&(_,(frame_begin,_))),
                         Some(&(_,(_,frame_end)))) => {
                            let FINISHED_FADE_INTERVAL = TRANSITION_INTERVAL - FADE_INTERVAL ;
                            let begin_first_fade_frame = frame_begin.saturating_sub(TRANSITION_INTERVAL);
                            let end_first_fade_frame = frame_begin.saturating_sub(FINISHED_FADE_INTERVAL);
                            let begin_second_fade_frame = frame_end.saturating_add(FINISHED_FADE_INTERVAL);
                            let end_second_fade_frame = frame_end.saturating_add(TRANSITION_INTERVAL);
                            assert!(end_second_fade_frame - begin_second_fade_frame == FADE_INTERVAL);
                            if (end_first_fade_frame < frame_number &&
                                begin_second_fade_frame > frame_number) {
                                1.0
                            } else if begin_first_fade_frame <= frame_number
                                   && end_first_fade_frame >= frame_number
                            {
                                (frame_number - begin_first_fade_frame) as f32 /
                                 (end_first_fade_frame - begin_first_fade_frame) as f32
                            } else if begin_second_fade_frame <= frame_number
                                   && end_second_fade_frame >= frame_number
                            {
                                1.0 -
                                ((frame_number - begin_second_fade_frame) as f32 /
                                (end_second_fade_frame - begin_second_fade_frame) as f32)
                            } else {
                                0.0
                            }
                        },
                        _ => {0.0}
                    };
                    let text_elts = line.iter().fold(vec![],|mut accu_vec,&(ref syllabus,(frame_begin,frame_end))|{
                        if (frame_number < frame_begin){ // not yet

                                let text_2d = display::TextElement {
                                    text:syllabus.clone(),
                                    color:fade_color(sub_colors.2,alpha),
                                    outline:Some(Color::RGB(0,0,0)),
                                    shadow:None
                                };
                                accu_vec.push(text_2d);

                        } else if (frame_begin <= frame_number) && (frame_number <= frame_end) {
                            let percent = (frame_number - frame_begin) as f32 / (frame_end - frame_begin) as f32  ;
                            // lets ease the percent a lil bits
                            let percent = 1.0 - (1.0 - percent).sqrt();
                            //let percent = (percent * consts::PI / 2.0).sin();
                            let transition_color = mix_colors(sub_colors.1, sub_colors.0, percent);
                            let text_2d = display::TextElement {
                                text:syllabus.clone(),
                                color:transition_color,
                                outline:Some(Color::RGB(0,0,0)),
                                shadow:None
                            };
                            accu_vec.push(text_2d);
                        } else {
                            if (accu_vec.is_empty()){
                                let text_2d = display::TextElement {
                                    text:syllabus.clone(),
                                    color:fade_color(sub_colors.0,alpha),
                                    outline:Some(Color::RGB(0,0,0)),
                                    shadow:None
                                };
                                accu_vec.push(text_2d);
                            } else {
                                let mut text_2d = accu_vec.last_mut().unwrap();
                                text_2d.text.push_str(&syllabus) ;
                            }
                        }
                        accu_vec
                    });
                    let text_2d = display::Text2D {
                        text:text_elts,
                        size:display::Size::FitPercent(Some(0.95),Some(0.1)),
                        pos:(display::PosX::Centered,text_pos_y),
                        anchor:(0.5,0.0)
                    };
                    text2d_vec.push(text_2d);
                }
            },
            None => {}
        };
        for text2D in text2d_vec.into_iter() {
            text2D.draw(self);
        }
    }

    pub fn display(&mut self, text: &str) {
        let size: f32 = 0.039;
        let window_width = self.renderer.window().unwrap().size().0 ;
        let font_set = self.fonts.get_fittest_font_set(text, (Some(window_width),None),true).unwrap();
        let font = font_set.get_regular_font();
        let font_outline = font_set.get_outline_font();
        let surface = font.render(text)
                          .blended(Color::RGB(180, 180, 180))
                          .unwrap();
        let mut surface_outline = font_outline.render(text)
                                              .blended(Color::RGB(0, 0, 0))
                                              .unwrap();
        let outline_width: u32 = 2;
        let (width, height) = surface_outline.size();
        surface.blit(None,
                     surface_outline.deref_mut(),
                     Some(Rect::new(outline_width as i32,
                                    outline_width as i32,
                                    (width - outline_width),
                                    (height - outline_width)))).expect("Failed to blit texture");
        let mut texture = self.renderer.create_texture_from_surface(&surface_outline).unwrap();
        texture.set_blend_mode(BlendMode::Blend);
        texture.set_alpha_mod(128);
        let TextureQuery { width:texture_width, height:texture_height, format:texture_format,.. } = texture.query();
        self.renderer.copy(&mut texture,
                           None,
                           Some(Rect::new(3, 3, texture_width, texture_height)));
    }

    pub fn example(&mut self) {
        let text_element_1 = display::TextElement {
            text:"S".to_string(),
            color:Color::RGBA(0,0,0,255),
            outline:Some(Color::RGB(255,255,255)),
            shadow:None
        };
        let text_element_2 = display::TextElement {
            text:"L".to_string(),
            color:Color::RGBA(255,255,255,255),
            outline:Some(Color::RGB(0,0,0)),
            shadow:None
        };
        let text_element_3 = display::TextElement {
            text:"T".to_string(),
            color:Color::RGBA(255,0,0,200),
            outline:None,
            shadow:None
        };
        let text_2d : display::Text2D = display::Text2D {
            text:vec![text_element_1,text_element_2,text_element_3],
            size:display::Size::FitPercent(Some(0.9),Some(0.1)),
            pos:(display::PosX::Centered,display::PosY::FromTopPercent(0.50)),
            anchor:(0.5,0.5)
        };
        text_2d.draw(self);
    }

    // width and height must be between 0 and 1
    pub fn sub_screen_dims(&self,width:Option<f32>,height:Option<f32>) -> (Option<u32>,Option<u32>){
        let dims : (u32,u32)= self.renderer.window().unwrap().size();
        (
            width.and_then(|width|{
                Some((width *  (dims.0 as f32)) as u32)
            }) ,
            height.and_then(|height|{
                Some((height *  (dims.1 as f32)) as u32)
            })
        )
    }

    pub fn render(&mut self) {
        self.sdl_renderer_mut().window().unwrap().gl_swap_window();
    }

    pub fn sdl_renderer_mut(&mut self) -> &mut Renderer<'a> {
        &mut self.renderer
    }

    pub fn sdl_renderer(&self) -> &Renderer<'a> {
        &self.renderer
    }

    pub fn fonts(&self) -> &FontList {
        &self.fonts
    }
}
<|endoftext|>


<commit_before>#![crate_name = "geoip"]
#![crate_type = "rlib"]

#![warn(non_camel_case_types,
        non_upper_case_globals,
        unused_qualifications)]

extern crate libc;
extern crate rustc_serialize;
extern crate geoip_sys;
#[macro_use]
extern crate lazy_static;

use libc::{c_char, c_int, c_ulong};
use std::ffi;
use std::net::{Ipv4Addr, Ipv6Addr};
use std::path::Path;
use std::sync::Mutex;

lazy_static! {
    static ref LOCK: Mutex<()> = Mutex::new(());
}

#[derive(Debug, Clone)]
pub enum IpAddr {
    V4(Ipv4Addr),
    V6(Ipv6Addr),
}

enum Charset {
    // Iso88591 = 0,
    Utf8 = 1,
}

#[derive(Debug, Clone)]
pub enum Options {
    Standard = 0,
    MemoryCache = 1,
    CheckCache = 2,
    IndexCache = 4,
    MmapCache = 8,
}

#[derive(Debug, Clone)]
pub enum DBType {
    CountryEdition = 1,
    RegionEditionRev0 = 7,
    CityEditionRev0 = 6,
    ORGEdition = 5,
    ISPEdition = 4,
    CityEditionRev1 = 2,
    RegionEditionRev1 = 3,
    ProxyEdition = 8,
    ASNUMEdition = 9,
    NetSpeedEdition = 10,
    DomainEdition = 11,
    CountryEditionV6 = 12,
    LocationAEdition = 13,
    AccuracyRadiusEdition = 14,
    LargeCountryEdition = 17,
    LargeCountryEditionV6 = 18,
    ASNumEditionV6 = 21,
    ISPEditionV6 = 22,
    ORGEditionV6 = 23,
    DomainEditionV6 = 24,
    LoctionAEditionV6 = 25,
    RegistrarEdition = 26,
    RegistrarEditionV6 = 27,
    UserTypeEdition = 28,
    UserTypeEditionV6 = 29,
    CityEditionRev1V6 = 30,
    CityEditionRev0V6 = 31,
    NetSpeedEditionRev1 = 32,
    NetSpeedEditionRev1V6 = 33,
    CountryConfEdition = 34,
    CityConfEdition = 35,
    RegionConfEdition = 36,
    PostalConfEdition = 37,
    AccuracyRadiusEditionV6 = 38,
}

pub struct GeoIp {
    db: geoip_sys::RawGeoIp,
}

#[derive(Debug, Clone, RustcDecodable, RustcEncodable)]
pub struct ASInfo {
    pub asn: u32,
    pub name: String,
    pub netmask: u32,
}

#[derive(Debug, Clone, RustcDecodable, RustcEncodable)]
pub struct CityInfo {
    pub country_code: Option<String>,
    pub country_code3: Option<String>,
    pub country_name: Option<String>,
    pub region: Option<String>,
    pub city: Option<String>,
    pub postal_code: Option<String>,
    pub latitude: f32,
    pub longitude: f32,
    pub dma_code: Option<u32>,
    pub area_code: Option<u32>,
    pub continent_code: Option<String>,
    pub netmask: u32,
}

fn maybe_string(c_str: *const c_char) -> Option<String> {
    if c_str.is_null() {
        None
    } else {
        String::from_utf8(unsafe { ffi::CStr::from_ptr(c_str).to_bytes() }.to_vec()).ok()
    }
}

fn maybe_code(code: u32) -> Option<u32> {
    if code == 0 {
        None
    } else {
        Some(code)
    }
}

impl CityInfo {
    unsafe fn from_geoiprecord(res: &geoip_sys::GeoIpRecord) -> CityInfo {
        CityInfo {
            country_code: maybe_string(res.country_code),
            country_code3: maybe_string(res.country_code3),
            country_name: maybe_string(res.country_name),
            region: maybe_string(res.region),
            city: maybe_string(res.city),
            postal_code: maybe_string(res.postal_code),
            latitude: res.latitude,
            longitude: res.longitude,
            dma_code: maybe_code(res.dma_code as u32),
            area_code: maybe_code(res.area_code as u32),
            continent_code: maybe_string(res.continent_code),
            netmask: res.netmask as u32,
        }
    }
}

enum CNetworkIp {
    V4(c_ulong),
    V6(geoip_sys::In6Addr),
}

impl CNetworkIp {
    fn new(ip: IpAddr) -> CNetworkIp {
        match ip {
            IpAddr::V4(addr) => {
                let b = addr.octets();
                CNetworkIp::V4(((b[0] as c_ulong) << 24) | ((b[1] as c_ulong) << 16) |
                               ((b[2] as c_ulong) << 8) |
                               ((b[3] as c_ulong)))
            }
            IpAddr::V6(addr) => {
                let b = addr.segments();
                CNetworkIp::V6([(b[0] >> 8) as u8,
                                b[0] as u8,
                                (b[1] >> 8) as u8,
                                b[1] as u8,
                                (b[2] >> 8) as u8,
                                b[2] as u8,
                                (b[3] >> 8) as u8,
                                b[3] as u8,
                                (b[4] >> 8) as u8,
                                b[4] as u8,
                                (b[5] >> 8) as u8,
                                b[5] as u8,
                                (b[6] >> 8) as u8,
                                b[6] as u8,
                                (b[7] >> 8) as u8,
                                b[7] as u8])
            }
        }
    }
}

impl GeoIp {
    pub fn open(path: &Path, options: Options) -> Result<GeoIp, String> {
        let file = match path.to_str() {
            None => return Err(format!("Invalid path {}", path.display())),
            Some(file) => file,
        };
        let db = unsafe {
            geoip_sys::GeoIP_open(ffi::CString::new(file.as_bytes())
                                      .unwrap()
                                      .as_ptr(),
                                  options as c_int)
        };
        if db.is_null() {
            return Err(format!("Can't open {}", file));
        }
        if unsafe { geoip_sys::GeoIP_set_charset(db, Charset::Utf8 as c_int) } != 0 {
            return Err("Can't set charset to UTF8".to_string());
        }
        Ok(GeoIp { db: db })
    }

    pub fn open_type(db_type: DBType, options: Options) -> Result<GeoIp, String> {
        let db = unsafe {
            // GeoIP_open_type initialises global state causing races
            let _lock = LOCK.lock().unwrap();
            geoip_sys::GeoIP_open_type(db_type.clone() as c_int, options as c_int)
        };
        if db.is_null() {
            return Err(format!("Can't open DB of type {:?}", db_type));
        }
        if unsafe { geoip_sys::GeoIP_set_charset(db, Charset::Utf8 as c_int) } != 0 {
            return Err("Can't set charset to UTF8".to_string());
        }
        Ok(GeoIp { db: db })
    }

    pub fn city_info_by_ip(&self, ip: IpAddr) -> Option<CityInfo> {
        let cres = match CNetworkIp::new(ip) {
            CNetworkIp::V4(ip) => unsafe { geoip_sys::GeoIP_record_by_ipnum(self.db, ip) },
            CNetworkIp::V6(ip) => unsafe { geoip_sys::GeoIP_record_by_ipnum_v6(self.db, ip) },
        };

        if cres.is_null() {
            return None;
        }

        unsafe {
            let city_info = CityInfo::from_geoiprecord(&*cres);
            geoip_sys::GeoIPRecord_delete(cres);
            std::mem::forget(cres);
            Some(city_info)
        }
    }

    pub fn region_name_by_code(country_code: &str, region_code: &str) -> Option<&'static str> {
        unsafe {
            let cstr = geoip_sys::GeoIP_region_name_by_code(ffi::CString::new(country_code)
                                                                .unwrap()
                                                                .as_ptr(),
                                                            ffi::CString::new(region_code)
                                                                .unwrap()
                                                                .as_ptr());

            if cstr.is_null() {
                return None;
            }

            Some(ffi::CStr::from_ptr(cstr).to_str().expect("invalid region name data"))
        }
    }

    pub fn time_zone_by_country_and_region(country_code: &str,
                                           region_code: &str)
                                           -> Option<&'static str> {
        unsafe {
            let cstr =
                geoip_sys::GeoIP_time_zone_by_country_and_region(ffi::CString::new(country_code)
                                                                     .unwrap()
                                                                     .as_ptr(),
                                                                 ffi::CString::new(region_code)
                                                                     .unwrap()
                                                                     .as_ptr());

            if cstr.is_null() {
                return None;
            }

            Some(ffi::CStr::from_ptr(cstr).to_str().expect("invalid time zone data"))
        }
    }

    pub fn as_info_by_ip(&self, ip: IpAddr) -> Option<ASInfo> {
        let mut gl = geoip_sys::GeoIpLookup::new();
        let cres = match CNetworkIp::new(ip) {
            CNetworkIp::V4(ip) => unsafe {
                geoip_sys::GeoIP_name_by_ipnum_gl(self.db, ip, &mut gl)
            },
            CNetworkIp::V6(ip) => unsafe {
                geoip_sys::GeoIP_name_by_ipnum_v6_gl(self.db, ip, &mut gl)
            },
        };

        if cres.is_null() {
            return None;
        }
        let description = match maybe_string(cres) {
            None => return None,
            Some(description) => description,
        };
        let mut di = description.splitn(2, ' ');
        let asn = match di.next() {
            None => return None,
            Some(asn) => {
                if !asn.starts_with("AS") {
                    return None;
                } else {
                    asn[2..].splitn(2, ' ').next().unwrap().parse::<u32>().unwrap()
                }
            }
        };
        let name = di.next().unwrap_or("(none)");
        let as_info = ASInfo {
            asn: asn,
            name: name.to_string(),
            netmask: gl.netmask as u32,
        };
        Some(as_info)
    }
}

impl Drop for GeoIp {
    fn drop(&mut self) {
        unsafe {
            geoip_sys::GeoIP_delete(self.db);
        }
    }
}

#[test]
fn geoip_test_basic() {
    let geoip = GeoIp::open(&Path::new("/opt/geoip/GeoIPASNum.dat"),
                            Options::MemoryCache)
        .unwrap();

    let ip = IpAddr::V4("8.8.8.8".parse().unwrap());
    let res = geoip.as_info_by_ip(ip).unwrap();
    assert_eq!(res.asn, 15169);
    assert_eq!(res.name, "Google Inc.".to_string());
    assert_eq!(res.netmask, 24);
}

#[test]
fn geoip_test_city() {
    let geoip = GeoIp::open(&Path::new("/opt/geoip/GeoLiteCity.dat"),
                            Options::MemoryCache)
        .unwrap();

    let ip = IpAddr::V4("8.8.8.8".parse().unwrap());
    let res = geoip.city_info_by_ip(ip).unwrap();
    assert_eq!(res.city, Some("Mountain View".to_string()));
}

#[test]
fn geoip_test_city_maybe_code() {
    let geoip = GeoIp::open(&Path::new("/opt/geoip/GeoLiteCity.dat"),
                            Options::MemoryCache)
        .unwrap();

    let ip = IpAddr::V4("8.8.8.8".parse().unwrap());
    let res = geoip.city_info_by_ip(ip).unwrap();
    assert!(res.city.is_some());
    assert_eq!(res.dma_code, Some(807));
    assert_eq!(res.area_code, Some(650));

    let ip = IpAddr::V4("95.144.124.132".parse().unwrap());
    let res = geoip.city_info_by_ip(ip).unwrap();
    assert!(res.city.is_some());
    assert!(res.dma_code.is_none());
    assert!(res.area_code.is_none());
}

#[test]
fn geoip_test_city_type() {
    let geoip = GeoIp::open_type(DBType::CityEditionRev1, Options::MemoryCache).unwrap();
    let ip = IpAddr::V4("8.8.8.8".parse().unwrap());
    let res = geoip.city_info_by_ip(ip).unwrap();
    assert!(res.city.unwrap() == "Mountain View");
}

#[test]
fn geoip_region_name_by_code() {
    assert_eq!(GeoIp::region_name_by_code("foo", "bar"), None);
    assert_eq!(GeoIp::region_name_by_code("US", "CA"), Some("California"));
}

#[test]
fn geoip_time_zone_by_country_and_region() {
    assert_eq!(GeoIp::time_zone_by_country_and_region("foo", "bar"), None);
    assert_eq!(GeoIp::time_zone_by_country_and_region("US", "CA"),
               Some("America/Los_Angeles"));
}

#[test]
fn geoip_test_city_type_race() {
    use std::sync::{Arc, Barrier};
    use std::thread;
    const N: usize = 20;

    let barrier = Arc::new(Barrier::new(N));

    (0..N).map(|_| {
        let c = barrier.clone();
        thread::spawn(move|| {
            // hopefully this will exercise a race condition
            c.wait();
            let geoip = GeoIp::open_type(DBType::CityEditionRev1, Options::MemoryCache).unwrap();
            let ip = IpAddr::V4("8.8.8.8".parse().unwrap());
            let res = geoip.city_info_by_ip(ip).unwrap();
            assert_eq!(res.city.as_ref().map(String::as_str), Some("Mountain View"));
        })
    })
    .collect::<Vec<_>>()                // spawn all treads
    .into_iter().map(|t| t.join())      // wait for treads to finish and get their results
    .collect::<Result<Vec<_>, _>>()     // will be Err(Any) if one of the Result was Err
    .map_err(|any| any.downcast_ref::<String>().unwrap().to_owned())
    .expect("one of the threads failed");
}
<commit_msg>Remove commented out code<commit_after>#![crate_name = "geoip"]
#![crate_type = "rlib"]

#![warn(non_camel_case_types,
        non_upper_case_globals,
        unused_qualifications)]

extern crate libc;
extern crate rustc_serialize;
extern crate geoip_sys;
#[macro_use]
extern crate lazy_static;

use libc::{c_char, c_int, c_ulong};
use std::ffi;
use std::net::{Ipv4Addr, Ipv6Addr};
use std::path::Path;
use std::sync::Mutex;

lazy_static! {
    static ref LOCK: Mutex<()> = Mutex::new(());
}

#[derive(Debug, Clone)]
pub enum IpAddr {
    V4(Ipv4Addr),
    V6(Ipv6Addr),
}

enum Charset {
    Utf8 = 1,
}

#[derive(Debug, Clone)]
pub enum Options {
    Standard = 0,
    MemoryCache = 1,
    CheckCache = 2,
    IndexCache = 4,
    MmapCache = 8,
}

#[derive(Debug, Clone)]
pub enum DBType {
    CountryEdition = 1,
    RegionEditionRev0 = 7,
    CityEditionRev0 = 6,
    ORGEdition = 5,
    ISPEdition = 4,
    CityEditionRev1 = 2,
    RegionEditionRev1 = 3,
    ProxyEdition = 8,
    ASNUMEdition = 9,
    NetSpeedEdition = 10,
    DomainEdition = 11,
    CountryEditionV6 = 12,
    LocationAEdition = 13,
    AccuracyRadiusEdition = 14,
    LargeCountryEdition = 17,
    LargeCountryEditionV6 = 18,
    ASNumEditionV6 = 21,
    ISPEditionV6 = 22,
    ORGEditionV6 = 23,
    DomainEditionV6 = 24,
    LoctionAEditionV6 = 25,
    RegistrarEdition = 26,
    RegistrarEditionV6 = 27,
    UserTypeEdition = 28,
    UserTypeEditionV6 = 29,
    CityEditionRev1V6 = 30,
    CityEditionRev0V6 = 31,
    NetSpeedEditionRev1 = 32,
    NetSpeedEditionRev1V6 = 33,
    CountryConfEdition = 34,
    CityConfEdition = 35,
    RegionConfEdition = 36,
    PostalConfEdition = 37,
    AccuracyRadiusEditionV6 = 38,
}

pub struct GeoIp {
    db: geoip_sys::RawGeoIp,
}

#[derive(Debug, Clone, RustcDecodable, RustcEncodable)]
pub struct ASInfo {
    pub asn: u32,
    pub name: String,
    pub netmask: u32,
}

#[derive(Debug, Clone, RustcDecodable, RustcEncodable)]
pub struct CityInfo {
    pub country_code: Option<String>,
    pub country_code3: Option<String>,
    pub country_name: Option<String>,
    pub region: Option<String>,
    pub city: Option<String>,
    pub postal_code: Option<String>,
    pub latitude: f32,
    pub longitude: f32,
    pub dma_code: Option<u32>,
    pub area_code: Option<u32>,
    pub continent_code: Option<String>,
    pub netmask: u32,
}

fn maybe_string(c_str: *const c_char) -> Option<String> {
    if c_str.is_null() {
        None
    } else {
        String::from_utf8(unsafe { ffi::CStr::from_ptr(c_str).to_bytes() }.to_vec()).ok()
    }
}

fn maybe_code(code: u32) -> Option<u32> {
    if code == 0 {
        None
    } else {
        Some(code)
    }
}

impl CityInfo {
    unsafe fn from_geoiprecord(res: &geoip_sys::GeoIpRecord) -> CityInfo {
        CityInfo {
            country_code: maybe_string(res.country_code),
            country_code3: maybe_string(res.country_code3),
            country_name: maybe_string(res.country_name),
            region: maybe_string(res.region),
            city: maybe_string(res.city),
            postal_code: maybe_string(res.postal_code),
            latitude: res.latitude,
            longitude: res.longitude,
            dma_code: maybe_code(res.dma_code as u32),
            area_code: maybe_code(res.area_code as u32),
            continent_code: maybe_string(res.continent_code),
            netmask: res.netmask as u32,
        }
    }
}

enum CNetworkIp {
    V4(c_ulong),
    V6(geoip_sys::In6Addr),
}

impl CNetworkIp {
    fn new(ip: IpAddr) -> CNetworkIp {
        match ip {
            IpAddr::V4(addr) => {
                let b = addr.octets();
                CNetworkIp::V4(((b[0] as c_ulong) << 24) | ((b[1] as c_ulong) << 16) |
                               ((b[2] as c_ulong) << 8) |
                               ((b[3] as c_ulong)))
            }
            IpAddr::V6(addr) => {
                let b = addr.segments();
                CNetworkIp::V6([(b[0] >> 8) as u8,
                                b[0] as u8,
                                (b[1] >> 8) as u8,
                                b[1] as u8,
                                (b[2] >> 8) as u8,
                                b[2] as u8,
                                (b[3] >> 8) as u8,
                                b[3] as u8,
                                (b[4] >> 8) as u8,
                                b[4] as u8,
                                (b[5] >> 8) as u8,
                                b[5] as u8,
                                (b[6] >> 8) as u8,
                                b[6] as u8,
                                (b[7] >> 8) as u8,
                                b[7] as u8])
            }
        }
    }
}

impl GeoIp {
    pub fn open(path: &Path, options: Options) -> Result<GeoIp, String> {
        let file = match path.to_str() {
            None => return Err(format!("Invalid path {}", path.display())),
            Some(file) => file,
        };
        let db = unsafe {
            geoip_sys::GeoIP_open(ffi::CString::new(file.as_bytes())
                                      .unwrap()
                                      .as_ptr(),
                                  options as c_int)
        };
        if db.is_null() {
            return Err(format!("Can't open {}", file));
        }
        if unsafe { geoip_sys::GeoIP_set_charset(db, Charset::Utf8 as c_int) } != 0 {
            return Err("Can't set charset to UTF8".to_string());
        }
        Ok(GeoIp { db: db })
    }

    pub fn open_type(db_type: DBType, options: Options) -> Result<GeoIp, String> {
        let db = unsafe {
            // GeoIP_open_type initialises global state causing races
            let _lock = LOCK.lock().unwrap();
            geoip_sys::GeoIP_open_type(db_type.clone() as c_int, options as c_int)
        };
        if db.is_null() {
            return Err(format!("Can't open DB of type {:?}", db_type));
        }
        if unsafe { geoip_sys::GeoIP_set_charset(db, Charset::Utf8 as c_int) } != 0 {
            return Err("Can't set charset to UTF8".to_string());
        }
        Ok(GeoIp { db: db })
    }

    pub fn city_info_by_ip(&self, ip: IpAddr) -> Option<CityInfo> {
        let cres = match CNetworkIp::new(ip) {
            CNetworkIp::V4(ip) => unsafe { geoip_sys::GeoIP_record_by_ipnum(self.db, ip) },
            CNetworkIp::V6(ip) => unsafe { geoip_sys::GeoIP_record_by_ipnum_v6(self.db, ip) },
        };

        if cres.is_null() {
            return None;
        }

        unsafe {
            let city_info = CityInfo::from_geoiprecord(&*cres);
            geoip_sys::GeoIPRecord_delete(cres);
            std::mem::forget(cres);
            Some(city_info)
        }
    }

    pub fn region_name_by_code(country_code: &str, region_code: &str) -> Option<&'static str> {
        unsafe {
            let cstr = geoip_sys::GeoIP_region_name_by_code(ffi::CString::new(country_code)
                                                                .unwrap()
                                                                .as_ptr(),
                                                            ffi::CString::new(region_code)
                                                                .unwrap()
                                                                .as_ptr());

            if cstr.is_null() {
                return None;
            }

            Some(ffi::CStr::from_ptr(cstr).to_str().expect("invalid region name data"))
        }
    }

    pub fn time_zone_by_country_and_region(country_code: &str,
                                           region_code: &str)
                                           -> Option<&'static str> {
        unsafe {
            let cstr =
                geoip_sys::GeoIP_time_zone_by_country_and_region(ffi::CString::new(country_code)
                                                                     .unwrap()
                                                                     .as_ptr(),
                                                                 ffi::CString::new(region_code)
                                                                     .unwrap()
                                                                     .as_ptr());

            if cstr.is_null() {
                return None;
            }

            Some(ffi::CStr::from_ptr(cstr).to_str().expect("invalid time zone data"))
        }
    }

    pub fn as_info_by_ip(&self, ip: IpAddr) -> Option<ASInfo> {
        let mut gl = geoip_sys::GeoIpLookup::new();
        let cres = match CNetworkIp::new(ip) {
            CNetworkIp::V4(ip) => unsafe {
                geoip_sys::GeoIP_name_by_ipnum_gl(self.db, ip, &mut gl)
            },
            CNetworkIp::V6(ip) => unsafe {
                geoip_sys::GeoIP_name_by_ipnum_v6_gl(self.db, ip, &mut gl)
            },
        };

        if cres.is_null() {
            return None;
        }
        let description = match maybe_string(cres) {
            None => return None,
            Some(description) => description,
        };
        let mut di = description.splitn(2, ' ');
        let asn = match di.next() {
            None => return None,
            Some(asn) => {
                if !asn.starts_with("AS") {
                    return None;
                } else {
                    asn[2..].splitn(2, ' ').next().unwrap().parse::<u32>().unwrap()
                }
            }
        };
        let name = di.next().unwrap_or("(none)");
        let as_info = ASInfo {
            asn: asn,
            name: name.to_string(),
            netmask: gl.netmask as u32,
        };
        Some(as_info)
    }
}

impl Drop for GeoIp {
    fn drop(&mut self) {
        unsafe {
            geoip_sys::GeoIP_delete(self.db);
        }
    }
}

#[test]
fn geoip_test_basic() {
    let geoip = GeoIp::open(&Path::new("/opt/geoip/GeoIPASNum.dat"),
                            Options::MemoryCache)
        .unwrap();

    let ip = IpAddr::V4("8.8.8.8".parse().unwrap());
    let res = geoip.as_info_by_ip(ip).unwrap();
    assert_eq!(res.asn, 15169);
    assert_eq!(res.name, "Google Inc.".to_string());
    assert_eq!(res.netmask, 24);
}

#[test]
fn geoip_test_city() {
    let geoip = GeoIp::open(&Path::new("/opt/geoip/GeoLiteCity.dat"),
                            Options::MemoryCache)
        .unwrap();

    let ip = IpAddr::V4("8.8.8.8".parse().unwrap());
    let res = geoip.city_info_by_ip(ip).unwrap();
    assert_eq!(res.city, Some("Mountain View".to_string()));
}

#[test]
fn geoip_test_city_maybe_code() {
    let geoip = GeoIp::open(&Path::new("/opt/geoip/GeoLiteCity.dat"),
                            Options::MemoryCache)
        .unwrap();

    let ip = IpAddr::V4("8.8.8.8".parse().unwrap());
    let res = geoip.city_info_by_ip(ip).unwrap();
    assert!(res.city.is_some());
    assert_eq!(res.dma_code, Some(807));
    assert_eq!(res.area_code, Some(650));

    let ip = IpAddr::V4("95.144.124.132".parse().unwrap());
    let res = geoip.city_info_by_ip(ip).unwrap();
    assert!(res.city.is_some());
    assert!(res.dma_code.is_none());
    assert!(res.area_code.is_none());
}

#[test]
fn geoip_test_city_type() {
    let geoip = GeoIp::open_type(DBType::CityEditionRev1, Options::MemoryCache).unwrap();
    let ip = IpAddr::V4("8.8.8.8".parse().unwrap());
    let res = geoip.city_info_by_ip(ip).unwrap();
    assert!(res.city.unwrap() == "Mountain View");
}

#[test]
fn geoip_region_name_by_code() {
    assert_eq!(GeoIp::region_name_by_code("foo", "bar"), None);
    assert_eq!(GeoIp::region_name_by_code("US", "CA"), Some("California"));
}

#[test]
fn geoip_time_zone_by_country_and_region() {
    assert_eq!(GeoIp::time_zone_by_country_and_region("foo", "bar"), None);
    assert_eq!(GeoIp::time_zone_by_country_and_region("US", "CA"),
               Some("America/Los_Angeles"));
}

#[test]
fn geoip_test_city_type_race() {
    use std::sync::{Arc, Barrier};
    use std::thread;
    const N: usize = 20;

    let barrier = Arc::new(Barrier::new(N));

    (0..N).map(|_| {
        let c = barrier.clone();
        thread::spawn(move|| {
            // hopefully this will exercise a race condition
            c.wait();
            let geoip = GeoIp::open_type(DBType::CityEditionRev1, Options::MemoryCache).unwrap();
            let ip = IpAddr::V4("8.8.8.8".parse().unwrap());
            let res = geoip.city_info_by_ip(ip).unwrap();
            assert_eq!(res.city.as_ref().map(String::as_str), Some("Mountain View"));
        })
    })
    .collect::<Vec<_>>()                // spawn all treads
    .into_iter().map(|t| t.join())      // wait for treads to finish and get their results
    .collect::<Result<Vec<_>, _>>()     // will be Err(Any) if one of the Result was Err
    .map_err(|any| any.downcast_ref::<String>().unwrap().to_owned())
    .expect("one of the threads failed");
}
<|endoftext|>