<commit_before>//
//   Copyright 2016 Andrew Hunter
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.
//

use std::rc::*;
use std::cell::*;

use super::super::util::clonecell::*;
use super::super::tree::*;

use super::component::*;

struct Subscription<TData: Clone> {
    callback: RefCell<SubscriptionCallback>,
    data: TData
}

type SubscriptionRef<TData> = Rc<Subscription<TData>>;

///
/// SubscriptionCallback gets around an issue with RefCell.
///
/// If we do RefCell<Box<Fn>> we can't do .borrow_mut()() because the borrow checker treats the box as immutable
/// for reasons that seem counter-intuitive.
///
struct SubscriptionCallback {
    callback: ConsumerCallback
}

impl SubscriptionCallback {
    #[inline]
    fn run_callback(&mut self, change: &TreeChange) {
        let ref mut callback = self.callback;
        callback(change);
    }
}

///
/// The subscription manager is an interior mutable type that can store subscriptions created from consumers.
/// The principle use case is to make it so that publishers and consumers can share a list of subscriptions.
///
pub struct SubscriptionManager<TData: Clone> {
    subscriptions: CloneCell<Vec<SubscriptionRef<TData>>>
}

impl<TData: Clone> SubscriptionManager<TData> {
    ///
    /// Creates a new subscription manager
    ///
    pub fn new() -> SubscriptionManager<TData> {
        SubscriptionManager { subscriptions: CloneCell::new(vec![]) }
    }

    ///
    /// Modifies this subscription manager to add the specified subscription
    ///
    pub fn add_subscription(&self, callback_data: TData, callback: ConsumerCallback) {
        // Turn the callback into a reference
        let new_callback = Rc::new(Subscription { callback: RefCell::new(SubscriptionCallback { callback: callback }), data: callback_data });

        // Retrieve and update the subscriptions
        let mut subscriptions = self.subscriptions.get();
        subscriptions.push(new_callback);
        self.subscriptions.set(subscriptions);
    }

    ///
    /// Calls the subscriptions matching a particular filter
    ///
    pub fn call_subscriptions(&self, call_filter: &Fn(&TData) -> bool, change: &TreeChange) {
        // Retrieve the active subscriptions
        let subscriptions = self.subscriptions.get();

        // Call any subscription matching the filter
        for possible_subscription in subscriptions {
            if call_filter(&possible_subscription.data) {
                // Caution: this will fail at runtime with a borrowing error if this function is re-entered (ie, if there is a feedback loop)
                let mut callback = possible_subscription.callback.borrow_mut();
                callback.run_callback(change);
            }
        }
    }
}

#[cfg(test)]
mod subscriptionmanager_tests {
    use std::rc::*;
    use std::cell::*;

    use super::*;
    use super::super::super::tree::*;

    #[test]
    pub fn can_call_subscription() {
        // Create a subscription manager and a sample change (doesn't matter what the change is)
        let manager         = SubscriptionManager::<i32>::new();
        let a_change        = TreeChange::new(&TreeAddress::Here, TreeChangeType::Child, Some(&"".to_tree_node()));

        // Store the change count in a shared cell
        let change_count    = Rc::new(Cell::<i32>::new(0));
        let callback_count  = change_count.clone();

        // Must initially be 0
        assert!(change_count.get() == 0);

        // Create a subscription that updates the change count
        manager.add_subscription(0, Box::new(move |_change: &TreeChange| {
            let count_value = callback_count.get();
            let new_value   = count_value + 1;
            callback_count.set(new_value);
        }));

        // Call the subscription a few times (result should update)
        manager.call_subscriptions(&|_data| { true }, &a_change);
        assert!(change_count.get() == 1);
        manager.call_subscriptions(&|_data| { true }, &a_change);
        assert!(change_count.get() == 2);
    }

    #[test]
    pub fn can_filter_all_subscriptions() {
        // Create a subscription manager and a sample change (doesn't matter what the change is)
        let manager         = SubscriptionManager::<i32>::new();
        let a_change        = TreeChange::new(&TreeAddress::Here, TreeChangeType::Child, Some(&"".to_tree_node()));

        // Store the change count in a shared cell
        let change_count    = Rc::new(Cell::<i32>::new(0));
        let callback_count  = change_count.clone();

        // Must initially be 0
        assert!(change_count.get() == 0);

        // Create a subscription that updates the change count
        manager.add_subscription(0, Box::new(move |_change: &TreeChange| {
            let count_value = callback_count.get();
            let new_value   = count_value + 1;
            callback_count.set(new_value);
        }));

        // Call the subscription a few times (result should not update)
        manager.call_subscriptions(&|_data| { false }, &a_change);
        assert!(change_count.get() == 0);
        manager.call_subscriptions(&|_data| { false }, &a_change);
        assert!(change_count.get() == 0);
    }

    #[test]
    pub fn can_filter_some_subscriptions() {
        // Create a subscription manager and a sample change (doesn't matter what the change is)
        let manager         = SubscriptionManager::<i32>::new();
        let a_change        = TreeChange::new(&TreeAddress::Here, TreeChangeType::Child, Some(&"".to_tree_node()));

        // Store the change count in a shared cell
        let change_count    = Rc::new(Cell::<i32>::new(0));
        let callback_count  = change_count.clone();
        let callback_count2 = change_count.clone();

        // Must initially be 0
        assert!(change_count.get() == 0);

        // Create a subscription that updates the change count
        manager.add_subscription(0, Box::new(move |_change: &TreeChange| {
            let count_value = callback_count.get();
            let new_value   = count_value + 1;
            callback_count.set(new_value);
        }));
        manager.add_subscription(1, Box::new(move |_change: &TreeChange| {
            let count_value = callback_count2.get();
            let new_value   = count_value + 1;
            callback_count2.set(new_value);
        }));

        // Call the subscription a few times (result should not update)
        manager.call_subscriptions(&|_data| { true }, &a_change);
        assert!(change_count.get() == 2);
        manager.call_subscriptions(&|_data| { false }, &a_change);
        assert!(change_count.get() == 2);
        manager.call_subscriptions(&|data| { *data == 1 }, &a_change);
        assert!(change_count.get() == 3);
    }
}
<commit_msg>Bunch of tree changes in the subscription manager tests<commit_after>//
//   Copyright 2016 Andrew Hunter
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.
//

use std::rc::*;
use std::cell::*;

use super::super::util::clonecell::*;
use super::super::tree::*;

use super::component::*;

struct Subscription<TData: Clone> {
    callback: RefCell<SubscriptionCallback>,
    data: TData
}

type SubscriptionRef<TData> = Rc<Subscription<TData>>;

///
/// SubscriptionCallback gets around an issue with RefCell.
///
/// If we do RefCell<Box<Fn>> we can't do .borrow_mut()() because the borrow checker treats the box as immutable
/// for reasons that seem counter-intuitive.
///
struct SubscriptionCallback {
    callback: ConsumerCallback
}

impl SubscriptionCallback {
    #[inline]
    fn run_callback(&mut self, change: &TreeChange) {
        let ref mut callback = self.callback;
        callback(change);
    }
}

///
/// The subscription manager is an interior mutable type that can store subscriptions created from consumers.
/// The principle use case is to make it so that publishers and consumers can share a list of subscriptions.
///
pub struct SubscriptionManager<TData: Clone> {
    subscriptions: CloneCell<Vec<SubscriptionRef<TData>>>
}

impl<TData: Clone> SubscriptionManager<TData> {
    ///
    /// Creates a new subscription manager
    ///
    pub fn new() -> SubscriptionManager<TData> {
        SubscriptionManager { subscriptions: CloneCell::new(vec![]) }
    }

    ///
    /// Modifies this subscription manager to add the specified subscription
    ///
    pub fn add_subscription(&self, callback_data: TData, callback: ConsumerCallback) {
        // Turn the callback into a reference
        let new_callback = Rc::new(Subscription { callback: RefCell::new(SubscriptionCallback { callback: callback }), data: callback_data });

        // Retrieve and update the subscriptions
        let mut subscriptions = self.subscriptions.get();
        subscriptions.push(new_callback);
        self.subscriptions.set(subscriptions);
    }

    ///
    /// Calls the subscriptions matching a particular filter
    ///
    pub fn call_subscriptions(&self, call_filter: &Fn(&TData) -> bool, change: &TreeChange) {
        // Retrieve the active subscriptions
        let subscriptions = self.subscriptions.get();

        // Call any subscription matching the filter
        for possible_subscription in subscriptions {
            if call_filter(&possible_subscription.data) {
                // Caution: this will fail at runtime with a borrowing error if this function is re-entered (ie, if there is a feedback loop)
                let mut callback = possible_subscription.callback.borrow_mut();
                callback.run_callback(change);
            }
        }
    }
}

#[cfg(test)]
mod subscriptionmanager_tests {
    use std::rc::*;
    use std::cell::*;

    use super::*;
    use super::super::super::tree::*;

    #[test]
    pub fn can_call_subscription() {
        // Create a subscription manager and a sample change (doesn't matter what the change is)
        let manager         = SubscriptionManager::<i32>::new();
        let a_change        = TreeChange::new(&TreeAddress::Here, &"".to_tree_node());

        // Store the change count in a shared cell
        let change_count    = Rc::new(Cell::<i32>::new(0));
        let callback_count  = change_count.clone();

        // Must initially be 0
        assert!(change_count.get() == 0);

        // Create a subscription that updates the change count
        manager.add_subscription(0, Box::new(move |_change: &TreeChange| {
            let count_value = callback_count.get();
            let new_value   = count_value + 1;
            callback_count.set(new_value);
        }));

        // Call the subscription a few times (result should update)
        manager.call_subscriptions(&|_data| { true }, &a_change);
        assert!(change_count.get() == 1);
        manager.call_subscriptions(&|_data| { true }, &a_change);
        assert!(change_count.get() == 2);
    }

    #[test]
    pub fn can_filter_all_subscriptions() {
        // Create a subscription manager and a sample change (doesn't matter what the change is)
        let manager         = SubscriptionManager::<i32>::new();
        let a_change        = TreeChange::new(&TreeAddress::Here, &"".to_tree_node());

        // Store the change count in a shared cell
        let change_count    = Rc::new(Cell::<i32>::new(0));
        let callback_count  = change_count.clone();

        // Must initially be 0
        assert!(change_count.get() == 0);

        // Create a subscription that updates the change count
        manager.add_subscription(0, Box::new(move |_change: &TreeChange| {
            let count_value = callback_count.get();
            let new_value   = count_value + 1;
            callback_count.set(new_value);
        }));

        // Call the subscription a few times (result should not update)
        manager.call_subscriptions(&|_data| { false }, &a_change);
        assert!(change_count.get() == 0);
        manager.call_subscriptions(&|_data| { false }, &a_change);
        assert!(change_count.get() == 0);
    }

    #[test]
    pub fn can_filter_some_subscriptions() {
        // Create a subscription manager and a sample change (doesn't matter what the change is)
        let manager         = SubscriptionManager::<i32>::new();
        let a_change        = TreeChange::new(&TreeAddress::Here, &"".to_tree_node());

        // Store the change count in a shared cell
        let change_count    = Rc::new(Cell::<i32>::new(0));
        let callback_count  = change_count.clone();
        let callback_count2 = change_count.clone();

        // Must initially be 0
        assert!(change_count.get() == 0);

        // Create a subscription that updates the change count
        manager.add_subscription(0, Box::new(move |_change: &TreeChange| {
            let count_value = callback_count.get();
            let new_value   = count_value + 1;
            callback_count.set(new_value);
        }));
        manager.add_subscription(1, Box::new(move |_change: &TreeChange| {
            let count_value = callback_count2.get();
            let new_value   = count_value + 1;
            callback_count2.set(new_value);
        }));

        // Call the subscription a few times (result should not update)
        manager.call_subscriptions(&|_data| { true }, &a_change);
        assert!(change_count.get() == 2);
        manager.call_subscriptions(&|_data| { false }, &a_change);
        assert!(change_count.get() == 2);
        manager.call_subscriptions(&|data| { *data == 1 }, &a_change);
        assert!(change_count.get() == 3);
    }
}
<|endoftext|>


<commit_before>extern crate cursive;
extern crate rand;

mod game;

use cursive::Cursive;
use cursive::Printer;
use cursive::direction::Direction;
use cursive::event::{Event, EventResult, MouseButton, MouseEvent};
use cursive::theme::{BaseColor, Color, ColorStyle};
use cursive::vec::Vec2;
use cursive::views::{Button, Dialog, LinearLayout, Panel, SelectView};

fn main() {
    let mut siv = Cursive::new();

    siv.add_layer(
        Dialog::new()
            .title("Minesweeper")
            .padding((2, 2, 1, 1))
            .content(
                LinearLayout::vertical()
                    .child(Button::new_raw("  New game   ", show_options))
                    .child(Button::new_raw(" Best scores ", |s| {
                        s.add_layer(Dialog::info("Not yet!").title("Scores"))
                    }))
                    .child(Button::new_raw("    Exit     ", |s| s.quit())),
            ),
    );

    siv.run();
}

fn show_options(siv: &mut Cursive) {
    siv.add_layer(
        Dialog::new()
            .title("Select difficulty")
            .content(
                SelectView::new()
                    .item(
                        "Easy:      8x8,   10 mines",
                        game::Options {
                            size: Vec2::new(8, 8),
                            mines: 10,
                        },
                    )
                    .item(
                        "Medium:    16x16, 40 mines",
                        game::Options {
                            size: Vec2::new(16, 16),
                            mines: 40,
                        },
                    )
                    .item(
                        "Difficult: 24x24, 99 mines",
                        game::Options {
                            size: Vec2::new(24, 24),
                            mines: 99,
                        },
                    )
                    .on_submit(|s, option| {
                        s.pop_layer();
                        new_game(s, *option);
                    }),
            )
            .button("Back", |s| s.pop_layer()),
    );
}

#[derive(Clone, Copy, PartialEq)]
enum Cell {
    Visible(usize),
    Flag,
    Unknown,
}

struct BoardView {
    // Actual board, unknown to the player.
    board: game::Board,

    // Visible board
    overlay: Vec<Cell>,

    focused: Option<Vec2>,
    missing_mines: usize,
}

impl BoardView {
    pub fn new(options: game::Options) -> Self {
        let overlay = vec![Cell::Unknown; options.size.x * options.size.y];
        let board = game::Board::new(options);
        BoardView {
            board,
            overlay,
            focused: None,
            missing_mines: options.mines,
        }
    }

    fn get_cell(&self, mouse_pos: Vec2, offset: Vec2) -> Option<Vec2> {
        mouse_pos
            .checked_sub(offset)
            .map(|pos| pos.map_x(|x| x / 2))
            .and_then(|pos| {
                if pos.fits_in(self.board.size) {
                    Some(pos)
                } else {
                    None
                }
            })
    }

    fn flag(&mut self, pos: Vec2) {
        if let Some(i) = self.board.cell_id(pos) {
            let new_cell = match self.overlay[i] {
                Cell::Unknown => Cell::Flag,
                Cell::Flag => Cell::Unknown,
                other => other,
            };
            self.overlay[i] = new_cell;
        }
    }

    fn reveal(&mut self, pos: Vec2) -> EventResult {
        if let Some(i) = self.board.cell_id(pos) {
            if self.overlay[i] != Cell::Unknown {
                return EventResult::Consumed(None);
            }

            // Action!
            match self.board.cells[i] {
                game::Cell::Bomb => {
                    return EventResult::with_cb(|s| {
                        s.add_layer(Dialog::text("BOOOM").button("Ok", |s| {
                            s.pop_layer();
                            s.pop_layer();
                        }));
                    })
                }
                game::Cell::Free(n) => {
                    self.overlay[i] = Cell::Visible(n);
                    if n == 0 {
                        // Reveal all surrounding cells
                        for p in self.board.neighbours(pos) {
                            self.reveal(p);
                        }
                    }
                }
            }
        }
        return EventResult::Consumed(None);
    }

    fn auto_reveal(&mut self, pos: Vec2) -> EventResult {
        if let Some(i) = self.board.cell_id(pos) {
            if let Cell::Visible(n) = self.overlay[i] {
                // First: is every possible cell tagged?
                let neighbours = self.board.neighbours(pos);
                let tagged = neighbours
                    .iter()
                    .filter_map(|&pos| self.board.cell_id(pos))
                    .map(|i| self.overlay[i])
                    .filter(|&cell| cell == Cell::Flag)
                    .count();
                if tagged != n {
                    return EventResult::Consumed(None);
                }

                for p in neighbours {
                    let result = self.reveal(p);
                    if result.has_callback() {
                        return result;
                    }
                }
            }
        }

        EventResult::Consumed(None)
    }
}

impl cursive::view::View for BoardView {
    fn draw(&self, printer: &Printer) {
        for (i, cell) in self.overlay.iter().enumerate() {
            let x = (i % self.board.size.x) * 2;
            let y = i / self.board.size.x;

            let text = match *cell {
                Cell::Unknown => "[]",
                Cell::Flag => "()",
                Cell::Visible(n) => {
                    ["  ", " 1", " 2", " 3", " 4", " 5", " 6", " 7", " 8"][n]
                }
            };

            let color = match *cell {
                Cell::Unknown => Color::RgbLowRes(3, 3, 3),
                Cell::Flag => Color::RgbLowRes(4, 4, 2),
                Cell::Visible(1) => Color::RgbLowRes(3, 5, 3),
                Cell::Visible(2) => Color::RgbLowRes(5, 5, 3),
                Cell::Visible(3) => Color::RgbLowRes(5, 4, 3),
                Cell::Visible(4) => Color::RgbLowRes(5, 3, 3),
                Cell::Visible(5) => Color::RgbLowRes(5, 2, 2),
                Cell::Visible(6) => Color::RgbLowRes(5, 0, 1),
                Cell::Visible(7) => Color::RgbLowRes(5, 0, 2),
                Cell::Visible(8) => Color::RgbLowRes(5, 0, 3),
                _ => Color::Dark(BaseColor::White),
            };

            printer.with_color(
                ColorStyle::new(color, Color::Dark(BaseColor::Black)),
                |printer| printer.print((x, y), text),
            );
        }
    }

    fn take_focus(&mut self, _: Direction) -> bool {
        true
    }

    fn on_event(&mut self, event: Event) -> EventResult {
        match event {
            Event::Mouse {
                offset,
                position,
                event: MouseEvent::Press(btn),
            } => {
                // Get cell for position
                if let Some(pos) = self.get_cell(position, offset) {
                    self.focused = Some(pos);
                    return EventResult::Consumed(None);
                }
            }
            Event::Mouse {
                offset,
                position,
                event: MouseEvent::Release(btn),
            } => {
                // Get cell for position
                if let Some(pos) = self.get_cell(position, offset) {
                    if self.focused == Some(pos) {
                        // We got a click here!
                        match btn {
                            MouseButton::Left => return self.reveal(pos),
                            MouseButton::Right => {
                                self.flag(pos);
                                return EventResult::Consumed(None);
                            }
                            MouseButton::Middle => {
                                return self.auto_reveal(pos);
                            }
                            _ => (),
                        }
                    }

                    self.focused = None;
                }
            }
            _ => (),
        }

        EventResult::Ignored
    }

    fn required_size(&mut self, _: Vec2) -> Vec2 {
        self.board.size.map_x(|x| 2 * x)
    }
}

fn new_game(siv: &mut Cursive, options: game::Options) {
    let board = game::Board::new(options);

    siv.add_layer(
        Dialog::new()
            .title("Minesweeper")
            .content(
                LinearLayout::horizontal()
                    .child(Panel::new(BoardView::new(options))),
            )
            .button("Quit game", |s| {
                s.pop_layer();
            }),
    );

    siv.add_layer(Dialog::info(
        "Controls:
Reveal cell:                  left click
Mark as mine:                 right-click
Reveal nearby unmarked cells: middle-click",
    ));
}
<commit_msg>Revert colors in mines example<commit_after>extern crate cursive;
extern crate rand;

mod game;

use cursive::Cursive;
use cursive::Printer;
use cursive::direction::Direction;
use cursive::event::{Event, EventResult, MouseButton, MouseEvent};
use cursive::theme::{BaseColor, Color, ColorStyle};
use cursive::vec::Vec2;
use cursive::views::{Button, Dialog, LinearLayout, Panel, SelectView};

fn main() {
    let mut siv = Cursive::new();

    siv.add_layer(
        Dialog::new()
            .title("Minesweeper")
            .padding((2, 2, 1, 1))
            .content(
                LinearLayout::vertical()
                    .child(Button::new_raw("  New game   ", show_options))
                    .child(Button::new_raw(" Best scores ", |s| {
                        s.add_layer(Dialog::info("Not yet!").title("Scores"))
                    }))
                    .child(Button::new_raw("    Exit     ", |s| s.quit())),
            ),
    );

    siv.run();
}

fn show_options(siv: &mut Cursive) {
    siv.add_layer(
        Dialog::new()
            .title("Select difficulty")
            .content(
                SelectView::new()
                    .item(
                        "Easy:      8x8,   10 mines",
                        game::Options {
                            size: Vec2::new(8, 8),
                            mines: 10,
                        },
                    )
                    .item(
                        "Medium:    16x16, 40 mines",
                        game::Options {
                            size: Vec2::new(16, 16),
                            mines: 40,
                        },
                    )
                    .item(
                        "Difficult: 24x24, 99 mines",
                        game::Options {
                            size: Vec2::new(24, 24),
                            mines: 99,
                        },
                    )
                    .on_submit(|s, option| {
                        s.pop_layer();
                        new_game(s, *option);
                    }),
            )
            .button("Back", |s| s.pop_layer()),
    );
}

#[derive(Clone, Copy, PartialEq)]
enum Cell {
    Visible(usize),
    Flag,
    Unknown,
}

struct BoardView {
    // Actual board, unknown to the player.
    board: game::Board,

    // Visible board
    overlay: Vec<Cell>,

    focused: Option<Vec2>,
    missing_mines: usize,
}

impl BoardView {
    pub fn new(options: game::Options) -> Self {
        let overlay = vec![Cell::Unknown; options.size.x * options.size.y];
        let board = game::Board::new(options);
        BoardView {
            board,
            overlay,
            focused: None,
            missing_mines: options.mines,
        }
    }

    fn get_cell(&self, mouse_pos: Vec2, offset: Vec2) -> Option<Vec2> {
        mouse_pos
            .checked_sub(offset)
            .map(|pos| pos.map_x(|x| x / 2))
            .and_then(|pos| {
                if pos.fits_in(self.board.size) {
                    Some(pos)
                } else {
                    None
                }
            })
    }

    fn flag(&mut self, pos: Vec2) {
        if let Some(i) = self.board.cell_id(pos) {
            let new_cell = match self.overlay[i] {
                Cell::Unknown => Cell::Flag,
                Cell::Flag => Cell::Unknown,
                other => other,
            };
            self.overlay[i] = new_cell;
        }
    }

    fn reveal(&mut self, pos: Vec2) -> EventResult {
        if let Some(i) = self.board.cell_id(pos) {
            if self.overlay[i] != Cell::Unknown {
                return EventResult::Consumed(None);
            }

            // Action!
            match self.board.cells[i] {
                game::Cell::Bomb => {
                    return EventResult::with_cb(|s| {
                        s.add_layer(Dialog::text("BOOOM").button("Ok", |s| {
                            s.pop_layer();
                            s.pop_layer();
                        }));
                    })
                }
                game::Cell::Free(n) => {
                    self.overlay[i] = Cell::Visible(n);
                    if n == 0 {
                        // Reveal all surrounding cells
                        for p in self.board.neighbours(pos) {
                            self.reveal(p);
                        }
                    }
                }
            }
        }
        return EventResult::Consumed(None);
    }

    fn auto_reveal(&mut self, pos: Vec2) -> EventResult {
        if let Some(i) = self.board.cell_id(pos) {
            if let Cell::Visible(n) = self.overlay[i] {
                // First: is every possible cell tagged?
                let neighbours = self.board.neighbours(pos);
                let tagged = neighbours
                    .iter()
                    .filter_map(|&pos| self.board.cell_id(pos))
                    .map(|i| self.overlay[i])
                    .filter(|&cell| cell == Cell::Flag)
                    .count();
                if tagged != n {
                    return EventResult::Consumed(None);
                }

                for p in neighbours {
                    let result = self.reveal(p);
                    if result.has_callback() {
                        return result;
                    }
                }
            }
        }

        EventResult::Consumed(None)
    }
}

impl cursive::view::View for BoardView {
    fn draw(&self, printer: &Printer) {
        for (i, cell) in self.overlay.iter().enumerate() {
            let x = (i % self.board.size.x) * 2;
            let y = i / self.board.size.x;

            let text = match *cell {
                Cell::Unknown => "[]",
                Cell::Flag => "()",
                Cell::Visible(n) => {
                    ["  ", " 1", " 2", " 3", " 4", " 5", " 6", " 7", " 8"][n]
                }
            };

            let color = match *cell {
                Cell::Unknown => Color::RgbLowRes(3, 3, 3),
                Cell::Flag => Color::RgbLowRes(4, 4, 2),
                Cell::Visible(1) => Color::RgbLowRes(3, 5, 3),
                Cell::Visible(2) => Color::RgbLowRes(5, 5, 3),
                Cell::Visible(3) => Color::RgbLowRes(5, 4, 3),
                Cell::Visible(4) => Color::RgbLowRes(5, 3, 3),
                Cell::Visible(5) => Color::RgbLowRes(5, 2, 2),
                Cell::Visible(6) => Color::RgbLowRes(5, 0, 1),
                Cell::Visible(7) => Color::RgbLowRes(5, 0, 2),
                Cell::Visible(8) => Color::RgbLowRes(5, 0, 3),
                _ => Color::Dark(BaseColor::White),
            };

            printer.with_color(
                ColorStyle::new(Color::Dark(BaseColor::Black), color),
                |printer| printer.print((x, y), text),
            );
        }
    }

    fn take_focus(&mut self, _: Direction) -> bool {
        true
    }

    fn on_event(&mut self, event: Event) -> EventResult {
        match event {
            Event::Mouse {
                offset,
                position,
                event: MouseEvent::Press(btn),
            } => {
                // Get cell for position
                if let Some(pos) = self.get_cell(position, offset) {
                    self.focused = Some(pos);
                    return EventResult::Consumed(None);
                }
            }
            Event::Mouse {
                offset,
                position,
                event: MouseEvent::Release(btn),
            } => {
                // Get cell for position
                if let Some(pos) = self.get_cell(position, offset) {
                    if self.focused == Some(pos) {
                        // We got a click here!
                        match btn {
                            MouseButton::Left => return self.reveal(pos),
                            MouseButton::Right => {
                                self.flag(pos);
                                return EventResult::Consumed(None);
                            }
                            MouseButton::Middle => {
                                return self.auto_reveal(pos);
                            }
                            _ => (),
                        }
                    }

                    self.focused = None;
                }
            }
            _ => (),
        }

        EventResult::Ignored
    }

    fn required_size(&mut self, _: Vec2) -> Vec2 {
        self.board.size.map_x(|x| 2 * x)
    }
}

fn new_game(siv: &mut Cursive, options: game::Options) {
    let board = game::Board::new(options);

    siv.add_layer(
        Dialog::new()
            .title("Minesweeper")
            .content(
                LinearLayout::horizontal()
                    .child(Panel::new(BoardView::new(options))),
            )
            .button("Quit game", |s| {
                s.pop_layer();
            }),
    );

    siv.add_layer(Dialog::info(
        "Controls:
Reveal cell:                  left click
Mark as mine:                 right-click
Reveal nearby unmarked cells: middle-click",
    ));
}
<|endoftext|>


<commit_before>extern crate irc;

use std::default::Default;
use irc::client::prelude::*;

// This is the same as simple.rs, except we use an Iterator over Commands
// instead of an Iterator over Messages. A Command is basically a parsed Message,
// so Commands and Messages are interchangeable. It is up to the library user to
// choose one.

fn main() {
    let config = Config {
        nickname: Some(format!("pickles")),
        alt_nicks: Some(vec![format!("bananas"), format!("apples")]),
        server: Some(format!("irc.fyrechat.net")),
        channels: Some(vec![format!("#vana")]),
        .. Default::default()
    };
    let irc_server = IrcServer::from_config(config).unwrap();
    // The wrapper provides us with methods like send_privmsg(...) and identify(...)
    let server = Wrapper::new(&irc_server);
    server.identify().unwrap();
    for command in server.iter_cmd() {
        // Ignore errors
        // Use of unwrap() with iter_cmd() is discouraged because iter_cmd() is still unstable
        // and has trouble converting some custom Messages into Commands
        match command {
            Ok(cmd) => {
                print!("{}", cmd.to_message().into_string());
                match cmd {
                    Command::PRIVMSG(target, text) => {
                        if text[..].contains("pickles") {
                            server.send_privmsg(&target[..], "Hi!").unwrap();
                        }
                    },
                    _ => ()
                }
            },
            Err(_) => ()
        };
    }
}
<commit_msg>Simplified command iterator example.<commit_after>extern crate irc;

use std::default::Default;
use irc::client::prelude::*;

fn main() {
    let config = Config {
        nickname: Some(format!("pickles")),
        alt_nicks: Some(vec![format!("bananas"), format!("apples")]),
        server: Some(format!("irc.fyrechat.net")),
        channels: Some(vec![format!("#vana")]),
        .. Default::default()
    };
    let irc_server = IrcServer::from_config(config).unwrap();
    // The wrapper provides us with methods like send_privmsg(...) and identify(...)
    let server = Wrapper::new(&irc_server);
    server.identify().unwrap();
    for command in server.iter_cmd() {
        // Use of unwrap() on the results of iter_cmd() is currently discouraged on servers where
        // the v3 capabilities extension is enabled, and the current lapse in specification
        // compliance on that specific command will then cause the program to panic.
        if let Ok(Command::PRIVMSG(chan, msg)) = command { // Ignore errors.
            if msg.contains("pickles") {
                server.send_privmsg(&chan, "Hi!").unwrap();
            }
        }
    }
}
<|endoftext|>


<commit_before>#[macro_use] extern crate rocket;
extern crate redis;
use redis::Commands;

use serde::{Serialize, Deserialize};
use rocket::serde::json::Json;

#[derive(Serialize, Deserialize, Debug)]
struct User {
    name: String,
    status: String
}

#[get("/")]
fn a() -> Json<User> {
    let _x = fetch_an_integer().ok().unwrap();
    let u = User { name: "a".to_string(), status: _x };
    Json(u)
}

fn fetch_an_integer() -> redis::RedisResult<String> {
    let client = redis::Client::open("redis://127.0.0.1/")?;
    let mut con = client.get_connection()?;
    let _ : () = con.set("my_key", 42)?;
    con.get("my_key")
}

#[launch]
fn rocket() -> _ {
    rocket::build().mount("/", routes![a])
}
<commit_msg>get set<commit_after>#[macro_use] extern crate rocket;
extern crate redis;
use redis::Commands;

use serde::{Serialize, Deserialize};
use rocket::serde::json::Json;

#[derive(Serialize, Deserialize, Debug)]
struct User {
    name: String,
    status: String
}

fn getcon() -> Result<redis::Connection,redis::RedisError> {
    let client = redis::Client::open("redis://127.0.0.1/")?;
    client.get_connection()
}

fn get(id:String) -> Result<String,redis::RedisError> {
    let x:Result<String,redis::RedisError> = getcon()?.get(id);
    x.or(Ok("".to_string()))
}
fn set(id:String,val:String) -> Result<(),redis::RedisError> {
    getcon()?.set(&id,val)
}

#[get("/r/<id>")]
fn r(id:String) -> Json<User> {
    let u = User { name: id.to_string(), status: get(id).unwrap() };
    Json(u)
}

// s/b post
#[get("/s/<id>?<val>")]
fn s(id:String, val:String) -> Json<User> {
    set(id.to_string(),val).unwrap();
    r(id)
}

#[launch]
fn rocket() -> _ {
    rocket::build().mount("/", routes![r,s])
}
<|endoftext|>


<commit_before>use sync::atomic::Ordering;

#[cfg(test)]
use stdsimd_test::assert_instr;

/// Compare and exchange 16 bytes (128 bits) of data atomically.
///
/// This intrinsic corresponds to the `cmpxchg16b` instruction on x86_64
/// processors. It performs an atomic compare-and-swap, updating the `ptr`
/// memory location to `val` if the current value in memory equals `old`.
///
/// # Return value
///
/// This function returns the previous value at the memory location. If it is
/// equal to `old` then the memory was updated to `new`.
///
/// # Memory Orderings
///
/// This atomic operations has the same semantics of memory orderings as
/// `AtomicUsize::compare_exchange` does, only operating on 16 bytes of memory
/// instead of just a pointer.
///
/// For more information on memory orderings here see the `compare_exchange`
/// documentation for other `Atomic*` types in the standard library.
///
/// # Unsafety
///
/// This method is unsafe because it takes a raw pointer and will attempt to
/// read and possibly write the memory at the pointer. The pointer must also be
/// aligned on a 16-byte boundary.
///
/// This method also requires the `cmpxchg16b` CPU feature to be available at
/// runtime to work correctly. If the CPU running the binary does not actually
/// support `cmpxchg16b` and the program enters an execution path that
/// eventually would reach this function the behavior is undefined.
///
/// The `success` ordering must also be stronger or equal to `failure`, or this
/// function call is undefined. See the `Atomic*` documentation's
/// `compare_exchange` function for more information. When `compare_exchange`
/// panics, this is undefined behavior. Currently this function aborts the
/// process with an undefined instruction.
#[inline]
#[cfg_attr(test, assert_instr(cmpxchg16b, success = Ordering::SeqCst, failure = Ordering::SeqCst))]
#[target_feature(enable = "cmpxchg16b")]
pub unsafe fn cmpxchg16b(
    dst: *mut u128,
    old: u128,
    new: u128,
    success: Ordering,
    failure: Ordering,
) -> u128 {
    use intrinsics;
    use sync::atomic::Ordering::*;

    debug_assert!(dst as usize % 16 == 0);

    let (val, _ok) = match (success, failure) {
        (Acquire, Acquire) => intrinsics::atomic_cxchg_acq(dst, old, new),
        (Release, Relaxed) => intrinsics::atomic_cxchg_rel(dst, old, new),
        (AcqRel, Acquire) => intrinsics::atomic_cxchg_acqrel(dst, old, new),
        (Relaxed, Relaxed) => intrinsics::atomic_cxchg_relaxed(dst, old, new),
        (SeqCst, SeqCst) => intrinsics::atomic_cxchg(dst, old, new),
        (Acquire, Relaxed) => intrinsics::atomic_cxchg_acq_failrelaxed(dst, old, new),
        (AcqRel, Relaxed) => intrinsics::atomic_cxchg_acqrel_failrelaxed(dst, old, new),
        (SeqCst, Relaxed) => intrinsics::atomic_cxchg_failrelaxed(dst, old, new),
        (SeqCst, Acquire) => intrinsics::atomic_cxchg_failacq(dst, old, new),

        // The above block is all copied from libcore, and this statement is
        // also copied from libcore except that it's a panic in libcore and we
        // have a little bit more of a lightweight panic here.
        _ => ::coresimd::x86::ud2(),
    };
    val
}
<commit_msg>Add stage0 guard for cmpxchg16b<commit_after>use sync::atomic::Ordering;

#[cfg(test)]
use stdsimd_test::assert_instr;

/// Compare and exchange 16 bytes (128 bits) of data atomically.
///
/// This intrinsic corresponds to the `cmpxchg16b` instruction on x86_64
/// processors. It performs an atomic compare-and-swap, updating the `ptr`
/// memory location to `val` if the current value in memory equals `old`.
///
/// # Return value
///
/// This function returns the previous value at the memory location. If it is
/// equal to `old` then the memory was updated to `new`.
///
/// # Memory Orderings
///
/// This atomic operations has the same semantics of memory orderings as
/// `AtomicUsize::compare_exchange` does, only operating on 16 bytes of memory
/// instead of just a pointer.
///
/// For more information on memory orderings here see the `compare_exchange`
/// documentation for other `Atomic*` types in the standard library.
///
/// # Unsafety
///
/// This method is unsafe because it takes a raw pointer and will attempt to
/// read and possibly write the memory at the pointer. The pointer must also be
/// aligned on a 16-byte boundary.
///
/// This method also requires the `cmpxchg16b` CPU feature to be available at
/// runtime to work correctly. If the CPU running the binary does not actually
/// support `cmpxchg16b` and the program enters an execution path that
/// eventually would reach this function the behavior is undefined.
///
/// The `success` ordering must also be stronger or equal to `failure`, or this
/// function call is undefined. See the `Atomic*` documentation's
/// `compare_exchange` function for more information. When `compare_exchange`
/// panics, this is undefined behavior. Currently this function aborts the
/// process with an undefined instruction.
#[inline]
#[cfg_attr(test, assert_instr(cmpxchg16b, success = Ordering::SeqCst, failure = Ordering::SeqCst))]
#[target_feature(enable = "cmpxchg16b")]
#[cfg(not(stage0))]
pub unsafe fn cmpxchg16b(
    dst: *mut u128,
    old: u128,
    new: u128,
    success: Ordering,
    failure: Ordering,
) -> u128 {
    use intrinsics;
    use sync::atomic::Ordering::*;

    debug_assert!(dst as usize % 16 == 0);

    let (val, _ok) = match (success, failure) {
        (Acquire, Acquire) => intrinsics::atomic_cxchg_acq(dst, old, new),
        (Release, Relaxed) => intrinsics::atomic_cxchg_rel(dst, old, new),
        (AcqRel, Acquire) => intrinsics::atomic_cxchg_acqrel(dst, old, new),
        (Relaxed, Relaxed) => intrinsics::atomic_cxchg_relaxed(dst, old, new),
        (SeqCst, SeqCst) => intrinsics::atomic_cxchg(dst, old, new),
        (Acquire, Relaxed) => intrinsics::atomic_cxchg_acq_failrelaxed(dst, old, new),
        (AcqRel, Relaxed) => intrinsics::atomic_cxchg_acqrel_failrelaxed(dst, old, new),
        (SeqCst, Relaxed) => intrinsics::atomic_cxchg_failrelaxed(dst, old, new),
        (SeqCst, Acquire) => intrinsics::atomic_cxchg_failacq(dst, old, new),

        // The above block is all copied from libcore, and this statement is
        // also copied from libcore except that it's a panic in libcore and we
        // have a little bit more of a lightweight panic here.
        _ => ::coresimd::x86::ud2(),
    };
    val
}
<|endoftext|>


<commit_before>// Copyright (c) Aptos
// SPDX-License-Identifier: Apache-2.0

use crate::common::types::{
    CliCommand, CliResult, CliTypedResult, TransactionOptions, TransactionSummary,
};
use crate::common::utils::prompt_yes_with_override;
use aptos_types::account_address::{default_stake_pool_address, AccountAddress};
use async_trait::async_trait;
use cached_packages::aptos_stdlib;
use clap::Parser;

/// Tool for manipulating stake
///
#[derive(Parser)]
pub enum StakeTool {
    AddStake(AddStake),
    CreateStakingContract(CreateStakingContract),
    UnlockStake(UnlockStake),
    WithdrawStake(WithdrawStake),
    IncreaseLockup(IncreaseLockup),
    InitializeStakeOwner(InitializeStakeOwner),
    SetOperator(SetOperator),
    SetDelegatedVoter(SetDelegatedVoter),
}

impl StakeTool {
    pub async fn execute(self) -> CliResult {
        use StakeTool::*;
        match self {
            AddStake(tool) => tool.execute_serialized().await,
            CreateStakingContract(tool) => tool.execute_serialized().await,
            UnlockStake(tool) => tool.execute_serialized().await,
            WithdrawStake(tool) => tool.execute_serialized().await,
            IncreaseLockup(tool) => tool.execute_serialized().await,
            InitializeStakeOwner(tool) => tool.execute_serialized().await,
            SetOperator(tool) => tool.execute_serialized().await,
            SetDelegatedVoter(tool) => tool.execute_serialized().await,
        }
    }
}

/// Stake APT coins to the stake pool
///
/// This command allows stake pool owners to add APT coins to their stake.
#[derive(Parser)]
pub struct AddStake {
    /// Amount of Octas (10^-8 APT) to add to stake
    #[clap(long)]
    pub amount: u64,

    #[clap(flatten)]
    pub(crate) txn_options: TransactionOptions,
}

#[async_trait]
impl CliCommand<TransactionSummary> for AddStake {
    fn command_name(&self) -> &'static str {
        "AddStake"
    }

    async fn execute(mut self) -> CliTypedResult<TransactionSummary> {
        self.txn_options
            .submit_transaction(aptos_stdlib::stake_add_stake(self.amount))
            .await
            .map(|inner| inner.into())
    }
}

/// Unlock staked APT coins
///
/// APT coins can only be unlocked if they no longer have an applied lockup period
#[derive(Parser)]
pub struct UnlockStake {
    /// Amount of Octas (10^-8 APT) to unlock
    #[clap(long)]
    pub amount: u64,

    #[clap(flatten)]
    pub(crate) txn_options: TransactionOptions,
}

#[async_trait]
impl CliCommand<TransactionSummary> for UnlockStake {
    fn command_name(&self) -> &'static str {
        "UnlockStake"
    }

    async fn execute(mut self) -> CliTypedResult<TransactionSummary> {
        self.txn_options
            .submit_transaction(aptos_stdlib::stake_unlock(self.amount))
            .await
            .map(|inner| inner.into())
    }
}

/// Withdraw unlocked staked APT coins
///
/// This allows users to withdraw stake back into their CoinStore.
/// Before calling `WithdrawStake`, `UnlockStake` must be called first.
#[derive(Parser)]
pub struct WithdrawStake {
    /// Amount of Octas (10^-8 APT) to withdraw
    #[clap(long)]
    pub amount: u64,

    #[clap(flatten)]
    pub(crate) node_op_options: TransactionOptions,
}

#[async_trait]
impl CliCommand<TransactionSummary> for WithdrawStake {
    fn command_name(&self) -> &'static str {
        "WithdrawStake"
    }

    async fn execute(mut self) -> CliTypedResult<TransactionSummary> {
        self.node_op_options
            .submit_transaction(aptos_stdlib::stake_withdraw(self.amount))
            .await
            .map(|inner| inner.into())
    }
}

/// Increase lockup of all staked APT coins in the stake pool
///
/// Lockup may need to be increased in order to vote on a proposal.
#[derive(Parser)]
pub struct IncreaseLockup {
    #[clap(flatten)]
    pub(crate) txn_options: TransactionOptions,
}

#[async_trait]
impl CliCommand<TransactionSummary> for IncreaseLockup {
    fn command_name(&self) -> &'static str {
        "IncreaseLockup"
    }

    async fn execute(mut self) -> CliTypedResult<TransactionSummary> {
        self.txn_options
            .submit_transaction(aptos_stdlib::stake_increase_lockup())
            .await
            .map(|inner| inner.into())
    }
}

/// Initialize stake owner
///
/// Initializing stake owner adds the capability to delegate the
/// stake pool to an operator, or delegate voting to a different account.
#[derive(Parser)]
pub struct InitializeStakeOwner {
    /// Initial amount of Octas (10^-8 APT) to be staked
    #[clap(long)]
    pub initial_stake_amount: u64,

    /// Account Address of delegated operator
    ///
    /// If not specified, it will be the same as the owner
    #[clap(long, parse(try_from_str=crate::common::types::load_account_arg))]
    pub operator_address: Option<AccountAddress>,

    /// Account address of delegated voter
    ///
    /// If not specified, it will be the same as the owner
    #[clap(long, parse(try_from_str=crate::common::types::load_account_arg))]
    pub voter_address: Option<AccountAddress>,

    #[clap(flatten)]
    pub(crate) txn_options: TransactionOptions,
}

#[async_trait]
impl CliCommand<TransactionSummary> for InitializeStakeOwner {
    fn command_name(&self) -> &'static str {
        "InitializeStakeOwner"
    }

    async fn execute(mut self) -> CliTypedResult<TransactionSummary> {
        let owner_address = self.txn_options.sender_address()?;
        self.txn_options
            .submit_transaction(aptos_stdlib::stake_initialize_stake_owner(
                self.initial_stake_amount,
                self.operator_address.unwrap_or(owner_address),
                self.voter_address.unwrap_or(owner_address),
            ))
            .await
            .map(|inner| inner.into())
    }
}

/// Delegate operator capability from the stake owner to another account
#[derive(Parser)]
pub struct SetOperator {
    /// Account Address of delegated operator
    ///
    /// If not specified, it will be the same as the owner
    #[clap(long, parse(try_from_str=crate::common::types::load_account_arg))]
    pub operator_address: AccountAddress,

    #[clap(flatten)]
    pub(crate) txn_options: TransactionOptions,
}

#[async_trait]
impl CliCommand<TransactionSummary> for SetOperator {
    fn command_name(&self) -> &'static str {
        "SetOperator"
    }

    async fn execute(mut self) -> CliTypedResult<TransactionSummary> {
        self.txn_options
            .submit_transaction(aptos_stdlib::stake_set_operator(self.operator_address))
            .await
            .map(|inner| inner.into())
    }
}

/// Delegate voting capability from the stake owner to another account
#[derive(Parser)]
pub struct SetDelegatedVoter {
    /// Account Address of delegated voter
    ///
    /// If not specified, it will be the same as the owner
    #[clap(long, parse(try_from_str=crate::common::types::load_account_arg))]
    pub voter_address: AccountAddress,

    #[clap(flatten)]
    pub(crate) txn_options: TransactionOptions,
}

#[async_trait]
impl CliCommand<TransactionSummary> for SetDelegatedVoter {
    fn command_name(&self) -> &'static str {
        "SetDelegatedVoter"
    }

    async fn execute(mut self) -> CliTypedResult<TransactionSummary> {
        self.txn_options
            .submit_transaction(aptos_stdlib::stake_set_delegated_voter(self.voter_address))
            .await
            .map(|inner| inner.into())
    }
}

#[derive(Parser)]
pub struct CreateStakingContract {
    /// Account Address of operator
    #[clap(long, parse(try_from_str=crate::common::types::load_account_arg))]
    pub operator: AccountAddress,

    /// Account Address of delegated voter
    #[clap(long, parse(try_from_str=crate::common::types::load_account_arg))]
    pub voter: AccountAddress,

    /// Amount to create the staking contract with
    #[clap(long)]
    pub amount: u64,

    /// Percentage of accumulated rewards to pay the operator as commission
    #[clap(long)]
    pub commission_percentage: u64,

    #[clap(flatten)]
    pub(crate) txn_options: TransactionOptions,
}

#[async_trait]
impl CliCommand<TransactionSummary> for CreateStakingContract {
    fn command_name(&self) -> &'static str {
        "CreateStakingContract"
    }

    async fn execute(mut self) -> CliTypedResult<TransactionSummary> {
        let pool_address = default_stake_pool_address(
            self.txn_options.profile_options.account_address()?,
            self.operator,
        );
        prompt_yes_with_override(
            &format!(
                "Creating a new staking contract with pool address 0x{}. Confirm?",
                pool_address
            ),
            self.txn_options.prompt_options,
        )?;

        self.txn_options
            .submit_transaction(aptos_stdlib::staking_contract_create_staking_contract(
                self.operator,
                self.voter,
                self.amount,
                self.commission_percentage,
                vec![],
            ))
            .await
            .map(|inner| inner.into())
    }
}
<commit_msg>[CLI] Add support for vesting contract<commit_after>// Copyright (c) Aptos
// SPDX-License-Identifier: Apache-2.0

use crate::common::types::{
    CliCommand, CliResult, CliTypedResult, TransactionOptions, TransactionSummary,
};
use crate::common::utils::prompt_yes_with_override;
use aptos_types::account_address::{
    create_vesting_contract_address, default_stake_pool_address, AccountAddress,
};
use async_trait::async_trait;
use cached_packages::aptos_stdlib;
use clap::Parser;

/// Tool for manipulating stake
///
#[derive(Parser)]
pub enum StakeTool {
    AddStake(AddStake),
    CreateStakingContract(CreateStakingContract),
    UnlockStake(UnlockStake),
    WithdrawStake(WithdrawStake),
    IncreaseLockup(IncreaseLockup),
    InitializeStakeOwner(InitializeStakeOwner),
    SetOperator(SetOperator),
    SetDelegatedVoter(SetDelegatedVoter),
    UnlockVestedCoins(UnlockVestedCoins),
    DistributeVestedCoins(DistributeVestedCoins),
}

impl StakeTool {
    pub async fn execute(self) -> CliResult {
        use StakeTool::*;
        match self {
            AddStake(tool) => tool.execute_serialized().await,
            CreateStakingContract(tool) => tool.execute_serialized().await,
            UnlockStake(tool) => tool.execute_serialized().await,
            WithdrawStake(tool) => tool.execute_serialized().await,
            IncreaseLockup(tool) => tool.execute_serialized().await,
            InitializeStakeOwner(tool) => tool.execute_serialized().await,
            SetOperator(tool) => tool.execute_serialized().await,
            SetDelegatedVoter(tool) => tool.execute_serialized().await,
            UnlockVestedCoins(tool) => tool.execute_serialized().await,
            DistributeVestedCoins(tool) => tool.execute_serialized().await,
        }
    }
}

/// Stake APT coins to the stake pool
///
/// This command allows stake pool owners to add APT coins to their stake.
#[derive(Parser)]
pub struct AddStake {
    /// Amount of Octas (10^-8 APT) to add to stake
    #[clap(long)]
    pub amount: u64,

    #[clap(flatten)]
    pub(crate) txn_options: TransactionOptions,
}

#[async_trait]
impl CliCommand<TransactionSummary> for AddStake {
    fn command_name(&self) -> &'static str {
        "AddStake"
    }

    async fn execute(mut self) -> CliTypedResult<TransactionSummary> {
        self.txn_options
            .submit_transaction(aptos_stdlib::stake_add_stake(self.amount))
            .await
            .map(|inner| inner.into())
    }
}

/// Unlock staked APT coins
///
/// APT coins can only be unlocked if they no longer have an applied lockup period
#[derive(Parser)]
pub struct UnlockStake {
    /// Amount of Octas (10^-8 APT) to unlock
    #[clap(long)]
    pub amount: u64,

    #[clap(flatten)]
    pub(crate) txn_options: TransactionOptions,
}

#[async_trait]
impl CliCommand<TransactionSummary> for UnlockStake {
    fn command_name(&self) -> &'static str {
        "UnlockStake"
    }

    async fn execute(mut self) -> CliTypedResult<TransactionSummary> {
        self.txn_options
            .submit_transaction(aptos_stdlib::stake_unlock(self.amount))
            .await
            .map(|inner| inner.into())
    }
}

/// Withdraw unlocked staked APT coins
///
/// This allows users to withdraw stake back into their CoinStore.
/// Before calling `WithdrawStake`, `UnlockStake` must be called first.
#[derive(Parser)]
pub struct WithdrawStake {
    /// Amount of Octas (10^-8 APT) to withdraw
    #[clap(long)]
    pub amount: u64,

    #[clap(flatten)]
    pub(crate) node_op_options: TransactionOptions,
}

#[async_trait]
impl CliCommand<TransactionSummary> for WithdrawStake {
    fn command_name(&self) -> &'static str {
        "WithdrawStake"
    }

    async fn execute(mut self) -> CliTypedResult<TransactionSummary> {
        self.node_op_options
            .submit_transaction(aptos_stdlib::stake_withdraw(self.amount))
            .await
            .map(|inner| inner.into())
    }
}

/// Increase lockup of all staked APT coins in the stake pool
///
/// Lockup may need to be increased in order to vote on a proposal.
#[derive(Parser)]
pub struct IncreaseLockup {
    #[clap(flatten)]
    pub(crate) txn_options: TransactionOptions,
}

#[async_trait]
impl CliCommand<TransactionSummary> for IncreaseLockup {
    fn command_name(&self) -> &'static str {
        "IncreaseLockup"
    }

    async fn execute(mut self) -> CliTypedResult<TransactionSummary> {
        self.txn_options
            .submit_transaction(aptos_stdlib::stake_increase_lockup())
            .await
            .map(|inner| inner.into())
    }
}

/// Initialize stake owner
///
/// Initializing stake owner adds the capability to delegate the
/// stake pool to an operator, or delegate voting to a different account.
#[derive(Parser)]
pub struct InitializeStakeOwner {
    /// Initial amount of Octas (10^-8 APT) to be staked
    #[clap(long)]
    pub initial_stake_amount: u64,

    /// Account Address of delegated operator
    ///
    /// If not specified, it will be the same as the owner
    #[clap(long, parse(try_from_str=crate::common::types::load_account_arg))]
    pub operator_address: Option<AccountAddress>,

    /// Account address of delegated voter
    ///
    /// If not specified, it will be the same as the owner
    #[clap(long, parse(try_from_str=crate::common::types::load_account_arg))]
    pub voter_address: Option<AccountAddress>,

    #[clap(flatten)]
    pub(crate) txn_options: TransactionOptions,
}

#[async_trait]
impl CliCommand<TransactionSummary> for InitializeStakeOwner {
    fn command_name(&self) -> &'static str {
        "InitializeStakeOwner"
    }

    async fn execute(mut self) -> CliTypedResult<TransactionSummary> {
        let owner_address = self.txn_options.sender_address()?;
        self.txn_options
            .submit_transaction(aptos_stdlib::stake_initialize_stake_owner(
                self.initial_stake_amount,
                self.operator_address.unwrap_or(owner_address),
                self.voter_address.unwrap_or(owner_address),
            ))
            .await
            .map(|inner| inner.into())
    }
}

/// Delegate operator capability from the stake owner to another account
#[derive(Parser)]
pub struct SetOperator {
    /// Account Address of delegated operator
    ///
    /// If not specified, it will be the same as the owner
    #[clap(long, parse(try_from_str=crate::common::types::load_account_arg))]
    pub operator_address: AccountAddress,

    #[clap(flatten)]
    pub(crate) txn_options: TransactionOptions,
}

#[async_trait]
impl CliCommand<TransactionSummary> for SetOperator {
    fn command_name(&self) -> &'static str {
        "SetOperator"
    }

    async fn execute(mut self) -> CliTypedResult<TransactionSummary> {
        self.txn_options
            .submit_transaction(aptos_stdlib::stake_set_operator(self.operator_address))
            .await
            .map(|inner| inner.into())
    }
}

/// Delegate voting capability from the stake owner to another account
#[derive(Parser)]
pub struct SetDelegatedVoter {
    /// Account Address of delegated voter
    ///
    /// If not specified, it will be the same as the owner
    #[clap(long, parse(try_from_str=crate::common::types::load_account_arg))]
    pub voter_address: AccountAddress,

    #[clap(flatten)]
    pub(crate) txn_options: TransactionOptions,
}

#[async_trait]
impl CliCommand<TransactionSummary> for SetDelegatedVoter {
    fn command_name(&self) -> &'static str {
        "SetDelegatedVoter"
    }

    async fn execute(mut self) -> CliTypedResult<TransactionSummary> {
        self.txn_options
            .submit_transaction(aptos_stdlib::stake_set_delegated_voter(self.voter_address))
            .await
            .map(|inner| inner.into())
    }
}

#[derive(Parser)]
pub struct CreateStakingContract {
    /// Account Address of operator
    #[clap(long, parse(try_from_str=crate::common::types::load_account_arg))]
    pub operator: AccountAddress,

    /// Account Address of delegated voter
    #[clap(long, parse(try_from_str=crate::common::types::load_account_arg))]
    pub voter: AccountAddress,

    /// Amount to create the staking contract with
    #[clap(long)]
    pub amount: u64,

    /// Percentage of accumulated rewards to pay the operator as commission
    #[clap(long)]
    pub commission_percentage: u64,

    #[clap(flatten)]
    pub(crate) txn_options: TransactionOptions,
}

#[async_trait]
impl CliCommand<TransactionSummary> for CreateStakingContract {
    fn command_name(&self) -> &'static str {
        "CreateStakingContract"
    }

    async fn execute(mut self) -> CliTypedResult<TransactionSummary> {
        let pool_address = default_stake_pool_address(
            self.txn_options.profile_options.account_address()?,
            self.operator,
        );
        prompt_yes_with_override(
            &format!(
                "Creating a new staking contract with pool address 0x{}. Confirm?",
                pool_address
            ),
            self.txn_options.prompt_options,
        )?;

        self.txn_options
            .submit_transaction(aptos_stdlib::staking_contract_create_staking_contract(
                self.operator,
                self.voter,
                self.amount,
                self.commission_percentage,
                vec![],
            ))
            .await
            .map(|inner| inner.into())
    }
}

/// Distribute any fully unlocked tokens (rewards and/or vested tokens) from the vesting contract
/// to shareholders.
#[derive(Parser)]
pub struct DistributeVestedCoins {
    /// Address of the vesting contract's admin.
    ///
    /// Defaults to the profile's address if not set.
    #[clap(long, parse(try_from_str=crate::common::types::load_account_arg))]
    pub admin_address: AccountAddress,

    #[clap(flatten)]
    pub(crate) txn_options: TransactionOptions,
}

#[async_trait]
impl CliCommand<TransactionSummary> for DistributeVestedCoins {
    fn command_name(&self) -> &'static str {
        "DistributeVestedCoins"
    }

    async fn execute(mut self) -> CliTypedResult<TransactionSummary> {
        let vesting_contract_address = create_vesting_contract_address(self.admin_address, 0, &[]);
        self.txn_options
            .submit_transaction(aptos_stdlib::vesting_distribute(vesting_contract_address))
            .await
            .map(|inner| inner.into())
    }
}

/// Unlock any vesting tokens according to the vesting contract's schedule.
/// This also unlock any accumulated staking rewards and pays commission to the operator of the
/// vesting contract's stake pool first.
///
/// The unlocked vested tokens and staking rewards are still subject to the staking lockup and
/// cannot be withdrawn until after the lockup expires.
#[derive(Parser)]
pub struct UnlockVestedCoins {
    /// Address of the vesting contract's admin.
    ///
    /// Defaults to the profile's address if not set.
    #[clap(long, parse(try_from_str=crate::common::types::load_account_arg))]
    pub admin_address: AccountAddress,

    #[clap(flatten)]
    pub(crate) txn_options: TransactionOptions,
}

#[async_trait]
impl CliCommand<TransactionSummary> for UnlockVestedCoins {
    fn command_name(&self) -> &'static str {
        "UnlockVestedCoins"
    }

    async fn execute(mut self) -> CliTypedResult<TransactionSummary> {
        let vesting_contract_address = create_vesting_contract_address(self.admin_address, 0, &[]);
        self.txn_options
            .submit_transaction(aptos_stdlib::vesting_vest(vesting_contract_address))
            .await
            .map(|inner| inner.into())
    }
}
<|endoftext|>


<commit_before><commit_msg>Check for integer overflow in read_exact_bytes<commit_after><|endoftext|>


<commit_before>use regex::Regex;
use std::path::PathBuf;
use std::result::Result;
use subprocess::{Exec, Redirection};

const LOOP_BACKING_DEVICE_BASENAME: &'static str = "flaglock6-home";
const LOOP_DEVICE_PARTITION_LABEL: &'static str = "fl6";
const RSYNC_DOT_NET_REMOTE_DIR: &'static str = "usw-s007.rsync.net:./";

#[derive(Debug)]
pub enum AuricError {
    Invocation(String),
    Io(String),
    Subprocess(String),
    NotFound,
}

enum AuricOperationMode {
    Mount,
    Unmount,
}

impl From<subprocess::PopenError> for AuricError {
    fn from(err: subprocess::PopenError) -> AuricError {
        AuricError::Subprocess(err.to_string())
    }
}

struct SshfsManager {
    mountpoint: PathBuf,
}

struct LoopManager {
    target: Regex,
    backing: PathBuf,
}

struct LuksManager {
    device: PathBuf,
}

struct AuricImpl {
    sshfs_manager: SshfsManager,
    loop_manager: LoopManager,
    luks_manager: LuksManager,
}

impl SshfsManager {
    pub fn new() -> Result<SshfsManager, AuricError> {
        let xdg_runtime_dir = match std::env::var("XDG_RUNTIME_DIR") {
            Ok(val) => val,
            Err(e) => {
                return Err(AuricError::Invocation(format!(
                    "fatal error getting ${{XDG_RUNTIME_DIR}}: ``{}''",
                    e
                )))
            }
        };
        let mut mountpoint = PathBuf::new();
        mountpoint.push(xdg_runtime_dir);
        mountpoint.push("rsync.net-mountpoint");

        Ok(SshfsManager {
            mountpoint: mountpoint,
        })
    }

    fn ensure_mountpoint_is_dir(&self) -> Result<(), AuricError> {
        if self.mountpoint.is_dir() {
            return Ok(());
        }
        std::fs::create_dir(self.mountpoint.as_path())
            .map_err(|e| AuricError::Io(format!("bad mountpoint: ``{}''", e)))
    }

    // Does _not_ ensure that backing device exists.
    pub fn loop_backing_device_path(&self) -> PathBuf {
        let mut result: PathBuf = self.mountpoint.clone();
        result.push(LOOP_BACKING_DEVICE_BASENAME);
        result
    }

    fn loop_backing_device_present(&self) -> bool {
        self.loop_backing_device_path().is_file()
    }

    pub fn mount(&self) -> Result<(), AuricError> {
        self.ensure_mountpoint_is_dir()?;
        if self.loop_backing_device_present() {
            return Ok(());
        }

        // Invoke `sshfs`
        // *    with compression,
        // *    allowing other users to access the mounted filesystem
        //      (necessary to allow loop-mounts), and
        // *    demanding synchronous writes, making it easier to reason
        //      about when a write is done.
        let result = Exec::cmd("sshfs")
            .arg("-C")
            .arg("-o")
            .arg("allow_other,sshfs_sync")
            .arg(RSYNC_DOT_NET_REMOTE_DIR)
            .arg(&self.mountpoint)
            .stdout(Redirection::Pipe)
            .stderr(Redirection::Pipe)
            .capture()?;
        if !result.success() {
            return Err(AuricError::Subprocess(format!(
                "sshfs failed: ``{}''",
                result.stderr_str()
            )));
        }
        Ok(())
    }

    pub fn unmount(&self) -> Result<(), AuricError> {
        if !self.loop_backing_device_present() {
            return Ok(());
        }

        let result = Exec::cmd("fusermount")
            .arg("-u")
            .arg(&self.mountpoint)
            .stdout(Redirection::Pipe)
            .stderr(Redirection::Pipe)
            .capture()?;
        if !result.success() {
            return Err(AuricError::Subprocess(format!(
                "fusermount -u failed: ``{}''",
                result.stderr_str()
            )));
        }
        Ok(())
    }
}

impl LoopManager {
    pub fn new(backing: &PathBuf) -> LoopManager {
        let target = format!(
            r"^(/dev/loop\d)(\s+\d+){{4}}\s+({})\s+",
            backing.to_str().unwrap()
        );
        LoopManager {
            target: Regex::new(&target).unwrap(),
            backing: backing.clone(),
        }
    }

    pub fn find(&self) -> Result<PathBuf, AuricError> {
        let result = Exec::cmd("losetup")
            .arg("-l")
            .stdout(Redirection::Pipe)
            .stderr(Redirection::Pipe)
            .capture()?;
        if !result.success() {
            return Err(AuricError::Subprocess(format!(
                "failed to list loop devices: ``{}''",
                result.stderr_str()
            )));
        }

        for line in result.stdout_str().lines() {
            if let Some(captures) = self.target.captures(line) {
                return Ok(PathBuf::from(captures.get(1).unwrap().as_str()));
            }
        }
        Err(AuricError::NotFound)
    }

    pub fn attach(&self) -> Result<(), AuricError> {
        if let Ok(_attached_loop) = self.find() {
            return Ok(());
        }

        let result = Exec::cmd("udisksctl")
            .arg("loop-setup")
            .arg("-f")
            .arg(&self.backing)
            .stdout(Redirection::Pipe)
            .stderr(Redirection::Pipe)
            .capture()?;
        if !result.success() {
            return Err(AuricError::Subprocess(format!(
                "failed to attach loop device: ``{}''",
                result.stderr_str()
            )));
        }
        Ok(())
    }

    // Note that the loop device will linger if the volume remains
    // unlocked. Prefer not to call this before locking.
    pub fn detach(&self) -> Result<(), AuricError> {
        let attached_loop = match self.find() {
            Ok(path) => path,
            Err(e) => {
                if let AuricError::NotFound = e {
                    return Ok(());
                }
                return Err(e);
            }
        };
        let result = Exec::cmd("udisksctl")
            .arg("loop-delete")
            .arg("-b")
            .arg(&attached_loop)
            .stdout(Redirection::Pipe)
            .stderr(Redirection::Pipe)
            .capture()?;
        if !result.success() {
            return Err(AuricError::Subprocess(format!(
                "failed to detach loop device: ``{}''",
                result.stderr_str()
            )));
        }
        Ok(())
    }
}

impl LuksManager {
    pub fn new() -> LuksManager {
        let mut device = PathBuf::from("/dev/disk/by-label");
        device.push(LOOP_DEVICE_PARTITION_LABEL);
        LuksManager { device: device }
    }

    pub fn unlock(&self, locked_device: &PathBuf) -> Result<(), AuricError> {
        let result = Exec::cmd("udisksctl")
            .arg("unlock")
            .arg("-b")
            .arg(locked_device)
            .stdout(Redirection::Pipe)
            .stderr(Redirection::Pipe)
            .capture()?;
        if !result.success() {
            return Err(AuricError::Subprocess(format!(
                "failed to unlock {}: ``{}''",
                locked_device.to_str().unwrap(),
                result.stderr_str()
            )));
        }
        Ok(())
    }

    pub fn lock(&self, underlying_device: &PathBuf) -> Result<(), AuricError> {
        let result = Exec::cmd("udisksctl")
            .arg("lock")
            .arg("-b")
            .arg(underlying_device)
            .stdout(Redirection::Pipe)
            .stderr(Redirection::Pipe)
            .capture()?;
        if !result.success() {
            return Err(AuricError::Subprocess(format!(
                "failed to lock {}: ``{}''",
                underlying_device.to_str().unwrap(),
                result.stderr_str()
            )));
        }
        Ok(())
    }

    pub fn mount(&self) -> Result<(), AuricError> {
        let result = Exec::cmd("udisksctl")
            .arg("mount")
            .arg("-b")
            .arg(&self.device)
            .stdout(Redirection::Pipe)
            .stderr(Redirection::Pipe)
            .capture()?;
        if !result.success() {
            return Err(AuricError::Subprocess(format!(
                "failed to mount {}: ``{}''",
                self.device.to_str().unwrap(),
                result.stderr_str()
            )));
        }
        Ok(())
    }

    pub fn unmount(&self) -> Result<(), AuricError> {
        let result = Exec::cmd("udisksctl")
            .arg("unmount")
            .arg("-b")
            .arg(&self.device)
            .stdout(Redirection::Pipe)
            .stderr(Redirection::Pipe)
            .capture()?;
        if !result.success() {
            return Err(AuricError::Subprocess(format!(
                "failed to unmount {}: ``{}''",
                self.device.to_str().unwrap(),
                result.stderr_str()
            )));
        }
        Ok(())
    }
}

fn get_action() -> Result<AuricOperationMode, AuricError> {
    let mut args_iter = std::env::args();
    args_iter.next();
    if let Some(action) = args_iter.next() {
        match action.as_str() {
            "mount" => return Ok(AuricOperationMode::Mount),
            "unmount" => return Ok(AuricOperationMode::Unmount),
            _ => {
                return Err(AuricError::Invocation(format!(
                    "unknown action: ``{}''",
                    action
                )))
            }
        }
    }
    Err(AuricError::Invocation("no action given".to_string()))
}

impl AuricImpl {
    pub fn new() -> Result<AuricImpl, AuricError> {
        let sshfs_manager = SshfsManager::new()?;
        let loop_backing_device_path = sshfs_manager.loop_backing_device_path();
        Ok(AuricImpl {
            sshfs_manager: sshfs_manager,
            loop_manager: LoopManager::new(&loop_backing_device_path),
            luks_manager: LuksManager::new(),
        })
    }

    fn mount(&self) -> Result<(), AuricError> {
        self.sshfs_manager.mount()?;
        self.loop_manager.attach()?;
        let loop_device = self.loop_manager.find()?;
        self.luks_manager.unlock(&loop_device)?;
        self.luks_manager.mount()
    }

    fn unmount(&self) -> Result<(), AuricError> {
        self.luks_manager.unmount()?;
        let loop_device = self.loop_manager.find()?;
        self.luks_manager.lock(&loop_device)?;
        self.loop_manager.detach()?;
        self.sshfs_manager.unmount()
    }

    pub fn act(&self, mode: AuricOperationMode) -> Result<(), AuricError> {
        match mode {
            AuricOperationMode::Mount => return self.mount(),
            AuricOperationMode::Unmount => return self.unmount(),
        }
    }
}

fn main_impl() -> Result<(), AuricError> {
    let action = get_action()?;
    let auric_impl = AuricImpl::new()?;
    auric_impl.act(action)
}

fn main() {
    std::process::exit(match main_impl() {
        Ok(_) => 0,
        Err(e) => {
            eprintln!("{:?}", e);
            1
        }
    });
}
<commit_msg>apricot-auric: Don't demand sshfs syncs<commit_after>use regex::Regex;
use std::path::PathBuf;
use std::result::Result;
use subprocess::{Exec, Redirection};

const LOOP_BACKING_DEVICE_BASENAME: &'static str = "flaglock6-home";
const LOOP_DEVICE_PARTITION_LABEL: &'static str = "fl6";
const RSYNC_DOT_NET_REMOTE_DIR: &'static str = "usw-s007.rsync.net:./";

#[derive(Debug)]
pub enum AuricError {
    Invocation(String),
    Io(String),
    Subprocess(String),
    NotFound,
}

enum AuricOperationMode {
    Mount,
    Unmount,
}

impl From<subprocess::PopenError> for AuricError {
    fn from(err: subprocess::PopenError) -> AuricError {
        AuricError::Subprocess(err.to_string())
    }
}

struct SshfsManager {
    mountpoint: PathBuf,
}

struct LoopManager {
    target: Regex,
    backing: PathBuf,
}

struct LuksManager {
    device: PathBuf,
}

struct AuricImpl {
    sshfs_manager: SshfsManager,
    loop_manager: LoopManager,
    luks_manager: LuksManager,
}

impl SshfsManager {
    pub fn new() -> Result<SshfsManager, AuricError> {
        let xdg_runtime_dir = match std::env::var("XDG_RUNTIME_DIR") {
            Ok(val) => val,
            Err(e) => {
                return Err(AuricError::Invocation(format!(
                    "fatal error getting ${{XDG_RUNTIME_DIR}}: ``{}''",
                    e
                )))
            }
        };
        let mut mountpoint = PathBuf::new();
        mountpoint.push(xdg_runtime_dir);
        mountpoint.push("rsync.net-mountpoint");

        Ok(SshfsManager {
            mountpoint: mountpoint,
        })
    }

    fn ensure_mountpoint_is_dir(&self) -> Result<(), AuricError> {
        if self.mountpoint.is_dir() {
            return Ok(());
        }
        std::fs::create_dir(self.mountpoint.as_path())
            .map_err(|e| AuricError::Io(format!("bad mountpoint: ``{}''", e)))
    }

    // Does _not_ ensure that backing device exists.
    pub fn loop_backing_device_path(&self) -> PathBuf {
        let mut result: PathBuf = self.mountpoint.clone();
        result.push(LOOP_BACKING_DEVICE_BASENAME);
        result
    }

    fn loop_backing_device_present(&self) -> bool {
        self.loop_backing_device_path().is_file()
    }

    pub fn mount(&self) -> Result<(), AuricError> {
        self.ensure_mountpoint_is_dir()?;
        if self.loop_backing_device_present() {
            return Ok(());
        }

        // Invoke `sshfs`
        // *    with compression and
        // *    allowing other users to access the mounted filesystem
        //      (necessary to allow loop-mounts).
        let result = Exec::cmd("sshfs")
            .arg("-C")
            .arg("-o")
            .arg("allow_other")
            .arg(RSYNC_DOT_NET_REMOTE_DIR)
            .arg(&self.mountpoint)
            .stdout(Redirection::Pipe)
            .stderr(Redirection::Pipe)
            .capture()?;
        if !result.success() {
            return Err(AuricError::Subprocess(format!(
                "sshfs failed: ``{}''",
                result.stderr_str()
            )));
        }
        Ok(())
    }

    pub fn unmount(&self) -> Result<(), AuricError> {
        if !self.loop_backing_device_present() {
            return Ok(());
        }

        let result = Exec::cmd("fusermount")
            .arg("-u")
            .arg(&self.mountpoint)
            .stdout(Redirection::Pipe)
            .stderr(Redirection::Pipe)
            .capture()?;
        if !result.success() {
            return Err(AuricError::Subprocess(format!(
                "fusermount -u failed: ``{}''",
                result.stderr_str()
            )));
        }
        Ok(())
    }
}

impl LoopManager {
    pub fn new(backing: &PathBuf) -> LoopManager {
        let target = format!(
            r"^(/dev/loop\d)(\s+\d+){{4}}\s+({})\s+",
            backing.to_str().unwrap()
        );
        LoopManager {
            target: Regex::new(&target).unwrap(),
            backing: backing.clone(),
        }
    }

    pub fn find(&self) -> Result<PathBuf, AuricError> {
        let result = Exec::cmd("losetup")
            .arg("-l")
            .stdout(Redirection::Pipe)
            .stderr(Redirection::Pipe)
            .capture()?;
        if !result.success() {
            return Err(AuricError::Subprocess(format!(
                "failed to list loop devices: ``{}''",
                result.stderr_str()
            )));
        }

        for line in result.stdout_str().lines() {
            if let Some(captures) = self.target.captures(line) {
                return Ok(PathBuf::from(captures.get(1).unwrap().as_str()));
            }
        }
        Err(AuricError::NotFound)
    }

    pub fn attach(&self) -> Result<(), AuricError> {
        if let Ok(_attached_loop) = self.find() {
            return Ok(());
        }

        let result = Exec::cmd("udisksctl")
            .arg("loop-setup")
            .arg("-f")
            .arg(&self.backing)
            .stdout(Redirection::Pipe)
            .stderr(Redirection::Pipe)
            .capture()?;
        if !result.success() {
            return Err(AuricError::Subprocess(format!(
                "failed to attach loop device: ``{}''",
                result.stderr_str()
            )));
        }
        Ok(())
    }

    // Note that the loop device will linger if the volume remains
    // unlocked. Prefer not to call this before locking.
    pub fn detach(&self) -> Result<(), AuricError> {
        let attached_loop = match self.find() {
            Ok(path) => path,
            Err(e) => {
                if let AuricError::NotFound = e {
                    return Ok(());
                }
                return Err(e);
            }
        };
        let result = Exec::cmd("udisksctl")
            .arg("loop-delete")
            .arg("-b")
            .arg(&attached_loop)
            .stdout(Redirection::Pipe)
            .stderr(Redirection::Pipe)
            .capture()?;
        if !result.success() {
            return Err(AuricError::Subprocess(format!(
                "failed to detach loop device: ``{}''",
                result.stderr_str()
            )));
        }
        Ok(())
    }
}

impl LuksManager {
    pub fn new() -> LuksManager {
        let mut device = PathBuf::from("/dev/disk/by-label");
        device.push(LOOP_DEVICE_PARTITION_LABEL);
        LuksManager { device: device }
    }

    pub fn unlock(&self, locked_device: &PathBuf) -> Result<(), AuricError> {
        let result = Exec::cmd("udisksctl")
            .arg("unlock")
            .arg("-b")
            .arg(locked_device)
            .stdout(Redirection::Pipe)
            .stderr(Redirection::Pipe)
            .capture()?;
        if !result.success() {
            return Err(AuricError::Subprocess(format!(
                "failed to unlock {}: ``{}''",
                locked_device.to_str().unwrap(),
                result.stderr_str()
            )));
        }
        Ok(())
    }

    pub fn lock(&self, underlying_device: &PathBuf) -> Result<(), AuricError> {
        let result = Exec::cmd("udisksctl")
            .arg("lock")
            .arg("-b")
            .arg(underlying_device)
            .stdout(Redirection::Pipe)
            .stderr(Redirection::Pipe)
            .capture()?;
        if !result.success() {
            return Err(AuricError::Subprocess(format!(
                "failed to lock {}: ``{}''",
                underlying_device.to_str().unwrap(),
                result.stderr_str()
            )));
        }
        Ok(())
    }

    pub fn mount(&self) -> Result<(), AuricError> {
        let result = Exec::cmd("udisksctl")
            .arg("mount")
            .arg("-b")
            .arg(&self.device)
            .stdout(Redirection::Pipe)
            .stderr(Redirection::Pipe)
            .capture()?;
        if !result.success() {
            return Err(AuricError::Subprocess(format!(
                "failed to mount {}: ``{}''",
                self.device.to_str().unwrap(),
                result.stderr_str()
            )));
        }
        Ok(())
    }

    pub fn unmount(&self) -> Result<(), AuricError> {
        let result = Exec::cmd("udisksctl")
            .arg("unmount")
            .arg("-b")
            .arg(&self.device)
            .stdout(Redirection::Pipe)
            .stderr(Redirection::Pipe)
            .capture()?;
        if !result.success() {
            return Err(AuricError::Subprocess(format!(
                "failed to unmount {}: ``{}''",
                self.device.to_str().unwrap(),
                result.stderr_str()
            )));
        }
        Ok(())
    }
}

fn get_action() -> Result<AuricOperationMode, AuricError> {
    let mut args_iter = std::env::args();
    args_iter.next();
    if let Some(action) = args_iter.next() {
        match action.as_str() {
            "mount" => return Ok(AuricOperationMode::Mount),
            "unmount" => return Ok(AuricOperationMode::Unmount),
            _ => {
                return Err(AuricError::Invocation(format!(
                    "unknown action: ``{}''",
                    action
                )))
            }
        }
    }
    Err(AuricError::Invocation("no action given".to_string()))
}

impl AuricImpl {
    pub fn new() -> Result<AuricImpl, AuricError> {
        let sshfs_manager = SshfsManager::new()?;
        let loop_backing_device_path = sshfs_manager.loop_backing_device_path();
        Ok(AuricImpl {
            sshfs_manager: sshfs_manager,
            loop_manager: LoopManager::new(&loop_backing_device_path),
            luks_manager: LuksManager::new(),
        })
    }

    fn mount(&self) -> Result<(), AuricError> {
        self.sshfs_manager.mount()?;
        self.loop_manager.attach()?;
        let loop_device = self.loop_manager.find()?;
        self.luks_manager.unlock(&loop_device)?;
        self.luks_manager.mount()
    }

    fn unmount(&self) -> Result<(), AuricError> {
        self.luks_manager.unmount()?;
        let loop_device = self.loop_manager.find()?;
        self.luks_manager.lock(&loop_device)?;
        self.loop_manager.detach()?;
        self.sshfs_manager.unmount()
    }

    pub fn act(&self, mode: AuricOperationMode) -> Result<(), AuricError> {
        match mode {
            AuricOperationMode::Mount => return self.mount(),
            AuricOperationMode::Unmount => return self.unmount(),
        }
    }
}

fn main_impl() -> Result<(), AuricError> {
    let action = get_action()?;
    let auric_impl = AuricImpl::new()?;
    auric_impl.act(action)
}

fn main() {
    std::process::exit(match main_impl() {
        Ok(_) => 0,
        Err(e) => {
            eprintln!("{:?}", e);
            1
        }
    });
}
<|endoftext|>


<commit_before>// Copyright (c) Facebook, Inc. and its affiliates
// SPDX-License-Identifier: MIT OR Apache-2.0

use include_dir::include_dir as include_directory;
use serde_generate::{dart, test_utils, CodeGeneratorConfig, SourceInstaller};
use std::fs::{copy, create_dir_all, read_dir, File};
use std::{io::Result, io::Write, path::Path, process::Command};
use tempfile::tempdir;
use test_utils::{Choice, Runtime, Test};

fn install_test_dependency(path: &Path) -> Result<()> {
    Command::new("dart")
        .current_dir(path)
        .env("PUB_CACHE", "../.pub-cache")
        .args(["pub", "add", "-d", "test"])
        .status()?;

    Ok(())
}

#[test]
fn test_dart_runtime_autotest() {
    let tests = include_directory!("runtime/dart/test");
    let tempdir = tempdir().unwrap();
    let source_path = tempdir.path().join("dart_project_autotest");

    let registry = test_utils::get_simple_registry().unwrap();
    let config = CodeGeneratorConfig::new("example".to_string());

    let installer = dart::Installer::new(source_path.clone());
    installer.install_module(&config, &registry).unwrap();
    installer.install_serde_runtime().unwrap();
    installer.install_bincode_runtime().unwrap();
    installer.install_bcs_runtime().unwrap();

    create_dir_all(source_path.join("test")).unwrap();
    for f in read_dir(tests.path().join("runtime/dart/test")).unwrap() {
        let file = f.unwrap();
        copy(
            file.path().to_path_buf(),
            source_path.join("test").join(file.file_name()),
        )
        .unwrap();
    }

    install_test_dependency(&source_path).unwrap();

    let dart_test = Command::new("dart")
        .current_dir(&source_path)
        .env("PUB_CACHE", "../.pub-cache")
        .args(["test"])
        .status()
        .unwrap();

    assert!(dart_test.success());
}

#[test]
fn test_dart_bcs_runtime_on_simple_data() {
    test_dart_runtime_on_simple_data(Runtime::Bcs);
}

#[test]
fn test_dart_bincode_runtime_on_simple_data() {
    test_dart_runtime_on_simple_data(Runtime::Bincode);
}

fn test_dart_runtime_on_simple_data(runtime: Runtime) {
    let tempdir = tempdir().unwrap();
    let source_path = tempdir
        .path()
        .join(format!("dart_project_{}", runtime.name().to_lowercase()));

    let registry = test_utils::get_simple_registry().unwrap();

    let config = CodeGeneratorConfig::new("example".to_string())
        .with_encodings(vec![runtime.into()])
        .with_c_style_enums(false);

    let installer = dart::Installer::new(source_path.clone());
    installer.install_module(&config, &registry).unwrap();
    installer.install_serde_runtime().unwrap();
    installer.install_bincode_runtime().unwrap();
    installer.install_bcs_runtime().unwrap();

    create_dir_all(source_path.join("test")).unwrap();

    install_test_dependency(&source_path).unwrap();

    let mut source = File::create(source_path.join("test/runtime_test.dart")).unwrap();
    writeln!(
        source,
        r#"
import 'dart:typed_data';
import 'package:example/example.dart';
import 'package:test/test.dart';
import 'package:tuple/tuple.dart';
import '../lib/src/bcs/bcs.dart';
import '../lib/src/bincode/bincode.dart';

void main() {{"#
    )
    .unwrap();
    let reference = runtime.serialize(&Test {
        a: vec![4, 6],
        b: (-3, 5),
        c: Choice::C { x: 7 },
    });

    writeln!(
        source,
        r#"
    test('{1} serialization matches deserialization', () {{
        final expectedBytes = Uint8List.fromList([{0}]);
        Test deserializedInstance = Test.{1}Deserialize(expectedBytes);

        Test expectedInstance = Test(
            a: [4, 6],
            b: Tuple2(-3, Uint64.parse('5')),
            c: ChoiceCItem(x: 7),
        );

        expect(deserializedInstance, equals(expectedInstance));

        final serializedBytes = expectedInstance.{1}Serialize();

        expect(serializedBytes, equals(expectedBytes));
    }});"#,
        reference
            .iter()
            .map(|x| format!("{}", x))
            .collect::<Vec<_>>()
            .join(", "),
        runtime.name().to_lowercase(),
    )
    .unwrap();

    writeln!(source, "}}").unwrap();

    let dart_test = Command::new("dart")
        .current_dir(&source_path)
        .env("PUB_CACHE", "../.pub-cache")
        .args(["test", "test/runtime_test.dart"])
        .status()
        .unwrap();

    assert!(dart_test.success());
}
<commit_msg>[Dart] Use an empty registry for unit tests since they aren't testing generated code<commit_after>// Copyright (c) Facebook, Inc. and its affiliates
// SPDX-License-Identifier: MIT OR Apache-2.0

use include_dir::include_dir as include_directory;
use serde_generate::{dart, test_utils, CodeGeneratorConfig, SourceInstaller};
use serde_reflection::Registry;
use std::fs::{copy, create_dir_all, read_dir, File};
use std::{io::Result, io::Write, path::Path, process::Command};
use tempfile::tempdir;
use test_utils::{Choice, Runtime, Test};

fn install_test_dependency(path: &Path) -> Result<()> {
    Command::new("dart")
        .current_dir(path)
        .env("PUB_CACHE", "../.pub-cache")
        .args(["pub", "add", "-d", "test"])
        .status()?;

    Ok(())
}

#[test]
fn test_dart_runtime_autotest() {
    let tests = include_directory!("runtime/dart/test");
    let tempdir = tempdir().unwrap();
    let source_path = tempdir.path().join("dart_project_autotest");

    let config = CodeGeneratorConfig::new("example".to_string());

    let installer = dart::Installer::new(source_path.clone());
    installer
        .install_module(&config, &Registry::default())
        .unwrap();
    installer.install_serde_runtime().unwrap();
    installer.install_bincode_runtime().unwrap();
    installer.install_bcs_runtime().unwrap();

    create_dir_all(source_path.join("test")).unwrap();
    for f in read_dir(tests.path().join("runtime/dart/test")).unwrap() {
        let file = f.unwrap();
        copy(
            file.path().to_path_buf(),
            source_path.join("test").join(file.file_name()),
        )
        .unwrap();
    }

    install_test_dependency(&source_path).unwrap();

    let dart_test = Command::new("dart")
        .current_dir(&source_path)
        .env("PUB_CACHE", "../.pub-cache")
        .args(["test"])
        .status()
        .unwrap();

    assert!(dart_test.success());
}

#[test]
fn test_dart_bcs_runtime_on_simple_data() {
    test_dart_runtime_on_simple_data(Runtime::Bcs);
}

#[test]
fn test_dart_bincode_runtime_on_simple_data() {
    test_dart_runtime_on_simple_data(Runtime::Bincode);
}

fn test_dart_runtime_on_simple_data(runtime: Runtime) {
    let tempdir = tempdir().unwrap();
    let source_path = tempdir
        .path()
        .join(format!("dart_project_{}", runtime.name().to_lowercase()));

    let registry = test_utils::get_simple_registry().unwrap();

    let config = CodeGeneratorConfig::new("example".to_string())
        .with_encodings(vec![runtime.into()])
        .with_c_style_enums(false);

    let installer = dart::Installer::new(source_path.clone());
    installer.install_module(&config, &registry).unwrap();
    installer.install_serde_runtime().unwrap();
    installer.install_bincode_runtime().unwrap();
    installer.install_bcs_runtime().unwrap();

    create_dir_all(source_path.join("test")).unwrap();

    install_test_dependency(&source_path).unwrap();

    let mut source = File::create(source_path.join("test/runtime_test.dart")).unwrap();
    writeln!(
        source,
        r#"
import 'dart:typed_data';
import 'package:example/example.dart';
import 'package:test/test.dart';
import 'package:tuple/tuple.dart';
import '../lib/src/bcs/bcs.dart';
import '../lib/src/bincode/bincode.dart';

void main() {{"#
    )
    .unwrap();
    let reference = runtime.serialize(&Test {
        a: vec![4, 6],
        b: (-3, 5),
        c: Choice::C { x: 7 },
    });

    writeln!(
        source,
        r#"
    test('{1} serialization matches deserialization', () {{
        final expectedBytes = Uint8List.fromList([{0}]);
        Test deserializedInstance = Test.{1}Deserialize(expectedBytes);

        Test expectedInstance = Test(
            a: [4, 6],
            b: Tuple2(-3, Uint64.parse('5')),
            c: ChoiceCItem(x: 7),
        );

        expect(deserializedInstance, equals(expectedInstance));

        final serializedBytes = expectedInstance.{1}Serialize();

        expect(serializedBytes, equals(expectedBytes));
    }});"#,
        reference
            .iter()
            .map(|x| format!("{}", x))
            .collect::<Vec<_>>()
            .join(", "),
        runtime.name().to_lowercase(),
    )
    .unwrap();

    writeln!(source, "}}").unwrap();

    let dart_test = Command::new("dart")
        .current_dir(&source_path)
        .env("PUB_CACHE", "../.pub-cache")
        .args(["test", "test/runtime_test.dart"])
        .status()
        .unwrap();

    assert!(dart_test.success());
}
<|endoftext|>