<commit_before>//! Utility functions for computing in-gamut regions for CIELuv color space.
use crate::{FloatComponent, LabHue};
#[allow(unused)]
use num_traits::Float;
use num_traits::{Pow, ToPrimitive};

/// Boundary line in the u-v plane of the Luv color space.
struct BoundaryLine {
    slope: f64,
    intercept: f64,
}

impl BoundaryLine {
    /// Given array starting at the origin at angle theta, determine
    /// the signed length at which the ray intersects with the
    /// boundary.
    fn intersect_length_at_angle(&self, theta: f64) -> Option<f64> {
        let (sin_theta, cos_theta) = theta.to_f64().unwrap().sin_cos();
        let denom = sin_theta - self.slope * cos_theta;
        if denom.abs() > 1.0e-6 {
            Some(self.intercept / denom)
        } else {
            None
        }
    }

    /// Return the distance from this line to the origin.
    fn distance_to_origin(&self) -> f64 {
        self.intercept.abs() / (self.slope * self.slope + 1.0).sqrt()
    }
}

/// `LuvBounds` represents the convex polygon formed by the in-gamut
/// region in the uv plane at a given lightness.
pub struct LuvBounds {
    bounds: [BoundaryLine; 6],
}

const M: [[f64; 3]; 3] = [
    [3.240969941904521, -1.537383177570093, -0.498610760293],
    [-0.96924363628087, 1.87596750150772, 0.041555057407175],
    [0.055630079696993, -0.20397695888897, 1.056971514242878],
];
const KAPPA: f64 = 903.2962962;
const EPSILON: f64 = 0.0088564516;

impl LuvBounds {
    pub fn from_lightness<T: FloatComponent>(l: T) -> Self {
        let l: f64 = l.to_f64().unwrap();

        let sub1 = (l + 16.0).pow(3.0) / 1560896.0;
        let sub2 = if sub1 > EPSILON { sub1 } else { l / KAPPA };

	let line = |c: usize, t: f64| {
            let m: &[f64; 3] = &M[c];
            let top1 = (284517.0 * m[0] - 94839.0 * m[2]) * sub2;
            let top2 = (838422.0 * m[2] + 769860.0 * m[1] + 731718.0 * m[0]) * l * sub2
                - 769860.0 * t * l;
            let bottom = (632260.0 * m[2] - 126452.0 * m[1]) * sub2 + 126452.0 * t;

            BoundaryLine {
                slope: top1 / bottom,
                intercept: top2 / bottom,
            }
	};

        Self { bounds: [line(0, 0.0), line(0, 1.0),
			line(1, 0.0), line(1, 1.0),
			line(2, 0.0), line(2, 1.0)]}
    }

    /// Given a particular hue, return the distance to the boundary at
    /// the angle determined by the hue.
    pub fn max_chroma_at_hue<T: FloatComponent>(&self, hue: LabHue<T>) -> T {
        let mut min_chroma = f64::MAX;
        let h = hue.to_positive_radians().to_f64().unwrap();

        // minimize the distance across all individual boundaries
        for b in &self.bounds {
            if let Some(t) = b.intersect_length_at_angle(h) {
                if t >= 0.0 && min_chroma > t {
                    min_chroma = t;
                }
            }
        }
        T::from_f64(min_chroma)
    }

    /// Return the minimum chroma such that, at any hue, the chroma is
    /// in-gamut.
    ///
    /// This is equivalent to finding the minimum distance to the
    /// origin across all boundaries.
    pub fn max_safe_chroma<T>(&self) -> T
    where
        T: FloatComponent,
    {
        let mut min_dist = f64::MAX;

        // minimize the distance across all individual boundaries
        for b in &self.bounds {
            let d = b.distance_to_origin();
            if min_dist > d {
                min_dist = d;
            }
        }
        T::from_f64(min_dist)
    }
}

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

    #[test]
    fn boundary_intersect() {
        let line = BoundaryLine {
            slope: -1.0,
            intercept: 1.0,
        };
        assert_relative_eq!(line.intersect_length_at_angle(0.0).unwrap(), 1.0);
        assert_relative_eq!(
            line.intersect_length_at_angle(std::f64::consts::FRAC_PI_4)
                .unwrap(),
            std::f64::consts::FRAC_1_SQRT_2
        );
        assert_eq!(
            line.intersect_length_at_angle(-std::f64::consts::FRAC_PI_4),
            None
        );

        let line = BoundaryLine {
            slope: 0.0,
            intercept: 2.0,
        };
        assert_eq!(line.intersect_length_at_angle(0.0), None);
        assert_relative_eq!(
            line.intersect_length_at_angle(std::f64::consts::FRAC_PI_2)
                .unwrap(),
            2.0
        );
        assert_relative_eq!(
            line.intersect_length_at_angle(2.0 * std::f64::consts::FRAC_PI_3)
                .unwrap(),
            4.0 / 3.0f64.sqrt()
        );
    }

    #[test]
    fn line_distance() {
        let line = BoundaryLine {
            slope: 0.0,
            intercept: 2.0,
        };
        assert_relative_eq!(line.distance_to_origin(), 2.0);

        let line = BoundaryLine {
            slope: 1.0,
            intercept: 2.0,
        };
        assert_relative_eq!(line.distance_to_origin(), std::f64::consts::SQRT_2);
    }
}
<commit_msg>minor comment improvements for luv bounds<commit_after>//! Utility functions for computing in-gamut regions for the CIELUV
//! color space.
use crate::{FloatComponent, LuvHue};
#[allow(unused)]
use num_traits::Float;
use num_traits::{Pow, ToPrimitive};

/// Boundary line in the u-v plane of the Luv color space.
struct BoundaryLine {
    slope: f64,
    intercept: f64,
}

impl BoundaryLine {
    /// Given array starting at the origin at angle theta, determine
    /// the signed length at which the ray intersects with the
    /// boundary.
    fn intersect_length_at_angle(&self, theta: f64) -> Option<f64> {
        let (sin_theta, cos_theta) = theta.to_f64().unwrap().sin_cos();
        let denom = sin_theta - self.slope * cos_theta;
        if denom.abs() > 1.0e-6 {
            Some(self.intercept / denom)
        } else {
            None
        }
    }

    /// Return the distance from this line to the origin.
    fn distance_to_origin(&self) -> f64 {
        self.intercept.abs() / (self.slope * self.slope + 1.0).sqrt()
    }
}

/// `LuvBounds` represents the convex polygon formed by the in-gamut
/// region in the u*-v* plane at a given lightness L*.
pub struct LuvBounds {
    bounds: [BoundaryLine; 6],
}

const M: [[f64; 3]; 3] = [
    [3.240969941904521, -1.537383177570093, -0.498610760293],
    [-0.96924363628087, 1.87596750150772, 0.041555057407175],
    [0.055630079696993, -0.20397695888897, 1.056971514242878],
];
const KAPPA: f64 = 903.2962962;
const EPSILON: f64 = 0.0088564516;

impl LuvBounds {
    pub fn from_lightness<T: FloatComponent>(l: T) -> Self {
        let l: f64 = l.to_f64().unwrap();

        let sub1 = (l + 16.0).pow(3.0) / 1560896.0;
        let sub2 = if sub1 > EPSILON { sub1 } else { l / KAPPA };

	let line = |c: usize, t: f64| {
            let m: &[f64; 3] = &M[c];
            let top1 = (284517.0 * m[0] - 94839.0 * m[2]) * sub2;
            let top2 = (838422.0 * m[2] + 769860.0 * m[1] + 731718.0 * m[0]) * l * sub2
                - 769860.0 * t * l;
            let bottom = (632260.0 * m[2] - 126452.0 * m[1]) * sub2 + 126452.0 * t;

            BoundaryLine {
                slope: top1 / bottom,
                intercept: top2 / bottom,
            }
	};

        Self { bounds: [line(0, 0.0), line(0, 1.0),
			line(1, 0.0), line(1, 1.0),
			line(2, 0.0), line(2, 1.0)]}
    }

    /// Given a particular hue, return the distance to the boundary at
    /// the angle determined by the hue.
    pub fn max_chroma_at_hue<T: FloatComponent>(&self, hue: LuvHue<T>) -> T {
        let mut min_chroma = f64::MAX;
        let h = hue.to_positive_radians().to_f64().unwrap();

        // minimize the distance across all individual boundaries
        for b in &self.bounds {
            if let Some(t) = b.intersect_length_at_angle(h) {
                if t >= 0.0 && min_chroma > t {
                    min_chroma = t;
                }
            }
        }
        T::from_f64(min_chroma)
    }

    /// Return the minimum chroma such that, at any hue, the chroma is
    /// in-gamut.
    ///
    /// This is equivalent to finding the minimum distance to the
    /// origin across all boundaries.
    pub fn max_safe_chroma<T>(&self) -> T
    where
        T: FloatComponent,
    {
        let mut min_dist = f64::MAX;

        // minimize the distance across all individual boundaries
        for b in &self.bounds {
            let d = b.distance_to_origin();
            if min_dist > d {
                min_dist = d;
            }
        }
        T::from_f64(min_dist)
    }
}

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

    #[test]
    fn boundary_intersect() {
        let line = BoundaryLine {
            slope: -1.0,
            intercept: 1.0,
        };
        assert_relative_eq!(line.intersect_length_at_angle(0.0).unwrap(), 1.0);
        assert_relative_eq!(
            line.intersect_length_at_angle(std::f64::consts::FRAC_PI_4)
                .unwrap(),
            std::f64::consts::FRAC_1_SQRT_2
        );
        assert_eq!(
            line.intersect_length_at_angle(-std::f64::consts::FRAC_PI_4),
            None
        );

        let line = BoundaryLine {
            slope: 0.0,
            intercept: 2.0,
        };
        assert_eq!(line.intersect_length_at_angle(0.0), None);
        assert_relative_eq!(
            line.intersect_length_at_angle(std::f64::consts::FRAC_PI_2)
                .unwrap(),
            2.0
        );
        assert_relative_eq!(
            line.intersect_length_at_angle(2.0 * std::f64::consts::FRAC_PI_3)
                .unwrap(),
            4.0 / 3.0f64.sqrt()
        );
    }

    #[test]
    fn line_distance() {
        let line = BoundaryLine {
            slope: 0.0,
            intercept: 2.0,
        };
        assert_relative_eq!(line.distance_to_origin(), 2.0);

        let line = BoundaryLine {
            slope: 1.0,
            intercept: 2.0,
        };
        assert_relative_eq!(line.distance_to_origin(), std::f64::consts::SQRT_2);
    }
}
<|endoftext|>


<commit_before>extern crate racer_testutils;
use racer_testutils::*;

#[test]
fn finds_type_parameter_for_fnarg() {
    let src = "
        fn main() {
            trait Trait {
                fn method(&self);
            }
            fn func<T: Trait>(arg: &T) {
                arg.meth~
            }
        }
        ";
    assert_eq!(get_only_completion(src, None).matchstr, "method");
}

#[test]
fn completes_methods_for_fnarg_by_trait_bounds() {
    let src = "
        fn main() {
            trait Trait {
                fn method(&self);
            }
            fn func<T: Trait>(arg: &T) {
                arg.meth~
            }
        }
        ";
    assert_eq!(get_only_completion(src, None).matchstr, "method");
}

#[test]
fn completes_methods_for_fnarg_by_where_clause() {
    let src = "
        fn main() {
            trait Trait {
                fn method(&self);
            }
            fn func<T>(arg: &T)
            where
                T: Trait,
            {
                arg.meth~
            }
        }
        ";
    assert_eq!(get_only_completion(src, None).matchstr, "method");
}

#[test]
fn completes_external_methods_for_fnarg_by_trait_bounds() {
    let src = "
        fn main() {
            fn func<T: Debug + Clone>(arg: &T) {
                arg.clo~
            }
        }
        ";
    assert!(
        get_all_completions(src, None)
            .into_iter()
            .any(|ma| ma.matchstr == "clone")
    );
}

#[test]
fn completes_inherited_methods_for_fnarg_by_trait_bounds() {
    let src = "
        fn main() {
            trait Inherited {
                fn inherited(&self);
            }
            trait Trait: Inherited {
                fn method(&self);
            }
            fn func<T: Trait>(arg: &T) {
                arg.inheri~
            }
        }
        ";
    assert_eq!(get_only_completion(src, None).matchstr, "inherited");
}

// test for checking racer don't cause INF loop
#[test]
fn completes_inherited_methods_with_cycle() {
    let src = "
        fn main() {
            trait Inherited2: Inherited1 {}
            trait Inherited1: Inherited2 {
                fn inherited(&self);
            }
            trait Trait: Inherited1 {
                fn method(&self);
            }
            fn func<T: Trait>(arg: &T) {
                arg.inheri~
            }
        }
        ";
    assert_eq!(get_only_completion(src, None).matchstr, "inherited");
}

#[test]
fn completes_impled_bounds_for_fnarg() {
    let src = "
        fn main() {
            struct St<T>(T);
            trait Trait: Sized {
                fn method(&self);
            }
            impl<T: Trait> St<T> {
                fn new(t: T) -> Self {
                    t.met~
                }
            }
        }
        ";
    assert_eq!(get_only_completion(src, None).matchstr, "method");
}

#[test]
fn completes_impled_bounds_for_self() {
    let src = "
        fn main() {
            trait Trait: Sized {
                fn method(&self);
            }
            impl<T: Trait> Clone for T {
                fn f(self) -> Self {
                    self.me~
                }
            }
        }
        ";
    assert_eq!(get_only_completion(src, None).matchstr, "method");
}

#[test]
fn completes_impled_bounds_for_self_field() {
    let src = "
        fn main() {
            struct St<T> {
                mem1: String,
                mem2: T,
            }
            trait Trait: Sized {
                fn method(&self);
            }
            impl<T: Trait> St<T> {
                fn f(self) -> Self {
                    self.mem2.m~
                }
            }
        }
        ";
    assert_eq!(get_only_completion(src, None).matchstr, "method");
}

#[test]
fn completes_impled_bounds_for_ref_self_field() {
    let src = "
        fn main() {
            struct St<'a, T> {
                mem1: String,
                mem2: &'a T,
            }
            trait Trait: Sized {
                fn method(&self);
            }
            impl<T: Trait> St<T> {
                fn f(self) -> Self {
                    self.mem2.m~
                }
            }
        }
        ";
    assert_eq!(get_only_completion(src, None).matchstr, "method");
}

#[test]
fn completes_fn_bounds_for_struct_member() {
    let src = "
        fn main() {
            struct St<T> {
                mem1: String,
                mem2: T,
            }
            fn f<T: Clone>(st: St<T>) {
                st.mem2.clone_fro~
            }
        }
        ";
    assert_eq!(get_only_completion(src, None).matchstr, "clone_from");
}
<commit_msg>Add tests<commit_after>extern crate racer_testutils;
use racer_testutils::*;

#[test]
fn finds_type_parameter_for_fnarg() {
    let src = "
        fn main() {
            trait Trait {
                fn method(&self);
            }
            fn func<T: Trait>(arg: &T) {
                arg.meth~
            }
        }
        ";
    assert_eq!(get_only_completion(src, None).matchstr, "method");
}

#[test]
fn completes_methods_for_fnarg_by_trait_bounds() {
    let src = "
        fn main() {
            trait Trait {
                fn method(&self);
            }
            fn func<T: Trait>(arg: &T) {
                arg.meth~
            }
        }
        ";
    assert_eq!(get_only_completion(src, None).matchstr, "method");
}

#[test]
fn completes_methods_for_fnarg_by_where_clause() {
    let src = "
        fn main() {
            trait Trait {
                fn method(&self);
            }
            fn func<T>(arg: &T)
            where
                T: Trait,
            {
                arg.meth~
            }
        }
        ";
    assert_eq!(get_only_completion(src, None).matchstr, "method");
}

#[test]
fn completes_external_methods_for_fnarg_by_trait_bounds() {
    let src = "
        fn main() {
            fn func<T: Debug + Clone>(arg: &T) {
                arg.clo~
            }
        }
        ";
    assert!(
        get_all_completions(src, None)
            .into_iter()
            .any(|ma| ma.matchstr == "clone")
    );
}

#[test]
fn completes_inherited_methods_for_fnarg_by_trait_bounds() {
    let src = "
        fn main() {
            trait Inherited {
                fn inherited(&self);
            }
            trait Trait: Inherited {
                fn method(&self);
            }
            fn func<T: Trait>(arg: &T) {
                arg.inheri~
            }
        }
        ";
    assert_eq!(get_only_completion(src, None).matchstr, "inherited");
}

// test for checking racer don't cause INF loop
#[test]
fn completes_inherited_methods_with_cycle() {
    let src = "
        fn main() {
            trait Inherited2: Inherited1 {}
            trait Inherited1: Inherited2 {
                fn inherited(&self);
            }
            trait Trait: Inherited1 {
                fn method(&self);
            }
            fn func<T: Trait>(arg: &T) {
                arg.inheri~
            }
        }
        ";
    assert_eq!(get_only_completion(src, None).matchstr, "inherited");
}

#[test]
fn completes_impled_bounds_for_fnarg() {
    let src = "
        fn main() {
            struct St<T>(T);
            trait Trait: Sized {
                fn method(&self);
            }
            impl<T: Trait> St<T> {
                fn new(t: T) -> Self {
                    t.met~
                }
            }
        }
        ";
    assert_eq!(get_only_completion(src, None).matchstr, "method");
}

#[test]
fn completes_impled_bounds_for_self() {
    let src = "
        fn main() {
            trait Trait: Sized {
                fn method(&self);
            }
            impl<T: Trait> Clone for T {
                fn f(self) -> Self {
                    self.me~
                }
            }
        }
        ";
    assert_eq!(get_only_completion(src, None).matchstr, "method");
}

#[test]
fn completes_impled_bounds_for_self_field() {
    let src = "
        fn main() {
            struct St<T> {
                mem1: String,
                mem2: T,
            }
            trait Trait: Sized {
                fn method(&self);
            }
            impl<T: Trait> St<T> {
                fn f(self) -> Self {
                    self.mem2.m~
                }
            }
        }
        ";
    assert_eq!(get_only_completion(src, None).matchstr, "method");
}

#[test]
fn completes_impled_bounds_for_ref_self_field() {
    let src = "
        fn main() {
            struct St<'a, T> {
                mem1: String,
                mem2: &'a T,
            }
            trait Trait: Sized {
                fn method(&self);
            }
            impl<T: Trait> St<T> {
                fn f(self) -> Self {
                    self.mem2.m~
                }
            }
        }
        ";
    assert_eq!(get_only_completion(src, None).matchstr, "method");
}

#[test]
fn completes_fn_bounds_for_struct_member() {
    let src = "
        fn main() {
            struct St<T> {
                mem1: String,
                mem2: T,
            }
            fn f<T: Clone>(st: St<T>) {
                st.mem2.clone_fro~
            }
        }
        ";
    assert_eq!(get_only_completion(src, None).matchstr, "clone_from");
}

#[test]
fn completes_impl_trait_in_arg_position() {
    let src = "
    struct MyType;
    trait Foo {
        fn foo(&self);
    }
    impl Foo for MyType {
        fn foo(&self) {
            unimplemented!();
        }
    }

    fn bar(b: impl Foo) {
        b.~
    }
    ";
    assert_eq!(get_only_completion(src, None).matchstr, "foo");
}

#[test]
fn completes_impl_trait_in_fn_return_position() {
    let src = "
    struct MyType;
    trait Foo {
        fn foo(&self);
    }
    impl Foo for MyType {
        fn foo(&self) {
            unimplemented!();
        }
    }
    fn bar() -> impl Foo {
        MyType
    }
    fn main() {
        let mytype = bar();
        mytype.~
    }
    ";
    assert_eq!(get_only_completion(src, None).matchstr, "foo");
}
<|endoftext|>


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

use backup_service::start_backup_service;
use consensus::{consensus_provider::start_consensus, gen_consensus_reconfig_subscription};
use debug_interface::node_debug_service::NodeDebugService;
use executor::{db_bootstrapper::maybe_bootstrap, Executor};
use executor_types::ChunkExecutor;
use futures::{channel::mpsc::channel, executor::block_on};
use libra_config::{
    config::{NetworkConfig, NodeConfig, RoleType},
    network_id::NodeNetworkId,
    utils::get_genesis_txn,
};
use libra_json_rpc::bootstrap_from_config as bootstrap_rpc;
use libra_logger::{prelude::*, Logger};
use libra_mempool::gen_mempool_reconfig_subscription;
use libra_metrics::metric_server;
use libra_types::{
    account_config::libra_root_address, account_state::AccountState, chain_id::ChainId,
    move_resource::MoveStorage,
};
use libra_vm::LibraVM;
use libradb::LibraDB;
use network_builder::builder::NetworkBuilder;
use state_synchronizer::StateSynchronizer;
use std::{boxed::Box, convert::TryFrom, net::ToSocketAddrs, sync::Arc, thread, time::Instant};
use storage_interface::DbReaderWriter;
use storage_service::start_storage_service_with_db;
use tokio::runtime::{Builder, Runtime};

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

pub struct LibraHandle {
    _rpc: Runtime,
    _mempool: Runtime,
    _state_synchronizer: StateSynchronizer,
    _network_runtimes: Vec<Runtime>,
    _consensus_runtime: Option<Runtime>,
    _debug: NodeDebugService,
    _backup: Runtime,
}

// Fetch chain ID from on-chain resource
fn fetch_chain_id(db: &DbReaderWriter) -> ChainId {
    let blob = db
        .reader
        .get_account_state_with_proof_by_version(
            libra_root_address(),
            (&*db.reader)
                .fetch_synced_version()
                .expect("[libra-node] failed fetching synced version."),
        )
        .expect("[libra-node] failed to get Libra root address account state")
        .0
        .expect("[libra-node] missing Libra root address account state");
    AccountState::try_from(&blob)
        .expect("[libra-node] failed to convert blob to account state")
        .get_chain_id_resource()
        .expect("[libra-node] failed to get chain ID resource")
        .expect("[libra-node] missing chain ID resource")
        .chain_id()
}

fn setup_chunk_executor(db: DbReaderWriter) -> Box<dyn ChunkExecutor> {
    Box::new(Executor::<LibraVM>::new(db))
}

fn setup_debug_interface(config: &NodeConfig, logger: Option<Arc<Logger>>) -> NodeDebugService {
    let addr = format!(
        "{}:{}",
        config.debug_interface.address, config.debug_interface.admission_control_node_debug_port,
    )
    .to_socket_addrs()
    .unwrap()
    .next()
    .unwrap();

    libra_trace::set_libra_trace(&config.debug_interface.libra_trace.sampling)
        .expect("Failed to set libra trace sampling rate.");

    NodeDebugService::new(addr, logger)
}

pub fn setup_environment(node_config: &NodeConfig, logger: Option<Arc<Logger>>) -> LibraHandle {
    let debug_if = setup_debug_interface(&node_config, logger);

    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)
    });

    let mut instant = Instant::now();
    let (libra_db, db_rw) = DbReaderWriter::wrap(
        LibraDB::open(
            &node_config.storage.dir(),
            false, /* readonly */
            node_config.storage.prune_window,
        )
        .expect("DB should open."),
    );
    let _simple_storage_service =
        start_storage_service_with_db(&node_config, Arc::clone(&libra_db));
    let backup_service = start_backup_service(
        node_config.storage.backup_service_address,
        Arc::clone(&libra_db),
    );

    let waypoint = node_config.base.waypoint.waypoint();
    println!("WWWW1111: {}", waypoint);
    // if there's genesis txn and waypoint, commit it if the result matches.
    if let Some(genesis) = get_genesis_txn(&node_config) {
        maybe_bootstrap::<LibraVM>(&db_rw, genesis, waypoint)
            .expect("Db-bootstrapper should not fail.");
    }

    debug!(
        "Storage service started in {} ms",
        instant.elapsed().as_millis()
    );

    instant = Instant::now();
    let chunk_executor = setup_chunk_executor(db_rw.clone());
    debug!(
        "ChunkExecutor setup in {} ms",
        instant.elapsed().as_millis()
    );
    let chain_id = fetch_chain_id(&db_rw);
    let mut network_runtimes = vec![];
    let mut state_sync_network_handles = vec![];
    let mut mempool_network_handles = vec![];
    let mut consensus_network_handles = None;
    let mut reconfig_subscriptions = vec![];

    let (mempool_reconfig_subscription, mempool_reconfig_events) =
        gen_mempool_reconfig_subscription();
    reconfig_subscriptions.push(mempool_reconfig_subscription);
    // consensus has to subscribe to ALL on-chain configs
    let (consensus_reconfig_subscription, consensus_reconfig_events) =
        gen_consensus_reconfig_subscription();
    if node_config.base.role.is_validator() {
        reconfig_subscriptions.push(consensus_reconfig_subscription);
    }

    // Gather all network configs into a single vector.
    // TODO:  consider explicitly encoding the role in the NetworkConfig
    let mut network_configs: Vec<(RoleType, &NetworkConfig)> = node_config
        .full_node_networks
        .iter()
        .map(|network_config| (RoleType::FullNode, network_config))
        .collect();
    if let Some(network_config) = node_config.validator_network.as_ref() {
        network_configs.push((RoleType::Validator, network_config));
    }

    let mut network_builders = Vec::new();

    // Instantiate every network and collect the requisite endpoints for state_sync, mempool, and consensus.
    for (idx, (role, network_config)) in network_configs.into_iter().enumerate() {
        // Perform common instantiation steps
        let mut network_builder = NetworkBuilder::create(chain_id, role, network_config);
        let network_id = network_config.network_id.clone();

        // Create the endpoints to connect the Network to StateSynchronizer.
        let (state_sync_sender, state_sync_events) = network_builder
            .add_protocol_handler(state_synchronizer::network::network_endpoint_config());
        state_sync_network_handles.push((
            NodeNetworkId::new(network_id.clone(), idx),
            state_sync_sender,
            state_sync_events,
        ));

        // Create the endpoints to connect the Network to mempool.
        let (mempool_sender, mempool_events) =
            network_builder.add_protocol_handler(libra_mempool::network::network_endpoint_config(
                // TODO:  Make this configuration option more clear.
                node_config.mempool.max_broadcasts_per_peer,
            ));
        mempool_network_handles.push((
            NodeNetworkId::new(network_id, idx),
            mempool_sender,
            mempool_events,
        ));

        match role {
            // Perform steps relevant specifically to Validator networks.
            RoleType::Validator => {
                // A valid config is allowed to have at most one ValidatorNetwork
                // TODO:  `expect_none` would be perfect here, once it is stable.
                if consensus_network_handles.is_some() {
                    panic!("There can be at most one validator network!");
                }

                consensus_network_handles =
                    Some(network_builder.add_protocol_handler(
                        consensus::network_interface::network_endpoint_config(),
                    ));
            }
            // Currently no FullNode network specific steps.
            RoleType::FullNode => (),
        }

        reconfig_subscriptions.append(network_builder.reconfig_subscriptions());

        network_builders.push(network_builder);
    }

    // Build the configured networks.
    for network_builder in &mut network_builders {
        debug!("Creating runtime for {}", network_builder.network_context());
        let runtime = Builder::new()
            .thread_name("network-")
            .threaded_scheduler()
            .enable_all()
            .build()
            .expect("Failed to start runtime. Won't be able to start networking.");
        network_builder.build(runtime.handle().clone());
        network_runtimes.push(runtime);
        debug!(
            "Network built for network context: {}",
            network_builder.network_context()
        );
    }

    // Start the configured networks.
    // TODO:  Collect all component starts at the end of this function
    for network_builder in &mut network_builders {
        network_builder.start();
    }

    // TODO set up on-chain discovery network based on UpstreamConfig.fallback_network
    // and pass network handles to mempool/state sync

    // 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(&db_rw.reader),
        chunk_executor,
        &node_config,
        waypoint,
        reconfig_subscriptions,
    );
    let (mp_client_sender, mp_client_events) = channel(AC_SMP_CHANNEL_BUFFER_SIZE);

    let rpc_runtime = bootstrap_rpc(&node_config, chain_id, libra_db.clone(), mp_client_sender);

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

    instant = Instant::now();
    let mempool = libra_mempool::bootstrap(
        node_config,
        Arc::clone(&db_rw.reader),
        mempool_network_handles,
        mp_client_events,
        consensus_requests,
        state_sync_requests,
        mempool_reconfig_events,
    );
    debug!("Mempool started in {} ms", instant.elapsed().as_millis());

    // StateSync should be instantiated and started before Consensus to avoid a cyclic dependency:
    // network provider -> consensus -> state synchronizer -> network provider.  This has resulted
    // in a deadlock as observed in GitHub issue #749.
    if let Some((consensus_network_sender, consensus_network_events)) = consensus_network_handles {
        // 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();
        consensus_runtime = Some(start_consensus(
            node_config,
            consensus_network_sender,
            consensus_network_events,
            state_synchronizer.create_client(),
            consensus_to_mempool_sender,
            libra_db,
            consensus_reconfig_events,
        ));
        debug!("Consensus started in {} ms", instant.elapsed().as_millis());
    }

    LibraHandle {
        _network_runtimes: network_runtimes,
        _rpc: rpc_runtime,
        _mempool: mempool,
        _state_synchronizer: state_synchronizer,
        _consensus_runtime: consensus_runtime,
        _debug: debug_if,
        _backup: backup_service,
    }
}
<commit_msg>graceful shutdown network runtimes in to avoid panic error log thread network- panicked at SelectNextSome polled after terminated<commit_after>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

use backup_service::start_backup_service;
use consensus::{consensus_provider::start_consensus, gen_consensus_reconfig_subscription};
use debug_interface::node_debug_service::NodeDebugService;
use executor::{db_bootstrapper::maybe_bootstrap, Executor};
use executor_types::ChunkExecutor;
use futures::{channel::mpsc::channel, executor::block_on};
use libra_config::{
    config::{NetworkConfig, NodeConfig, RoleType},
    network_id::NodeNetworkId,
    utils::get_genesis_txn,
};
use libra_json_rpc::bootstrap_from_config as bootstrap_rpc;
use libra_logger::{prelude::*, Logger};
use libra_mempool::gen_mempool_reconfig_subscription;
use libra_metrics::metric_server;
use libra_types::{
    account_config::libra_root_address, account_state::AccountState, chain_id::ChainId,
    move_resource::MoveStorage,
};
use libra_vm::LibraVM;
use libradb::LibraDB;
use network_builder::builder::NetworkBuilder;
use state_synchronizer::StateSynchronizer;
use std::{boxed::Box, convert::TryFrom, net::ToSocketAddrs, sync::Arc, thread, time::Instant};
use storage_interface::DbReaderWriter;
use storage_service::start_storage_service_with_db;
use tokio::runtime::{Builder, Runtime};

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

pub struct LibraHandle {
    _rpc: Runtime,
    _mempool: Runtime,
    _state_synchronizer: StateSynchronizer,
    network_runtimes: Vec<Runtime>,
    _consensus_runtime: Option<Runtime>,
    _debug: NodeDebugService,
    _backup: Runtime,
}

impl Drop for LibraHandle {
    fn drop(&mut self) {
        while self.network_runtimes.len() > 0 {
            self.network_runtimes.remove(0).shutdown_background();
        }
    }
}

// Fetch chain ID from on-chain resource
fn fetch_chain_id(db: &DbReaderWriter) -> ChainId {
    let blob = db
        .reader
        .get_account_state_with_proof_by_version(
            libra_root_address(),
            (&*db.reader)
                .fetch_synced_version()
                .expect("[libra-node] failed fetching synced version."),
        )
        .expect("[libra-node] failed to get Libra root address account state")
        .0
        .expect("[libra-node] missing Libra root address account state");
    AccountState::try_from(&blob)
        .expect("[libra-node] failed to convert blob to account state")
        .get_chain_id_resource()
        .expect("[libra-node] failed to get chain ID resource")
        .expect("[libra-node] missing chain ID resource")
        .chain_id()
}

fn setup_chunk_executor(db: DbReaderWriter) -> Box<dyn ChunkExecutor> {
    Box::new(Executor::<LibraVM>::new(db))
}

fn setup_debug_interface(config: &NodeConfig, logger: Option<Arc<Logger>>) -> NodeDebugService {
    let addr = format!(
        "{}:{}",
        config.debug_interface.address, config.debug_interface.admission_control_node_debug_port,
    )
    .to_socket_addrs()
    .unwrap()
    .next()
    .unwrap();

    libra_trace::set_libra_trace(&config.debug_interface.libra_trace.sampling)
        .expect("Failed to set libra trace sampling rate.");

    NodeDebugService::new(addr, logger)
}

pub fn setup_environment(node_config: &NodeConfig, logger: Option<Arc<Logger>>) -> LibraHandle {
    let debug_if = setup_debug_interface(&node_config, logger);

    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)
    });

    let mut instant = Instant::now();
    let (libra_db, db_rw) = DbReaderWriter::wrap(
        LibraDB::open(
            &node_config.storage.dir(),
            false, /* readonly */
            node_config.storage.prune_window,
        )
        .expect("DB should open."),
    );
    let _simple_storage_service =
        start_storage_service_with_db(&node_config, Arc::clone(&libra_db));
    let backup_service = start_backup_service(
        node_config.storage.backup_service_address,
        Arc::clone(&libra_db),
    );

    let waypoint = node_config.base.waypoint.waypoint();
    println!("WWWW1111: {}", waypoint);
    // if there's genesis txn and waypoint, commit it if the result matches.
    if let Some(genesis) = get_genesis_txn(&node_config) {
        maybe_bootstrap::<LibraVM>(&db_rw, genesis, waypoint)
            .expect("Db-bootstrapper should not fail.");
    }

    debug!(
        "Storage service started in {} ms",
        instant.elapsed().as_millis()
    );

    instant = Instant::now();
    let chunk_executor = setup_chunk_executor(db_rw.clone());
    debug!(
        "ChunkExecutor setup in {} ms",
        instant.elapsed().as_millis()
    );
    let chain_id = fetch_chain_id(&db_rw);
    let mut network_runtimes = vec![];
    let mut state_sync_network_handles = vec![];
    let mut mempool_network_handles = vec![];
    let mut consensus_network_handles = None;
    let mut reconfig_subscriptions = vec![];

    let (mempool_reconfig_subscription, mempool_reconfig_events) =
        gen_mempool_reconfig_subscription();
    reconfig_subscriptions.push(mempool_reconfig_subscription);
    // consensus has to subscribe to ALL on-chain configs
    let (consensus_reconfig_subscription, consensus_reconfig_events) =
        gen_consensus_reconfig_subscription();
    if node_config.base.role.is_validator() {
        reconfig_subscriptions.push(consensus_reconfig_subscription);
    }

    // Gather all network configs into a single vector.
    // TODO:  consider explicitly encoding the role in the NetworkConfig
    let mut network_configs: Vec<(RoleType, &NetworkConfig)> = node_config
        .full_node_networks
        .iter()
        .map(|network_config| (RoleType::FullNode, network_config))
        .collect();
    if let Some(network_config) = node_config.validator_network.as_ref() {
        network_configs.push((RoleType::Validator, network_config));
    }

    let mut network_builders = Vec::new();

    // Instantiate every network and collect the requisite endpoints for state_sync, mempool, and consensus.
    for (idx, (role, network_config)) in network_configs.into_iter().enumerate() {
        // Perform common instantiation steps
        let mut network_builder = NetworkBuilder::create(chain_id, role, network_config);
        let network_id = network_config.network_id.clone();

        // Create the endpoints to connect the Network to StateSynchronizer.
        let (state_sync_sender, state_sync_events) = network_builder
            .add_protocol_handler(state_synchronizer::network::network_endpoint_config());
        state_sync_network_handles.push((
            NodeNetworkId::new(network_id.clone(), idx),
            state_sync_sender,
            state_sync_events,
        ));

        // Create the endpoints to connect the Network to mempool.
        let (mempool_sender, mempool_events) =
            network_builder.add_protocol_handler(libra_mempool::network::network_endpoint_config(
                // TODO:  Make this configuration option more clear.
                node_config.mempool.max_broadcasts_per_peer,
            ));
        mempool_network_handles.push((
            NodeNetworkId::new(network_id, idx),
            mempool_sender,
            mempool_events,
        ));

        match role {
            // Perform steps relevant specifically to Validator networks.
            RoleType::Validator => {
                // A valid config is allowed to have at most one ValidatorNetwork
                // TODO:  `expect_none` would be perfect here, once it is stable.
                if consensus_network_handles.is_some() {
                    panic!("There can be at most one validator network!");
                }

                consensus_network_handles =
                    Some(network_builder.add_protocol_handler(
                        consensus::network_interface::network_endpoint_config(),
                    ));
            }
            // Currently no FullNode network specific steps.
            RoleType::FullNode => (),
        }

        reconfig_subscriptions.append(network_builder.reconfig_subscriptions());

        network_builders.push(network_builder);
    }

    // Build the configured networks.
    for network_builder in &mut network_builders {
        debug!("Creating runtime for {}", network_builder.network_context());
        let runtime = Builder::new()
            .thread_name("network-")
            .threaded_scheduler()
            .enable_all()
            .build()
            .expect("Failed to start runtime. Won't be able to start networking.");
        network_builder.build(runtime.handle().clone());
        network_runtimes.push(runtime);
        debug!(
            "Network built for network context: {}",
            network_builder.network_context()
        );
    }

    // Start the configured networks.
    // TODO:  Collect all component starts at the end of this function
    for network_builder in &mut network_builders {
        network_builder.start();
    }

    // TODO set up on-chain discovery network based on UpstreamConfig.fallback_network
    // and pass network handles to mempool/state sync

    // 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(&db_rw.reader),
        chunk_executor,
        &node_config,
        waypoint,
        reconfig_subscriptions,
    );
    let (mp_client_sender, mp_client_events) = channel(AC_SMP_CHANNEL_BUFFER_SIZE);

    let rpc_runtime = bootstrap_rpc(&node_config, chain_id, libra_db.clone(), mp_client_sender);

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

    instant = Instant::now();
    let mempool = libra_mempool::bootstrap(
        node_config,
        Arc::clone(&db_rw.reader),
        mempool_network_handles,
        mp_client_events,
        consensus_requests,
        state_sync_requests,
        mempool_reconfig_events,
    );
    debug!("Mempool started in {} ms", instant.elapsed().as_millis());

    // StateSync should be instantiated and started before Consensus to avoid a cyclic dependency:
    // network provider -> consensus -> state synchronizer -> network provider.  This has resulted
    // in a deadlock as observed in GitHub issue #749.
    if let Some((consensus_network_sender, consensus_network_events)) = consensus_network_handles {
        // 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();
        consensus_runtime = Some(start_consensus(
            node_config,
            consensus_network_sender,
            consensus_network_events,
            state_synchronizer.create_client(),
            consensus_to_mempool_sender,
            libra_db,
            consensus_reconfig_events,
        ));
        debug!("Consensus started in {} ms", instant.elapsed().as_millis());
    }

    LibraHandle {
        network_runtimes,
        _rpc: rpc_runtime,
        _mempool: mempool,
        _state_synchronizer: state_synchronizer,
        _consensus_runtime: consensus_runtime,
        _debug: debug_if,
        _backup: backup_service,
    }
}
<|endoftext|>


<commit_before>use std::process::{Child, Command};

use anyhow::{bail, Result};
use log::{debug, info, warn};
use nix::{
    sys::signal::{self, Signal},
    unistd::Pid,
};
use procfs::process::{all_processes, Process};

use crate::task_handler::ProcessAction;

pub fn compile_shell_command(command_string: &str) -> Command {
    let mut command = Command::new("sh");
    command.arg("-c").arg(command_string);

    command
}

/// Send a signal to one of Pueue's child process handles.
/// We need a special since there exists some inconsistent behavior.
///
/// In some circumstances and environments `sh -c $command` doesn't spawn a shell,
/// but rather spawns the `$command` directly.
///
/// This makes things a lot more complicated, since we need to either send signals
/// to the root process directly OR to all it's child processes.
/// This also affects the `--children` flag on all commands. We then have to either send the signal
/// to all direct children or to all of the childrens' children.
pub fn send_signal_to_child(
    child: &Child,
    action: &ProcessAction,
    send_to_children: bool,
) -> Result<bool> {
    let signal = get_signal_from_action(action);
    let pid = child.id() as i32;
    // Check whether this process actually spawned a shell.
    let is_shell = if let Ok(is_shell) = did_process_spawn_shell(pid) {
        is_shell
    } else {
        return Ok(false);
    };

    if is_shell {
        // If it's a shell, we have to send the signal to the actual shell and to all it's children.
        // There might be multiple children, for instance, when users use the `&` operator.
        // If the `send_to_children` flag is given, the

        // Send the signal to the shell, don't propagate to it's children yet.
        send_signal_to_process(pid, action, false)?;

        // Now send the signal to the shells child processes and their respective
        // children if the user wants to do so.
        let shell_children = get_child_processes(pid);
        for shell_child in shell_children {
            send_signal_to_process(shell_child.pid(), action, send_to_children)?;
        }
    } else {
        // If it isn't a shell, send the signal directly to the process.
        // Handle children normally.
        send_signal_to_process(pid, action, send_to_children)?;
    }

    signal::kill(Pid::from_raw(pid), signal)?;
    Ok(true)
}

/// This is a helper function to safely kill a child process.
/// It's purpose is to properly kill all processes and prevent any dangling processes.
///
/// Sadly, this needs some extra handling. Check the docstring of `send_signal_to_child` for
/// additional information on why this has to be done.
pub fn kill_child(task_id: usize, child: &mut Child, kill_children: bool) -> bool {
    let pid = child.id() as i32;

    // Check whether this process actually spawned a shell.
    let is_shell = if let Ok(is_shell) = did_process_spawn_shell(pid) {
        is_shell
    } else {
        return false;
    };

    // We have to kill the root process first, to prevent it from spawning new processes.
    // However, this prevents us from getting it's child processes afterwards.
    // That's why we have to get the list of child processes already now.
    let mut child_processes = None;
    if kill_children || is_shell {
        child_processes = Some(get_child_processes(pid));
    }

    // Kill the parent first
    match child.kill() {
        Err(_) => {
            info!("Task {} has already finished by itself", task_id);
            false
        }
        _ => {
            // Now kill all remaining children, after the parent has been killed.
            // If a shell is spawned, we have to manually send the kill signal to all children.
            // Otherwise only send a signal to all children if the `kill_children` flag is set.
            if let Some(child_processes) = child_processes {
                if is_shell {
                    for child_process in child_processes {
                        // Send the signal to each child process, show warning if this fails.
                        let process_pid = child_process.pid();
                        if let Err(error) =
                            send_signal_to_process(process_pid, &ProcessAction::Kill, kill_children)
                        {
                            warn!(
                                "Failed to send kill to pid {} with error {:?}",
                                process_pid, error
                            );
                        }
                    }
                } else if kill_children {
                    send_signal_to_processes(child_processes, &ProcessAction::Kill);
                }
            }

            true
        }
    }
}

/// Check whether a process's commandline string is actually a shell or not
fn did_process_spawn_shell(pid: i32) -> Result<bool> {
    // Get the /proc representation of the child, so we can do some checks
    let process = if let Ok(process) = Process::new(pid) {
        process
    } else {
        info!(
            "Process to kill has probably just gone away. Process {}",
            pid
        );
        bail!("Process has just gone away");
    };

    // Get the root command and, so we check whether it's actually a shell with `sh -c`.
    let mut cmdline = if let Ok(cmdline) = process.cmdline() {
        cmdline
    } else {
        info!(
            "Process to kill has probably just gone away. Process {}",
            pid
        );
        bail!("Process has just gone away");
    };

    if cmdline.len() < 3 {
        return Ok(false);
    }

    if cmdline.remove(0) != "sh" {
        return Ok(false);
    }

    if cmdline.remove(0) != "-c" {
        return Ok(false);
    }

    Ok(true)
}

/// Send a signal to a unix process.
fn send_signal_to_process(
    pid: i32,
    action: &ProcessAction,
    send_to_children: bool,
) -> Result<bool, nix::Error> {
    let signal = get_signal_from_action(action);
    debug!("Sending signal {} to {}", signal, pid);

    // Send the signal to all children, if that's what the user wants.
    if send_to_children {
        send_signal_to_children(pid, action);
    }

    signal::kill(Pid::from_raw(pid), signal)?;
    Ok(true)
}

/// A small helper that sends a signal to all children of a specific process by id.
fn send_signal_to_children(pid: i32, action: &ProcessAction) {
    send_signal_to_processes(get_child_processes(pid), action);
}

/// Send a signal to a list of processes
fn send_signal_to_processes(processes: Vec<Process>, action: &ProcessAction) {
    let signal = get_signal_from_action(action);
    for process in processes {
        // Process is no longer alive, skip this one.
        if !process.is_alive() {
            continue;
        }

        let pid = Pid::from_raw(process.pid);
        if let Err(error) = signal::kill(pid, signal) {
            warn!(
                "Failed send signal {:?} to Pid {}: {:?}",
                signal, process.pid, error
            );
        }
    }
}

/// Get all children of a specific process
fn get_child_processes(pid: i32) -> Vec<Process> {
    let all_processes = match all_processes() {
        Err(error) => {
            warn!("Failed to get full process list: {}", error);
            return Vec::new();
        }
        Ok(processes) => processes,
    };

    all_processes
        .into_iter()
        .filter(|process| process.stat.ppid == pid)
        .collect()
}

fn get_signal_from_action(action: &ProcessAction) -> Signal {
    match action {
        ProcessAction::Kill => Signal::SIGKILL,
        ProcessAction::Pause => Signal::SIGSTOP,
        ProcessAction::Resume => Signal::SIGCONT,
    }
}
<commit_msg>Add linux process tests<commit_after>use std::convert::TryInto;
use std::process::{Child, Command};

use anyhow::{bail, Result};
use log::{debug, info, warn};
use nix::{
    sys::signal::{self, Signal},
    unistd::Pid,
};
use procfs::process::{all_processes, Process};

use crate::task_handler::ProcessAction;

pub fn compile_shell_command(command_string: &str) -> Command {
    let mut command = Command::new("sh");
    command.arg("-c").arg(command_string);

    command
}

/// Send a signal to one of Pueue's child process handles.
/// We need a special since there exists some inconsistent behavior.
///
/// In some circumstances and environments `sh -c $command` doesn't spawn a shell,
/// but rather spawns the `$command` directly.
///
/// This makes things a lot more complicated, since we need to either send signals
/// to the root process directly OR to all it's child processes.
/// This also affects the `--children` flag on all commands. We then have to either send the signal
/// to all direct children or to all of the childrens' children.
pub fn send_signal_to_child(
    child: &Child,
    action: &ProcessAction,
    send_to_children: bool,
) -> Result<bool> {
    let signal = get_signal_from_action(action);
    let pid: i32 = child.id().try_into().unwrap();
    // Check whether this process actually spawned a shell.
    let is_shell = if let Ok(is_shell) = did_process_spawn_shell(pid) {
        is_shell
    } else {
        return Ok(false);
    };

    if is_shell {
        // If it's a shell, we have to send the signal to the actual shell and to all it's children.
        // There might be multiple children, for instance, when users use the `&` operator.
        // If the `send_to_children` flag is given, the

        // Send the signal to the shell, don't propagate to it's children yet.
        send_signal_to_process(pid, action, false)?;

        // Now send the signal to the shells child processes and their respective
        // children if the user wants to do so.
        let shell_children = get_child_processes(pid);
        for shell_child in shell_children {
            send_signal_to_process(shell_child.pid(), action, send_to_children)?;
        }
    } else {
        // If it isn't a shell, send the signal directly to the process.
        // Handle children normally.
        send_signal_to_process(pid, action, send_to_children)?;
    }

    signal::kill(Pid::from_raw(pid), signal)?;
    Ok(true)
}

/// This is a helper function to safely kill a child process.
/// It's purpose is to properly kill all processes and prevent any dangling processes.
///
/// Sadly, this needs some extra handling. Check the docstring of `send_signal_to_child` for
/// additional information on why this has to be done.
pub fn kill_child(task_id: usize, child: &mut Child, kill_children: bool) -> bool {
    let pid: i32 = child.id().try_into().unwrap();

    // Check whether this process actually spawned a shell.
    let is_shell = if let Ok(is_shell) = did_process_spawn_shell(pid) {
        is_shell
    } else {
        return false;
    };

    // We have to kill the root process first, to prevent it from spawning new processes.
    // However, this prevents us from getting it's child processes afterwards.
    // That's why we have to get the list of child processes already now.
    let mut child_processes = None;
    if kill_children || is_shell {
        child_processes = Some(get_child_processes(pid));
    }

    // Kill the parent first
    match child.kill() {
        Err(_) => {
            info!("Task {} has already finished by itself", task_id);
            false
        }
        _ => {
            // Now kill all remaining children, after the parent has been killed.
            // If a shell is spawned, we have to manually send the kill signal to all children.
            // Otherwise only send a signal to all children if the `kill_children` flag is set.
            if let Some(child_processes) = child_processes {
                if is_shell {
                    for child_process in child_processes {
                        // Send the signal to each child process, show warning if this fails.
                        let process_pid = child_process.pid();
                        if let Err(error) =
                            send_signal_to_process(process_pid, &ProcessAction::Kill, kill_children)
                        {
                            warn!(
                                "Failed to send kill to pid {} with error {:?}",
                                process_pid, error
                            );
                        }
                    }
                } else if kill_children {
                    send_signal_to_processes(child_processes, &ProcessAction::Kill);
                }
            }

            true
        }
    }
}

/// Check whether a process's commandline string is actually a shell or not
fn did_process_spawn_shell(pid: i32) -> Result<bool> {
    // Get the /proc representation of the child, so we can do some checks
    let process = if let Ok(process) = Process::new(pid) {
        process
    } else {
        info!(
            "Process to kill has probably just gone away. Process {}",
            pid
        );
        bail!("Process has just gone away");
    };

    // Get the root command and, so we check whether it's actually a shell with `sh -c`.
    let mut cmdline = if let Ok(cmdline) = process.cmdline() {
        cmdline
    } else {
        info!(
            "Process to kill has probably just gone away. Process {}",
            pid
        );
        bail!("Process has just gone away");
    };

    if cmdline.len() < 3 {
        return Ok(false);
    }

    if cmdline.remove(0) != "sh" {
        return Ok(false);
    }

    if cmdline.remove(0) != "-c" {
        return Ok(false);
    }

    Ok(true)
}

/// Send a signal to a unix process.
fn send_signal_to_process(
    pid: i32,
    action: &ProcessAction,
    send_to_children: bool,
) -> Result<bool, nix::Error> {
    let signal = get_signal_from_action(action);
    debug!("Sending signal {} to {}", signal, pid);

    // Send the signal to all children, if that's what the user wants.
    if send_to_children {
        send_signal_to_children(pid, action);
    }

    signal::kill(Pid::from_raw(pid), signal)?;
    Ok(true)
}

/// A small helper that sends a signal to all children of a specific process by id.
fn send_signal_to_children(pid: i32, action: &ProcessAction) {
    send_signal_to_processes(get_child_processes(pid), action);
}

/// Send a signal to a list of processes
fn send_signal_to_processes(processes: Vec<Process>, action: &ProcessAction) {
    let signal = get_signal_from_action(action);
    for process in processes {
        // Process is no longer alive, skip this one.
        if !process.is_alive() {
            continue;
        }

        let pid = Pid::from_raw(process.pid);
        if let Err(error) = signal::kill(pid, signal) {
            warn!(
                "Failed send signal {:?} to Pid {}: {:?}",
                signal, process.pid, error
            );
        }
    }
}

/// Get all children of a specific process
fn get_child_processes(pid: i32) -> Vec<Process> {
    let all_processes = match all_processes() {
        Err(error) => {
            warn!("Failed to get full process list: {}", error);
            return Vec::new();
        }
        Ok(processes) => processes,
    };

    all_processes
        .into_iter()
        .filter(|process| process.stat.ppid == pid)
        .collect()
}

fn get_signal_from_action(action: &ProcessAction) -> Signal {
    match action {
        ProcessAction::Kill => Signal::SIGKILL,
        ProcessAction::Pause => Signal::SIGSTOP,
        ProcessAction::Resume => Signal::SIGCONT,
    }
}

#[cfg(test)]
#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))]
mod tests {
    use super::*;
    use std::thread::sleep;
    use std::time::Duration;

    /// Assert that certain process id no longer exists
    fn process_is_gone(pid: i32) -> bool {
        match Process::new(pid) {
            Ok(process) => !process.is_alive(),
            Err(_) => true,
        }
    }

    #[test]
    fn test_spawn_command() {
        let mut child = compile_shell_command("echo 'this is a test'")
            .spawn()
            .expect("Failed to spawn echo");

        let ecode = child.wait().expect("failed to wait on echo");

        assert!(ecode.success());
    }

    #[test]
    /// Ensure a `sh -c` command will be properly killed without detached processes.
    fn test_shell_command_is_killed() {
        let mut child = compile_shell_command("sleep 60 & sleep 60 && echo 'this is a test'")
            .spawn()
            .expect("Failed to spawn echo");
        let pid: i32 = child.id().try_into().unwrap();

        // Make sure the process indeed spawned a shell.
        assert!(did_process_spawn_shell(pid).unwrap());

        // Sleep a little to give the shell time to spawn the sleep command
        sleep(Duration::from_millis(500));

        // Get all child processes, so we can make sure they no longer exist afterwards
        let child_processes = get_child_processes(pid);
        assert_eq!(child_processes.len(), 2);

        // Kill the process and make sure it'll be killed
        assert!(kill_child(0, &mut child, false));

        // Sleep a little to give all processes time to shutdown.
        sleep(Duration::from_millis(500));

        // Assert that the direct child (sh -c) has been killed.
        assert!(process_is_gone(pid));

        // Assert that all child processes have been killed
        for child_process in child_processes {
            assert!(process_is_gone(child_process.stat.pid));
        }
    }

    #[test]
    /// Ensure that a `sh -c` process with a child process that has children of it's own
    /// will properly kill all processes and their children's children without detached processes.
    fn test_shell_command_children_are_killed() {
        let mut child = compile_shell_command("bash -c 'sleep 60 && sleep 60' && sleep 60")
            .spawn()
            .expect("Failed to spawn echo");
        let pid: i32 = child.id().try_into().unwrap();

        // Make sure the process indeed spawned a shell.
        assert!(did_process_spawn_shell(pid).unwrap());

        // Sleep a little to give the shell time to spawn the sleep command
        sleep(Duration::from_millis(500));

        // Get all child processes and all childrens children,
        // so we can make sure they no longer exist afterwards
        let child_processes = get_child_processes(pid);
        assert_eq!(child_processes.len(), 1);
        let mut childrens_children = Vec::new();
        for child_process in &child_processes {
            childrens_children.extend(get_child_processes(child_process.stat.pid));
        }
        assert_eq!(childrens_children.len(), 1);

        // Kill the process and make sure its childen will be killed
        assert!(kill_child(0, &mut child, true));

        // Sleep a little to give all processes time to shutdown.
        sleep(Duration::from_millis(500));

        // Assert that the direct child (sh -c) has been killed.
        assert!(process_is_gone(pid));

        // Assert that all child processes have been killed
        for child_process in child_processes {
            assert!(process_is_gone(child_process.stat.pid));
        }

        // Assert that all children's child processes have been killed
        for child_process in childrens_children {
            assert!(process_is_gone(child_process.stat.pid));
        }
    }

    #[test]
    /// Ensure a normal command without `sh -c` will be killed.
    fn test_normal_command_is_killed() {
        let mut child = Command::new("sleep")
            .arg("60")
            .spawn()
            .expect("Failed to spawn echo");
        let pid: i32 = child.id().try_into().unwrap();

        // Make sure the process did not spawn a shell.
        assert!(!did_process_spawn_shell(pid).unwrap());

        // No childprocesses exist
        let child_processes = get_child_processes(pid);
        assert_eq!(child_processes.len(), 0);

        // Kill the process and make sure it'll be killed
        assert!(kill_child(0, &mut child, false));

        // Sleep a little to give all processes time to shutdown.
        sleep(Duration::from_millis(500));

        assert!(process_is_gone(pid));
    }

    #[test]
    /// Ensure a normal command and all it's children will be
    /// properly killed without any detached processes.
    fn test_normal_command_children_are_killed() {
        let mut child = Command::new("bash")
            .arg("-c")
            .arg("sleep 60 & sleep 60 && sleep 60")
            .spawn()
            .expect("Failed to spawn echo");
        let pid: i32 = child.id().try_into().unwrap();

        // Make sure the process indeed spawned a shell.
        assert!(!did_process_spawn_shell(pid).unwrap());

        // Sleep a little to give the shell time to spawn the sleep command
        sleep(Duration::from_millis(500));

        // Get all child processes, so we can make sure they no longer exist afterwards
        let child_processes = get_child_processes(pid);
        assert_eq!(child_processes.len(), 2);

        // Kill the process and make sure it'll be killed
        assert!(kill_child(0, &mut child, true));

        // Sleep a little to give all processes time to shutdown.
        sleep(Duration::from_millis(500));

        // Assert that the direct child (sh -c) has been killed.
        assert!(process_is_gone(pid));

        // Assert that all child processes have been killed
        for child_process in child_processes {
            assert!(process_is_gone(child_process.stat.pid));
        }
    }
}
<|endoftext|>


<commit_before>//! Implements polynomial operations in GF(2^8).

use std::rand;

use gf256::{div, mul};

/// Evaluate a polynomial, returning the Y value for the given X value.
pub fn eval(p: &Vec<u8>, x: u8) -> u8 {
    p.iter().rev().fold(0u8, |res, &v| mul(res, x) ^ v)
}

/// Generates a random polynomial of the Nth degree with a Y-intercept with the
/// given value.
pub fn generate<T: rand::Rng>(n: u8, y: u8, rng: &mut T) -> Vec<u8> {
    let mut p = Vec::with_capacity(n as usize);

    // Set its Y-intercept to the given value.
    p.push(y);

    // Generate random coefficients.
    p.extend(rng.gen_iter().take(n as usize - 2));

    // Ensure the Nth coefficient is non-zero, otherwise it's an (N-1)th-degree
    // polynomial.
    p.push(rng.gen_range(1, 255));

    p
}

/// Interpolates a vector of (X, Y) points, returning the Y value at zero.
pub fn y_intercept<'a>(points: &'a Vec<(u8, u8)>) -> u8 {
    let mut value = 0u8;
    for (i, &(ax, ay)) in points.iter().enumerate() {
        let mut weight = 1u8;
        for (j, &(bx, _)) in points.iter().enumerate() {
            if i != j {
                let top = bx; // xor 0
                let bottom = ax ^ bx;
                let factor = div(top, bottom);
                weight = mul(weight, factor);
            }
        }
        value ^= mul(weight, ay)
    }
    value
}

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

    #[test]
    fn test_eval() {
        assert_eq!(eval(&vec![1, 0, 2, 3], 2), 17);
    }

    #[test]
    fn test_generate() {
        let mut rng = rand::ChaChaRng::new_unseeded();
        assert_eq!(generate(5, 50, &mut rng),
                   vec![50, 118, 160, 64, 84])
    }

    #[test]
    fn test_y_intercept() {
        assert_eq!(y_intercept(&vec![(1,1), (2,2), (3,3)]), 0);
    }
}
<commit_msg>Simplify y_intercept.<commit_after>//! Implements polynomial operations in GF(2^8).

use std::rand;

use gf256::{div, mul};

/// Evaluate a polynomial, returning the Y value for the given X value.
pub fn eval(p: &Vec<u8>, x: u8) -> u8 {
    p.iter().rev().fold(0u8, |res, &v| mul(res, x) ^ v)
}

/// Generates a random polynomial of the Nth degree with a Y-intercept with the
/// given value.
pub fn generate<T: rand::Rng>(n: u8, y: u8, rng: &mut T) -> Vec<u8> {
    let mut p = Vec::with_capacity(n as usize);

    // Set its Y-intercept to the given value.
    p.push(y);

    // Generate random coefficients.
    p.extend(rng.gen_iter().take(n as usize - 2));

    // Ensure the Nth coefficient is non-zero, otherwise it's an (N-1)th-degree
    // polynomial.
    p.push(rng.gen_range(1, 255));

    p
}

/// Interpolates a vector of (X, Y) points, returning the Y value at zero.
pub fn y_intercept<'a>(points: &'a Vec<(u8, u8)>) -> u8 {
    let mut value = 0u8;
    for (i, &(ax, ay)) in points.iter().enumerate() {
        let mut weight = 1u8;
        for (j, &(bx, _)) in points.iter().enumerate() {
            if i != j {
                weight = mul(weight, div(bx, ax ^ bx));
            }
        }
        value ^= mul(weight, ay)
    }
    value
}

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

    #[test]
    fn test_eval() {
        assert_eq!(eval(&vec![1, 0, 2, 3], 2), 17);
    }

    #[test]
    fn test_generate() {
        let mut rng = rand::ChaChaRng::new_unseeded();
        assert_eq!(generate(5, 50, &mut rng),
                   vec![50, 118, 160, 64, 84])
    }

    #[test]
    fn test_y_intercept() {
        assert_eq!(y_intercept(&vec![(1,1), (2,2), (3,3)]), 0);
    }
}
<|endoftext|>


<commit_before>
use std::io;

use parser::ast::*;
use processing::structs::*;
use scope::context::*;
use scope::bindings::*;
use output::writers::*;

#[derive(Debug, Default)]
pub struct ValueWriterHtml {}

impl ValueWriter for ValueWriterHtml {
    fn write_literal_string(&mut self, w: &mut io::Write, s: &str) -> Result {
        write!(w, "{}", s)?;
        Ok(())
    }

    fn write_literal_number(&mut self, w: &mut io::Write, n: &i32) -> Result {
        write!(w, "{}", n)?;
        Ok(())
    }

    fn write_literal_bool(&mut self, w: &mut io::Write, b: bool) -> Result {
        if b { write!(w, "true")?; } else { write!(w, "false")?; }
        Ok(())
    }

    fn write_simple_binding(&mut self, _w: &mut io::Write, _ctx: &mut Context, _bindings: &BindingContext, _binding: &BindingType) -> Result {
        Ok(())
    }

    fn write_op(&mut self, _w: &mut io::Write, _op: &ExprOp) -> Result {
        Ok(())
    }

    fn write_undefined(&mut self, w: &mut io::Write) -> Result {
        write!(w, "[undefined]")?;
        Ok(())
    }
}
impl StaticValueWriter for ValueWriterHtml {}

#[derive(Debug, Default)]
pub struct ExpressionWriterHtml {}

impl ExpressionWriter for ExpressionWriterHtml {
    type V = ValueWriterHtml;

    fn write_expression(&mut self, w: &mut io::Write, doc: &Document, value_writer: &mut Self::V, ctx: &mut Context, bindings: &BindingContext, op: &ExprOp, left: &ExprValue, right: &ExprValue) -> Result {
        self.write_expr_to(w, doc, value_writer, ctx, bindings, left)?;
        value_writer.write_op(w, op)?;
        self.write_expr_to(w, doc, value_writer, ctx, bindings, right)?;

        Ok(())
    }

    fn write_apply_expression<'a, I: IntoIterator<Item = &'a ExprValue>>(&mut self, w: &mut io::Write, doc: &Document, value_writer: &mut Self::V, ctx: &mut Context, bindings: &BindingContext, a_op: &ExprApplyOp, arr: Option<I>) -> Result {
        match a_op {
            &ExprApplyOp::JoinString(ref sep) => {
                write!(w, "[")?;
                let mut first = true;
                if let Some(arr) = arr {
                    for v in arr {
                        if !first { write!(w, ", ")?; }
                        self.write_expr_to(w, doc, value_writer, ctx, bindings, v)?;
                        first = false;
                    }
                };
                write!(w, "].join(\"{}\")", sep.as_ref().map_or("", |s| s.as_str()))?;
            },
            _ => {}
        };
        Ok(())
    }

    fn write_array<'a, I: IntoIterator<Item = &'a ExprValue>>(&mut self, w: &mut io::Write, doc: &Document, value_writer: &mut Self::V, ctx: &mut Context, bindings: &BindingContext, arr: Option<I>, ty: Option<VarType>) -> Result {
        write!(w, "[array]")?;
        Ok(())
    }

    fn write_props<'a, I: IntoIterator<Item = &'a Prop>>(&mut self, w: &mut io::Write, doc: &Document, value_writer: &mut Self::V, ctx: &mut Context, bindings: &BindingContext, props: Option<I>) -> Result {
        // write!(w, "{{")?;
        // if let Some(props) = props {
        //     let mut first = true;
        //     for prop in props {
        //         if !first { write!(w, ", ")?; }
        //         write!(w, "\"{}\": ", &prop.0)?;
        //         if let Some(ref v) = prop.1 {
        //             self.write_expr_to(w, doc, value_writer, ctx, bindings, v)?;
        //         }
        //         first = false;
        //     };
        // };
        // write!(w, "}}")?;
        Ok(())
    }

    fn write_binding(&mut self, w: &mut io::Write, doc: &Document, value_writer: &mut Self::V, ctx: &mut Context, bindings: &BindingContext, binding: &BindingType) -> Result {
        match binding {
            &BindingType::ComponentPropBinding(ref key) => {
                // if let Some(sym) = ctx.resolve_sym(key) {
                if let Some(expr) = ctx.eval_key(doc, key) {
                    self.write_expr_to(w, doc, value_writer, ctx, bindings, &expr)?;
                };
            }

            _ => {}
        };
        Ok(())
    }

    fn write_symbol(&mut self, w: &mut io::Write, doc: &Document, value_writer: &mut Self::V, ctx: &mut Context, bindings: &BindingContext, sym: &Symbol) -> Result {
        match sym.sym_ref() {
            &SymbolReferenceType::InitialValue(_, box ref after) => {
                return self.write_symbol(w, doc, value_writer, ctx, bindings, after)?;
            }

            &SymbolReferenceType::Binding(ref binding) => {
                return self.write_binding(w, doc, value_writer, ctx, bindings, binding)?;
            }

            _ => {}
        };

        if let Some(ref expr) = sym.value() {
            self.write_expr_to(w, doc, value_writer, ctx, bindings, expr)?;
        };

        if let Some(expr) = ctx.eval_sym(doc, sym) {
            self.write_expr_to(w, doc, value_writer, ctx, bindings, &expr)?;
        };

        Ok(())
    }

}

// #[cfg(test)]
// mod tests {
//     use super::*;
//     use std::str;
//     use std::io::Write;
//     use scope::context::*;
//     use scope::bindings::*;

//     #[test]
//     fn test_stream_writers_value_writer_js_write_binding1() {
//         let mut value_writer = ValueWriterJs::default();
//         let mut ctx = Context::default();
//         let binding = BindingType::ReducerPathBinding("todo".into(), None);

//         {
//             let mut s: Vec<u8> = Default::default();
//             let bindings = BindingContext::default();
//             let res = value_writer.write_binding(&mut s, &mut ctx, &bindings, &binding);
//             assert!(res.is_ok());
//             assert_eq!(str::from_utf8(&s), Ok("store.getState().todo".into()));
//         }

//         {
//             let mut s: Vec<u8> = Default::default();
//             let bindings = BindingContext::default();
//             // let mut expr_writer = DefaultExpressionWriter::default();
//             let mut expr_writer = ExpressionWriterJs::default();
//             let expr = ExprValue::Binding(binding.clone());

//             let res = expr_writer.write_expr(&mut s, doc, &mut value_writer, &mut ctx, &bindings, &expr);
//             assert!(res.is_ok());
//             assert_eq!(str::from_utf8(&s), Ok("store.getState().todo".into()));
//         }
//     }

//     #[test]
//     fn test_stream_writers_value_writer_js_write_dynamic_expression1() {
//         let bindings = BindingContext::default();
//         let mut ctx = Context::default();
//         let binding = BindingType::ReducerPathBinding("todo".into(), None);
//         let literal_string = ExprValue::LiteralString("test".into());

//         let expr = ExprValue::Expr(ExprOp::Add,
//             Box::new(ExprValue::Binding(binding.clone())),
//             Box::new(literal_string.clone())
//         );

//         let mut value_writer = ValueWriterJs::default();
//         let mut expr_writer = ExpressionWriterJs::default();

//         let mut s: Vec<u8> = Default::default();
//         let res = expr_writer.write_expr(&mut s, doc, &mut value_writer, &mut ctx, &bindings, &expr);
//         assert!(res.is_ok());
//         assert_eq!(str::from_utf8(&s), Ok("store.getState().todo+\"test\"".into()));

//     }

//     #[test]
//     fn test_stream_writers_writerjs_write_dynamic_expression1() {
//         let bindings = BindingContext::default();
//         let mut ctx = Context::default();

//         let binding = BindingType::ReducerPathBinding("todo".into(), None);
//         let literal_string = ExprValue::LiteralString("test".into());

//         let expr = ExprValue::Expr(ExprOp::Add,
//             Box::new(ExprValue::Binding(binding.clone())),
//             Box::new(literal_string.clone())
//         );

//         let mut s: Vec<u8> = Default::default();
//         let mut writer = WriterJs::default();
//         // let res = writer.write_expr(&mut s, doc, &mut value_writer, &mut ctx, &bindings, &expr);
//         // assert!(res.is_ok());
//         // assert_eq!(str::from_utf8(&s), Ok("store.getState().todo+\"test\"".into()));

//     }
// }<commit_msg>Fix return values in `expr_writer_html::write_symbol`<commit_after>
use std::io;

use parser::ast::*;
use processing::structs::*;
use scope::context::*;
use scope::bindings::*;
use output::writers::*;

#[derive(Debug, Default)]
pub struct ValueWriterHtml {}

impl ValueWriter for ValueWriterHtml {
    fn write_literal_string(&mut self, w: &mut io::Write, s: &str) -> Result {
        write!(w, "{}", s)?;
        Ok(())
    }

    fn write_literal_number(&mut self, w: &mut io::Write, n: &i32) -> Result {
        write!(w, "{}", n)?;
        Ok(())
    }

    fn write_literal_bool(&mut self, w: &mut io::Write, b: bool) -> Result {
        if b { write!(w, "true")?; } else { write!(w, "false")?; }
        Ok(())
    }

    fn write_simple_binding(&mut self, _w: &mut io::Write, _ctx: &mut Context, _bindings: &BindingContext, _binding: &BindingType) -> Result {
        Ok(())
    }

    fn write_op(&mut self, _w: &mut io::Write, _op: &ExprOp) -> Result {
        Ok(())
    }

    fn write_undefined(&mut self, w: &mut io::Write) -> Result {
        write!(w, "[undefined]")?;
        Ok(())
    }
}
impl StaticValueWriter for ValueWriterHtml {}

#[derive(Debug, Default)]
pub struct ExpressionWriterHtml {}

impl ExpressionWriter for ExpressionWriterHtml {
    type V = ValueWriterHtml;

    fn write_expression(&mut self, w: &mut io::Write, doc: &Document, value_writer: &mut Self::V, ctx: &mut Context, bindings: &BindingContext, op: &ExprOp, left: &ExprValue, right: &ExprValue) -> Result {
        self.write_expr_to(w, doc, value_writer, ctx, bindings, left)?;
        value_writer.write_op(w, op)?;
        self.write_expr_to(w, doc, value_writer, ctx, bindings, right)?;

        Ok(())
    }

    fn write_apply_expression<'a, I: IntoIterator<Item = &'a ExprValue>>(&mut self, w: &mut io::Write, doc: &Document, value_writer: &mut Self::V, ctx: &mut Context, bindings: &BindingContext, a_op: &ExprApplyOp, arr: Option<I>) -> Result {
        match a_op {
            &ExprApplyOp::JoinString(ref sep) => {
                write!(w, "[")?;
                let mut first = true;
                if let Some(arr) = arr {
                    for v in arr {
                        if !first { write!(w, ", ")?; }
                        self.write_expr_to(w, doc, value_writer, ctx, bindings, v)?;
                        first = false;
                    }
                };
                write!(w, "].join(\"{}\")", sep.as_ref().map_or("", |s| s.as_str()))?;
            },
            _ => {}
        };
        Ok(())
    }

    fn write_array<'a, I: IntoIterator<Item = &'a ExprValue>>(&mut self, w: &mut io::Write, doc: &Document, value_writer: &mut Self::V, ctx: &mut Context, bindings: &BindingContext, arr: Option<I>, ty: Option<VarType>) -> Result {
        write!(w, "[array]")?;
        Ok(())
    }

    fn write_props<'a, I: IntoIterator<Item = &'a Prop>>(&mut self, w: &mut io::Write, doc: &Document, value_writer: &mut Self::V, ctx: &mut Context, bindings: &BindingContext, props: Option<I>) -> Result {
        // write!(w, "{{")?;
        // if let Some(props) = props {
        //     let mut first = true;
        //     for prop in props {
        //         if !first { write!(w, ", ")?; }
        //         write!(w, "\"{}\": ", &prop.0)?;
        //         if let Some(ref v) = prop.1 {
        //             self.write_expr_to(w, doc, value_writer, ctx, bindings, v)?;
        //         }
        //         first = false;
        //     };
        // };
        // write!(w, "}}")?;
        Ok(())
    }

    fn write_binding(&mut self, w: &mut io::Write, doc: &Document, value_writer: &mut Self::V, ctx: &mut Context, bindings: &BindingContext, binding: &BindingType) -> Result {
        match binding {
            &BindingType::ComponentPropBinding(ref key) => {
                // if let Some(sym) = ctx.resolve_sym(key) {
                if let Some(expr) = ctx.eval_key(doc, key) {
                    self.write_expr_to(w, doc, value_writer, ctx, bindings, &expr)?;
                };
            }

            _ => {}
        };
        Ok(())
    }

    fn write_symbol(&mut self, w: &mut io::Write, doc: &Document, value_writer: &mut Self::V, ctx: &mut Context, bindings: &BindingContext, sym: &Symbol) -> Result {
        match sym.sym_ref() {
            &SymbolReferenceType::InitialValue(_, box ref after) => {
                return self.write_symbol(w, doc, value_writer, ctx, bindings, after);
            }

            &SymbolReferenceType::Binding(ref binding) => {
                return self.write_binding(w, doc, value_writer, ctx, bindings, binding);
            }

            _ => {}
        };

        if let Some(ref expr) = sym.value() {
            return self.write_expr_to(w, doc, value_writer, ctx, bindings, expr);
        };

        if let Some(expr) = ctx.eval_sym(doc, sym) {
            return self.write_expr_to(w, doc, value_writer, ctx, bindings, &expr);
        };

        Ok(())
    }

}

// #[cfg(test)]
// mod tests {
//     use super::*;
//     use std::str;
//     use std::io::Write;
//     use scope::context::*;
//     use scope::bindings::*;

//     #[test]
//     fn test_stream_writers_value_writer_js_write_binding1() {
//         let mut value_writer = ValueWriterJs::default();
//         let mut ctx = Context::default();
//         let binding = BindingType::ReducerPathBinding("todo".into(), None);

//         {
//             let mut s: Vec<u8> = Default::default();
//             let bindings = BindingContext::default();
//             let res = value_writer.write_binding(&mut s, &mut ctx, &bindings, &binding);
//             assert!(res.is_ok());
//             assert_eq!(str::from_utf8(&s), Ok("store.getState().todo".into()));
//         }

//         {
//             let mut s: Vec<u8> = Default::default();
//             let bindings = BindingContext::default();
//             // let mut expr_writer = DefaultExpressionWriter::default();
//             let mut expr_writer = ExpressionWriterJs::default();
//             let expr = ExprValue::Binding(binding.clone());

//             let res = expr_writer.write_expr(&mut s, doc, &mut value_writer, &mut ctx, &bindings, &expr);
//             assert!(res.is_ok());
//             assert_eq!(str::from_utf8(&s), Ok("store.getState().todo".into()));
//         }
//     }

//     #[test]
//     fn test_stream_writers_value_writer_js_write_dynamic_expression1() {
//         let bindings = BindingContext::default();
//         let mut ctx = Context::default();
//         let binding = BindingType::ReducerPathBinding("todo".into(), None);
//         let literal_string = ExprValue::LiteralString("test".into());

//         let expr = ExprValue::Expr(ExprOp::Add,
//             Box::new(ExprValue::Binding(binding.clone())),
//             Box::new(literal_string.clone())
//         );

//         let mut value_writer = ValueWriterJs::default();
//         let mut expr_writer = ExpressionWriterJs::default();

//         let mut s: Vec<u8> = Default::default();
//         let res = expr_writer.write_expr(&mut s, doc, &mut value_writer, &mut ctx, &bindings, &expr);
//         assert!(res.is_ok());
//         assert_eq!(str::from_utf8(&s), Ok("store.getState().todo+\"test\"".into()));

//     }

//     #[test]
//     fn test_stream_writers_writerjs_write_dynamic_expression1() {
//         let bindings = BindingContext::default();
//         let mut ctx = Context::default();

//         let binding = BindingType::ReducerPathBinding("todo".into(), None);
//         let literal_string = ExprValue::LiteralString("test".into());

//         let expr = ExprValue::Expr(ExprOp::Add,
//             Box::new(ExprValue::Binding(binding.clone())),
//             Box::new(literal_string.clone())
//         );

//         let mut s: Vec<u8> = Default::default();
//         let mut writer = WriterJs::default();
//         // let res = writer.write_expr(&mut s, doc, &mut value_writer, &mut ctx, &bindings, &expr);
//         // assert!(res.is_ok());
//         // assert_eq!(str::from_utf8(&s), Ok("store.getState().todo+\"test\"".into()));

//     }
// }<|endoftext|>