<commit_before>/* diosix hypervisor manifest file-system management
 *
 * (c) Chris Williams, 2020-2021.
 *
 * See LICENSE for usage and copying.
 */

use super::physmem;
use super::error::Cause;
use super::capsule;
use super::hardware;
use super::loader;
use super::virtmem::Mapping;
use super::vcore::Priority;
use dmfs::{ManifestImageIter, ManifestObject, ManifestObjectType, ManifestObjectData};
use alloc::string::String;
use alloc::vec::Vec;

/* bring in the built-in dmfs image */
use core::slice;
use core::intrinsics::transmute;
extern "C"
{
    static _binary_dmfs_img_start: u8;
    static _binary_dmfs_img_size: u8;
}

/* convert the included dmfs image into a byte slice */
macro_rules! get_dmfs_image
{
    () =>
    {
        unsafe
        {
            slice::from_raw_parts
            (
                transmute(&_binary_dmfs_img_start),
                transmute(&_binary_dmfs_img_size)
            )
        }
    }
}

/* return a list of a DMFS image's asset names and descriptions
   <= array of (names, descriptions) of image's assets */
pub fn list_assets() ->  Result<Vec<(String, String)>, Cause>
{
    let image = get_dmfs_image!();
    let manifest = match ManifestImageIter::from_slice(image)
    {
        Ok(m) => m,
        Err(_) => return Err(Cause::ManifestBadFS)
    };

    let mut list: Vec<(String, String)> = Vec::new();
    for asset in manifest
    {
        list.push((asset.get_name(), asset.get_description()));
    }

    Ok(list)
}

/* look up an asset from the given DMFS image by its name */
pub fn get_named_asset(name: &str) -> Result<ManifestObject, Cause>
{
    let image = get_dmfs_image!();
    let manifest = match ManifestImageIter::from_slice(image)
    {
        Ok(m) => m,
        Err(_) => return Err(Cause::ManifestBadFS)
    };

    for asset in manifest
    {
        /* sadly no simple strcmp() in rust? */
        if asset.get_name().as_str().starts_with(name) == true && asset.get_name().len() == name.len()
        {
            return Ok(asset);
        }
    }

    Err(Cause::ManifestNoSuchAsset)
}

/* parse the hypervisor's bundled manifest, creating services and capsules as required,
   and output any included boot banner messages, during system start up */
pub fn unpack_at_boot() -> Result<(), Cause>
{
    let image = get_dmfs_image!();
    let manifest = match ManifestImageIter::from_slice(image)
    {
        Ok(m) => m,
        Err(_) => return Err(Cause::ManifestBadFS)
    };

    for asset in manifest
    {
        match asset.get_type()
        {
            /* only unpack and process boot messages and system services at startup */
            ManifestObjectType::BootMsg => load_asset(asset)?,
            ManifestObjectType::SystemService => load_asset(asset)?,
            // ManifestObjectType::GuestOS => load_asset(asset)?,
            _ => ()
        }
    }

    Ok(())
}

/* process the given asset, such as printing it to the debug output stream if it's a boot message
   or parsing it and running it if it's an executable, from the given DMFS image
   => asset = manifest asset to parse and process into memory
*/
pub fn load_asset(asset: ManifestObject) -> Result<(), Cause>
{
    let image = get_dmfs_image!();
    let content = match asset.get_contents()
    {
        ManifestObjectData::Bytes(b) => b.as_slice(),
        ManifestObjectData::Region(r) => &image[r.start..r.end]
    };

    match asset.get_type()
    {
        /* print the included boot message */
        ManifestObjectType::BootMsg =>
        {
            hvdebugraw!("\n{}\n\n", String::from_utf8_lossy(content));
            debughousekeeper!(); /* ensure the message is seen */
        },

        /* run a system service and ensure it auto-restarts if it crashes */
        ManifestObjectType::SystemService => match create_capsule_from_exec(true, content)
        {
            Ok(_) => hvdebug!("Created capsule for {}, {} bytes in manifest",
                        asset.get_description(), asset.get_contents_size()),
            Err(_e) => hvdebug!("Failed to create capsule for system service {}: {:?}", asset.get_name(), _e)
        },

        /* run an included guest OS */
        ManifestObjectType::GuestOS => match create_capsule_from_exec(false, content)
        {
            Ok(_) => hvdebug!("Created guest OS capsule for {}: {}, {} bytes in manifest",
                        asset.get_name(), asset.get_description(), asset.get_contents_size()),
            Err(_e) => hvdebug!("Failed to create capsule for system service {}: {:?}", asset.get_name(), _e)
        },

        t => hvdebug!("Found manifest object type {:?}", t)
    }

    Ok(())
}

/* create a capsule from an executable in a DMFS image
   => auto_crash_restart = true to restart this automatically in case of a crash
      binary = slice containing the executable to parse and load
   <= Ok, or an error code
*/
fn create_capsule_from_exec(auto_crash_restart: bool, binary: &[u8]) -> Result<(), Cause>
{
    /* create an auto-restarting capsule */
    let capid = capsule::create(auto_crash_restart)?;

    /* assign one virtual CPU core to the capsule */
    let cpus = 1;

    /* reserve 64MB of physical RAM for the capsule */
    let size = 64 * 1024 * 1024;
    let ram = physmem::alloc_region(size)?;

    /* create device tree blob for the virtual hardware available to the guest
    capsule and copy into the end of the region's physical RAM.
    a zero-length DTB indicates something went wrong */
    let guest_dtb = hardware::clone_dtb_for_capsule(cpus, 0, ram.base(), ram.size())?;
    if guest_dtb.len() == 0
    {
        return Err(Cause::BootDeviceTreeBad);
    }

    let guest_dtb_base = ram.fill_end(guest_dtb)?;

    /* map that physical RAM into the capsule */
    let mut mapping = Mapping::new();
    mapping.set_physical(ram);
    mapping.identity_mapping()?;
    capsule::map_memory(capid, mapping)?;

    /* parse + copy the capsule's binary into its physical RAM */
    let entry = loader::load(ram, binary)?;

    /* create virtual CPU cores for the capsule as required */
    for vcoreid in 0..cpus
    {
        capsule::add_vcore(capid, vcoreid, entry, guest_dtb_base, Priority::High)?;
    }

    Ok(())
}<commit_msg>Enable inclusion of guest OSes TODO: At some point only system services will be able to load and run guest OSes. they can't start automatically unless the user specifically configures diosix to do that<commit_after>/* diosix hypervisor manifest file-system management
 *
 * (c) Chris Williams, 2020-2021.
 *
 * See LICENSE for usage and copying.
 */

use super::physmem;
use super::error::Cause;
use super::capsule;
use super::hardware;
use super::loader;
use super::virtmem::Mapping;
use super::vcore::Priority;
use dmfs::{ManifestImageIter, ManifestObject, ManifestObjectType, ManifestObjectData};
use alloc::string::String;
use alloc::vec::Vec;

/* bring in the built-in dmfs image */
use core::slice;
use core::intrinsics::transmute;
extern "C"
{
    static _binary_dmfs_img_start: u8;
    static _binary_dmfs_img_size: u8;
}

/* convert the included dmfs image into a byte slice */
macro_rules! get_dmfs_image
{
    () =>
    {
        unsafe
        {
            slice::from_raw_parts
            (
                transmute(&_binary_dmfs_img_start),
                transmute(&_binary_dmfs_img_size)
            )
        }
    }
}

/* return a list of a DMFS image's asset names and descriptions
   <= array of (names, descriptions) of image's assets */
pub fn list_assets() ->  Result<Vec<(String, String)>, Cause>
{
    let image = get_dmfs_image!();
    let manifest = match ManifestImageIter::from_slice(image)
    {
        Ok(m) => m,
        Err(_) => return Err(Cause::ManifestBadFS)
    };

    let mut list: Vec<(String, String)> = Vec::new();
    for asset in manifest
    {
        list.push((asset.get_name(), asset.get_description()));
    }

    Ok(list)
}

/* look up an asset from the given DMFS image by its name */
pub fn get_named_asset(name: &str) -> Result<ManifestObject, Cause>
{
    let image = get_dmfs_image!();
    let manifest = match ManifestImageIter::from_slice(image)
    {
        Ok(m) => m,
        Err(_) => return Err(Cause::ManifestBadFS)
    };

    for asset in manifest
    {
        /* sadly no simple strcmp() in rust? */
        if asset.get_name().as_str().starts_with(name) == true && asset.get_name().len() == name.len()
        {
            return Ok(asset);
        }
    }

    Err(Cause::ManifestNoSuchAsset)
}

/* parse the hypervisor's bundled manifest, creating services and capsules as required,
   and output any included boot banner messages, during system start up */
pub fn unpack_at_boot() -> Result<(), Cause>
{
    let image = get_dmfs_image!();
    let manifest = match ManifestImageIter::from_slice(image)
    {
        Ok(m) => m,
        Err(_) => return Err(Cause::ManifestBadFS)
    };

    for asset in manifest
    {
        match asset.get_type()
        {
            /* only unpack and process boot messages and system services at startup */
            ManifestObjectType::BootMsg => load_asset(asset)?,
            ManifestObjectType::SystemService => load_asset(asset)?,
            ManifestObjectType::GuestOS => load_asset(asset)?,
            _ => ()
        }
    }

    Ok(())
}

/* process the given asset, such as printing it to the debug output stream if it's a boot message
   or parsing it and running it if it's an executable, from the given DMFS image
   => asset = manifest asset to parse and process into memory
*/
pub fn load_asset(asset: ManifestObject) -> Result<(), Cause>
{
    let image = get_dmfs_image!();
    let content = match asset.get_contents()
    {
        ManifestObjectData::Bytes(b) => b.as_slice(),
        ManifestObjectData::Region(r) => &image[r.start..r.end]
    };

    match asset.get_type()
    {
        /* print the included boot message */
        ManifestObjectType::BootMsg =>
        {
            hvdebugraw!("\n{}\n\n", String::from_utf8_lossy(content));
            debughousekeeper!(); /* ensure the message is seen */
        },

        /* run a system service and ensure it auto-restarts if it crashes */
        ManifestObjectType::SystemService => match create_capsule_from_exec(true, content)
        {
            Ok(_) => hvdebug!("Created capsule for {}, {} bytes in manifest",
                        asset.get_description(), asset.get_contents_size()),
            Err(_e) => hvdebug!("Failed to create capsule for system service {}: {:?}", asset.get_name(), _e)
        },

        /* run an included guest OS */
        ManifestObjectType::GuestOS => match create_capsule_from_exec(false, content)
        {
            Ok(_) => hvdebug!("Created guest OS capsule for {}: {}, {} bytes in manifest",
                        asset.get_name(), asset.get_description(), asset.get_contents_size()),
            Err(_e) => hvdebug!("Failed to create capsule for system service {}: {:?}", asset.get_name(), _e)
        },

        t => hvdebug!("Found manifest object type {:?}", t)
    }

    Ok(())
}

/* create a capsule from an executable in a DMFS image
   => auto_crash_restart = true to restart this automatically in case of a crash
      binary = slice containing the executable to parse and load
   <= Ok, or an error code
*/
fn create_capsule_from_exec(auto_crash_restart: bool, binary: &[u8]) -> Result<(), Cause>
{
    /* create an auto-restarting capsule */
    let capid = capsule::create(auto_crash_restart)?;

    /* assign one virtual CPU core to the capsule */
    let cpus = 1;

    /* reserve 64MB of physical RAM for the capsule */
    let size = 64 * 1024 * 1024;
    let ram = physmem::alloc_region(size)?;

    /* create device tree blob for the virtual hardware available to the guest
    capsule and copy into the end of the region's physical RAM.
    a zero-length DTB indicates something went wrong */
    let guest_dtb = hardware::clone_dtb_for_capsule(cpus, 0, ram.base(), ram.size())?;
    if guest_dtb.len() == 0
    {
        return Err(Cause::BootDeviceTreeBad);
    }

    let guest_dtb_base = ram.fill_end(guest_dtb)?;

    /* map that physical RAM into the capsule */
    let mut mapping = Mapping::new();
    mapping.set_physical(ram);
    mapping.identity_mapping()?;
    capsule::map_memory(capid, mapping)?;

    /* parse + copy the capsule's binary into its physical RAM */
    let entry = loader::load(ram, binary)?;

    /* create virtual CPU cores for the capsule as required */
    for vcoreid in 0..cpus
    {
        capsule::add_vcore(capid, vcoreid, entry, guest_dtb_base, Priority::High)?;
    }

    Ok(())
}<|endoftext|>


<commit_before>use formats::FORMATS;
use once_cell::sync::Lazy;
use parser::{
    eyre::{bail, Result},
    graph_triples::{Pairs, Relation},
    Parser, ParserTrait,
};
use std::sync::Arc;
use std::{collections::BTreeMap, path::Path};

// Re-exports for convenience elsewhere
pub use parser::ParseOptions;

// The following high level functions hide the implementation
// detail of having a static list of parsers. They are intended as the
// only public interface for this crate.

pub fn parse<P: AsRef<Path>>(path: P, code: &str, language: &str) -> Result<Pairs> {
    PARSERS.parse(path, code, language)
}

/// The set of registered parsers in the current process
static PARSERS: Lazy<Arc<Parsers>> = Lazy::new(|| Arc::new(Parsers::new()));

/// A set of registered parsers, either built-in, or provided by plugins
struct Parsers {
    inner: BTreeMap<String, Parser>,
}

/// A macro to dispatch methods to builtin parsers
///
/// This avoids having to do a search over the parsers's specs for matching `languages`.
macro_rules! dispatch_builtins {
    ($var:expr, $method:ident $(,$arg:expr)*) => {
        match $var.as_str() {
            #[cfg(feature = "parser-bash")]
            "bash" => Some(parser_bash::BashParser::$method($($arg),*)),
            #[cfg(feature = "parser-calc")]
            "calc" => Some(parser_calc::CalcParser::$method($($arg),*)),
            #[cfg(feature = "parser-js")]
            "js" => Some(parser_js::JsParser::$method($($arg),*)),
            #[cfg(feature = "parser-py")]
            "py" => Some(parser_py::PyParser::$method($($arg),*)),
            #[cfg(feature = "parser-r")]
            "r" => Some(parser_r::RParser::$method($($arg),*)),
            #[cfg(feature = "parser-rust")]
            "rust" => Some(parser_rust::RustParser::$method($($arg),*)),
            #[cfg(feature = "parser-ts")]
            "ts" => Some(parser_ts::TsParser::$method($($arg),*)),
            _ => None
        }
    };
}

impl Parsers {
    /// Create a set of parsers
    pub fn new() -> Self {
        let inner = vec![
            #[cfg(feature = "parser-bash")]
            ("bash", parser_bash::BashParser::spec()),
            #[cfg(feature = "parser-calc")]
            ("calc", parser_calc::CalcParser::spec()),
            #[cfg(feature = "parser-js")]
            ("js", parser_js::JsParser::spec()),
            #[cfg(feature = "parser-py")]
            ("py", parser_py::PyParser::spec()),
            #[cfg(feature = "parser-r")]
            ("r", parser_r::RParser::spec()),
            #[cfg(feature = "parser-rust")]
            ("rust", parser_rust::RustParser::spec()),
            #[cfg(feature = "parser-ts")]
            ("ts", parser_ts::TsParser::spec()),
        ]
        .into_iter()
        .map(|(label, parser)| (label.to_string(), parser))
        .collect();

        Self { inner }
    }

    /// List the available parsers
    fn list(&self) -> Vec<String> {
        self.inner
            .keys()
            .into_iter()
            .cloned()
            .collect::<Vec<String>>()
    }

    /// Get a parser by label
    fn get(&self, label: &str) -> Result<Parser> {
        match self.inner.get(label) {
            Some(parser) => Ok(parser.clone()),
            None => bail!("No parser with label `{}`", label),
        }
    }

    /// Parse code in a particular language
    fn parse<P: AsRef<Path>>(&self, path: P, code: &str, language: &str) -> Result<Pairs> {
        let path = path.as_ref();
        let format = FORMATS.match_name(language);

        let pairs = if let Some(result) = dispatch_builtins!(format.name, parse, path, code) {
            result?
        } else {
            bail!(
                "Unable to parse code in language `{}`: no matching parser found",
                format.name
            )
        };

        // Normalize pairs by removing any `Uses` of locally assigned variables
        let mut normalized: Pairs = Vec::with_capacity(pairs.len());
        for (relation, object) in pairs {
            let mut include = true;
            if matches!(relation, Relation::Use(..)) {
                for (other_relation, other_object) in &normalized {
                    if matches!(other_relation, Relation::Assign(..)) && *other_object == object {
                        include = false;
                        break;
                    }
                }
            }
            if !include {
                continue;
            }

            normalized.push((relation, object))
        }
        Ok(normalized)
    }
}

impl Default for Parsers {
    fn default() -> Self {
        Self::new()
    }
}

#[cfg(feature = "cli")]
pub mod commands {
    use super::*;
    use cli_utils::{async_trait::async_trait, result, Result, Run};
    use structopt::StructOpt;

    #[derive(Debug, StructOpt)]
    #[structopt(
        about = "Manage parsers",
        setting = structopt::clap::AppSettings::ColoredHelp,
        setting = structopt::clap::AppSettings::VersionlessSubcommands
    )]
    pub struct Command {
        #[structopt(subcommand)]
        pub action: Action,
    }

    #[derive(Debug, StructOpt)]
    #[structopt(
        setting = structopt::clap::AppSettings::DeriveDisplayOrder
    )]
    pub enum Action {
        List(List),
        Show(Show),
    }

    #[async_trait]
    impl Run for Command {
        async fn run(&self) -> Result {
            let Self { action } = self;
            match action {
                Action::List(action) => action.run().await,
                Action::Show(action) => action.run().await,
            }
        }
    }

    /// List the parsers that are available
    ///
    /// The list of available parsers includes those that are built into the Stencila
    /// binary as well as any parsers provided by plugins.
    #[derive(Debug, StructOpt)]
    #[structopt(
        setting = structopt::clap::AppSettings::ColoredHelp
    )]
    pub struct List {}
    #[async_trait]
    impl Run for List {
        async fn run(&self) -> Result {
            let list = PARSERS.list();
            result::value(list)
        }
    }

    /// Show the specifications of a parser
    #[derive(Debug, StructOpt)]
    #[structopt(
        setting = structopt::clap::AppSettings::ColoredHelp
    )]
    pub struct Show {
        /// The label of the parser
        ///
        /// To get the list of parser labels use `stencila parsers list`.
        label: String,
    }
    #[async_trait]
    impl Run for Show {
        async fn run(&self) -> Result {
            let parser = PARSERS.get(&self.label)?;
            result::value(parser)
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use parser::graph_triples::{relations, resources};
    use std::path::PathBuf;
    use test_utils::assert_json_eq;

    #[test]
    fn test_parse() -> Result<()> {
        let path = PathBuf::from("<test>");
        let pairs = parse(&path, "a = 1\nb = a * a", "calc")?;
        assert_json_eq!(
            pairs,
            vec![
                (
                    relations::assigns((0, 0, 0, 1)),
                    resources::symbol(&path, "a", "Number")
                ),
                (
                    relations::assigns((1, 0, 1, 1)),
                    resources::symbol(&path, "b", "Number")
                )
            ]
        );
        Ok(())
    }
}
<commit_msg>chore(Parsers): Add typings for when includes with no parsers enabled<commit_after>use formats::FORMATS;
use once_cell::sync::Lazy;
use parser::{
    eyre::{bail, Result},
    graph_triples::{Pairs, Relation},
    Parser, ParserTrait,
};
use std::sync::Arc;
use std::{collections::BTreeMap, path::Path};

// Re-exports for convenience elsewhere
pub use parser::ParseOptions;

// The following high level functions hide the implementation
// detail of having a static list of parsers. They are intended as the
// only public interface for this crate.

pub fn parse<P: AsRef<Path>>(path: P, code: &str, language: &str) -> Result<Pairs> {
    PARSERS.parse(path, code, language)
}

/// The set of registered parsers in the current process
static PARSERS: Lazy<Arc<Parsers>> = Lazy::new(|| Arc::new(Parsers::new()));

/// A set of registered parsers, either built-in, or provided by plugins
struct Parsers {
    inner: BTreeMap<String, Parser>,
}

/// A macro to dispatch methods to builtin parsers
///
/// This avoids having to do a search over the parsers's specs for matching `languages`.
macro_rules! dispatch_builtins {
    ($var:expr, $method:ident $(,$arg:expr)*) => {
        match $var.as_str() {
            #[cfg(feature = "parser-bash")]
            "bash" => Some(parser_bash::BashParser::$method($($arg),*)),
            #[cfg(feature = "parser-calc")]
            "calc" => Some(parser_calc::CalcParser::$method($($arg),*)),
            #[cfg(feature = "parser-js")]
            "js" => Some(parser_js::JsParser::$method($($arg),*)),
            #[cfg(feature = "parser-py")]
            "py" => Some(parser_py::PyParser::$method($($arg),*)),
            #[cfg(feature = "parser-r")]
            "r" => Some(parser_r::RParser::$method($($arg),*)),
            #[cfg(feature = "parser-rust")]
            "rust" => Some(parser_rust::RustParser::$method($($arg),*)),
            #[cfg(feature = "parser-ts")]
            "ts" => Some(parser_ts::TsParser::$method($($arg),*)),
            _ => Option::<Result<Pairs>>::None
        }
    };
}

impl Parsers {
    /// Create a set of parsers
    pub fn new() -> Self {
        let inner = vec![
            #[cfg(feature = "parser-bash")]
            ("bash", parser_bash::BashParser::spec()),
            #[cfg(feature = "parser-calc")]
            ("calc", parser_calc::CalcParser::spec()),
            #[cfg(feature = "parser-js")]
            ("js", parser_js::JsParser::spec()),
            #[cfg(feature = "parser-py")]
            ("py", parser_py::PyParser::spec()),
            #[cfg(feature = "parser-r")]
            ("r", parser_r::RParser::spec()),
            #[cfg(feature = "parser-rust")]
            ("rust", parser_rust::RustParser::spec()),
            #[cfg(feature = "parser-ts")]
            ("ts", parser_ts::TsParser::spec()),
        ]
        .into_iter()
        .map(|(label, parser): (&str, Parser)| (label.to_string(), parser))
        .collect();

        Self { inner }
    }

    /// List the available parsers
    fn list(&self) -> Vec<String> {
        self.inner
            .keys()
            .into_iter()
            .cloned()
            .collect::<Vec<String>>()
    }

    /// Get a parser by label
    fn get(&self, label: &str) -> Result<Parser> {
        match self.inner.get(label) {
            Some(parser) => Ok(parser.clone()),
            None => bail!("No parser with label `{}`", label),
        }
    }

    /// Parse code in a particular language
    fn parse<P: AsRef<Path>>(&self, path: P, code: &str, language: &str) -> Result<Pairs> {
        let path = path.as_ref();
        let format = FORMATS.match_name(language);

        let pairs = if let Some(result) = dispatch_builtins!(format.name, parse, path, code) {
            result?
        } else {
            bail!(
                "Unable to parse code in language `{}`: no matching parser found",
                format.name
            )
        };

        // Normalize pairs by removing any `Uses` of locally assigned variables
        let mut normalized: Pairs = Vec::with_capacity(pairs.len());
        for (relation, object) in pairs {
            let mut include = true;
            if matches!(relation, Relation::Use(..)) {
                for (other_relation, other_object) in &normalized {
                    if matches!(other_relation, Relation::Assign(..)) && *other_object == object {
                        include = false;
                        break;
                    }
                }
            }
            if !include {
                continue;
            }

            normalized.push((relation, object))
        }
        Ok(normalized)
    }
}

impl Default for Parsers {
    fn default() -> Self {
        Self::new()
    }
}

#[cfg(feature = "cli")]
pub mod commands {
    use super::*;
    use cli_utils::{async_trait::async_trait, result, Result, Run};
    use structopt::StructOpt;

    #[derive(Debug, StructOpt)]
    #[structopt(
        about = "Manage parsers",
        setting = structopt::clap::AppSettings::ColoredHelp,
        setting = structopt::clap::AppSettings::VersionlessSubcommands
    )]
    pub struct Command {
        #[structopt(subcommand)]
        pub action: Action,
    }

    #[derive(Debug, StructOpt)]
    #[structopt(
        setting = structopt::clap::AppSettings::DeriveDisplayOrder
    )]
    pub enum Action {
        List(List),
        Show(Show),
    }

    #[async_trait]
    impl Run for Command {
        async fn run(&self) -> Result {
            let Self { action } = self;
            match action {
                Action::List(action) => action.run().await,
                Action::Show(action) => action.run().await,
            }
        }
    }

    /// List the parsers that are available
    ///
    /// The list of available parsers includes those that are built into the Stencila
    /// binary as well as any parsers provided by plugins.
    #[derive(Debug, StructOpt)]
    #[structopt(
        setting = structopt::clap::AppSettings::ColoredHelp
    )]
    pub struct List {}
    #[async_trait]
    impl Run for List {
        async fn run(&self) -> Result {
            let list = PARSERS.list();
            result::value(list)
        }
    }

    /// Show the specifications of a parser
    #[derive(Debug, StructOpt)]
    #[structopt(
        setting = structopt::clap::AppSettings::ColoredHelp
    )]
    pub struct Show {
        /// The label of the parser
        ///
        /// To get the list of parser labels use `stencila parsers list`.
        label: String,
    }
    #[async_trait]
    impl Run for Show {
        async fn run(&self) -> Result {
            let parser = PARSERS.get(&self.label)?;
            result::value(parser)
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use parser::graph_triples::{relations, resources};
    use std::path::PathBuf;
    use test_utils::assert_json_eq;

    #[test]
    #[cfg(feature = "parser-calc")]
    fn test_parse() -> Result<()> {
        let path = PathBuf::from("<test>");
        let pairs = parse(&path, "a = 1\nb = a * a", "calc")?;
        assert_json_eq!(
            pairs,
            vec![
                (
                    relations::assigns((0, 0, 0, 1)),
                    resources::symbol(&path, "a", "Number")
                ),
                (
                    relations::assigns((1, 0, 1, 1)),
                    resources::symbol(&path, "b", "Number")
                )
            ]
        );
        Ok(())
    }
}
<|endoftext|>


<commit_before>//! Trait for generic events

use std::any::Any;

use {AfterRenderEvent, ButtonEvent, CloseEvent, ControllerAxisEvent, CursorEvent, FocusEvent,
     IdleEvent, MouseCursorEvent, MouseRelativeEvent, MouseScrollEvent, PressEvent, ReleaseEvent,
     RenderEvent, ResizeEvent, TextEvent, TouchEvent, UpdateEvent};
use {Event, EventId, Input, Loop, Motion};
use {AFTER_RENDER, BUTTON, CONTROLLER_AXIS, CURSOR, FOCUS, CLOSE, IDLE, MOUSE_CURSOR, MOUSE_RELATIVE,
     MOUSE_SCROLL, RENDER, RESIZE, TEXT, TOUCH, UPDATE, FILE_DRAG};

/// Implemented by all events
pub trait GenericEvent: Sized +
    AfterRenderEvent + CloseEvent + ControllerAxisEvent + CursorEvent + FocusEvent + IdleEvent +
    MouseCursorEvent + MouseRelativeEvent + MouseScrollEvent + ButtonEvent + PressEvent +
    ReleaseEvent + RenderEvent + ResizeEvent + TextEvent + TouchEvent + UpdateEvent +
    From<Input> + From<Loop> + Into<Option<Input>> + Into<Option<Loop>>
{
/// The id of this event.
    fn event_id(&self) -> EventId;
/// Calls closure with arguments
    fn with_args<'a, F, U>(&'a self, f: F) -> U
        where F: FnMut(&Any) -> U
;
}

impl GenericEvent for Event {
    fn event_id(&self) -> EventId {
        match *self {
            Event::Input(Input::Cursor(_)) => CURSOR,
            Event::Input(Input::Focus(_)) => FOCUS,
            Event::Input(Input::Close(_)) => CLOSE,
            Event::Input(Input::Move(Motion::MouseCursor(_, _))) => MOUSE_CURSOR,
            Event::Input(Input::Move(Motion::MouseRelative(_, _))) => MOUSE_RELATIVE,
            Event::Input(Input::Move(Motion::MouseScroll(_, _))) => MOUSE_SCROLL,
            Event::Input(Input::Move(Motion::ControllerAxis(_))) => CONTROLLER_AXIS,
            Event::Input(Input::Move(Motion::Touch(_))) => TOUCH,
            Event::Input(Input::Button(_)) => BUTTON,
            Event::Input(Input::Resize(_, _)) => RESIZE,
            Event::Input(Input::Text(_)) => TEXT,
            Event::Input(Input::FileDrag(_)) => FILE_DRAG,
            Event::Loop(Loop::Update(_)) => UPDATE,
            Event::Loop(Loop::Render(_)) => RENDER,
            Event::Loop(Loop::AfterRender(_)) => AFTER_RENDER,
            Event::Loop(Loop::Idle(_)) => IDLE,
            Event::Custom(event_id, _) => event_id,
        }
    }

    fn with_args<'a, F, U>(&'a self, mut f: F) -> U
        where F: FnMut(&Any) -> U
    {
        match *self {
            Event::Input(Input::Cursor(cursor)) => f(&cursor as &Any),
            Event::Input(Input::Focus(focused)) => f(&focused as &Any),
            Event::Input(Input::Close(ref args)) => f(args as &Any),
            Event::Input(Input::Move(Motion::ControllerAxis(args))) => f(&args as &Any),
            Event::Input(Input::Move(Motion::MouseCursor(x, y))) => f(&(x, y) as &Any),
            Event::Input(Input::Move(Motion::MouseRelative(x, y))) => f(&(x, y) as &Any),
            Event::Input(Input::Move(Motion::MouseScroll(x, y))) => f(&(x, y) as &Any),
            Event::Input(Input::Move(Motion::Touch(args))) => f(&args as &Any),
            Event::Input(Input::Button(ref args)) => f(args as &Any),
            Event::Input(Input::Resize(w, h)) => f(&(w, h) as &Any),
            Event::Input(Input::Text(ref text)) => f(text as &Any),
            Event::Input(Input::FileDrag(ref file_drag)) => f(file_drag as &Any),
            Event::Loop(Loop::Update(ref args)) => f(args as &Any),
            Event::Loop(Loop::Render(ref args)) => f(args as &Any),
            Event::Loop(Loop::AfterRender(ref args)) => f(args as &Any),
            Event::Loop(Loop::Idle(ref args)) => f(args as &Any),
            Event::Custom(_, ref args) => f(args),
        }
    }
}
<commit_msg>Improved docs of `GenericEvent`<commit_after>//! Trait for generic events

use std::any::Any;

use {AfterRenderEvent, ButtonEvent, CloseEvent, ControllerAxisEvent, CursorEvent, FocusEvent,
     IdleEvent, MouseCursorEvent, MouseRelativeEvent, MouseScrollEvent, PressEvent, ReleaseEvent,
     RenderEvent, ResizeEvent, TextEvent, TouchEvent, UpdateEvent};
use {Event, EventId, Input, Loop, Motion};
use {AFTER_RENDER, BUTTON, CONTROLLER_AXIS, CURSOR, FOCUS, CLOSE, IDLE, MOUSE_CURSOR, MOUSE_RELATIVE,
     MOUSE_SCROLL, RENDER, RESIZE, TEXT, TOUCH, UPDATE, FILE_DRAG};

/// Implemented by all events.
///
/// Use this trait when you need to handle events, e.g. `fn event(&mut self, e: &impl GenericEvent)`.
/// Events are usually handles by controllers (in the Model-View-Controller programming pattern).
/// There is no requirement that you need to implement some trait for controllers,
/// just that the standard convention for handling events is through a `event` method.
/// For more information about Model-View-Controller, see [Wikipedia article](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller).
///
/// This trait makes it possible to auto impl new events for all types that implements `GenericEvent`.
/// This way, you can define your own event types without breaking compatibility with Piston.
pub trait GenericEvent: Sized +
    AfterRenderEvent + CloseEvent + ControllerAxisEvent + CursorEvent + FocusEvent + IdleEvent +
    MouseCursorEvent + MouseRelativeEvent + MouseScrollEvent + ButtonEvent + PressEvent +
    ReleaseEvent + RenderEvent + ResizeEvent + TextEvent + TouchEvent + UpdateEvent +
    From<Input> + From<Loop> + Into<Option<Input>> + Into<Option<Loop>>
{
    /// The id of this event.
    fn event_id(&self) -> EventId;
    /// Calls closure with arguments
    fn with_args<'a, F, U>(&'a self, f: F) -> U
        where F: FnMut(&Any) -> U;
}

impl GenericEvent for Event {
    fn event_id(&self) -> EventId {
        match *self {
            Event::Input(Input::Cursor(_)) => CURSOR,
            Event::Input(Input::Focus(_)) => FOCUS,
            Event::Input(Input::Close(_)) => CLOSE,
            Event::Input(Input::Move(Motion::MouseCursor(_, _))) => MOUSE_CURSOR,
            Event::Input(Input::Move(Motion::MouseRelative(_, _))) => MOUSE_RELATIVE,
            Event::Input(Input::Move(Motion::MouseScroll(_, _))) => MOUSE_SCROLL,
            Event::Input(Input::Move(Motion::ControllerAxis(_))) => CONTROLLER_AXIS,
            Event::Input(Input::Move(Motion::Touch(_))) => TOUCH,
            Event::Input(Input::Button(_)) => BUTTON,
            Event::Input(Input::Resize(_, _)) => RESIZE,
            Event::Input(Input::Text(_)) => TEXT,
            Event::Input(Input::FileDrag(_)) => FILE_DRAG,
            Event::Loop(Loop::Update(_)) => UPDATE,
            Event::Loop(Loop::Render(_)) => RENDER,
            Event::Loop(Loop::AfterRender(_)) => AFTER_RENDER,
            Event::Loop(Loop::Idle(_)) => IDLE,
            Event::Custom(event_id, _) => event_id,
        }
    }

    fn with_args<'a, F, U>(&'a self, mut f: F) -> U
        where F: FnMut(&Any) -> U
    {
        match *self {
            Event::Input(Input::Cursor(cursor)) => f(&cursor as &Any),
            Event::Input(Input::Focus(focused)) => f(&focused as &Any),
            Event::Input(Input::Close(ref args)) => f(args as &Any),
            Event::Input(Input::Move(Motion::ControllerAxis(args))) => f(&args as &Any),
            Event::Input(Input::Move(Motion::MouseCursor(x, y))) => f(&(x, y) as &Any),
            Event::Input(Input::Move(Motion::MouseRelative(x, y))) => f(&(x, y) as &Any),
            Event::Input(Input::Move(Motion::MouseScroll(x, y))) => f(&(x, y) as &Any),
            Event::Input(Input::Move(Motion::Touch(args))) => f(&args as &Any),
            Event::Input(Input::Button(ref args)) => f(args as &Any),
            Event::Input(Input::Resize(w, h)) => f(&(w, h) as &Any),
            Event::Input(Input::Text(ref text)) => f(text as &Any),
            Event::Input(Input::FileDrag(ref file_drag)) => f(file_drag as &Any),
            Event::Loop(Loop::Update(ref args)) => f(args as &Any),
            Event::Loop(Loop::Render(ref args)) => f(args as &Any),
            Event::Loop(Loop::AfterRender(ref args)) => f(args as &Any),
            Event::Loop(Loop::Idle(ref args)) => f(args as &Any),
            Event::Custom(_, ref args) => f(args),
        }
    }
}
<|endoftext|>


<commit_before>extern crate hyper;
extern crate xml;

use std::io::Read;

pub struct Client {
    http_client: hyper::Client,
    server_uri: String,
}

impl Client {
    pub fn new(server_uri: String) -> Client {
        Client {
            http_client: hyper::Client::new(),
            server_uri: server_uri,
        }
    }

    pub fn request(&self, function_name: String, parameters: &Vec<String>) -> Member {
        let mut body = Vec::<u8>::new();
        {
            let mut writer = xml::EventWriter::new(&mut body);
            writer.write(xml::writer::XmlEvent::start_element("methodCall")).unwrap();
            writer.write(xml::writer::XmlEvent::start_element("methodName")).unwrap();
            writer.write(xml::writer::XmlEvent::characters(function_name.as_str())).unwrap();
            writer.write(xml::writer::XmlEvent::end_element()).unwrap();
            writer.write(xml::writer::XmlEvent::start_element("params")).unwrap();
            for param in parameters {
                writer.write(xml::writer::XmlEvent::start_element("value")).unwrap();
                writer.write(xml::writer::XmlEvent::start_element("string")).unwrap();
                writer.write(xml::writer::XmlEvent::characters(param.as_str())).unwrap();
                writer.write(xml::writer::XmlEvent::end_element()).unwrap();
                writer.write(xml::writer::XmlEvent::end_element()).unwrap();
            }
            writer.write(xml::writer::XmlEvent::end_element()).unwrap();
            writer.write(xml::writer::XmlEvent::end_element()).unwrap();
        }

        let res = self.http_client
            .post(self.server_uri.as_str())
            .body(String::from_utf8(body).unwrap().as_str())
            .send()
            .unwrap();

        let xml_tree = read_xml_tree(&mut xml::EventReader::new(res)).unwrap();
        parse_xml_tree(&xml_tree).unwrap()
    }
}

fn read_xml_tree<T: Read>(mut parser: &mut xml::EventReader<T>) -> Option<XmlTreeNode> {
    match parser.next() {
        Ok(xml::reader::XmlEvent::StartElement { name, .. }) => {
            let mut children = Vec::<XmlTreeNode>::new();
            while let Some(tree) = read_xml_tree(&mut parser) {
                children.push(tree);
            }
            Some(XmlTreeNode::Node(name.local_name, children))
        }
        Ok(xml::reader::XmlEvent::Characters(value)) => Some(XmlTreeNode::Leaf(value)),
        Ok(xml::reader::XmlEvent::EndElement { .. }) => None,
        Err(..) => None,
        _ => read_xml_tree(&mut parser),
    }
}

fn parse_xml_tree(tree: &XmlTreeNode) -> Option<Member> {
    if let Some(tree) = unwrap_xml_layer(&tree, "methodResponse".to_string()) {
        if let Some(tree) = unwrap_xml_layer(&tree, "params".to_string()) {
            if let Some(tree) = unwrap_xml_layer(&tree, "param".to_string()) {
                return parse_xml_tree_helper(&tree);
            }
        }
    }
    None
}

fn parse_xml_tree_helper(tree: &XmlTreeNode) -> Option<Member> {
    if let Some(tree) = unwrap_xml_layer(&tree, "value".to_string()) {
        if let XmlTreeNode::Node(ref name, ref children) = *tree {
            if children.len() == 1 {
                let child = &children[0];
                return match name.as_str() {
                    "array" => parse_xml_array(child),
                    "string" => parse_xml_string(child),
                    "int" | "i4" => parse_xml_int(child),
                    _ => None,
                };
            }
        }
    }
    None
}

fn parse_xml_array(tree: &XmlTreeNode) -> Option<Member> {
    if let XmlTreeNode::Node(ref name, ref children) = *tree {
        if name.eq(&"data".to_string()) {
            return Some(Member::Array(children.iter().filter_map(parse_xml_tree_helper).collect()));
        }
    }
    None
}

fn parse_xml_int(tree: &XmlTreeNode) -> Option<Member> {
    if let Some(Member::String(text)) = parse_xml_string(&tree) {
        if let Ok(value) = text.parse::<i32>() {
            return Some(Member::Int(value));
        }
    }
    None
}

fn parse_xml_string(tree: &XmlTreeNode) -> Option<Member> {
    if let XmlTreeNode::Leaf(ref value) = *tree {
        return Some(Member::String(value.clone()));
    }
    None
}

fn unwrap_xml_layer(tree: &XmlTreeNode, node_name: String) -> Option<&XmlTreeNode> {
    if let XmlTreeNode::Node(ref name, ref children) = *tree {
        if name.eq(&node_name) && children.len() == 1 {
            return Some(&children[0]);
        }
    }
    None
}

enum XmlTreeNode {
    Leaf(String),
    Node(String, Vec<XmlTreeNode>),
}

pub enum Member {
    Array(Vec<Member>),
    String(String),
    Int(i32),
}
<commit_msg>Replace several String-s to str in client<commit_after>extern crate hyper;
extern crate xml;

use std::io::Read;

pub struct Client<'t> {
    http_client: hyper::Client,
    server_uri: &'t str,
}

impl<'t> Client<'t> {
    pub fn new(server_uri: &'t str) -> Client<'t> {
        Client {
            http_client: hyper::Client::new(),
            server_uri: server_uri,
        }
    }

    pub fn request(&self, function_name: &str, parameters: &Vec<&str>) -> Member {
        let mut body = Vec::<u8>::new();
        {
            let mut writer = xml::EventWriter::new(&mut body);
            writer.write(xml::writer::XmlEvent::start_element("methodCall")).unwrap();
            writer.write(xml::writer::XmlEvent::start_element("methodName")).unwrap();
            writer.write(xml::writer::XmlEvent::characters(function_name)).unwrap();
            writer.write(xml::writer::XmlEvent::end_element()).unwrap();
            writer.write(xml::writer::XmlEvent::start_element("params")).unwrap();
            for param in parameters {
                writer.write(xml::writer::XmlEvent::start_element("value")).unwrap();
                writer.write(xml::writer::XmlEvent::start_element("string")).unwrap();
                writer.write(xml::writer::XmlEvent::characters(param)).unwrap();
                writer.write(xml::writer::XmlEvent::end_element()).unwrap();
                writer.write(xml::writer::XmlEvent::end_element()).unwrap();
            }
            writer.write(xml::writer::XmlEvent::end_element()).unwrap();
            writer.write(xml::writer::XmlEvent::end_element()).unwrap();
        }

        let res = self.http_client
            .post(self.server_uri)
            .body(String::from_utf8(body).unwrap().as_str())
            .send()
            .unwrap();

        let xml_tree = read_xml_tree(&mut xml::EventReader::new(res)).unwrap();
        parse_xml_tree(&xml_tree).unwrap()
    }
}

fn read_xml_tree<T: Read>(mut parser: &mut xml::EventReader<T>) -> Option<XmlTreeNode> {
    match parser.next() {
        Ok(xml::reader::XmlEvent::StartElement { name, .. }) => {
            let mut children = Vec::<XmlTreeNode>::new();
            while let Some(tree) = read_xml_tree(&mut parser) {
                children.push(tree);
            }
            Some(XmlTreeNode::Node(name.local_name, children))
        }
        Ok(xml::reader::XmlEvent::Characters(value)) => Some(XmlTreeNode::Leaf(value)),
        Ok(xml::reader::XmlEvent::EndElement { .. }) => None,
        Err(..) => None,
        _ => read_xml_tree(&mut parser),
    }
}

fn parse_xml_tree(tree: &XmlTreeNode) -> Option<Member> {
    if let Some(tree) = unwrap_xml_layer(&tree, "methodResponse".to_string()) {
        if let Some(tree) = unwrap_xml_layer(&tree, "params".to_string()) {
            if let Some(tree) = unwrap_xml_layer(&tree, "param".to_string()) {
                return parse_xml_tree_helper(&tree);
            }
        }
    }
    None
}

fn parse_xml_tree_helper(tree: &XmlTreeNode) -> Option<Member> {
    if let Some(tree) = unwrap_xml_layer(&tree, "value".to_string()) {
        if let XmlTreeNode::Node(ref name, ref children) = *tree {
            if children.len() == 1 {
                let child = &children[0];
                return match name.as_str() {
                    "array" => parse_xml_array(child),
                    "string" => parse_xml_string(child),
                    "int" | "i4" => parse_xml_int(child),
                    _ => None,
                };
            }
        }
    }
    None
}

fn parse_xml_array(tree: &XmlTreeNode) -> Option<Member> {
    if let XmlTreeNode::Node(ref name, ref children) = *tree {
        if name.eq(&"data".to_string()) {
            return Some(Member::Array(children.iter().filter_map(parse_xml_tree_helper).collect()));
        }
    }
    None
}

fn parse_xml_int(tree: &XmlTreeNode) -> Option<Member> {
    if let Some(Member::String(text)) = parse_xml_string(&tree) {
        if let Ok(value) = text.parse::<i32>() {
            return Some(Member::Int(value));
        }
    }
    None
}

fn parse_xml_string(tree: &XmlTreeNode) -> Option<Member> {
    if let XmlTreeNode::Leaf(ref value) = *tree {
        return Some(Member::String(value.clone()));
    }
    None
}

fn unwrap_xml_layer(tree: &XmlTreeNode, node_name: String) -> Option<&XmlTreeNode> {
    if let XmlTreeNode::Node(ref name, ref children) = *tree {
        if name.eq(&node_name) && children.len() == 1 {
            return Some(&children[0]);
        }
    }
    None
}

enum XmlTreeNode {
    Leaf(String),
    Node(String, Vec<XmlTreeNode>),
}

pub enum Member {
    Array(Vec<Member>),
    String(String),
    Int(i32),
}
<|endoftext|>


<commit_before>use std::error::Error;
use std::fmt::{Debug, Display, Formatter};
use std::fmt;

use regex::Regex;

pub mod id;
pub mod id_type;
pub mod header;
pub mod hash;

use module::Module;
use storage::file::id::*;
use storage::file::id_type::FileIDType;
use super::parser::{FileHeaderParser, Parser, ParserError};

use self::header::spec::*;
use self::header::data::*;

/*
 * Internal abstract view on a file. Does not exist on the FS and is just kept
 * internally until it is written to disk.
 */
pub struct File<'a> {
    owning_module   : &'a Module<'a>,
    header          : FileHeaderData,
    data            : String,
    id              : FileID,
}

impl<'a> File<'a> {

    pub fn new(module: &'a Module<'a>) -> File<'a> {
        let f = File {
            owning_module: module,
            header: FileHeaderData::Null,
            data: String::from(""),
            id: File::get_new_file_id(),
        };
        debug!("Create new File object: {:?}", f);
        f
    }

    pub fn from_parser_result(module: &'a Module<'a>, id: FileID, header: FileHeaderData, data: String) -> File<'a> {
        let f = File {
            owning_module: module,
            header: header,
            data: data,
            id: id,
        };
        debug!("Create new File object from parser result: {:?}", f);
        f
    }

    pub fn new_with_header(module: &'a Module<'a>, h: FileHeaderData) -> File<'a> {
        let f = File {
            owning_module: module,
            header: h,
            data: String::from(""),
            id: File::get_new_file_id(),
        };
        debug!("Create new File object with header: {:?}", f);
        f
    }

    pub fn new_with_data(module: &'a Module<'a>, d: String) -> File<'a> {
        let f = File {
            owning_module: module,
            header: FileHeaderData::Null,
            data: d,
            id: File::get_new_file_id(),
        };
        debug!("Create new File object with data: {:?}", f);
        f
    }

    pub fn new_with_content(module: &'a Module<'a>, h: FileHeaderData, d: String) -> File<'a> {
        let f = File {
            owning_module: module,
            header: h,
            data: d,
            id: File::get_new_file_id(),
        };
        debug!("Create new File object with content: {:?}", f);
        f
    }

    pub fn header(&self) -> FileHeaderData {
        self.header.clone()
    }

    pub fn data(&self) -> String {
        self.data.clone()
    }

    pub fn contents(&self) -> (FileHeaderData, String) {
        (self.header(), self.data())
    }

    pub fn id(&self) -> FileID {
        self.id.clone()
    }

    pub fn owner(&self) -> &'a Module<'a> {
        self.owning_module
    }

    pub fn matches_with(&self, r: &Regex) -> bool {
        r.is_match(&self.data[..]) || self.header.matches_with(r)
    }

    fn get_new_file_id() -> FileID {
        use uuid::Uuid;
        FileID::new(FileIDType::UUID, Uuid::new_v4().to_hyphenated_string())
    }
}

impl<'a> Display for File<'a> {

    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
        write!(fmt,
"[File] Owner : '{:?}'
        FileID: '{:?}'
        Header: '{:?}'
        Data  : '{:?}'",
               self.owning_module,
               self.header,
               self.data,
               self.id);
        Ok(())
    }

}

impl<'a> Debug for File<'a> {

    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
        write!(fmt,
"[File] Owner : '{:?}'
        FileID: '{:?}'
        Header: '{:?}'
        Data  : '{:?}'",
               self.owning_module,
               self.header,
               self.data,
               self.id);
        Ok(())
    }

}

#[cfg(test)]
mod test {
    // we use the JSON parser here, so we can generate FileHeaderData
    use storage::json::parser::JsonHeaderParser;
    use super::match_header_spec;
    use storage::parser::{FileHeaderParser, ParserError};
    use storage::file::FileHeaderData as FHD;
    use storage::file::FileHeaderSpec as FHS;

    #[test]
    fn test_spec_matching() {
        let text = String::from("{\"a\": 1, \"b\": -2}");
        let spec = FHS::Map {
            keys: vec![
                FHS::Key {
                    name: String::from("a"),
                    value_type: Box::new(FHS::UInteger)
                },
                FHS::Key {
                    name: String::from("b"),
                    value_type: Box::new(FHS::Integer)
                }
            ]
        };

        let parser  = JsonHeaderParser::new(Some(spec.clone()));
        let datares = parser.read(Some(text.clone()));
        assert!(datares.is_ok(), "Text could not be parsed: '{}'", text);
        let data = datares.unwrap();

        let matchres = match_header_spec(&spec, &data);
        assert!(matchres.is_none(), "Matching returns error: {:?}", matchres);
    }
}

<commit_msg>When generating a new FileID, use the FileHash type<commit_after>use std::error::Error;
use std::fmt::{Debug, Display, Formatter};
use std::fmt;

use regex::Regex;

pub mod id;
pub mod id_type;
pub mod header;
pub mod hash;

use module::Module;
use storage::file::id::*;
use storage::file::id_type::FileIDType;
use storage::file::hash::FileHash;
use super::parser::{FileHeaderParser, Parser, ParserError};

use self::header::spec::*;
use self::header::data::*;

/*
 * Internal abstract view on a file. Does not exist on the FS and is just kept
 * internally until it is written to disk.
 */
pub struct File<'a> {
    owning_module   : &'a Module<'a>,
    header          : FileHeaderData,
    data            : String,
    id              : FileID,
}

impl<'a> File<'a> {

    pub fn new(module: &'a Module<'a>) -> File<'a> {
        let f = File {
            owning_module: module,
            header: FileHeaderData::Null,
            data: String::from(""),
            id: File::get_new_file_id(),
        };
        debug!("Create new File object: {:?}", f);
        f
    }

    pub fn from_parser_result(module: &'a Module<'a>, id: FileID, header: FileHeaderData, data: String) -> File<'a> {
        let f = File {
            owning_module: module,
            header: header,
            data: data,
            id: id,
        };
        debug!("Create new File object from parser result: {:?}", f);
        f
    }

    pub fn new_with_header(module: &'a Module<'a>, h: FileHeaderData) -> File<'a> {
        let f = File {
            owning_module: module,
            header: h,
            data: String::from(""),
            id: File::get_new_file_id(),
        };
        debug!("Create new File object with header: {:?}", f);
        f
    }

    pub fn new_with_data(module: &'a Module<'a>, d: String) -> File<'a> {
        let f = File {
            owning_module: module,
            header: FileHeaderData::Null,
            data: d,
            id: File::get_new_file_id(),
        };
        debug!("Create new File object with data: {:?}", f);
        f
    }

    pub fn new_with_content(module: &'a Module<'a>, h: FileHeaderData, d: String) -> File<'a> {
        let f = File {
            owning_module: module,
            header: h,
            data: d,
            id: File::get_new_file_id(),
        };
        debug!("Create new File object with content: {:?}", f);
        f
    }

    pub fn header(&self) -> FileHeaderData {
        self.header.clone()
    }

    pub fn data(&self) -> String {
        self.data.clone()
    }

    pub fn contents(&self) -> (FileHeaderData, String) {
        (self.header(), self.data())
    }

    pub fn id(&self) -> FileID {
        self.id.clone()
    }

    pub fn owner(&self) -> &'a Module<'a> {
        self.owning_module
    }

    pub fn matches_with(&self, r: &Regex) -> bool {
        r.is_match(&self.data[..]) || self.header.matches_with(r)
    }

    fn get_new_file_id() -> FileID {
        use uuid::Uuid;
        let hash = FileHash::from(Uuid::new_v4().to_hyphenated_string());
        FileID::new(FileIDType::UUID, hash);
    }
}

impl<'a> Display for File<'a> {

    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
        write!(fmt,
"[File] Owner : '{:?}'
        FileID: '{:?}'
        Header: '{:?}'
        Data  : '{:?}'",
               self.owning_module,
               self.header,
               self.data,
               self.id);
        Ok(())
    }

}

impl<'a> Debug for File<'a> {

    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
        write!(fmt,
"[File] Owner : '{:?}'
        FileID: '{:?}'
        Header: '{:?}'
        Data  : '{:?}'",
               self.owning_module,
               self.header,
               self.data,
               self.id);
        Ok(())
    }

}

#[cfg(test)]
mod test {
    // we use the JSON parser here, so we can generate FileHeaderData
    use storage::json::parser::JsonHeaderParser;
    use super::match_header_spec;
    use storage::parser::{FileHeaderParser, ParserError};
    use storage::file::FileHeaderData as FHD;
    use storage::file::FileHeaderSpec as FHS;

    #[test]
    fn test_spec_matching() {
        let text = String::from("{\"a\": 1, \"b\": -2}");
        let spec = FHS::Map {
            keys: vec![
                FHS::Key {
                    name: String::from("a"),
                    value_type: Box::new(FHS::UInteger)
                },
                FHS::Key {
                    name: String::from("b"),
                    value_type: Box::new(FHS::Integer)
                }
            ]
        };

        let parser  = JsonHeaderParser::new(Some(spec.clone()));
        let datares = parser.read(Some(text.clone()));
        assert!(datares.is_ok(), "Text could not be parsed: '{}'", text);
        let data = datares.unwrap();

        let matchres = match_header_spec(&spec, &data);
        assert!(matchres.is_none(), "Matching returns error: {:?}", matchres);
    }
}

<|endoftext|>


<commit_before>use serde;
use serde_json;
use serde_yaml;
use uuid::Uuid;

use structure::translation::TranslationDocument;
use dsl::command::{GearsDsl, DslTree, DslToken, DslTokens, command_grammar};

#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
pub struct Document<T> {
    pub id: Uuid,
    pub name: String,
    pub doctype: String,
    pub doctype_version: i64,
    pub version: i64,
    pub doc: T,
}

pub type DocumentList<T> = Vec<Document<T>>;

impl<T> Document<T>
where
    T: serde::Serialize + serde::de::DeserializeOwned + Eq + Default,
{
    pub fn new_from_header(header: &DocumentHeader) -> Self {
        Self {
            id: header.id.clone(),
            name: header.name.clone(),
            doctype: header.doctype.clone(),
            doctype_version: header.doctype_version.clone(),
            version: header.version.clone(),
            doc: <T>::default(),
        }
    }

    pub fn get_header(&self) -> DocumentHeader {
        DocumentHeader {
            id: self.id.clone(),
            name: self.name.clone(),
            doctype: self.doctype.clone(),
            doctype_version: self.doctype_version.clone(),
            version: self.version.clone(),
        }
    }

    pub fn set_header(&mut self, header: &DocumentHeader) -> () {
        self.id = header.id.clone();
        self.name = header.name.clone();
        self.doctype = header.doctype.clone();
        self.doctype_version = header.doctype_version.clone();
        self.version = header.version.clone();
    }

    /// Return a string representation of the Document
    ///
    pub fn to_string(&self) -> String {
        format!("document {}", self.id)
    }

    /// Return a summary of the Document
    ///
    pub fn summary(&self) -> String {
        format!("Doc {:?} - {:?} - {:?}", self.doctype, self.id, self.name)
    }

    /// Return an indented JSON representation of the Document
    ///
    /// partof: SPC-serialization-json
    pub fn to_json(&self) -> String {
        serde_json::to_string_pretty(&self).unwrap()
    }

    /// Return a compact JSON representation of the Document
    ///
    /// partof: SPC-serialization-json
    pub fn to_json_compact(&self) -> String {
        serde_json::to_string(&self).unwrap()
    }

    /// Initialize a Document from a JSON string
    ///
    /// partof: SPC-serialization-json
    pub fn from_json(s: &str) -> Self {
        serde_json::from_str(s).unwrap()
    }

    /// Return a YAML representation of the Document
    ///
    /// partof: #SPC-serialization-yaml
    pub fn to_yaml(&self) -> String {
        serde_yaml::to_string(&self).unwrap()
    }

    /// Initialize a Document from a JSON string
    ///
    /// partof: SPC-serialization-yaml
    pub fn from_yaml(s: &str) -> Self {
        serde_yaml::from_str(s).unwrap()
    }
}

impl<T> Default for Document<T>
where
    T: Default,
{
    fn default() -> Self {
        Self {
            id: Uuid::new_v4(),
            name: "default".to_owned(),
            doctype: "".to_owned(),
            doctype_version: 1,
            version: 1,
            doc: <T>::default(),
        }
    }
}

#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
pub enum ListCommand {
    Add(String),
    Remove(String),
    List,
    Show(String),
}

impl ListCommand {
    fn as_dsl_token(&self) -> DslToken {
        let s = match *self {
            ListCommand::Add(ref e) => format!("add {}", e),
            ListCommand::Remove(ref e) => format!("remove {}", e),
            ListCommand::List => format!("list"),
            ListCommand::Show(ref e) => format!("show {}", e),
        };
        DslToken::Command(s)
    }
}

use std::fmt::Debug;

impl<T> GearsDsl for DocumentList<T>
where
    T: Default + Debug + GearsDsl,
{
    fn generate_dsl(&self) -> DslTokens {
        let mut res = DslTokens::new();

        for doc in self {
            res.push(ListCommand::Add(doc.name.clone()).as_dsl_token());
        }

        res
    }

    fn consume_command(&mut self, s: &str) -> Result<(), String> {
        debug!("consume_command : received command string '{:?}'", s);
        match command_grammar::document_list_command(&s) {
            Ok(cmd) => {
                debug!("consume_command : received parsed command '{:?}'", cmd);
                match cmd {
                    ListCommand::Add(name) => {
                        debug!("ListCommand::Add {:?}", name);
                        let mut doc = Document::<T>::default();
                        doc.name = name.to_string();
                        self.push(doc);
                    }
                    ListCommand::Remove(name) => {
                        self.retain({
                            |e| e.name.ne(&name)
                        });
                    }
                    ListCommand::List => {
                        for doc in self.iter() {
                            println!("{:?}", doc);
                        }
                    }
                    ListCommand::Show(name) => {
                        unimplemented!();
                    }
                }
                Ok(())
            }
            Err(err) => {
                error!("consume_command : {:?}", err);
                return Err(format!("{}", err));
            }
        }
    }

    fn consume_dsl_tree(&mut self, items: &Vec<DslTree>) -> Result<(), String> {
        debug!("consume_dsl_tree : items : '{:?}'", items);
        for item in items {
            match *item {
                DslTree::Scope(ref s, ref tree) => {
                    debug!("consume_dsl_tree : matching scope item '{:?}'", s);
                    match s {
                        _ => {
                            return Err(
                                "No scopes implemented for TranslationsDocumentList yet"
                                    .to_owned(),
                            );
                        }
                    }
                }
                DslTree::Command(ref s) => {
                    debug!("consume_dsl_tree command '{:?}'", s);
                    match self.consume_command(&s) {
                        Err(err) => {
                            return Err(err);
                        }
                        _ => {}
                    }
                }
                DslTree::Comment(ref s) => {
                    debug!("consume_dsl_tree comment '{:?}'", s);
                }
            }
        }
        Ok(())
    }
}

#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
pub struct I18NString {
    pub locale: String,
    pub key: String,
    pub value: String,
}

impl I18NString {
    pub fn new(s: String) -> I18NString {
        I18NString {
            locale: "en_US".to_owned(),
            key: "".to_owned(),
            value: s,
        }
    }
}

impl Default for I18NString {
    fn default() -> I18NString {
        I18NString {
            locale: "en_US".to_owned(),
            key: "-no-key-".to_owned(),
            value: "-no-value-".to_owned(),
        }
    }
}

impl I18NString {
    pub fn translate(&self, t: &TranslationDocument) -> Self {
        I18NString {
            locale: t.doc.locale.clone(),
            key: self.key.clone(),
            value: t.doc
                .items
                .get(&self.key)
                .unwrap_or(&I18NString::new("".to_owned()))
                .value
                .clone(),
        }
    }

    pub fn translate_self(&mut self, t: &TranslationDocument) -> () {
        match t.doc.items.get(&self.key) {
            Some(item) => {
                self.locale = item.locale.clone();
                self.value = item.value.clone();
            }
            None => {
                warn!(
                    "No translation found for '{:?}' in locale '{:?}'",
                    self.key,
                    t.doc.locale
                );
                self.locale = t.doc.locale.clone();
                self.value = "-no-value-".to_owned();
            }
        };

        debug!("Translated value is '{:?}'", self.value);
    }
}

pub trait Translatable {
    fn translate_in_place(&mut self, t: &TranslationDocument) -> ();
    fn translate(&self, t: &TranslationDocument) -> Self;
    fn all_i18n_strings(&self) -> Vec<&I18NString>;
}

//
// This struct only exists to make the top-level Model object serializable into a project's
// model.json
//

#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)]
pub struct DocumentHeader {
    pub id: Uuid,
    pub name: String,
    pub doctype: String,
    pub doctype_version: i64,
    pub version: i64,
}

impl DocumentHeader {
    /// Return a string representation of the DocumentHeader
    ///
    pub fn to_string(&self) -> String {
        format!("document {}", self.id)
    }

    /// Return an indented JSON representation of the DocumentHeader
    ///
    /// partof: SPC-serialization-json
    pub fn to_json(&self) -> String {
        serde_json::to_string_pretty(&self).unwrap()
    }

    /// Return a compact JSON representation of the DocumentHeader
    ///
    /// partof: SPC-serialization-json
    pub fn to_json_compact(&self) -> String {
        serde_json::to_string(&self).unwrap()
    }

    /// Initialize a DocumentHeader from a JSON string
    ///
    /// partof: SPC-serialization-json
    pub fn from_json(s: &str) -> Self {
        serde_json::from_str(s).unwrap()
    }

    /// Return a YAML representation of the DocumentHeader
    ///
    /// partof: #SPC-serialization-yaml
    pub fn to_yaml(&self) -> String {
        serde_yaml::to_string(&self).unwrap()
    }

    /// Initialize a DocumentHeader from a JSON string
    ///
    /// partof: SPC-serialization-yaml
    pub fn from_yaml(s: &str) -> Self {
        serde_yaml::from_str(s).unwrap()
    }
}
<commit_msg>wip: gears-dsl stub for Document<commit_after>use serde;
use serde_json;
use serde_yaml;
use uuid::Uuid;

use structure::translation::TranslationDocument;
use dsl::command::{GearsDsl, DslTree, DslToken, DslTokens, command_grammar};

#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
pub struct Document<T> {
    pub id: Uuid,
    pub name: String,
    pub doctype: String,
    pub doctype_version: i64,
    pub version: i64,
    pub doc: T,
}

pub type DocumentList<T> = Vec<Document<T>>;

impl<T> Document<T>
where
    T: serde::Serialize + serde::de::DeserializeOwned + Eq + Default,
{
    pub fn new_from_header(header: &DocumentHeader) -> Self {
        Self {
            id: header.id.clone(),
            name: header.name.clone(),
            doctype: header.doctype.clone(),
            doctype_version: header.doctype_version.clone(),
            version: header.version.clone(),
            doc: <T>::default(),
        }
    }

    pub fn get_header(&self) -> DocumentHeader {
        DocumentHeader {
            id: self.id.clone(),
            name: self.name.clone(),
            doctype: self.doctype.clone(),
            doctype_version: self.doctype_version.clone(),
            version: self.version.clone(),
        }
    }

    pub fn set_header(&mut self, header: &DocumentHeader) -> () {
        self.id = header.id.clone();
        self.name = header.name.clone();
        self.doctype = header.doctype.clone();
        self.doctype_version = header.doctype_version.clone();
        self.version = header.version.clone();
    }

    /// Return a string representation of the Document
    ///
    pub fn to_string(&self) -> String {
        format!("document {}", self.id)
    }

    /// Return a summary of the Document
    ///
    pub fn summary(&self) -> String {
        format!("Doc {:?} - {:?} - {:?}", self.doctype, self.id, self.name)
    }

    /// Return an indented JSON representation of the Document
    ///
    /// partof: SPC-serialization-json
    pub fn to_json(&self) -> String {
        serde_json::to_string_pretty(&self).unwrap()
    }

    /// Return a compact JSON representation of the Document
    ///
    /// partof: SPC-serialization-json
    pub fn to_json_compact(&self) -> String {
        serde_json::to_string(&self).unwrap()
    }

    /// Initialize a Document from a JSON string
    ///
    /// partof: SPC-serialization-json
    pub fn from_json(s: &str) -> Self {
        serde_json::from_str(s).unwrap()
    }

    /// Return a YAML representation of the Document
    ///
    /// partof: #SPC-serialization-yaml
    pub fn to_yaml(&self) -> String {
        serde_yaml::to_string(&self).unwrap()
    }

    /// Initialize a Document from a JSON string
    ///
    /// partof: SPC-serialization-yaml
    pub fn from_yaml(s: &str) -> Self {
        serde_yaml::from_str(s).unwrap()
    }
}

impl<T> Default for Document<T>
where
    T: Default,
{
    fn default() -> Self {
        Self {
            id: Uuid::new_v4(),
            name: "default".to_owned(),
            doctype: "".to_owned(),
            doctype_version: 1,
            version: 1,
            doc: <T>::default(),
        }
    }
}

#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
pub enum ListCommand {
    Add(String),
    Remove(String),
    List,
    Show(String),
}

impl ListCommand {
    fn as_dsl_token(&self) -> DslToken {
        let s = match *self {
            ListCommand::Add(ref e) => format!("add {}", e),
            ListCommand::Remove(ref e) => format!("remove {}", e),
            ListCommand::List => format!("list"),
            ListCommand::Show(ref e) => format!("show {}", e),
        };
        DslToken::Command(s)
    }
}

use std::fmt::Debug;
impl<T> GearsDsl for Document<T> {
    fn generate_dsl(&self) -> DslTokens {
        let mut res = DslTokens::new();
        unimplemented!();
    }

    fn consume_command(&mut self, s: &str) -> Result<(), String> {
        debug!("consume_command : received command string '{:?}'", s);
        unimplemented!();
    }

    fn consume_dsl_tree(&mut self, items: &Vec<DslTree>) -> Result<(), String> {
        debug!("consume_dsl_tree : items : '{:?}'", items);
        unimplemented!();
        Ok(())
    }
}

impl<T> GearsDsl for DocumentList<T>
where
    T: Default + Debug + GearsDsl,
{
    fn generate_dsl(&self) -> DslTokens {
        let mut res = DslTokens::new();

        for doc in self {
            res.push(ListCommand::Add(doc.name.clone()).as_dsl_token());
        }

        res
    }

    fn consume_command(&mut self, s: &str) -> Result<(), String> {
        debug!("consume_command : received command string '{:?}'", s);
        match command_grammar::document_list_command(&s) {
            Ok(cmd) => {
                debug!("consume_command : received parsed command '{:?}'", cmd);
                match cmd {
                    ListCommand::Add(name) => {
                        debug!("ListCommand::Add {:?}", name);
                        let mut doc = Document::<T>::default();
                        doc.name = name.to_string();
                        self.push(doc);
                    }
                    ListCommand::Remove(name) => {
                        self.retain({
                            |e| e.name.ne(&name)
                        });
                    }
                    ListCommand::List => {
                        for doc in self.iter() {
                            println!("{:?}", doc);
                        }
                    }
                    ListCommand::Show(name) => {
                        unimplemented!();
                    }
                }
                Ok(())
            }
            Err(err) => {
                error!("consume_command : {:?}", err);
                return Err(format!("{}", err));
            }
        }
    }

    fn consume_dsl_tree(&mut self, items: &Vec<DslTree>) -> Result<(), String> {
        debug!("consume_dsl_tree : items : '{:?}'", items);
        for item in items {
            match *item {
                DslTree::Scope(ref s, ref tree) => {
                    debug!("consume_dsl_tree : matching scope item '{:?}'", s);
                    match s {
                        _ => {
                            return Err(
                                "No scopes implemented for TranslationsDocumentList yet"
                                    .to_owned(),
                            );
                        }
                    }
                }
                DslTree::Command(ref s) => {
                    debug!("consume_dsl_tree command '{:?}'", s);
                    match self.consume_command(&s) {
                        Err(err) => {
                            return Err(err);
                        }
                        _ => {}
                    }
                }
                DslTree::Comment(ref s) => {
                    debug!("consume_dsl_tree comment '{:?}'", s);
                }
            }
        }
        Ok(())
    }
}

#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
pub struct I18NString {
    pub locale: String,
    pub key: String,
    pub value: String,
}

impl I18NString {
    pub fn new(s: String) -> I18NString {
        I18NString {
            locale: "en_US".to_owned(),
            key: "".to_owned(),
            value: s,
        }
    }
}

impl Default for I18NString {
    fn default() -> I18NString {
        I18NString {
            locale: "en_US".to_owned(),
            key: "-no-key-".to_owned(),
            value: "-no-value-".to_owned(),
        }
    }
}

impl I18NString {
    pub fn translate(&self, t: &TranslationDocument) -> Self {
        I18NString {
            locale: t.doc.locale.clone(),
            key: self.key.clone(),
            value: t.doc
                .items
                .get(&self.key)
                .unwrap_or(&I18NString::new("".to_owned()))
                .value
                .clone(),
        }
    }

    pub fn translate_self(&mut self, t: &TranslationDocument) -> () {
        match t.doc.items.get(&self.key) {
            Some(item) => {
                self.locale = item.locale.clone();
                self.value = item.value.clone();
            }
            None => {
                warn!(
                    "No translation found for '{:?}' in locale '{:?}'",
                    self.key,
                    t.doc.locale
                );
                self.locale = t.doc.locale.clone();
                self.value = "-no-value-".to_owned();
            }
        };

        debug!("Translated value is '{:?}'", self.value);
    }
}

pub trait Translatable {
    fn translate_in_place(&mut self, t: &TranslationDocument) -> ();
    fn translate(&self, t: &TranslationDocument) -> Self;
    fn all_i18n_strings(&self) -> Vec<&I18NString>;
}

//
// This struct only exists to make the top-level Model object serializable into a project's
// model.json
//

#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)]
pub struct DocumentHeader {
    pub id: Uuid,
    pub name: String,
    pub doctype: String,
    pub doctype_version: i64,
    pub version: i64,
}

impl DocumentHeader {
    /// Return a string representation of the DocumentHeader
    ///
    pub fn to_string(&self) -> String {
        format!("document {}", self.id)
    }

    /// Return an indented JSON representation of the DocumentHeader
    ///
    /// partof: SPC-serialization-json
    pub fn to_json(&self) -> String {
        serde_json::to_string_pretty(&self).unwrap()
    }

    /// Return a compact JSON representation of the DocumentHeader
    ///
    /// partof: SPC-serialization-json
    pub fn to_json_compact(&self) -> String {
        serde_json::to_string(&self).unwrap()
    }

    /// Initialize a DocumentHeader from a JSON string
    ///
    /// partof: SPC-serialization-json
    pub fn from_json(s: &str) -> Self {
        serde_json::from_str(s).unwrap()
    }

    /// Return a YAML representation of the DocumentHeader
    ///
    /// partof: #SPC-serialization-yaml
    pub fn to_yaml(&self) -> String {
        serde_yaml::to_string(&self).unwrap()
    }

    /// Initialize a DocumentHeader from a JSON string
    ///
    /// partof: SPC-serialization-yaml
    pub fn from_yaml(s: &str) -> Self {
        serde_yaml::from_str(s).unwrap()
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2014-2016 Robert Clipsham <robert@octarineparrot.com>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

extern crate libc;

use std::collections::VecDeque;
use std::cmp;
use std::ffi::CString;
use std::io;
use std::iter::repeat;
use std::mem;
use std::sync::Arc;

use bindings::bpf;
use packet::Packet;
use packet::ethernet::{EthernetPacket, MutableEthernetPacket};
use datalink::{EthernetDataLinkChannelIterator, DataLinkChannelType, EthernetDataLinkReceiver,
               EthernetDataLinkSender};
use datalink::DataLinkChannelType::{Layer2, Layer3};
use internal;
use util::NetworkInterface;

// NOTE buffer must be word aligned.
#[inline]
pub fn datalink_channel(network_interface: &NetworkInterface,
                        write_buffer_size: usize,
                        read_buffer_size: usize,
                        channel_type: DataLinkChannelType)
    -> io::Result<(Box<EthernetDataLinkSender>, Box<EthernetDataLinkReceiver>)> {
    #[cfg(target_os = "freebsd")]
    fn get_fd() -> libc::c_int {
        unsafe {
            libc::open(CString::new(&b"/dev/bpf"[..]).unwrap().as_ptr(),
                       libc::O_RDWR,
                       0)
        }
    }

    #[cfg(target_os = "macos")]
    fn get_fd() -> libc::c_int {
        // FIXME This is an arbitrary number of attempts
        for i in 0..1_000isize {
            let fd = unsafe {
                let file_name = format!("/dev/bpf{}", i);
                libc::open(CString::new(file_name.as_bytes()).unwrap().as_ptr(),
                           libc::O_RDWR,
                           0)
            };
            if fd != -1 {
                return fd;
            }
        }

        -1
    }

    #[cfg(target_os = "freebsd")]
    fn set_feedback(fd: libc::c_int) -> io::Result<()> {
        let one: libc::c_uint = 1;
        if unsafe { bpf::ioctl(fd, bpf::BIOCFEEDBACK, &one) } == -1 {
            let err = io::Error::last_os_error();
            unsafe {
                libc::close(fd);
            }
            return Err(err);
        }
        Ok(())
    }

    #[cfg(target_os = "macos")]
    fn set_feedback(_fd: libc::c_int) -> io::Result<()> {
        Ok(())
    }

    match channel_type {
        Layer2 => (),
        Layer3(_) => unimplemented!(),
    }

    let fd = get_fd();
    if fd == -1 {
        return Err(io::Error::last_os_error());
    }
    let mut iface: bpf::ifreq = unsafe { mem::zeroed() };
    for (i, c) in network_interface.name.bytes().enumerate() {
        iface.ifr_name[i] = c as i8;
    }

    let buflen = read_buffer_size as libc::c_uint;
    // NOTE Buffer length must be set before binding to an interface
    //      otherwise this will return Invalid Argument
    if unsafe { bpf::ioctl(fd, bpf::BIOCSBLEN, &buflen) } == -1 {
        let err = io::Error::last_os_error();
        unsafe {
            libc::close(fd);
        }
        return Err(err);
    }

    // Set the interface to use
    if unsafe { bpf::ioctl(fd, bpf::BIOCSETIF, &iface) } == -1 {
        let err = io::Error::last_os_error();
        unsafe {
            libc::close(fd);
        }
        return Err(err);
    }

    // Return from read as soon as packets are available - don't wait to fill the
    // buffer
    let one: libc::c_uint = 1;
    if unsafe { bpf::ioctl(fd, bpf::BIOCIMMEDIATE, &one) } == -1 {
        let err = io::Error::last_os_error();
        unsafe {
            libc::close(fd);
        }
        return Err(err);
    }

    let mut header_size = 0;

    // Get the device type
    let mut dlt: libc::c_uint = 0;
    if unsafe { bpf::ioctl(fd, bpf::BIOCGDLT, &mut dlt) } == -1 {
        let err = io::Error::last_os_error();
        unsafe {
            libc::close(fd);
        }
        return Err(err);
    }

    // The loopback device does weird things
    // FIXME This should really just be another L2 packet header type
    if dlt == bpf::DLT_NULL {
        header_size = 4;

        // Allow packets to be read back after they are written
        if let Err(e) = set_feedback(fd) {
            return Err(e);
        }

    } else {
        // Don't fill in source MAC
        if unsafe { bpf::ioctl(fd, bpf::BIOCSHDRCMPLT, &one) } == -1 {
            let err = io::Error::last_os_error();
            unsafe {
                libc::close(fd);
            }
            return Err(err);
        }
    }

    let fd = Arc::new(internal::FileDesc { fd: fd });
    let sender = Box::new(DataLinkSenderImpl {
        fd: fd.clone(),
        write_buffer: repeat(0u8).take(write_buffer_size).collect(),
        header_size: header_size,
    });
    let receiver = Box::new(DataLinkReceiverImpl {
        fd: fd,
        read_buffer: repeat(0u8).take(read_buffer_size).collect(),
        header_size: header_size,
    });

    Ok((sender, receiver))
}

pub struct DataLinkSenderImpl {
    fd: Arc<internal::FileDesc>,
    write_buffer: Vec<u8>,
    header_size: usize,
}

impl EthernetDataLinkSender for DataLinkSenderImpl {
    #[inline]
    fn build_and_send(&mut self,
                      num_packets: usize,
                      packet_size: usize,
                      func: &mut FnMut(MutableEthernetPacket))
        -> Option<io::Result<()>> {
        let len = num_packets * (packet_size + self.header_size);
        if len >= self.write_buffer.len() {
            None
        } else {
            let min = cmp::min(self.write_buffer.len(), len);
            for chunk in self.write_buffer[..min].chunks_mut(packet_size + self.header_size) {
                // If we're sending on the loopback device, the first 4 bytes must be set to
                // AF_INET
                if self.header_size == 4 {
                    unsafe {
                        *(chunk.as_mut_ptr() as *mut u32) = libc::AF_INET as u32;
                    }
                }
                {
                    let eh = MutableEthernetPacket::new(&mut chunk[self.header_size..]).unwrap();
                    func(eh);
                }
                match unsafe {
                    libc::write(self.fd.fd,
                                chunk.as_ptr() as *const libc::c_void,
                                chunk.len() as libc::size_t)
                } {
                    len if len == -1 => return Some(Err(io::Error::last_os_error())),
                    _ => (),
                }
            }
            Some(Ok(()))
        }
    }

    #[inline]
    fn send_to(&mut self,
               packet: &EthernetPacket,
               _dst: Option<NetworkInterface>)
        -> Option<io::Result<()>> {
        match unsafe {
            libc::write(self.fd.fd,
                        packet.packet().as_ptr() as *const libc::c_void,
                        packet.packet().len() as libc::size_t)
        } {
            len if len == -1 => Some(Err(io::Error::last_os_error())),
            _ => Some(Ok(())),
        }
    }
}

pub struct DataLinkReceiverImpl {
    fd: Arc<internal::FileDesc>,
    read_buffer: Vec<u8>,
    header_size: usize,
}

impl EthernetDataLinkReceiver for DataLinkReceiverImpl {
    fn iter<'a>(&'a mut self) -> Box<EthernetDataLinkChannelIterator + 'a> {
        let buflen = self.read_buffer.len();
        Box::new(DataLinkChannelIteratorImpl {
            pc: self,
            // Enough room for minimally sized packets without reallocating
            packets: VecDeque::with_capacity(buflen / 64),
        })
    }
}

pub struct DataLinkChannelIteratorImpl<'a> {
    pc: &'a mut DataLinkReceiverImpl,
    packets: VecDeque<(usize, usize)>,
}

impl<'a> EthernetDataLinkChannelIterator<'a> for DataLinkChannelIteratorImpl<'a> {
    fn next(&mut self) -> io::Result<EthernetPacket> {
        if self.packets.is_empty() {
            let buflen = match unsafe {
                libc::read(self.pc.fd.fd,
                           self.pc.read_buffer.as_ptr() as *mut libc::c_void,
                           self.pc.read_buffer.len() as libc::size_t)
            } {
                len if len > 0 => len,
                _ => return Err(io::Error::last_os_error()),
            };
            let mut ptr = self.pc.read_buffer.as_mut_ptr();
            let end = unsafe { self.pc.read_buffer.as_ptr().offset(buflen as isize) };
            while (ptr as *const u8) < end {
                unsafe {
                    let packet: *const bpf::bpf_hdr = mem::transmute(ptr);
                    let start = ptr as isize + (*packet).bh_hdrlen as isize -
                                self.pc.read_buffer.as_ptr() as isize;
                    self.packets.push_back((start as usize + self.pc.header_size,
                                            (*packet).bh_caplen as usize - self.pc.header_size));
                    let offset = (*packet).bh_hdrlen as isize + (*packet).bh_caplen as isize;
                    ptr = ptr.offset(bpf::BPF_WORDALIGN(offset));
                }
            }
        }
        let (start, len) = self.packets.pop_front().unwrap();
        Ok(EthernetPacket::new(&self.pc.read_buffer[start..start + len]).unwrap())
    }
}
<commit_msg>Handle headers on loopback devices on bsd/osx<commit_after>// Copyright (c) 2014-2016 Robert Clipsham <robert@octarineparrot.com>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

extern crate libc;

use std::collections::VecDeque;
use std::ffi::CString;
use std::io;
use std::iter::repeat;
use std::mem;
use std::sync::Arc;

use bindings::bpf;
use packet::Packet;
use packet::ethernet::{EthernetPacket, MutableEthernetPacket};
use datalink::{EthernetDataLinkChannelIterator, DataLinkChannelType, EthernetDataLinkReceiver,
               EthernetDataLinkSender};
use datalink::DataLinkChannelType::{Layer2, Layer3};
use internal;
use util::NetworkInterface;

// NOTE buffer must be word aligned.
#[inline]
pub fn datalink_channel(network_interface: &NetworkInterface,
                        write_buffer_size: usize,
                        read_buffer_size: usize,
                        channel_type: DataLinkChannelType)
    -> io::Result<(Box<EthernetDataLinkSender>, Box<EthernetDataLinkReceiver>)> {
    #[cfg(target_os = "freebsd")]
    fn get_fd() -> libc::c_int {
        unsafe {
            libc::open(CString::new(&b"/dev/bpf"[..]).unwrap().as_ptr(),
                       libc::O_RDWR,
                       0)
        }
    }

    #[cfg(target_os = "macos")]
    fn get_fd() -> libc::c_int {
        // FIXME This is an arbitrary number of attempts
        for i in 0..1_000isize {
            let fd = unsafe {
                let file_name = format!("/dev/bpf{}", i);
                libc::open(CString::new(file_name.as_bytes()).unwrap().as_ptr(),
                           libc::O_RDWR,
                           0)
            };
            if fd != -1 {
                return fd;
            }
        }

        -1
    }

    #[cfg(target_os = "freebsd")]
    fn set_feedback(fd: libc::c_int) -> io::Result<()> {
        let one: libc::c_uint = 1;
        if unsafe { bpf::ioctl(fd, bpf::BIOCFEEDBACK, &one) } == -1 {
            let err = io::Error::last_os_error();
            unsafe {
                libc::close(fd);
            }
            return Err(err);
        }
        Ok(())
    }

    #[cfg(target_os = "macos")]
    fn set_feedback(_fd: libc::c_int) -> io::Result<()> {
        Ok(())
    }

    match channel_type {
        Layer2 => (),
        Layer3(_) => unimplemented!(),
    }

    let fd = get_fd();
    if fd == -1 {
        return Err(io::Error::last_os_error());
    }
    let mut iface: bpf::ifreq = unsafe { mem::zeroed() };
    for (i, c) in network_interface.name.bytes().enumerate() {
        iface.ifr_name[i] = c as i8;
    }

    let buflen = read_buffer_size as libc::c_uint;
    // NOTE Buffer length must be set before binding to an interface
    //      otherwise this will return Invalid Argument
    if unsafe { bpf::ioctl(fd, bpf::BIOCSBLEN, &buflen) } == -1 {
        let err = io::Error::last_os_error();
        unsafe {
            libc::close(fd);
        }
        return Err(err);
    }

    // Set the interface to use
    if unsafe { bpf::ioctl(fd, bpf::BIOCSETIF, &iface) } == -1 {
        let err = io::Error::last_os_error();
        unsafe {
            libc::close(fd);
        }
        return Err(err);
    }

    // Return from read as soon as packets are available - don't wait to fill the
    // buffer
    let one: libc::c_uint = 1;
    if unsafe { bpf::ioctl(fd, bpf::BIOCIMMEDIATE, &one) } == -1 {
        let err = io::Error::last_os_error();
        unsafe {
            libc::close(fd);
        }
        return Err(err);
    }

    // Get the device type
    let mut dlt: libc::c_uint = 0;
    if unsafe { bpf::ioctl(fd, bpf::BIOCGDLT, &mut dlt) } == -1 {
        let err = io::Error::last_os_error();
        unsafe {
            libc::close(fd);
        }
        return Err(err);
    }

    let mut loopback = false;
    let mut allocated_read_buffer_size = read_buffer_size;
    // The loopback device does weird things
    // FIXME This should really just be another L2 packet header type
    if dlt == bpf::DLT_NULL {
        loopback = true;
        // So we can guaranatee that we can have a header before the packet.
        // Loopback packets arrive without the header.
        allocated_read_buffer_size += EthernetPacket::minimum_packet_size();

        // Allow packets to be read back after they are written
        if let Err(e) = set_feedback(fd) {
            return Err(e);
        }
    } else {
        // Don't fill in source MAC
        if unsafe { bpf::ioctl(fd, bpf::BIOCSHDRCMPLT, &one) } == -1 {
            let err = io::Error::last_os_error();
            unsafe {
                libc::close(fd);
            }
            return Err(err);
        }
    }

    let fd = Arc::new(internal::FileDesc { fd: fd });
    let sender = Box::new(DataLinkSenderImpl {
        fd: fd.clone(),
        write_buffer: repeat(0u8).take(write_buffer_size).collect(),
        loopback: loopback,
    });
    let receiver = Box::new(DataLinkReceiverImpl {
        fd: fd,
        read_buffer: repeat(0u8).take(allocated_read_buffer_size).collect(),
        loopback: loopback,
    });

    Ok((sender, receiver))
}

pub struct DataLinkSenderImpl {
    fd: Arc<internal::FileDesc>,
    write_buffer: Vec<u8>,
    loopback: bool,
}

impl EthernetDataLinkSender for DataLinkSenderImpl {
    #[inline]
    fn build_and_send(&mut self,
                      num_packets: usize,
                      packet_size: usize,
                      func: &mut FnMut(MutableEthernetPacket))
                      -> Option<io::Result<()>> {
        let len = num_packets * packet_size;
        if len >= self.write_buffer.len() {
            None
        } else {
            // If we're sending on the loopback device, discard the ethernet header.
            // The OS will prepend the packet with 4 bytes set to AF_INET.
            let offset = if self.loopback {
                MutableEthernetPacket::minimum_packet_size()
            } else {
                0
            };
            for chunk in self.write_buffer[..len].chunks_mut(packet_size) {
                {
                    let eh = MutableEthernetPacket::new(chunk).unwrap();
                    func(eh);
                }
                match unsafe {
                    libc::write(self.fd.fd,
                                chunk.as_ptr().offset(offset as isize) as *const libc::c_void,
                                (chunk.len() - offset) as libc::size_t)
                } {
                    len if len == -1 => return Some(Err(io::Error::last_os_error())),
                    _ => (),
                }
            }
            Some(Ok(()))
        }
    }

    #[inline]
    fn send_to(&mut self,
               packet: &EthernetPacket,
               _dst: Option<NetworkInterface>)
               -> Option<io::Result<()>> {
        // If we're sending on the loopback device, discard the ethernet header.
        // The OS will prepend the packet with 4 bytes set to AF_INET.
        let offset = if self.loopback {
            MutableEthernetPacket::minimum_packet_size()
        } else {
            0
        };
        match unsafe {
            libc::write(self.fd.fd,
                        packet.packet().as_ptr().offset(offset as isize) as *const libc::c_void,
                        (packet.packet().len() - offset) as libc::size_t)
        } {
            len if len == -1 => Some(Err(io::Error::last_os_error())),
            _ => Some(Ok(())),
        }
    }
}

pub struct DataLinkReceiverImpl {
    fd: Arc<internal::FileDesc>,
    read_buffer: Vec<u8>,
    loopback: bool,
}

impl EthernetDataLinkReceiver for DataLinkReceiverImpl {
    fn iter<'a>(&'a mut self) -> Box<EthernetDataLinkChannelIterator + 'a> {
        let buflen = self.read_buffer.len();
        Box::new(DataLinkChannelIteratorImpl {
            pc: self,
            // Enough room for minimally sized packets without reallocating
            packets: VecDeque::with_capacity(buflen / 64),
        })
    }
}

pub struct DataLinkChannelIteratorImpl<'a> {
    pc: &'a mut DataLinkReceiverImpl,
    packets: VecDeque<(usize, usize)>,
}

impl<'a> EthernetDataLinkChannelIterator<'a> for DataLinkChannelIteratorImpl<'a> {
    fn next(&mut self) -> io::Result<EthernetPacket> {
        // Loopback packets arrive with a 4 byte header instead of normal ethernet header.
        // Discard that header and replace with zeroed out ethernet header.
        let (header_size, buffer_offset) = if self.pc.loopback {
            (4, EthernetPacket::minimum_packet_size())
        } else {
            (0, 0)
        };
        if self.packets.is_empty() {
            let buffer = &mut self.pc.read_buffer[buffer_offset..];
            let buflen = match unsafe {
                libc::read(self.pc.fd.fd,
                           buffer.as_ptr() as *mut libc::c_void,
                           buffer.len() as libc::size_t)
            } {
                len if len > 0 => len,
                _ => return Err(io::Error::last_os_error()),
            };
            let mut ptr = buffer.as_mut_ptr();
            let end = unsafe { buffer.as_ptr().offset(buflen as isize) };
            while (ptr as *const u8) < end {
                unsafe {
                    let packet: *const bpf::bpf_hdr = mem::transmute(ptr);
                    let start = ptr as isize + (*packet).bh_hdrlen as isize -
                                buffer.as_ptr() as isize;
                    self.packets.push_back((start as usize + header_size,
                                            (*packet).bh_caplen as usize - header_size));
                    let offset = (*packet).bh_hdrlen as isize + (*packet).bh_caplen as isize;
                    ptr = ptr.offset(bpf::BPF_WORDALIGN(offset));
                }
            }
        }
        let (start, mut len) = self.packets.pop_front().unwrap();
        len += buffer_offset;
        // Zero out part that will become fake ethernet header if on loopback.
        for i in (&mut self.pc.read_buffer[start..start + buffer_offset]).iter_mut() {
            *i = 0;
        }
        Ok(EthernetPacket::new(&self.pc.read_buffer[start..start + len]).unwrap())
    }
}
<|endoftext|>