<commit_before>use std::collections::BTreeMap;
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
use std::path::PathBuf;
use std::process::Command;
use std::io::{self, Write};
use std::fs;

use rustc_version::VersionMeta;
use tempdir::TempDir;
use toml::{Table, Value};

use CompilationMode;
use cargo::{Root, Rustflags};
use errors::*;
use extensions::CommandExt;
use rustc::{Src, Sysroot};
use util;
use xargo::Home;
use {cargo, xargo};

#[cfg(feature = "dev")]
fn profile() -> &'static str {
    "debug"
}

#[cfg(not(feature = "dev"))]
fn profile() -> &'static str {
    "release"
}

fn build(
    cmode: &CompilationMode,
    blueprint: Blueprint,
    ctoml: &cargo::Toml,
    home: &Home,
    rustflags: &Rustflags,
    src: &Src,
    sysroot: &Sysroot,
    hash: u64,
    verbose: bool,
) -> Result<()> {
    const TOML: &'static str = r#"
[package]
authors = ["The Rust Project Developers"]
name = "sysroot"
version = "0.0.0"
"#;

    let rustlib = home.lock_rw(cmode.triple())?;
    rustlib
        .remove_siblings()
        .chain_err(|| format!("couldn't clear {}", rustlib.path().display()))?;
    let dst = rustlib.parent().join("lib");
    util::mkdir(&dst)?;

    if cmode.triple().contains("pc-windows-gnu") {
        let src = &sysroot.path()
            .join("lib")
            .join("rustlib")
            .join(cmode.triple())
            .join("lib");

        // These are required for linking executables/dlls
        for file in ["rsbegin.o", "rsend.o", "crt2.o", "dllcrt2.o"].iter() {
            let file_src = src.join(file);
            let file_dst = dst.join(file);
            fs::copy(&file_src, &file_dst)
                .chain_err(|| format!("couldn't copy {} to {}", file_src.display(), file_dst.display()))?;
        }
    }

    for (_, stage) in blueprint.stages {
        let td = TempDir::new("xargo").chain_err(|| "couldn't create a temporary directory")?;
        let td = td.path();

        let mut stoml = TOML.to_owned();

        let mut map = Table::new();
        map.insert("dependencies".to_owned(), Value::Table(stage.toml));
        stoml.push_str(&Value::Table(map).to_string());

        if let Some(profile) = ctoml.profile() {
            stoml.push_str(&profile.to_string())
        }

        {
            // Recent rust-src comes with a lockfile for libstd. Use it.
            let lockfile = src.path().join("Cargo.lock");
            if lockfile.exists() {
                fs::copy(lockfile, &td.join("Cargo.lock")).chain_err(|| "couldn't copy lock file")?;
            }
        }
        util::write(&td.join("Cargo.toml"), &stoml)?;
        util::mkdir(&td.join("src"))?;
        util::write(&td.join("src/lib.rs"), "")?;

        let cargo = || {
            let mut cmd = Command::new("cargo");
            let mut flags = rustflags.for_xargo(home);
            flags.push_str(" -Z force-unstable-if-unmarked");
            if verbose {
                writeln!(io::stderr(), "+ RUSTFLAGS={:?}", flags).ok();
            }
            cmd.env("RUSTFLAGS", flags);
            cmd.env_remove("CARGO_TARGET_DIR");
            cmd.arg("build");

            match () {
                #[cfg(feature = "dev")]
                () => {}
                #[cfg(not(feature = "dev"))]
                () => {
                    cmd.arg("--release");
                }
            }
            cmd.arg("--manifest-path");
            cmd.arg(td.join("Cargo.toml"));
            cmd.args(&["--target", cmode.triple()]);

            if verbose {
                cmd.arg("-v");
            }

            cmd
        };

        for krate in stage.crates {
            cargo().arg("-p").arg(krate).run(verbose)?;
        }

        // Copy artifacts to Xargo sysroot
        util::cp_r(
            &td.join("target")
                .join(cmode.triple())
                .join(profile())
                .join("deps"),
            &dst,
        )?;
    }

    // Create hash file
    util::write(&rustlib.parent().join(".hash"), &hash.to_string())?;

    Ok(())
}

fn old_hash(cmode: &CompilationMode, home: &Home) -> Result<Option<u64>> {
    // FIXME this should be `lock_ro`
    let lock = home.lock_rw(cmode.triple())?;
    let hfile = lock.parent().join(".hash");

    if hfile.exists() {
        Ok(util::read(&hfile)?.parse().ok())
    } else {
        Ok(None)
    }
}

/// Computes the hash of the would-be target sysroot
///
/// This information is used to compute the hash
///
/// - Dependencies in `Xargo.toml` for a specific target
/// - RUSTFLAGS / build.rustflags / target.*.rustflags
/// - The target specification file, is any
/// - `[profile.release]` in `Cargo.toml`
/// - `rustc` commit hash
fn hash(
    cmode: &CompilationMode,
    blueprint: &Blueprint,
    rustflags: &Rustflags,
    ctoml: &cargo::Toml,
    meta: &VersionMeta,
) -> Result<u64> {
    let mut hasher = DefaultHasher::new();

    blueprint.hash(&mut hasher);

    rustflags.hash(&mut hasher);

    cmode.hash(&mut hasher)?;

    if let Some(profile) = ctoml.profile() {
        profile.hash(&mut hasher);
    }

    if let Some(ref hash) = meta.commit_hash {
        hash.hash(&mut hasher);
    }

    Ok(hasher.finish())
}

pub fn update(
    cmode: &CompilationMode,
    home: &Home,
    root: &Root,
    rustflags: &Rustflags,
    meta: &VersionMeta,
    src: &Src,
    sysroot: &Sysroot,
    verbose: bool,
) -> Result<()> {
    let ctoml = cargo::toml(root)?;
    let xtoml = xargo::toml(root)?;

    let blueprint = Blueprint::from(xtoml.as_ref(), cmode.triple(), root, &src)?;

    let hash = hash(cmode, &blueprint, rustflags, &ctoml, meta)?;

    if old_hash(cmode, home)? != Some(hash) {
        build(
            cmode,
            blueprint,
            &ctoml,
            home,
            rustflags,
            src,
            sysroot,
            hash,
            verbose,
        )?;
    }

    // copy host artifacts into the sysroot, if necessary
    if cmode.is_native() {
        return Ok(());
    }

    let lock = home.lock_rw(&meta.host)?;
    let hfile = lock.parent().join(".hash");

    let hash = meta.commit_hash.as_ref().map(|s| &**s).unwrap_or("");
    if hfile.exists() {
        if util::read(&hfile)? == hash {
            return Ok(());
        }
    }

    lock.remove_siblings()
        .chain_err(|| format!("couldn't clear {}", lock.path().display()))?;
    let dst = lock.parent().join("lib");
    util::mkdir(&dst)?;
    util::cp_r(
        &sysroot
            .path()
            .join("lib/rustlib")
            .join(&meta.host)
            .join("lib"),
        &dst,
    )?;

    util::write(&hfile, hash)?;

    Ok(())
}

/// Per stage dependencies
#[derive(Debug)]
pub struct Stage {
    crates: Vec<String>,
    toml: Table,
}

/// A sysroot that will be built in "stages"
#[derive(Debug)]
pub struct Blueprint {
    stages: BTreeMap<i64, Stage>,
}

impl Blueprint {
    fn new() -> Self {
        Blueprint {
            stages: BTreeMap::new(),
        }
    }

    fn from(toml: Option<&xargo::Toml>, target: &str, root: &Root, src: &Src) -> Result<Self> {
        let deps = match (
            toml.and_then(|t| t.dependencies()),
            toml.and_then(|t| t.target_dependencies(target)),
        ) {
            (Some(value), Some(tvalue)) => {
                let mut deps = value
                    .as_table()
                    .cloned()
                    .ok_or_else(|| format!("Xargo.toml: `dependencies` must be a table"))?;

                let more_deps = tvalue.as_table().ok_or_else(|| {
                    format!(
                        "Xargo.toml: `target.{}.dependencies` must be \
                         a table",
                        target
                    )
                })?;
                for (k, v) in more_deps {
                    if deps.insert(k.to_owned(), v.clone()).is_some() {
                        Err(format!(
                            "found duplicate dependency name {}, \
                             but all dependencies must have a \
                             unique name",
                            k
                        ))?
                    }
                }

                deps
            }
            (Some(value), None) | (None, Some(value)) => if let Some(table) = value.as_table() {
                table.clone()
            } else {
                Err(format!(
                    "Xargo.toml: target.{}.dependencies must be \
                     a table",
                    target
                ))?
            },
            (None, None) => {
                // If no dependencies were listed, we assume `core` as the
                // only dependency
                let mut t = BTreeMap::new();
                t.insert("core".to_owned(), Value::Table(BTreeMap::new()));
                t
            }
        };

        let mut blueprint = Blueprint::new();
        for (k, v) in deps {
            if let Value::Table(mut map) = v {
                let stage = if let Some(value) = map.remove("stage") {
                    value
                        .as_integer()
                        .ok_or_else(|| format!("dependencies.{}.stage must be an integer", k))?
                } else {
                    0
                };

                if let Some(path) = map.get_mut("path") {
                    let p = PathBuf::from(path.as_str()
                        .ok_or_else(|| format!("dependencies.{}.path must be a string", k))?);

                    if !p.is_absolute() {
                        *path = Value::String(
                            root.path()
                                .join(&p)
                                .canonicalize()
                                .chain_err(|| format!("couldn't canonicalize {}", p.display()))?
                                .display()
                                .to_string(),
                        );
                    }
                }

                if !map.contains_key("path") && !map.contains_key("git") {
                    let path = src.path().join(format!("lib{}", k)).display().to_string();

                    map.insert("path".to_owned(), Value::String(path));
                }

                blueprint.push(stage, k, map);
            } else {
                Err(format!(
                    "Xargo.toml: target.{}.dependencies.{} must be \
                     a table",
                    target,
                    k
                ))?
            }
        }

        Ok(blueprint)
    }

    fn push(&mut self, stage: i64, krate: String, toml: Table) {
        let stage = self.stages.entry(stage).or_insert_with(|| {
            Stage {
                crates: vec![],
                toml: Table::new(),
            }
        });

        stage.toml.insert(krate.clone(), Value::Table(toml));
        stage.crates.push(krate);
    }

    fn hash<H>(&self, hasher: &mut H)
    where
        H: Hasher,
    {
        for stage in self.stages.values() {
            for (k, v) in stage.toml.iter() {
                k.hash(hasher);
                v.to_string().hash(hasher);
            }
        }
    }
}
<commit_msg>set RUST_TARGET_PATH when compiling the sysroot<commit_after>use std::collections::BTreeMap;
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
use std::path::PathBuf;
use std::process::Command;
use std::io::{self, Write};
use std::{fs, env};

use rustc_version::VersionMeta;
use tempdir::TempDir;
use toml::{Table, Value};

use CompilationMode;
use cargo::{Root, Rustflags};
use errors::*;
use extensions::CommandExt;
use rustc::{Src, Sysroot, Target};
use util;
use xargo::Home;
use {cargo, xargo};

#[cfg(feature = "dev")]
fn profile() -> &'static str {
    "debug"
}

#[cfg(not(feature = "dev"))]
fn profile() -> &'static str {
    "release"
}

fn build(
    cmode: &CompilationMode,
    blueprint: Blueprint,
    ctoml: &cargo::Toml,
    home: &Home,
    rustflags: &Rustflags,
    src: &Src,
    sysroot: &Sysroot,
    hash: u64,
    verbose: bool,
) -> Result<()> {
    const TOML: &'static str = r#"
[package]
authors = ["The Rust Project Developers"]
name = "sysroot"
version = "0.0.0"
"#;

    let rustlib = home.lock_rw(cmode.triple())?;
    rustlib
        .remove_siblings()
        .chain_err(|| format!("couldn't clear {}", rustlib.path().display()))?;
    let dst = rustlib.parent().join("lib");
    util::mkdir(&dst)?;

    if cmode.triple().contains("pc-windows-gnu") {
        let src = &sysroot.path()
            .join("lib")
            .join("rustlib")
            .join(cmode.triple())
            .join("lib");

        // These are required for linking executables/dlls
        for file in ["rsbegin.o", "rsend.o", "crt2.o", "dllcrt2.o"].iter() {
            let file_src = src.join(file);
            let file_dst = dst.join(file);
            fs::copy(&file_src, &file_dst)
                .chain_err(|| format!("couldn't copy {} to {}", file_src.display(), file_dst.display()))?;
        }
    }

    for (_, stage) in blueprint.stages {
        let td = TempDir::new("xargo").chain_err(|| "couldn't create a temporary directory")?;
        let td = td.path();

        let mut stoml = TOML.to_owned();

        let mut map = Table::new();
        map.insert("dependencies".to_owned(), Value::Table(stage.toml));
        stoml.push_str(&Value::Table(map).to_string());

        if let Some(profile) = ctoml.profile() {
            stoml.push_str(&profile.to_string())
        }

        {
            // Recent rust-src comes with a lockfile for libstd. Use it.
            let lockfile = src.path().join("Cargo.lock");
            if lockfile.exists() {
                fs::copy(lockfile, &td.join("Cargo.lock")).chain_err(|| "couldn't copy lock file")?;
            }
        }
        util::write(&td.join("Cargo.toml"), &stoml)?;
        util::mkdir(&td.join("src"))?;
        util::write(&td.join("src/lib.rs"), "")?;

        let cargo = || {
            let mut cmd = Command::new("cargo");
            let mut flags = rustflags.for_xargo(home);
            flags.push_str(" -Z force-unstable-if-unmarked");
            if verbose {
                writeln!(io::stderr(), "+ RUSTFLAGS={:?}", flags).ok();
            }
            cmd.env("RUSTFLAGS", flags);
            cmd.env_remove("CARGO_TARGET_DIR");

            // As of rust-lang/cargo#4788 Cargo invokes rustc with a changed "current directory" so
            // we can't assume that such directory will be the same as the directory from which
            // Xargo was invoked. This is specially true when compiling the sysroot as the std
            // source is provided as a workspace and Cargo will change the current directory to the
            // root of the workspace when building one. To ensure rustc finds a target specification
            // file stored in the current directory we'll set `RUST_TARGET_PATH`  to the current
            // directory.
            if env::var_os("RUST_TARGET_PATH").is_none() {
                if let CompilationMode::Cross(ref target) = *cmode {
                    if let Target::Custom { ref json, .. } = *target {
                        cmd.env("RUST_TARGET_PATH", json.parent().unwrap());
                    }
                }
            }

            cmd.arg("build");

            match () {
                #[cfg(feature = "dev")]
                () => {}
                #[cfg(not(feature = "dev"))]
                () => {
                    cmd.arg("--release");
                }
            }
            cmd.arg("--manifest-path");
            cmd.arg(td.join("Cargo.toml"));
            cmd.args(&["--target", cmode.triple()]);

            if verbose {
                cmd.arg("-v");
            }

            cmd
        };

        for krate in stage.crates {
            cargo().arg("-p").arg(krate).run(verbose)?;
        }

        // Copy artifacts to Xargo sysroot
        util::cp_r(
            &td.join("target")
                .join(cmode.triple())
                .join(profile())
                .join("deps"),
            &dst,
        )?;
    }

    // Create hash file
    util::write(&rustlib.parent().join(".hash"), &hash.to_string())?;

    Ok(())
}

fn old_hash(cmode: &CompilationMode, home: &Home) -> Result<Option<u64>> {
    // FIXME this should be `lock_ro`
    let lock = home.lock_rw(cmode.triple())?;
    let hfile = lock.parent().join(".hash");

    if hfile.exists() {
        Ok(util::read(&hfile)?.parse().ok())
    } else {
        Ok(None)
    }
}

/// Computes the hash of the would-be target sysroot
///
/// This information is used to compute the hash
///
/// - Dependencies in `Xargo.toml` for a specific target
/// - RUSTFLAGS / build.rustflags / target.*.rustflags
/// - The target specification file, is any
/// - `[profile.release]` in `Cargo.toml`
/// - `rustc` commit hash
fn hash(
    cmode: &CompilationMode,
    blueprint: &Blueprint,
    rustflags: &Rustflags,
    ctoml: &cargo::Toml,
    meta: &VersionMeta,
) -> Result<u64> {
    let mut hasher = DefaultHasher::new();

    blueprint.hash(&mut hasher);

    rustflags.hash(&mut hasher);

    cmode.hash(&mut hasher)?;

    if let Some(profile) = ctoml.profile() {
        profile.hash(&mut hasher);
    }

    if let Some(ref hash) = meta.commit_hash {
        hash.hash(&mut hasher);
    }

    Ok(hasher.finish())
}

pub fn update(
    cmode: &CompilationMode,
    home: &Home,
    root: &Root,
    rustflags: &Rustflags,
    meta: &VersionMeta,
    src: &Src,
    sysroot: &Sysroot,
    verbose: bool,
) -> Result<()> {
    let ctoml = cargo::toml(root)?;
    let xtoml = xargo::toml(root)?;

    let blueprint = Blueprint::from(xtoml.as_ref(), cmode.triple(), root, &src)?;

    let hash = hash(cmode, &blueprint, rustflags, &ctoml, meta)?;

    if old_hash(cmode, home)? != Some(hash) {
        build(
            cmode,
            blueprint,
            &ctoml,
            home,
            rustflags,
            src,
            sysroot,
            hash,
            verbose,
        )?;
    }

    // copy host artifacts into the sysroot, if necessary
    if cmode.is_native() {
        return Ok(());
    }

    let lock = home.lock_rw(&meta.host)?;
    let hfile = lock.parent().join(".hash");

    let hash = meta.commit_hash.as_ref().map(|s| &**s).unwrap_or("");
    if hfile.exists() {
        if util::read(&hfile)? == hash {
            return Ok(());
        }
    }

    lock.remove_siblings()
        .chain_err(|| format!("couldn't clear {}", lock.path().display()))?;
    let dst = lock.parent().join("lib");
    util::mkdir(&dst)?;
    util::cp_r(
        &sysroot
            .path()
            .join("lib/rustlib")
            .join(&meta.host)
            .join("lib"),
        &dst,
    )?;

    util::write(&hfile, hash)?;

    Ok(())
}

/// Per stage dependencies
#[derive(Debug)]
pub struct Stage {
    crates: Vec<String>,
    toml: Table,
}

/// A sysroot that will be built in "stages"
#[derive(Debug)]
pub struct Blueprint {
    stages: BTreeMap<i64, Stage>,
}

impl Blueprint {
    fn new() -> Self {
        Blueprint {
            stages: BTreeMap::new(),
        }
    }

    fn from(toml: Option<&xargo::Toml>, target: &str, root: &Root, src: &Src) -> Result<Self> {
        let deps = match (
            toml.and_then(|t| t.dependencies()),
            toml.and_then(|t| t.target_dependencies(target)),
        ) {
            (Some(value), Some(tvalue)) => {
                let mut deps = value
                    .as_table()
                    .cloned()
                    .ok_or_else(|| format!("Xargo.toml: `dependencies` must be a table"))?;

                let more_deps = tvalue.as_table().ok_or_else(|| {
                    format!(
                        "Xargo.toml: `target.{}.dependencies` must be \
                         a table",
                        target
                    )
                })?;
                for (k, v) in more_deps {
                    if deps.insert(k.to_owned(), v.clone()).is_some() {
                        Err(format!(
                            "found duplicate dependency name {}, \
                             but all dependencies must have a \
                             unique name",
                            k
                        ))?
                    }
                }

                deps
            }
            (Some(value), None) | (None, Some(value)) => if let Some(table) = value.as_table() {
                table.clone()
            } else {
                Err(format!(
                    "Xargo.toml: target.{}.dependencies must be \
                     a table",
                    target
                ))?
            },
            (None, None) => {
                // If no dependencies were listed, we assume `core` as the
                // only dependency
                let mut t = BTreeMap::new();
                t.insert("core".to_owned(), Value::Table(BTreeMap::new()));
                t
            }
        };

        let mut blueprint = Blueprint::new();
        for (k, v) in deps {
            if let Value::Table(mut map) = v {
                let stage = if let Some(value) = map.remove("stage") {
                    value
                        .as_integer()
                        .ok_or_else(|| format!("dependencies.{}.stage must be an integer", k))?
                } else {
                    0
                };

                if let Some(path) = map.get_mut("path") {
                    let p = PathBuf::from(path.as_str()
                        .ok_or_else(|| format!("dependencies.{}.path must be a string", k))?);

                    if !p.is_absolute() {
                        *path = Value::String(
                            root.path()
                                .join(&p)
                                .canonicalize()
                                .chain_err(|| format!("couldn't canonicalize {}", p.display()))?
                                .display()
                                .to_string(),
                        );
                    }
                }

                if !map.contains_key("path") && !map.contains_key("git") {
                    let path = src.path().join(format!("lib{}", k)).display().to_string();

                    map.insert("path".to_owned(), Value::String(path));
                }

                blueprint.push(stage, k, map);
            } else {
                Err(format!(
                    "Xargo.toml: target.{}.dependencies.{} must be \
                     a table",
                    target,
                    k
                ))?
            }
        }

        Ok(blueprint)
    }

    fn push(&mut self, stage: i64, krate: String, toml: Table) {
        let stage = self.stages.entry(stage).or_insert_with(|| {
            Stage {
                crates: vec![],
                toml: Table::new(),
            }
        });

        stage.toml.insert(krate.clone(), Value::Table(toml));
        stage.crates.push(krate);
    }

    fn hash<H>(&self, hasher: &mut H)
    where
        H: Hasher,
    {
        for stage in self.stages.values() {
            for (k, v) in stage.toml.iter() {
                k.hash(hasher);
                v.to_string().hash(hasher);
            }
        }
    }
}
<|endoftext|>


<commit_before>use std::u16;
use std::net::{TcpStream, Shutdown};
use std::io;
use std::io::{Write, Read, Cursor};
use std::time::Duration;
use std::borrow::BorrowMut;
use byteorder::{BigEndian, WriteBytesExt, ReadBytesExt};
use bincode::rustc_serialize::{encode, decode};
use bincode::SizeLimit;

use enum_primitive::FromPrimitive;

use {Function, ModbusResult, ModbusExceptionCode, ModbusError, BitValue};

const MODBUS_PROTOCOL_TCP: u16 = 0x0000;
const MODBUS_TCP_DEFAULT_PORT: u16 = 502;
const MODBUS_HEADER_SIZE: usize = 7;
const MODBUS_MAX_READ_COUNT: usize = 0x7d;
const MODBUS_MAX_WRITE_COUNT: usize = 0x79;

/// Context object which holds state for all modbus operations.
pub struct Ctx {
    tid: u16,
    uid: u8,
    stream: TcpStream
}

impl Ctx {
    /// Create a new context `Ctx` context object and connect it to `addr` on modbus-tcp default
    /// port (502)
    pub fn new(addr: &str) -> io::Result<Ctx> {
        Self::new_with_port(addr, MODBUS_TCP_DEFAULT_PORT)
    }

    /// Create a new context `Ctx` context object and connect it to `addr` on port `port`
    pub fn new_with_port(addr: &str, port: u16) -> io::Result<Ctx> {
        match TcpStream::connect((addr, port)) {
            Ok(s) => {
                // set some sane tcp socket options
                let t = Duration::from_secs(5);
                try!(s.set_read_timeout(Some(t)));
                try!(s.set_write_timeout(Some(t)));
//                try!(s.set_nodelay(true));
//                try!(s.set_keepalive(None));
                Ok(Ctx { tid: 0, uid: 1, stream: s })
            }
            Err(e) => Err(e)
        }
    }

    /// Create a new transaction Id, incrementing the previous one.
    ///
    /// The Id is wrapping around if the Id reaches `u16::MAX`.
    fn new_tid(&mut self) -> u16 {
        // wrap around or increment
        if self.tid  < u16::MAX {
            self.tid += 1u16;
        } else {
            self.tid = 0u16;
        }
        self.tid
    }
}

impl Drop for Ctx {
    fn drop(&mut self) {
        self.stream.shutdown(Shutdown::Both).unwrap();
    }
}

#[derive(RustcEncodable, RustcDecodable)]
#[repr(packed)]
struct Header {
    tid: u16,
    pid: u16,
    len: u16,
    uid: u8
}

impl Header {
    fn new(ctx: &mut Ctx, len: u16) -> Header {
        Header {
            tid: ctx.new_tid(),
            pid: MODBUS_PROTOCOL_TCP,
            len: len - MODBUS_HEADER_SIZE as u16,
            uid: ctx.uid
        }
    }
}

pub fn read_coils(ctx: &mut Ctx, addr: u16, count: u16) -> ModbusResult<Vec<BitValue>>
{
    let bytes = try!(read(ctx, Function::ReadCoils(addr, count)));
    let res = unpack_bits(&bytes, count);
    Ok(res)
}

pub fn read_discrete_inputs(ctx: &mut Ctx, addr: u16, count: u16) -> ModbusResult<Vec<BitValue>>
{
    let bytes = try!(read(ctx, Function::ReadDiscreteInputs(addr, count)));
    let res = unpack_bits(&bytes, count);
    Ok(res)
}

pub fn read_holding_registers(ctx: &mut Ctx, addr: u16, count: u16) -> ModbusResult<Vec<u16>>
{
    let bytes = try!(read(ctx, Function::ReadHoldingRegisters(addr, count)));
    let res: Vec<u16> = bytes.iter().map(|&v| v as u16).collect();
    Ok(res)
}

pub fn read_input_registers(ctx: &mut Ctx, addr: u16, count: u16) -> ModbusResult<Vec<u16>>
{
    let bytes = try!(read(ctx, Function::ReadInputRegisters(addr, count)));
    let res: Vec<u16> = bytes.iter().map(|&v| v as u16).collect();
    Ok(res)
}

fn read(ctx: &mut Ctx, fun: Function) -> ModbusResult<Vec<u8>>
{
    let packed_size = |v: u16| {v/8 + if v%8 > 0 {1} else {0}};
    let (addr, count, expected_bytes) = match fun {
            Function::ReadCoils(a, c)            => (a, c, packed_size(c) as usize),
            Function::ReadDiscreteInputs(a, c)   => (a, c, packed_size(c) as usize),
            Function::ReadHoldingRegisters(a, c) => (a, c, 2*c as usize),
            Function::ReadInputRegisters(a, c)   => (a, c, 2*c as usize),
            _ => panic!("Unexpected modbus function")
    };

    if count < 1 || count as usize > MODBUS_MAX_READ_COUNT {
        return Err(ModbusError::InvalidData);
    }

    let header = Header::new(ctx, MODBUS_HEADER_SIZE as u16 + 6u16);
    let mut buff = try!(encode(&header, SizeLimit::Infinite));
    try!(buff.write_u8(fun.code()));
    try!(buff.write_u16::<BigEndian>(addr));
    try!(buff.write_u16::<BigEndian>(count));

    match ctx.stream.write_all(&buff[..]) {
        Ok(_s) => {
                let mut reply = vec![0; MODBUS_HEADER_SIZE + expected_bytes + 2];
                match ctx.stream.read(&mut reply) {
                    Ok(_s) => {
                        let resp_hd = try!(decode(&reply[..MODBUS_HEADER_SIZE]));
                        try!(validate_response_header(&header, &resp_hd));
                        try!(validate_response_code(&buff, &reply[..]));
                        get_reply_data(&reply, expected_bytes)
                    }
                    Err(e) => Err(ModbusError::Io(e))
                }
        }
        Err(e) => Err(ModbusError::Io(e))
    }
}

pub fn write_single_coil(ctx: &mut Ctx, addr: u16, value: BitValue) -> ModbusResult<()>
{
    write_single(ctx, Function::WriteSingleCoil(addr, value.code()))
}

pub fn write_single_register(ctx: &mut Ctx, addr: u16, value: u16) -> ModbusResult<()>
{
    write_single(ctx, Function::WriteSingleRegister(addr, value))
}

fn write_single(ctx: &mut Ctx, fun: Function) -> ModbusResult<()>
{
    let (addr, value) = match fun {
        Function::WriteSingleCoil(a, v)     => (a, v),
        Function::WriteSingleRegister(a, v) => (a, v),
        _ => panic!("Unexpected modbus function")
    };

    let mut buff = vec![0; MODBUS_HEADER_SIZE];  // Header gets filled in later
    try!(buff.write_u8(fun.code()));
    try!(buff.write_u16::<BigEndian>(addr));
    try!(buff.write_u16::<BigEndian>(value));
    write(ctx, &mut buff)
}

fn write(ctx: &mut Ctx, buff: &mut Vec<u8>) -> ModbusResult<()> {
    if buff.len() < 1 || buff.len() > MODBUS_MAX_WRITE_COUNT {
        return Err(ModbusError::InvalidData);
    }
    let header = Header::new(ctx, buff.len() as u16 + 1u16);
    let head_buff = try!(encode(&header, SizeLimit::Infinite));
    {
        let mut start = Cursor::new(buff.borrow_mut());
        try!(start.write(&head_buff[..]));
    }
    match ctx.stream.write_all(&buff[..]) {
        Ok(_s) => {
                let reply = &mut [0; 12];
                match ctx.stream.read(reply) {
                    Ok(_s) => {
                        let resp_hd = try!(decode(&reply[..MODBUS_HEADER_SIZE]));
                        try!(validate_response_header(&header, &resp_hd));
                        validate_response_code(&buff, reply)
                    }
                    Err(e) => Err(ModbusError::Io(e))
                }
        }
        Err(e) => Err(ModbusError::Io(e))
    }
}

fn validate_response_header(req: &Header, resp: &Header) -> ModbusResult<()> {
    if req.tid != resp.tid || resp.pid != MODBUS_PROTOCOL_TCP {
        Err(ModbusError::InvalidResponse)
    }
    else {
        Ok(())
    }
}

fn validate_response_code(req: &[u8], resp: &[u8]) -> ModbusResult<()> {
    if req[7] + 0x80  == resp[7] {
        let code = ModbusExceptionCode::from_u8(resp[8]).unwrap();
        Err(ModbusError::ModbusException(code))
    }
    else if req[7] != resp[7] {
        Err(ModbusError::InvalidResponse)
    }
    else {
        Ok(())
    }
}

fn get_reply_data(reply: &Vec<u8>, expected_bytes: usize) -> ModbusResult<Vec<u8>> {
    if reply[8] as usize != expected_bytes ||
       reply.len() != MODBUS_HEADER_SIZE + expected_bytes + 2
    {
        Err(ModbusError::InvalidData)
    }
    else {

        let mut d = Vec::new();
        d.extend(reply[MODBUS_HEADER_SIZE+2..].iter());
        Ok(d)
    }
}

fn unpack_bits(bytes: &[u8], count: u16) -> Vec<BitValue> {
    let mut res = Vec::with_capacity(count as usize);
    for i in 0..count {
        if (bytes[(i/8u16) as usize] >> i%8) & 0x1 > 0 {
            res.push(BitValue::On);
        }
        else {
            res.push(BitValue::Off);
        }
    }
    res
}

#[cfg(test)]
fn start_dummy_server(port: &str) -> ChildKiller {
    use std::process::{Command, Stdio};
    use std::thread::sleep_ms;
    let ck = ChildKiller(Command::new("./test/diagslave")
                        .arg("-m").arg("tcp")
                        .arg("-p").arg(port)
                        .stdout(Stdio::null())
                        .spawn()
                        .unwrap_or_else(|e| { panic!("failed to execute process: {}", e) }));
    sleep_ms(500);
    ck
}

#[cfg(test)]
use std::process::Child;
#[cfg(test)]
struct ChildKiller(Child);

#[cfg(test)]
impl Drop for ChildKiller {
    fn drop(&mut self) {
        self.0.kill().unwrap();
    }
}

#[test]
fn test_packet_tid_creation() {
    let _s = start_dummy_server("2222");
    let mut ctx = Ctx::new_with_port("127.0.0.1", 2222).unwrap();
    let mut hd = Header::new(&mut ctx, 10);
    assert!(hd.tid == 1u16);
    hd = Header::new(&mut ctx, 10);
    assert!(hd.tid == 2u16);
    ctx.tid = u16::MAX;
    hd = Header::new(&mut ctx, 10);
    assert!(hd.tid == 0);
}

#[test]
fn test_write_single_coil() {
    let _s = start_dummy_server("2223");
    let mut ctx = Ctx::new_with_port("127.0.0.1", 2223).unwrap();
    for i in 0..10 {
        assert!(write_single_coil(&mut ctx, i, BitValue::On).is_ok());
    }
}

#[test]
fn test_write_single_register() {
    let _s = start_dummy_server("2224");
    let mut ctx = Ctx::new_with_port("127.0.0.1", 2224).unwrap();
    for i in 0..10 {
        assert!(write_single_register(&mut ctx, i, i).is_ok());
    }
}

#[test]
fn test_read_coils() {
    let _s = start_dummy_server("2225");
    let mut ctx = Ctx::new_with_port("127.0.0.1", 2225).unwrap();

    assert!(write_single_coil(&mut ctx, 1, BitValue::On).is_ok());
    assert!(write_single_coil(&mut ctx, 3, BitValue::On).is_ok());
    assert!(read_coils(&mut ctx, 0, 5).unwrap() ==
            vec![BitValue::Off, BitValue::On, BitValue::Off, BitValue::On, BitValue::Off]);
}
<commit_msg>Little code cleanup<commit_after>use std::u16;
use std::net::{TcpStream, Shutdown};
use std::io;
use std::io::{Write, Read, Cursor};
use std::time::Duration;
use std::borrow::BorrowMut;
use byteorder::{BigEndian, WriteBytesExt};
use bincode::rustc_serialize::{encode, decode};
use bincode::SizeLimit;

use enum_primitive::FromPrimitive;

use {Function, ModbusResult, ModbusExceptionCode, ModbusError, BitValue};

const MODBUS_PROTOCOL_TCP: u16 = 0x0000;
const MODBUS_TCP_DEFAULT_PORT: u16 = 502;
const MODBUS_HEADER_SIZE: usize = 7;
const MODBUS_MAX_READ_COUNT: usize = 0x7d;
const MODBUS_MAX_WRITE_COUNT: usize = 0x79;

/// Context object which holds state for all modbus operations.
pub struct Ctx {
    tid: u16,
    uid: u8,
    stream: TcpStream
}

impl Ctx {
    /// Create a new context `Ctx` context object and connect it to `addr` on modbus-tcp default
    /// port (502)
    pub fn new(addr: &str) -> io::Result<Ctx> {
        Self::new_with_port(addr, MODBUS_TCP_DEFAULT_PORT)
    }

    /// Create a new context `Ctx` context object and connect it to `addr` on port `port`
    pub fn new_with_port(addr: &str, port: u16) -> io::Result<Ctx> {
        match TcpStream::connect((addr, port)) {
            Ok(s) => {
                // set some sane tcp socket options
                let t = Duration::from_secs(5);
                try!(s.set_read_timeout(Some(t)));
                try!(s.set_write_timeout(Some(t)));
//                try!(s.set_nodelay(true));
//                try!(s.set_keepalive(None));
                Ok(Ctx { tid: 0, uid: 1, stream: s })
            }
            Err(e) => Err(e)
        }
    }

    /// Create a new transaction Id, incrementing the previous one.
    ///
    /// The Id is wrapping around if the Id reaches `u16::MAX`.
    fn new_tid(&mut self) -> u16 {
        // wrap around or increment
        if self.tid  < u16::MAX {
            self.tid += 1u16;
        } else {
            self.tid = 0u16;
        }
        self.tid
    }
}

impl Drop for Ctx {
    fn drop(&mut self) {
        self.stream.shutdown(Shutdown::Both).unwrap();
    }
}

#[derive(RustcEncodable, RustcDecodable)]
#[repr(packed)]
struct Header {
    tid: u16,
    pid: u16,
    len: u16,
    uid: u8
}

impl Header {
    fn new(ctx: &mut Ctx, len: u16) -> Header {
        Header {
            tid: ctx.new_tid(),
            pid: MODBUS_PROTOCOL_TCP,
            len: len - MODBUS_HEADER_SIZE as u16,
            uid: ctx.uid
        }
    }
}

pub fn read_coils(ctx: &mut Ctx, addr: u16, count: u16) -> ModbusResult<Vec<BitValue>>
{
    let bytes = try!(read(ctx, Function::ReadCoils(addr, count)));
    let res = unpack_bits(&bytes, count);
    Ok(res)
}

pub fn read_discrete_inputs(ctx: &mut Ctx, addr: u16, count: u16) -> ModbusResult<Vec<BitValue>>
{
    let bytes = try!(read(ctx, Function::ReadDiscreteInputs(addr, count)));
    let res = unpack_bits(&bytes, count);
    Ok(res)
}

pub fn read_holding_registers(ctx: &mut Ctx, addr: u16, count: u16) -> ModbusResult<Vec<u16>>
{
    let bytes = try!(read(ctx, Function::ReadHoldingRegisters(addr, count)));
    let res: Vec<u16> = bytes.iter().map(|&v| v as u16).collect();
    Ok(res)
}

pub fn read_input_registers(ctx: &mut Ctx, addr: u16, count: u16) -> ModbusResult<Vec<u16>>
{
    let bytes = try!(read(ctx, Function::ReadInputRegisters(addr, count)));
    let res: Vec<u16> = bytes.iter().map(|&v| v as u16).collect();
    Ok(res)
}

fn read(ctx: &mut Ctx, fun: Function) -> ModbusResult<Vec<u8>>
{
    let packed_size = |v: u16| {v/8 + if v%8 > 0 {1} else {0}};
    let (addr, count, expected_bytes) = match fun {
            Function::ReadCoils(a, c)            => (a, c, packed_size(c) as usize),
            Function::ReadDiscreteInputs(a, c)   => (a, c, packed_size(c) as usize),
            Function::ReadHoldingRegisters(a, c) => (a, c, 2*c as usize),
            Function::ReadInputRegisters(a, c)   => (a, c, 2*c as usize),
            _ => panic!("Unexpected modbus function")
    };

    if count < 1 || count as usize > MODBUS_MAX_READ_COUNT {
        return Err(ModbusError::InvalidData);
    }

    let header = Header::new(ctx, MODBUS_HEADER_SIZE as u16 + 6u16);
    let mut buff = try!(encode(&header, SizeLimit::Infinite));
    try!(buff.write_u8(fun.code()));
    try!(buff.write_u16::<BigEndian>(addr));
    try!(buff.write_u16::<BigEndian>(count));

    match ctx.stream.write_all(&buff[..]) {
        Ok(_s) => {
                let mut reply = vec![0; MODBUS_HEADER_SIZE + expected_bytes + 2];
                match ctx.stream.read(&mut reply) {
                    Ok(_s) => {
                        let resp_hd = try!(decode(&reply[..MODBUS_HEADER_SIZE]));
                        try!(validate_response_header(&header, &resp_hd));
                        try!(validate_response_code(&buff, &reply[..]));
                        get_reply_data(&reply, expected_bytes)
                    }
                    Err(e) => Err(ModbusError::Io(e))
                }
        }
        Err(e) => Err(ModbusError::Io(e))
    }
}

pub fn write_single_coil(ctx: &mut Ctx, addr: u16, value: BitValue) -> ModbusResult<()>
{
    write_single(ctx, Function::WriteSingleCoil(addr, value.code()))
}

pub fn write_single_register(ctx: &mut Ctx, addr: u16, value: u16) -> ModbusResult<()>
{
    write_single(ctx, Function::WriteSingleRegister(addr, value))
}

fn write_single(ctx: &mut Ctx, fun: Function) -> ModbusResult<()>
{
    let (addr, value) = match fun {
        Function::WriteSingleCoil(a, v)     => (a, v),
        Function::WriteSingleRegister(a, v) => (a, v),
        _ => panic!("Unexpected modbus function")
    };

    let mut buff = vec![0; MODBUS_HEADER_SIZE];  // Header gets filled in later
    try!(buff.write_u8(fun.code()));
    try!(buff.write_u16::<BigEndian>(addr));
    try!(buff.write_u16::<BigEndian>(value));
    write(ctx, &mut buff)
}

fn write(ctx: &mut Ctx, buff: &mut Vec<u8>) -> ModbusResult<()> {
    if buff.len() < 1 || buff.len() > MODBUS_MAX_WRITE_COUNT {
        return Err(ModbusError::InvalidData);
    }
    let header = Header::new(ctx, buff.len() as u16 + 1u16);
    let head_buff = try!(encode(&header, SizeLimit::Infinite));
    {
        let mut start = Cursor::new(buff.borrow_mut());
        try!(start.write(&head_buff[..]));
    }
    match ctx.stream.write_all(&buff[..]) {
        Ok(_s) => {
                let reply = &mut [0; 12];
                match ctx.stream.read(reply) {
                    Ok(_s) => {
                        let resp_hd = try!(decode(&reply[..MODBUS_HEADER_SIZE]));
                        try!(validate_response_header(&header, &resp_hd));
                        validate_response_code(&buff, reply)
                    }
                    Err(e) => Err(ModbusError::Io(e))
                }
        }
        Err(e) => Err(ModbusError::Io(e))
    }
}

fn validate_response_header(req: &Header, resp: &Header) -> ModbusResult<()> {
    if req.tid != resp.tid || resp.pid != MODBUS_PROTOCOL_TCP {
        Err(ModbusError::InvalidResponse)
    }
    else {
        Ok(())
    }
}

fn validate_response_code(req: &[u8], resp: &[u8]) -> ModbusResult<()> {
    if req[7] + 0x80  == resp[7] {
        let code = ModbusExceptionCode::from_u8(resp[8]).unwrap();
        Err(ModbusError::ModbusException(code))
    }
    else if req[7] != resp[7] {
        Err(ModbusError::InvalidResponse)
    }
    else {
        Ok(())
    }
}

fn get_reply_data(reply: &Vec<u8>, expected_bytes: usize) -> ModbusResult<Vec<u8>> {
    if reply[8] as usize != expected_bytes ||
       reply.len() != MODBUS_HEADER_SIZE + expected_bytes + 2
    {
        Err(ModbusError::InvalidData)
    }
    else {

        let mut d = Vec::new();
        d.extend(reply[MODBUS_HEADER_SIZE+2..].iter());
        Ok(d)
    }
}

fn unpack_bits(bytes: &[u8], count: u16) -> Vec<BitValue> {
    let mut res = Vec::with_capacity(count as usize);
    for i in 0..count {
        if (bytes[(i/8u16) as usize] >> i%8) & 0x1 > 0 {
            res.push(BitValue::On);
        }
        else {
            res.push(BitValue::Off);
        }
    }
    res
}

#[cfg(test)]
fn start_dummy_server(port: &str) -> ChildKiller {
    use std::process::{Command, Stdio};
    use std::thread::sleep;
    use std::time::Duration;
    let ck = ChildKiller(Command::new("./test/diagslave")
                        .arg("-m").arg("tcp")
                        .arg("-p").arg(port)
                        .stdout(Stdio::null())
                        .spawn()
                        .unwrap_or_else(|e| { panic!("failed to execute process: {}", e) }));
    sleep(Duration::from_millis(500));
    ck
}

#[cfg(test)]
use std::process::Child;
#[cfg(test)]
struct ChildKiller(Child);

#[cfg(test)]
impl Drop for ChildKiller {
    fn drop(&mut self) {
        self.0.kill().unwrap();
    }
}

#[test]
fn test_packet_tid_creation() {
    let _s = start_dummy_server("2222");
    let mut ctx = Ctx::new_with_port("127.0.0.1", 2222).unwrap();
    let mut hd = Header::new(&mut ctx, 10);
    assert!(hd.tid == 1u16);
    hd = Header::new(&mut ctx, 10);
    assert!(hd.tid == 2u16);
    ctx.tid = u16::MAX;
    hd = Header::new(&mut ctx, 10);
    assert!(hd.tid == 0);
}

#[test]
fn test_write_single_coil() {
    let _s = start_dummy_server("2223");
    let mut ctx = Ctx::new_with_port("127.0.0.1", 2223).unwrap();
    for i in 0..10 {
        assert!(write_single_coil(&mut ctx, i, BitValue::On).is_ok());
    }
}

#[test]
fn test_write_single_register() {
    let _s = start_dummy_server("2224");
    let mut ctx = Ctx::new_with_port("127.0.0.1", 2224).unwrap();
    for i in 0..10 {
        assert!(write_single_register(&mut ctx, i, i).is_ok());
    }
}

#[test]
fn test_read_coils() {
    let _s = start_dummy_server("2225");
    let mut ctx = Ctx::new_with_port("127.0.0.1", 2225).unwrap();

    assert!(write_single_coil(&mut ctx, 1, BitValue::On).is_ok());
    assert!(write_single_coil(&mut ctx, 3, BitValue::On).is_ok());
    assert!(read_coils(&mut ctx, 0, 5).unwrap() ==
            vec![BitValue::Off, BitValue::On, BitValue::Off, BitValue::On, BitValue::Off]);
    assert!(read_coils(&mut ctx, 1, 5).unwrap() ==
            vec![BitValue::On, BitValue::Off, BitValue::On, BitValue::Off, BitValue::Off]);

}
<|endoftext|>


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

use dom::bindings::callback::ReportExceptions;
use dom::bindings::codegen::InheritTypes::{EventTargetCast, NodeCast, NodeDerived};
use dom::bindings::js::JS;
use dom::eventtarget::{Capturing, Bubbling, EventTarget};
use dom::event::{Event, PhaseAtTarget, PhaseNone, PhaseBubbling, PhaseCapturing};
use dom::node::{Node, NodeHelpers};

// See http://dom.spec.whatwg.org/#concept-event-dispatch for the full dispatch algorithm
pub fn dispatch_event(target: &JS<EventTarget>,
                      pseudo_target: Option<JS<EventTarget>>,
                      event: &mut JS<Event>) -> bool {
    assert!(!event.get().dispatching);

    {
        let event = event.get_mut();
        event.target = pseudo_target.or_else(|| {
            Some(target.clone())
        });
        event.dispatching = true;
    }

    let type_ = event.get().type_.clone();
    let mut chain = ~[];

    //TODO: no chain if not participating in a tree
    if target.get().is_node() {
        let target_node: JS<Node> = NodeCast::to(target).unwrap();
        for ancestor in target_node.ancestors() {
            let ancestor_target: JS<EventTarget> = EventTargetCast::from(&ancestor);
            chain.push(ancestor_target);
        }
    }

    event.get_mut().phase = PhaseCapturing;

    //FIXME: The "callback this value" should be currentTarget

    /* capturing */
    for cur_target in chain.rev_iter() {
        let stopped = match cur_target.get().get_listeners_for(type_, Capturing) {
            Some(listeners) => {
                event.get_mut().current_target = Some(cur_target.clone());
                for listener in listeners.iter() {
                    //FIXME: this should have proper error handling, or explicitly
                    //       drop the exception on the floor
                    assert!(listener.HandleEvent__(event, ReportExceptions).is_ok());

                    if event.get().stop_immediate {
                        break;
                    }
                }

                event.get().stop_propagation
            }
            None => false
        };

        if stopped {
            break;
        }
    }

    /* at target */
    if !event.get().stop_propagation {
        {
            let event = event.get_mut();
            event.phase = PhaseAtTarget;
            event.current_target = Some(target.clone());
        }

        let opt_listeners = target.get().get_listeners(type_);
        for listeners in opt_listeners.iter() {
            for listener in listeners.iter() {
                //FIXME: this should have proper error handling, or explicitly drop the
                //       exception on the floor.
                assert!(listener.HandleEvent__(event, ReportExceptions).is_ok());
                if event.get().stop_immediate {
                    break;
                }
            }
        }
    }

    /* bubbling */
    if event.get().bubbles && !event.get().stop_propagation {
        event.get_mut().phase = PhaseBubbling;

        for cur_target in chain.iter() {
            let stopped = match cur_target.get().get_listeners_for(type_, Bubbling) {
                Some(listeners) => {
                    event.get_mut().current_target = Some(cur_target.clone());
                    for listener in listeners.iter() {
                        //FIXME: this should have proper error handling or explicitly
                        //       drop exceptions on the floor.
                        assert!(listener.HandleEvent__(event, ReportExceptions).is_ok());

                        if event.get().stop_immediate {
                            break;
                        }
                    }

                    event.get().stop_propagation
                }
                None => false
            };
            if stopped {
                break;
            }
        }
    }

    let event = event.get_mut();
    event.dispatching = false;
    event.phase = PhaseNone;
    event.current_target = None;

    !event.DefaultPrevented()
}
<commit_msg>Make chain in eventdispatcher.rs use Vec.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use dom::bindings::callback::ReportExceptions;
use dom::bindings::codegen::InheritTypes::{EventTargetCast, NodeCast, NodeDerived};
use dom::bindings::js::JS;
use dom::eventtarget::{Capturing, Bubbling, EventTarget};
use dom::event::{Event, PhaseAtTarget, PhaseNone, PhaseBubbling, PhaseCapturing};
use dom::node::{Node, NodeHelpers};

// See http://dom.spec.whatwg.org/#concept-event-dispatch for the full dispatch algorithm
pub fn dispatch_event(target: &JS<EventTarget>,
                      pseudo_target: Option<JS<EventTarget>>,
                      event: &mut JS<Event>) -> bool {
    assert!(!event.get().dispatching);

    {
        let event = event.get_mut();
        event.target = pseudo_target.or_else(|| {
            Some(target.clone())
        });
        event.dispatching = true;
    }

    let type_ = event.get().type_.clone();

    //TODO: no chain if not participating in a tree
    let chain: Vec<JS<EventTarget>> = if target.get().is_node() {
        let target_node: JS<Node> = NodeCast::to(target).unwrap();
        target_node.ancestors().map(|ancestor| {
            let ancestor_target: JS<EventTarget> = EventTargetCast::from(&ancestor);
            ancestor_target
        }).collect()
    } else {
        vec!()
    };

    event.get_mut().phase = PhaseCapturing;

    //FIXME: The "callback this value" should be currentTarget

    /* capturing */
    for cur_target in chain.as_slice().rev_iter() {
        let stopped = match cur_target.get().get_listeners_for(type_, Capturing) {
            Some(listeners) => {
                event.get_mut().current_target = Some(cur_target.clone());
                for listener in listeners.iter() {
                    //FIXME: this should have proper error handling, or explicitly
                    //       drop the exception on the floor
                    assert!(listener.HandleEvent__(event, ReportExceptions).is_ok());

                    if event.get().stop_immediate {
                        break;
                    }
                }

                event.get().stop_propagation
            }
            None => false
        };

        if stopped {
            break;
        }
    }

    /* at target */
    if !event.get().stop_propagation {
        {
            let event = event.get_mut();
            event.phase = PhaseAtTarget;
            event.current_target = Some(target.clone());
        }

        let opt_listeners = target.get().get_listeners(type_);
        for listeners in opt_listeners.iter() {
            for listener in listeners.iter() {
                //FIXME: this should have proper error handling, or explicitly drop the
                //       exception on the floor.
                assert!(listener.HandleEvent__(event, ReportExceptions).is_ok());
                if event.get().stop_immediate {
                    break;
                }
            }
        }
    }

    /* bubbling */
    if event.get().bubbles && !event.get().stop_propagation {
        event.get_mut().phase = PhaseBubbling;

        for cur_target in chain.iter() {
            let stopped = match cur_target.get().get_listeners_for(type_, Bubbling) {
                Some(listeners) => {
                    event.get_mut().current_target = Some(cur_target.clone());
                    for listener in listeners.iter() {
                        //FIXME: this should have proper error handling or explicitly
                        //       drop exceptions on the floor.
                        assert!(listener.HandleEvent__(event, ReportExceptions).is_ok());

                        if event.get().stop_immediate {
                            break;
                        }
                    }

                    event.get().stop_propagation
                }
                None => false
            };
            if stopped {
                break;
            }
        }
    }

    let event = event.get_mut();
    event.dispatching = false;
    event.phase = PhaseNone;
    event.current_target = None;

    !event.DefaultPrevented()
}
<|endoftext|>


<commit_before>use std;
use sdl2;
use cgmath::*;
use sdl2::surface;
use utils::get_full_path;
use dll_import::IMG_Load;

pub struct Surface {
    pub size_x: usize,
    pub size_y: usize,
    pub data: Vec<Vector3<f32>>,
}

pub struct Texture {
    pub levels: Vec<Surface>,
}

impl Surface {
    fn new(size_x: usize, size_y: usize) -> Surface {
        Surface {
            size_x: size_x,
            size_y: size_y,
            data: Vec::<Vector3<f32>>::with_capacity(size_x * size_y),
        }
    }
}

impl Texture {
    fn load_surface<'a>(filename: &str) -> Result<surface::Surface<'a>, String> {
        let standart =
            match surface::Surface::new(1, 1, sdl2::pixels::PixelFormatEnum::ARGB8888) {
                Ok(v) => v,
                Err(e) => return Err(format!("can't create standart surface for texture {}, error = {}",
                                             filename, e))
            };

        let fullpath = try!(get_full_path(filename));
        let surface_load =
            unsafe {
                let raw = IMG_Load(std::ffi::CString::new(fullpath).unwrap().as_ptr());
                if (raw as *mut ()).is_null() {
                    return Err(format!("can't load texture {}, error = {}", filename, sdl2::get_error()));
                } else {
                    surface::Surface::from_ll(raw)
                }
            };

        match surface_load.as_ref().convert(&standart.as_ref().pixel_format()) {
            Ok(v) => Ok(v),
            Err(e) => return Err(format!("can't convert surface for texture {}, error = {}",
                                         filename, e))
        }
    }

    #[inline]
    fn add_color(clr: &mut Vector3<f32>, src: &Surface, x: i32, y: i32, k: u32) -> u32 {
        if x >= 0 && y >= 0 && x < (src.size_x as i32) && y < (src.size_y as i32) {
            let ind = (y as usize) * src.size_x + x as usize;
            clr.add_self_v(&src.data[ind].mul_s(k as f32));
            k
        } else {
            0
        }
    }

    fn gen_mipmap(&mut self) {
        let mut surface_ind = 0;
        let mut size_x = self.levels[surface_ind].size_x as i32;
        let mut size_y = self.levels[surface_ind].size_y as i32;

        while std::cmp::min(size_x, size_y) != 1 {
            let next_size_x = size_x >> 1;
            let next_size_y = size_y >> 1;
            let mut s = Surface::new(next_size_x as usize, next_size_y as usize);
            {
                let src = &(self.levels[surface_ind]);
                let mut dst = &mut s.data;

                // koeff table
                // 1 2 1
		        // 2 4 2
		        // 1 2 1
                for y in 0..next_size_y {
                    for x in 0..next_size_x {
                        let mut clr = Vector3::zero();
                        let mut cnt: u32 = 0;
                        {
                            let mut f = |add_x: i32, add_y: i32, k: u32| {
                                Texture::add_color(&mut clr, src, x * 2 + add_x, y * 2 + add_y, k)
                            };

                            cnt += f(-1, -1, 1);
                            cnt += f( 0, -1, 2);
                            cnt += f( 1, -1, 1);
                            cnt += f(-1,  0, 2);
                            cnt += f( 0,  0, 4);
                            cnt += f( 1,  0, 2);
                            cnt += f(-1,  1, 1);
                            cnt += f( 0,  1, 2);
                            cnt += f( 1,  1, 1);
                        }
                        dst.push(clr.div_s(cnt as f32));
                    }
                }
            }

            self.levels.push(s);
            size_x = next_size_x;
            size_y = next_size_y;
            surface_ind += 1;
        }
    }

    pub fn new(filename: &str) -> Result<Texture, String> {
        let surface = try!(Texture::load_surface(filename));

        let size_x = surface.as_ref().width() as usize;
        let size_y = surface.as_ref().height() as usize;
        let data_u8: &[u8] = match surface.as_ref().without_lock() {
            Some(v) => v,
            None => return Err(format!("can't lock surface for texture {}", filename))
        };

        let mut lvl0 = Surface::new(size_x, size_y);
        for y in 0..size_y {
            for x in 0..size_x {
                lvl0.data.push(Vector3::new(
                    data_u8[(y * size_x + size_x - x - 1) * 4 + 2] as f32,
                    data_u8[(y * size_x + size_x - x - 1) * 4 + 1] as f32,
                    data_u8[(y * size_x + size_x - x - 1) * 4 + 0] as f32));
            }
        }

        let mut tex = Texture {levels: vec![lvl0], };
        tex.gen_mipmap();

        Ok(tex)
    }
}
<commit_msg>new texture interface<commit_after>use std;
use sdl2;
use cgmath::*;
use sdl2::surface;
use utils::get_full_path;
use dll_import::IMG_Load;

pub struct Surface {
    pub size_x: usize,
    pub size_y: usize,
    pub data: Vec<Vector3<f32>>,
}

pub struct Texture {
    pub levels: Vec<Surface>,
}

impl Surface {
    fn new(size_x: usize, size_y: usize) -> Surface {
        Surface {
            size_x: size_x,
            size_y: size_y,
            data: Vec::<Vector3<f32>>::with_capacity(size_x * size_y),
        }
    }
}

impl Texture {
    pub fn from_dir(dir: &std::path::PathBuf, filename: &str) -> Result<Texture, String> {
        let mut path = dir.clone();
        path.push(filename);

        let fullpath = match path.as_path().to_str() {
            Some(path) => path.to_string(),
            None => return Err(format!("error get full path for filename {}", filename))
        };

        Ok(try!(Texture::new(&fullpath, filename)))
    }

    pub fn from_def(filename: &str) -> Result<Texture, String> {
        let fullpath = try!(get_full_path(filename));

        Ok(try!(Texture::new(&fullpath, filename)))
    }

    pub fn new(fullpath: &str, filename: &str) -> Result<Texture, String> {
        let surface = try!(Texture::load_surface(fullpath, filename));

        let size_x = surface.as_ref().width() as usize;
        let size_y = surface.as_ref().height() as usize;
        let data_u8: &[u8] = match surface.as_ref().without_lock() {
            Some(v) => v,
            None => return Err(format!("can't lock surface for texture {}", filename))
        };

        let mut lvl0 = Surface::new(size_x, size_y);
        for y in 0..size_y {
            for x in 0..size_x {
                lvl0.data.push(Vector3::new(
                    data_u8[(y * size_x + size_x - x - 1) * 4 + 2] as f32,
                    data_u8[(y * size_x + size_x - x - 1) * 4 + 1] as f32,
                    data_u8[(y * size_x + size_x - x - 1) * 4 + 0] as f32));
            }
        }

        this = Texture {
            levels: vec![lvl0],
        };
        this.gen_mipmap();

        Ok(this)
    }

    fn load_surface<'a>(fullpath: &str, filename: &str) -> Result<surface::Surface<'a>, String> {
        let standart =
            match surface::Surface::new(1, 1, sdl2::pixels::PixelFormatEnum::ARGB8888) {
                Ok(v) => v,
                Err(e) => return Err(format!("can't create standart surface for texture {}, error = {}",
                                             filename, e))
            };

        let surface_load =
            unsafe {
                let raw = IMG_Load(std::ffi::CString::new(fullpath).unwrap().as_ptr());
                if (raw as *mut ()).is_null() {
                    return Err(format!("can't load texture {}, error = {}", filename, sdl2::get_error()));
                } else {
                    surface::Surface::from_ll(raw)
                }
            };

        match surface_load.as_ref().convert(&standart.as_ref().pixel_format()) {
            Ok(v) => Ok(v),
            Err(e) => return Err(format!("can't convert surface for texture {}, error = {}",
                                         filename, e))
        }
    }

    #[inline]
    fn add_color(clr: &mut Vector3<f32>, src: &Surface, x: i32, y: i32, k: u32) -> u32 {
        if x >= 0 && y >= 0 && x < (src.size_x as i32) && y < (src.size_y as i32) {
            let ind = (y as usize) * src.size_x + x as usize;
            clr.add_self_v(&src.data[ind].mul_s(k as f32));
            k
        } else {
            0
        }
    }

    fn gen_mipmap(&mut self) {
        let mut surface_ind = 0;
        let mut size_x = self.levels[surface_ind].size_x as i32;
        let mut size_y = self.levels[surface_ind].size_y as i32;

        while std::cmp::min(size_x, size_y) != 1 {
            let next_size_x = size_x >> 1;
            let next_size_y = size_y >> 1;
            let mut s = Surface::new(next_size_x as usize, next_size_y as usize);
            {
                let src = &(self.levels[surface_ind]);
                let mut dst = &mut s.data;

                // koeff table
                // 1 2 1
		        // 2 4 2
		        // 1 2 1
                for y in 0..next_size_y {
                    for x in 0..next_size_x {
                        let mut clr = Vector3::zero();
                        let mut cnt: u32 = 0;
                        {
                            let mut f = |add_x: i32, add_y: i32, k: u32| {
                                Texture::add_color(&mut clr, src, x * 2 + add_x, y * 2 + add_y, k)
                            };

                            cnt += f(-1, -1, 1);
                            cnt += f( 0, -1, 2);
                            cnt += f( 1, -1, 1);
                            cnt += f(-1,  0, 2);
                            cnt += f( 0,  0, 4);
                            cnt += f( 1,  0, 2);
                            cnt += f(-1,  1, 1);
                            cnt += f( 0,  1, 2);
                            cnt += f( 1,  1, 1);
                        }
                        dst.push(clr.div_s(cnt as f32));
                    }
                }
            }

            self.levels.push(s);
            size_x = next_size_x;
            size_y = next_size_y;
            surface_ind += 1;
        }
    }
}
<|endoftext|>


<commit_before>//! Create textures and build texture atlas.

use gfx::CommandBuffer;
use gfx::Device;
use image;
use image::{ GenericImage, ImageBuf, MutableRefImage, Pixel, Rgba, SubImage };
use std::collections::HashMap;
use std::collections::hash_map::{ Occupied, Vacant };
use std::mem;

pub use gfx_texture::Texture;

/// Loads RGBA image from path.
fn load_rgba8(path: &Path) -> Result<ImageBuf<Rgba<u8>>, String> {
    Ok(match image::open(path) {
        Ok(image::ImageRgba8(img)) => img,
        Ok(image::ImageRgb8(img)) => {
            let (w, h) = img.dimensions();
            ImageBuf::from_fn(w, h, |x, y| img.get_pixel(x, y).to_rgba())
        }
        Ok(img) => {
            return Err(format!("Unsupported color type {} in '{}'",
                img.color(), path.display()));
        }
        Err(e)  => {
            return Err(format!("Could not load '{}': {}", path.display(), e));
        }
    })
}

/// A 256x256 image that stores colors.
pub struct ColorMap {
    image: ImageBuf<Rgba<u8>>
}

impl ColorMap {
    /// Creates a new `ColorMap` from path.
    pub fn from_path(path: &Path) -> Result<ColorMap, String> {
        let img = try!(load_rgba8(path));

        match img.dimensions() {
            (256, 256) => Ok(ColorMap {image: img}),
            (w, h) => Err(format!("ColorMap expected 256x256, found {}x{} in '{}'",
                                  w, h, path.display()))
        }
    }

    /// Gets RGB color from the color map.
    pub fn get(&self, x: f32, y: f32) -> [u8, ..3] {
        // Clamp to [0.0, 1.0].
        let x = x.max(0.0).min(1.0);
        let y = y.max(0.0).min(1.0);

        // Scale y from [0.0, 1.0] to [0.0, x], forming a triangle.
        let y = x * y;

        // Origin is in the bottom-right corner.
        let x = ((1.0 - x) * 255.0) as u8;
        let y = ((1.0 - y) * 255.0) as u8;

        let (r, g, b, _) = self.image.get_pixel(x as u32, y as u32).channels();
        [r, g, b]
    }
}

/// Builds an atlas of textures.
pub struct AtlasBuilder {
    image: ImageBuf<Rgba<u8>>,
    // Base path for loading tiles.
    path: Path,
    // Size of an individual tile.
    unit_width: u32,
    unit_height: u32,
    // Size of the entirely occupied square, in tiles.
    completed_tiles_size: u32,
    // Position in the current strip.
    position: u32,
    // Position cache for loaded tiles (in pixels).
    tile_positions: HashMap<String, (u32, u32)>,
    // Lowest-alpha cache for rectangles in the atlas.
    min_alpha_cache: HashMap<(u32, u32, u32, u32), u8>
}

impl AtlasBuilder {
    /// Creates a new `AtlasBuilder`.
    pub fn new(path: Path, unit_width: u32, unit_height: u32) -> AtlasBuilder {
        AtlasBuilder {
            image: ImageBuf::new(unit_width * 4, unit_height * 4),
            path: path,
            unit_width: unit_width,
            unit_height: unit_height,
            completed_tiles_size: 0,
            position: 0,
            tile_positions: HashMap::new(),
            min_alpha_cache: HashMap::new()
        }
    }

    /// Loads a file into the texture atlas.
    /// Checks if the file is loaded and returns position within the atlas.
    /// The name should be specified without file extension.
    /// PNG is the only supported format.
    pub fn load(&mut self, name: &str) -> (u32, u32) {
        match self.tile_positions.find_equiv(name) {
            Some(pos) => return *pos,
            None => {}
        }

        let mut path = self.path.join(name);
        path.set_extension("png");
        let img = load_rgba8(&path).unwrap();

        let (iw, ih) = img.dimensions();
        assert!(iw == self.unit_width);
        assert!((ih % self.unit_height) == 0);
        if ih > self.unit_height {
            println!("ignoring {} extra frames in '{}'", (ih / self.unit_height) - 1, name);
        }

        let (uw, uh) = (self.unit_width, self.unit_height);
        let (w, h) = self.image.dimensions();
        let size = self.completed_tiles_size;

        // Expand the image buffer if necessary.
        if self.position == 0 && (uw * size >= w || uh * size >= h) {
            let old = mem::replace(&mut self.image, ImageBuf::new(w * 2, h * 2));
            let mut dest = SubImage::new(&mut self.image, 0, 0, w, h);
            for ((_, _, a), (_, _, b)) in dest.pixels_mut().zip(old.pixels()) {
                *a = b;
            }
        }

        let (x, y) = if self.position < size {
            (self.position, size)
        } else {
            (size, self.position - size)
        };

        self.position += 1;
        if self.position >= size * 2 + 1 {
            self.position = 0;
            self.completed_tiles_size += 1;
        }

        let mut dest = SubImage::new(&mut self.image, x * uw, y * uh, uw, uh);
        for ((_, _, a), (_, _, b)) in dest.pixels_mut().zip(img.pixels()) {
            *a = b;
        }

        *match self.tile_positions.entry(name.to_string()) {
            Occupied(entry) => entry.into_mut(),
            Vacant(entry) => entry.set((x * uw, y * uh))
        }
    }

    /// Finds the minimum alpha value in a given sub texture of the image.
    pub fn min_alpha(&mut self, rect: [u32, ..4]) -> u8 {
        let [x, y, w, h] = rect;
        match self.min_alpha_cache.get(&(x, y, w, h)) {
            Some(alpha) => return *alpha,
            None => {}
        }

        let tile = SubImage::new(&mut self.image, x, y, w, h);
        let min_alpha = tile.pixels().map(|(_, _, p)| p.alpha())
            .min().unwrap_or(0);
        self.min_alpha_cache.insert((x, y, w, h), min_alpha);
        min_alpha
    }

    /// Returns the complete texture atlas as a texture.
    pub fn complete<D: Device<C>, C: CommandBuffer>(self, d: &mut D) -> Texture {
        Texture::from_rgba8(self.image, d)
    }
}
<commit_msg>Fixed build.<commit_after>//! Create textures and build texture atlas.

use gfx::CommandBuffer;
use gfx::Device;
use image;
use image::{ GenericImage, ImageBuf, MutableRefImage, Pixel, Rgba, SubImage };
use std::num::FloatMath;
use std::collections::HashMap;
use std::collections::hash_map::{ Occupied, Vacant };
use std::mem;

pub use gfx_texture::Texture;

/// Loads RGBA image from path.
fn load_rgba8(path: &Path) -> Result<ImageBuf<Rgba<u8>>, String> {
    Ok(match image::open(path) {
        Ok(image::ImageRgba8(img)) => img,
        Ok(image::ImageRgb8(img)) => {
            let (w, h) = img.dimensions();
            ImageBuf::from_fn(w, h, |x, y| img.get_pixel(x, y).to_rgba())
        }
        Ok(img) => {
            return Err(format!("Unsupported color type {} in '{}'",
                img.color(), path.display()));
        }
        Err(e)  => {
            return Err(format!("Could not load '{}': {}", path.display(), e));
        }
    })
}

/// A 256x256 image that stores colors.
pub struct ColorMap {
    image: ImageBuf<Rgba<u8>>
}

impl ColorMap {
    /// Creates a new `ColorMap` from path.
    pub fn from_path(path: &Path) -> Result<ColorMap, String> {
        let img = try!(load_rgba8(path));

        match img.dimensions() {
            (256, 256) => Ok(ColorMap {image: img}),
            (w, h) => Err(format!("ColorMap expected 256x256, found {}x{} in '{}'",
                                  w, h, path.display()))
        }
    }

    /// Gets RGB color from the color map.
    pub fn get(&self, x: f32, y: f32) -> [u8, ..3] {
        // Clamp to [0.0, 1.0].
        let x = x.max(0.0).min(1.0);
        let y = y.max(0.0).min(1.0);

        // Scale y from [0.0, 1.0] to [0.0, x], forming a triangle.
        let y = x * y;

        // Origin is in the bottom-right corner.
        let x = ((1.0 - x) * 255.0) as u8;
        let y = ((1.0 - y) * 255.0) as u8;

        let (r, g, b, _) = self.image.get_pixel(x as u32, y as u32).channels();
        [r, g, b]
    }
}

/// Builds an atlas of textures.
pub struct AtlasBuilder {
    image: ImageBuf<Rgba<u8>>,
    // Base path for loading tiles.
    path: Path,
    // Size of an individual tile.
    unit_width: u32,
    unit_height: u32,
    // Size of the entirely occupied square, in tiles.
    completed_tiles_size: u32,
    // Position in the current strip.
    position: u32,
    // Position cache for loaded tiles (in pixels).
    tile_positions: HashMap<String, (u32, u32)>,
    // Lowest-alpha cache for rectangles in the atlas.
    min_alpha_cache: HashMap<(u32, u32, u32, u32), u8>
}

impl AtlasBuilder {
    /// Creates a new `AtlasBuilder`.
    pub fn new(path: Path, unit_width: u32, unit_height: u32) -> AtlasBuilder {
        AtlasBuilder {
            image: ImageBuf::new(unit_width * 4, unit_height * 4),
            path: path,
            unit_width: unit_width,
            unit_height: unit_height,
            completed_tiles_size: 0,
            position: 0,
            tile_positions: HashMap::new(),
            min_alpha_cache: HashMap::new()
        }
    }

    /// Loads a file into the texture atlas.
    /// Checks if the file is loaded and returns position within the atlas.
    /// The name should be specified without file extension.
    /// PNG is the only supported format.
    pub fn load(&mut self, name: &str) -> (u32, u32) {
        match self.tile_positions.find_equiv(name) {
            Some(pos) => return *pos,
            None => {}
        }

        let mut path = self.path.join(name);
        path.set_extension("png");
        let img = load_rgba8(&path).unwrap();

        let (iw, ih) = img.dimensions();
        assert!(iw == self.unit_width);
        assert!((ih % self.unit_height) == 0);
        if ih > self.unit_height {
            println!("ignoring {} extra frames in '{}'", (ih / self.unit_height) - 1, name);
        }

        let (uw, uh) = (self.unit_width, self.unit_height);
        let (w, h) = self.image.dimensions();
        let size = self.completed_tiles_size;

        // Expand the image buffer if necessary.
        if self.position == 0 && (uw * size >= w || uh * size >= h) {
            let old = mem::replace(&mut self.image, ImageBuf::new(w * 2, h * 2));
            let mut dest = SubImage::new(&mut self.image, 0, 0, w, h);
            for ((_, _, a), (_, _, b)) in dest.pixels_mut().zip(old.pixels()) {
                *a = b;
            }
        }

        let (x, y) = if self.position < size {
            (self.position, size)
        } else {
            (size, self.position - size)
        };

        self.position += 1;
        if self.position >= size * 2 + 1 {
            self.position = 0;
            self.completed_tiles_size += 1;
        }

        let mut dest = SubImage::new(&mut self.image, x * uw, y * uh, uw, uh);
        for ((_, _, a), (_, _, b)) in dest.pixels_mut().zip(img.pixels()) {
            *a = b;
        }

        *match self.tile_positions.entry(name.to_string()) {
            Occupied(entry) => entry.into_mut(),
            Vacant(entry) => entry.set((x * uw, y * uh))
        }
    }

    /// Finds the minimum alpha value in a given sub texture of the image.
    pub fn min_alpha(&mut self, rect: [u32, ..4]) -> u8 {
        let [x, y, w, h] = rect;
        match self.min_alpha_cache.get(&(x, y, w, h)) {
            Some(alpha) => return *alpha,
            None => {}
        }

        let tile = SubImage::new(&mut self.image, x, y, w, h);
        let min_alpha = tile.pixels().map(|(_, _, p)| p.alpha())
            .min().unwrap_or(0);
        self.min_alpha_cache.insert((x, y, w, h), min_alpha);
        min_alpha
    }

    /// Returns the complete texture atlas as a texture.
    pub fn complete<D: Device<C>, C: CommandBuffer>(self, d: &mut D) -> Texture {
        Texture::from_rgba8(self.image, d)
    }
}
<|endoftext|>


<commit_before>#![feature(phase, macro_rules)]
#![deny(unused)]

extern crate serialize;
#[phase(plugin, link)] extern crate log;

extern crate cargo;

use std::collections::TreeSet;
use std::os;
use std::io;
use std::io::fs::{mod, PathExtensions};
use std::io::process::{Command,InheritFd,ExitStatus,ExitSignal};

use cargo::{execute_main_without_stdin, handle_error, shell};
use cargo::core::MultiShell;
use cargo::util::{CliError, CliResult};

#[deriving(Decodable)]
struct Flags {
    flag_list: bool,
    flag_verbose: bool,
    arg_command: String,
    arg_args: Vec<String>,
}

const USAGE: &'static str = "
Rust's package manager

Usage:
    cargo <command> [<args>...]
    cargo [options]

Options:
    -h, --help       Display this message
    -V, --version    Print version info and exit
    --list           List installed commands
    -v, --verbose    Use verbose output

Some common cargo commands are:
    build       Compile the current project
    clean       Remove the target directory
    doc         Build this project's and its dependencies' documentation
    new         Create a new cargo project
    run         Build and execute src/main.rs
    test        Run the tests
    bench       Run the benchmarks
    update      Update dependencies listed in Cargo.lock

See 'cargo help <command>' for more information on a specific command.
";

fn main() {
    execute_main_without_stdin(execute, true, USAGE)
}

macro_rules! each_subcommand( ($macro:ident) => ({
    $macro!(bench)
    $macro!(build)
    $macro!(clean)
    $macro!(config_for_key)
    $macro!(config_list)
    $macro!(doc)
    $macro!(fetch)
    $macro!(generate_lockfile)
    $macro!(git_checkout)
    $macro!(help)
    $macro!(locate_project)
    $macro!(login)
    $macro!(new)
    $macro!(owner)
    $macro!(package)
    $macro!(pkgid)
    $macro!(publish)
    $macro!(read_manifest)
    $macro!(run)
    $macro!(test)
    $macro!(update)
    $macro!(verify_project)
    $macro!(version)
    $macro!(yank)
}) )

/**
  The top-level `cargo` command handles configuration and project location
  because they are fundamental (and intertwined). Other commands can rely
  on this top-level information.
*/
fn execute(flags: Flags, shell: &mut MultiShell) -> CliResult<Option<()>> {
    debug!("executing; cmd=cargo; args={}", os::args());
    shell.set_verbose(flags.flag_verbose);

    if flags.flag_list {
        println!("Installed Commands:");
        for command in list_commands().into_iter() {
            println!("    {}", command);
        };
        return Ok(None)
    }

    let (mut args, command) = match flags.arg_command.as_slice() {
        "" | "help" if flags.arg_args.len() == 0 => {
            shell.set_verbose(true);
            let r = cargo::call_main_without_stdin(execute, shell, USAGE,
                                                   [os::args()[0].clone(), "-h".to_string()], false);
            cargo::process_executed(r, shell);
            return Ok(None)
        }
        "help" if flags.arg_args[0].as_slice() == "-h" ||
                  flags.arg_args[0].as_slice() == "--help" =>
            (flags.arg_args, "help"),
        "help" => (vec!["-h".to_string()], flags.arg_args[0].as_slice()),
        s => (flags.arg_args.clone(), s),
    };
    args.insert(0, command.to_string());
    args.insert(0, os::args()[0].clone());

    macro_rules! cmd( ($name:ident) => (
        if command.as_slice() == stringify!($name).replace("_", "-").as_slice() {
            mod $name;
            shell.set_verbose(true);
            let r = cargo::call_main_without_stdin($name::execute, shell,
                                                   $name::USAGE,
                                                   args.as_slice(),
                                                   false);
            cargo::process_executed(r, shell);
            return Ok(None)
        }
    ) )
    each_subcommand!(cmd)

    execute_subcommand(command.as_slice(), args.as_slice(), shell);
    Ok(None)
}

fn find_closest(cmd: &str) -> Option<String> {
    match list_commands().iter()
                            // doing it this way (instead of just .min_by(|c| c.lev_distance(cmd)))
                            // allows us to only make suggestions that have an edit distance of
                            // 3 or less
                            .map(|c| (c.lev_distance(cmd), c))
                            .filter(|&(d, _): &(uint, &String)| d < 4u)
                            .min_by(|&(d, _)| d) {
        Some((_, c)) => {
            Some(c.to_string())
        },
        None => None
    }
}

fn execute_subcommand(cmd: &str, args: &[String], shell: &mut MultiShell) {
    let command = match find_command(cmd) {
        Some(command) => command,
        None => {
            let msg = match find_closest(cmd) {
                Some(closest) => format!("No such subcommand\n\n\tDid you mean ``{}''?\n", closest),
                None => "No such subcommand".to_string()
            };
            return handle_error(CliError::new(msg, 127), shell)
        }
    };
    let status = Command::new(command)
                         .args(args)
                         .stdin(InheritFd(0))
                         .stdout(InheritFd(1))
                         .stderr(InheritFd(2))
                         .status();

    match status {
        Ok(ExitStatus(0)) => (),
        Ok(ExitStatus(i)) => {
            handle_error(CliError::new("", i as uint), shell)
        }
        Ok(ExitSignal(i)) => {
            let msg = format!("subcommand failed with signal: {}", i);
            handle_error(CliError::new(msg, i as uint), shell)
        }
        Err(io::IoError{kind, ..}) if kind == io::FileNotFound =>
            handle_error(CliError::new("No such subcommand", 127), shell),
        Err(err) => handle_error(
            CliError::new(
                format!("Subcommand failed to run: {}", err), 127),
            shell)
    }
}

/// List all runnable commands. find_command should always succeed
/// if given one of returned command.
fn list_commands() -> TreeSet<String> {
    let command_prefix = "cargo-";
    let mut commands = TreeSet::new();
    for dir in list_command_directory().iter() {
        let entries = match fs::readdir(dir) {
            Ok(entries) => entries,
            _ => continue
        };
        for entry in entries.iter() {
            let filename = match entry.filename_str() {
                Some(filename) => filename,
                _ => continue
            };
            if filename.starts_with(command_prefix) &&
                    filename.ends_with(os::consts::EXE_SUFFIX) &&
                    is_executable(entry) {
                let command = filename.slice(
                    command_prefix.len(),
                    filename.len() - os::consts::EXE_SUFFIX.len());
                commands.insert(String::from_str(command));
            }
        }
    }

    macro_rules! add_cmd( ($cmd:ident) => ({
        commands.insert(stringify!($cmd).replace("_", "-"));
    }) )
    each_subcommand!(add_cmd);
    commands
}

fn is_executable(path: &Path) -> bool {
    match fs::stat(path) {
        Ok(io::FileStat{ kind: io::TypeFile, perm, ..}) =>
            perm.contains(io::OTHER_EXECUTE),
        _ => false
    }
}

/// Get `Command` to run given command.
fn find_command(cmd: &str) -> Option<Path> {
    let command_exe = format!("cargo-{}{}", cmd, os::consts::EXE_SUFFIX);
    let dirs = list_command_directory();
    let mut command_paths = dirs.iter().map(|dir| dir.join(command_exe.as_slice()));
    command_paths.find(|path| path.exists())
}

/// List candidate locations where subcommands might be installed.
fn list_command_directory() -> Vec<Path> {
    let mut dirs = vec![];
    match os::self_exe_path() {
        Some(path) => {
            dirs.push(path.join("../lib/cargo"));
            dirs.push(path);
        },
        None => {}
    };
    match std::os::getenv("PATH") {
        Some(val) => {
            for dir in os::split_paths(val).iter() {
                dirs.push(Path::new(dir))
            }
        },
        None => {}
    };
    dirs
}
<commit_msg>Fix a tidy issue with src/bin/cargo.rs<commit_after>#![feature(phase, macro_rules)]
#![deny(unused)]

extern crate serialize;
#[phase(plugin, link)] extern crate log;

extern crate cargo;

use std::collections::TreeSet;
use std::os;
use std::io;
use std::io::fs::{mod, PathExtensions};
use std::io::process::{Command,InheritFd,ExitStatus,ExitSignal};

use cargo::{execute_main_without_stdin, handle_error, shell};
use cargo::core::MultiShell;
use cargo::util::{CliError, CliResult};

#[deriving(Decodable)]
struct Flags {
    flag_list: bool,
    flag_verbose: bool,
    arg_command: String,
    arg_args: Vec<String>,
}

const USAGE: &'static str = "
Rust's package manager

Usage:
    cargo <command> [<args>...]
    cargo [options]

Options:
    -h, --help       Display this message
    -V, --version    Print version info and exit
    --list           List installed commands
    -v, --verbose    Use verbose output

Some common cargo commands are:
    build       Compile the current project
    clean       Remove the target directory
    doc         Build this project's and its dependencies' documentation
    new         Create a new cargo project
    run         Build and execute src/main.rs
    test        Run the tests
    bench       Run the benchmarks
    update      Update dependencies listed in Cargo.lock

See 'cargo help <command>' for more information on a specific command.
";

fn main() {
    execute_main_without_stdin(execute, true, USAGE)
}

macro_rules! each_subcommand( ($macro:ident) => ({
    $macro!(bench)
    $macro!(build)
    $macro!(clean)
    $macro!(config_for_key)
    $macro!(config_list)
    $macro!(doc)
    $macro!(fetch)
    $macro!(generate_lockfile)
    $macro!(git_checkout)
    $macro!(help)
    $macro!(locate_project)
    $macro!(login)
    $macro!(new)
    $macro!(owner)
    $macro!(package)
    $macro!(pkgid)
    $macro!(publish)
    $macro!(read_manifest)
    $macro!(run)
    $macro!(test)
    $macro!(update)
    $macro!(verify_project)
    $macro!(version)
    $macro!(yank)
}) )

/**
  The top-level `cargo` command handles configuration and project location
  because they are fundamental (and intertwined). Other commands can rely
  on this top-level information.
*/
fn execute(flags: Flags, shell: &mut MultiShell) -> CliResult<Option<()>> {
    debug!("executing; cmd=cargo; args={}", os::args());
    shell.set_verbose(flags.flag_verbose);

    if flags.flag_list {
        println!("Installed Commands:");
        for command in list_commands().into_iter() {
            println!("    {}", command);
        };
        return Ok(None)
    }

    let (mut args, command) = match flags.arg_command.as_slice() {
        "" | "help" if flags.arg_args.len() == 0 => {
            shell.set_verbose(true);
            let args = &[os::args()[0].clone(), "-h".to_string()];
            let r = cargo::call_main_without_stdin(execute, shell, USAGE, args,
                                                   false);
            cargo::process_executed(r, shell);
            return Ok(None)
        }
        "help" if flags.arg_args[0].as_slice() == "-h" ||
                  flags.arg_args[0].as_slice() == "--help" =>
            (flags.arg_args, "help"),
        "help" => (vec!["-h".to_string()], flags.arg_args[0].as_slice()),
        s => (flags.arg_args.clone(), s),
    };
    args.insert(0, command.to_string());
    args.insert(0, os::args()[0].clone());

    macro_rules! cmd( ($name:ident) => (
        if command.as_slice() == stringify!($name).replace("_", "-").as_slice() {
            mod $name;
            shell.set_verbose(true);
            let r = cargo::call_main_without_stdin($name::execute, shell,
                                                   $name::USAGE,
                                                   args.as_slice(),
                                                   false);
            cargo::process_executed(r, shell);
            return Ok(None)
        }
    ) )
    each_subcommand!(cmd)

    execute_subcommand(command.as_slice(), args.as_slice(), shell);
    Ok(None)
}

fn find_closest(cmd: &str) -> Option<String> {
    match list_commands().iter()
                            // doing it this way (instead of just .min_by(|c| c.lev_distance(cmd)))
                            // allows us to only make suggestions that have an edit distance of
                            // 3 or less
                            .map(|c| (c.lev_distance(cmd), c))
                            .filter(|&(d, _): &(uint, &String)| d < 4u)
                            .min_by(|&(d, _)| d) {
        Some((_, c)) => {
            Some(c.to_string())
        },
        None => None
    }
}

fn execute_subcommand(cmd: &str, args: &[String], shell: &mut MultiShell) {
    let command = match find_command(cmd) {
        Some(command) => command,
        None => {
            let msg = match find_closest(cmd) {
                Some(closest) => format!("No such subcommand\n\n\tDid you mean ``{}''?\n", closest),
                None => "No such subcommand".to_string()
            };
            return handle_error(CliError::new(msg, 127), shell)
        }
    };
    let status = Command::new(command)
                         .args(args)
                         .stdin(InheritFd(0))
                         .stdout(InheritFd(1))
                         .stderr(InheritFd(2))
                         .status();

    match status {
        Ok(ExitStatus(0)) => (),
        Ok(ExitStatus(i)) => {
            handle_error(CliError::new("", i as uint), shell)
        }
        Ok(ExitSignal(i)) => {
            let msg = format!("subcommand failed with signal: {}", i);
            handle_error(CliError::new(msg, i as uint), shell)
        }
        Err(io::IoError{kind, ..}) if kind == io::FileNotFound =>
            handle_error(CliError::new("No such subcommand", 127), shell),
        Err(err) => handle_error(
            CliError::new(
                format!("Subcommand failed to run: {}", err), 127),
            shell)
    }
}

/// List all runnable commands. find_command should always succeed
/// if given one of returned command.
fn list_commands() -> TreeSet<String> {
    let command_prefix = "cargo-";
    let mut commands = TreeSet::new();
    for dir in list_command_directory().iter() {
        let entries = match fs::readdir(dir) {
            Ok(entries) => entries,
            _ => continue
        };
        for entry in entries.iter() {
            let filename = match entry.filename_str() {
                Some(filename) => filename,
                _ => continue
            };
            if filename.starts_with(command_prefix) &&
                    filename.ends_with(os::consts::EXE_SUFFIX) &&
                    is_executable(entry) {
                let command = filename.slice(
                    command_prefix.len(),
                    filename.len() - os::consts::EXE_SUFFIX.len());
                commands.insert(String::from_str(command));
            }
        }
    }

    macro_rules! add_cmd( ($cmd:ident) => ({
        commands.insert(stringify!($cmd).replace("_", "-"));
    }) )
    each_subcommand!(add_cmd);
    commands
}

fn is_executable(path: &Path) -> bool {
    match fs::stat(path) {
        Ok(io::FileStat{ kind: io::TypeFile, perm, ..}) =>
            perm.contains(io::OTHER_EXECUTE),
        _ => false
    }
}

/// Get `Command` to run given command.
fn find_command(cmd: &str) -> Option<Path> {
    let command_exe = format!("cargo-{}{}", cmd, os::consts::EXE_SUFFIX);
    let dirs = list_command_directory();
    let mut command_paths = dirs.iter().map(|dir| dir.join(command_exe.as_slice()));
    command_paths.find(|path| path.exists())
}

/// List candidate locations where subcommands might be installed.
fn list_command_directory() -> Vec<Path> {
    let mut dirs = vec![];
    match os::self_exe_path() {
        Some(path) => {
            dirs.push(path.join("../lib/cargo"));
            dirs.push(path);
        },
        None => {}
    };
    match std::os::getenv("PATH") {
        Some(val) => {
            for dir in os::split_paths(val).iter() {
                dirs.push(Path::new(dir))
            }
        },
        None => {}
    };
    dirs
}
<|endoftext|>


<commit_before>use std::convert::TryInto;

use anyhow::Result;
use log::info;

use mhost::app::cli::{list_predefined_nameservers, ExitStatus};
use mhost::app::global_config;
use mhost::app::GlobalConfig;
use mhost::app::logging::start_logging_for_level;
use mhost::app::modules;

async fn run() -> Result<ExitStatus> {
    let args = global_config::setup_clap().get_matches();

    if args.is_present("no-color") {
        mhost::output::styles::no_color_mode();
    }
    if args.is_present("ascii") {
        mhost::output::styles::ascii_mode();
    }

    start_logging_for_level(args.occurrences_of("v"));
    info!("Set up logging.");

    let global_config: GlobalConfig = if let Ok(config) = (&args).try_into() {
        config
    } else {
        return Ok(ExitStatus::ConfigParsingFailed)
    };
    info!("Parsed global args.");

    if global_config.list_predefined {
        list_predefined_nameservers();
        return Ok(ExitStatus::Ok);
    }

    let res = match args.subcommand_name() {
        Some("lookup") => modules::lookup::run(&args, &global_config).await,
        Some("download-server-lists") => modules::download_server_lists::run(&args, &global_config).await,
        Some("soa-check") => modules::soa_check::run(&args, &global_config).await,
        // Due to the invariant that a subcommand is required, this is unreachable.
        _ => Ok(ExitStatus::UnrecoverableError),
    };
    info!("Finished.");

    res
}

#[tokio::main]
async fn main() {
    let res = run().await;

    let exit_status = match res {
        Ok(exit_status) => exit_status,
        Err(err) => {
            eprintln!("Failed: {:#}", err);
            ExitStatus::Failed
        },
    };

    std::process::exit(exit_status as i32);
}<commit_msg>Minor tweak to error messaging in app<commit_after>use std::convert::TryInto;

use anyhow::Result;
use log::info;

use mhost::app::cli::{list_predefined_nameservers, ExitStatus};
use mhost::app::global_config;
use mhost::app::GlobalConfig;
use mhost::app::logging::start_logging_for_level;
use mhost::app::modules;
use mhost::output::styles::ERROR_PREFIX;

async fn run() -> Result<ExitStatus> {
    let args = global_config::setup_clap().get_matches();

    if args.is_present("no-color") {
        mhost::output::styles::no_color_mode();
    }
    if args.is_present("ascii") {
        mhost::output::styles::ascii_mode();
    }

    start_logging_for_level(args.occurrences_of("v"));
    info!("Set up logging.");

    let global_config: GlobalConfig = if let Ok(config) = (&args).try_into() {
        config
    } else {
        return Ok(ExitStatus::ConfigParsingFailed)
    };
    info!("Parsed global args.");

    if global_config.list_predefined {
        list_predefined_nameservers();
        return Ok(ExitStatus::Ok);
    }

    let res = match args.subcommand_name() {
        Some("lookup") => modules::lookup::run(&args, &global_config).await,
        Some("download-server-lists") => modules::download_server_lists::run(&args, &global_config).await,
        Some("soa-check") => modules::soa_check::run(&args, &global_config).await,
        // Due to the invariant that a subcommand is required, this is unreachable.
        _ => Ok(ExitStatus::UnrecoverableError),
    };
    info!("Finished.");

    res
}

#[tokio::main]
async fn main() {
    let res = run().await;

    let exit_status = match res {
        Ok(exit_status) => exit_status,
        Err(err) => {
            eprintln!("{} Failed: {:#}", &*ERROR_PREFIX, err);
            ExitStatus::Failed
        },
    };

    std::process::exit(exit_status as i32);
}<|endoftext|>


<commit_before>fn main() {

}
<commit_msg>implement touch<commit_after>use std::env;
use std::fs::File;
use std::process;

fn main() {
    if env::args().count() < 2 {
        println!("No arguments provided!");
        process::exit(1);
    }

    for arg in env::args().skip(1) {
        if let Err(_) = File::open(arg.to_string()) {
            File::create(arg).unwrap();
        }
    }
}
<|endoftext|>