<commit_before>use std::collections::HashSet;
use std::collections::hash_map::HashMap;
use std::sync::mpsc::{channel, Sender, Receiver};

use threadpool::ThreadPool;
use term::color::YELLOW;

use core::{Package, PackageId, Resolve, PackageSet};
use util::{Config, DependencyQueue, Fresh, Dirty, Freshness};
use util::{CargoResult, Dependency, profile};

use super::job::Job;

/// A management structure of the entire dependency graph to compile.
///
/// This structure is backed by the `DependencyQueue` type and manages the
/// actual compilation step of each package. Packages enqueue units of work and
/// then later on the entire graph is processed and compiled.
pub struct JobQueue<'a> {
    pool: ThreadPool,
    queue: DependencyQueue<(&'a PackageId, Stage),
                           (&'a Package, Vec<(Job, Freshness)>)>,
    tx: Sender<Message>,
    rx: Receiver<Message>,
    resolve: &'a Resolve,
    packages: &'a PackageSet,
    active: u32,
    pending: HashMap<(&'a PackageId, Stage), PendingBuild>,
    pkgids: HashSet<&'a PackageId>,
    printed: HashSet<&'a PackageId>,
}

/// A helper structure for metadata about the state of a building package.
struct PendingBuild {
    /// Number of jobs currently active
    amt: u32,
    /// Current freshness state of this package. Any dirty target within a
    /// package will cause the entire package to become dirty.
    fresh: Freshness,
}

/// Current stage of compilation for an individual package.
///
/// This is the second layer of keys on the dependency queue to track the state
/// of where a particular package is in the compilation pipeline. Each of these
/// stages has a network of dependencies among them, outlined by the
/// `Dependency` implementation found below.
///
/// Each build step for a package is registered with one of these stages, and
/// each stage has a vector of work to perform in parallel.
#[derive(Hash, PartialEq, Eq, Clone, PartialOrd, Ord, Debug, Copy)]
pub enum Stage {
    Start,
    BuildCustomBuild,
    RunCustomBuild,
    Libraries,
    Binaries,
    LibraryTests,
    BinaryTests,
    End,
}

type Message = (PackageId, Stage, Freshness, CargoResult<()>);

impl<'a> JobQueue<'a> {
    pub fn new(resolve: &'a Resolve, packages: &'a PackageSet, jobs: u32)
               -> JobQueue<'a> {
        let (tx, rx) = channel();
        JobQueue {
            pool: ThreadPool::new(jobs as usize),
            queue: DependencyQueue::new(),
            tx: tx,
            rx: rx,
            resolve: resolve,
            packages: packages,
            active: 0,
            pending: HashMap::new(),
            pkgids: HashSet::new(),
            printed: HashSet::new(),
        }
    }

    pub fn queue(&mut self, pkg: &'a Package, stage: Stage)
                 -> &mut Vec<(Job, Freshness)> {
        self.pkgids.insert(pkg.package_id());
        &mut self.queue.queue(&(self.resolve, self.packages), Fresh,
                              (pkg.package_id(), stage),
                              (pkg, Vec::new())).1
    }

    /// Execute all jobs necessary to build the dependency graph.
    ///
    /// This function will spawn off `config.jobs()` workers to build all of the
    /// necessary dependencies, in order. Freshness is propagated as far as
    /// possible along each dependency chain.
    pub fn execute(&mut self, config: &Config) -> CargoResult<()> {
        let _p = profile::start("executing the job graph");

        // Iteratively execute the dependency graph. Each turn of this loop will
        // schedule as much work as possible and then wait for one job to finish,
        // possibly scheduling more work afterwards.
        while self.queue.len() > 0 {
            loop {
                match self.queue.dequeue() {
                    Some((fresh, (_, stage), (pkg, jobs))) => {
                        info!("start: {} {:?}", pkg, stage);
                        try!(self.run(pkg, stage, fresh, jobs, config));
                    }
                    None => break,
                }
            }

            // Now that all possible work has been scheduled, wait for a piece
            // of work to finish. If any package fails to build then we stop
            // scheduling work as quickly as possibly.
            let (id, stage, fresh, result) = self.rx.recv().unwrap();
            info!("  end: {} {:?}", id, stage);
            let id = *self.pkgids.iter().find(|&k| *k == &id).unwrap();
            self.active -= 1;
            match result {
                Ok(()) => {
                    let state = self.pending.get_mut(&(id, stage)).unwrap();
                    state.amt -= 1;
                    state.fresh = state.fresh.combine(fresh);
                    if state.amt == 0 {
                        self.queue.finish(&(id, stage), state.fresh);
                    }
                }
                Err(e) => {
                    if self.active > 0 {
                        try!(config.shell().say(
                                    "Build failed, waiting for other \
                                     jobs to finish...", YELLOW));
                        for _ in self.rx.iter().take(self.active as usize) {}
                    }
                    return Err(e)
                }
            }
        }

        trace!("rustc jobs completed");

        Ok(())
    }

    /// Execute a stage of compilation for a package.
    ///
    /// The input freshness is from `dequeue()` and indicates the combined
    /// freshness of all upstream dependencies. This function will schedule all
    /// work in `jobs` to be executed.
    fn run(&mut self, pkg: &'a Package, stage: Stage, fresh: Freshness,
           jobs: Vec<(Job, Freshness)>, config: &Config) -> CargoResult<()> {
        let njobs = jobs.len();
        let amt = if njobs == 0 {1} else {njobs as u32};
        let id = pkg.package_id().clone();

        // While the jobs are all running, we maintain some metadata about how
        // many are running, the current state of freshness (of all the combined
        // jobs), and the stage to pass to finish() later on.
        self.active += amt;
        self.pending.insert((pkg.package_id(), stage), PendingBuild {
            amt: amt,
            fresh: fresh,
        });

        let mut total_fresh = fresh;
        let mut running = Vec::new();
        debug!("start {:?} at {:?} for {}", total_fresh, stage, pkg);
        for (job, job_freshness) in jobs.into_iter() {
            debug!("job: {:?} ({:?})", job_freshness, total_fresh);
            let fresh = job_freshness.combine(fresh);
            total_fresh = total_fresh.combine(fresh);
            let my_tx = self.tx.clone();
            let id = id.clone();
            let (desc_tx, desc_rx) = channel();
            self.pool.execute(move|| {
                my_tx.send((id, stage, fresh, job.run(fresh, desc_tx))).unwrap();
            });
            // only the first message of each job is processed
            match desc_rx.recv() {
                Ok(msg) => running.push(msg),
                Err(..) => {}
            }
        }

        // If no work was scheduled, make sure that a message is actually send
        // on this channel.
        if njobs == 0 {
            self.tx.send((id, stage, fresh, Ok(()))).unwrap();
        }

        // Print out some nice progress information
        try!(self.note_working_on(config, pkg.package_id(), stage, total_fresh,
                                  running.len()));
        for msg in running.iter() {
            try!(config.shell().verbose(|c| c.status("Running", msg)));
        }
        Ok(())
    }

    // This isn't super trivial becuase we don't want to print loads and
    // loads of information to the console, but we also want to produce a
    // faithful representation of what's happening. This is somewhat nuanced
    // as a package can start compiling *very* early on because of custom
    // build commands and such.
    //
    // In general, we try to print "Compiling" for the first nontrivial task
    // run for a package, regardless of when that is. We then don't print
    // out any more information for a package after we've printed it once.
    fn note_working_on(&mut self, config: &Config, pkg: &'a PackageId,
                       stage: Stage, fresh: Freshness, cmds_run: usize)
                       -> CargoResult<()> {
        if self.printed.contains(&pkg) { return Ok(()) }

        match fresh {
            // Any dirty stage which runs at least one command gets printed as
            // being a compiled package
            Dirty if cmds_run == 0 => {}
            Dirty => {
                self.printed.insert(pkg);
                try!(config.shell().status("Compiling", pkg));
            }
            Fresh if stage == Stage::End => {
                self.printed.insert(pkg);
                try!(config.shell().verbose(|c| c.status("Fresh", pkg)));
            }
            Fresh => {}
        }
        Ok(())
    }
}

impl<'a> Dependency for (&'a PackageId, Stage) {
    type Context = (&'a Resolve, &'a PackageSet);

    fn dependencies(&self, &(resolve, packages): &(&'a Resolve, &'a PackageSet))
                    -> Vec<(&'a PackageId, Stage)> {
        // This implementation of `Dependency` is the driver for the structure
        // of the dependency graph of packages to be built. The "key" here is
        // a pair of the package being built and the stage that it's at.
        //
        // Each stage here lists dependencies on the previous stages except for
        // the start state which depends on the ending state of all dependent
        // packages (as determined by the resolve context).
        let (id, stage) = *self;
        let pkg = packages.iter().find(|p| p.package_id() == id).unwrap();
        let deps = resolve.deps(id).into_iter().flat_map(|a| a)
                          .filter(|dep| *dep != id)
                          .map(|dep| {
                              (dep, pkg.dependencies().iter().find(|d| {
                                  d.name() == dep.name()
                              }).unwrap())
                          });
        match stage {
            Stage::Start => Vec::new(),

            // Building the build command itself starts off pretty easily,we
            // just need to depend on all of the library stages of our own build
            // dependencies (making them available to us).
            Stage::BuildCustomBuild => {
                let mut base = vec![(id, Stage::Start)];
                base.extend(deps.filter(|&(_, dep)| dep.is_build())
                                .map(|(id, _)| (id, Stage::Libraries)));
                base
            }

            // When running a custom build command, we need to be sure that our
            // own custom build command is actually built, and then we need to
            // wait for all our dependencies to finish their custom build
            // commands themselves (as they may provide input to us).
            Stage::RunCustomBuild => {
                let mut base = vec![(id, Stage::BuildCustomBuild)];
                base.extend(deps.filter(|&(_, dep)| dep.is_transitive())
                                .map(|(id, _)| (id, Stage::RunCustomBuild)));
                base
            }

            // Building a library depends on our own custom build command plus
            // all our transitive dependencies.
            Stage::Libraries => {
                let mut base = vec![(id, Stage::RunCustomBuild)];
                base.extend(deps.filter(|&(_, dep)| dep.is_transitive())
                                .map(|(id, _)| (id, Stage::Libraries)));
                base
            }

            // Binaries only depend on libraries being available. Note that they
            // do not depend on dev-dependencies.
            Stage::Binaries => vec![(id, Stage::Libraries)],

            // Tests depend on all dependencies (including dev-dependencies) in
            // addition to the library stage for this package. Note, however,
            // that library tests only need to depend the custom build command
            // being run, not the libraries themselves.
            Stage::BinaryTests | Stage::LibraryTests => {
                let mut base = if stage == Stage::BinaryTests {
                    vec![(id, Stage::Libraries)]
                } else {
                    vec![(id, Stage::RunCustomBuild)]
                };
                base.extend(deps.map(|(id, _)| (id, Stage::Libraries)));
                base
            }

            // A marker stage to indicate when a package has entirely finished
            // compiling, nothing is actually built as part of this stage.
            Stage::End => {
                vec![(id, Stage::Binaries), (id, Stage::BinaryTests),
                     (id, Stage::LibraryTests)]
            }
        }
    }
}
<commit_msg>Extend jakerr's fix<commit_after>use std::collections::HashSet;
use std::collections::hash_map::HashMap;
use std::sync::mpsc::{channel, Sender, Receiver};

use threadpool::ThreadPool;
use term::color::YELLOW;

use core::{Package, PackageId, Resolve, PackageSet};
use util::{Config, DependencyQueue, Fresh, Dirty, Freshness};
use util::{CargoResult, Dependency, profile};

use super::job::Job;

/// A management structure of the entire dependency graph to compile.
///
/// This structure is backed by the `DependencyQueue` type and manages the
/// actual compilation step of each package. Packages enqueue units of work and
/// then later on the entire graph is processed and compiled.
pub struct JobQueue<'a> {
    pool: ThreadPool,
    queue: DependencyQueue<(&'a PackageId, Stage),
                           (&'a Package, Vec<(Job, Freshness)>)>,
    tx: Sender<Message>,
    rx: Receiver<Message>,
    resolve: &'a Resolve,
    packages: &'a PackageSet,
    active: u32,
    pending: HashMap<(&'a PackageId, Stage), PendingBuild>,
    pkgids: HashSet<&'a PackageId>,
    printed: HashSet<&'a PackageId>,
}

/// A helper structure for metadata about the state of a building package.
struct PendingBuild {
    /// Number of jobs currently active
    amt: u32,
    /// Current freshness state of this package. Any dirty target within a
    /// package will cause the entire package to become dirty.
    fresh: Freshness,
}

/// Current stage of compilation for an individual package.
///
/// This is the second layer of keys on the dependency queue to track the state
/// of where a particular package is in the compilation pipeline. Each of these
/// stages has a network of dependencies among them, outlined by the
/// `Dependency` implementation found below.
///
/// Each build step for a package is registered with one of these stages, and
/// each stage has a vector of work to perform in parallel.
#[derive(Hash, PartialEq, Eq, Clone, PartialOrd, Ord, Debug, Copy)]
pub enum Stage {
    Start,
    BuildCustomBuild,
    RunCustomBuild,
    Libraries,
    Binaries,
    LibraryTests,
    BinaryTests,
    End,
}

type Message = (PackageId, Stage, Freshness, CargoResult<()>);

impl<'a> JobQueue<'a> {
    pub fn new(resolve: &'a Resolve, packages: &'a PackageSet, jobs: u32)
               -> JobQueue<'a> {
        let (tx, rx) = channel();
        JobQueue {
            pool: ThreadPool::new(jobs as usize),
            queue: DependencyQueue::new(),
            tx: tx,
            rx: rx,
            resolve: resolve,
            packages: packages,
            active: 0,
            pending: HashMap::new(),
            pkgids: HashSet::new(),
            printed: HashSet::new(),
        }
    }

    pub fn queue(&mut self, pkg: &'a Package, stage: Stage)
                 -> &mut Vec<(Job, Freshness)> {
        self.pkgids.insert(pkg.package_id());
        &mut self.queue.queue(&(self.resolve, self.packages), Fresh,
                              (pkg.package_id(), stage),
                              (pkg, Vec::new())).1
    }

    /// Execute all jobs necessary to build the dependency graph.
    ///
    /// This function will spawn off `config.jobs()` workers to build all of the
    /// necessary dependencies, in order. Freshness is propagated as far as
    /// possible along each dependency chain.
    pub fn execute(&mut self, config: &Config) -> CargoResult<()> {
        let _p = profile::start("executing the job graph");

        // Iteratively execute the dependency graph. Each turn of this loop will
        // schedule as much work as possible and then wait for one job to finish,
        // possibly scheduling more work afterwards.
        while self.queue.len() > 0 {
            loop {
                match self.queue.dequeue() {
                    Some((fresh, (_, stage), (pkg, jobs))) => {
                        info!("start: {} {:?}", pkg, stage);
                        try!(self.run(pkg, stage, fresh, jobs, config));
                    }
                    None => break,
                }
            }

            // Now that all possible work has been scheduled, wait for a piece
            // of work to finish. If any package fails to build then we stop
            // scheduling work as quickly as possibly.
            let (id, stage, fresh, result) = self.rx.recv().unwrap();
            info!("  end: {} {:?}", id, stage);
            let id = *self.pkgids.iter().find(|&k| *k == &id).unwrap();
            self.active -= 1;
            match result {
                Ok(()) => {
                    let state = self.pending.get_mut(&(id, stage)).unwrap();
                    state.amt -= 1;
                    state.fresh = state.fresh.combine(fresh);
                    if state.amt == 0 {
                        self.queue.finish(&(id, stage), state.fresh);
                    }
                }
                Err(e) => {
                    if self.active > 0 {
                        try!(config.shell().say(
                                    "Build failed, waiting for other \
                                     jobs to finish...", YELLOW));
                        for _ in self.rx.iter().take(self.active as usize) {}
                    }
                    return Err(e)
                }
            }
        }

        trace!("rustc jobs completed");

        Ok(())
    }

    /// Execute a stage of compilation for a package.
    ///
    /// The input freshness is from `dequeue()` and indicates the combined
    /// freshness of all upstream dependencies. This function will schedule all
    /// work in `jobs` to be executed.
    fn run(&mut self, pkg: &'a Package, stage: Stage, fresh: Freshness,
           jobs: Vec<(Job, Freshness)>, config: &Config) -> CargoResult<()> {
        let njobs = jobs.len();
        let amt = if njobs == 0 {1} else {njobs as u32};
        let id = pkg.package_id().clone();

        // While the jobs are all running, we maintain some metadata about how
        // many are running, the current state of freshness (of all the combined
        // jobs), and the stage to pass to finish() later on.
        self.active += amt;
        self.pending.insert((pkg.package_id(), stage), PendingBuild {
            amt: amt,
            fresh: fresh,
        });

        let mut total_fresh = fresh;
        let mut running = Vec::new();
        debug!("start {:?} at {:?} for {}", total_fresh, stage, pkg);
        for (job, job_freshness) in jobs.into_iter() {
            debug!("job: {:?} ({:?})", job_freshness, total_fresh);
            let fresh = job_freshness.combine(fresh);
            total_fresh = total_fresh.combine(fresh);
            let my_tx = self.tx.clone();
            let id = id.clone();
            let (desc_tx, desc_rx) = channel();
            self.pool.execute(move|| {
                my_tx.send((id, stage, fresh, job.run(fresh, desc_tx))).unwrap();
            });
            // only the first message of each job is processed
            match desc_rx.recv() {
                Ok(msg) => running.push(msg),
                Err(..) => {}
            }
        }

        // If no work was scheduled, make sure that a message is actually send
        // on this channel.
        if njobs == 0 {
            self.tx.send((id, stage, fresh, Ok(()))).unwrap();
        }

        // Print out some nice progress information
        try!(self.note_working_on(config, pkg.package_id(), stage, total_fresh,
                                  running.len()));
        for msg in running.iter() {
            try!(config.shell().verbose(|c| c.status("Running", msg)));
        }
        Ok(())
    }

    // This isn't super trivial becuase we don't want to print loads and
    // loads of information to the console, but we also want to produce a
    // faithful representation of what's happening. This is somewhat nuanced
    // as a package can start compiling *very* early on because of custom
    // build commands and such.
    //
    // In general, we try to print "Compiling" for the first nontrivial task
    // run for a package, regardless of when that is. We then don't print
    // out any more information for a package after we've printed it once.
    fn note_working_on(&mut self, config: &Config, pkg: &'a PackageId,
                       stage: Stage, fresh: Freshness, cmds_run: usize)
                       -> CargoResult<()> {
        if self.printed.contains(&pkg) { return Ok(()) }

        match fresh {
            // Any dirty stage which runs at least one command gets printed as
            // being a compiled package
            Dirty if cmds_run == 0 => {}
            Dirty => {
                self.printed.insert(pkg);
                try!(config.shell().status("Compiling", pkg));
            }
            Fresh if stage == Stage::End => {
                self.printed.insert(pkg);
                try!(config.shell().verbose(|c| c.status("Fresh", pkg)));
            }
            Fresh => {}
        }
        Ok(())
    }
}

impl<'a> Dependency for (&'a PackageId, Stage) {
    type Context = (&'a Resolve, &'a PackageSet);

    fn dependencies(&self, &(resolve, packages): &(&'a Resolve, &'a PackageSet))
                    -> Vec<(&'a PackageId, Stage)> {
        // This implementation of `Dependency` is the driver for the structure
        // of the dependency graph of packages to be built. The "key" here is
        // a pair of the package being built and the stage that it's at.
        //
        // Each stage here lists dependencies on the previous stages except for
        // the start state which depends on the ending state of all dependent
        // packages (as determined by the resolve context).
        let (id, stage) = *self;
        let pkg = packages.iter().find(|p| p.package_id() == id).unwrap();
        let deps = resolve.deps(id).into_iter().flat_map(|a| a)
                          .filter(|dep| *dep != id);
        match stage {
            Stage::Start => Vec::new(),

            // Building the build command itself starts off pretty easily,we
            // just need to depend on all of the library stages of our own build
            // dependencies (making them available to us).
            Stage::BuildCustomBuild => {
                let mut base = vec![(id, Stage::Start)];
                base.extend(deps
                    .filter(|id| pkg.dependencies().iter()
                        .find(|d| d.name() == id.name() && d.is_build())
                        .is_some())
                    .map(|id| (id, Stage::Libraries)));
                base
            }

            // When running a custom build command, we need to be sure that our
            // own custom build command is actually built, and then we need to
            // wait for all our dependencies to finish their custom build
            // commands themselves (as they may provide input to us).
            Stage::RunCustomBuild => {
                let mut base = vec![(id, Stage::BuildCustomBuild)];
                base.extend(deps
                    .filter(|id| pkg.dependencies().iter()
                        .find(|d| d.name() == id.name() && d.is_transitive())
                        .is_some())
                    .map(|id| (id, Stage::RunCustomBuild)));
                base
            }

            // Building a library depends on our own custom build command plus
            // all our transitive dependencies.
            Stage::Libraries => {
                let mut base = vec![(id, Stage::RunCustomBuild)];
                base.extend(deps
                    .filter(|id| pkg.dependencies().iter()
                        .find(|d| d.name() == id.name() && d.is_transitive())
                        .is_some())
                    .map(|id| (id, Stage::Libraries)));
                base
            }

            // Binaries only depend on libraries being available. Note that they
            // do not depend on dev-dependencies.
            Stage::Binaries => vec![(id, Stage::Libraries)],

            // Tests depend on all dependencies (including dev-dependencies) in
            // addition to the library stage for this package. Note, however,
            // that library tests only need to depend the custom build command
            // being run, not the libraries themselves.
            Stage::BinaryTests | Stage::LibraryTests => {
                let mut base = if stage == Stage::BinaryTests {
                    vec![(id, Stage::Libraries)]
                } else {
                    vec![(id, Stage::RunCustomBuild)]
                };
                base.extend(deps.map(|id| (id, Stage::Libraries)));
                base
            }

            // A marker stage to indicate when a package has entirely finished
            // compiling, nothing is actually built as part of this stage.
            Stage::End => {
                vec![(id, Stage::Binaries), (id, Stage::BinaryTests),
                     (id, Stage::LibraryTests)]
            }
        }
    }
}
<|endoftext|>


<commit_before>use std::net::ToSocketAddrs;
use std::convert::AsRef;
use std::net;
use std::io::Write;
use rustc_serialize::Encodable;
use time;
use record::Record;
use record::FluentError;

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Fluent
    where A: ToSocketAddrs
{
    addr: A,
    tag: String,
}

impl Fluent {
    /// Create Fluent type.
    ///
    /// ### Usage
    ///
    /// ```
    /// use fruently::fluent::Fluent;
    /// let fruently_with_str_tag = Fluent::new("0.0.0.0:24224", "test");
    /// let fruently_with_string_tag = Fluent::new("0.0.0.0:24224", "test".to_string());
    /// ```
    pub fn new<T>(addr: A, tag: T) -> Fluent
        where T: AsRef<str>
    {
        Fluent {
            addr: addr,
            tag: format!("{}", tag.as_ref()),
        }
    }

    /// Post record into Fluentd. Without time version.
    pub fn post<T>(self, record: T) -> Result<(), FluentError>
        where T: Encodable
    {
        let time = time::now();
        return self.post_with_time(record, time);
    }

    /// Post record into Fluentd. With time version.
    pub fn post_with_time<T>(self, record: T, time: time::Tm) -> Result<(), FluentError>
        where T: Encodable
    {
        let record = Record::new(self.tag, time, record);
        let message = try!(record.make_forwardable_json());
        let mut stream = try!(net::TcpStream::connect(self.addr));
        let result = stream.write(&message.into_bytes());
        drop(stream);

        match result {
            Ok(_) => Ok(()),
            Err(v) => Err(From::from(v)),
        }
    }
}

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

    #[test]
    fn create_fruently() {
        let fruently = Fluent::new("0.0.0.0:24224", "test");
        let expected = Fluent {
            addr: "0.0.0.0:24224",
            tag: "test".to_string(),
        };
        assert_eq!(expected, fruently);
    }

    #[test]
    #[cfg(feature="fluentd")]
    fn post_to_fluentd() {
        use std::collections::HashMap;

        let fruently = Fluent::new("0.0.0.0:24224", "test");
        let mut obj: HashMap<String, String> = HashMap::new();
        obj.insert("hey".to_string(), "Rust!".to_string());
        let result = fruently.post(obj);
        let is_ok = match result {
            Ok(()) => true,
            _ => false,
        };
        assert_eq!(true, is_ok);
    }
}
<commit_msg>fluent: Extract send_data function<commit_after>use std::net::ToSocketAddrs;
use std::convert::AsRef;
use std::net;
use std::io::Write;
use rustc_serialize::Encodable;
use time;
use record::Record;
use record::FluentError;

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Fluent
    where A: ToSocketAddrs
{
    addr: A,
    tag: String,
}

impl Fluent {
    /// Create Fluent type.
    ///
    /// ### Usage
    ///
    /// ```
    /// use fruently::fluent::Fluent;
    /// let fruently_with_str_tag = Fluent::new("0.0.0.0:24224", "test");
    /// let fruently_with_string_tag = Fluent::new("0.0.0.0:24224", "test".to_string());
    /// ```
    pub fn new<T>(addr: A, tag: T) -> Fluent
        where T: AsRef<str>
    {
        Fluent {
            addr: addr,
            tag: format!("{}", tag.as_ref()),
        }
    }

    /// Post record into Fluentd. Without time version.
    pub fn post<T>(self, record: T) -> Result<(), FluentError>
        where T: Encodable
    {
        let time = time::now();
        return self.post_with_time(record, time);
    }

    /// Post record into Fluentd. With time version.
    pub fn post_with_time<T>(self, record: T, time: time::Tm) -> Result<(), FluentError>
        where T: Encodable
    {
        let record = Record::new(self.tag.clone(), time, record);
        let message = try!(record.make_forwardable_json());
        return self.send_data(message)
    }

    fn send_data(self, message: String) -> Result<(), FluentError> {
        let mut stream = try!(net::TcpStream::connect(self.addr));
        let result = stream.write(&message.into_bytes());
        drop(stream);

        match result {
            Ok(_) => Ok(()),
            Err(v) => Err(From::from(v)),
        }
    }
}

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

    #[test]
    fn create_fruently() {
        let fruently = Fluent::new("0.0.0.0:24224", "test");
        let expected = Fluent {
            addr: "0.0.0.0:24224",
            tag: "test".to_string(),
        };
        assert_eq!(expected, fruently);
    }

    #[test]
    #[cfg(feature="fluentd")]
    fn post_to_fluentd() {
        use std::collections::HashMap;

        let fruently = Fluent::new("0.0.0.0:24224", "test");
        let mut obj: HashMap<String, String> = HashMap::new();
        obj.insert("hey".to_string(), "Rust!".to_string());
        let result = fruently.post(obj);
        let is_ok = match result {
            Ok(()) => true,
            _ => false,
        };
        assert_eq!(true, is_ok);
    }
}
<|endoftext|>


<commit_before>// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// https://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! The Bernoulli distribution.

use Rng;
use distributions::Distribution;

/// The Bernoulli distribution.
///
/// This is a special case of the Binomial distribution where `n = 1`.
///
/// # Example
///
/// ```rust
/// use rand::distributions::{Bernoulli, Distribution};
///
/// let d = Bernoulli::new(0.3);
/// let v = d.sample(&mut rand::thread_rng());
/// println!("{} is from a Bernoulli distribution", v);
/// ```
///
/// # Precision
///
/// This `Bernoulli` distribution uses 64 bits from the RNG (a `u64`), making
/// its bias less than 1 in 264. In practice the floating point
/// accuracy of `p` will usually be the limiting factor.
#[derive(Clone, Copy, Debug)]
pub struct Bernoulli {
    /// Probability of success, relative to the maximal integer.
    p_int: u64,
}

impl Bernoulli {
    /// Construct a new `Bernoulli` with the given probability of success `p`.
    ///
    /// # Panics
    ///
    /// If `p < 0` or `p > 1`.
    ///
    /// # Precision
    ///
    /// For `p = 1.0`, the resulting distribution will always generate true.
    /// For `p = 0.0`, the resulting distribution will always generate false.
    #[inline]
    pub fn new(p: f64) -> Bernoulli {
        assert!(p >= 0.0, "Bernoulli::new called with p < 0");
        assert!(p <= 1.0, "Bernoulli::new called with p > 1");
        let p_int = if p < 1.0 {
            (p * (::core::u64::MAX as f64)) as u64
        } else {
            // Avoid overflow: u64::MAX as f64 cannot be represented as u64.
            ::core::u64::MAX
        };
        Bernoulli { p_int }
    }
}

impl Distribution<bool> for Bernoulli {
    #[inline]
    fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> bool {
        // Make sure to always return true for p = 1.0.
        if self.p_int == ::core::u64::MAX {
            return true;
        }
        let r: u64 = rng.gen();
        r < self.p_int
    }
}

#[cfg(test)]
mod test {
    use {Rng, SmallRng, FromEntropy};
    use distributions::Distribution;
    use super::Bernoulli;

    #[test]
    fn test_trivial() {
        let mut r = SmallRng::from_entropy();
        let always_false = Bernoulli::new(0.0);
        let always_true = Bernoulli::new(1.0);
        for _ in 0..5 {
            assert_eq!(r.sample::<bool, _>(&always_false), false);
            assert_eq!(r.sample::<bool, _>(&always_true), true);
            assert_eq!(Distribution::<bool>::sample(&always_false, &mut r), false);
            assert_eq!(Distribution::<bool>::sample(&always_true, &mut r), true);
        }
    }

    #[test]
    fn test_average() {
        const P: f64 = 0.3;
        let d = Bernoulli::new(P);
        const N: u32 = 10_000_000;

        let mut sum: u32 = 0;
        let mut rng = SmallRng::from_entropy();
        for _ in 0..N {
            if d.sample(&mut rng) {
                sum += 1;
            }
        }
        let avg = (sum as f64) / (N as f64);

        assert!((avg - P).abs() < 1e-3);
    }
}
<commit_msg>Bernoulli: Correct remarks about precision<commit_after>// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// https://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! The Bernoulli distribution.

use Rng;
use distributions::Distribution;

/// The Bernoulli distribution.
///
/// This is a special case of the Binomial distribution where `n = 1`.
///
/// # Example
///
/// ```rust
/// use rand::distributions::{Bernoulli, Distribution};
///
/// let d = Bernoulli::new(0.3);
/// let v = d.sample(&mut rand::thread_rng());
/// println!("{} is from a Bernoulli distribution", v);
/// ```
///
/// # Precision
///
/// This `Bernoulli` distribution uses 64 bits from the RNG (a `u64`),
/// so only probabilities that are multiples of 2-64 can be
/// represented.
#[derive(Clone, Copy, Debug)]
pub struct Bernoulli {
    /// Probability of success, relative to the maximal integer.
    p_int: u64,
}

impl Bernoulli {
    /// Construct a new `Bernoulli` with the given probability of success `p`.
    ///
    /// # Panics
    ///
    /// If `p < 0` or `p > 1`.
    ///
    /// # Precision
    ///
    /// For `p = 1.0`, the resulting distribution will always generate true.
    /// For `p = 0.0`, the resulting distribution will always generate false.
    /// Due to the limitations of floating point numbers, not all internally
    /// supported probabilities (multiples of 2-64) can be specified
    /// using this constructor. If you need more precision, use
    /// `Bernoulli::from_int` instead.
    #[inline]
    pub fn new(p: f64) -> Bernoulli {
        assert!(p >= 0.0, "Bernoulli::new called with p < 0");
        assert!(p <= 1.0, "Bernoulli::new called with p > 1");
        let p_int = if p < 1.0 {
            (p * (::core::u64::MAX as f64)) as u64
        } else {
            // Avoid overflow: u64::MAX as f64 cannot be represented as u64.
            ::core::u64::MAX
        };
        Bernoulli { p_int }
    }

    /// Construct a new `Bernoulli` with the probability of success given as an
    /// integer `p_int = p * 2^64`.
    ///
    /// `p_int = 0` corresponds to `p = 0.0`.
    /// `p_int = u64::MAX` corresponds to `p = 1.0`.
    ///
    /// This is more precise than using `Bernoulli::new`.
    #[inline]
    pub fn from_int(p_int: u64) -> Bernoulli {
        Bernoulli { p_int }
    }
}

impl Distribution<bool> for Bernoulli {
    #[inline]
    fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> bool {
        // Make sure to always return true for p = 1.0.
        if self.p_int == ::core::u64::MAX {
            return true;
        }
        let r: u64 = rng.gen();
        r < self.p_int
    }
}

#[cfg(test)]
mod test {
    use {Rng, SmallRng, FromEntropy};
    use distributions::Distribution;
    use super::Bernoulli;

    #[test]
    fn test_trivial() {
        let mut r = SmallRng::from_entropy();
        let always_false = Bernoulli::new(0.0);
        let always_true = Bernoulli::new(1.0);
        for _ in 0..5 {
            assert_eq!(r.sample::<bool, _>(&always_false), false);
            assert_eq!(r.sample::<bool, _>(&always_true), true);
            assert_eq!(Distribution::<bool>::sample(&always_false, &mut r), false);
            assert_eq!(Distribution::<bool>::sample(&always_true, &mut r), true);
        }
    }

    #[test]
    fn test_average() {
        const P: f64 = 0.3;
        let d = Bernoulli::new(P);
        const N: u32 = 10_000_000;

        let mut sum: u32 = 0;
        let mut rng = SmallRng::from_entropy();
        for _ in 0..N {
            if d.sample(&mut rng) {
                sum += 1;
            }
        }
        let avg = (sum as f64) / (N as f64);

        assert!((avg - P).abs() < 1e-3);
    }
}
<|endoftext|>


<commit_before>use crate::{
    exporter::{ExportResult, Exporter},
    image::{DynamicImage, ImageBuffer, Rgba},
    texture::Texture,
};
use std::marker::PhantomData;

#[derive(Copy, Clone)]
pub struct ImageExporter<T>(PhantomData<T>);

impl<T: Texture<Pixel = Rgba<u8>>> ImageExporter<T> {
    pub fn export(texture: &T) -> ExportResult<DynamicImage> {
        <Self as Exporter<T>>::export(texture)
    }
}

impl<T: Texture<Pixel = Rgba<u8>>> Exporter<T> for ImageExporter<T> {
    type Output = DynamicImage;

    fn export(texture: &T) -> ExportResult<DynamicImage> {
        let width = texture.width();
        let height = texture.height();

        if width == 0 || height == 0 {
            return Err("Widht or height of this texture is zero".to_string());
        }

        let mut pixels = Vec::new();

        for row in 0..height {
            for col in 0..width {
                if let Some(pixel) = texture.get(col, row) {
                    pixels.push(pixel[0]);
                    pixels.push(pixel[1]);
                    pixels.push(pixel[2]);
                    pixels.push(pixel[3]);
                } else {
                    pixels.push(0);
                    pixels.push(0);
                    pixels.push(0);
                    pixels.push(0);
                }
            }
        }

        if let Some(image_buffer) =
            ImageBuffer::<Rgba<u8>, Vec<u8>>::from_raw(width, height, pixels)
        {
            Ok(DynamicImage::ImageRgba8(image_buffer))
        } else {
            Err("Can't export texture".to_string())
        }
    }
}
<commit_msg>exporter: Fix spelling in error message<commit_after>use crate::{
    exporter::{ExportResult, Exporter},
    image::{DynamicImage, ImageBuffer, Rgba},
    texture::Texture,
};
use std::marker::PhantomData;

#[derive(Copy, Clone)]
pub struct ImageExporter<T>(PhantomData<T>);

impl<T: Texture<Pixel = Rgba<u8>>> ImageExporter<T> {
    pub fn export(texture: &T) -> ExportResult<DynamicImage> {
        <Self as Exporter<T>>::export(texture)
    }
}

impl<T: Texture<Pixel = Rgba<u8>>> Exporter<T> for ImageExporter<T> {
    type Output = DynamicImage;

    fn export(texture: &T) -> ExportResult<DynamicImage> {
        let width = texture.width();
        let height = texture.height();

        if width == 0 || height == 0 {
            return Err("Width or height of this texture is zero".to_string());
        }

        let mut pixels = Vec::new();

        for row in 0..height {
            for col in 0..width {
                if let Some(pixel) = texture.get(col, row) {
                    pixels.push(pixel[0]);
                    pixels.push(pixel[1]);
                    pixels.push(pixel[2]);
                    pixels.push(pixel[3]);
                } else {
                    pixels.push(0);
                    pixels.push(0);
                    pixels.push(0);
                    pixels.push(0);
                }
            }
        }

        if let Some(image_buffer) =
            ImageBuffer::<Rgba<u8>, Vec<u8>>::from_raw(width, height, pixels)
        {
            Ok(DynamicImage::ImageRgba8(image_buffer))
        } else {
            Err("Can't export texture".to_string())
        }
    }
}
<|endoftext|>


<commit_before>#![crate_id="initdb"]
#![crate_type = "bin"]

extern crate sqlite3;

mod init {
    use sqlite3::{open, SqliteResult};

    pub fn main() -> SqliteResult<()> {
        let args = ::std::os::args();

        let db = try!(open(args[1]));

        try!(db.exec("CREATE TABLE Words(Word TEXT);"));
        try!(db.exec("CREATE TABLE Definitions(Definee TEXT, Idx INTEGER, Definer TEXT);"));

        let mut input = ::std::io::stdin();
        for line in input.lines() {
            try!(db.exec(format!("INSERT INTO Words VALUES(\"{}\");", line.unwrap().trim())));
        }
        println!("hello world");
        Ok(())
    }
}

pub fn main() {
    match init::main() {
        Ok(()) => {}
        Err(e) => { println!("error: {}", e) }
    }
}
<commit_msg>more initialization<commit_after>#![crate_id="initdb"]
#![crate_type = "bin"]

extern crate sqlite3;

mod init {
    use sqlite3::{open, SqliteResult};

    pub fn main() -> SqliteResult<()> {
        let args = ::std::os::args();

        let db = try!(open(args[1]));

        try!(db.exec("CREATE TABLE Words(Word TEXT);"));
        try!(db.exec("CREATE TABLE Definitions(Definee TEXT, Idx INTEGER, Definer TEXT);"));

        let mut input = ::std::io::stdin();
        for line in input.lines() {
            let word = line.unwrap().clone();
            let trimmed = word.trim();
            assert!(trimmed.is_alphanumeric(), "not alphanumeric: {}", trimmed);
            try!(db.exec(format!("INSERT INTO Words VALUES(\"{}\");", trimmed)));
        }
        Ok(())
    }
}

pub fn main() {
    match init::main() {
        Ok(()) => {}
        Err(e) => { println!("error: {}", e) }
    }
}
<|endoftext|>


<commit_before>#![allow(clippy::cognitive_complexity)]
#![allow(clippy::unneeded_field_pattern)]

//! IRC event handling

use futures::stream::StreamExt;
use libtiny_client::Client;
use libtiny_ui::{MsgTarget, TabStyle, UI};
use libtiny_wire as wire;
use std::error::Error;
use tokio::sync::mpsc;

pub(crate) async fn task(
    mut rcv_ev: mpsc::Receiver<libtiny_client::Event>,
    ui: Box<dyn UI>,
    client: Client,
) {
    while let Some(ev) = rcv_ev.next().await {
        if handle_conn_ev(&*ui, &client, ev) {
            return;
        }
        ui.draw();
    }
}

fn handle_conn_ev(ui: &dyn UI, client: &Client, ev: libtiny_client::Event) -> bool {
    use libtiny_client::Event::*;
    match ev {
        ResolvingHost => {
            ui.add_client_msg(
                "Resolving host...",
                &MsgTarget::AllServTabs {
                    serv: client.get_serv_name(),
                },
            );
        }
        Connecting(sock_addr) => {
            ui.add_client_msg(
                &format!("Connecting to {}", sock_addr),
                &MsgTarget::AllServTabs {
                    serv: client.get_serv_name(),
                },
            );
        }
        Connected => {
            ui.add_msg(
                "Connected.",
                time::now(),
                &MsgTarget::AllServTabs {
                    serv: client.get_serv_name(),
                },
            );
        }
        Disconnected => {
            let serv = client.get_serv_name();
            ui.add_err_msg(
                &format!(
                    "Disconnected. Will try to reconnect in {} seconds.",
                    libtiny_client::RECONNECT_SECS
                ),
                time::now(),
                &MsgTarget::AllServTabs { serv },
            );
            ui.clear_nicks(serv);
        }
        IoErr(err) => {
            ui.add_err_msg(
                &format!("Connection error: {}", err.description()),
                time::now(),
                &MsgTarget::AllServTabs {
                    serv: client.get_serv_name(),
                },
            );
        }
        ConnectionClosed => {
            ui.add_err_msg(
                "Connection closed on the remote end",
                time::now(),
                &MsgTarget::AllServTabs {
                    serv: client.get_serv_name(),
                },
            );
        }
        TlsErr(err) => {
            ui.add_err_msg(
                &format!("TLS error: {}", err.description()),
                time::now(),
                &MsgTarget::AllServTabs {
                    serv: client.get_serv_name(),
                },
            );
        }
        CantResolveAddr => {
            ui.add_err_msg(
                "Can't resolve address",
                time::now(),
                &MsgTarget::AllServTabs {
                    serv: client.get_serv_name(),
                },
            );
        }
        NickChange(new_nick) => {
            ui.set_nick(client.get_serv_name(), &new_nick);
        }
        Msg(msg) => {
            handle_irc_msg(ui, client, msg);
        }
        Closed => {
            return true;
        }
    }
    false
}

fn handle_irc_msg(ui: &dyn UI, client: &Client, msg: wire::Msg) {
    use wire::Cmd::*;
    use wire::Pfx::*;

    let wire::Msg { pfx, cmd } = msg;
    let ts = time::now();
    let serv = client.get_serv_name();
    match cmd {
        PRIVMSG {
            target,
            msg,
            is_notice,
            ctcp,
        } => {
            let pfx = match pfx {
                Some(pfx) => pfx,
                None => {
                    // TODO: log this?
                    return;
                }
            };

            // sender to be shown in the UI
            let origin = match pfx {
                Server(_) => serv,
                User { ref nick, .. } => nick,
            };

            if ctcp == Some(wire::CTCP::Version) {
                let msg_target = if ui.user_tab_exists(serv, origin) {
                    MsgTarget::User { serv, nick: origin }
                } else {
                    MsgTarget::Server { serv }
                };
                ui.add_client_msg(
                    &format!("Received version request from {}", origin),
                    &msg_target,
                );
                return;
            }

            let is_action = ctcp == Some(wire::CTCP::Action);

            match target {
                wire::MsgTarget::Chan(chan) => {
                    let ui_msg_target = MsgTarget::Chan { serv, chan: &chan };
                    // highlight the message if it mentions us
                    if msg.find(&client.get_nick()).is_some() {
                        ui.add_privmsg(origin, &msg, ts, &ui_msg_target, true, is_action);
                        ui.set_tab_style(TabStyle::Highlight, &ui_msg_target);
                        let mentions_target = MsgTarget::Server { serv: "mentions" };
                        ui.add_msg(
                            &format!("{} in {}:{}: {}", origin, serv, chan, msg),
                            ts,
                            &mentions_target,
                        );
                        ui.set_tab_style(TabStyle::Highlight, &mentions_target);
                    } else {
                        ui.add_privmsg(origin, &msg, ts, &ui_msg_target, false, is_action);
                        ui.set_tab_style(TabStyle::NewMsg, &ui_msg_target);
                    }
                }
                wire::MsgTarget::User(target) => {
                    let msg_target = {
                        match pfx {
                            Server(_) => MsgTarget::Server { serv },
                            User { ref nick, .. } => {
                                // show NOTICE messages in server tabs if we don't have a tab
                                // for the sender already (see #21)
                                if is_notice && !ui.user_tab_exists(serv, nick) {
                                    MsgTarget::Server { serv }
                                } else {
                                    MsgTarget::User { serv, nick }
                                }
                            }
                        }
                    };
                    ui.add_privmsg(origin, &msg, ts, &msg_target, false, is_action);
                    if target == client.get_nick() {
                        ui.set_tab_style(TabStyle::Highlight, &msg_target);
                    } else {
                        // not sure if this case can happen
                        ui.set_tab_style(TabStyle::NewMsg, &msg_target);
                    }
                }
            }
        }

        JOIN { chan } => {
            let nick = match pfx {
                Some(User { nick, .. }) => nick,
                _ => {
                    // TODO: log this?
                    return;
                }
            };

            if nick == client.get_nick() {
                ui.new_chan_tab(serv, &chan);
            } else {
                let nick = wire::drop_nick_prefix(&nick);
                let ts = Some(time::now());
                ui.add_nick(nick, ts, &MsgTarget::Chan { serv, chan: &chan });
                // Also update the private message tab if it exists
                // Nothing will be shown if the user already known to be online by the tab
                if ui.user_tab_exists(serv, nick) {
                    ui.add_nick(nick, ts, &MsgTarget::User { serv, nick });
                }
            }
        }

        PART { chan, .. } => {
            let nick = match pfx {
                Some(User { nick, .. }) => nick,
                _ => {
                    // TODO: log this?
                    return;
                }
            };
            if nick != client.get_nick() {
                ui.remove_nick(
                    &nick,
                    Some(time::now()),
                    &MsgTarget::Chan { serv, chan: &chan },
                );
            }
        }

        QUIT { chans, .. } => {
            let nick = match pfx {
                Some(User { ref nick, .. }) => nick,
                _ => {
                    // TODO: log this?
                    return;
                }
            };

            for chan in &chans {
                ui.remove_nick(nick, Some(time::now()), &MsgTarget::Chan { serv, chan });
            }
            if ui.user_tab_exists(serv, nick) {
                ui.remove_nick(nick, Some(time::now()), &MsgTarget::User { serv, nick });
            }
        }

        NICK { nick, chans } => {
            let old_nick = match pfx {
                Some(User { nick, .. }) => nick,
                _ => {
                    // TODO: log this?
                    return;
                }
            };

            for chan in &chans {
                ui.rename_nick(
                    &old_nick,
                    &nick,
                    time::now(),
                    &MsgTarget::Chan { serv, chan },
                );
            }
            if ui.user_tab_exists(serv, &old_nick) {
                ui.rename_nick(
                    &old_nick,
                    &nick,
                    time::now(),
                    &MsgTarget::User {
                        serv,
                        nick: &old_nick,
                    },
                );
            }
        }

        Reply { num: 433, .. } => {
            // ERR_NICKNAMEINUSE
            if client.is_nick_accepted() {
                // Nick change request from user failed. Just show an error message.
                ui.add_err_msg(
                    "Nickname is already in use",
                    time::now(),
                    &MsgTarget::AllServTabs { serv },
                );
            }
        }

        PING { .. } | PONG { .. } => {
            // Ignore
        }

        ERROR { msg } => {
            ui.add_err_msg(&msg, time::now(), &MsgTarget::AllServTabs { serv });
        }

        TOPIC { chan, topic } => {
            ui.set_topic(&topic, time::now(), serv, &chan);
        }

        CAP {
            client: _,
            subcommand,
            params,
        } => {
            match subcommand.as_ref() {
                "NAK" => {
                    if params.iter().any(|cap| cap.as_str() == "sasl") {
                        let msg_target = MsgTarget::Server { serv };
                        ui.add_err_msg(
                            "Server rejected using SASL authenication capability",
                            time::now(),
                            &msg_target,
                        );
                    }
                }
                "LS" => {
                    if !params.iter().any(|cap| cap.as_str() == "sasl") {
                        let msg_target = MsgTarget::Server { serv };
                        ui.add_err_msg(
                            "Server does not support SASL authenication",
                            time::now(),
                            &msg_target,
                        );
                    }
                }
                "ACK" => {}
                _cmd => {
                    // self.logger
                    //     .get_debug_logs()
                    //     .write_line(format_args!("CAP subcommand {} is not handled", cmd));
                }
            }
        }

        AUTHENTICATE { .. } => {
            // Ignore
        }

        Reply { num: n, params } => {
            if n <= 003 /* RPL_WELCOME, RPL_YOURHOST, RPL_CREATED */
                    || n == 251 /* RPL_LUSERCLIENT */
                    || n == 255 /* RPL_LUSERME */
                    || n == 372 /* RPL_MOTD */
                    || n == 375 /* RPL_MOTDSTART */
                    || n == 376
            /* RPL_ENDOFMOTD */
            {
                debug_assert_eq!(params.len(), 2);
                let msg = &params[1];
                ui.add_msg(msg, time::now(), &MsgTarget::Server { serv });
            } else if n == 4 // RPL_MYINFO
                    || n == 5 // RPL_BOUNCE
                    || (n >= 252 && n <= 254)
            /* RPL_LUSEROP, RPL_LUSERUNKNOWN, */
            /* RPL_LUSERCHANNELS */
            {
                let msg = params.into_iter().collect::<Vec<String>>().join(" ");
                ui.add_msg(&msg, time::now(), &MsgTarget::Server { serv });
            } else if n == 265 || n == 266 || n == 250 {
                let msg = &params[params.len() - 1];
                ui.add_msg(msg, time::now(), &MsgTarget::Server { serv });
            }
            // RPL_TOPIC
            else if n == 332 {
                // FIXME: RFC 2812 says this will have 2 arguments, but freenode
                // sends 3 arguments (extra one being our nick).
                assert!(params.len() == 3 || params.len() == 2);
                let chan = &params[params.len() - 2];
                let topic = &params[params.len() - 1];
                ui.set_topic(topic, time::now(), serv, chan);
            }
            // RPL_NAMREPLY: List of users in a channel
            else if n == 353 {
                let chan = &params[2];
                let chan_target = MsgTarget::Chan { serv, chan };

                for nick in params[3].split_whitespace() {
                    ui.add_nick(wire::drop_nick_prefix(nick), None, &chan_target);
                }
            }
            // RPL_ENDOFNAMES: End of NAMES list
            else if n == 366 {
            }
            // RPL_UNAWAY or RPL_NOWAWAY
            else if n == 305 || n == 306 {
                let msg = &params[1];
                ui.add_client_msg(msg, &MsgTarget::AllServTabs { serv });
            }
            // ERR_NOSUCHNICK
            else if n == 401 {
                let nick = &params[1];
                let msg = &params[2];
                ui.add_client_msg(msg, &MsgTarget::User { serv, nick });
            // RPL_AWAY
            } else if n == 301 {
                let nick = &params[1];
                let msg = &params[2];
                ui.add_client_msg(
                    &format!("{} is away: {}", nick, msg),
                    &MsgTarget::User { serv, nick },
                );
            } else {
                match pfx {
                    Some(Server(msg_serv)) => {
                        let msg_target = MsgTarget::Server { serv };
                        ui.add_privmsg(
                            &msg_serv,
                            &params.join(" "),
                            time::now(),
                            &msg_target,
                            false,
                            false,
                        );
                        ui.set_tab_style(TabStyle::NewMsg, &msg_target);
                    }
                    _pfx => {
                        // add everything else to debug file
                        // self.logger.get_debug_logs().write_line(format_args!(
                        //     "Ignoring numeric reply msg:\nPfx: {:?}, num: {:?}, args: {:?}",
                        //     pfx, n, params
                        // ));
                    }
                }
            }
        }

        Other { cmd: _, params } => match pfx {
            Some(Server(msg_serv)) => {
                let msg_target = MsgTarget::Server { serv };
                ui.add_privmsg(
                    &msg_serv,
                    &params.join(" "),
                    time::now(),
                    &msg_target,
                    false,
                    false,
                );
                ui.set_tab_style(TabStyle::NewMsg, &msg_target);
            }
            _pfx => {
                // self.logger.get_debug_logs().write_line(format_args!(
                //     "Ignoring msg:\nPfx: {:?}, msg: {} :{}",
                //     pfx,
                //     cmd,
                //     params.join(" "),
                // ));
            }
        },
    }
}
<commit_msg>Fix nightly warnings<commit_after>#![allow(clippy::cognitive_complexity)]
#![allow(clippy::unneeded_field_pattern)]

//! IRC event handling

use futures::stream::StreamExt;
use libtiny_client::Client;
use libtiny_ui::{MsgTarget, TabStyle, UI};
use libtiny_wire as wire;
use tokio::sync::mpsc;

pub(crate) async fn task(
    mut rcv_ev: mpsc::Receiver<libtiny_client::Event>,
    ui: Box<dyn UI>,
    client: Client,
) {
    while let Some(ev) = rcv_ev.next().await {
        if handle_conn_ev(&*ui, &client, ev) {
            return;
        }
        ui.draw();
    }
}

fn handle_conn_ev(ui: &dyn UI, client: &Client, ev: libtiny_client::Event) -> bool {
    use libtiny_client::Event::*;
    match ev {
        ResolvingHost => {
            ui.add_client_msg(
                "Resolving host...",
                &MsgTarget::AllServTabs {
                    serv: client.get_serv_name(),
                },
            );
        }
        Connecting(sock_addr) => {
            ui.add_client_msg(
                &format!("Connecting to {}", sock_addr),
                &MsgTarget::AllServTabs {
                    serv: client.get_serv_name(),
                },
            );
        }
        Connected => {
            ui.add_msg(
                "Connected.",
                time::now(),
                &MsgTarget::AllServTabs {
                    serv: client.get_serv_name(),
                },
            );
        }
        Disconnected => {
            let serv = client.get_serv_name();
            ui.add_err_msg(
                &format!(
                    "Disconnected. Will try to reconnect in {} seconds.",
                    libtiny_client::RECONNECT_SECS
                ),
                time::now(),
                &MsgTarget::AllServTabs { serv },
            );
            ui.clear_nicks(serv);
        }
        IoErr(err) => {
            ui.add_err_msg(
                &format!("Connection error: {}", err),
                time::now(),
                &MsgTarget::AllServTabs {
                    serv: client.get_serv_name(),
                },
            );
        }
        ConnectionClosed => {
            ui.add_err_msg(
                "Connection closed on the remote end",
                time::now(),
                &MsgTarget::AllServTabs {
                    serv: client.get_serv_name(),
                },
            );
        }
        TlsErr(err) => {
            ui.add_err_msg(
                &format!("TLS error: {}", err),
                time::now(),
                &MsgTarget::AllServTabs {
                    serv: client.get_serv_name(),
                },
            );
        }
        CantResolveAddr => {
            ui.add_err_msg(
                "Can't resolve address",
                time::now(),
                &MsgTarget::AllServTabs {
                    serv: client.get_serv_name(),
                },
            );
        }
        NickChange(new_nick) => {
            ui.set_nick(client.get_serv_name(), &new_nick);
        }
        Msg(msg) => {
            handle_irc_msg(ui, client, msg);
        }
        Closed => {
            return true;
        }
    }
    false
}

fn handle_irc_msg(ui: &dyn UI, client: &Client, msg: wire::Msg) {
    use wire::Cmd::*;
    use wire::Pfx::*;

    let wire::Msg { pfx, cmd } = msg;
    let ts = time::now();
    let serv = client.get_serv_name();
    match cmd {
        PRIVMSG {
            target,
            msg,
            is_notice,
            ctcp,
        } => {
            let pfx = match pfx {
                Some(pfx) => pfx,
                None => {
                    // TODO: log this?
                    return;
                }
            };

            // sender to be shown in the UI
            let origin = match pfx {
                Server(_) => serv,
                User { ref nick, .. } => nick,
            };

            if ctcp == Some(wire::CTCP::Version) {
                let msg_target = if ui.user_tab_exists(serv, origin) {
                    MsgTarget::User { serv, nick: origin }
                } else {
                    MsgTarget::Server { serv }
                };
                ui.add_client_msg(
                    &format!("Received version request from {}", origin),
                    &msg_target,
                );
                return;
            }

            let is_action = ctcp == Some(wire::CTCP::Action);

            match target {
                wire::MsgTarget::Chan(chan) => {
                    let ui_msg_target = MsgTarget::Chan { serv, chan: &chan };
                    // highlight the message if it mentions us
                    if msg.find(&client.get_nick()).is_some() {
                        ui.add_privmsg(origin, &msg, ts, &ui_msg_target, true, is_action);
                        ui.set_tab_style(TabStyle::Highlight, &ui_msg_target);
                        let mentions_target = MsgTarget::Server { serv: "mentions" };
                        ui.add_msg(
                            &format!("{} in {}:{}: {}", origin, serv, chan, msg),
                            ts,
                            &mentions_target,
                        );
                        ui.set_tab_style(TabStyle::Highlight, &mentions_target);
                    } else {
                        ui.add_privmsg(origin, &msg, ts, &ui_msg_target, false, is_action);
                        ui.set_tab_style(TabStyle::NewMsg, &ui_msg_target);
                    }
                }
                wire::MsgTarget::User(target) => {
                    let msg_target = {
                        match pfx {
                            Server(_) => MsgTarget::Server { serv },
                            User { ref nick, .. } => {
                                // show NOTICE messages in server tabs if we don't have a tab
                                // for the sender already (see #21)
                                if is_notice && !ui.user_tab_exists(serv, nick) {
                                    MsgTarget::Server { serv }
                                } else {
                                    MsgTarget::User { serv, nick }
                                }
                            }
                        }
                    };
                    ui.add_privmsg(origin, &msg, ts, &msg_target, false, is_action);
                    if target == client.get_nick() {
                        ui.set_tab_style(TabStyle::Highlight, &msg_target);
                    } else {
                        // not sure if this case can happen
                        ui.set_tab_style(TabStyle::NewMsg, &msg_target);
                    }
                }
            }
        }

        JOIN { chan } => {
            let nick = match pfx {
                Some(User { nick, .. }) => nick,
                _ => {
                    // TODO: log this?
                    return;
                }
            };

            if nick == client.get_nick() {
                ui.new_chan_tab(serv, &chan);
            } else {
                let nick = wire::drop_nick_prefix(&nick);
                let ts = Some(time::now());
                ui.add_nick(nick, ts, &MsgTarget::Chan { serv, chan: &chan });
                // Also update the private message tab if it exists
                // Nothing will be shown if the user already known to be online by the tab
                if ui.user_tab_exists(serv, nick) {
                    ui.add_nick(nick, ts, &MsgTarget::User { serv, nick });
                }
            }
        }

        PART { chan, .. } => {
            let nick = match pfx {
                Some(User { nick, .. }) => nick,
                _ => {
                    // TODO: log this?
                    return;
                }
            };
            if nick != client.get_nick() {
                ui.remove_nick(
                    &nick,
                    Some(time::now()),
                    &MsgTarget::Chan { serv, chan: &chan },
                );
            }
        }

        QUIT { chans, .. } => {
            let nick = match pfx {
                Some(User { ref nick, .. }) => nick,
                _ => {
                    // TODO: log this?
                    return;
                }
            };

            for chan in &chans {
                ui.remove_nick(nick, Some(time::now()), &MsgTarget::Chan { serv, chan });
            }
            if ui.user_tab_exists(serv, nick) {
                ui.remove_nick(nick, Some(time::now()), &MsgTarget::User { serv, nick });
            }
        }

        NICK { nick, chans } => {
            let old_nick = match pfx {
                Some(User { nick, .. }) => nick,
                _ => {
                    // TODO: log this?
                    return;
                }
            };

            for chan in &chans {
                ui.rename_nick(
                    &old_nick,
                    &nick,
                    time::now(),
                    &MsgTarget::Chan { serv, chan },
                );
            }
            if ui.user_tab_exists(serv, &old_nick) {
                ui.rename_nick(
                    &old_nick,
                    &nick,
                    time::now(),
                    &MsgTarget::User {
                        serv,
                        nick: &old_nick,
                    },
                );
            }
        }

        Reply { num: 433, .. } => {
            // ERR_NICKNAMEINUSE
            if client.is_nick_accepted() {
                // Nick change request from user failed. Just show an error message.
                ui.add_err_msg(
                    "Nickname is already in use",
                    time::now(),
                    &MsgTarget::AllServTabs { serv },
                );
            }
        }

        PING { .. } | PONG { .. } => {
            // Ignore
        }

        ERROR { msg } => {
            ui.add_err_msg(&msg, time::now(), &MsgTarget::AllServTabs { serv });
        }

        TOPIC { chan, topic } => {
            ui.set_topic(&topic, time::now(), serv, &chan);
        }

        CAP {
            client: _,
            subcommand,
            params,
        } => {
            match subcommand.as_ref() {
                "NAK" => {
                    if params.iter().any(|cap| cap.as_str() == "sasl") {
                        let msg_target = MsgTarget::Server { serv };
                        ui.add_err_msg(
                            "Server rejected using SASL authenication capability",
                            time::now(),
                            &msg_target,
                        );
                    }
                }
                "LS" => {
                    if !params.iter().any(|cap| cap.as_str() == "sasl") {
                        let msg_target = MsgTarget::Server { serv };
                        ui.add_err_msg(
                            "Server does not support SASL authenication",
                            time::now(),
                            &msg_target,
                        );
                    }
                }
                "ACK" => {}
                _cmd => {
                    // self.logger
                    //     .get_debug_logs()
                    //     .write_line(format_args!("CAP subcommand {} is not handled", cmd));
                }
            }
        }

        AUTHENTICATE { .. } => {
            // Ignore
        }

        Reply { num: n, params } => {
            if n <= 003 /* RPL_WELCOME, RPL_YOURHOST, RPL_CREATED */
                    || n == 251 /* RPL_LUSERCLIENT */
                    || n == 255 /* RPL_LUSERME */
                    || n == 372 /* RPL_MOTD */
                    || n == 375 /* RPL_MOTDSTART */
                    || n == 376
            /* RPL_ENDOFMOTD */
            {
                debug_assert_eq!(params.len(), 2);
                let msg = &params[1];
                ui.add_msg(msg, time::now(), &MsgTarget::Server { serv });
            } else if n == 4 // RPL_MYINFO
                    || n == 5 // RPL_BOUNCE
                    || (n >= 252 && n <= 254)
            /* RPL_LUSEROP, RPL_LUSERUNKNOWN, */
            /* RPL_LUSERCHANNELS */
            {
                let msg = params.into_iter().collect::<Vec<String>>().join(" ");
                ui.add_msg(&msg, time::now(), &MsgTarget::Server { serv });
            } else if n == 265 || n == 266 || n == 250 {
                let msg = &params[params.len() - 1];
                ui.add_msg(msg, time::now(), &MsgTarget::Server { serv });
            }
            // RPL_TOPIC
            else if n == 332 {
                // FIXME: RFC 2812 says this will have 2 arguments, but freenode
                // sends 3 arguments (extra one being our nick).
                assert!(params.len() == 3 || params.len() == 2);
                let chan = &params[params.len() - 2];
                let topic = &params[params.len() - 1];
                ui.set_topic(topic, time::now(), serv, chan);
            }
            // RPL_NAMREPLY: List of users in a channel
            else if n == 353 {
                let chan = &params[2];
                let chan_target = MsgTarget::Chan { serv, chan };

                for nick in params[3].split_whitespace() {
                    ui.add_nick(wire::drop_nick_prefix(nick), None, &chan_target);
                }
            }
            // RPL_ENDOFNAMES: End of NAMES list
            else if n == 366 {
            }
            // RPL_UNAWAY or RPL_NOWAWAY
            else if n == 305 || n == 306 {
                let msg = &params[1];
                ui.add_client_msg(msg, &MsgTarget::AllServTabs { serv });
            }
            // ERR_NOSUCHNICK
            else if n == 401 {
                let nick = &params[1];
                let msg = &params[2];
                ui.add_client_msg(msg, &MsgTarget::User { serv, nick });
            // RPL_AWAY
            } else if n == 301 {
                let nick = &params[1];
                let msg = &params[2];
                ui.add_client_msg(
                    &format!("{} is away: {}", nick, msg),
                    &MsgTarget::User { serv, nick },
                );
            } else {
                match pfx {
                    Some(Server(msg_serv)) => {
                        let msg_target = MsgTarget::Server { serv };
                        ui.add_privmsg(
                            &msg_serv,
                            &params.join(" "),
                            time::now(),
                            &msg_target,
                            false,
                            false,
                        );
                        ui.set_tab_style(TabStyle::NewMsg, &msg_target);
                    }
                    _pfx => {
                        // add everything else to debug file
                        // self.logger.get_debug_logs().write_line(format_args!(
                        //     "Ignoring numeric reply msg:\nPfx: {:?}, num: {:?}, args: {:?}",
                        //     pfx, n, params
                        // ));
                    }
                }
            }
        }

        Other { cmd: _, params } => match pfx {
            Some(Server(msg_serv)) => {
                let msg_target = MsgTarget::Server { serv };
                ui.add_privmsg(
                    &msg_serv,
                    &params.join(" "),
                    time::now(),
                    &msg_target,
                    false,
                    false,
                );
                ui.set_tab_style(TabStyle::NewMsg, &msg_target);
            }
            _pfx => {
                // self.logger.get_debug_logs().write_line(format_args!(
                //     "Ignoring msg:\nPfx: {:?}, msg: {} :{}",
                //     pfx,
                //     cmd,
                //     params.join(" "),
                // ));
            }
        },
    }
}
<|endoftext|>


<commit_before>use num::Integer;
use euclid::Point2D;
use hex::Dir6;

pub trait FovValue: Eq + Clone {
    /// Construct a new FovValue based on previous one and a new point in the fov.
    fn advance(&self, offset: Point2D<i32>) -> Option<Self>;

    /// Optional method for showing acute corners of fake isometric rooms in fov.
    fn is_fake_isometric_wall(&self, offset: Point2D<i32>) -> bool {
        let _ = offset;
        false
    }
}

/// Field of view iterator for a hexagonal map.
///
/// Takes a function that maps cells into user-specified values and indicates when traversal should
/// stop.
pub struct HexFov<T> {
    stack: Vec<Sector<T>>,
    /// Extra values generated by special cases.
    side_channel: Vec<(Point2D<i32>, T)>,
}

impl<T: FovValue> HexFov<T> {
    pub fn new(init: T) -> HexFov<T> {
        // We could run f for (0, 0) here, but the traditional way for the FOV to work is to only
        // consider your surroundings, not the origin site itself.
        HexFov {
            stack: vec![Sector {
                            begin: PolarPoint::new(0.0, 1),
                            pt: PolarPoint::new(0.0, 1),
                            end: PolarPoint::new(6.0, 1),
                            prev_value: init.clone(),
                            group_value: None,
                        }],
            // The FOV algorithm will not generate the origin point, so we use
            // the side channel to explicitly add it in the beginning.
            side_channel: vec![(Point2D::new(0, 0), init)],
        }
    }
}

impl<T: FovValue> Iterator for HexFov<T> {
    type Item = (Point2D<i32>, T);
    fn next(&mut self) -> Option<(Point2D<i32>, T)> {
        // Empty the side channel before proceeding to the algorithm proper.
        if let Some(ret) = self.side_channel.pop() {
            return Some(ret);
        }

        // Start processing the next sector in the stack.
        if let Some(mut current) = self.stack.pop() {
            // Generate the user value from the previous value in the stack item and the working
            // position in the current sector.
            let current_value = current.prev_value.advance(current.pt.to_v2());

            // Proceed along the sector if we're not at its end yet and the user value exists.
            if current.pt.is_below(current.end) && current_value.is_some() {
                let pos = current.pt.to_v2();
                let current_value = current_value.unwrap();

                match current.group_value {
                    None => {
                        // Beginning of group, value isn't set.
                        current.group_value = Some(current_value.clone());
                    }
                    Some(ref g) if g != &current_value => {
                        // Value changed, branch out.

                        // Add the rest of this sector with the new value.
                        self.stack.push(Sector {
                            begin: current.pt,
                            pt: current.pt,
                            end: current.end,
                            prev_value: current.prev_value,
                            group_value: Some(current_value.clone()),
                        });

                        // Branch further on the arc processed so far.
                        self.stack.push(Sector {
                            begin: current.begin.further(),
                            pt: current.begin.further(),
                            end: current.pt.further(),
                            prev_value: g.clone(),
                            group_value: None,
                        });
                        return self.next();
                    }
                    _ => {}
                }

                // Current value and group value are assumed to be the same from here on.
                assert!(current.group_value == Some(current_value.clone()));

                // Hack for making acute corner tiles of fake-isometric rooms visible.

                // We're moving along a vertical line on the hex circle, so there are side
                // points to check.
                if let Some(side_pos) = current.pt.side_point() {

                    let next = current.pt.next();
                    // If the next cell is within the current span and the current cell is
                    // wallform,
                    if next.is_below(current.end) &&
                       current.prev_value.is_fake_isometric_wall(current.pt.to_v2()) {
                        // and if the next cell is visible,
                        if let Some(next_value) = current.prev_value.advance(next.to_v2()) {
                            // and if the current and the next cell are in the same value group,
                            // both the next cell and the third corner point cell are
                            // wallforms, and the side point would not be otherwise
                            // visible:
                            if next_value == current.prev_value &&
                               next_value.is_fake_isometric_wall(next.to_v2()) &&
                               current.prev_value.advance(side_pos).is_none() &&
                               current.prev_value.is_fake_isometric_wall(side_pos) {
                                // Add the side point to the side channel.
                                self.side_channel.push((side_pos, current.prev_value.clone()));
                            }
                        }
                    }
                }

                // Proceed along the current sector.
                current.pt = current.pt.next();
                self.stack.push(current);
                return Some((pos, current_value));
            } else {
                // Hit the end of the sector, branch ahead.
                if let Some(value) = current.group_value {
                    self.stack.push(Sector {
                        begin: current.begin.further(),
                        pt: current.begin.further(),
                        end: current.end.further(),
                        prev_value: value,
                        group_value: None,
                    });
                }

                self.next()
            }
        } else {
            None
        }
    }
}

struct Sector<T> {
    /// Start point of current sector.
    begin: PolarPoint,
    /// Point currently being processed.
    pt: PolarPoint,
    /// End point of current sector.
    end: PolarPoint,
    /// The user value from previous iteration.
    prev_value: T,
    /// The user value for this group.
    group_value: Option<T>,
}

/// Points on a hex circle expressed in polar coordinates.
#[derive(Copy, Clone, PartialEq)]
struct PolarPoint {
    pos: f32,
    radius: u32,
}

impl PolarPoint {
    pub fn new(pos: f32, radius: u32) -> PolarPoint {
        PolarPoint {
            pos: pos,
            radius: radius,
        }
    }
    /// Index of the discrete hex cell along the circle that corresponds to this point.
    fn winding_index(self) -> i32 { (self.pos + 0.5).floor() as i32 }

    pub fn is_below(self, other: PolarPoint) -> bool { self.winding_index() < other.end_index() }

    fn end_index(self) -> i32 { (self.pos + 0.5).ceil() as i32 }

    pub fn to_v2(self) -> Point2D<i32> {
        if self.radius == 0 {
            return Point2D::new(0, 0);
        }
        let index = self.winding_index();
        let sector = index.mod_floor(&(self.radius as i32 * 6)) / self.radius as i32;
        let offset = index.mod_floor(&(self.radius as i32));

        let rod = Dir6::from_int(sector).to_v2();
        let tangent = Dir6::from_int((sector + 2) % 6).to_v2();

        rod * (self.radius as i32) + tangent * offset
    }

    /// If this point and the next point are adjacent vertically (along the xy
    /// axis), return the point outside of the circle between the two points.
    ///
    /// This is a helper function for the FOV special case where acute corners
    /// of fake isometric rooms are marked visible even though strict hex FOV
    /// logic would keep them unseen.
    pub fn side_point(self) -> Option<Point2D<i32>> {
        let next = self.next();
        let a = self.to_v2();
        let b = next.to_v2();

        if b.x == a.x + 1 && b.y == a.y + 1 {
            // Going down the right rim.
            Some(Point2D::new(a.x + 1, a.y))
        } else if b.x == a.x - 1 && b.y == a.y - 1 {
            // Going up the left rim.
            Some(Point2D::new(a.x - 1, a.y))
        } else {
            None
        }
    }

    /// The point corresponding to this one on the hex circle with radius +1.
    pub fn further(self) -> PolarPoint {
        PolarPoint::new(self.pos * (self.radius + 1) as f32 / self.radius as f32,
                        self.radius + 1)
    }

    /// The point next to this one along the hex circle.
    pub fn next(self) -> PolarPoint { PolarPoint::new((self.pos + 0.5).floor() + 0.5, self.radius) }
}

#[cfg(test)]
mod test {
    use std::collections::HashMap;
    use std::iter::FromIterator;
    use euclid::Point2D;
    use super::{FovValue, HexFov};
    use hex::HexGeom;

    #[derive(PartialEq, Eq, Clone)]
    struct Cell1 {
        range: i32,
    }

    impl FovValue for Cell1 {
        fn advance(&self, offset: Point2D<i32>) -> Option<Self> {
            if offset.hex_dist() < self.range { Some(self.clone()) } else { None }
        }
    }

    #[derive(PartialEq, Eq, Clone)]
    struct Cell2 {
        range: i32,
    }

    impl FovValue for Cell2 {
        fn advance(&self, offset: Point2D<i32>) -> Option<Self> {
            if offset.hex_dist() < self.range { Some(self.clone()) } else { None }
        }

        fn is_fake_isometric_wall(&self, offset: Point2D<i32>) -> bool {
            let _ = offset;
            true
        }
    }

    #[test]
    fn trivial_fov() {
        // Just draw a small circle.
        let field: HashMap<Point2D<i32>, Cell1> = HashMap::from_iter(HexFov::new(Cell1 {
            range: 2,
        }));
        assert!(field.contains_key(&Point2D::new(1, 0)));
        assert!(!field.contains_key(&Point2D::new(1, -1)));

        // Now test out the fake-isometric corners.
        let field: HashMap<Point2D<i32>, Cell2> = HashMap::from_iter(HexFov::new(Cell2 {
            range: 2,
        }));
        assert!(field.contains_key(&Point2D::new(1, 0)));
        assert!(field.contains_key(&Point2D::new(1, -1)));
    }
}
<commit_msg>Fix Fov logic again<commit_after>use num::Integer;
use euclid::Point2D;
use hex::Dir6;

pub trait FovValue: Eq + Clone {
    /// Construct a new FovValue based on previous one and a new point in the fov.
    fn advance(&self, offset: Point2D<i32>) -> Option<Self>;

    /// Optional method for showing acute corners of fake isometric rooms in fov.
    fn is_fake_isometric_wall(&self, offset: Point2D<i32>) -> bool {
        let _ = offset;
        false
    }
}

/// Field of view iterator for a hexagonal map.
///
/// Takes a function that maps cells into user-specified values and indicates when traversal should
/// stop.
pub struct HexFov<T> {
    stack: Vec<Sector<T>>,
    /// Extra values generated by special cases.
    side_channel: Vec<(Point2D<i32>, T)>,
}

impl<T: FovValue> HexFov<T> {
    pub fn new(init: T) -> HexFov<T> {
        // We could run f for (0, 0) here, but the traditional way for the FOV to work is to only
        // consider your surroundings, not the origin site itself.
        HexFov {
            stack: vec![Sector {
                            begin: PolarPoint::new(0.0, 1),
                            pt: PolarPoint::new(0.0, 1),
                            end: PolarPoint::new(6.0, 1),
                            prev_value: init.clone(),
                            group_value: None,
                        }],
            // The FOV algorithm will not generate the origin point, so we use
            // the side channel to explicitly add it in the beginning.
            side_channel: vec![(Point2D::new(0, 0), init)],
        }
    }
}

impl<T: FovValue> Iterator for HexFov<T> {
    type Item = (Point2D<i32>, T);
    fn next(&mut self) -> Option<(Point2D<i32>, T)> {
        // Empty the side channel before proceeding to the algorithm proper.
        if let Some(ret) = self.side_channel.pop() {
            return Some(ret);
        }

        // Start processing the next sector in the stack.
        if let Some(mut current) = self.stack.pop() {
            // Generate the user value from the previous value in the stack item and the working
            // position in the current sector.
            let next_value = current.prev_value.advance(current.pt.to_v2());

            // Proceed along the sector if we're not at its end yet.
            if current.pt.is_below(current.end) {
                let pos = current.pt.to_v2();

                match current.group_value {
                    None => {
                        // Beginning of group, value isn't set.
                        current.group_value = next_value.clone();
                    }
                    ref old_value if old_value != &next_value => {
                        // Value changed, branch out.

                        // Add the rest of this sector with the new value.
                        self.stack.push(Sector {
                            begin: current.pt,
                            pt: current.pt,
                            end: current.end,
                            prev_value: current.prev_value,
                            group_value: next_value.clone(),
                        });

                        // Branch further on the arc processed so far.
                        if let &Some(ref old_value) = old_value {
                            self.stack.push(Sector {
                                begin: current.begin.further(),
                                pt: current.begin.further(),
                                end: current.pt.further(),
                                prev_value: old_value.clone(),
                                group_value: None,
                            });
                        }
                        return self.next();
                    }
                    // Otherwise don't modify the stack.
                    _ => {}
                }

                // Current value and group value are assumed to be the same from here on.
                assert!(current.group_value == next_value);

                // Hack for making acute corner tiles of fake-isometric rooms visible.

                // We're moving along a vertical line on the hex circle, so there are side
                // points to check.
                if let Some(side_pos) = current.pt.side_point() {

                    let next = current.pt.next();
                    // If the next cell is within the current span and the current cell is
                    // wallform,
                    if next.is_below(current.end) &&
                       current.prev_value.is_fake_isometric_wall(current.pt.to_v2()) {
                        // and if the next cell is visible,
                        if let Some(next_value) = current.prev_value.advance(next.to_v2()) {
                            // and if the current and the next cell are in the same value group,
                            // both the next cell and the third corner point cell are
                            // wallforms, and the side point would not be otherwise
                            // visible:
                            if next_value == current.prev_value &&
                               next_value.is_fake_isometric_wall(next.to_v2()) &&
                               current.prev_value.advance(side_pos).is_none() &&
                               current.prev_value.is_fake_isometric_wall(side_pos) {
                                // Add the side point to the side channel.
                                self.side_channel.push((side_pos, current.prev_value.clone()));
                            }
                        }
                    }
                }

                // Proceed along the current sector.
                current.pt = current.pt.next();
                self.stack.push(current);
                if let Some(next_value) = next_value {
                    return Some((pos, next_value));
                } else {
                    return self.next();
                }
            } else {
                // Hit the end of the sector, branch ahead.
                if let Some(value) = current.group_value {
                    self.stack.push(Sector {
                        begin: current.begin.further(),
                        pt: current.begin.further(),
                        end: current.end.further(),
                        prev_value: value,
                        group_value: None,
                    });
                }

                self.next()
            }
        } else {
            None
        }
    }
}

struct Sector<T> {
    /// Start point of current sector.
    begin: PolarPoint,
    /// Point currently being processed.
    pt: PolarPoint,
    /// End point of current sector.
    end: PolarPoint,
    /// The user value from previous iteration.
    prev_value: T,
    /// The user value for this group.
    group_value: Option<T>,
}

/// Points on a hex circle expressed in polar coordinates.
#[derive(Copy, Clone, PartialEq)]
struct PolarPoint {
    pos: f32,
    radius: u32,
}

impl PolarPoint {
    pub fn new(pos: f32, radius: u32) -> PolarPoint {
        PolarPoint {
            pos: pos,
            radius: radius,
        }
    }
    /// Index of the discrete hex cell along the circle that corresponds to this point.
    fn winding_index(self) -> i32 { (self.pos + 0.5).floor() as i32 }

    pub fn is_below(self, other: PolarPoint) -> bool { self.winding_index() < other.end_index() }

    fn end_index(self) -> i32 { (self.pos + 0.5).ceil() as i32 }

    pub fn to_v2(self) -> Point2D<i32> {
        if self.radius == 0 {
            return Point2D::new(0, 0);
        }
        let index = self.winding_index();
        let sector = index.mod_floor(&(self.radius as i32 * 6)) / self.radius as i32;
        let offset = index.mod_floor(&(self.radius as i32));

        let rod = Dir6::from_int(sector).to_v2();
        let tangent = Dir6::from_int((sector + 2) % 6).to_v2();

        rod * (self.radius as i32) + tangent * offset
    }

    /// If this point and the next point are adjacent vertically (along the xy
    /// axis), return the point outside of the circle between the two points.
    ///
    /// This is a helper function for the FOV special case where acute corners
    /// of fake isometric rooms are marked visible even though strict hex FOV
    /// logic would keep them unseen.
    pub fn side_point(self) -> Option<Point2D<i32>> {
        let next = self.next();
        let a = self.to_v2();
        let b = next.to_v2();

        if b.x == a.x + 1 && b.y == a.y + 1 {
            // Going down the right rim.
            Some(Point2D::new(a.x + 1, a.y))
        } else if b.x == a.x - 1 && b.y == a.y - 1 {
            // Going up the left rim.
            Some(Point2D::new(a.x - 1, a.y))
        } else {
            None
        }
    }

    /// The point corresponding to this one on the hex circle with radius +1.
    pub fn further(self) -> PolarPoint {
        PolarPoint::new(self.pos * (self.radius + 1) as f32 / self.radius as f32,
                        self.radius + 1)
    }

    /// The point next to this one along the hex circle.
    pub fn next(self) -> PolarPoint { PolarPoint::new((self.pos + 0.5).floor() + 0.5, self.radius) }
}

#[cfg(test)]
mod test {
    use std::collections::HashMap;
    use std::iter::FromIterator;
    use euclid::Point2D;
    use super::{FovValue, HexFov};
    use hex::HexGeom;

    #[derive(PartialEq, Eq, Clone)]
    struct Cell1 {
        range: i32,
    }

    impl FovValue for Cell1 {
        fn advance(&self, offset: Point2D<i32>) -> Option<Self> {
            if offset.hex_dist() < self.range { Some(self.clone()) } else { None }
        }
    }

    #[derive(PartialEq, Eq, Clone)]
    struct Cell2 {
        range: i32,
    }

    impl FovValue for Cell2 {
        fn advance(&self, offset: Point2D<i32>) -> Option<Self> {
            if offset.hex_dist() < self.range { Some(self.clone()) } else { None }
        }

        fn is_fake_isometric_wall(&self, offset: Point2D<i32>) -> bool {
            let _ = offset;
            true
        }
    }

    #[test]
    fn trivial_fov() {
        // Just draw a small circle.
        let field: HashMap<Point2D<i32>, Cell1> = HashMap::from_iter(HexFov::new(Cell1 {
            range: 2,
        }));
        assert!(field.contains_key(&Point2D::new(1, 0)));
        assert!(!field.contains_key(&Point2D::new(1, -1)));

        // Now test out the fake-isometric corners.
        let field: HashMap<Point2D<i32>, Cell2> = HashMap::from_iter(HexFov::new(Cell2 {
            range: 2,
        }));
        assert!(field.contains_key(&Point2D::new(1, 0)));
        assert!(field.contains_key(&Point2D::new(1, -1)));
    }
}
<|endoftext|>