<commit_before>use std::{
    cell::{Cell, RefCell},
    sync::Arc,
};

use crate::prelude::*;

use super::behaviors::{MouseBehavior, SelectionBehavior};

static CONTAINER: &'static str = "container";
static ITEMS_PANEL: &'static str = "items_panel";

type SelectedItem = Option<Entity>;

#[derive(Debug, Copy, Clone)]
enum Action {
    CheckMouseUpOutside { x: f64, y: f64 },
}

/// The `ComboBoxItemState` handles the interaction an selection of a `ComboBoxItem`.
#[derive(Default, AsAny)]
pub struct ComboBoxItemState {
    request_selection_toggle: Cell<bool>,
    index: usize,
    selected_container: Entity,
    combo_box: Entity,
    // ugly work around for item builder context clone, todo make it better 😉
    builder: Option<Arc<RefCell<dyn Fn(&mut BuildContext, usize) -> Entity + 'static>>>,
}

impl ComboBoxItemState {
    fn toggle_selection(&self) {
        self.request_selection_toggle.set(true);
    }
}

impl State for ComboBoxItemState {
    fn update(&mut self, _: &mut Registry, ctx: &mut Context<'_>) {
        let selected_index = ctx
            .get_widget(self.combo_box)
            .clone_or_default::<i32>("selected_index");
        let selected: bool = *ctx.widget().get("selected");

        if selected_index >= 0 && (selected_index as usize) == self.index && !selected {
            self.request_selection_toggle.set(true);
        }

        if !ctx.widget().get::<bool>("enabled") || !self.request_selection_toggle.get() {
            return;
        }
        self.request_selection_toggle.set(false);

        let entity = ctx.entity;

        // unselect previous selected item.
        if let Some(item) = ctx
            .get_widget(self.combo_box)
            .clone::<SelectedItem>("selected_item")
        {
            ctx.get_widget(item).set("selected", false);
            ctx.get_widget(item).update_theme_by_state(false);
        }

        ctx.widget().set("selected", true);
        ctx.widget().update_theme_by_state(false);
        ctx.get_widget(self.combo_box)
            .set("selected_index", self.index as i32);
        ctx.get_widget(self.combo_box)
            .set("selected_item", Some(entity));

        // Add selected content to combobox
        let index = self.index;
        let selected_container = self.selected_container;
        if let Some(builder) = &self.builder {
            ctx.clear_children_of(selected_container);
            let build_context = &mut ctx.build_context();
            let selected_content = builder.borrow()(build_context, index);
            build_context.append_child(selected_container, selected_content);
        }
    }
}

widget!(
    /// The `ComboBoxItem` describes an item inside of a `ComboBox`.
    ///
    /// **CSS element:** `combo_box_item``
    ComboBoxItem<ComboBoxItemState>: MouseHandler {
        /// Sets or shares the background property.
        background: Brush,

        /// Sets or shares the border radius property.
        border_radius: f64,

        /// Sets or shares the border thickness property.
        border_width: Thickness,

        /// Sets or shares the border brush property.
        border_brush: Brush,

        /// Sets or shares the foreground property.
        foreground: Brush,

        /// Sets or share the font size property.
        font_size: f64,

        /// Sets or shares the font property.
        font: String,

        /// Sets or shares the padding property.
        padding: Thickness,

        /// Sets or shares the pressed property.
        pressed: bool,

        /// Sets or shares the selected property.
        selected: bool
    }
);

impl ComboBoxItem {
    fn selected_container(mut self, selected_container: impl Into<Entity>) -> Self {
        self.state_mut().selected_container = selected_container.into();
        self
    }

    fn index(mut self, index: usize) -> Self {
        self.state_mut().index = index;
        self
    }

    fn combo_box(mut self, combo_box: impl Into<Entity>) -> Self {
        self.state_mut().combo_box = combo_box.into();
        self
    }

    // Define the template build function for the selected content of the ComboBoxItems.
    fn items_builder(
        &mut self,
        builder: &Arc<RefCell<dyn Fn(&mut BuildContext, usize) -> Entity + 'static>>,
    ) {
        self.state_mut().builder = Some(builder.clone());
    }
}

impl Template for ComboBoxItem {
    fn template(self, id: Entity, _: &mut BuildContext) -> Self {
        self.name("ComboBoxItem")
            .min_width(64.0)
            .height(24.0)
            .selected(false)
            .pressed(false)
            .element("combo_box_item")
            .padding(0.0)
            .background("transparent")
            .border_radius(0.0)
            .border_width(0.0)
            .border_brush("transparent")
            .foreground(colors::LINK_WATER_COLOR)
            .font_size(32.0)
            .font("Roboto Regular")
            .on_click(move |states, _| {
                states.get::<ComboBoxItemState>(id).toggle_selection();
                false
            })
    }

    fn render_object(&self) -> Box<dyn RenderObject> {
        Box::new(RectangleRenderObject)
    }

    fn layout(&self) -> Box<dyn Layout> {
        Box::new(PaddingLayout::new())
    }
}

/// The `ComboBoxState` is used to manipulate the position of the thumb of the slider widget.
#[derive(Default, AsAny)]
pub struct ComboBoxState {
    popup: Entity,
    action: Option<Action>,
    builder: Option<Arc<RefCell<dyn Fn(&mut BuildContext, usize) -> Entity + 'static>>>,
    count: usize,
    items_panel: Entity,
    selected_container: Entity,
}

impl ComboBoxState {
    fn action(&mut self, action: impl Into<Option<Action>>) {
        self.action = action.into();
    }

    // closes the popup on mouse up outside of the combobox and popup.
    fn close_popup(&mut self, ctx: &mut Context, x: f64, y: f64) {
        let combo_box_position = ctx.widget().clone::<Point>("position");
        let combo_box_bounds = ctx.widget().clone::<Rectangle>("bounds");

        let combo_box_global_bounds = Rectangle::new(
            combo_box_position.x,
            combo_box_position.y,
            combo_box_bounds.width,
            combo_box_bounds.height,
        );

        let popup_position = ctx.get_widget(self.popup).clone::<Point>("position");
        let popup_bounds = ctx.get_widget(self.popup).clone::<Rectangle>("bounds");

        let popup_global_bounds = Rectangle::new(
            popup_position.x,
            popup_position.y,
            popup_bounds.width,
            popup_bounds.height,
        );

        if !combo_box_global_bounds.contains((x, y)) && !popup_global_bounds.contains((x, y)) {
            ctx.widget().set("selected", false);
            ctx.get_widget(self.popup)
                .set("visibility", Visibility::Collapsed);
            ctx.get_widget(self.popup).update_theme_by_state(false);
            ctx.widget().update_theme_by_state(false);
        }
    }
}

impl State for ComboBoxState {
    fn update(&mut self, _: &mut Registry, ctx: &mut Context<'_>) {
        let count = ctx.widget().clone_or_default::<usize>("count");
        let entity = ctx.entity;

        // build the combobox items
        if count != self.count {
            if let Some(builder) = &self.builder {
                ctx.clear_children_of(self.items_panel);

                for i in 0..count {
                    let item = {
                        let build_context = &mut ctx.build_context();
                        let child = builder.borrow()(build_context, i);
                        let mut item = ComboBoxItem::create()
                            .index(i)
                            .combo_box(entity)
                            .selected_container(self.selected_container);

                        if let Some(builder) = &self.builder {
                            item.items_builder(builder);
                        }
                        let item = item.build(build_context);

                        let mouse_behavior = MouseBehavior::create().build(build_context);
                        build_context.register_shared_property::<Selector>(
                            "selector",
                            mouse_behavior,
                            item,
                        );
                        build_context.register_shared_property::<bool>(
                            "pressed",
                            mouse_behavior,
                            item,
                        );
                        build_context.append_child(item, mouse_behavior);

                        build_context.register_shared_property::<Brush>("foreground", child, item);
                        build_context.register_shared_property::<f32>("opacity", item, entity);
                        build_context.register_shared_property::<f32>("opacity", child, entity);
                        build_context.register_shared_property::<f64>("font_size", child, item);
                        build_context.append_child(self.items_panel, item);
                        build_context.append_child(mouse_behavior, child);

                        item
                    };
                    ctx.get_widget(item).update_properties_by_theme();
                }
            }

            self.count = count;
        }
    }

    fn update_post_layout(&mut self, _: &mut Registry, ctx: &mut Context<'_>) {
        if self.action.is_none() || !(*ctx.widget().get::<bool>("selected")) {
            return;
        }

        if let Some(action) = self.action {
            match action {
                Action::CheckMouseUpOutside { x, y } => {
                    self.close_popup(ctx, x, y);
                }
            }
        }
    }
}

// todo use code of list view item, by create combobox item insert entity of popup container

widget!(
    /// The `ComboBox` represents an selection widget with a drop-down list.
    ///
    /// **CSS element:** `combo_box`
    ComboBox<ComboBoxState>: MouseHandler, ChangedHandler {
        /// Sets or shares the background property.
        background: Brush,

        /// Sets or shares the border radius property.
        border_radius: f64,

        /// Sets or shares the border thickness property.
        border_width: Thickness,

        /// Sets or shares the border brush property.
        border_brush: Brush,

        /// Sets or shares the foreground property.
        foreground: Brush,

        /// Sets or share the font size property.
        font_size: f64,

        /// Sets or shares the font property.
        font: String,

        /// Sets or shared the count.
        count: usize,

        /// Sets or shares the selected index. If the value is -1 no item is selected.
        selected_index: i32,

        /// The entity of the selected item.
        selected_item: SelectedItem,

        /// Sets or shares the padding property.
        padding: Thickness,

        /// Sets or shares the pressed property.
        pressed: bool,

        /// Sets or shares the flag if the drop down is open.
        selected: bool
    }
);

impl ComboBox {
    /// Define the template build function for the content of the ComboBoxItems.
    pub fn items_builder<F: Fn(&mut BuildContext, usize) -> Entity + 'static + Clone>(
        mut self,
        builder: F,
    ) -> Self {
        self.state_mut().builder = Some(Arc::new(RefCell::new(builder)));
        self
    }
}

impl Template for ComboBox {
    fn template(mut self, id: Entity, ctx: &mut BuildContext) -> Self {
        let selected_container = Container::create().attach(Grid::column(0)).build(ctx);

        let container = Container::create()
            .id(CONTAINER)
            .background(id)
            .border_radius(id)
            .border_width(id)
            .border_brush(id)
            .padding(id)
            .child(
                Grid::create()
                    .columns(
                        Columns::create()
                            .column("*")
                            .column(4.0)
                            .column(14.0)
                            .build(),
                    )
                    .child(selected_container)
                    .child(
                        FontIconBlock::create()
                            .attach(Grid::column(2))
                            .vertical_alignment("center")
                            .icon(material_font_icons::ARROW_DOWN_ICON)
                            .build(ctx),
                    )
                    .build(ctx),
            )
            .build(ctx);
        self.state_mut().selected_container = selected_container;

        let items_panel = Stack::create()
            .vertical_alignment("start")
            .id(ITEMS_PANEL)
            .orientation("vertical")
            .build(ctx);

        self.state_mut().items_panel = items_panel;
        let scroll_viewer = ScrollViewer::create()
            .scroll_viewer_mode(("disabled", "auto"))
            .child(items_panel)
            .build(ctx);

        let popup = Popup::create()
            .height(200.0)
            .open(("selected", id))
            .child(scroll_viewer)
            .child(
                ScrollIndicator::create()
                    .padding(2.0)
                    .content_id(items_panel.0)
                    .scroll_offset(scroll_viewer)
                    .opacity(id)
                    .build(ctx),
            )
            .target(container.0)
            .build(ctx);
        self.state_mut().popup = popup;

        let _ = ctx.append_child_to_overlay(popup);

        self.name("ComboBox")
            .element("combo_box")
            .height(32.0)
            .min_width(80.0)
            .selected(false)
            .selected_index(-1)
            .child(
                MouseBehavior::create()
                    .pressed(id)
                    .enabled(id)
                    .target(id.0)
                    .child(
                        SelectionBehavior::create()
                            .selected(id)
                            .enabled(id)
                            .target(id.0)
                            .child(container)
                            .build(ctx),
                    )
                    .build(ctx),
            )
            .on_global_mouse_up(move |states, e| {
                states
                    .get_mut::<ComboBoxState>(id)
                    .action(Action::CheckMouseUpOutside { x: e.x, y: e.y })
            })
    }
}
<commit_msg>Made `ComboBox` close when an item is selected<commit_after>use std::{
    cell::{Cell, RefCell},
    sync::Arc,
};

use crate::prelude::*;

use super::behaviors::{MouseBehavior, SelectionBehavior};

static CONTAINER: &'static str = "container";
static ITEMS_PANEL: &'static str = "items_panel";

type SelectedItem = Option<Entity>;

#[derive(Debug, Copy, Clone)]
enum Action {
    CheckMouseUpOutside { x: f64, y: f64 },
}

/// The `ComboBoxItemState` handles the interaction an selection of a `ComboBoxItem`.
#[derive(Default, AsAny)]
pub struct ComboBoxItemState {
    request_selection_toggle: Cell<bool>,
    index: usize,
    selected_container: Entity,
    combo_box: Entity,
    // ugly work around for item builder context clone, todo make it better 😉
    builder: Option<Arc<RefCell<dyn Fn(&mut BuildContext, usize) -> Entity + 'static>>>,
}

impl ComboBoxItemState {
    fn toggle_selection(&self) {
        self.request_selection_toggle.set(true);
    }
}

impl State for ComboBoxItemState {
    fn update(&mut self, _: &mut Registry, ctx: &mut Context<'_>) {
        let selected_index = ctx
            .get_widget(self.combo_box)
            .clone_or_default::<i32>("selected_index");
        let selected: bool = *ctx.widget().get("selected");

        if selected_index >= 0 && (selected_index as usize) == self.index && !selected {
            self.request_selection_toggle.set(true);
        }

        if !ctx.widget().get::<bool>("enabled") || !self.request_selection_toggle.get() {
            return;
        }
        self.request_selection_toggle.set(false);

        let entity = ctx.entity;

        // unselect previous selected item.
        if let Some(item) = ctx
            .get_widget(self.combo_box)
            .clone::<SelectedItem>("selected_item")
        {
            ctx.get_widget(item).set("selected", false);
            ctx.get_widget(item).update_theme_by_state(false);
        }

        ctx.widget().set("selected", true);
        ctx.widget().update_theme_by_state(false);
        ctx.get_widget(self.combo_box)
            .set("selected_index", self.index as i32);
        ctx.get_widget(self.combo_box)
            .set("selected_item", Some(entity));

        // Add selected content to combobox
        let index = self.index;
        let selected_container = self.selected_container;
        if let Some(builder) = &self.builder {
            ctx.clear_children_of(selected_container);
            let build_context = &mut ctx.build_context();
            let selected_content = builder.borrow()(build_context, index);
            build_context.append_child(selected_container, selected_content);
        }
    }
}

widget!(
    /// The `ComboBoxItem` describes an item inside of a `ComboBox`.
    ///
    /// **CSS element:** `combo_box_item``
    ComboBoxItem<ComboBoxItemState>: MouseHandler {
        /// Sets or shares the background property.
        background: Brush,

        /// Sets or shares the border radius property.
        border_radius: f64,

        /// Sets or shares the border thickness property.
        border_width: Thickness,

        /// Sets or shares the border brush property.
        border_brush: Brush,

        /// Sets or shares the foreground property.
        foreground: Brush,

        /// Sets or share the font size property.
        font_size: f64,

        /// Sets or shares the font property.
        font: String,

        /// Sets or shares the padding property.
        padding: Thickness,

        /// Sets or shares the pressed property.
        pressed: bool,

        /// Sets or shares the selected property.
        selected: bool
    }
);

impl ComboBoxItem {
    fn selected_container(mut self, selected_container: impl Into<Entity>) -> Self {
        self.state_mut().selected_container = selected_container.into();
        self
    }

    fn index(mut self, index: usize) -> Self {
        self.state_mut().index = index;
        self
    }

    fn combo_box(mut self, combo_box: impl Into<Entity>) -> Self {
        self.state_mut().combo_box = combo_box.into();
        self
    }

    // Define the template build function for the selected content of the ComboBoxItems.
    fn items_builder(
        &mut self,
        builder: &Arc<RefCell<dyn Fn(&mut BuildContext, usize) -> Entity + 'static>>,
    ) {
        self.state_mut().builder = Some(builder.clone());
    }
}

impl Template for ComboBoxItem {
    fn template(self, id: Entity, _: &mut BuildContext) -> Self {
        self.name("ComboBoxItem")
            .min_width(64.0)
            .height(24.0)
            .selected(false)
            .pressed(false)
            .element("combo_box_item")
            .padding(0.0)
            .background("transparent")
            .border_radius(0.0)
            .border_width(0.0)
            .border_brush("transparent")
            .foreground(colors::LINK_WATER_COLOR)
            .font_size(32.0)
            .font("Roboto Regular")
            .on_click(move |states, _| {
                states.get::<ComboBoxItemState>(id).toggle_selection();
                false
            })
    }

    fn render_object(&self) -> Box<dyn RenderObject> {
        Box::new(RectangleRenderObject)
    }

    fn layout(&self) -> Box<dyn Layout> {
        Box::new(PaddingLayout::new())
    }
}

/// The `ComboBoxState` is used to manipulate the position of the thumb of the slider widget.
#[derive(Default, AsAny)]
pub struct ComboBoxState {
    popup: Entity,
    action: Option<Action>,
    builder: Option<Arc<RefCell<dyn Fn(&mut BuildContext, usize) -> Entity + 'static>>>,
    count: usize,
    items_panel: Entity,
    selected_container: Entity,
}

impl ComboBoxState {
    fn action(&mut self, action: impl Into<Option<Action>>) {
        self.action = action.into();
    }

    // closes the popup on mouse up outside of the combobox and popup.
    fn close_popup(&mut self, ctx: &mut Context, x: f64, y: f64) {
        let combo_box_position = ctx.widget().clone::<Point>("position");
        let combo_box_bounds = ctx.widget().clone::<Rectangle>("bounds");

        let combo_box_global_bounds = Rectangle::new(
            combo_box_position.x,
            combo_box_position.y,
            combo_box_bounds.width,
            combo_box_bounds.height,
        );

        let popup_position = ctx.get_widget(self.popup).clone::<Point>("position");
        let popup_bounds = ctx.get_widget(self.popup).clone::<Rectangle>("bounds");

        let popup_global_bounds = Rectangle::new(
            popup_position.x,
            popup_position.y,
            popup_bounds.width,
            popup_bounds.height,
        );

        if !combo_box_global_bounds.contains((x, y)) {
            ctx.widget().set("selected", false);
            ctx.get_widget(self.popup)
                .set("visibility", Visibility::Collapsed);
            ctx.get_widget(self.popup).update_theme_by_state(false);
            ctx.widget().update_theme_by_state(false);
        }
    }
}

impl State for ComboBoxState {
    fn update(&mut self, _: &mut Registry, ctx: &mut Context<'_>) {
        let count = ctx.widget().clone_or_default::<usize>("count");
        let entity = ctx.entity;

        // build the combobox items
        if count != self.count {
            if let Some(builder) = &self.builder {
                ctx.clear_children_of(self.items_panel);

                for i in 0..count {
                    let item = {
                        let build_context = &mut ctx.build_context();
                        let child = builder.borrow()(build_context, i);
                        let mut item = ComboBoxItem::create()
                            .index(i)
                            .combo_box(entity)
                            .selected_container(self.selected_container);

                        if let Some(builder) = &self.builder {
                            item.items_builder(builder);
                        }
                        let item = item.build(build_context);

                        let mouse_behavior = MouseBehavior::create().build(build_context);
                        build_context.register_shared_property::<Selector>(
                            "selector",
                            mouse_behavior,
                            item,
                        );
                        build_context.register_shared_property::<bool>(
                            "pressed",
                            mouse_behavior,
                            item,
                        );
                        build_context.append_child(item, mouse_behavior);

                        build_context.register_shared_property::<Brush>("foreground", child, item);
                        build_context.register_shared_property::<f32>("opacity", item, entity);
                        build_context.register_shared_property::<f32>("opacity", child, entity);
                        build_context.register_shared_property::<f64>("font_size", child, item);
                        build_context.append_child(self.items_panel, item);
                        build_context.append_child(mouse_behavior, child);

                        item
                    };
                    ctx.get_widget(item).update_properties_by_theme();
                }
            }

            self.count = count;
        }
    }

    fn update_post_layout(&mut self, _: &mut Registry, ctx: &mut Context<'_>) {
        if self.action.is_none() || !(*ctx.widget().get::<bool>("selected")) {
            return;
        }

        if let Some(action) = self.action {
            match action {
                Action::CheckMouseUpOutside { x, y } => {
                    self.close_popup(ctx, x, y);
                }
            }
        }
    }
}

// todo use code of list view item, by create combobox item insert entity of popup container

widget!(
    /// The `ComboBox` represents an selection widget with a drop-down list.
    ///
    /// **CSS element:** `combo_box`
    ComboBox<ComboBoxState>: MouseHandler, ChangedHandler {
        /// Sets or shares the background property.
        background: Brush,

        /// Sets or shares the border radius property.
        border_radius: f64,

        /// Sets or shares the border thickness property.
        border_width: Thickness,

        /// Sets or shares the border brush property.
        border_brush: Brush,

        /// Sets or shares the foreground property.
        foreground: Brush,

        /// Sets or share the font size property.
        font_size: f64,

        /// Sets or shares the font property.
        font: String,

        /// Sets or shared the count.
        count: usize,

        /// Sets or shares the selected index. If the value is -1 no item is selected.
        selected_index: i32,

        /// The entity of the selected item.
        selected_item: SelectedItem,

        /// Sets or shares the padding property.
        padding: Thickness,

        /// Sets or shares the pressed property.
        pressed: bool,

        /// Sets or shares the flag if the drop down is open.
        selected: bool
    }
);

impl ComboBox {
    /// Define the template build function for the content of the ComboBoxItems.
    pub fn items_builder<F: Fn(&mut BuildContext, usize) -> Entity + 'static + Clone>(
        mut self,
        builder: F,
    ) -> Self {
        self.state_mut().builder = Some(Arc::new(RefCell::new(builder)));
        self
    }
}

impl Template for ComboBox {
    fn template(mut self, id: Entity, ctx: &mut BuildContext) -> Self {
        let selected_container = Container::create().attach(Grid::column(0)).build(ctx);

        let container = Container::create()
            .id(CONTAINER)
            .background(id)
            .border_radius(id)
            .border_width(id)
            .border_brush(id)
            .padding(id)
            .child(
                Grid::create()
                    .columns(
                        Columns::create()
                            .column("*")
                            .column(4.0)
                            .column(14.0)
                            .build(),
                    )
                    .child(selected_container)
                    .child(
                        FontIconBlock::create()
                            .attach(Grid::column(2))
                            .vertical_alignment("center")
                            .icon(material_font_icons::ARROW_DOWN_ICON)
                            .build(ctx),
                    )
                    .build(ctx),
            )
            .build(ctx);
        self.state_mut().selected_container = selected_container;

        let items_panel = Stack::create()
            .vertical_alignment("start")
            .id(ITEMS_PANEL)
            .orientation("vertical")
            .build(ctx);

        self.state_mut().items_panel = items_panel;
        let scroll_viewer = ScrollViewer::create()
            .scroll_viewer_mode(("disabled", "auto"))
            .child(items_panel)
            .build(ctx);

        let popup = Popup::create()
            .height(200.0)
            .open(("selected", id))
            .child(scroll_viewer)
            .child(
                ScrollIndicator::create()
                    .padding(2.0)
                    .content_id(items_panel.0)
                    .scroll_offset(scroll_viewer)
                    .opacity(id)
                    .build(ctx),
            )
            .target(container.0)
            .build(ctx);
        self.state_mut().popup = popup;

        let _ = ctx.append_child_to_overlay(popup);

        self.name("ComboBox")
            .element("combo_box")
            .height(32.0)
            .min_width(80.0)
            .selected(false)
            .selected_index(-1)
            .child(
                MouseBehavior::create()
                    .pressed(id)
                    .enabled(id)
                    .target(id.0)
                    .child(
                        SelectionBehavior::create()
                            .selected(id)
                            .enabled(id)
                            .target(id.0)
                            .child(container)
                            .build(ctx),
                    )
                    .build(ctx),
            )
            .on_global_mouse_up(move |states, e| {
                states
                    .get_mut::<ComboBoxState>(id)
                    .action(Action::CheckMouseUpOutside { x: e.x, y: e.y })
            })
    }
}
<|endoftext|>


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

//! For each transaction the VM executes, the VM will output a `WriteSet` that contains each access
//! path it updates. For each access path, the VM can either give its new value or delete it.

use crate::access_path::AccessPath;
use anyhow::Result;
use serde::{Deserialize, Serialize};

#[derive(Clone, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub enum WriteOp {
    Deletion,
    Value(Vec<u8>),
}

impl WriteOp {
    #[inline]
    pub fn is_deletion(&self) -> bool {
        match self {
            WriteOp::Deletion => true,
            WriteOp::Value(_) => false,
        }
    }
}

impl std::fmt::Debug for WriteOp {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            WriteOp::Value(value) => write!(
                f,
                "Value({})",
                value
                    .iter()
                    .map(|byte| format!("{:02x}", byte))
                    .collect::<String>()
            ),
            WriteOp::Deletion => write!(f, "Deletion"),
        }
    }
}

/// `WriteSet` contains all access paths that one transaction modifies. Each of them is a `WriteOp`
/// where `Value(val)` means that serialized representation should be updated to `val`, and
/// `Deletion` means that we are going to delete this access path.
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub struct WriteSet(WriteSetMut);

impl WriteSet {
    #[inline]
    pub fn len(&self) -> usize {
        self.0.len()
    }

    #[inline]
    pub fn is_empty(&self) -> bool {
        self.0.is_empty()
    }

    #[inline]
    pub fn iter(&self) -> ::std::slice::Iter<'_, (AccessPath, WriteOp)> {
        self.into_iter()
    }

    #[inline]
    pub fn into_mut(self) -> WriteSetMut {
        self.0
    }
}

/// A mutable version of `WriteSet`.
///
/// This is separate because it goes through validation before becoming an immutable `WriteSet`.
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub struct WriteSetMut {
    write_set: Vec<(AccessPath, WriteOp)>,
}

impl WriteSetMut {
    pub fn new(write_set: Vec<(AccessPath, WriteOp)>) -> Self {
        Self { write_set }
    }

    pub fn push(&mut self, item: (AccessPath, WriteOp)) {
        self.write_set.push(item);
    }

    #[inline]
    pub fn len(&self) -> usize {
        self.write_set.len()
    }

    #[inline]
    pub fn is_empty(&self) -> bool {
        self.write_set.is_empty()
    }

    pub fn freeze(self) -> Result<WriteSet> {
        // TODO: add structural validation
        Ok(WriteSet(self))
    }
}

impl ::std::iter::FromIterator<(AccessPath, WriteOp)> for WriteSetMut {
    fn from_iter<I: IntoIterator<Item = (AccessPath, WriteOp)>>(iter: I) -> Self {
        let mut ws = WriteSetMut::default();
        for write in iter {
            ws.push((write.0, write.1));
        }
        ws
    }
}

impl<'a> IntoIterator for &'a WriteSet {
    type Item = &'a (AccessPath, WriteOp);
    type IntoIter = ::std::slice::Iter<'a, (AccessPath, WriteOp)>;

    fn into_iter(self) -> Self::IntoIter {
        self.0.write_set.iter()
    }
}

impl ::std::iter::IntoIterator for WriteSet {
    type Item = (AccessPath, WriteOp);
    type IntoIter = ::std::vec::IntoIter<(AccessPath, WriteOp)>;

    fn into_iter(self) -> Self::IntoIter {
        self.0.write_set.into_iter()
    }
}
<commit_msg>Removing len from types/src/write_set.rs<commit_after>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

//! For each transaction the VM executes, the VM will output a `WriteSet` that contains each access
//! path it updates. For each access path, the VM can either give its new value or delete it.

use crate::access_path::AccessPath;
use anyhow::Result;
use serde::{Deserialize, Serialize};

#[derive(Clone, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub enum WriteOp {
    Deletion,
    Value(Vec<u8>),
}

impl WriteOp {
    #[inline]
    pub fn is_deletion(&self) -> bool {
        match self {
            WriteOp::Deletion => true,
            WriteOp::Value(_) => false,
        }
    }
}

impl std::fmt::Debug for WriteOp {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            WriteOp::Value(value) => write!(
                f,
                "Value({})",
                value
                    .iter()
                    .map(|byte| format!("{:02x}", byte))
                    .collect::<String>()
            ),
            WriteOp::Deletion => write!(f, "Deletion"),
        }
    }
}

/// `WriteSet` contains all access paths that one transaction modifies. Each of them is a `WriteOp`
/// where `Value(val)` means that serialized representation should be updated to `val`, and
/// `Deletion` means that we are going to delete this access path.
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub struct WriteSet(WriteSetMut);

impl WriteSet {
    #[inline]
    pub fn is_empty(&self) -> bool {
        self.0.is_empty()
    }

    #[inline]
    pub fn iter(&self) -> ::std::slice::Iter<'_, (AccessPath, WriteOp)> {
        self.into_iter()
    }

    #[inline]
    pub fn into_mut(self) -> WriteSetMut {
        self.0
    }
}

/// A mutable version of `WriteSet`.
///
/// This is separate because it goes through validation before becoming an immutable `WriteSet`.
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub struct WriteSetMut {
    write_set: Vec<(AccessPath, WriteOp)>,
}

impl WriteSetMut {
    pub fn new(write_set: Vec<(AccessPath, WriteOp)>) -> Self {
        Self { write_set }
    }

    pub fn push(&mut self, item: (AccessPath, WriteOp)) {
        self.write_set.push(item);
    }

    #[inline]
    pub fn is_empty(&self) -> bool {
        self.write_set.is_empty()
    }

    pub fn freeze(self) -> Result<WriteSet> {
        // TODO: add structural validation
        Ok(WriteSet(self))
    }
}

impl ::std::iter::FromIterator<(AccessPath, WriteOp)> for WriteSetMut {
    fn from_iter<I: IntoIterator<Item = (AccessPath, WriteOp)>>(iter: I) -> Self {
        let mut ws = WriteSetMut::default();
        for write in iter {
            ws.push((write.0, write.1));
        }
        ws
    }
}

impl<'a> IntoIterator for &'a WriteSet {
    type Item = &'a (AccessPath, WriteOp);
    type IntoIter = ::std::slice::Iter<'a, (AccessPath, WriteOp)>;

    fn into_iter(self) -> Self::IntoIter {
        self.0.write_set.iter()
    }
}

impl ::std::iter::IntoIterator for WriteSet {
    type Item = (AccessPath, WriteOp);
    type IntoIter = ::std::vec::IntoIter<(AccessPath, WriteOp)>;

    fn into_iter(self) -> Self::IntoIter {
        self.0.write_set.into_iter()
    }
}
<|endoftext|>


<commit_before>#![allow(missing_debug_implementations)]

//! This module implement all core commands.
use toml::Value;

use std::fs;
use std::path::Path;
use std::net::SocketAddr;
use std::collections::BTreeMap;

use blockchain::GenesisConfig;
use helpers::generate_testnet_config;
use config::ConfigFile;
use node::NodeConfig;
use storage::Storage;
use crypto;

use super::internal::{Command, Feedback};
use super::{Argument, Context, CommandName};

const DEFAULT_EXONUM_LISTEN_PORT: u16 = 6333;
use helpers::clap::{ValidatorIdent, ConfigTemplate, KeyConfig, PubKeyConfig};
// TODO:How to split `NodeConfig`, from services configs?
// We should extend `NodeConfig` type to take services configs aswell.

pub struct RunCommand;

impl RunCommand {

    pub fn name() -> CommandName {
        "run"
    }

    #[cfg(not(feature="memorydb"))]
    pub fn db_helper(ctx: &Context) -> Storage {
        use storage::{LevelDB, LevelDBOptions};

        let path = ctx.arg::<String>("LEVELDB_PATH")
                      .expect("LEVELDB_PATH not found.");
        let mut options = LevelDBOptions::new();
        options.create_if_missing = true;
        LevelDB::new(Path::new(&path), options).unwrap()
    }

    #[cfg(feature="memorydb")]
    pub fn db_helper(_: &Context) -> Storage {
        use storage::MemoryDB;
        MemoryDB::new()
    }

    fn node_config(ctx: &Context) -> NodeConfig {
        let path = ctx.arg::<String>("NODE_CONFIG_PATH")
                      .expect("NODE_CONFIG_PATH not found.");
        ConfigFile::load(path).unwrap()
    }

    fn public_api_address(ctx: &Context) -> Option<SocketAddr> {
        ctx.arg("PUBLIC_API_ADDRESS").ok()
    }

    fn private_api_address(ctx: &Context) -> Option<SocketAddr> {
        ctx.arg("PRIVATE_API_ADDRESS").ok()
    }
}

impl Command for RunCommand {
    fn args(&self) -> Vec<Argument> {
        vec![
            Argument::new_named("NODE_CONFIG_PATH", true,
            "Path to node configuration file.", "c", "node-config"),
            Argument::new_named("LEVELDB_PATH", true,
            "Use leveldb database with the given path.", "d", "leveldb"),
            Argument::new_named("PUBLIC_API_ADDRESS", false,
            "Listen address for public api.", None, "public-api-address"),
            Argument::new_named("PRIVATE_API_ADDRESS", false,
            "Listen address for private api.", None, "private-api-address"),
        ]
    }

    fn name(&self) -> CommandName {
        Self::name()
    }

    fn about(&self) -> &str {
        "Run application"
    }

    fn execute(&self,
               mut context: Context,
               exts: &Fn(Context) -> Context) -> Feedback {
        let config = Self::node_config(&context);
        let public_addr = Self::public_api_address(&context);
        let private_addr = Self::private_api_address(&context);

        context.set("node_config", config);
        let mut new_context = exts(context);
        let mut config: NodeConfig = new_context
                                    .get("node_config")
                                    .expect("cant load node_config");
        // Override api options
        if let Some(addr) = public_addr {
            config.api.public_api_address = Some(addr);
        }
        if let Some(addr) = private_addr {
            config.api.private_api_address = Some(addr);
        }
        new_context.set("node_config", config);

        Feedback::RunNode(new_context)
    }
}

pub struct KeyGeneratorCommand;

impl KeyGeneratorCommand {
    pub fn name() -> CommandName {
        "keygen"
    }
}

impl Command for KeyGeneratorCommand {
    fn args(&self) -> Vec<Argument> {
        vec![
            Argument::new_positional("KEYCHAIN", true,
            "Path to key config."),
        ]
    }

    fn name(&self) -> CommandName {
        Self::name()
    }

    fn about(&self) -> &str {
        "Generate node secret and public keys."
    }

    fn execute(&self,
               mut context: Context,
               exts: &Fn(Context) -> Context) -> Feedback {
        let (pub_key, sec_key) = crypto::gen_keypair();
        let keyconfig = context.arg::<String>("KEYCHAIN")
                              .expect("expected keychain path");

        let pub_key_path = Path::new(&keyconfig).with_extension("pub");
        context.set("validator_ident", BTreeMap::<String, Value>::default());
        context.set("template", BTreeMap::<String, Value>::default());
        let new_context = exts(context);
        let services_pub_keys = new_context.get("services_pub_keys");
        let services_sec_keys = new_context.get("services_sec_keys");

        let pub_key_config: PubKeyConfig = PubKeyConfig {
            public_key: pub_key,
            services_pub_keys: services_pub_keys
                                .expect("service_pub_keys not found after exts call"),
        };
        // save pub_key seperately
        ConfigFile::save(&pub_key_config, &pub_key_path)
                    .expect("Could not write public key file.");

        let config = KeyConfig {
            public_key: pub_key,
            secret_key: sec_key,
            services_sec_keys: services_sec_keys
                                .expect("service_sec_keys not found after exts call"),
        };

        ConfigFile::save(&config, keyconfig)
                    .expect("Could not write keychain file.");
        Feedback::None
    }
}

/// implements command for template generating
pub struct GenerateTemplateCommand;
impl GenerateTemplateCommand {
    pub fn name() -> CommandName {
        "generate-template"
    }
}

impl Command for GenerateTemplateCommand {
    fn args(&self) -> Vec<Argument> {
        vec![
            Argument::new_positional("COUNT", true,
            "Validator total count."),
            Argument::new_positional("TEMPLATE", true,
            "Path to template config."),
        ]
    }

    fn name(&self) -> CommandName {
        Self::name()
    }

    fn about(&self) -> &str {
        "Generate basic config template."
    }

    fn execute(&self,
               mut context: Context,
               exts: &Fn(Context) -> Context) -> Feedback {
        let template_path = context.arg::<String>("TEMPLATE")
                                   .expect("template not found");
        let count = context.arg("COUNT")
                                   .expect("expected count to be int");

        context.set("VALUE", BTreeMap::<String, Value>::default());
        let new_context = exts(context);
        let values = new_context.get("VALUE").unwrap_or_default();

        let template = ConfigTemplate {
            count: count,
            services: values,
            ..ConfigTemplate::default()
        };

        ConfigFile::save(&template, template_path)
                        .expect("Could not write template file.");
        Feedback::None
    }
}

/// `add-validator` - append validator to template.
/// Automaticaly share keys from public key config.
pub struct AddValidatorCommand;
impl AddValidatorCommand {
    pub fn name() -> CommandName {
        "add-validator"
    }
}

impl Command for AddValidatorCommand {
    fn args(&self) -> Vec<Argument> {
        vec![
            Argument::new_positional("TEMPLATE", true,
            "Path to template config."),
            Argument::new_positional("PUBLIC_KEY", true,
            "Path to public key file."),
            Argument::new_named("LISTEN_ADDR", true,
            "Path to public key file.", "a", "listen-addr"),
        ]
    }

    fn name(&self) -> CommandName {
        Self::name()
    }

    fn about(&self) -> &str {
        "Preinit configuration, add validator to config template."
    }

    fn execute(&self,
               mut context: Context,
               exts: &Fn(Context) -> Context) -> Feedback {
        let template_path = context.arg::<String>("TEMPLATE")
                                   .expect("template not found");
        let public_key_path = context.arg::<String>("PUBLIC_KEY")
                                   .expect("public_key path not found");
        let addr = context.arg::<String>("LISTEN_ADDR").unwrap_or_default();

        let mut addr_parts = addr.split(':');
        let template: ConfigTemplate = ConfigFile::load(&template_path).unwrap();
        let public_key_config: PubKeyConfig = ConfigFile::load(public_key_path).unwrap();

        let addr_str = format!("{}:{}",
                           addr_parts.next()
                                     .unwrap_or("0.0.0.0"),
                           addr_parts.next()
                                     .map_or(DEFAULT_EXONUM_LISTEN_PORT,
                                                |s| s.parse()
                                                     .expect("could not parse port")));
        let addr = addr_str.parse().unwrap();
        let template =
        if !template.validators.contains_key(&public_key_config.public_key) {
            if template.validators.len() >= template.count {
                panic!("This template already full.");
            }

            let ident = ValidatorIdent {
                addr: addr,
                variables: BTreeMap::default(),
                keys: public_key_config.services_pub_keys,
            };
            context.set("validator_ident", ident);
            context.set("template", template);

            let new_context = exts(context);
            let ident = new_context.get("validator_ident")
                            .expect("validator_ident not found after call exts");
            let mut template: ConfigTemplate = new_context.get("template")
                            .expect("template not found after call exts");

            template.validators.insert(public_key_config.public_key, ident);
            template
        } else {
            panic!("This node already in template")
        };

        ConfigFile::save(&template, &template_path).unwrap();
        Feedback::None
    }
}

pub struct InitCommand;
impl InitCommand {
    pub fn name() -> CommandName {
        "init"
    }
}

impl Command for InitCommand {
    fn args(&self) -> Vec<Argument> {
        vec![
            Argument::new_positional("FULL_TEMPLATE", true,
            "Path to full template."),
            Argument::new_positional("KEYCHAIN", true,
            "Path to keychain config."),
            Argument::new_positional("CONFIG_PATH", true,
            "Path to output node config."),
        ]
    }

    fn name(&self) -> CommandName {
        Self::name()
    }

    fn about(&self) -> &str {
        "Toolchain to generate configuration."
    }

    fn execute(&self,
               mut context: Context,
               exts: &Fn(Context) -> Context) -> Feedback {
        let template_path = context.arg::<String>("FULL_TEMPLATE")
                                   .expect("template not found");
        let template_path = Path::new(&template_path);
        let keychain_path = context.arg::<String>("KEYCHAIN")
                                   .expect("keychain path not found");
        let keychain_path = Path::new(&keychain_path);

        let config_path = context.arg::<String>("CONFIG_PATH")
                                   .expect("config path not found");
        let config_path = Path::new(&config_path);

        let template: ConfigTemplate = ConfigFile::load(template_path)
                                                .expect("Failed to load config template.");
        let keychain: KeyConfig = ConfigFile::load(keychain_path)
                                                .expect("Failed to load key config.");

        if template.validators.len() != template.count {
            panic!("Template should be full.");
        }

        let genesis = GenesisConfig::new(template.validators.iter().map(|(k, _)| *k));
        let peers = template
            .validators
            .iter()
            .map(|(_, ident)| ident.addr)
            .collect();
        let config = {
            let validator_ident = &template.validators
                                            .get(&keychain.public_key)
                                            .expect("validator not found in template");

            NodeConfig {
                listen_address: validator_ident.addr,
                network: Default::default(),
                whitelist: Default::default(),
                peers: peers,
                public_key: keychain.public_key,
                secret_key: keychain.secret_key,
                genesis: genesis,
                api: Default::default(),
                services_configs: Default::default(),
            }
        };

        context.set("node_config", config);
        context.set("template", template);
        context.set("services_sec_keys", keychain.services_sec_keys);

        let new_context = exts(context);

        let value: Value = new_context.get("node_config")
            .expect("Could not create config from template, services return error");
        ConfigFile::save(&value, config_path)
                .expect("Could not write config file.");

        Feedback::None
    }
}

pub struct GenerateTestnetCommand;
impl GenerateTestnetCommand {
    pub fn name() -> CommandName {
        "generate-testnet"
    }
}

impl Command for GenerateTestnetCommand {
    fn args(&self) -> Vec<Argument> {
        vec![
            Argument::new_named("OUTPUT_DIR", true,
                "Path to directory where save configs.",
                "o", "output_dir"),
            Argument::new_named("START_PORT", false,
                "Port number started from which should validators listen.",
                "p", "start"),
            Argument::new_positional("COUNT", true,
                "Count of validators in testnet."),
        ]
    }

    fn name(&self) -> CommandName {
        Self::name()
    }

    fn about(&self) -> &str {
        "Generates genesis configuration for testnet"
    }

    fn execute(&self,
               context: Context,
               _: &Fn(Context) -> Context) -> Feedback {

        let dir = context.arg::<String>("OUTPUT_DIR").expect("output dir");
        let count: u8 = context.arg("COUNT")
                                .expect("count as int");
        let start_port = context.arg::<u16>("START_PORT")
                                .unwrap_or(DEFAULT_EXONUM_LISTEN_PORT);

        let dir = Path::new(&dir);
        let dir = dir.join("validators");
        if !dir.exists() {
            fs::create_dir_all(&dir).unwrap();
        }

        let configs = generate_testnet_config(count, start_port);
        for (idx, cfg) in configs.into_iter().enumerate() {
            let file_name = format!("{}.toml", idx);
            ConfigFile::save(&cfg, &dir.join(file_name)).unwrap();
        }

        Feedback::None
    }
}
<commit_msg>fix add-validator command<commit_after>#![allow(missing_debug_implementations)]

//! This module implement all core commands.
use toml::Value;

use std::fs;
use std::path::Path;
use std::net::SocketAddr;
use std::collections::BTreeMap;

use blockchain::GenesisConfig;
use helpers::generate_testnet_config;
use config::ConfigFile;
use node::NodeConfig;
use storage::Storage;
use crypto;

use super::internal::{Command, Feedback};
use super::{Argument, Context, CommandName};

const DEFAULT_EXONUM_LISTEN_PORT: u16 = 6333;
use helpers::clap::{ValidatorIdent, ConfigTemplate, KeyConfig, PubKeyConfig};
// TODO:How to split `NodeConfig`, from services configs?
// We should extend `NodeConfig` type to take services configs aswell.

pub struct RunCommand;

impl RunCommand {

    pub fn name() -> CommandName {
        "run"
    }

    #[cfg(not(feature="memorydb"))]
    pub fn db_helper(ctx: &Context) -> Storage {
        use storage::{LevelDB, LevelDBOptions};

        let path = ctx.arg::<String>("LEVELDB_PATH")
                      .expect("LEVELDB_PATH not found.");
        let mut options = LevelDBOptions::new();
        options.create_if_missing = true;
        LevelDB::new(Path::new(&path), options).unwrap()
    }

    #[cfg(feature="memorydb")]
    pub fn db_helper(_: &Context) -> Storage {
        use storage::MemoryDB;
        MemoryDB::new()
    }

    fn node_config(ctx: &Context) -> NodeConfig {
        let path = ctx.arg::<String>("NODE_CONFIG_PATH")
                      .expect("NODE_CONFIG_PATH not found.");
        ConfigFile::load(path).unwrap()
    }

    fn public_api_address(ctx: &Context) -> Option<SocketAddr> {
        ctx.arg("PUBLIC_API_ADDRESS").ok()
    }

    fn private_api_address(ctx: &Context) -> Option<SocketAddr> {
        ctx.arg("PRIVATE_API_ADDRESS").ok()
    }
}

impl Command for RunCommand {
    fn args(&self) -> Vec<Argument> {
        vec![
            Argument::new_named("NODE_CONFIG_PATH", true,
            "Path to node configuration file.", "c", "node-config"),
            Argument::new_named("LEVELDB_PATH", true,
            "Use leveldb database with the given path.", "d", "leveldb"),
            Argument::new_named("PUBLIC_API_ADDRESS", false,
            "Listen address for public api.", None, "public-api-address"),
            Argument::new_named("PRIVATE_API_ADDRESS", false,
            "Listen address for private api.", None, "private-api-address"),
        ]
    }

    fn name(&self) -> CommandName {
        Self::name()
    }

    fn about(&self) -> &str {
        "Run application"
    }

    fn execute(&self,
               mut context: Context,
               exts: &Fn(Context) -> Context) -> Feedback {
        let config = Self::node_config(&context);
        let public_addr = Self::public_api_address(&context);
        let private_addr = Self::private_api_address(&context);

        context.set("node_config", config);
        let mut new_context = exts(context);
        let mut config: NodeConfig = new_context
                                    .get("node_config")
                                    .expect("cant load node_config");
        // Override api options
        if let Some(addr) = public_addr {
            config.api.public_api_address = Some(addr);
        }
        if let Some(addr) = private_addr {
            config.api.private_api_address = Some(addr);
        }
        new_context.set("node_config", config);

        Feedback::RunNode(new_context)
    }
}

pub struct KeyGeneratorCommand;

impl KeyGeneratorCommand {
    pub fn name() -> CommandName {
        "keygen"
    }
}

impl Command for KeyGeneratorCommand {
    fn args(&self) -> Vec<Argument> {
        vec![
            Argument::new_positional("KEYCHAIN", true,
            "Path to key config."),
        ]
    }

    fn name(&self) -> CommandName {
        Self::name()
    }

    fn about(&self) -> &str {
        "Generate node secret and public keys."
    }

    fn execute(&self,
               mut context: Context,
               exts: &Fn(Context) -> Context) -> Feedback {
        let (pub_key, sec_key) = crypto::gen_keypair();
        let keyconfig = context.arg::<String>("KEYCHAIN")
                              .expect("expected keychain path");

        let pub_key_path = Path::new(&keyconfig).with_extension("pub");
        context.set("services_pub_keys", BTreeMap::<String, Value>::default());
        context.set("services_sec_keys", BTreeMap::<String, Value>::default());
        let new_context = exts(context);
        let services_pub_keys = new_context.get("services_pub_keys");
        let services_sec_keys = new_context.get("services_sec_keys");

        let pub_key_config: PubKeyConfig = PubKeyConfig {
            public_key: pub_key,
            services_pub_keys: services_pub_keys
                                .expect("service_pub_keys not found after exts call"),
        };
        // save pub_key seperately
        ConfigFile::save(&pub_key_config, &pub_key_path)
                    .expect("Could not write public key file.");

        let config = KeyConfig {
            public_key: pub_key,
            secret_key: sec_key,
            services_sec_keys: services_sec_keys
                                .expect("service_sec_keys not found after exts call"),
        };

        ConfigFile::save(&config, keyconfig)
                    .expect("Could not write keychain file.");
        Feedback::None
    }
}

/// implements command for template generating
pub struct GenerateTemplateCommand;
impl GenerateTemplateCommand {
    pub fn name() -> CommandName {
        "generate-template"
    }
}

impl Command for GenerateTemplateCommand {
    fn args(&self) -> Vec<Argument> {
        vec![
            Argument::new_positional("COUNT", true,
            "Validator total count."),
            Argument::new_positional("TEMPLATE", true,
            "Path to template config."),
        ]
    }

    fn name(&self) -> CommandName {
        Self::name()
    }

    fn about(&self) -> &str {
        "Generate basic config template."
    }

    fn execute(&self,
               mut context: Context,
               exts: &Fn(Context) -> Context) -> Feedback {
        let template_path = context.arg::<String>("TEMPLATE")
                                   .expect("template not found");
        let count = context.arg("COUNT")
                                   .expect("expected count to be int");

        context.set("VALUE", BTreeMap::<String, Value>::default());
        let new_context = exts(context);
        let values = new_context.get("VALUE").unwrap_or_default();

        let template = ConfigTemplate {
            count: count,
            services: values,
            ..ConfigTemplate::default()
        };

        ConfigFile::save(&template, template_path)
                        .expect("Could not write template file.");
        Feedback::None
    }
}

/// `add-validator` - append validator to template.
/// Automaticaly share keys from public key config.
pub struct AddValidatorCommand;
impl AddValidatorCommand {
    pub fn name() -> CommandName {
        "add-validator"
    }
}

impl Command for AddValidatorCommand {
    fn args(&self) -> Vec<Argument> {
        vec![
            Argument::new_positional("TEMPLATE", true,
            "Path to template config."),
            Argument::new_positional("PUBLIC_KEY", true,
            "Path to public key file."),
            Argument::new_named("LISTEN_ADDR", true,
            "Path to public key file.", "a", "listen-addr"),
        ]
    }

    fn name(&self) -> CommandName {
        Self::name()
    }

    fn about(&self) -> &str {
        "Preinit configuration, add validator to config template."
    }

    fn execute(&self,
               mut context: Context,
               exts: &Fn(Context) -> Context) -> Feedback {
        let template_path = context.arg::<String>("TEMPLATE")
                                   .expect("template not found");
        let public_key_path = context.arg::<String>("PUBLIC_KEY")
                                   .expect("public_key path not found");
        let addr = context.arg::<String>("LISTEN_ADDR").unwrap_or_default();

        let mut addr_parts = addr.split(':');
        let template: ConfigTemplate = ConfigFile::load(&template_path).unwrap();
        let public_key_config: PubKeyConfig = ConfigFile::load(public_key_path).unwrap();

        let addr_str = format!("{}:{}",
                           addr_parts.next()
                                     .unwrap_or("0.0.0.0"),
                           addr_parts.next()
                                     .map_or(DEFAULT_EXONUM_LISTEN_PORT,
                                                |s| s.parse()
                                                     .expect("could not parse port")));
        let addr = addr_str.parse().unwrap();
        let template =
        if !template.validators.contains_key(&public_key_config.public_key) {
            if template.validators.len() >= template.count {
                panic!("This template already full.");
            }

            let ident = ValidatorIdent {
                addr: addr,
                variables: BTreeMap::default(),
                keys: public_key_config.services_pub_keys,
            };
            context.set("validator_ident", ident);
            context.set("template", template);

            let new_context = exts(context);
            let ident = new_context.get("validator_ident")
                            .expect("validator_ident not found after call exts");
            let mut template: ConfigTemplate = new_context.get("template")
                            .expect("template not found after call exts");

            template.validators.insert(public_key_config.public_key, ident);
            template
        } else {
            panic!("This node already in template")
        };

        ConfigFile::save(&template, &template_path).unwrap();
        Feedback::None
    }
}

pub struct InitCommand;
impl InitCommand {
    pub fn name() -> CommandName {
        "init"
    }
}

impl Command for InitCommand {
    fn args(&self) -> Vec<Argument> {
        vec![
            Argument::new_positional("FULL_TEMPLATE", true,
            "Path to full template."),
            Argument::new_positional("KEYCHAIN", true,
            "Path to keychain config."),
            Argument::new_positional("CONFIG_PATH", true,
            "Path to output node config."),
        ]
    }

    fn name(&self) -> CommandName {
        Self::name()
    }

    fn about(&self) -> &str {
        "Toolchain to generate configuration."
    }

    fn execute(&self,
               mut context: Context,
               exts: &Fn(Context) -> Context) -> Feedback {
        let template_path = context.arg::<String>("FULL_TEMPLATE")
                                   .expect("template not found");
        let template_path = Path::new(&template_path);
        let keychain_path = context.arg::<String>("KEYCHAIN")
                                   .expect("keychain path not found");
        let keychain_path = Path::new(&keychain_path);

        let config_path = context.arg::<String>("CONFIG_PATH")
                                   .expect("config path not found");
        let config_path = Path::new(&config_path);

        let template: ConfigTemplate = ConfigFile::load(template_path)
                                                .expect("Failed to load config template.");
        let keychain: KeyConfig = ConfigFile::load(keychain_path)
                                                .expect("Failed to load key config.");

        if template.validators.len() != template.count {
            panic!("Template should be full.");
        }

        let genesis = GenesisConfig::new(template.validators.iter().map(|(k, _)| *k));
        let peers = template
            .validators
            .iter()
            .map(|(_, ident)| ident.addr)
            .collect();
        let config = {
            let validator_ident = &template.validators
                                            .get(&keychain.public_key)
                                            .expect("validator not found in template");

            NodeConfig {
                listen_address: validator_ident.addr,
                network: Default::default(),
                whitelist: Default::default(),
                peers: peers,
                public_key: keychain.public_key,
                secret_key: keychain.secret_key,
                genesis: genesis,
                api: Default::default(),
                services_configs: Default::default(),
            }
        };

        context.set("node_config", config);
        context.set("template", template);
        context.set("services_sec_keys", keychain.services_sec_keys);

        let new_context = exts(context);

        let value: Value = new_context.get("node_config")
            .expect("Could not create config from template, services return error");
        ConfigFile::save(&value, config_path)
                .expect("Could not write config file.");

        Feedback::None
    }
}

pub struct GenerateTestnetCommand;
impl GenerateTestnetCommand {
    pub fn name() -> CommandName {
        "generate-testnet"
    }
}

impl Command for GenerateTestnetCommand {
    fn args(&self) -> Vec<Argument> {
        vec![
            Argument::new_named("OUTPUT_DIR", true,
                "Path to directory where save configs.",
                "o", "output_dir"),
            Argument::new_named("START_PORT", false,
                "Port number started from which should validators listen.",
                "p", "start"),
            Argument::new_positional("COUNT", true,
                "Count of validators in testnet."),
        ]
    }

    fn name(&self) -> CommandName {
        Self::name()
    }

    fn about(&self) -> &str {
        "Generates genesis configuration for testnet"
    }

    fn execute(&self,
               context: Context,
               _: &Fn(Context) -> Context) -> Feedback {

        let dir = context.arg::<String>("OUTPUT_DIR").expect("output dir");
        let count: u8 = context.arg("COUNT")
                                .expect("count as int");
        let start_port = context.arg::<u16>("START_PORT")
                                .unwrap_or(DEFAULT_EXONUM_LISTEN_PORT);

        let dir = Path::new(&dir);
        let dir = dir.join("validators");
        if !dir.exists() {
            fs::create_dir_all(&dir).unwrap();
        }

        let configs = generate_testnet_config(count, start_port);
        for (idx, cfg) in configs.into_iter().enumerate() {
            let file_name = format!("{}.toml", idx);
            ConfigFile::save(&cfg, &dir.join(file_name)).unwrap();
        }

        Feedback::None
    }
}
<|endoftext|>


<commit_before>use std::any::Any;
use std::fmt;
use std::io::Read;
use std::io::Write;
use std::any::TypeId;

#[cfg(feature = "bytes")]
use bytes::Bytes;

use crate::clear::Clear;
use crate::error::ProtobufError;
use crate::error::ProtobufResult;
use crate::reflect::MessageDescriptor;
use crate::reflect::ProtobufValue;
use crate::stream::CodedInputStream;
use crate::stream::CodedOutputStream;
use crate::stream::WithCodedInputStream;
use crate::stream::WithCodedOutputStream;
use crate::unknown::UnknownFields;

/// Trait implemented for all generated structs for protobuf messages.
///
/// Also, generated messages implement `Clone + Default + PartialEq`
pub trait Message: fmt::Debug + Clear + Send + Sync + ProtobufValue {
    /// Message descriptor for this message, used for reflection.
    fn descriptor(&self) -> &'static MessageDescriptor;

    /// True iff all required fields are initialized.
    /// Always returns `true` for protobuf 3.
    fn is_initialized(&self) -> bool;

    /// Update this message object with fields read from given stream.
    fn merge_from(&mut self, is: &mut CodedInputStream) -> ProtobufResult<()>;

    /// Write message to the stream.
    ///
    /// Sizes of this messages and nested messages must be cached
    /// by calling `compute_size` prior to this call.
    fn write_to_with_cached_sizes(&self, os: &mut CodedOutputStream) -> ProtobufResult<()>;

    /// Compute and cache size of this message and all nested messages
    fn compute_size(&self) -> u32;

    /// Get size previously computed by `compute_size`.
    fn get_cached_size(&self) -> u32;

    /// Write the message to the stream.
    ///
    /// Results in error if message is not fully initialized.
    fn write_to(&self, os: &mut CodedOutputStream) -> ProtobufResult<()> {
        self.check_initialized()?;

        // cache sizes
        self.compute_size();
        // TODO: reserve additional
        self.write_to_with_cached_sizes(os)?;

        Ok(())
    }

    /// Write the message to the stream prepending the message with message length
    /// encoded as varint.
    fn write_length_delimited_to(&self, os: &mut CodedOutputStream) -> ProtobufResult<()> {
        let size = self.compute_size();
        os.write_raw_varint32(size)?;
        self.write_to_with_cached_sizes(os)?;

        // TODO: assert we've written same number of bytes as computed

        Ok(())
    }

    /// Write the message to the vec, prepend the message with message length
    /// encoded as varint.
    fn write_length_delimited_to_vec(&self, vec: &mut Vec<u8>) -> ProtobufResult<()> {
        let mut os = CodedOutputStream::vec(vec);
        self.write_length_delimited_to(&mut os)?;
        os.flush()?;
        Ok(())
    }

    /// Update this message object with fields read from given stream.
    fn merge_from_bytes(&mut self, bytes: &[u8]) -> ProtobufResult<()> {
        let mut is = CodedInputStream::from_bytes(bytes);
        self.merge_from(&mut is)
    }

    /// Check if all required fields of this object are initialized.
    fn check_initialized(&self) -> ProtobufResult<()> {
        if !self.is_initialized() {
            Err(ProtobufError::message_not_initialized(
                self.descriptor().name(),
            ))
        } else {
            Ok(())
        }
    }

    /// Write the message to the writer.
    fn write_to_writer(&self, w: &mut Write) -> ProtobufResult<()> {
        w.with_coded_output_stream(|os| self.write_to(os))
    }

    /// Write the message to bytes vec.
    fn write_to_vec(&self, v: &mut Vec<u8>) -> ProtobufResult<()> {
        v.with_coded_output_stream(|os| self.write_to(os))
    }

    /// Write the message to bytes vec.
    ///
    /// > **Note**: You can use `parse_from_bytes` to do the reverse.
    fn write_to_bytes(&self) -> ProtobufResult<Vec<u8>> {
        self.check_initialized()?;

        let size = self.compute_size() as usize;
        let mut v = Vec::with_capacity(size);
        // skip zerofill
        unsafe {
            v.set_len(size);
        }
        {
            let mut os = CodedOutputStream::bytes(&mut v);
            self.write_to_with_cached_sizes(&mut os)?;
            os.check_eof();
        }
        Ok(v)
    }

    /// Write the message to the writer, prepend the message with message length
    /// encoded as varint.
    fn write_length_delimited_to_writer(&self, w: &mut Write) -> ProtobufResult<()> {
        w.with_coded_output_stream(|os| self.write_length_delimited_to(os))
    }

    /// Write the message to the bytes vec, prepend the message with message length
    /// encoded as varint.
    fn write_length_delimited_to_bytes(&self) -> ProtobufResult<Vec<u8>> {
        let mut v = Vec::new();
        v.with_coded_output_stream(|os| self.write_length_delimited_to(os))?;
        Ok(v)
    }

    /// Get a reference to unknown fields.
    fn get_unknown_fields(&self) -> &UnknownFields;
    /// Get a mutable reference to unknown fields.
    fn mut_unknown_fields(&mut self) -> &mut UnknownFields;

    /// Create an empty message object.
    fn new() -> Self
    where
        Self: Sized;

    /// Get message descriptor for message type.
    fn descriptor_static() -> &'static MessageDescriptor
    where
        Self: Sized,
    {
        panic!(
            "descriptor_static is not implemented for message, \
             LITE_RUNTIME must be used"
        );
    }

    /// Return a pointer to default immutable message.
    fn default_instance() -> &'static Self
    where
        Self: Sized;
}

impl dyn Message {
    /// Downcast `Box<dyn Message>` to specific message type.
    pub fn downcast_box<T: Any>(self: Box<Self>) -> Result<Box<T>, Box<Message>> {
        if Any::type_id(&*self) == TypeId::of::<T>() {
            unsafe {
                let raw: *mut Message = Box::into_raw(self);
                Ok(Box::from_raw(raw as *mut T))
            }
        } else {
            Err(self)
        }
    }

    /// Downcast `&dyn Message` to specific message type.
    pub fn downcast_ref<'a, M: Message + 'a>(&'a self) -> Option<&'a M> {
        if Any::type_id(&*self) == TypeId::of::<M>() {
            unsafe {
                Some(&*(self as *const dyn Message as *const M))
            }
        } else {
            None
        }
    }

    /// Downcast `&mut dyn Message` to specific message type.
    pub fn downcast_mut<'a, M: Message + 'a>(&'a mut self) -> Option<&'a mut M> {
        if Any::type_id(&*self) == TypeId::of::<M>() {
            unsafe {
                Some(&mut *(self as *mut dyn Message as *mut M))
            }
        } else {
            None
        }
    }
}

impl Clone for Box<Message> {
    fn clone(&self) -> Self {
        use std::ops::Deref;
        self.descriptor().clone(self.deref())
    }
}

#[cfg(off)] // don't need it
impl PartialEq for Box<Message> {
    fn eq(&self, other: &Box<Message>) -> bool {
        use std::ops::Deref;
        self.descriptor() == other.descriptor() && self.descriptor().eq(self.deref(), other.deref())
    }
}

/// Parse message from stream.
pub fn parse_from<M: Message>(is: &mut CodedInputStream) -> ProtobufResult<M> {
    let mut r: M = Message::new();
    r.merge_from(is)?;
    r.check_initialized()?;
    Ok(r)
}

/// Parse message from reader.
/// Parse stops on EOF or when error encountered.
pub fn parse_from_reader<M: Message>(reader: &mut Read) -> ProtobufResult<M> {
    reader.with_coded_input_stream(|is| parse_from::<M>(is))
}

/// Parse message from byte array.
pub fn parse_from_bytes<M: Message>(bytes: &[u8]) -> ProtobufResult<M> {
    bytes.with_coded_input_stream(|is| parse_from::<M>(is))
}

/// Parse message from `Bytes` object.
/// Resulting message may share references to the passed bytes object.
#[cfg(feature = "bytes")]
pub fn parse_from_carllerche_bytes<M: Message>(bytes: &Bytes) -> ProtobufResult<M> {
    // Call trait explicitly to avoid accidental construction from `&[u8]`
    WithCodedInputStream::with_coded_input_stream(bytes, |is| parse_from::<M>(is))
}
<commit_msg>More rustdoc<commit_after>use std::any::Any;
use std::fmt;
use std::io::Read;
use std::io::Write;
use std::any::TypeId;

#[cfg(feature = "bytes")]
use bytes::Bytes;

use crate::clear::Clear;
use crate::error::ProtobufError;
use crate::error::ProtobufResult;
use crate::reflect::MessageDescriptor;
use crate::reflect::ProtobufValue;
use crate::stream::CodedInputStream;
use crate::stream::CodedOutputStream;
use crate::stream::WithCodedInputStream;
use crate::stream::WithCodedOutputStream;
use crate::unknown::UnknownFields;

/// Trait implemented for all generated structs for protobuf messages.
///
/// Also, generated messages implement `Clone + Default + PartialEq`
pub trait Message: fmt::Debug + Clear + Send + Sync + ProtobufValue {
    /// Message descriptor for this message, used for reflection.
    fn descriptor(&self) -> &'static MessageDescriptor;

    /// True iff all required fields are initialized.
    /// Always returns `true` for protobuf 3.
    fn is_initialized(&self) -> bool;

    /// Update this message object with fields read from given stream.
    fn merge_from(&mut self, is: &mut CodedInputStream) -> ProtobufResult<()>;

    /// Write message to the stream.
    ///
    /// Sizes of this messages and nested messages must be cached
    /// by calling `compute_size` prior to this call.
    fn write_to_with_cached_sizes(&self, os: &mut CodedOutputStream) -> ProtobufResult<()>;

    /// Compute and cache size of this message and all nested messages
    fn compute_size(&self) -> u32;

    /// Get size previously computed by `compute_size`.
    fn get_cached_size(&self) -> u32;

    /// Write the message to the stream.
    ///
    /// Results in error if message is not fully initialized.
    fn write_to(&self, os: &mut CodedOutputStream) -> ProtobufResult<()> {
        self.check_initialized()?;

        // cache sizes
        self.compute_size();
        // TODO: reserve additional
        self.write_to_with_cached_sizes(os)?;

        Ok(())
    }

    /// Write the message to the stream prepending the message with message length
    /// encoded as varint.
    fn write_length_delimited_to(&self, os: &mut CodedOutputStream) -> ProtobufResult<()> {
        let size = self.compute_size();
        os.write_raw_varint32(size)?;
        self.write_to_with_cached_sizes(os)?;

        // TODO: assert we've written same number of bytes as computed

        Ok(())
    }

    /// Write the message to the vec, prepend the message with message length
    /// encoded as varint.
    fn write_length_delimited_to_vec(&self, vec: &mut Vec<u8>) -> ProtobufResult<()> {
        let mut os = CodedOutputStream::vec(vec);
        self.write_length_delimited_to(&mut os)?;
        os.flush()?;
        Ok(())
    }

    /// Update this message object with fields read from given stream.
    fn merge_from_bytes(&mut self, bytes: &[u8]) -> ProtobufResult<()> {
        let mut is = CodedInputStream::from_bytes(bytes);
        self.merge_from(&mut is)
    }

    /// Check if all required fields of this object are initialized.
    fn check_initialized(&self) -> ProtobufResult<()> {
        if !self.is_initialized() {
            Err(ProtobufError::message_not_initialized(
                self.descriptor().name(),
            ))
        } else {
            Ok(())
        }
    }

    /// Write the message to the writer.
    fn write_to_writer(&self, w: &mut Write) -> ProtobufResult<()> {
        w.with_coded_output_stream(|os| self.write_to(os))
    }

    /// Write the message to bytes vec.
    fn write_to_vec(&self, v: &mut Vec<u8>) -> ProtobufResult<()> {
        v.with_coded_output_stream(|os| self.write_to(os))
    }

    /// Write the message to bytes vec.
    ///
    /// > **Note**: You can use `parse_from_bytes` to do the reverse.
    fn write_to_bytes(&self) -> ProtobufResult<Vec<u8>> {
        self.check_initialized()?;

        let size = self.compute_size() as usize;
        let mut v = Vec::with_capacity(size);
        // skip zerofill
        unsafe {
            v.set_len(size);
        }
        {
            let mut os = CodedOutputStream::bytes(&mut v);
            self.write_to_with_cached_sizes(&mut os)?;
            os.check_eof();
        }
        Ok(v)
    }

    /// Write the message to the writer, prepend the message with message length
    /// encoded as varint.
    fn write_length_delimited_to_writer(&self, w: &mut Write) -> ProtobufResult<()> {
        w.with_coded_output_stream(|os| self.write_length_delimited_to(os))
    }

    /// Write the message to the bytes vec, prepend the message with message length
    /// encoded as varint.
    fn write_length_delimited_to_bytes(&self) -> ProtobufResult<Vec<u8>> {
        let mut v = Vec::new();
        v.with_coded_output_stream(|os| self.write_length_delimited_to(os))?;
        Ok(v)
    }

    /// Get a reference to unknown fields.
    fn get_unknown_fields(&self) -> &UnknownFields;
    /// Get a mutable reference to unknown fields.
    fn mut_unknown_fields(&mut self) -> &mut UnknownFields;

    /// Create an empty message object.
    ///
    /// ```
    /// # use protobuf::Message;
    /// # fn foo<MyMessage: Message>() {
    /// let m = MyMessage::new();
    /// # }
    /// ```
    fn new() -> Self
    where
        Self: Sized;

    /// Get message descriptor for message type.
    ///
    /// ```
    /// # use protobuf::Message;
    /// # fn foo<MyMessage: Message>() {
    /// let descriptor = MyMessage::descriptor_static();
    /// assert_eq!("MyMessage", descriptor.name());
    /// # }
    /// ```
    fn descriptor_static() -> &'static MessageDescriptor
    where
        Self: Sized,
    {
        panic!(
            "descriptor_static is not implemented for message, \
             LITE_RUNTIME must be used"
        );
    }

    /// Return a pointer to default immutable message with static lifetime.
    ///
    /// ```
    /// # use protobuf::Message;
    /// # fn foo<MyMessage: Message>() {
    /// let m: &MyMessage = MyMessage::default_instance();
    /// # }
    /// ```
    fn default_instance() -> &'static Self
    where
        Self: Sized;
}

impl dyn Message {
    /// Downcast `Box<dyn Message>` to specific message type.
    ///
    /// ```
    /// # use protobuf::Message;
    /// # fn foo<MyMessage: Message>(message: Box<Message>) {
    /// let m: Box<Message> = message;
    /// let m: Box<MyMessage> = Message::downcast_box(m).unwrap();
    /// # }
    /// ```
    pub fn downcast_box<T: Any>(self: Box<Self>) -> Result<Box<T>, Box<Message>> {
        if Any::type_id(&*self) == TypeId::of::<T>() {
            unsafe {
                let raw: *mut Message = Box::into_raw(self);
                Ok(Box::from_raw(raw as *mut T))
            }
        } else {
            Err(self)
        }
    }

    /// Downcast `&dyn Message` to specific message type.
    ///
    /// ```
    /// # use protobuf::Message;
    /// # fn foo<MyMessage: Message>(message: &Message) {
    /// let m: &Message = message;
    /// let m: &MyMessage = Message::downcast_ref(m).unwrap();
    /// # }
    /// ```
    pub fn downcast_ref<'a, M: Message + 'a>(&'a self) -> Option<&'a M> {
        if Any::type_id(&*self) == TypeId::of::<M>() {
            unsafe {
                Some(&*(self as *const dyn Message as *const M))
            }
        } else {
            None
        }
    }

    /// Downcast `&mut dyn Message` to specific message type.
    ///
    /// ```
    /// # use protobuf::Message;
    /// # fn foo<MyMessage: Message>(message: &mut Message) {
    /// let m: &mut Message = message;
    /// let m: &mut MyMessage = Message::downcast_mut(m).unwrap();
    /// # }
    /// ```
    pub fn downcast_mut<'a, M: Message + 'a>(&'a mut self) -> Option<&'a mut M> {
        if Any::type_id(&*self) == TypeId::of::<M>() {
            unsafe {
                Some(&mut *(self as *mut dyn Message as *mut M))
            }
        } else {
            None
        }
    }
}

impl Clone for Box<Message> {
    fn clone(&self) -> Self {
        use std::ops::Deref;
        self.descriptor().clone(self.deref())
    }
}

#[cfg(off)] // don't need it
impl PartialEq for Box<Message> {
    fn eq(&self, other: &Box<Message>) -> bool {
        use std::ops::Deref;
        self.descriptor() == other.descriptor() && self.descriptor().eq(self.deref(), other.deref())
    }
}

/// Parse message from stream.
pub fn parse_from<M: Message>(is: &mut CodedInputStream) -> ProtobufResult<M> {
    let mut r: M = Message::new();
    r.merge_from(is)?;
    r.check_initialized()?;
    Ok(r)
}

/// Parse message from reader.
/// Parse stops on EOF or when error encountered.
pub fn parse_from_reader<M: Message>(reader: &mut Read) -> ProtobufResult<M> {
    reader.with_coded_input_stream(|is| parse_from::<M>(is))
}

/// Parse message from byte array.
pub fn parse_from_bytes<M: Message>(bytes: &[u8]) -> ProtobufResult<M> {
    bytes.with_coded_input_stream(|is| parse_from::<M>(is))
}

/// Parse message from `Bytes` object.
/// Resulting message may share references to the passed bytes object.
#[cfg(feature = "bytes")]
pub fn parse_from_carllerche_bytes<M: Message>(bytes: &Bytes) -> ProtobufResult<M> {
    // Call trait explicitly to avoid accidental construction from `&[u8]`
    WithCodedInputStream::with_coded_input_stream(bytes, |is| parse_from::<M>(is))
}
<|endoftext|>


<commit_before>extern crate etcd;

use etcd::{Client, Error};

/// Wrapper around Client that automatically cleans up etcd after each test.
struct TestClient {
    c: Client,
}

impl TestClient {
    /// Creates a new client for a test.
    fn new() -> TestClient {
        TestClient {
            c: Client::new("http://etcd:2379").unwrap(),
        }
    }
}

impl Drop for TestClient {
    #[allow(unused_must_use)]
    fn drop(&mut self) {
        self.c.delete("/test", true);
    }
}

#[test]
fn create() {
    let client = TestClient::new();

    let response = match client.c.create("/test/foo", "bar", Some(60)) {
        Ok(response) => response,
        Err(error) => panic!("{:?}", error),
    };

    assert_eq!(response.action, "create".to_string());
    assert_eq!(response.node.value.unwrap(), "bar".to_string());
    assert_eq!(response.node.ttl.unwrap(), 60);
}

#[test]
fn create_fail() {
    let client = TestClient::new();

    client.c.create("/test/foo", "bar", Some(60)).ok().unwrap();

    match client.c.create("/test/foo", "bar", None).err().unwrap() {
        Error::Etcd(error) => assert_eq!(error.message, "Key already exists".to_string()),
        _ => panic!("expected EtcdError due to pre-existing key"),
    };
}

#[test]
fn get() {
    let client = TestClient::new();
    client.c.create("/test/foo", "bar", Some(60)).ok().unwrap();

    let response = client.c.get("/test/foo", false, false).ok().unwrap();

    assert_eq!(response.action, "get".to_string());
    assert_eq!(response.node.value.unwrap(), "bar".to_string());
    assert_eq!(response.node.ttl.unwrap(), 60);

}

#[test]
fn get_recursive() {
    let client = TestClient::new();

    client.c.set("/test/dir/baz", "blah", None).ok();
    client.c.set("/test/foo", "bar", None).ok();

    let mut response = client.c.get("/test", true, false).ok().unwrap();

    assert_eq!(response.node.dir.unwrap(), true);

    let mut nodes = response.node.nodes.unwrap();

    assert_eq!(nodes[0].clone().key.unwrap(), "/test/dir".to_string());
    assert_eq!(nodes[0].clone().dir.unwrap(), true);
    assert_eq!(nodes[1].clone().key.unwrap(), "/test/foo".to_string());
    assert_eq!(nodes[1].clone().value.unwrap(), "bar".to_string());

    response = client.c.get("/test", true, true).ok().unwrap();
    nodes = response.node.nodes.unwrap();

    assert_eq!(
        nodes[0].clone().nodes.unwrap()[0].clone().value.unwrap(),
        "blah".to_string()
    );
}

#[test]
fn set() {
    let client = TestClient::new();

    let response = client.c.set("/test/foo", "baz", None).ok().unwrap();

    assert_eq!(response.action, "set".to_string());
    assert_eq!(response.node.value.unwrap(), "baz".to_string());
    assert!(response.node.ttl.is_none());
}

#[test]
fn set_dir() {
    let client = TestClient::new();

    assert!(client.c.set_dir("/test", None).is_ok());
    assert!(client.c.set_dir("/test", None).is_err());

    client.c.set("/test/foo", "bar", None).ok().unwrap();

    assert!(client.c.set_dir("/test/foo", None).is_ok());
}

#[test]
fn update() {
    let client = TestClient::new();
    client.c.create("/test/foo", "bar", None).ok().unwrap();

    let response = client.c.update("/test/foo", "blah", Some(30)).ok().unwrap();

    assert_eq!(response.action, "update".to_string());
    assert_eq!(response.node.value.unwrap(), "blah".to_string());
    assert_eq!(response.node.ttl.unwrap(), 30);
}

#[test]
fn update_fail() {
    let client = TestClient::new();

    match client.c.update("/test/foo", "bar", None).err().unwrap() {
        Error::Etcd(error) => assert_eq!(error.message, "Key not found".to_string()),
        _ => panic!("expected EtcdError due to missing key"),
    };
}

#[test]
fn update_dir() {
    let client = TestClient::new();

    client.c.create_dir("/test", None).ok().unwrap();

    let response = client.c.update_dir("/test", Some(60)).ok().unwrap();

    assert_eq!(response.node.ttl.unwrap(), 60);
}

#[test]
fn update_dir_replaces_key() {
    let client = TestClient::new();

    client.c.set("/test/foo", "bar", None).ok().unwrap();

    let response = client.c.update_dir("/test/foo", Some(60)).ok().unwrap();

    assert_eq!(response.node.value.unwrap(), "");
    assert_eq!(response.node.ttl.unwrap(), 60);
}

#[test]
fn update_dir_requires_existing_dir() {
    let client = TestClient::new();

    assert!(client.c.update_dir("/test", None).is_err());
}

#[test]
fn delete() {
    let client = TestClient::new();
    client.c.create("/test/foo", "bar", None).ok().unwrap();

    let response = client.c.delete("/test/foo", false).ok().unwrap();

    assert_eq!(response.action, "delete");
}

#[test]
fn create_dir() {
    let client = TestClient::new();

    let response = client.c.create_dir("/test/dir", None).ok().unwrap();

    assert_eq!(response.action, "create".to_string());
    assert!(response.node.dir.unwrap());
    assert!(response.node.value.is_none());
}

#[test]
fn delete_dir() {
    let client = TestClient::new();
    client.c.create_dir("/test/dir", None).ok().unwrap();

    let response = client.c.delete_dir("/test/dir").ok().unwrap();

    assert_eq!(response.action, "delete");
}

#[test]
fn leader_stats() {
    let client = TestClient::new();

    client.c.leader_stats().unwrap();
}
<commit_msg>Improve a few test names.<commit_after>extern crate etcd;

use etcd::{Client, Error};

/// Wrapper around Client that automatically cleans up etcd after each test.
struct TestClient {
    c: Client,
}

impl TestClient {
    /// Creates a new client for a test.
    fn new() -> TestClient {
        TestClient {
            c: Client::new("http://etcd:2379").unwrap(),
        }
    }
}

impl Drop for TestClient {
    #[allow(unused_must_use)]
    fn drop(&mut self) {
        self.c.delete("/test", true);
    }
}

#[test]
fn create() {
    let client = TestClient::new();

    let response = match client.c.create("/test/foo", "bar", Some(60)) {
        Ok(response) => response,
        Err(error) => panic!("{:?}", error),
    };

    assert_eq!(response.action, "create".to_string());
    assert_eq!(response.node.value.unwrap(), "bar".to_string());
    assert_eq!(response.node.ttl.unwrap(), 60);
}

#[test]
fn create_does_not_replace_existing_key() {
    let client = TestClient::new();

    client.c.create("/test/foo", "bar", Some(60)).ok().unwrap();

    match client.c.create("/test/foo", "bar", None).err().unwrap() {
        Error::Etcd(error) => assert_eq!(error.message, "Key already exists".to_string()),
        _ => panic!("expected EtcdError due to pre-existing key"),
    };
}

#[test]
fn get() {
    let client = TestClient::new();
    client.c.create("/test/foo", "bar", Some(60)).ok().unwrap();

    let response = client.c.get("/test/foo", false, false).ok().unwrap();

    assert_eq!(response.action, "get".to_string());
    assert_eq!(response.node.value.unwrap(), "bar".to_string());
    assert_eq!(response.node.ttl.unwrap(), 60);

}

#[test]
fn get_recursive() {
    let client = TestClient::new();

    client.c.set("/test/dir/baz", "blah", None).ok();
    client.c.set("/test/foo", "bar", None).ok();

    let mut response = client.c.get("/test", true, false).ok().unwrap();

    assert_eq!(response.node.dir.unwrap(), true);

    let mut nodes = response.node.nodes.unwrap();

    assert_eq!(nodes[0].clone().key.unwrap(), "/test/dir".to_string());
    assert_eq!(nodes[0].clone().dir.unwrap(), true);
    assert_eq!(nodes[1].clone().key.unwrap(), "/test/foo".to_string());
    assert_eq!(nodes[1].clone().value.unwrap(), "bar".to_string());

    response = client.c.get("/test", true, true).ok().unwrap();
    nodes = response.node.nodes.unwrap();

    assert_eq!(
        nodes[0].clone().nodes.unwrap()[0].clone().value.unwrap(),
        "blah".to_string()
    );
}

#[test]
fn set() {
    let client = TestClient::new();

    let response = client.c.set("/test/foo", "baz", None).ok().unwrap();

    assert_eq!(response.action, "set".to_string());
    assert_eq!(response.node.value.unwrap(), "baz".to_string());
    assert!(response.node.ttl.is_none());
}

#[test]
fn set_dir() {
    let client = TestClient::new();

    assert!(client.c.set_dir("/test", None).is_ok());
    assert!(client.c.set_dir("/test", None).is_err());

    client.c.set("/test/foo", "bar", None).ok().unwrap();

    assert!(client.c.set_dir("/test/foo", None).is_ok());
}

#[test]
fn update() {
    let client = TestClient::new();
    client.c.create("/test/foo", "bar", None).ok().unwrap();

    let response = client.c.update("/test/foo", "blah", Some(30)).ok().unwrap();

    assert_eq!(response.action, "update".to_string());
    assert_eq!(response.node.value.unwrap(), "blah".to_string());
    assert_eq!(response.node.ttl.unwrap(), 30);
}

#[test]
fn update_requires_existing_key() {
    let client = TestClient::new();

    match client.c.update("/test/foo", "bar", None).err().unwrap() {
        Error::Etcd(error) => assert_eq!(error.message, "Key not found".to_string()),
        _ => panic!("expected EtcdError due to missing key"),
    };
}

#[test]
fn update_dir() {
    let client = TestClient::new();

    client.c.create_dir("/test", None).ok().unwrap();

    let response = client.c.update_dir("/test", Some(60)).ok().unwrap();

    assert_eq!(response.node.ttl.unwrap(), 60);
}

#[test]
fn update_dir_replaces_key() {
    let client = TestClient::new();

    client.c.set("/test/foo", "bar", None).ok().unwrap();

    let response = client.c.update_dir("/test/foo", Some(60)).ok().unwrap();

    assert_eq!(response.node.value.unwrap(), "");
    assert_eq!(response.node.ttl.unwrap(), 60);
}

#[test]
fn update_dir_requires_existing_dir() {
    let client = TestClient::new();

    assert!(client.c.update_dir("/test", None).is_err());
}

#[test]
fn delete() {
    let client = TestClient::new();
    client.c.create("/test/foo", "bar", None).ok().unwrap();

    let response = client.c.delete("/test/foo", false).ok().unwrap();

    assert_eq!(response.action, "delete");
}

#[test]
fn create_dir() {
    let client = TestClient::new();

    let response = client.c.create_dir("/test/dir", None).ok().unwrap();

    assert_eq!(response.action, "create".to_string());
    assert!(response.node.dir.unwrap());
    assert!(response.node.value.is_none());
}

#[test]
fn delete_dir() {
    let client = TestClient::new();
    client.c.create_dir("/test/dir", None).ok().unwrap();

    let response = client.c.delete_dir("/test/dir").ok().unwrap();

    assert_eq!(response.action, "delete");
}

#[test]
fn leader_stats() {
    let client = TestClient::new();

    client.c.leader_stats().unwrap();
}
<|endoftext|>