<commit_before>use std::io::prelude::*;
use std::io;
use std::path::Path;

use analysis::types::IsIncomplete;
use env::Env;
use file_saver::save_to_file;
use library::{Type, MAIN_NAMESPACE};
use nameutil::crate_name;
use codegen::general;

struct CType {
    /// Name of type, as used in C.
    name: String,
    /// Expression describing when type is available (when defined only conditionally).
    cfg_condition: Option<String>,
}

pub fn generate(env :&Env) {
    let tests = env.config.target_path.join("tests");
    let abi_c = tests.join("abi.c");
    let abi_rs = tests.join("abi.rs");

    let ns = env.library.namespace(MAIN_NAMESPACE);
    let ctypes = ns.types
        .iter()
        .filter_map(|t| t.as_ref())
        .filter(|t| !t.is_incomplete(&env.library))
        .filter_map(|t| match *t {
            Type::Alias(_) |
            Type::Class(_) |
            Type::Record(_) |
            Type::Union(_) |
            Type::Enumeration(_) |
            Type::Bitfield(_) |
            Type::Interface(_)
            => {
                let full_name = format!("{}.{}", &ns.name, t.get_name());
                if env.type_status_sys(&full_name).ignored() {
                    return None;
                }
                let name = match t.get_glib_name() {
                    None => return None,
                    Some(name) => name,
                };
                if is_name_made_up(name) {
                    return None;
                }
                let cfg_condition = env.config.objects.get(&full_name).and_then(|obj| {
                    obj.cfg_condition.clone()
                });
                Some(CType {
                    name: name.to_owned(),
                    cfg_condition,
                })
            },
            _ => None,
        })
        .collect::<Vec<_>>();

    if ctypes.is_empty() {
        return;
    }

    save_to_file(&abi_c, env.config.make_backup, |w| {
        generate_abi_c(env, &abi_c, w)
    });
    save_to_file(&abi_rs, env.config.make_backup, |w| {
        generate_abi_rs(env, &abi_rs, w, &ctypes)
    });
}

/// Checks if type name is unlikely to correspond to a real C type name.
fn is_name_made_up(name: &str) -> bool {
    // Unnamed types are assigned name during parsing, those names contain an underscore.
    name.contains('_')
}

fn generate_abi_c(env: &Env, path: &Path, w: &mut Write) -> io::Result<()> {
    info!("Generating file {:?}", path);
    general::start_comments(w, &env.config)?;
    writeln!(w, "")?;
    writeln!(w, "/* For %z support in printf when using MinGW. */")?;
    writeln!(w, "#define _POSIX_C_SOURCE 200809L")?;
    writeln!(w, "#include <stdalign.h>")?;
    writeln!(w, "#include <stdio.h>")?;

    let ns = env.library.namespace(MAIN_NAMESPACE);
    for include in ns.c_includes.iter() {
        writeln!(w, "#include <{}>", include)?;
    }

    writeln!(w, "{}", r##"
int main() {
  printf("%zu\n%zu\n", sizeof(ABI_TEST_TYPE), alignof(ABI_TEST_TYPE));
}"##)

}

fn generate_abi_rs(env: &Env, path: &Path, w: &mut Write, ctypes: &[CType]) -> io::Result<()> {
    info!("Generating file {:?}", path);
    general::start_comments(w, &env.config)?;
    writeln!(w, "")?;
    let name = format!("{}_sys", crate_name(&env.config.library_name));
    writeln!(w, "extern crate {};", &name)?;
    writeln!(w, "extern crate shell_words;")?;
    writeln!(w, "{}", r##"
use std::collections::BTreeMap;
use std::env;
use std::error::Error;
use std::mem::{align_of, size_of};
use std::process::Command;
use std::str;"##)?;
    writeln!(w, "use {}::*;", &name)?;
    writeln!(w, "{}", r##"

#[derive(Clone, Debug)]
struct Compiler {
    pub args: Vec<String>,
}

impl Compiler {
    pub fn new() -> Result<Compiler, Box<Error>> {
        let mut args = get_var("CC", "cc")?;
        args.extend(get_var("CFLAGS", "")?);
        args.extend(get_var("CPPFLAGS", "")?);
        Ok(Compiler { args })
    }

    pub fn define<'a, V: Into<Option<&'a str>>>(&mut self, var: &str, val: V) {
        let arg = match val.into() {
            None => format!("-D{}", var),
            Some(val) => format!("-D{}={}", var, val),
        };
        self.args.push(arg);
    }

    pub fn to_command(&self) -> Command {
        let mut cmd = Command::new(&self.args[0]);
        cmd.args(&self.args[1..]);
        cmd
    }
}

fn get_var(name: &str, default: &str) -> Result<Vec<String>, Box<Error>> {
    match env::var(name) {
        Ok(value) => Ok(shell_words::split(&value)?),
        Err(env::VarError::NotPresent) => Ok(shell_words::split(default)?),
        Err(err) => Err(format!("{} {}", name, err).into()),
    }
}

fn pkg_config_cflags(package: &str) -> Result<Vec<String>, Box<Error>> {
    let mut cmd = Command::new("pkg-config");
    cmd.arg("--cflags");
    cmd.arg(package);
    let out = cmd.output()?;
    if !out.status.success() {
        return Err(format!("command {:?} returned {}",
                           &cmd, out.status).into());
    }
    let stdout = str::from_utf8(&out.stdout)?;
    Ok(shell_words::split(stdout)?)
}

#[derive(Copy, Clone, Debug, Eq, PartialEq)]
struct ABI {
    size: usize,
    alignment: usize,
}

impl ABI {
    pub fn from_type<T: Sized>() -> ABI {
        ABI {
            size: size_of::<T>(),
            alignment: align_of::<T>(),
        }
    }
}

#[test]
fn test_cross_validate_abi_with_c() {
    // Configure compiler instance."##)?;

    let ns = env.library.namespace(MAIN_NAMESPACE);
    let package_name = ns.package_name.as_ref()
        .expect("Missing package name");

    writeln!(w, "\tlet package_name = \"{}\";", package_name)?;
    writeln!(w, "{}", r##"    let mut cc = Compiler::new()
        .expect("compiler from environment");
    let cflags = pkg_config_cflags(package_name)
        .expect("cflags from pkg-config");
    cc.args.extend(cflags);
    cc.args.push("-Wno-deprecated-declarations".to_owned());

    // Sanity check that compilation works.
    assert_eq!(ABI {size: 1, alignment: 1},
               get_c_abi(&cc, "char").expect("C ABI for char"));

    let rust = get_rust_abi();
    let mut failed = false;
    for (name, rust_abi) in &rust {
        match get_c_abi(&cc, name) {
            Err(e) => {
                failed = true;
                eprintln!("{}", e);
                continue;
            },
            Ok(ref c_abi) => {
                if rust_abi != c_abi {
                    failed = true;
                    eprintln!("ABI mismatch for {}\nRust: {:?}\nC:    {:?}",
                              name, rust_abi, c_abi);
                }
            }
        };
    }
    assert!(!failed);
}

fn get_c_abi(cc: &Compiler, name: &str) -> Result<ABI, Box<Error>> {
    let mut cc = cc.clone();
    cc.define("ABI_TEST_TYPE", name);
    cc.args.push("tests/abi.c".to_owned());
    cc.args.push("-oabi".to_owned());

    let mut cc_cmd = cc.to_command();
    let status = cc_cmd.spawn()?.wait()?;
    if !status.success() {
        return Err(format!("compilation command {:?} failed, {}",
                           &cc_cmd, status).into());
    }

    let mut abi_cmd = Command::new("./abi");
    let output = abi_cmd.output()?;
    if !output.status.success() {
        return Err(format!("command {:?} failed, {}",
                           &abi_cmd, output.status).into());
    }

    let stdout = str::from_utf8(&output.stdout)?;
    let mut words = stdout.split_whitespace();
    let size = words.next().unwrap().parse().unwrap();
    let alignment = words.next().unwrap().parse().unwrap();
    Ok(ABI {size, alignment})
}

fn get_rust_abi() -> BTreeMap<String, ABI> {
    let mut abi = BTreeMap::new();"##)?;

    for ctype in ctypes {
        general::cfg_condition(w, &ctype.cfg_condition, false, 1)?;
        writeln!(w, r##"    abi.insert("{ctype}".to_owned(), ABI::from_type::<{ctype}>());"##,
                 ctype=ctype.name)?;
    }

    writeln!(w, "{}", r##"    abi
}

"##)

}

<commit_msg>Show ABI tests results summary on failure.<commit_after>use std::io::prelude::*;
use std::io;
use std::path::Path;

use analysis::types::IsIncomplete;
use env::Env;
use file_saver::save_to_file;
use library::{Type, MAIN_NAMESPACE};
use nameutil::crate_name;
use codegen::general;

struct CType {
    /// Name of type, as used in C.
    name: String,
    /// Expression describing when type is available (when defined only conditionally).
    cfg_condition: Option<String>,
}

pub fn generate(env :&Env) {
    let tests = env.config.target_path.join("tests");
    let abi_c = tests.join("abi.c");
    let abi_rs = tests.join("abi.rs");

    let ns = env.library.namespace(MAIN_NAMESPACE);
    let ctypes = ns.types
        .iter()
        .filter_map(|t| t.as_ref())
        .filter(|t| !t.is_incomplete(&env.library))
        .filter_map(|t| match *t {
            Type::Alias(_) |
            Type::Class(_) |
            Type::Record(_) |
            Type::Union(_) |
            Type::Enumeration(_) |
            Type::Bitfield(_) |
            Type::Interface(_)
            => {
                let full_name = format!("{}.{}", &ns.name, t.get_name());
                if env.type_status_sys(&full_name).ignored() {
                    return None;
                }
                let name = match t.get_glib_name() {
                    None => return None,
                    Some(name) => name,
                };
                if is_name_made_up(name) {
                    return None;
                }
                let cfg_condition = env.config.objects.get(&full_name).and_then(|obj| {
                    obj.cfg_condition.clone()
                });
                Some(CType {
                    name: name.to_owned(),
                    cfg_condition,
                })
            },
            _ => None,
        })
        .collect::<Vec<_>>();

    if ctypes.is_empty() {
        return;
    }

    save_to_file(&abi_c, env.config.make_backup, |w| {
        generate_abi_c(env, &abi_c, w)
    });
    save_to_file(&abi_rs, env.config.make_backup, |w| {
        generate_abi_rs(env, &abi_rs, w, &ctypes)
    });
}

/// Checks if type name is unlikely to correspond to a real C type name.
fn is_name_made_up(name: &str) -> bool {
    // Unnamed types are assigned name during parsing, those names contain an underscore.
    name.contains('_')
}

fn generate_abi_c(env: &Env, path: &Path, w: &mut Write) -> io::Result<()> {
    info!("Generating file {:?}", path);
    general::start_comments(w, &env.config)?;
    writeln!(w, "")?;
    writeln!(w, "/* For %z support in printf when using MinGW. */")?;
    writeln!(w, "#define _POSIX_C_SOURCE 200809L")?;
    writeln!(w, "#include <stdalign.h>")?;
    writeln!(w, "#include <stdio.h>")?;

    let ns = env.library.namespace(MAIN_NAMESPACE);
    for include in ns.c_includes.iter() {
        writeln!(w, "#include <{}>", include)?;
    }

    writeln!(w, "{}", r##"
int main() {
  printf("%zu\n%zu\n", sizeof(ABI_TEST_TYPE), alignof(ABI_TEST_TYPE));
}"##)

}

fn generate_abi_rs(env: &Env, path: &Path, w: &mut Write, ctypes: &[CType]) -> io::Result<()> {
    info!("Generating file {:?}", path);
    general::start_comments(w, &env.config)?;
    writeln!(w, "")?;
    let name = format!("{}_sys", crate_name(&env.config.library_name));
    writeln!(w, "extern crate {};", &name)?;
    writeln!(w, "extern crate shell_words;")?;
    writeln!(w, "{}", r##"
use std::collections::BTreeMap;
use std::env;
use std::error::Error;
use std::mem::{align_of, size_of};
use std::process::Command;
use std::str;"##)?;
    writeln!(w, "use {}::*;", &name)?;
    writeln!(w, "{}", r##"

#[derive(Clone, Debug)]
struct Compiler {
    pub args: Vec<String>,
}

impl Compiler {
    pub fn new() -> Result<Compiler, Box<Error>> {
        let mut args = get_var("CC", "cc")?;
        args.extend(get_var("CFLAGS", "")?);
        args.extend(get_var("CPPFLAGS", "")?);
        Ok(Compiler { args })
    }

    pub fn define<'a, V: Into<Option<&'a str>>>(&mut self, var: &str, val: V) {
        let arg = match val.into() {
            None => format!("-D{}", var),
            Some(val) => format!("-D{}={}", var, val),
        };
        self.args.push(arg);
    }

    pub fn to_command(&self) -> Command {
        let mut cmd = Command::new(&self.args[0]);
        cmd.args(&self.args[1..]);
        cmd
    }
}

fn get_var(name: &str, default: &str) -> Result<Vec<String>, Box<Error>> {
    match env::var(name) {
        Ok(value) => Ok(shell_words::split(&value)?),
        Err(env::VarError::NotPresent) => Ok(shell_words::split(default)?),
        Err(err) => Err(format!("{} {}", name, err).into()),
    }
}

fn pkg_config_cflags(package: &str) -> Result<Vec<String>, Box<Error>> {
    let mut cmd = Command::new("pkg-config");
    cmd.arg("--cflags");
    cmd.arg(package);
    let out = cmd.output()?;
    if !out.status.success() {
        return Err(format!("command {:?} returned {}",
                           &cmd, out.status).into());
    }
    let stdout = str::from_utf8(&out.stdout)?;
    Ok(shell_words::split(stdout)?)
}

#[derive(Copy, Clone, Debug, Eq, PartialEq)]
struct ABI {
    size: usize,
    alignment: usize,
}

impl ABI {
    pub fn from_type<T: Sized>() -> ABI {
        ABI {
            size: size_of::<T>(),
            alignment: align_of::<T>(),
        }
    }
}

#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
struct TestResults {
    /// Number of successfully completed tests.
    passed: usize,
    /// Total number of failed tests (including those that failed to compile).
    failed: usize,
    /// Number of tests that failed to compile.
    failed_compile: usize,
}

#[test]
fn test_cross_validate_abi_with_c() {
    // Configure compiler instance."##)?;

    let ns = env.library.namespace(MAIN_NAMESPACE);
    let package_name = ns.package_name.as_ref()
        .expect("Missing package name");

    writeln!(w, "\tlet package_name = \"{}\";", package_name)?;
    writeln!(w, "{}", r##"    let mut cc = Compiler::new()
        .expect("compiler from environment");
    let cflags = pkg_config_cflags(package_name)
        .expect("cflags from pkg-config");
    cc.args.extend(cflags);
    cc.args.push("-Wno-deprecated-declarations".to_owned());

    // Sanity check that compilation works.
    assert_eq!(ABI {size: 1, alignment: 1},
               get_c_abi(&cc, "char").expect("C ABI for char"),
               "failed to obtain correct ABI for char type");

    let mut results : TestResults = Default::default();
    for (name, rust_abi) in &get_rust_abi() {
        match get_c_abi(&cc, name) {
            Err(e) => {
                results.failed += 1;
                results.failed_compile += 1;
                eprintln!("{}", e);
                continue;
            },
            Ok(ref c_abi) => {
                if rust_abi == c_abi {
                    results.passed += 1;
                } else {
                    results.failed += 1;
                    eprintln!("ABI mismatch for {}\nRust: {:?}\nC:    {:?}",
                              name, rust_abi, c_abi);
                }
            }
        };
    }

    if results.failed + results.failed_compile != 0 {
        panic!("FAILED\nABI test results: {} passed; {} failed (compilation errors: {})",
               results.passed,
               results.failed,
               results.failed_compile);
    }
}

fn get_c_abi(cc: &Compiler, name: &str) -> Result<ABI, Box<Error>> {
    let mut cc = cc.clone();
    cc.define("ABI_TEST_TYPE", name);
    cc.args.push("tests/abi.c".to_owned());
    cc.args.push("-oabi".to_owned());

    let mut cc_cmd = cc.to_command();
    let status = cc_cmd.spawn()?.wait()?;
    if !status.success() {
        return Err(format!("compilation command {:?} failed, {}",
                           &cc_cmd, status).into());
    }

    let mut abi_cmd = Command::new("./abi");
    let output = abi_cmd.output()?;
    if !output.status.success() {
        return Err(format!("command {:?} failed, {}",
                           &abi_cmd, output.status).into());
    }

    let stdout = str::from_utf8(&output.stdout)?;
    let mut words = stdout.split_whitespace();
    let size = words.next().unwrap().parse().unwrap();
    let alignment = words.next().unwrap().parse().unwrap();
    Ok(ABI {size, alignment})
}

fn get_rust_abi() -> BTreeMap<String, ABI> {
    let mut abi = BTreeMap::new();"##)?;

    for ctype in ctypes {
        general::cfg_condition(w, &ctype.cfg_condition, false, 1)?;
        writeln!(w, r##"    abi.insert("{ctype}".to_owned(), ABI::from_type::<{ctype}>());"##,
                 ctype=ctype.name)?;
    }

    writeln!(w, "{}", r##"    abi
}
"##)

}
<|endoftext|>


<commit_before>// Copyright 2016 Alexander Reece
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![cfg_attr(feature="clippy", feature(plugin))]
#![cfg_attr(feature="clippy", plugin(clippy))]
#![cfg_attr(not(feature="clippy"), allow(unknown_lints))]
extern crate env_logger;

#[cfg(feature = "amqp0-codegen")]
extern crate amqp0_codegen as codegen;
#[cfg(feature = "amqp0-specs")]
extern crate amqp0_specs as specs;

fn main() {
    amqp0::build();
}

#[cfg(not(feature = "amqp0-specs"))]
mod amqp0 {
    pub fn build() {
        println!("Skipping build (neither amqp0-build-primitives nor amqp0-pregen-primitives specified)");
    }
}

#[cfg(feature = "amqp0-specs")]
mod amqp0 {
    use std::env;
    use std::path::{Path, PathBuf};

    use env_logger;
    use codegen;
    use codegen::primalgen::ModulesWriter;
    use specs::specs as amqp0_specs;

    const BUILDER_CRATES: &'static [&'static str] = &["amqp0-codegen"];
    const BUILDER_REBUILD: &'static [&'static str] = &["amqp0-build-primitives"];
    const BUILDER_PREGEN: &'static [&'static str] = &["amqp0-pregen-primitives"];

    struct PrimitivesSource {
        base_dir: PathBuf,
    }

    impl codegen::Source for PrimitivesSource {
        fn name(&self) -> &str { "amqp0-primitives" }
        fn crates(&self) -> &[&str] { BUILDER_CRATES }

        fn rebuild_features(&self) -> &[&str] { BUILDER_REBUILD }
        fn pregeneration_features(&self) -> &[&str] { BUILDER_PREGEN }

        fn base_dir(&self) -> &Path { &self.base_dir }
        fn should_format(&self) -> bool { cfg!(feature = "rustfmt") }
    }

    pub fn build() {
        let base_dir = if cfg!(feature = "amqp0-pregen-primitives") {
            PathBuf::from("pregen")
        } else {
            env::var_os("OUT_DIR")
                .map(PathBuf::from)
                .expect("Error: OUT_DIR not set")
        };

        env_logger::init().unwrap();
        println!("Building primitives from amqpspec");

        let source = PrimitivesSource { base_dir: base_dir };
        let writer = ModulesWriter::new(&source, amqp0_specs());
        writer.write_files().unwrap();
    }
}<commit_msg>[primitives] Format when pregeneration is toggled, rather than no-longer-used rustfmt.<commit_after>// Copyright 2016 Alexander Reece
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![cfg_attr(feature="clippy", feature(plugin))]
#![cfg_attr(feature="clippy", plugin(clippy))]
#![cfg_attr(not(feature="clippy"), allow(unknown_lints))]
extern crate env_logger;

#[cfg(feature = "amqp0-codegen")]
extern crate amqp0_codegen as codegen;
#[cfg(feature = "amqp0-specs")]
extern crate amqp0_specs as specs;

fn main() {
    amqp0::build();
}

#[cfg(not(feature = "amqp0-specs"))]
mod amqp0 {
    pub fn build() {
        println!("Skipping build (neither amqp0-build-primitives nor amqp0-pregen-primitives specified)");
    }
}

#[cfg(feature = "amqp0-specs")]
mod amqp0 {
    use std::env;
    use std::path::{Path, PathBuf};

    use env_logger;
    use codegen;
    use codegen::primalgen::ModulesWriter;
    use specs::specs as amqp0_specs;

    const BUILDER_CRATES: &'static [&'static str] = &["amqp0-codegen"];
    const BUILDER_REBUILD: &'static [&'static str] = &["amqp0-build-primitives"];
    const BUILDER_PREGEN: &'static [&'static str] = &["amqp0-pregen-primitives"];

    struct PrimitivesSource {
        base_dir: PathBuf,
    }

    impl codegen::Source for PrimitivesSource {
        fn name(&self) -> &str { "amqp0-primitives" }
        fn crates(&self) -> &[&str] { BUILDER_CRATES }

        fn rebuild_features(&self) -> &[&str] { BUILDER_REBUILD }
        fn pregeneration_features(&self) -> &[&str] { BUILDER_PREGEN }

        fn base_dir(&self) -> &Path { &self.base_dir }
        fn should_format(&self) -> bool { cfg!(feature = "amqp0-pregen-primitives") }
    }

    pub fn build() {
        let base_dir = if cfg!(feature = "amqp0-pregen-primitives") {
            PathBuf::from("pregen")
        } else {
            env::var_os("OUT_DIR")
                .map(PathBuf::from)
                .expect("Error: OUT_DIR not set")
        };

        env_logger::init().unwrap();
        println!("Building primitives from amqpspec");

        let source = PrimitivesSource { base_dir: base_dir };
        let writer = ModulesWriter::new(&source, amqp0_specs());
        writer.write_files().unwrap();
    }
}<|endoftext|>


<commit_before>// Copyright 2018 Google LLC
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.

use futures::{
    future::{self, Ready},
    prelude::*,
    Future,
};
use publisher::Publisher as _;
use std::{
    collections::HashMap,
    io,
    net::SocketAddr,
    pin::Pin,
    sync::{Arc, Mutex},
    thread,
    time::Duration,
};
use subscriber::Subscriber as _;
use tarpc::{
    client, context,
    server::{self, Handler},
};
use tokio_serde::formats::Json;

pub mod subscriber {
    #[tarpc::service]
    pub trait Subscriber {
        async fn receive(message: String);
    }
}

pub mod publisher {
    use std::net::SocketAddr;

    #[tarpc::service]
    pub trait Publisher {
        async fn broadcast(message: String);
        async fn subscribe(id: u32, address: SocketAddr) -> Result<(), String>;
        async fn unsubscribe(id: u32);
    }
}

#[derive(Clone, Debug)]
struct Subscriber {
    id: u32,
}

impl subscriber::Subscriber for Subscriber {
    type ReceiveFut = Ready<()>;

    fn receive(self, _: context::Context, message: String) -> Self::ReceiveFut {
        eprintln!("{} received message: {}", self.id, message);
        future::ready(())
    }
}

impl Subscriber {
    async fn listen(id: u32, config: server::Config) -> io::Result<SocketAddr> {
        let incoming = tarpc::serde_transport::tcp::listen("localhost:0", Json::default)
            .await?
            .filter_map(|r| future::ready(r.ok()));
        let addr = incoming.get_ref().local_addr();
        tokio::spawn(
            server::new(config)
                .incoming(incoming)
                .take(1)
                .respond_with(Subscriber { id }.serve()),
        );
        Ok(addr)
    }
}

#[derive(Clone, Debug)]
struct Publisher {
    clients: Arc<Mutex<HashMap<u32, subscriber::SubscriberClient>>>,
}

impl Publisher {
    fn new() -> Publisher {
        Publisher {
            clients: Arc::new(Mutex::new(HashMap::new())),
        }
    }
}

impl publisher::Publisher for Publisher {
    type BroadcastFut = Pin<Box<dyn Future<Output = ()> + Send>>;

    fn broadcast(self, _: context::Context, message: String) -> Self::BroadcastFut {
        async fn broadcast(
            clients: Arc<Mutex<HashMap<u32, subscriber::SubscriberClient>>>,
            message: String,
        ) {
            let mut clients = clients.lock().unwrap().clone();
            for client in clients.values_mut() {
                // Ignore failing subscribers. In a real pubsub,
                // you'd want to continually retry until subscribers
                // ack.
                let _ = client.receive(context::current(), message.clone()).await;
            }
        }

        broadcast(self.clients.clone(), message).boxed()
    }

    type SubscribeFut = Pin<Box<dyn Future<Output = Result<(), String>> + Send>>;

    fn subscribe(self, _: context::Context, id: u32, addr: SocketAddr) -> Self::SubscribeFut {
        async fn subscribe(
            clients: Arc<Mutex<HashMap<u32, subscriber::SubscriberClient>>>,
            id: u32,
            addr: SocketAddr,
        ) -> io::Result<()> {
            let conn = tarpc::serde_transport::tcp::connect(addr, Json::default()).await?;
            let subscriber =
                subscriber::SubscriberClient::new(client::Config::default(), conn).spawn()?;
            eprintln!("Subscribing {}.", id);
            clients.lock().unwrap().insert(id, subscriber);
            Ok(())
        }

        subscribe(Arc::clone(&self.clients), id, addr)
            .map_err(|e| e.to_string())
            .boxed()
    }

    type UnsubscribeFut = Pin<Box<dyn Future<Output = ()> + Send>>;

    fn unsubscribe(self, _: context::Context, id: u32) -> Self::UnsubscribeFut {
        eprintln!("Unsubscribing {}", id);
        let mut clients = self.clients.lock().unwrap();
        if clients.remove(&id).is_none() {
            eprintln!(
                "Client {} not found. Existings clients: {:?}",
                id, &*clients
            );
        }
        future::ready(()).boxed()
    }
}

#[tokio::main]
async fn main() -> io::Result<()> {
    env_logger::init();

    let transport = tarpc::serde_transport::tcp::listen("localhost:0", Json::default)
        .await?
        .filter_map(|r| future::ready(r.ok()));
    let publisher_addr = transport.get_ref().local_addr();
    tokio::spawn(
        transport
            .take(1)
            .map(server::BaseChannel::with_defaults)
            .respond_with(Publisher::new().serve()),
    );

    let subscriber1 = Subscriber::listen(0, server::Config::default()).await?;
    let subscriber2 = Subscriber::listen(1, server::Config::default()).await?;

    let publisher_conn = tarpc::serde_transport::tcp::connect(publisher_addr, Json::default());
    let publisher_conn = publisher_conn.await?;
    let mut publisher =
        publisher::PublisherClient::new(client::Config::default(), publisher_conn).spawn()?;

    if let Err(e) = publisher
        .subscribe(context::current(), 0, subscriber1)
        .await?
    {
        eprintln!("Couldn't subscribe subscriber 0: {}", e);
    }
    if let Err(e) = publisher
        .subscribe(context::current(), 1, subscriber2)
        .await?
    {
        eprintln!("Couldn't subscribe subscriber 1: {}", e);
    }

    println!("Broadcasting...");
    publisher
        .broadcast(context::current(), "hello to all".to_string())
        .await?;
    publisher.unsubscribe(context::current(), 1).await?;
    publisher
        .broadcast(context::current(), "hi again".to_string())
        .await?;

    thread::sleep(Duration::from_millis(100));

    Ok(())
}
<commit_msg>Use tokio::time::delay_for in lieu of thread::sleep.<commit_after>// Copyright 2018 Google LLC
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.

use futures::{
    future::{self, Ready},
    prelude::*,
    Future,
};
use publisher::Publisher as _;
use std::{
    collections::HashMap,
    io,
    net::SocketAddr,
    pin::Pin,
    sync::{Arc, Mutex},
    time::Duration,
};
use subscriber::Subscriber as _;
use tarpc::{
    client, context,
    server::{self, Handler},
};
use tokio_serde::formats::Json;

pub mod subscriber {
    #[tarpc::service]
    pub trait Subscriber {
        async fn receive(message: String);
    }
}

pub mod publisher {
    use std::net::SocketAddr;

    #[tarpc::service]
    pub trait Publisher {
        async fn broadcast(message: String);
        async fn subscribe(id: u32, address: SocketAddr) -> Result<(), String>;
        async fn unsubscribe(id: u32);
    }
}

#[derive(Clone, Debug)]
struct Subscriber {
    id: u32,
}

impl subscriber::Subscriber for Subscriber {
    type ReceiveFut = Ready<()>;

    fn receive(self, _: context::Context, message: String) -> Self::ReceiveFut {
        eprintln!("{} received message: {}", self.id, message);
        future::ready(())
    }
}

impl Subscriber {
    async fn listen(id: u32, config: server::Config) -> io::Result<SocketAddr> {
        let incoming = tarpc::serde_transport::tcp::listen("localhost:0", Json::default)
            .await?
            .filter_map(|r| future::ready(r.ok()));
        let addr = incoming.get_ref().local_addr();
        tokio::spawn(
            server::new(config)
                .incoming(incoming)
                .take(1)
                .respond_with(Subscriber { id }.serve()),
        );
        Ok(addr)
    }
}

#[derive(Clone, Debug)]
struct Publisher {
    clients: Arc<Mutex<HashMap<u32, subscriber::SubscriberClient>>>,
}

impl Publisher {
    fn new() -> Publisher {
        Publisher {
            clients: Arc::new(Mutex::new(HashMap::new())),
        }
    }
}

impl publisher::Publisher for Publisher {
    type BroadcastFut = Pin<Box<dyn Future<Output = ()> + Send>>;

    fn broadcast(self, _: context::Context, message: String) -> Self::BroadcastFut {
        async fn broadcast(
            clients: Arc<Mutex<HashMap<u32, subscriber::SubscriberClient>>>,
            message: String,
        ) {
            let mut clients = clients.lock().unwrap().clone();
            for client in clients.values_mut() {
                // Ignore failing subscribers. In a real pubsub,
                // you'd want to continually retry until subscribers
                // ack.
                let _ = client.receive(context::current(), message.clone()).await;
            }
        }

        broadcast(self.clients.clone(), message).boxed()
    }

    type SubscribeFut = Pin<Box<dyn Future<Output = Result<(), String>> + Send>>;

    fn subscribe(self, _: context::Context, id: u32, addr: SocketAddr) -> Self::SubscribeFut {
        async fn subscribe(
            clients: Arc<Mutex<HashMap<u32, subscriber::SubscriberClient>>>,
            id: u32,
            addr: SocketAddr,
        ) -> io::Result<()> {
            let conn = tarpc::serde_transport::tcp::connect(addr, Json::default()).await?;
            let subscriber =
                subscriber::SubscriberClient::new(client::Config::default(), conn).spawn()?;
            eprintln!("Subscribing {}.", id);
            clients.lock().unwrap().insert(id, subscriber);
            Ok(())
        }

        subscribe(Arc::clone(&self.clients), id, addr)
            .map_err(|e| e.to_string())
            .boxed()
    }

    type UnsubscribeFut = Pin<Box<dyn Future<Output = ()> + Send>>;

    fn unsubscribe(self, _: context::Context, id: u32) -> Self::UnsubscribeFut {
        eprintln!("Unsubscribing {}", id);
        let mut clients = self.clients.lock().unwrap();
        if clients.remove(&id).is_none() {
            eprintln!(
                "Client {} not found. Existings clients: {:?}",
                id, &*clients
            );
        }
        future::ready(()).boxed()
    }
}

#[tokio::main]
async fn main() -> io::Result<()> {
    env_logger::init();

    let transport = tarpc::serde_transport::tcp::listen("localhost:0", Json::default)
        .await?
        .filter_map(|r| future::ready(r.ok()));
    let publisher_addr = transport.get_ref().local_addr();
    tokio::spawn(
        transport
            .take(1)
            .map(server::BaseChannel::with_defaults)
            .respond_with(Publisher::new().serve()),
    );

    let subscriber1 = Subscriber::listen(0, server::Config::default()).await?;
    let subscriber2 = Subscriber::listen(1, server::Config::default()).await?;

    let publisher_conn = tarpc::serde_transport::tcp::connect(publisher_addr, Json::default());
    let publisher_conn = publisher_conn.await?;
    let mut publisher =
        publisher::PublisherClient::new(client::Config::default(), publisher_conn).spawn()?;

    if let Err(e) = publisher
        .subscribe(context::current(), 0, subscriber1)
        .await?
    {
        eprintln!("Couldn't subscribe subscriber 0: {}", e);
    }
    if let Err(e) = publisher
        .subscribe(context::current(), 1, subscriber2)
        .await?
    {
        eprintln!("Couldn't subscribe subscriber 1: {}", e);
    }

    println!("Broadcasting...");
    publisher
        .broadcast(context::current(), "hello to all".to_string())
        .await?;
    publisher.unsubscribe(context::current(), 1).await?;
    publisher
        .broadcast(context::current(), "hi again".to_string())
        .await?;

    tokio::time::delay_for(Duration::from_millis(100)).await;
    println!("Done.");

    Ok(())
}
<|endoftext|>


<commit_before>use std::env;
use std::fs::File;
use std::io::Read;
use std::path::{Path, PathBuf};

fn template_dir() -> PathBuf {
    let mut path = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
    path.push("templates");
    path
}

pub fn find_template_from_path<'a>(path: &str, start_at: Option<&str>) -> PathBuf {
    let root = template_dir();
    match start_at {
        Some(rel) => {
            let mut fs_rel_path = root.clone();
            fs_rel_path.push(rel);
            fs_rel_path = fs_rel_path.with_file_name(path);
            if fs_rel_path.exists() {
                return fs_rel_path.strip_prefix(&root).unwrap().to_owned();
            }
        },
        None => {},
    }

    let mut fs_abs_path = root.clone();
    let path = Path::new(path);
    fs_abs_path.push(Path::new(path));
    if fs_abs_path.exists() {
        path.to_owned()
    } else {
        panic!(format!("template '{:?}' not found", path.to_str()));
    }
}

pub fn get_template_source(tpl_file: &str) -> String {
    let mut path = template_dir();
    path.push(Path::new(tpl_file));
    let mut f = match File::open(&path) {
        Err(_) => {
            let msg = format!("unable to open template file '{}'",
                              &path.to_str().unwrap());
            panic!(msg);
        },
        Ok(f) => f,
    };
    let mut s = String::new();
    f.read_to_string(&mut s).unwrap();
    s
}

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

    #[test]
    fn find_absolute() {
        let path = find_template_from_path("sub/b.html", Some("a.html"));
        assert_eq!(path, Path::new("sub/b.html"));
    }

    #[test]
    #[should_panic]
    fn find_relative_nonexistent() {
        find_template_from_path("b.html", Some("a.html"));
    }

    #[test]
    fn find_relative() {
        let path = find_template_from_path("c.html", Some("sub/b.html"));
        assert_eq!(path, Path::new("sub/c.html"));
    }

    #[test]
    fn find_relative_sub() {
        let path = find_template_from_path("sub1/d.html", Some("sub/b.html"));
        assert_eq!(path, Path::new("sub/sub1/d.html"));
    }
}
<commit_msg>Add test for path::get_template_source()<commit_after>use std::env;
use std::fs::File;
use std::io::Read;
use std::path::{Path, PathBuf};

fn template_dir() -> PathBuf {
    let mut path = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
    path.push("templates");
    path
}

pub fn find_template_from_path<'a>(path: &str, start_at: Option<&str>) -> PathBuf {
    let root = template_dir();
    match start_at {
        Some(rel) => {
            let mut fs_rel_path = root.clone();
            fs_rel_path.push(rel);
            fs_rel_path = fs_rel_path.with_file_name(path);
            if fs_rel_path.exists() {
                return fs_rel_path.strip_prefix(&root).unwrap().to_owned();
            }
        },
        None => {},
    }

    let mut fs_abs_path = root.clone();
    let path = Path::new(path);
    fs_abs_path.push(Path::new(path));
    if fs_abs_path.exists() {
        path.to_owned()
    } else {
        panic!(format!("template '{:?}' not found", path.to_str()));
    }
}

pub fn get_template_source(tpl_file: &str) -> String {
    let mut path = template_dir();
    path.push(Path::new(tpl_file));
    let mut f = match File::open(&path) {
        Err(_) => {
            let msg = format!("unable to open template file '{}'",
                              &path.to_str().unwrap());
            panic!(msg);
        },
        Ok(f) => f,
    };
    let mut s = String::new();
    f.read_to_string(&mut s).unwrap();
    s
}

#[cfg(test)]
mod tests {
    use super::{find_template_from_path, get_template_source};
    use super::Path;

    #[test]
    fn get_source() {
        assert_eq!(get_template_source("sub/b.html"), "bar\n");
    }

    #[test]
    fn find_absolute() {
        let path = find_template_from_path("sub/b.html", Some("a.html"));
        assert_eq!(path, Path::new("sub/b.html"));
    }

    #[test]
    #[should_panic]
    fn find_relative_nonexistent() {
        find_template_from_path("b.html", Some("a.html"));
    }

    #[test]
    fn find_relative() {
        let path = find_template_from_path("c.html", Some("sub/b.html"));
        assert_eq!(path, Path::new("sub/c.html"));
    }

    #[test]
    fn find_relative_sub() {
        let path = find_template_from_path("sub1/d.html", Some("sub/b.html"));
        assert_eq!(path, Path::new("sub/sub1/d.html"));
    }
}
<|endoftext|>


<commit_before>trait Parser<T, E> {
    fn next(&self) -> Option<&T>;
    fn preview(&self) -> Option<&T>;
    fn current_pos(&self) -> (i32, i32);
    fn error(&self, message: &str) -> E;
}

macro_rules! preview {
    ($p:expr) => {
        match $p.preview() {
            Some(x) => x,
            None => return Err($p.error("unexpected eof")),
        }
    }
}

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

    struct TP<'a> {
        input: &'a mut [i32],
    }

    impl<'a> TP<'a> {
        fn new(input: &mut [i32]) -> TP {
            TP { input }
        }
    }

    impl<'a> Parser<i32, String> for TP<'a> {
        fn next(&self) -> Option<&i32> {
            self.input.iter().next()
        }

        fn preview(&self) -> Option<&i32> {
            self.input.iter().nth(0)
        }

        fn current_pos(&self) -> (i32, i32) {
            (0, 0)
        }

        fn error(&self, message: &str) -> String {
            message.to_string()
        }
    }

    macro_rules! with_res {
        (test $i:ident, $b:block) => {
            #[test]
            fn $i() {
                let result = || {
                    $b;
                    Ok(())
                };
                result().unwrap()
            }
        };
        (panic $e:expr, $i:ident, $b:block) => {
            #[test]
            #[should_panic(expected = $e)]
            fn $i() {
                let result = || {
                    $b;
                    Ok(())
                };
                result().unwrap()
            }
        };
    }

    with_res!(test preview_success, {
        let input = &mut [1, 2, 3];
        let p = TP::new(input);
        assert_eq!(*preview!(p), 1);
    });

    with_res!(panic "unexpected eof", preview_fail_empty, {
        let input = &mut [];
        let p = TP::new(input);
        assert_eq!(*preview!(p), 1);
    });
}
<commit_msg>Use VecDeque instead of slice<commit_after>trait Parser<T, E> {
    fn next(&mut self) -> Option<T>;
    fn preview(&self) -> Option<&T>;
    fn current_pos(&self) -> (i32, i32);
    fn error(&self, message: &str) -> E;
}

macro_rules! next {
    ($p:expr) => {
        match $p.next() {
            Some(x) => x,
            None => return Err($p.error("unexpected eof")),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::collections::vec_deque::VecDeque;

    struct TP {
        input: VecDeque<i32>,
    }

    impl TP {
        fn new(input: Vec<i32>) -> TP {
            TP { input: VecDeque::from(input) }
        }
    }

    impl Parser<i32, String> for TP {
        fn next(&mut self) -> Option<i32> {
            self.input.pop_front()
        }

        fn preview(&self) -> Option<&i32> {
            self.input.front()
        }

        fn current_pos(&self) -> (i32, i32) {
            (0, 0)
        }

        fn error(&self, message: &str) -> String {
            message.to_string()
        }
    }

    macro_rules! with_res {
        (test $i:ident, $b:block) => {
            #[test]
            fn $i() {
                let result = || {
                    $b;
                    Ok(())
                };
                result().unwrap()
            }
        };
        (panic $e:expr, $i:ident, $b:block) => {
            #[test]
            #[should_panic(expected = $e)]
            fn $i() {
                let result = || {
                    $b;
                    Ok(())
                };
                result().unwrap()
            }
        };
    }

    with_res!(test next_success, {
        let mut p = TP::new(vec![1, 2, 3]);
        assert_eq!(next!(p), 1);
        assert_eq!(next!(p), 2);
        assert_eq!(next!(p), 3);
    });

    with_res!(panic "unexpected eof", next_fail_empty, {
        let mut p = TP::new(vec![]);
        assert_eq!(next!(p), 1);
    });
}
<|endoftext|>


<commit_before>extern crate pnet;
extern crate chrono;
extern crate timer;

use pnet::datalink::{self, NetworkInterface};
use pnet::datalink::Channel::Ethernet;
use pnet::datalink::EthernetDataLinkSender;
use pnet::datalink::EthernetDataLinkReceiver;
use pnet::packet::{Packet, MutablePacket};

use std::env;
use std::usize;
use std::thread;
use std::sync::mpsc::{Sender, Receiver};
use std::sync::mpsc::channel;

// Invoke as echo <interface name>
fn main() {
    let interface_name = env::args()
        .nth(1)
        .unwrap_or_else(|| {
            println!("Invalid number of arguments");
            std::process::exit(1);
        });

    if interface_name == "list" {
        print_interfaces();
    } else {
        let (tx, rx) = channel();
        thread::spawn(|| print_bandwith(rx));
        thread::spawn(|| listen_for_packets(interface_name));
        loop {};
    }
}

fn print_interfaces() {
    let interfaces = datalink::interfaces();
    let interface_names_iter = interfaces
        .into_iter()
        .map(|iface: NetworkInterface| iface.name);

    for iface in interface_names_iter {
        println!("{}", iface);
    }
}

fn print_bandwith(rx: Receiver<usize>) {
    loop {
        let size = rx.recv().unwrap();
        println!("{}", size);
    }
}

fn create_datalink_channel(interface_name: String) -> (Box<EthernetDataLinkSender>, Box<EthernetDataLinkReceiver>){
    let interface_names_match =
        |iface: &NetworkInterface| iface.name == interface_name;

    // Find the network interface with the provided name
    let interfaces = datalink::interfaces();
    let interface = interfaces.into_iter()
        .find(interface_names_match);

    if interface.is_none() {
        println!("Interface not found");
        std::process::exit(1);
    }

    // Create a new channel, dealing with layer 2 packets
    return match datalink::channel(&interface.unwrap(), Default::default()) {
        Ok(Ethernet(tx, rx)) => (tx, rx),
        Ok(_) => panic!("Unhandled channel type"),
        Err(e) => panic!("An error occurred when creating the data link channel: {}", e)
    };
}

fn listen_for_packets(interface_name: String) {
    let (mut tx, mut rx) = create_datalink_channel(interface_name);
    let mut iter = rx.iter();
    let mut sent_packets = 0;
    loop {
        match iter.next() {
            Ok(packet) => {
                sent_packets += packet.packet().len();
                tx.build_and_send(1, packet.packet().len(),
                                  &mut |mut new_packet| {
                                      new_packet.clone_from(&packet);
                                  });
            },
            Err(e) => {
                // If an error occurs, we can handle it here
                panic!("An error occurred while reading: {}", e);
            }
        }
    }
}<commit_msg>Fixed timer issues<commit_after>extern crate pnet;
extern crate chrono;
extern crate timer;

use pnet::datalink::{self, NetworkInterface};
use pnet::datalink::Channel::Ethernet;
use pnet::datalink::EthernetDataLinkSender;
use pnet::datalink::EthernetDataLinkReceiver;
use pnet::packet::{Packet, MutablePacket};

use std::env;
use std::usize;
use std::thread;
use std::sync::{Arc, Mutex};
use std::sync::mpsc::{Sender, Receiver};
use std::sync::mpsc::channel;

// Invoke as echo <interface name>
fn main() {
    let interface_name = env::args()
        .nth(1)
        .unwrap_or_else(|| {
            println!("Invalid number of arguments");
            std::process::exit(1);
        });

    if interface_name == "list" {
        print_interfaces();
    } else {
        let (tx, rx) = channel();
        thread::spawn(|| handle_packet_count(rx));
        thread::spawn(|| listen_for_packets(interface_name, tx));
        loop {};
    }
}

fn print_interfaces() {
    let interfaces = datalink::interfaces();
    let interface_names_iter = interfaces
        .into_iter()
        .map(|iface: NetworkInterface| iface.name);

    for iface in interface_names_iter {
        println!("{}", iface);
    }
}

fn print_bandwith(rx: Receiver<usize>) {
    loop {
        let size = rx.recv().unwrap();
        println!("{}", size);
    }
}

fn create_datalink_channel(interface_name: String) -> (Box<EthernetDataLinkSender>, Box<EthernetDataLinkReceiver>){
    let interface_names_match =
        |iface: &NetworkInterface| iface.name == interface_name;

    // Find the network interface with the provided name
    let interfaces = datalink::interfaces();
    let interface = interfaces.into_iter()
        .find(interface_names_match);

    if interface.is_none() {
        println!("Interface not found");
        std::process::exit(1);
    }

    // Create a new channel, dealing with layer 2 packets
    return match datalink::channel(&interface.unwrap(), Default::default()) {
        Ok(Ethernet(tx, rx)) => (tx, rx),
        Ok(_) => panic!("Unhandled channel type"),
        Err(e) => panic!("An error occurred when creating the data link channel: {}", e)
    };
}

fn listen_for_packets(interface_name: String, tx_bandwidth: Sender<usize>) {
    let (mut tx, mut rx) = create_datalink_channel(interface_name);
    let mut iter = rx.iter();
    loop {
        match iter.next() {
            Ok(packet) => {
                tx_bandwidth.send(packet.packet().len());
                tx.build_and_send(1, packet.packet().len(),
                                  &mut |mut new_packet| {
                                      new_packet.clone_from(&packet);
                                  });
            },
            Err(e) => {
                // If an error occurs, we can handle it here
                panic!("An error occurred while reading: {}", e);
            }
        }
    }
}

fn handle_packet_count(rx: Receiver<usize>) {
    let packets = Arc::new(Mutex::new(vec![]));
    let timer = timer::Timer::new();
    let cloned_packets = packets.clone();
    let guard = timer.schedule_repeating(chrono::Duration::seconds(1), move || {
        println!("{:?}", cloned_packets);
    });

    loop {
        let nr = rx.recv().unwrap();
        let packets = packets.clone();
        let mut locked_packets = packets.lock().unwrap();
        locked_packets.push(nr);
    }
}<|endoftext|>


<commit_before><commit_msg>fix build in no_std+alloc<commit_after><|endoftext|>


<commit_before>//! The push thread.
//!
//! This is the thread for distributing rumors to members. It distributes to `FANOUT` members, no
//! more often than `Timing::GOSSIP_PERIOD_DEFAULT_MS`.

use std::{thread,
          time::Duration};

use habitat_core::util::ToI64;
use prometheus::{IntCounterVec,
                 IntGaugeVec};
use time::SteadyTime;
use zmq;

use crate::{member::{Member,
                     Membership},
            rumor::{RumorEnvelope,
                    RumorKey,
                    RumorKind,
                    RumorType},
            server::{timing::Timing,
                     Server},
            trace::TraceKind,
            ZMQ_CONTEXT};

const FANOUT: usize = 5;

lazy_static! {
    static ref GOSSIP_MESSAGES_SENT: IntCounterVec =
        register_int_counter_vec!("hab_butterfly_gossip_messages_sent_total",
                                  "Total number of gossip messages sent",
                                  &["type", "mode"]).unwrap();
    static ref GOSSIP_BYTES_SENT: IntGaugeVec =
        register_int_gauge_vec!("hab_butterfly_gossip_sent_bytes",
                                "Gossip message size sent in bytes",
                                &["type", "mode"]).unwrap();
}

/// The Push server
#[derive(Debug)]
pub struct Push {
    pub server: Server,
    pub timing: Timing,
}

impl Push {
    /// Creates a new Push instance from a Server and Timing
    pub fn new(server: Server, timing: Timing) -> Push { Push { server, timing } }

    /// Executes the Push thread. Gets a list of members to talk to that are not Confirmed; then
    /// proceeds to process the list in `FANOUT` sized chunks. If we finish sending the messages to
    /// all FANOUT targets faster than `Timing::GOSSIP_PERIOD_DEFAULT_MS`, we will block until we
    /// exceed that time.
    pub fn run(&mut self) {
        loop {
            habitat_common::sync::mark_thread_alive();

            if self.server.paused() {
                thread::sleep(Duration::from_millis(100));
                continue;
            }

            self.server.update_gossip_round();

            let mut check_list = self.server.member_list.check_list(self.server.member_id());
            let long_wait = self.timing.gossip_timeout();

            'fanout: loop {
                let mut thread_list = Vec::with_capacity(FANOUT);
                if check_list.is_empty() {
                    break 'fanout;
                }
                let drain_length = if check_list.len() >= FANOUT {
                    FANOUT
                } else {
                    check_list.len()
                };
                let next_gossip = self.timing.gossip_timeout();
                for member in check_list.drain(0..drain_length) {
                    if self.server.is_member_blocked(&member.id) {
                        debug!("Not sending rumors to {} - it is blocked", member.id);

                        continue;
                    }
                    // Unlike the SWIM mechanism, we don't actually want to send gossip traffic to
                    // persistent members that are confirmed dead. When the failure detector thread
                    // finds them alive again, we'll go ahead and get back to the business at hand.
                    if self.server.member_list.pingable(&member)
                       && !self.server.member_list.persistent_and_confirmed(&member)
                    {
                        let rumors = self.server.rumor_heat.currently_hot_rumors(&member.id);
                        if !rumors.is_empty() {
                            let sc = self.server.clone();

                            let guard = match thread::Builder::new()
                                .name(String::from("push-worker"))
                                .spawn(move || {
                                    PushWorker::new(sc).send_rumors(&member, &rumors);
                                }) {
                                Ok(guard) => guard,
                                Err(e) => {
                                    error!("Could not spawn thread: {}", e);
                                    continue;
                                }
                            };
                            thread_list.push(guard);
                        }
                    }
                }
                let num_threads = thread_list.len();
                for guard in thread_list.drain(0..num_threads) {
                    let _ = guard.join()
                                 .map_err(|e| error!("Push worker died: {:?}", e));
                }
                if SteadyTime::now() < next_gossip {
                    let wait_time = (next_gossip - SteadyTime::now()).num_milliseconds();
                    if wait_time > 0 {
                        thread::sleep(Duration::from_millis(wait_time as u64));
                    }
                }
            }
            if SteadyTime::now() < long_wait {
                let wait_time = (long_wait - SteadyTime::now()).num_milliseconds();
                if wait_time > 0 {
                    thread::sleep(Duration::from_millis(wait_time as u64));
                }
            }
        }
    }
}

/// A worker thread for pushing messages to a target
struct PushWorker {
    pub server: Server,
}

impl PushWorker {
    /// Create a new PushWorker.
    pub fn new(server: Server) -> PushWorker { PushWorker { server } }

    /// Send the list of rumors to a given member. This method creates an outbound socket and then
    /// closes the connection as soon as we are done sending rumors. ZeroMQ may choose to keep the
    /// connection and socket open for 1 second longer - so it is possible, but unlikely, that this
    /// method can lose messages.
    // If we ever need to modify this function, it would be an excellent opportunity to
    // simplify the redundant aspects and remove this allow(clippy::cyclomatic_complexity),
    // but changing it in the absence of other necessity seems like too much risk for the
    // expected reward.
    fn send_rumors(&self, member: &Member, rumors: &[RumorKey]) {
        let socket = (**ZMQ_CONTEXT).as_mut()
                                    .socket(zmq::PUSH)
                                    .expect("Failure to create the ZMQ push socket");
        socket.set_linger(1000)
              .expect("Failure to set the ZMQ push socket to not linger");
        socket.set_tcp_keepalive(0)
              .expect("Failure to set the ZMQ push socket to not use keepalive");
        socket.set_immediate(true)
              .expect("Failure to set the ZMQ push socket to immediate");
        socket.set_sndhwm(1000)
              .expect("Failure to set the ZMQ push socket hwm");
        socket.set_sndtimeo(500)
              .expect("Failure to set the ZMQ send timeout");
        let to_addr = format!("{}:{}", member.address, member.gossip_port);
        match socket.connect(&format!("tcp://{}", to_addr)) {
            Ok(()) => debug!("Connected push socket to {:?}", member),
            Err(e) => {
                error!("Cannot connect push socket to {:?}: {:?}", member, e);
                let label_values = &["socket_connect", "failure"];
                GOSSIP_MESSAGES_SENT.with_label_values(label_values).inc();
                GOSSIP_BYTES_SENT.with_label_values(label_values).set(0);
                return;
            }
        }
        'rumorlist: for rumor_key in rumors.iter() {
            let rumor_as_bytes = match rumor_key.kind {
                RumorType::Member => {
                    let send_rumor = match self.create_member_rumor(&rumor_key) {
                        Some(rumor) => rumor,
                        None => continue 'rumorlist,
                    };
                    trace_it!(GOSSIP: &self.server,
                              TraceKind::SendRumor,
                              &member.id,
                              &send_rumor);
                    match send_rumor.encode() {
                        Ok(bytes) => bytes,
                        Err(e) => {
                            error!("Could not write our own rumor to bytes; abandoning sending \
                                    rumor: {:?}",
                                   e);
                            let label_values = &["member_rumor_encode", "failure"];
                            GOSSIP_MESSAGES_SENT.with_label_values(label_values).inc();
                            GOSSIP_BYTES_SENT.with_label_values(label_values).set(0);
                            continue 'rumorlist;
                        }
                    }
                }
                RumorType::Service => {
                    // trace_it!(GOSSIP: &self.server,
                    //           TraceKind::SendRumor,
                    //           &member.id,
                    //           &send_rumor);
                    match self.server
                              .service_store
                              .encode(&rumor_key.key, &rumor_key.id)
                    {
                        Ok(bytes) => bytes,
                        Err(e) => {
                            error!("Could not write our own rumor to bytes; abandoning sending \
                                    rumor: {:?}",
                                   e);
                            let label_values = &["service_rumor_encode", "failure"];
                            GOSSIP_MESSAGES_SENT.with_label_values(label_values).inc();
                            GOSSIP_BYTES_SENT.with_label_values(label_values).set(0);
                            continue 'rumorlist;
                        }
                    }
                }
                RumorType::ServiceConfig => {
                    // trace_it!(GOSSIP: &self.server,
                    //           TraceKind::SendRumor,
                    //           &member.id,
                    //           &send_rumor);
                    match self.server
                              .service_config_store
                              .encode(&rumor_key.key, &rumor_key.id)
                    {
                        Ok(bytes) => bytes,
                        Err(e) => {
                            error!("Could not write our own rumor to bytes; abandoning sending \
                                    rumor: {:?}",
                                   e);
                            let label_values = &["service_config_rumor_encode", "failure"];
                            GOSSIP_MESSAGES_SENT.with_label_values(label_values).inc();
                            GOSSIP_BYTES_SENT.with_label_values(label_values).set(0);
                            continue 'rumorlist;
                        }
                    }
                }
                RumorType::ServiceFile => {
                    // trace_it!(GOSSIP: &self.server,
                    //           TraceKind::SendRumor,
                    //           &member.id,
                    //           &send_rumor);
                    match self.server
                              .service_file_store
                              .encode(&rumor_key.key, &rumor_key.id)
                    {
                        Ok(bytes) => bytes,
                        Err(e) => {
                            error!("Could not write our own rumor to bytes; abandoning sending \
                                    rumor: {:?}",
                                   e);
                            let label_values = &["service_file_rumor_encode", "failure"];
                            GOSSIP_MESSAGES_SENT.with_label_values(label_values).inc();
                            GOSSIP_BYTES_SENT.with_label_values(label_values).set(0);
                            continue 'rumorlist;
                        }
                    }
                }
                RumorType::Departure => {
                    match self.server
                              .departure_store
                              .encode(&rumor_key.key, &rumor_key.id)
                    {
                        Ok(bytes) => bytes,
                        Err(e) => {
                            error!("Could not write our own rumor to bytes; abandoning sending \
                                    rumor: {:?}",
                                   e);
                            let label_values = &["departure_rumor_encode", "failure"];
                            GOSSIP_MESSAGES_SENT.with_label_values(label_values).inc();
                            GOSSIP_BYTES_SENT.with_label_values(label_values).set(0);
                            continue 'rumorlist;
                        }
                    }
                }
                RumorType::Election => {
                    // trace_it!(GOSSIP: &self.server,
                    //           TraceKind::SendRumor,
                    //           &member.id,
                    //           &send_rumor);
                    match self.server
                              .election_store
                              .encode(&rumor_key.key, &rumor_key.id)
                    {
                        Ok(bytes) => bytes,
                        Err(e) => {
                            error!("Could not write our own rumor to bytes; abandoning sending \
                                    rumor: {:?}",
                                   e);
                            let label_values = &["election_rumor_encode", "failure"];
                            GOSSIP_MESSAGES_SENT.with_label_values(label_values).inc();
                            GOSSIP_BYTES_SENT.with_label_values(label_values).set(0);
                            continue 'rumorlist;
                        }
                    }
                }
                RumorType::ElectionUpdate => {
                    match self.server
                              .update_store
                              .encode(&rumor_key.key, &rumor_key.id)
                    {
                        Ok(bytes) => bytes,
                        Err(e) => {
                            error!("Could not write our own rumor to bytes; abandoning sending \
                                    rumor: {:?}",
                                   e);
                            let label_values = &["election_update_rumor_encode", "failure"];
                            GOSSIP_MESSAGES_SENT.with_label_values(label_values).inc();
                            GOSSIP_BYTES_SENT.with_label_values(label_values).set(0);
                            continue 'rumorlist;
                        }
                    }
                }
                RumorType::Fake | RumorType::Fake2 => {
                    debug!("You have fake rumors; how odd!");
                    continue 'rumorlist;
                }
            };
            let rumor_len = rumor_as_bytes.len().to_i64();
            let payload = match self.server.generate_wire(rumor_as_bytes) {
                Ok(payload) => payload,
                Err(e) => {
                    error!("Generating protobuf failed: {}", e);
                    let label_values = &["generate_wire", "failure"];
                    GOSSIP_MESSAGES_SENT.with_label_values(label_values).inc();
                    GOSSIP_BYTES_SENT.with_label_values(label_values)
                                     .set(rumor_len);
                    continue 'rumorlist;
                }
            };
            match socket.send(&payload, 0) {
                Ok(()) => {
                    GOSSIP_MESSAGES_SENT.with_label_values(&[&rumor_key.kind.to_string(),
                                                             "success"])
                                        .inc();
                    GOSSIP_BYTES_SENT.with_label_values(&[&rumor_key.kind.to_string(), "success"])
                                     .set(payload.len().to_i64());
                    debug!("Sent rumor {:?} to {:?}", rumor_key, member);
                }
                Err(e) => {
                    warn!("Could not send rumor to {:?} @ {:?}; ZMQ said: {:?}",
                          member.id, to_addr, e)
                }
            }
        }
        self.server.rumor_heat.cool_rumors(&member.id, &rumors);
    }

    /// Given a rumorkey, creates a protobuf rumor for sharing.
    fn create_member_rumor(&self, rumor_key: &RumorKey) -> Option<RumorEnvelope> {
        let member = self.server.member_list.get_cloned(&rumor_key.key())?;
        let payload = Membership { member,
                                   health: self.server
                                               .member_list
                                               .health_of_by_id(&rumor_key.key())
                                               .unwrap() };
        let rumor = RumorEnvelope { r#type:  RumorType::Member,
                                    from_id: self.server.member_id().to_string(),
                                    kind:    RumorKind::Membership(payload), };
        Some(rumor)
    }
}
<commit_msg>Updated clippy lint name: cyclomatic_complexity -> cognitive_complexity<commit_after>//! The push thread.
//!
//! This is the thread for distributing rumors to members. It distributes to `FANOUT` members, no
//! more often than `Timing::GOSSIP_PERIOD_DEFAULT_MS`.

use std::{thread,
          time::Duration};

use habitat_core::util::ToI64;
use prometheus::{IntCounterVec,
                 IntGaugeVec};
use time::SteadyTime;
use zmq;

use crate::{member::{Member,
                     Membership},
            rumor::{RumorEnvelope,
                    RumorKey,
                    RumorKind,
                    RumorType},
            server::{timing::Timing,
                     Server},
            trace::TraceKind,
            ZMQ_CONTEXT};

const FANOUT: usize = 5;

lazy_static! {
    static ref GOSSIP_MESSAGES_SENT: IntCounterVec =
        register_int_counter_vec!("hab_butterfly_gossip_messages_sent_total",
                                  "Total number of gossip messages sent",
                                  &["type", "mode"]).unwrap();
    static ref GOSSIP_BYTES_SENT: IntGaugeVec =
        register_int_gauge_vec!("hab_butterfly_gossip_sent_bytes",
                                "Gossip message size sent in bytes",
                                &["type", "mode"]).unwrap();
}

/// The Push server
#[derive(Debug)]
pub struct Push {
    pub server: Server,
    pub timing: Timing,
}

impl Push {
    /// Creates a new Push instance from a Server and Timing
    pub fn new(server: Server, timing: Timing) -> Push { Push { server, timing } }

    /// Executes the Push thread. Gets a list of members to talk to that are not Confirmed; then
    /// proceeds to process the list in `FANOUT` sized chunks. If we finish sending the messages to
    /// all FANOUT targets faster than `Timing::GOSSIP_PERIOD_DEFAULT_MS`, we will block until we
    /// exceed that time.
    pub fn run(&mut self) {
        loop {
            habitat_common::sync::mark_thread_alive();

            if self.server.paused() {
                thread::sleep(Duration::from_millis(100));
                continue;
            }

            self.server.update_gossip_round();

            let mut check_list = self.server.member_list.check_list(self.server.member_id());
            let long_wait = self.timing.gossip_timeout();

            'fanout: loop {
                let mut thread_list = Vec::with_capacity(FANOUT);
                if check_list.is_empty() {
                    break 'fanout;
                }
                let drain_length = if check_list.len() >= FANOUT {
                    FANOUT
                } else {
                    check_list.len()
                };
                let next_gossip = self.timing.gossip_timeout();
                for member in check_list.drain(0..drain_length) {
                    if self.server.is_member_blocked(&member.id) {
                        debug!("Not sending rumors to {} - it is blocked", member.id);

                        continue;
                    }
                    // Unlike the SWIM mechanism, we don't actually want to send gossip traffic to
                    // persistent members that are confirmed dead. When the failure detector thread
                    // finds them alive again, we'll go ahead and get back to the business at hand.
                    if self.server.member_list.pingable(&member)
                       && !self.server.member_list.persistent_and_confirmed(&member)
                    {
                        let rumors = self.server.rumor_heat.currently_hot_rumors(&member.id);
                        if !rumors.is_empty() {
                            let sc = self.server.clone();

                            let guard = match thread::Builder::new()
                                .name(String::from("push-worker"))
                                .spawn(move || {
                                    PushWorker::new(sc).send_rumors(&member, &rumors);
                                }) {
                                Ok(guard) => guard,
                                Err(e) => {
                                    error!("Could not spawn thread: {}", e);
                                    continue;
                                }
                            };
                            thread_list.push(guard);
                        }
                    }
                }
                let num_threads = thread_list.len();
                for guard in thread_list.drain(0..num_threads) {
                    let _ = guard.join()
                                 .map_err(|e| error!("Push worker died: {:?}", e));
                }
                if SteadyTime::now() < next_gossip {
                    let wait_time = (next_gossip - SteadyTime::now()).num_milliseconds();
                    if wait_time > 0 {
                        thread::sleep(Duration::from_millis(wait_time as u64));
                    }
                }
            }
            if SteadyTime::now() < long_wait {
                let wait_time = (long_wait - SteadyTime::now()).num_milliseconds();
                if wait_time > 0 {
                    thread::sleep(Duration::from_millis(wait_time as u64));
                }
            }
        }
    }
}

/// A worker thread for pushing messages to a target
struct PushWorker {
    pub server: Server,
}

impl PushWorker {
    /// Create a new PushWorker.
    pub fn new(server: Server) -> PushWorker { PushWorker { server } }

    /// Send the list of rumors to a given member. This method creates an outbound socket and then
    /// closes the connection as soon as we are done sending rumors. ZeroMQ may choose to keep the
    /// connection and socket open for 1 second longer - so it is possible, but unlikely, that this
    /// method can lose messages.
    // If we ever need to modify this function, it would be an excellent opportunity to
    // simplify the redundant aspects and remove this allow(clippy::cognitive_complexity),
    // but changing it in the absence of other necessity seems like too much risk for the
    // expected reward.
    fn send_rumors(&self, member: &Member, rumors: &[RumorKey]) {
        let socket = (**ZMQ_CONTEXT).as_mut()
                                    .socket(zmq::PUSH)
                                    .expect("Failure to create the ZMQ push socket");
        socket.set_linger(1000)
              .expect("Failure to set the ZMQ push socket to not linger");
        socket.set_tcp_keepalive(0)
              .expect("Failure to set the ZMQ push socket to not use keepalive");
        socket.set_immediate(true)
              .expect("Failure to set the ZMQ push socket to immediate");
        socket.set_sndhwm(1000)
              .expect("Failure to set the ZMQ push socket hwm");
        socket.set_sndtimeo(500)
              .expect("Failure to set the ZMQ send timeout");
        let to_addr = format!("{}:{}", member.address, member.gossip_port);
        match socket.connect(&format!("tcp://{}", to_addr)) {
            Ok(()) => debug!("Connected push socket to {:?}", member),
            Err(e) => {
                error!("Cannot connect push socket to {:?}: {:?}", member, e);
                let label_values = &["socket_connect", "failure"];
                GOSSIP_MESSAGES_SENT.with_label_values(label_values).inc();
                GOSSIP_BYTES_SENT.with_label_values(label_values).set(0);
                return;
            }
        }
        'rumorlist: for rumor_key in rumors.iter() {
            let rumor_as_bytes = match rumor_key.kind {
                RumorType::Member => {
                    let send_rumor = match self.create_member_rumor(&rumor_key) {
                        Some(rumor) => rumor,
                        None => continue 'rumorlist,
                    };
                    trace_it!(GOSSIP: &self.server,
                              TraceKind::SendRumor,
                              &member.id,
                              &send_rumor);
                    match send_rumor.encode() {
                        Ok(bytes) => bytes,
                        Err(e) => {
                            error!("Could not write our own rumor to bytes; abandoning sending \
                                    rumor: {:?}",
                                   e);
                            let label_values = &["member_rumor_encode", "failure"];
                            GOSSIP_MESSAGES_SENT.with_label_values(label_values).inc();
                            GOSSIP_BYTES_SENT.with_label_values(label_values).set(0);
                            continue 'rumorlist;
                        }
                    }
                }
                RumorType::Service => {
                    // trace_it!(GOSSIP: &self.server,
                    //           TraceKind::SendRumor,
                    //           &member.id,
                    //           &send_rumor);
                    match self.server
                              .service_store
                              .encode(&rumor_key.key, &rumor_key.id)
                    {
                        Ok(bytes) => bytes,
                        Err(e) => {
                            error!("Could not write our own rumor to bytes; abandoning sending \
                                    rumor: {:?}",
                                   e);
                            let label_values = &["service_rumor_encode", "failure"];
                            GOSSIP_MESSAGES_SENT.with_label_values(label_values).inc();
                            GOSSIP_BYTES_SENT.with_label_values(label_values).set(0);
                            continue 'rumorlist;
                        }
                    }
                }
                RumorType::ServiceConfig => {
                    // trace_it!(GOSSIP: &self.server,
                    //           TraceKind::SendRumor,
                    //           &member.id,
                    //           &send_rumor);
                    match self.server
                              .service_config_store
                              .encode(&rumor_key.key, &rumor_key.id)
                    {
                        Ok(bytes) => bytes,
                        Err(e) => {
                            error!("Could not write our own rumor to bytes; abandoning sending \
                                    rumor: {:?}",
                                   e);
                            let label_values = &["service_config_rumor_encode", "failure"];
                            GOSSIP_MESSAGES_SENT.with_label_values(label_values).inc();
                            GOSSIP_BYTES_SENT.with_label_values(label_values).set(0);
                            continue 'rumorlist;
                        }
                    }
                }
                RumorType::ServiceFile => {
                    // trace_it!(GOSSIP: &self.server,
                    //           TraceKind::SendRumor,
                    //           &member.id,
                    //           &send_rumor);
                    match self.server
                              .service_file_store
                              .encode(&rumor_key.key, &rumor_key.id)
                    {
                        Ok(bytes) => bytes,
                        Err(e) => {
                            error!("Could not write our own rumor to bytes; abandoning sending \
                                    rumor: {:?}",
                                   e);
                            let label_values = &["service_file_rumor_encode", "failure"];
                            GOSSIP_MESSAGES_SENT.with_label_values(label_values).inc();
                            GOSSIP_BYTES_SENT.with_label_values(label_values).set(0);
                            continue 'rumorlist;
                        }
                    }
                }
                RumorType::Departure => {
                    match self.server
                              .departure_store
                              .encode(&rumor_key.key, &rumor_key.id)
                    {
                        Ok(bytes) => bytes,
                        Err(e) => {
                            error!("Could not write our own rumor to bytes; abandoning sending \
                                    rumor: {:?}",
                                   e);
                            let label_values = &["departure_rumor_encode", "failure"];
                            GOSSIP_MESSAGES_SENT.with_label_values(label_values).inc();
                            GOSSIP_BYTES_SENT.with_label_values(label_values).set(0);
                            continue 'rumorlist;
                        }
                    }
                }
                RumorType::Election => {
                    // trace_it!(GOSSIP: &self.server,
                    //           TraceKind::SendRumor,
                    //           &member.id,
                    //           &send_rumor);
                    match self.server
                              .election_store
                              .encode(&rumor_key.key, &rumor_key.id)
                    {
                        Ok(bytes) => bytes,
                        Err(e) => {
                            error!("Could not write our own rumor to bytes; abandoning sending \
                                    rumor: {:?}",
                                   e);
                            let label_values = &["election_rumor_encode", "failure"];
                            GOSSIP_MESSAGES_SENT.with_label_values(label_values).inc();
                            GOSSIP_BYTES_SENT.with_label_values(label_values).set(0);
                            continue 'rumorlist;
                        }
                    }
                }
                RumorType::ElectionUpdate => {
                    match self.server
                              .update_store
                              .encode(&rumor_key.key, &rumor_key.id)
                    {
                        Ok(bytes) => bytes,
                        Err(e) => {
                            error!("Could not write our own rumor to bytes; abandoning sending \
                                    rumor: {:?}",
                                   e);
                            let label_values = &["election_update_rumor_encode", "failure"];
                            GOSSIP_MESSAGES_SENT.with_label_values(label_values).inc();
                            GOSSIP_BYTES_SENT.with_label_values(label_values).set(0);
                            continue 'rumorlist;
                        }
                    }
                }
                RumorType::Fake | RumorType::Fake2 => {
                    debug!("You have fake rumors; how odd!");
                    continue 'rumorlist;
                }
            };
            let rumor_len = rumor_as_bytes.len().to_i64();
            let payload = match self.server.generate_wire(rumor_as_bytes) {
                Ok(payload) => payload,
                Err(e) => {
                    error!("Generating protobuf failed: {}", e);
                    let label_values = &["generate_wire", "failure"];
                    GOSSIP_MESSAGES_SENT.with_label_values(label_values).inc();
                    GOSSIP_BYTES_SENT.with_label_values(label_values)
                                     .set(rumor_len);
                    continue 'rumorlist;
                }
            };
            match socket.send(&payload, 0) {
                Ok(()) => {
                    GOSSIP_MESSAGES_SENT.with_label_values(&[&rumor_key.kind.to_string(),
                                                             "success"])
                                        .inc();
                    GOSSIP_BYTES_SENT.with_label_values(&[&rumor_key.kind.to_string(), "success"])
                                     .set(payload.len().to_i64());
                    debug!("Sent rumor {:?} to {:?}", rumor_key, member);
                }
                Err(e) => {
                    warn!("Could not send rumor to {:?} @ {:?}; ZMQ said: {:?}",
                          member.id, to_addr, e)
                }
            }
        }
        self.server.rumor_heat.cool_rumors(&member.id, &rumors);
    }

    /// Given a rumorkey, creates a protobuf rumor for sharing.
    fn create_member_rumor(&self, rumor_key: &RumorKey) -> Option<RumorEnvelope> {
        let member = self.server.member_list.get_cloned(&rumor_key.key())?;
        let payload = Membership { member,
                                   health: self.server
                                               .member_list
                                               .health_of_by_id(&rumor_key.key())
                                               .unwrap() };
        let rumor = RumorEnvelope { r#type:  RumorType::Member,
                                    from_id: self.server.member_id().to_string(),
                                    kind:    RumorKind::Membership(payload), };
        Some(rumor)
    }
}
<|endoftext|>