<commit_before>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

//! libra_channel provides an mpsc channel which has two ends `libra_channel::Receiver`
//! and `libra_channel::Sender` similar to existing mpsc data structures.
//! What makes it different from existing mpsc channels is that we have full control
//! over how the internal queueing in the channel happens and how we schedule messages
//! to be sent out from this channel.
//! Internally, it uses the `PerKeyQueue` to store messages
use crate::message_queues::{PerKeyQueue, QueueStyle};
use anyhow::{ensure, Result};
use futures::{async_await::FusedStream, channel::oneshot, stream::Stream};
use libra_metrics::IntCounterVec;
use std::{
    fmt::{Debug, Formatter},
    hash::Hash,
    num::NonZeroUsize,
    pin::Pin,
    sync::{Arc, Mutex},
    task::{Context, Poll, Waker},
};

/// SharedState is a data structure private to this module which is
/// shared by the sender and receiver.
#[derive(Debug)]
struct SharedState<K: Eq + Hash + Clone, M> {
    /// The internal queue of messages in this Channel
    internal_queue: PerKeyQueue<K, (M, Option<oneshot::Sender<ElementStatus<M>>>)>,
    /// Waker is needed so that the Sender can notify the task executor/scheduler
    /// that something has been pushed to the internal_queue and it ready for
    /// consumption by the Receiver and then the executor/scheduler will wake up
    /// the Receiver task to process the next item.
    waker: Option<Waker>,

    /// A boolean which tracks whether the receiver has dropped
    receiver_dropped: bool,
    /// A boolean which tracks whether the stream has terminated
    /// A stream is considered terminated when sender has dropped
    /// and we have drained everything inside our internal queue
    stream_terminated: bool,
}

/// The sending end of the libra_channel.
#[derive(Debug)]
pub struct Sender<K: Eq + Hash + Clone, M> {
    shared_state: Arc<Mutex<SharedState<K, M>>>,
}

/// The status of an element inserted into a libra_channel. If the element is successfully
/// dequeued, ElementStatus::Dequeued is sent to the sender. If it is dropped
/// ElementStatus::Dropped is sent to the sender along with the dropped element.
pub enum ElementStatus<M> {
    Dequeued,
    Dropped(M),
}

impl<M: PartialEq> PartialEq for ElementStatus<M> {
    fn eq(&self, other: &ElementStatus<M>) -> bool {
        match (self, other) {
            (ElementStatus::Dequeued, ElementStatus::Dequeued) => true,
            (ElementStatus::Dropped(a), ElementStatus::Dropped(b)) => a.eq(b),
            _ => false,
        }
    }
}

impl<M: Debug> Debug for ElementStatus<M> {
    fn fmt(&self, f: &mut Formatter) -> std::result::Result<(), std::fmt::Error> {
        match self {
            ElementStatus::Dequeued => write!(f, "Dequeued"),
            ElementStatus::Dropped(v) => write!(f, "Dropped({:?})", v),
        }
    }
}

impl<K: Eq + Hash + Clone, M> Sender<K, M> {
    /// This adds the message into the internal queue data structure. This is a
    /// synchronous call.
    pub fn push(&mut self, key: K, message: M) -> Result<()> {
        self.push_with_feedback(key, message, None)
    }

    /// Same as `push`, but this function also accepts a oneshot::Sender over which the sender can
    /// be notified when the message eventually gets delivered or dropped.
    pub fn push_with_feedback(
        &mut self,
        key: K,
        message: M,
        status_ch: Option<oneshot::Sender<ElementStatus<M>>>,
    ) -> Result<()> {
        let mut shared_state = self.shared_state.lock().unwrap();
        ensure!(!shared_state.receiver_dropped, "Channel is closed");
        let dropped = shared_state.internal_queue.push(key, (message, status_ch));
        // If this or an existing message had to be dropped because of the queue being full, we
        // notify the corresponding status channel if it was registered.
        if let Some((dropped_val, Some(dropped_status_ch))) = dropped {
            // Ignore errors.
            let _err = dropped_status_ch.send(ElementStatus::Dropped(dropped_val));
        }
        if let Some(w) = shared_state.waker.take() {
            w.wake();
        }
        Ok(())
    }
}

impl<K: Eq + Hash + Clone, M> Clone for Sender<K, M> {
    fn clone(&self) -> Self {
        Sender {
            shared_state: self.shared_state.clone(),
        }
    }
}

impl<K: Eq + Hash + Clone, M> Drop for Sender<K, M> {
    fn drop(&mut self) {
        let mut shared_state = self.shared_state.lock().unwrap();
        if let Some(w) = shared_state.waker.take() {
            w.wake();
        }
    }
}

/// The receiving end of the libra_channel.
pub struct Receiver<K: Eq + Hash + Clone, M> {
    shared_state: Arc<Mutex<SharedState<K, M>>>,
}

impl<K: Eq + Hash + Clone, M> Receiver<K, M> {
    /// Removes all the previously sent transactions that have not been consumed yet and cleans up
    /// the internal queue structure (GC of the previous keys).
    pub fn clear(&mut self) {
        let mut shared_state = self.shared_state.lock().unwrap();
        shared_state.internal_queue.clear();
    }
}

impl<K: Eq + Hash + Clone, M> Drop for Receiver<K, M> {
    fn drop(&mut self) {
        let mut shared_state = self.shared_state.lock().unwrap();
        shared_state.receiver_dropped = true;
    }
}

impl<K: Eq + Hash + Clone, M> Stream for Receiver<K, M> {
    type Item = M;
    /// poll_next checks whether there is something ready for consumption from the internal
    /// queue. If there is, then it returns immediately. If the internal_queue is empty,
    /// it sets the waker passed to it by the scheduler/executor and returns Pending
    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
        let mut shared_state = self.shared_state.lock().unwrap();
        if let Some((val, status_ch)) = shared_state.internal_queue.pop() {
            if let Some(status_ch) = status_ch {
                let _err = status_ch.send(ElementStatus::Dequeued);
            }
            Poll::Ready(Some(val))
        // if the only Arc reference to `shared_state` is 1, which is this receiver,
        // this must mean all senders have been dropped (and so the stream is terminated)
        } else if Arc::strong_count(&self.shared_state) == 1 {
            shared_state.stream_terminated = true;
            Poll::Ready(None)
        } else {
            shared_state.waker = Some(cx.waker().clone());
            Poll::Pending
        }
    }
}

impl<K: Eq + Hash + Clone, M> FusedStream for Receiver<K, M> {
    fn is_terminated(&self) -> bool {
        self.shared_state.lock().unwrap().stream_terminated
    }
}

/// Create a new Libra Channel and returns the two ends of the channel.
pub fn new<K: Eq + Hash + Clone, M>(
    queue_style: QueueStyle,
    max_queue_size_per_key: NonZeroUsize,
    counters: Option<&'static IntCounterVec>,
) -> (Sender<K, M>, Receiver<K, M>) {
    let shared_state = Arc::new(Mutex::new(SharedState {
        internal_queue: PerKeyQueue::new(queue_style, max_queue_size_per_key, counters),
        waker: None,
        receiver_dropped: false,
        stream_terminated: false,
    }));
    let shared_state_clone = Arc::clone(&shared_state);
    (
        Sender { shared_state },
        Receiver {
            shared_state: shared_state_clone,
        },
    )
}
<commit_msg>[deps] get futures updated<commit_after>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

//! libra_channel provides an mpsc channel which has two ends `libra_channel::Receiver`
//! and `libra_channel::Sender` similar to existing mpsc data structures.
//! What makes it different from existing mpsc channels is that we have full control
//! over how the internal queueing in the channel happens and how we schedule messages
//! to be sent out from this channel.
//! Internally, it uses the `PerKeyQueue` to store messages
use crate::message_queues::{PerKeyQueue, QueueStyle};
use anyhow::{ensure, Result};
use futures::{
    channel::oneshot,
    stream::{FusedStream, Stream},
};
use libra_metrics::IntCounterVec;
use std::{
    fmt::{Debug, Formatter},
    hash::Hash,
    num::NonZeroUsize,
    pin::Pin,
    sync::{Arc, Mutex},
    task::{Context, Poll, Waker},
};

/// SharedState is a data structure private to this module which is
/// shared by the sender and receiver.
#[derive(Debug)]
struct SharedState<K: Eq + Hash + Clone, M> {
    /// The internal queue of messages in this Channel
    internal_queue: PerKeyQueue<K, (M, Option<oneshot::Sender<ElementStatus<M>>>)>,
    /// Waker is needed so that the Sender can notify the task executor/scheduler
    /// that something has been pushed to the internal_queue and it ready for
    /// consumption by the Receiver and then the executor/scheduler will wake up
    /// the Receiver task to process the next item.
    waker: Option<Waker>,

    /// A boolean which tracks whether the receiver has dropped
    receiver_dropped: bool,
    /// A boolean which tracks whether the stream has terminated
    /// A stream is considered terminated when sender has dropped
    /// and we have drained everything inside our internal queue
    stream_terminated: bool,
}

/// The sending end of the libra_channel.
#[derive(Debug)]
pub struct Sender<K: Eq + Hash + Clone, M> {
    shared_state: Arc<Mutex<SharedState<K, M>>>,
}

/// The status of an element inserted into a libra_channel. If the element is successfully
/// dequeued, ElementStatus::Dequeued is sent to the sender. If it is dropped
/// ElementStatus::Dropped is sent to the sender along with the dropped element.
pub enum ElementStatus<M> {
    Dequeued,
    Dropped(M),
}

impl<M: PartialEq> PartialEq for ElementStatus<M> {
    fn eq(&self, other: &ElementStatus<M>) -> bool {
        match (self, other) {
            (ElementStatus::Dequeued, ElementStatus::Dequeued) => true,
            (ElementStatus::Dropped(a), ElementStatus::Dropped(b)) => a.eq(b),
            _ => false,
        }
    }
}

impl<M: Debug> Debug for ElementStatus<M> {
    fn fmt(&self, f: &mut Formatter) -> std::result::Result<(), std::fmt::Error> {
        match self {
            ElementStatus::Dequeued => write!(f, "Dequeued"),
            ElementStatus::Dropped(v) => write!(f, "Dropped({:?})", v),
        }
    }
}

impl<K: Eq + Hash + Clone, M> Sender<K, M> {
    /// This adds the message into the internal queue data structure. This is a
    /// synchronous call.
    pub fn push(&mut self, key: K, message: M) -> Result<()> {
        self.push_with_feedback(key, message, None)
    }

    /// Same as `push`, but this function also accepts a oneshot::Sender over which the sender can
    /// be notified when the message eventually gets delivered or dropped.
    pub fn push_with_feedback(
        &mut self,
        key: K,
        message: M,
        status_ch: Option<oneshot::Sender<ElementStatus<M>>>,
    ) -> Result<()> {
        let mut shared_state = self.shared_state.lock().unwrap();
        ensure!(!shared_state.receiver_dropped, "Channel is closed");
        let dropped = shared_state.internal_queue.push(key, (message, status_ch));
        // If this or an existing message had to be dropped because of the queue being full, we
        // notify the corresponding status channel if it was registered.
        if let Some((dropped_val, Some(dropped_status_ch))) = dropped {
            // Ignore errors.
            let _err = dropped_status_ch.send(ElementStatus::Dropped(dropped_val));
        }
        if let Some(w) = shared_state.waker.take() {
            w.wake();
        }
        Ok(())
    }
}

impl<K: Eq + Hash + Clone, M> Clone for Sender<K, M> {
    fn clone(&self) -> Self {
        Sender {
            shared_state: self.shared_state.clone(),
        }
    }
}

impl<K: Eq + Hash + Clone, M> Drop for Sender<K, M> {
    fn drop(&mut self) {
        let mut shared_state = self.shared_state.lock().unwrap();
        if let Some(w) = shared_state.waker.take() {
            w.wake();
        }
    }
}

/// The receiving end of the libra_channel.
pub struct Receiver<K: Eq + Hash + Clone, M> {
    shared_state: Arc<Mutex<SharedState<K, M>>>,
}

impl<K: Eq + Hash + Clone, M> Receiver<K, M> {
    /// Removes all the previously sent transactions that have not been consumed yet and cleans up
    /// the internal queue structure (GC of the previous keys).
    pub fn clear(&mut self) {
        let mut shared_state = self.shared_state.lock().unwrap();
        shared_state.internal_queue.clear();
    }
}

impl<K: Eq + Hash + Clone, M> Drop for Receiver<K, M> {
    fn drop(&mut self) {
        let mut shared_state = self.shared_state.lock().unwrap();
        shared_state.receiver_dropped = true;
    }
}

impl<K: Eq + Hash + Clone, M> Stream for Receiver<K, M> {
    type Item = M;
    /// poll_next checks whether there is something ready for consumption from the internal
    /// queue. If there is, then it returns immediately. If the internal_queue is empty,
    /// it sets the waker passed to it by the scheduler/executor and returns Pending
    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
        let mut shared_state = self.shared_state.lock().unwrap();
        if let Some((val, status_ch)) = shared_state.internal_queue.pop() {
            if let Some(status_ch) = status_ch {
                let _err = status_ch.send(ElementStatus::Dequeued);
            }
            Poll::Ready(Some(val))
        // if the only Arc reference to `shared_state` is 1, which is this receiver,
        // this must mean all senders have been dropped (and so the stream is terminated)
        } else if Arc::strong_count(&self.shared_state) == 1 {
            shared_state.stream_terminated = true;
            Poll::Ready(None)
        } else {
            shared_state.waker = Some(cx.waker().clone());
            Poll::Pending
        }
    }
}

impl<K: Eq + Hash + Clone, M> FusedStream for Receiver<K, M> {
    fn is_terminated(&self) -> bool {
        self.shared_state.lock().unwrap().stream_terminated
    }
}

/// Create a new Libra Channel and returns the two ends of the channel.
pub fn new<K: Eq + Hash + Clone, M>(
    queue_style: QueueStyle,
    max_queue_size_per_key: NonZeroUsize,
    counters: Option<&'static IntCounterVec>,
) -> (Sender<K, M>, Receiver<K, M>) {
    let shared_state = Arc::new(Mutex::new(SharedState {
        internal_queue: PerKeyQueue::new(queue_style, max_queue_size_per_key, counters),
        waker: None,
        receiver_dropped: false,
        stream_terminated: false,
    }));
    let shared_state_clone = Arc::clone(&shared_state);
    (
        Sender { shared_state },
        Receiver {
            shared_state: shared_state_clone,
        },
    )
}
<|endoftext|>


<commit_before>use std::marker::PhantomData;

use rand::distributions::IndependentSample;
use rand::distributions::Normal;
use rand::{thread_rng,Rng};
use num::Float;

use alg::{Vector,Matrix};
use ml::Optimizer;

/// Covariance Matrix Adaptation Evolution Strategy is a general purpose
/// black-box optimization algorithm.
pub struct CmaEs<T: Float> {
    // population size
    pop: usize,
    // Dimension of the input vector
    n: usize,
    // Steps
    t: usize,

    phantom: PhantomData<T>,
}

impl <T: Float> CmaEs<T> {
    /// Creates a new CMA-ES optimizer.
    ///
    /// - `n`: dimension of the function input vector
    /// - `pop`: size of the population.
    /// - `t`: number of generations to simulate.
    pub fn new(n: usize, pop: usize, t: usize) -> Self {
        CmaEs {
            n: n,
            pop: pop,
            t: t,
            phantom: PhantomData,
        }
    }
}

impl <T: Float> Optimizer for CmaEs<T> {
    type Input = Vector<T>;
    type Score = T;

    fn optimize<F>(&self, f: F) -> Vector<T>
        where F: Fn(Vector<T>) -> T
    {
        CmaEsSlave::new(self.n, self.pop).optimize(f, self.t)
    }
}

struct CmaEsSlave<T: Float> {
    // These are stable
    n: usize,
    pop: usize,
    weights: Vec<T>,

    // These vary
    population: Matrix<T>,
    covariance: Matrix<T>,
    mean: Vector<T>,
}

impl <T: Float> CmaEsSlave<T> {

    fn new(n: usize, pop: usize) -> Self {
        CmaEsSlave {
            n: n,
            pop: pop,
            weights: Vec::new(),

            population: Matrix::zero(pop, n),
            covariance: Matrix::zero(n,n),
            mean: Vector::zero(n),
        }
    }

    fn optimize<F>(mut self, f: F, t: usize) -> Vector<T>
        where F: Fn(Vector<T>) -> T
    {
        self.compute_weights();

        for _ in 0..t {
            // Generate population
            self.generate_population();

            // Adapt covariance and mean
            self.adapt_covariance(&f);
        }

        self.mean
    }

    fn compute_weights(&mut self) {
        let mu = self.n / 2;
        self.weights.reserve(mu);

        let mut sum = T::zero();
        let logmu = T::from(mu as f64 + 0.5).unwrap().log10();
        for i in 0..mu {
            let w = logmu - T::from(i+1).unwrap().log10();
            self.weights.push(w);
            sum = sum + w;
        }

        for w in self.weights.iter_mut() {
            *w = *w / sum;
        }
    }

    fn adapt_covariance<F>(&mut self, f: &F)
        where F: Fn(Vector<T>) -> T
    {
        let scores: Vec<T> = (0..self.pop).map(|i| self.population.row(i)).map(|sample| f(sample)).collect();
        // Maybe sort the population by its fitness?

        // The mean is easy...
        let mean = (0..self.pop)
            .map(|i| self.population.row(i))
            .zip(self.weights.iter())
            .fold(Vector::zero(self.n), |a,(b,&w)| a+b*w);
        let mean = mean / T::from(self.pop).unwrap();
        self.mean = mean;

        // Now, for the covariance...
    }

    fn generate_population(&mut self) {
        // Lower triangular cholesky decomposition
        let L = self.covariance.cholesky();

        let mut r = thread_rng();
        let d = Normal::new(0f64, 1f64);

        for i in 0..self.pop {
            self.population.set_col(i, CmaEsSlave::generate_sample(&self.mean, &L, &mut r, &d));
        }
    }

    fn generate_sample<R: Rng, D: IndependentSample<f64>>(mean: &Vector<T>, L: &Matrix<T>, r: &mut R, d: &D) -> Vector<T> {
        // Multivariate sampling from covariance matrix
        let normal = Vector::new(mean.dim(), |_| T::from(d.ind_sample(r)).unwrap());
        mean + L * normal
    }
}
<commit_msg>Add sigma to CMA-ES<commit_after>use std::marker::PhantomData;

use rand::distributions::IndependentSample;
use rand::distributions::Normal;
use rand::{thread_rng,Rng};
use num::Float;

use alg::{Vector,Matrix};
use ml::Optimizer;

/// Covariance Matrix Adaptation Evolution Strategy is a general purpose
/// black-box optimization algorithm.
pub struct CmaEs<T: Float> {
    // population size
    pop: usize,
    // Dimension of the input vector
    n: usize,
    // Steps
    t: usize,

    phantom: PhantomData<T>,
}

impl <T: Float> CmaEs<T> {
    /// Creates a new CMA-ES optimizer.
    ///
    /// - `n`: dimension of the function input vector
    /// - `pop`: size of the population.
    /// - `t`: number of generations to simulate.
    pub fn new(n: usize, pop: usize, t: usize) -> Self {
        CmaEs {
            n: n,
            pop: pop,
            t: t,
            phantom: PhantomData,
        }
    }
}

impl <T: Float> Optimizer for CmaEs<T> {
    type Input = Vector<T>;
    type Score = T;

    fn optimize<F>(&self, f: F) -> Vector<T>
        where F: Fn(Vector<T>) -> T
    {
        CmaEsSlave::new(self.n, self.pop).optimize(f, self.t)
    }
}

struct CmaEsSlave<T: Float> {
    // These are stable
    n: usize,
    pop: usize,
    weights: Vec<T>,

    sigma: T,

    // These vary
    population: Matrix<T>,
    covariance: Matrix<T>,
    mean: Vector<T>,
}

impl <T: Float> CmaEsSlave<T> {

    fn new(n: usize, pop: usize) -> Self {
        CmaEsSlave {
            n: n,
            pop: pop,
            weights: Vec::new(),
            sigma: T::one(),

            population: Matrix::zero(pop, n),
            covariance: Matrix::identity(n),
            mean: Vector::zero(n),
        }
    }

    fn optimize<F>(mut self, f: F, t: usize) -> Vector<T>
        where F: Fn(Vector<T>) -> T
    {
        self.compute_weights();

        for _ in 0..t {
            // Generate population
            self.generate_population();

            // Adapt covariance and mean
            self.adapt_covariance(&f);
        }

        self.mean
    }

    fn compute_weights(&mut self) {
        let mu = self.n / 2;
        self.weights.reserve(mu);

        let mut sum = T::zero();
        let logmu = T::from(mu as f64 + 0.5).unwrap().log10();
        for i in 0..mu {
            let w = logmu - T::from(i+1).unwrap().log10();
            self.weights.push(w);
            sum = sum + w;
        }

        for w in self.weights.iter_mut() {
            *w = *w / sum;
        }
    }

    fn adapt_covariance<F>(&mut self, f: &F)
        where F: Fn(Vector<T>) -> T
    {
        let scores: Vec<T> = (0..self.pop).map(|i| self.population.row(i)).map(|sample| f(sample)).collect();
        // Maybe sort the population by its fitness?

        // The mean is easy...
        let mean = (0..self.pop)
            .map(|i| self.population.row(i))
            .zip(self.weights.iter())
            .fold(Vector::zero(self.n), |a,(b,&w)| a+b*w);
        let mean = mean / T::from(self.pop).unwrap();
        self.mean = mean;

        // Now, for the covariance...

    }

    fn generate_population(&mut self) {
        // Lower triangular cholesky decomposition
        let L = self.covariance.cholesky();

        let mut r = thread_rng();
        let d = Normal::new(0f64, 1f64);

        for i in 0..self.pop {
            self.population.set_col(i, CmaEsSlave::generate_sample(&self.mean, self.sigma, &L, &mut r, &d));
        }
    }

    fn generate_sample<R: Rng, D: IndependentSample<f64>>(mean: &Vector<T>, sigma: T, L: &Matrix<T>, r: &mut R, d: &D) -> Vector<T> {
        // Multivariate sampling from covariance matrix
        let normal = Vector::new(mean.dim(), |_| T::from(d.ind_sample(r)).unwrap());
        mean + (L * normal) * sigma
    }
}
<|endoftext|>


<commit_before>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

use admission_control_service::admission_control_service::AdmissionControlService;
use consensus::consensus_provider::{make_consensus_provider, ConsensusProvider};
use debug_interface::{
    node_debug_service::NodeDebugService,
    proto::node_debug_interface_server::NodeDebugInterfaceServer,
};
use executor::Executor;
use futures::{channel::mpsc::channel, executor::block_on};
use libra_config::config::{NetworkConfig, NodeConfig, RoleType};
use libra_json_rpc::bootstrap_from_config as bootstrap_rpc;
use libra_logger::prelude::*;
use libra_metrics::metric_server;
use network::validator_network::network_builder::{NetworkBuilder, TransportType};
use state_synchronizer::StateSynchronizer;
use std::{collections::HashMap, net::ToSocketAddrs, sync::Arc, thread, time::Instant};
use storage_client::{StorageReadServiceClient, StorageWriteServiceClient};
use storage_service::start_storage_service;
use tokio::runtime::{Builder, Runtime};
use vm_runtime::LibraVM;

const AC_SMP_CHANNEL_BUFFER_SIZE: usize = 1_024;
const INTRA_NODE_CHANNEL_BUFFER_SIZE: usize = 1;

pub struct LibraHandle {
    _ac: Runtime,
    _rpc: Runtime,
    _mempool: Runtime,
    _state_synchronizer: StateSynchronizer,
    _network_runtimes: Vec<Runtime>,
    consensus: Option<Box<dyn ConsensusProvider>>,
    _storage: Runtime,
    _debug: Runtime,
}

impl Drop for LibraHandle {
    fn drop(&mut self) {
        if let Some(consensus) = &mut self.consensus {
            consensus.stop();
        }
    }
}

fn setup_executor(config: &NodeConfig) -> Arc<Executor<LibraVM>> {
    let storage_read_client = Arc::new(StorageReadServiceClient::new(&config.storage.address));
    let storage_write_client = Arc::new(StorageWriteServiceClient::new(&config.storage.address));

    Arc::new(Executor::new(
        storage_read_client,
        storage_write_client,
        config,
    ))
}

fn setup_debug_interface(config: &NodeConfig) -> Runtime {
    let rt = tokio::runtime::Runtime::new().unwrap();
    let addr = format!(
        "{}:{}",
        config.debug_interface.address, config.debug_interface.admission_control_node_debug_port,
    )
    .to_socket_addrs()
    .unwrap()
    .next()
    .unwrap();
    rt.spawn(
        tonic::transport::Server::builder()
            .add_service(NodeDebugInterfaceServer::new(NodeDebugService::new()))
            .serve(addr),
    );
    rt
}

// TODO(abhayb): Move to network crate (similar to consensus).
pub fn setup_network(config: &mut NetworkConfig, role: RoleType) -> (Runtime, NetworkBuilder) {
    let runtime = Builder::new()
        .thread_name("network-")
        .threaded_scheduler()
        .enable_all()
        .build()
        .expect("Failed to start runtime. Won't be able to start networking.");
    let mut network_builder = NetworkBuilder::new(
        runtime.handle().clone(),
        config.peer_id,
        config.listen_address.clone(),
        role,
    );
    network_builder
        .enable_remote_authentication(config.enable_remote_authentication)
        .advertised_address(config.advertised_address.clone())
        .add_connection_monitoring();
    if config.enable_remote_authentication {
        // If the node wants to run in permissioned mode, it should also have authentication and
        // encryption.
        assert!(
            config.enable_noise,
            "Permissioned network end-points should use authentication"
        );
        let seed_peers = config.seed_peers.seed_peers.clone();
        let network_keypairs = config
            .network_keypairs
            .as_mut()
            .expect("Network keypairs are not defined");
        let signing_keys = &mut network_keypairs.signing_keys;
        let identity_keys = &mut network_keypairs.identity_keys;

        let signing_private = signing_keys
            .take_private()
            .expect("Failed to take Network signing private key, key absent or already read");
        let signing_public = signing_keys.public().clone();
        let identity_private = identity_keys
            .take_private()
            .expect("Failed to take Network identity private key, key absent or already read");
        let identity_public = identity_keys.public().clone();
        let trusted_peers = if role == RoleType::Validator {
            // for validators, trusted_peers is empty will be populated from consensus
            HashMap::new()
        } else {
            config.network_peers.peers.clone()
        };
        network_builder
            .transport(TransportType::TcpNoise(Some((
                identity_private,
                identity_public,
            ))))
            .connectivity_check_interval_ms(config.connectivity_check_interval_ms)
            .seed_peers(seed_peers)
            .trusted_peers(trusted_peers)
            .signing_keys((signing_private, signing_public))
            .discovery_interval_ms(config.discovery_interval_ms)
            .add_discovery();
    } else if config.enable_noise {
        let identity_keys = &mut config
            .network_keypairs
            .as_mut()
            .expect("Network keypairs are not defined")
            .identity_keys;
        let identity_private = identity_keys
            .take_private()
            .expect("Failed to take Network identity private key, key absent or already read");
        let identity_public = identity_keys.public().clone();
        // Even if a network end-point operates without remote authentication, it might want to prove
        // its identity to another peer it connects to. For this, we use TCP + Noise but without
        // enforcing a trusted peers set.
        network_builder.transport(TransportType::TcpNoise(Some((
            identity_private,
            identity_public,
        ))));
    } else {
        network_builder.transport(TransportType::Tcp);
    }
    (runtime, network_builder)
}

pub fn setup_environment(node_config: &mut NodeConfig) -> LibraHandle {
    crash_handler::setup_panic_handler();

    // Some of our code uses the rayon global thread pool. Name the rayon threads so it doesn't
    // cause confusion, otherwise the threads would have their parent's name.
    rayon::ThreadPoolBuilder::new()
        .thread_name(|index| format!("rayon-global-{}", index))
        .build_global()
        .expect("Building rayon global thread pool should work.");

    let mut instant = Instant::now();
    let storage = start_storage_service(&node_config);
    debug!(
        "Storage service started in {} ms",
        instant.elapsed().as_millis()
    );

    instant = Instant::now();
    let executor = setup_executor(&node_config);
    debug!("Executor setup in {} ms", instant.elapsed().as_millis());
    let mut network_runtimes = vec![];
    let mut state_sync_network_handles = vec![];
    let mut mempool_network_handles = vec![];
    let mut validator_network_provider = None;

    if let Some(network) = node_config.validator_network.as_mut() {
        let (runtime, mut network_builder) = setup_network(network, RoleType::Validator);
        state_sync_network_handles.push(state_synchronizer::network::add_to_network(
            &mut network_builder,
        ));

        let (mempool_sender, mempool_events) =
            libra_mempool::network::add_to_network(&mut network_builder);
        mempool_network_handles.push((network.peer_id, mempool_sender, mempool_events));
        validator_network_provider = Some((network.peer_id, runtime, network_builder));
    }

    for i in 0..node_config.full_node_networks.len() {
        let (runtime, mut network_builder) =
            setup_network(&mut node_config.full_node_networks[i], RoleType::FullNode);
        state_sync_network_handles.push(state_synchronizer::network::add_to_network(
            &mut network_builder,
        ));

        let (mempool_sender, mempool_events) =
            libra_mempool::network::add_to_network(&mut network_builder);
        mempool_network_handles.push((
            node_config.full_node_networks[i].peer_id,
            mempool_sender,
            mempool_events,
        ));

        let network = &node_config.full_node_networks[i];
        // Start the network provider.
        let _listen_addr = network_builder.build();
        network_runtimes.push(runtime);
        debug!("Network started for peer_id: {}", network.peer_id);
    }

    let debug_if = setup_debug_interface(&node_config);

    let metrics_port = node_config.debug_interface.metrics_server_port;
    let metric_host = node_config.debug_interface.address.clone();
    thread::spawn(move || metric_server::start_server(metric_host, metrics_port, false));
    let public_metrics_port = node_config.debug_interface.public_metrics_server_port;
    let public_metric_host = node_config.debug_interface.address.clone();
    thread::spawn(move || {
        metric_server::start_server(public_metric_host, public_metrics_port, true)
    });

    // for state sync to send requests to mempool
    let (state_sync_to_mempool_sender, state_sync_requests) =
        channel(INTRA_NODE_CHANNEL_BUFFER_SIZE);
    let state_synchronizer = StateSynchronizer::bootstrap(
        state_sync_network_handles,
        state_sync_to_mempool_sender,
        Arc::clone(&executor),
        &node_config,
    );
    let (ac_sender, client_events) = channel(AC_SMP_CHANNEL_BUFFER_SIZE);
    let admission_control_runtime = AdmissionControlService::bootstrap(&node_config, ac_sender);

    let rpc_runtime = bootstrap_rpc(&node_config);

    let mut consensus = None;
    let (consensus_to_mempool_sender, consensus_requests) = channel(INTRA_NODE_CHANNEL_BUFFER_SIZE);

    instant = Instant::now();
    let mempool = libra_mempool::bootstrap(
        node_config,
        mempool_network_handles,
        client_events,
        consensus_requests,
        state_sync_requests,
    );
    debug!("Mempool started in {} ms", instant.elapsed().as_millis());

    if let Some((peer_id, runtime, mut network_builder)) = validator_network_provider {
        // Note: We need to start network provider before consensus, because the consensus
        // initialization is blocked on state synchronizer to sync to the initial root ledger
        // info, which in turn cannot make progress before network initialization
        // because the NewPeer events which state synchronizer uses to know its
        // peers are delivered by network provider. If we were to start network
        // provider after consensus, we create a cyclic dependency from
        // network provider -> consensus -> state synchronizer -> network provider. This deadlock
        // was observed in GitHub Issue #749. A long term fix might be make
        // consensus initialization async instead of blocking on state synchronizer.
        let (consensus_network_sender, consensus_network_events) =
            consensus::network_interface::add_to_network(&mut network_builder);
        let _listen_addr = network_builder.build();
        network_runtimes.push(runtime);
        debug!("Network started for peer_id: {}", peer_id);

        // Make sure that state synchronizer is caught up at least to its waypoint
        // (in case it's present). There is no sense to start consensus prior to that.
        // TODO: Note that we need the networking layer to be able to discover & connect to the
        // peers with potentially outdated network identity public keys.
        debug!("Wait until state synchronizer is initialized");
        block_on(state_synchronizer.wait_until_initialized())
            .expect("State synchronizer initialization failure");
        debug!("State synchronizer initialization complete.");

        // Initialize and start consensus.
        instant = Instant::now();
        let mut consensus_provider = make_consensus_provider(
            node_config,
            consensus_network_sender,
            consensus_network_events,
            executor,
            state_synchronizer.create_client(),
            consensus_to_mempool_sender,
        );
        consensus_provider
            .start()
            .expect("Failed to start consensus. Can't proceed.");
        consensus = Some(consensus_provider);
        debug!("Consensus started in {} ms", instant.elapsed().as_millis());
    }

    LibraHandle {
        _network_runtimes: network_runtimes,
        _ac: admission_control_runtime,
        _rpc: rpc_runtime,
        _mempool: mempool,
        _state_synchronizer: state_synchronizer,
        consensus,
        _storage: storage,
        _debug: debug_if,
    }
}
<commit_msg>[libra-node][easy] refactor of loop<commit_after>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

use admission_control_service::admission_control_service::AdmissionControlService;
use consensus::consensus_provider::{make_consensus_provider, ConsensusProvider};
use debug_interface::{
    node_debug_service::NodeDebugService,
    proto::node_debug_interface_server::NodeDebugInterfaceServer,
};
use executor::Executor;
use futures::{channel::mpsc::channel, executor::block_on};
use libra_config::config::{NetworkConfig, NodeConfig, RoleType};
use libra_json_rpc::bootstrap_from_config as bootstrap_rpc;
use libra_logger::prelude::*;
use libra_metrics::metric_server;
use network::validator_network::network_builder::{NetworkBuilder, TransportType};
use state_synchronizer::StateSynchronizer;
use std::{collections::HashMap, net::ToSocketAddrs, sync::Arc, thread, time::Instant};
use storage_client::{StorageReadServiceClient, StorageWriteServiceClient};
use storage_service::start_storage_service;
use tokio::runtime::{Builder, Runtime};
use vm_runtime::LibraVM;

const AC_SMP_CHANNEL_BUFFER_SIZE: usize = 1_024;
const INTRA_NODE_CHANNEL_BUFFER_SIZE: usize = 1;

pub struct LibraHandle {
    _ac: Runtime,
    _rpc: Runtime,
    _mempool: Runtime,
    _state_synchronizer: StateSynchronizer,
    _network_runtimes: Vec<Runtime>,
    consensus: Option<Box<dyn ConsensusProvider>>,
    _storage: Runtime,
    _debug: Runtime,
}

impl Drop for LibraHandle {
    fn drop(&mut self) {
        if let Some(consensus) = &mut self.consensus {
            consensus.stop();
        }
    }
}

fn setup_executor(config: &NodeConfig) -> Arc<Executor<LibraVM>> {
    let storage_read_client = Arc::new(StorageReadServiceClient::new(&config.storage.address));
    let storage_write_client = Arc::new(StorageWriteServiceClient::new(&config.storage.address));

    Arc::new(Executor::new(
        storage_read_client,
        storage_write_client,
        config,
    ))
}

fn setup_debug_interface(config: &NodeConfig) -> Runtime {
    let rt = tokio::runtime::Runtime::new().unwrap();
    let addr = format!(
        "{}:{}",
        config.debug_interface.address, config.debug_interface.admission_control_node_debug_port,
    )
    .to_socket_addrs()
    .unwrap()
    .next()
    .unwrap();
    rt.spawn(
        tonic::transport::Server::builder()
            .add_service(NodeDebugInterfaceServer::new(NodeDebugService::new()))
            .serve(addr),
    );
    rt
}

// TODO(abhayb): Move to network crate (similar to consensus).
pub fn setup_network(config: &mut NetworkConfig, role: RoleType) -> (Runtime, NetworkBuilder) {
    let runtime = Builder::new()
        .thread_name("network-")
        .threaded_scheduler()
        .enable_all()
        .build()
        .expect("Failed to start runtime. Won't be able to start networking.");
    let mut network_builder = NetworkBuilder::new(
        runtime.handle().clone(),
        config.peer_id,
        config.listen_address.clone(),
        role,
    );
    network_builder
        .enable_remote_authentication(config.enable_remote_authentication)
        .advertised_address(config.advertised_address.clone())
        .add_connection_monitoring();
    if config.enable_remote_authentication {
        // If the node wants to run in permissioned mode, it should also have authentication and
        // encryption.
        assert!(
            config.enable_noise,
            "Permissioned network end-points should use authentication"
        );
        let seed_peers = config.seed_peers.seed_peers.clone();
        let network_keypairs = config
            .network_keypairs
            .as_mut()
            .expect("Network keypairs are not defined");
        let signing_keys = &mut network_keypairs.signing_keys;
        let identity_keys = &mut network_keypairs.identity_keys;

        let signing_private = signing_keys
            .take_private()
            .expect("Failed to take Network signing private key, key absent or already read");
        let signing_public = signing_keys.public().clone();
        let identity_private = identity_keys
            .take_private()
            .expect("Failed to take Network identity private key, key absent or already read");
        let identity_public = identity_keys.public().clone();
        let trusted_peers = if role == RoleType::Validator {
            // for validators, trusted_peers is empty will be populated from consensus
            HashMap::new()
        } else {
            config.network_peers.peers.clone()
        };
        network_builder
            .transport(TransportType::TcpNoise(Some((
                identity_private,
                identity_public,
            ))))
            .connectivity_check_interval_ms(config.connectivity_check_interval_ms)
            .seed_peers(seed_peers)
            .trusted_peers(trusted_peers)
            .signing_keys((signing_private, signing_public))
            .discovery_interval_ms(config.discovery_interval_ms)
            .add_discovery();
    } else if config.enable_noise {
        let identity_keys = &mut config
            .network_keypairs
            .as_mut()
            .expect("Network keypairs are not defined")
            .identity_keys;
        let identity_private = identity_keys
            .take_private()
            .expect("Failed to take Network identity private key, key absent or already read");
        let identity_public = identity_keys.public().clone();
        // Even if a network end-point operates without remote authentication, it might want to prove
        // its identity to another peer it connects to. For this, we use TCP + Noise but without
        // enforcing a trusted peers set.
        network_builder.transport(TransportType::TcpNoise(Some((
            identity_private,
            identity_public,
        ))));
    } else {
        network_builder.transport(TransportType::Tcp);
    }
    (runtime, network_builder)
}

pub fn setup_environment(node_config: &mut NodeConfig) -> LibraHandle {
    crash_handler::setup_panic_handler();

    // Some of our code uses the rayon global thread pool. Name the rayon threads so it doesn't
    // cause confusion, otherwise the threads would have their parent's name.
    rayon::ThreadPoolBuilder::new()
        .thread_name(|index| format!("rayon-global-{}", index))
        .build_global()
        .expect("Building rayon global thread pool should work.");

    let mut instant = Instant::now();
    let storage = start_storage_service(&node_config);
    debug!(
        "Storage service started in {} ms",
        instant.elapsed().as_millis()
    );

    instant = Instant::now();
    let executor = setup_executor(&node_config);
    debug!("Executor setup in {} ms", instant.elapsed().as_millis());
    let mut network_runtimes = vec![];
    let mut state_sync_network_handles = vec![];
    let mut mempool_network_handles = vec![];
    let mut validator_network_provider = None;

    if let Some(network) = node_config.validator_network.as_mut() {
        let (runtime, mut network_builder) = setup_network(network, RoleType::Validator);
        state_sync_network_handles.push(state_synchronizer::network::add_to_network(
            &mut network_builder,
        ));

        let (mempool_sender, mempool_events) =
            libra_mempool::network::add_to_network(&mut network_builder);
        mempool_network_handles.push((network.peer_id, mempool_sender, mempool_events));
        validator_network_provider = Some((network.peer_id, runtime, network_builder));
    }

    for mut full_node_network in node_config.full_node_networks.iter_mut() {
        let (runtime, mut network_builder) =
            setup_network(&mut full_node_network, RoleType::FullNode);

        network_runtimes.push(runtime);
        state_sync_network_handles.push(state_synchronizer::network::add_to_network(
            &mut network_builder,
        ));
        let (mempool_sender, mempool_events) =
            libra_mempool::network::add_to_network(&mut network_builder);
        mempool_network_handles.push((full_node_network.peer_id, mempool_sender, mempool_events));

        // Start the network provider.
        let _listen_addr = network_builder.build();
        debug!("Network started for peer_id: {}", full_node_network.peer_id);
    }

    let debug_if = setup_debug_interface(&node_config);

    let metrics_port = node_config.debug_interface.metrics_server_port;
    let metric_host = node_config.debug_interface.address.clone();
    thread::spawn(move || metric_server::start_server(metric_host, metrics_port, false));
    let public_metrics_port = node_config.debug_interface.public_metrics_server_port;
    let public_metric_host = node_config.debug_interface.address.clone();
    thread::spawn(move || {
        metric_server::start_server(public_metric_host, public_metrics_port, true)
    });

    // for state sync to send requests to mempool
    let (state_sync_to_mempool_sender, state_sync_requests) =
        channel(INTRA_NODE_CHANNEL_BUFFER_SIZE);
    let state_synchronizer = StateSynchronizer::bootstrap(
        state_sync_network_handles,
        state_sync_to_mempool_sender,
        Arc::clone(&executor),
        &node_config,
    );
    let (ac_sender, client_events) = channel(AC_SMP_CHANNEL_BUFFER_SIZE);
    let admission_control_runtime = AdmissionControlService::bootstrap(&node_config, ac_sender);

    let rpc_runtime = bootstrap_rpc(&node_config);

    let mut consensus = None;
    let (consensus_to_mempool_sender, consensus_requests) = channel(INTRA_NODE_CHANNEL_BUFFER_SIZE);

    instant = Instant::now();
    let mempool = libra_mempool::bootstrap(
        node_config,
        mempool_network_handles,
        client_events,
        consensus_requests,
        state_sync_requests,
    );
    debug!("Mempool started in {} ms", instant.elapsed().as_millis());

    if let Some((peer_id, runtime, mut network_builder)) = validator_network_provider {
        // Note: We need to start network provider before consensus, because the consensus
        // initialization is blocked on state synchronizer to sync to the initial root ledger
        // info, which in turn cannot make progress before network initialization
        // because the NewPeer events which state synchronizer uses to know its
        // peers are delivered by network provider. If we were to start network
        // provider after consensus, we create a cyclic dependency from
        // network provider -> consensus -> state synchronizer -> network provider. This deadlock
        // was observed in GitHub Issue #749. A long term fix might be make
        // consensus initialization async instead of blocking on state synchronizer.
        let (consensus_network_sender, consensus_network_events) =
            consensus::network_interface::add_to_network(&mut network_builder);
        let _listen_addr = network_builder.build();
        network_runtimes.push(runtime);
        debug!("Network started for peer_id: {}", peer_id);

        // Make sure that state synchronizer is caught up at least to its waypoint
        // (in case it's present). There is no sense to start consensus prior to that.
        // TODO: Note that we need the networking layer to be able to discover & connect to the
        // peers with potentially outdated network identity public keys.
        debug!("Wait until state synchronizer is initialized");
        block_on(state_synchronizer.wait_until_initialized())
            .expect("State synchronizer initialization failure");
        debug!("State synchronizer initialization complete.");

        // Initialize and start consensus.
        instant = Instant::now();
        let mut consensus_provider = make_consensus_provider(
            node_config,
            consensus_network_sender,
            consensus_network_events,
            executor,
            state_synchronizer.create_client(),
            consensus_to_mempool_sender,
        );
        consensus_provider
            .start()
            .expect("Failed to start consensus. Can't proceed.");
        consensus = Some(consensus_provider);
        debug!("Consensus started in {} ms", instant.elapsed().as_millis());
    }

    LibraHandle {
        _network_runtimes: network_runtimes,
        _ac: admission_control_runtime,
        _rpc: rpc_runtime,
        _mempool: mempool,
        _state_synchronizer: state_synchronizer,
        consensus,
        _storage: storage,
        _debug: debug_if,
    }
}
<|endoftext|>


<commit_before>extern crate fs_extra;
extern crate rebuild;
extern crate site;
extern crate tempfile;

use std::env;
use std::fs::{self, File};
use std::io::prelude::*;

use fs_extra::dir;
use site::Site;
use tempfile::tempdir;

use rebuild::{after_content_change, after_content_rename};

// Loads the test_site in a tempdir and build it there
// Returns (site_path_in_tempdir, site)
macro_rules! load_and_build_site {
    ($tmp_dir: expr) => {{
        let mut path =
            env::current_dir().unwrap().parent().unwrap().parent().unwrap().to_path_buf();
        path.push("test_site");
        let mut options = dir::CopyOptions::new();
        options.copy_inside = true;
        dir::copy(&path, &$tmp_dir, &options).unwrap();

        let site_path = $tmp_dir.path().join("test_site");
        let mut site = Site::new(&site_path, "config.toml").unwrap();
        site.load().unwrap();
        let public = &site_path.join("public");
        site.set_output_path(&public);
        site.build().unwrap();

        (site_path, site)
    }};
}

/// Replace the file at the path (starting from root) by the given content
/// and return the file path that was modified
macro_rules! edit_file {
    ($site_path: expr, $path: expr, $content: expr) => {{
        let mut t = $site_path.clone();
        for c in $path.split('/') {
            t.push(c);
        }
        let mut file = File::create(&t).expect("Could not open/create file");
        file.write_all($content).expect("Could not write to the file");
        t
    }};
}

macro_rules! file_contains {
    ($site_path: expr, $path: expr, $text: expr) => {{
        let mut path = $site_path.clone();
        for component in $path.split("/") {
            path.push(component);
        }
        let mut file = File::open(&path).unwrap();
        let mut s = String::new();
        file.read_to_string(&mut s).unwrap();
        println!("{:?} -> {}", path, s);
        s.contains($text)
    }};
}

/// Rename a file or a folder to the new given name
macro_rules! rename {
    ($site_path: expr, $path: expr, $new_name: expr) => {{
        let mut t = $site_path.clone();
        for c in $path.split('/') {
            t.push(c);
        }
        let mut new_path = t.parent().unwrap().to_path_buf();
        new_path.push($new_name);
        fs::rename(&t, &new_path).unwrap();
        println!("Renamed {:?} to {:?}", t, new_path);
        (t, new_path)
    }};
}

#[test]
fn can_rebuild_after_simple_change_to_page_content() {
    let tmp_dir = tempdir().expect("create temp dir");
    let (site_path, mut site) = load_and_build_site!(tmp_dir);
    let file_path = edit_file!(
        site_path,
        "content/rebuild/first.md",
        br#"
+++
title = "first"
weight = 1
date = 2017-01-01
+++

Some content"#
    );

    let res = after_content_change(&mut site, &file_path);
    assert!(res.is_ok());
    assert!(file_contains!(site_path, "public/rebuild/first/index.html", "
Some content
"));
}

#[test]
fn can_rebuild_after_title_change_page_global_func_usage() {
    let tmp_dir = tempdir().expect("create temp dir");
    let (site_path, mut site) = load_and_build_site!(tmp_dir);
    let file_path = edit_file!(
        site_path,
        "content/rebuild/first.md",
        br#"
+++
title = "Premier"
weight = 10
date = 2017-01-01
+++

# A title"#
    );

    let res = after_content_change(&mut site, &file_path);
    assert!(res.is_ok());
    assert!(file_contains!(site_path, "public/rebuild/index.html", "
Premier
"));
}

#[test]
fn can_rebuild_after_sort_change_in_section() {
    let tmp_dir = tempdir().expect("create temp dir");
    let (site_path, mut site) = load_and_build_site!(tmp_dir);
    let file_path = edit_file!(
        site_path,
        "content/rebuild/_index.md",
        br#"
+++
paginate_by = 1
sort_by = "weight"
template = "rebuild.html"
+++
"#
    );

    let res = after_content_change(&mut site, &file_path);
    assert!(res.is_ok());
    assert!(file_contains!(
        site_path,
        "public/rebuild/index.html",
        "
first

second
"
    ));
}

#[test]
fn can_rebuild_after_transparent_change() {
    let tmp_dir = tempdir().expect("create temp dir");
    let (site_path, mut site) = load_and_build_site!(tmp_dir);
    let file_path = edit_file!(
        site_path,
        "content/posts/2018/_index.md",
        br#"
+++
transparent = false
render = false
+++
"#
    );
    // Also remove pagination from posts section so we check whether the transparent page title
    // is there or not without dealing with pagination
    edit_file!(
        site_path,
        "content/posts/_index.md",
        br#"
+++
template = "section.html"
insert_anchor_links = "left"
+++
"#
    );

    let res = after_content_change(&mut site, &file_path);
    assert!(res.is_ok());
    assert!(!file_contains!(site_path, "public/posts/index.html", "A transparent page"));
}

#[test]
fn can_rebuild_after_renaming_page() {
    let tmp_dir = tempdir().expect("create temp dir");
    let (site_path, mut site) = load_and_build_site!(tmp_dir);
    let (old_path, new_path) = rename!(site_path, "content/posts/simple.md", "hard.md");

    let res = after_content_rename(&mut site, &old_path, &new_path);
    println!("{:?}", res);
    assert!(res.is_ok());
    assert!(file_contains!(site_path, "public/posts/hard/index.html", "A simple page"));
}

// https://github.com/Keats/gutenberg/issues/385
#[test]
fn can_rebuild_after_renaming_colocated_asset_folder() {
    let tmp_dir = tempdir().expect("create temp dir");
    let (site_path, mut site) = load_and_build_site!(tmp_dir);
    let (old_path, new_path) =
        rename!(site_path, "content/posts/with-assets", "with-assets-updated");
    assert!(file_contains!(site_path, "content/posts/with-assets-updated/index.md", "Hello"));

    let res = after_content_rename(&mut site, &old_path, &new_path);
    println!("{:?}", res);
    assert!(res.is_ok());
    assert!(file_contains!(
        site_path,
        "public/posts/with-assets-updated/index.html",
        "Hello world"
    ));
}

// https://github.com/Keats/gutenberg/issues/385
#[test]
fn can_rebuild_after_renaming_section_folder() {
    let tmp_dir = tempdir().expect("create temp dir");
    let (site_path, mut site) = load_and_build_site!(tmp_dir);
    let (old_path, new_path) = rename!(site_path, "content/posts", "new-posts");
    assert!(file_contains!(site_path, "content/new-posts/simple.md", "simple"));

    let res = after_content_rename(&mut site, &old_path, &new_path);
    assert!(res.is_ok());

    assert!(file_contains!(site_path, "public/new-posts/simple/index.html", "simple"));
}

#[test]
fn can_rebuild_after_renaming_non_md_asset_in_colocated_folder() {
    let tmp_dir = tempdir().expect("create temp dir");
    let (site_path, mut site) = load_and_build_site!(tmp_dir);
    let (old_path, new_path) = rename!(site_path, "content/posts/with-assets/zola.png", "gutenberg.png");

    // Testing that we don't try to load some images as markdown or something
    let res = after_content_rename(&mut site, &old_path, &new_path);
    assert!(res.is_ok());
}
<commit_msg>Add test for rebuild after deletion<commit_after>extern crate fs_extra;
extern crate rebuild;
extern crate site;
extern crate tempfile;

use std::env;
use std::fs::{self, File};
use std::io::prelude::*;

use fs_extra::dir;
use site::Site;
use tempfile::tempdir;

use rebuild::{after_content_change, after_content_rename};

// Loads the test_site in a tempdir and build it there
// Returns (site_path_in_tempdir, site)
macro_rules! load_and_build_site {
    ($tmp_dir: expr) => {{
        let mut path =
            env::current_dir().unwrap().parent().unwrap().parent().unwrap().to_path_buf();
        path.push("test_site");
        let mut options = dir::CopyOptions::new();
        options.copy_inside = true;
        dir::copy(&path, &$tmp_dir, &options).unwrap();

        let site_path = $tmp_dir.path().join("test_site");
        let mut site = Site::new(&site_path, "config.toml").unwrap();
        site.load().unwrap();
        let public = &site_path.join("public");
        site.set_output_path(&public);
        site.build().unwrap();

        (site_path, site)
    }};
}

/// Replace the file at the path (starting from root) by the given content
/// and return the file path that was modified
macro_rules! edit_file {
    ($site_path: expr, $path: expr, $content: expr) => {{
        let mut t = $site_path.clone();
        for c in $path.split('/') {
            t.push(c);
        }
        let mut file = File::create(&t).expect("Could not open/create file");
        file.write_all($content).expect("Could not write to the file");
        t
    }};
}

macro_rules! file_contains {
    ($site_path: expr, $path: expr, $text: expr) => {{
        let mut path = $site_path.clone();
        for component in $path.split("/") {
            path.push(component);
        }
        let mut file = File::open(&path).unwrap();
        let mut s = String::new();
        file.read_to_string(&mut s).unwrap();
        println!("{:?} -> {}", path, s);
        s.contains($text)
    }};
}

/// Rename a file or a folder to the new given name
macro_rules! rename {
    ($site_path: expr, $path: expr, $new_name: expr) => {{
        let mut t = $site_path.clone();
        for c in $path.split('/') {
            t.push(c);
        }
        let mut new_path = t.parent().unwrap().to_path_buf();
        new_path.push($new_name);
        fs::rename(&t, &new_path).unwrap();
        println!("Renamed {:?} to {:?}", t, new_path);
        (t, new_path)
    }};
}

#[test]
fn can_rebuild_after_simple_change_to_page_content() {
    let tmp_dir = tempdir().expect("create temp dir");
    let (site_path, mut site) = load_and_build_site!(tmp_dir);
    let file_path = edit_file!(
        site_path,
        "content/rebuild/first.md",
        br#"
+++
title = "first"
weight = 1
date = 2017-01-01
+++

Some content"#
    );

    let res = after_content_change(&mut site, &file_path);
    assert!(res.is_ok());
    assert!(file_contains!(site_path, "public/rebuild/first/index.html", "
Some content
"));
}

#[test]
fn can_rebuild_after_title_change_page_global_func_usage() {
    let tmp_dir = tempdir().expect("create temp dir");
    let (site_path, mut site) = load_and_build_site!(tmp_dir);
    let file_path = edit_file!(
        site_path,
        "content/rebuild/first.md",
        br#"
+++
title = "Premier"
weight = 10
date = 2017-01-01
+++

# A title"#
    );

    let res = after_content_change(&mut site, &file_path);
    assert!(res.is_ok());
    assert!(file_contains!(site_path, "public/rebuild/index.html", "
Premier
"));
}

#[test]
fn can_rebuild_after_sort_change_in_section() {
    let tmp_dir = tempdir().expect("create temp dir");
    let (site_path, mut site) = load_and_build_site!(tmp_dir);
    let file_path = edit_file!(
        site_path,
        "content/rebuild/_index.md",
        br#"
+++
paginate_by = 1
sort_by = "weight"
template = "rebuild.html"
+++
"#
    );

    let res = after_content_change(&mut site, &file_path);
    assert!(res.is_ok());
    assert!(file_contains!(
        site_path,
        "public/rebuild/index.html",
        "
first

second
"
    ));
}

#[test]
fn can_rebuild_after_transparent_change() {
    let tmp_dir = tempdir().expect("create temp dir");
    let (site_path, mut site) = load_and_build_site!(tmp_dir);
    let file_path = edit_file!(
        site_path,
        "content/posts/2018/_index.md",
        br#"
+++
transparent = false
render = false
+++
"#
    );
    // Also remove pagination from posts section so we check whether the transparent page title
    // is there or not without dealing with pagination
    edit_file!(
        site_path,
        "content/posts/_index.md",
        br#"
+++
template = "section.html"
insert_anchor_links = "left"
+++
"#
    );

    let res = after_content_change(&mut site, &file_path);
    assert!(res.is_ok());
    assert!(!file_contains!(site_path, "public/posts/index.html", "A transparent page"));
}

#[test]
fn can_rebuild_after_renaming_page() {
    let tmp_dir = tempdir().expect("create temp dir");
    let (site_path, mut site) = load_and_build_site!(tmp_dir);
    let (old_path, new_path) = rename!(site_path, "content/posts/simple.md", "hard.md");

    let res = after_content_rename(&mut site, &old_path, &new_path);
    println!("{:?}", res);
    assert!(res.is_ok());
    assert!(file_contains!(site_path, "public/posts/hard/index.html", "A simple page"));
}

// https://github.com/Keats/gutenberg/issues/385
#[test]
fn can_rebuild_after_renaming_colocated_asset_folder() {
    let tmp_dir = tempdir().expect("create temp dir");
    let (site_path, mut site) = load_and_build_site!(tmp_dir);
    let (old_path, new_path) =
        rename!(site_path, "content/posts/with-assets", "with-assets-updated");
    assert!(file_contains!(site_path, "content/posts/with-assets-updated/index.md", "Hello"));

    let res = after_content_rename(&mut site, &old_path, &new_path);
    println!("{:?}", res);
    assert!(res.is_ok());
    assert!(file_contains!(
        site_path,
        "public/posts/with-assets-updated/index.html",
        "Hello world"
    ));
}

// https://github.com/Keats/gutenberg/issues/385
#[test]
fn can_rebuild_after_renaming_section_folder() {
    let tmp_dir = tempdir().expect("create temp dir");
    let (site_path, mut site) = load_and_build_site!(tmp_dir);
    let (old_path, new_path) = rename!(site_path, "content/posts", "new-posts");
    assert!(file_contains!(site_path, "content/new-posts/simple.md", "simple"));

    let res = after_content_rename(&mut site, &old_path, &new_path);
    assert!(res.is_ok());

    assert!(file_contains!(site_path, "public/new-posts/simple/index.html", "simple"));
}

#[test]
fn can_rebuild_after_renaming_non_md_asset_in_colocated_folder() {
    let tmp_dir = tempdir().expect("create temp dir");
    let (site_path, mut site) = load_and_build_site!(tmp_dir);
    let (old_path, new_path) = rename!(site_path, "content/posts/with-assets/zola.png", "gutenberg.png");

    // Testing that we don't try to load some images as markdown or something
    let res = after_content_rename(&mut site, &old_path, &new_path);
    assert!(res.is_ok());
}

#[test]
fn can_rebuild_after_deleting_file() {
    let tmp_dir = tempdir().expect("create temp dir");
    let (site_path, mut site) = load_and_build_site!(tmp_dir);
    let path = site_path.join("content").join("posts").join("fixed-slug.md");
    fs::remove_file(&path).unwrap();

    let res = after_content_change(&mut site, &path);
    println!("{:?}", res);
    assert!(res.is_ok());
}
<|endoftext|>


<commit_before>use devices::{NodeId, ServiceId, ServiceKind, Service, Input, Output};
use util::Exactly;
use values;

use serde::ser::Serializer;
use serde::de::Deserializer;

use std::cmp;

fn merge<T>(mut a: Vec<T>, mut b: Vec<T>) -> Vec<T> where T: Ord {
    a.append(&mut b);
    a.sort();
    a.dedup();
    a
}

/// A selector for one or more nodes.
///
///
/// # Example
///
/// ```
/// use fxbox_taxonomy::selectors::*;
/// use fxbox_taxonomy::devices::*;
///
/// let selector = NodeSelector::new()
///   .with_tags(vec!["entrance".to_owned()])
///   .with_inputs(vec![InputSelector::new() /* can be more restrictive */]);
/// ```
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct NodeSelector {
    /// If `Exactly(id)`, return only the node with the corresponding id.
    #[serde(default)]
    pub id: Exactly<NodeId>,

    ///  Restrict results to nodes that have all the tags in `tags`.
    #[serde(default)]
    pub tags: Vec<String>,

    /// Restrict results to nodes that have all the inputs in `inputs`.
    #[serde(default)]
    pub inputs: Vec<InputSelector>,

    /// Restrict results to nodes that have all the outputs in `outputs`.
    #[serde(default)]
    pub outputs: Vec<OutputSelector>,

    /// Make sure that we can't instantiate from another crate.
    #[serde(default, skip_serializing)]
    private: (),
}

impl NodeSelector {
    /// Create a new selector that accepts all nodes.
    pub fn new() -> Self {
        Self::default()
    }

    /// Selector for a node with a specific id.
    pub fn with_id(self, id: NodeId) -> Self {
        NodeSelector {
            id: self.id.and(Exactly::Exactly(id)),
            .. self
        }
    }

    ///  Restrict results to nodes that have all the tags in `tags`.
    pub fn with_tags(self, tags: Vec<String>) -> Self {
        NodeSelector {
            tags: merge(self.tags, tags),
            .. self
        }
    }

    /// Restrict results to nodes that have all the inputs in `inputs`.
    pub fn with_inputs(mut self, mut inputs: Vec<InputSelector>) -> Self {
        NodeSelector {
            inputs: {self.inputs.append(&mut inputs); self.inputs},
            .. self
        }
    }

    /// Restrict results to nodes that have all the outputs in `outputs`.
    pub fn with_outputs(mut self, mut outputs: Vec<OutputSelector>) -> Self {
        NodeSelector {
            outputs: {self.outputs.append(&mut outputs); self.outputs},
            .. self
        }
    }

    /// Restrict results to nodes that are accepted by two selectors.
    pub fn and(mut self, mut other: NodeSelector) -> Self {
        NodeSelector {
            id: self.id.and(other.id),
            tags: merge(self.tags, other.tags),
            inputs: {self.inputs.append(&mut other.inputs); self.inputs},
            outputs: {self.outputs.append(&mut other.outputs); self.outputs},
            private: (),
        }
    }
}

/// A selector for one or more input services.
///
///
/// # Example
///
/// ```
/// use fxbox_taxonomy::selectors::*;
/// use fxbox_taxonomy::devices::*;
///
/// let selector = InputSelector::new()
///   .with_parent(NodeId::new("foxbox".to_owned()))
///   .with_kind(ServiceKind::CurrentTimeOfDay);
/// ```
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct InputSelector {
    /// If `Exactly(id)`, return only the service with the corresponding id.
    #[serde(default)]
    pub id: Exactly<ServiceId>,

    /// If `Eactly(id)`, return only services that are children of
    /// node `id`.
    #[serde(default)]
    pub parent: Exactly<NodeId>,

    ///  Restrict results to services that have all the tags in `tags`.
    #[serde(default)]
    pub tags: Vec<String>,

    /// If `Exatly(k)`, restrict results to services that produce values
    /// of kind `k`.
    #[serde(default)]
    pub kind: Exactly<ServiceKind>,

    /// If `Some(r)`, restrict results to services that support polling
    /// with the acceptable period.
    #[serde(default)]
    pub poll: Option<Period>,

    /// If `Some(r)`, restrict results to services that support trigger
    /// with the acceptable period.
    #[serde(default)]
    pub trigger: Option<Period>,

    /// Make sure that we can't instantiate from another crate.
    #[serde(default, skip_serializing)]
    private: (),
}
impl InputSelector {
    /// Create a new selector that accepts all input services.
    pub fn new() -> Self {
        Self::default()
    }

    /// Restrict to a service with a specific id.
    pub fn with_id(self, id: ServiceId) -> Self {
        InputSelector {
            id: self.id.and(Exactly::Exactly(id)),
            .. self
        }
    }

    /// Restrict to a service with a specific parent.
    pub fn with_parent(self, id: NodeId) -> Self {
        InputSelector {
            parent: self.parent.and(Exactly::Exactly(id)),
            .. self
        }
    }

    /// Restrict to a service with a specific kind.
    pub fn with_kind(self, kind: ServiceKind) -> Self {
        InputSelector {
            kind: self.kind.and(Exactly::Exactly(kind)),
            .. self
        }
    }

    ///  Restrict to services that have all the tags in `tags`.
    pub fn with_tags(self, tags: Vec<String>) -> Self {
        InputSelector {
            tags: merge(self.tags, tags),
            .. self
        }
    }

    /// Restrict to services that support polling with the acceptable
    /// period
    pub fn with_poll(self, period: Period) -> Self {
        InputSelector {
            poll: Period::and_option(self.poll, Some(period)),
            .. self
        }
    }

    /// Restrict to services that support trigger with the acceptable
    /// period
    pub fn with_trigger(self, period: Period) -> Self {
        InputSelector {
            trigger: Period::and_option(self.trigger, Some(period)),
            .. self
        }
    }

    /// Restrict to services that are accepted by two selectors.
    pub fn and(self, other: Self) -> Self {
        InputSelector {
            id: self.id.and(other.id),
            parent: self.parent.and(other.parent),
            tags: merge(self.tags, other.tags),
            kind: self.kind.and(other.kind),
            poll: Period::and_option(self.poll, other.poll),
            trigger: Period::and_option(self.trigger, other.trigger),
            private: (),
        }
    }

    /// Determine if a service is matched by this selector.
    pub fn matches(&self, service: &Service<Input>) -> bool {
        if !self.id.matches(&service.id) {
            return false;
        }
        if !self.parent.matches(&service.node) {
            return false;
        }
        if !self.kind.matches(&service.mechanism.kind) {
            return false;
        }
        if !Period::matches_option(&self.poll, &service.mechanism.poll) {
            return false;
        }
        if !Period::matches_option(&self.trigger, &service.mechanism.trigger) {
            return false;
        }
        if !has_selected_tags(&self.tags, &service.tags) {
            return false;
        }
        return true;
    }
}

/// A selector for one or more output services.
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct OutputSelector {
    /// If `Exactly(id)`, return only the service with the corresponding id.
    #[serde(default)]
    pub id: Exactly<ServiceId>,

    /// If `Exactly(id)`, return only services that are immediate children
    /// of node `id`.
    #[serde(default)]
    pub parent: Exactly<NodeId>,

    ///  Restrict results to services that have all the tags in `tags`.
    #[serde(default)]
    pub tags: Vec<String>,

    /// If `Exactly(k)`, restrict results to services that accept values
    /// of kind `k`.
    #[serde(default)]
    pub kind: Exactly<ServiceKind>,

    /// If `Some(r)`, restrict results to services that support pushing
    /// with the acceptable period.
    #[serde(default)]
    pub push: Option<Period>,

    /// Make sure that we can't instantiate from another crate.
    #[serde(default, skip_serializing)]
    private: (),
}

impl OutputSelector {
    /// Create a new selector that accepts all input services.
    pub fn new() -> Self {
        OutputSelector::default()
    }

    /// Selector to a service with a specific id.
    pub fn with_id(self, id: ServiceId) -> Self {
        OutputSelector {
            id: self.id.and(Exactly::Exactly(id)),
            .. self
        }
    }

    /// Selector to services with a specific parent.
    pub fn with_parent(self, id: NodeId) -> Self {
        OutputSelector {
            parent: self.parent.and(Exactly::Exactly(id)),
            .. self
        }
    }

    /// Selector to services with a specific kind.
    pub fn with_kind(self, kind: ServiceKind) -> Self {
        OutputSelector {
            kind: self.kind.and(Exactly::Exactly(kind)),
            .. self
        }
    }

    ///  Restrict to services that have all the tags in `tags`.
    pub fn with_tags(self, tags: Vec<String>) -> Self {
        OutputSelector {
            tags: merge(self.tags, tags),
            .. self
        }
    }

    /// Restrict to services that support push with the acceptable
    /// period
    pub fn with_push(self, period: Period) -> Self {
        OutputSelector {
            push: Period::and_option(self.push, Some(period)),
            .. self
        }
    }

    /// Restrict results to services that are accepted by two selectors.
    pub fn and(self, other: Self) -> Self {
        OutputSelector {
            id: self.id.and(other.id),
            parent: self.parent.and(other.parent),
            tags: merge(self.tags, other.tags),
            kind: self.kind.and(other.kind),
            push: Period::and_option(self.push, other.push),
            private: (),
        }
    }

    /// Determine if a service is matched by this selector.
    pub fn matches(&self, service: &Service<Output>) -> bool {
        if !self.id.matches(&service.id) {
            return false;
        }
        if !self.parent.matches(&service.node) {
            return false;
        }
        if !self.kind.matches(&service.mechanism.kind) {
            return false;
        }
        if !Period::matches_option(&self.push, &service.mechanism.push) {
            return false;
        }
        if !has_selected_tags(&self.tags, &service.tags) {
            return false;
        }
        return true;
    }
}

/// An acceptable interval of time.
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct Period {
    #[serde(default)]
    pub min: Option<values::ValDuration>,
    #[serde(default)]
    pub max: Option<values::ValDuration>,
}
impl Period {
    pub fn and(self, other: Self) -> Self {
        let min = match (self.min, other.min) {
            (None, x@_) => x,
            (x@_, None) => x,
            (Some(min1), Some(min2)) => Some(cmp::max(min1, min2))
        };
        let max = match (self.max, other.max) {
            (None, x@_) => x,
            (x@_, None) => x,
            (Some(max1), Some(max2)) => Some(cmp::min(max1, max2))
        };
        Period {
            min: min,
            max: max
        }
    }

    pub fn and_option(a: Option<Self>, b: Option<Self>) -> Option<Self> {
        match (a, b) {
            (None, x@_) => x,
            (x@_, None) => x,
            (Some(a), Some(b)) => Some(a.and(b))
        }
    }

    pub fn matches(&self, duration: &values::ValDuration) -> bool {
        if let Some(ref min) = self.min {
            if min > duration {
                return false;
            }
        }
        if let Some(ref max) = self.max {
            if max < duration {
                return false;
            }
        }
        return true;
    }

    pub fn matches_option(period: &Option<Self>, duration: &Option<values::ValDuration>) -> bool {
        match (period, duration) {
            (&Some(ref period), &Some(ref duration))
                if !period.matches(duration) =>
                return false,
            (&None, &Some(_)) => return false,
            _ => return true
        }
    }
}

fn has_selected_tags(actual: &Vec<String>, requested: &Vec<String>) -> bool {
    for tag in &*actual {
        if requested.iter().find(|x| *x == tag).is_none() {
            return false;
        }
    }
    return true;
}
<commit_msg>[fxbox/taxonomy] Doctest fixup<commit_after>use devices::{NodeId, ServiceId, ServiceKind, Service, Input, Output};
use util::Exactly;
use values;

use serde::ser::Serializer;
use serde::de::Deserializer;

use std::cmp;

fn merge<T>(mut a: Vec<T>, mut b: Vec<T>) -> Vec<T> where T: Ord {
    a.append(&mut b);
    a.sort();
    a.dedup();
    a
}

/// A selector for one or more nodes.
///
///
/// # Example
///
/// ```
/// use fxbox_taxonomy::selector::*;
/// use fxbox_taxonomy::devices::*;
///
/// let selector = NodeSelector::new()
///   .with_tags(vec!["entrance".to_owned()])
///   .with_inputs(vec![InputSelector::new() /* can be more restrictive */]);
/// ```
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct NodeSelector {
    /// If `Exactly(id)`, return only the node with the corresponding id.
    #[serde(default)]
    pub id: Exactly<NodeId>,

    ///  Restrict results to nodes that have all the tags in `tags`.
    #[serde(default)]
    pub tags: Vec<String>,

    /// Restrict results to nodes that have all the inputs in `inputs`.
    #[serde(default)]
    pub inputs: Vec<InputSelector>,

    /// Restrict results to nodes that have all the outputs in `outputs`.
    #[serde(default)]
    pub outputs: Vec<OutputSelector>,

    /// Make sure that we can't instantiate from another crate.
    #[serde(default, skip_serializing)]
    private: (),
}

impl NodeSelector {
    /// Create a new selector that accepts all nodes.
    pub fn new() -> Self {
        Self::default()
    }

    /// Selector for a node with a specific id.
    pub fn with_id(self, id: NodeId) -> Self {
        NodeSelector {
            id: self.id.and(Exactly::Exactly(id)),
            .. self
        }
    }

    ///  Restrict results to nodes that have all the tags in `tags`.
    pub fn with_tags(self, tags: Vec<String>) -> Self {
        NodeSelector {
            tags: merge(self.tags, tags),
            .. self
        }
    }

    /// Restrict results to nodes that have all the inputs in `inputs`.
    pub fn with_inputs(mut self, mut inputs: Vec<InputSelector>) -> Self {
        NodeSelector {
            inputs: {self.inputs.append(&mut inputs); self.inputs},
            .. self
        }
    }

    /// Restrict results to nodes that have all the outputs in `outputs`.
    pub fn with_outputs(mut self, mut outputs: Vec<OutputSelector>) -> Self {
        NodeSelector {
            outputs: {self.outputs.append(&mut outputs); self.outputs},
            .. self
        }
    }

    /// Restrict results to nodes that are accepted by two selector.
    pub fn and(mut self, mut other: NodeSelector) -> Self {
        NodeSelector {
            id: self.id.and(other.id),
            tags: merge(self.tags, other.tags),
            inputs: {self.inputs.append(&mut other.inputs); self.inputs},
            outputs: {self.outputs.append(&mut other.outputs); self.outputs},
            private: (),
        }
    }
}

/// A selector for one or more input services.
///
///
/// # Example
///
/// ```
/// use fxbox_taxonomy::selector::*;
/// use fxbox_taxonomy::devices::*;
///
/// let selector = InputSelector::new()
///   .with_parent(NodeId::new("foxbox".to_owned()))
///   .with_kind(ServiceKind::CurrentTimeOfDay);
/// ```
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct InputSelector {
    /// If `Exactly(id)`, return only the service with the corresponding id.
    #[serde(default)]
    pub id: Exactly<ServiceId>,

    /// If `Eactly(id)`, return only services that are children of
    /// node `id`.
    #[serde(default)]
    pub parent: Exactly<NodeId>,

    ///  Restrict results to services that have all the tags in `tags`.
    #[serde(default)]
    pub tags: Vec<String>,

    /// If `Exatly(k)`, restrict results to services that produce values
    /// of kind `k`.
    #[serde(default)]
    pub kind: Exactly<ServiceKind>,

    /// If `Some(r)`, restrict results to services that support polling
    /// with the acceptable period.
    #[serde(default)]
    pub poll: Option<Period>,

    /// If `Some(r)`, restrict results to services that support trigger
    /// with the acceptable period.
    #[serde(default)]
    pub trigger: Option<Period>,

    /// Make sure that we can't instantiate from another crate.
    #[serde(default, skip_serializing)]
    private: (),
}
impl InputSelector {
    /// Create a new selector that accepts all input services.
    pub fn new() -> Self {
        Self::default()
    }

    /// Restrict to a service with a specific id.
    pub fn with_id(self, id: ServiceId) -> Self {
        InputSelector {
            id: self.id.and(Exactly::Exactly(id)),
            .. self
        }
    }

    /// Restrict to a service with a specific parent.
    pub fn with_parent(self, id: NodeId) -> Self {
        InputSelector {
            parent: self.parent.and(Exactly::Exactly(id)),
            .. self
        }
    }

    /// Restrict to a service with a specific kind.
    pub fn with_kind(self, kind: ServiceKind) -> Self {
        InputSelector {
            kind: self.kind.and(Exactly::Exactly(kind)),
            .. self
        }
    }

    ///  Restrict to services that have all the tags in `tags`.
    pub fn with_tags(self, tags: Vec<String>) -> Self {
        InputSelector {
            tags: merge(self.tags, tags),
            .. self
        }
    }

    /// Restrict to services that support polling with the acceptable
    /// period
    pub fn with_poll(self, period: Period) -> Self {
        InputSelector {
            poll: Period::and_option(self.poll, Some(period)),
            .. self
        }
    }

    /// Restrict to services that support trigger with the acceptable
    /// period
    pub fn with_trigger(self, period: Period) -> Self {
        InputSelector {
            trigger: Period::and_option(self.trigger, Some(period)),
            .. self
        }
    }

    /// Restrict to services that are accepted by two selector.
    pub fn and(self, other: Self) -> Self {
        InputSelector {
            id: self.id.and(other.id),
            parent: self.parent.and(other.parent),
            tags: merge(self.tags, other.tags),
            kind: self.kind.and(other.kind),
            poll: Period::and_option(self.poll, other.poll),
            trigger: Period::and_option(self.trigger, other.trigger),
            private: (),
        }
    }

    /// Determine if a service is matched by this selector.
    pub fn matches(&self, service: &Service<Input>) -> bool {
        if !self.id.matches(&service.id) {
            return false;
        }
        if !self.parent.matches(&service.node) {
            return false;
        }
        if !self.kind.matches(&service.mechanism.kind) {
            return false;
        }
        if !Period::matches_option(&self.poll, &service.mechanism.poll) {
            return false;
        }
        if !Period::matches_option(&self.trigger, &service.mechanism.trigger) {
            return false;
        }
        if !has_selected_tags(&self.tags, &service.tags) {
            return false;
        }
        return true;
    }
}

/// A selector for one or more output services.
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct OutputSelector {
    /// If `Exactly(id)`, return only the service with the corresponding id.
    #[serde(default)]
    pub id: Exactly<ServiceId>,

    /// If `Exactly(id)`, return only services that are immediate children
    /// of node `id`.
    #[serde(default)]
    pub parent: Exactly<NodeId>,

    ///  Restrict results to services that have all the tags in `tags`.
    #[serde(default)]
    pub tags: Vec<String>,

    /// If `Exactly(k)`, restrict results to services that accept values
    /// of kind `k`.
    #[serde(default)]
    pub kind: Exactly<ServiceKind>,

    /// If `Some(r)`, restrict results to services that support pushing
    /// with the acceptable period.
    #[serde(default)]
    pub push: Option<Period>,

    /// Make sure that we can't instantiate from another crate.
    #[serde(default, skip_serializing)]
    private: (),
}

impl OutputSelector {
    /// Create a new selector that accepts all input services.
    pub fn new() -> Self {
        OutputSelector::default()
    }

    /// Selector to a service with a specific id.
    pub fn with_id(self, id: ServiceId) -> Self {
        OutputSelector {
            id: self.id.and(Exactly::Exactly(id)),
            .. self
        }
    }

    /// Selector to services with a specific parent.
    pub fn with_parent(self, id: NodeId) -> Self {
        OutputSelector {
            parent: self.parent.and(Exactly::Exactly(id)),
            .. self
        }
    }

    /// Selector to services with a specific kind.
    pub fn with_kind(self, kind: ServiceKind) -> Self {
        OutputSelector {
            kind: self.kind.and(Exactly::Exactly(kind)),
            .. self
        }
    }

    ///  Restrict to services that have all the tags in `tags`.
    pub fn with_tags(self, tags: Vec<String>) -> Self {
        OutputSelector {
            tags: merge(self.tags, tags),
            .. self
        }
    }

    /// Restrict to services that support push with the acceptable
    /// period
    pub fn with_push(self, period: Period) -> Self {
        OutputSelector {
            push: Period::and_option(self.push, Some(period)),
            .. self
        }
    }

    /// Restrict results to services that are accepted by two selector.
    pub fn and(self, other: Self) -> Self {
        OutputSelector {
            id: self.id.and(other.id),
            parent: self.parent.and(other.parent),
            tags: merge(self.tags, other.tags),
            kind: self.kind.and(other.kind),
            push: Period::and_option(self.push, other.push),
            private: (),
        }
    }

    /// Determine if a service is matched by this selector.
    pub fn matches(&self, service: &Service<Output>) -> bool {
        if !self.id.matches(&service.id) {
            return false;
        }
        if !self.parent.matches(&service.node) {
            return false;
        }
        if !self.kind.matches(&service.mechanism.kind) {
            return false;
        }
        if !Period::matches_option(&self.push, &service.mechanism.push) {
            return false;
        }
        if !has_selected_tags(&self.tags, &service.tags) {
            return false;
        }
        return true;
    }
}

/// An acceptable interval of time.
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct Period {
    #[serde(default)]
    pub min: Option<values::ValDuration>,
    #[serde(default)]
    pub max: Option<values::ValDuration>,
}
impl Period {
    pub fn and(self, other: Self) -> Self {
        let min = match (self.min, other.min) {
            (None, x@_) => x,
            (x@_, None) => x,
            (Some(min1), Some(min2)) => Some(cmp::max(min1, min2))
        };
        let max = match (self.max, other.max) {
            (None, x@_) => x,
            (x@_, None) => x,
            (Some(max1), Some(max2)) => Some(cmp::min(max1, max2))
        };
        Period {
            min: min,
            max: max
        }
    }

    pub fn and_option(a: Option<Self>, b: Option<Self>) -> Option<Self> {
        match (a, b) {
            (None, x@_) => x,
            (x@_, None) => x,
            (Some(a), Some(b)) => Some(a.and(b))
        }
    }

    pub fn matches(&self, duration: &values::ValDuration) -> bool {
        if let Some(ref min) = self.min {
            if min > duration {
                return false;
            }
        }
        if let Some(ref max) = self.max {
            if max < duration {
                return false;
            }
        }
        return true;
    }

    pub fn matches_option(period: &Option<Self>, duration: &Option<values::ValDuration>) -> bool {
        match (period, duration) {
            (&Some(ref period), &Some(ref duration))
                if !period.matches(duration) =>
                return false,
            (&None, &Some(_)) => return false,
            _ => return true
        }
    }
}

fn has_selected_tags(actual: &Vec<String>, requested: &Vec<String>) -> bool {
    for tag in &*actual {
        if requested.iter().find(|x| *x == tag).is_none() {
            return false;
        }
    }
    return true;
}
<|endoftext|>