<commit_before>//! Main module to handle the layout.
//! This is where the i3-specific code is.

use std::sync::{Mutex, MutexGuard, TryLockError};
use std::ptr;

use super::container::{Container, Handle, ContainerType};
use super::node::{Node};
use super::super::rustwlc::{WlcView, WlcOutput, Geometry, Point, ResizeEdge};

pub type TreeErr = TryLockError<MutexGuard<'static, Tree>>;
pub type TreeResult = Result<MutexGuard<'static, Tree>, TreeErr>;

const ERR_BAD_TREE: &'static str = "Layout tree was in an invalid configuration";

lazy_static! {
    static ref TREE: Mutex<Tree> = {
        Mutex::new(Tree{
            root: Node::new(Container::new_root()),
            active_container: ptr::null_mut(),
        })
    };
}

pub struct Tree {
    root: Node,
    active_container: *mut Node,
}

unsafe impl Send for Tree {}

impl Tree {

    /// Moves the current active container to a new workspace
    pub fn move_container_to_workspace(&mut self, name: &str) {
        let container: Option<Node> = None;
        if let Some(sub_container) = self.get_active_container() {
            // NOTE Assumes workspace exists, fix this
            // NOTE Should not do this, because floating windows
            // Should do an if let
            let container = Some(sub_container.remove_from_parent()
                .expect("Could not remove container, was not part of tree"));
        }
        if let Some(container) = container {
            if let Some(workspace) = self.get_workspace_by_name(name) {
                // Assume workspace has a container
                let output = &mut workspace.get_children_mut()[0];
                output.new_child(container.get_val().clone());
            }
        }
    }

    /// Switch to the workspace with the give name
    pub fn switch_workspace(&mut self, name: &str ) {
        trace!("Switching to workspace {}", name);
        if let Some(old_workspace) = self.get_active_workspace() {
            old_workspace.set_visibility(false);
        }
        if self.active_container.is_null() {
            warn!("Not current active container, cannot switch workspace");
            return;
        }
        let current_workspace: *mut Node;
        {
            if let Some(_) = self.get_workspace_by_name(name) {
                trace!("Found workspace {}", name);
            } else {
                trace!("Adding workspace {}", name);
                self.add_workspace(name.to_string());
            }
            let new_current_workspace = self.get_workspace_by_name_mut(name).expect(ERR_BAD_TREE);
            new_current_workspace.set_visibility(true);
            // Set the first view to be focused, so the screen refreshes itself
            if new_current_workspace.get_children()[0].get_children().len() > 0 {
                trace!("Focusing view");
                match new_current_workspace.get_children_mut()[0].get_children_mut()[0]
                    .get_val().get_handle().expect(ERR_BAD_TREE) {
                    Handle::View(view) => view.focus(),
                    _ => {},
                }
            } else {
                WlcView::root().focus();
            }
            // Update the tree's pointer to the currently focused container
            current_workspace = &mut new_current_workspace.get_children_mut()[0] as *mut Node;
        }
        self.active_container = current_workspace;
    }

    /// Returns the currently viewed container.
    /// If multiple views are selected, the parent container they share is returned
    pub fn get_active_container(&self) -> Option<&mut Node> {
        if self.active_container.is_null() {
            None
        } else {
            unsafe {
                Some(&mut *self.active_container)
            }
        }
    }

    /// Get the monitor (output) that the active container is located on
    pub fn get_active_output(&self) -> Option<&mut Node> {
        if let Some(node) = self.get_active_container() {
            node.get_ancestor_of_type(ContainerType::Output)
        } else {
            None
        }
    }

    /// Get the workspace that the active container is located on
    pub fn get_active_workspace(&self) -> Option<&mut Node> {
        if let Some(container) = self.get_active_container() {
            if let Some(workspace) = container.get_ancestor_of_type(ContainerType::Workspace) {
                return Some(workspace)
            }
        }
        return None
    }

    /// Find the workspace node that has the given name
    pub fn get_workspace_by_name(&mut self, name: &str) -> Option<&mut Node> {
        for child in self.root.get_children_mut()[0].get_children_mut() {
            if child.get_val().get_name().expect(ERR_BAD_TREE) != name {
                continue
            }
            return Some(child);
        }
        return None
    }

    /// Find the workspace node that has the given name, with a mutable reference
    pub fn get_workspace_by_name_mut(&mut self, name: &str) -> Option<&mut Node> {
        for child in self.root.get_children_mut()[0].get_children_mut() {
            if child.get_val().get_name().expect(ERR_BAD_TREE) != name {
                continue
            }
            return Some(child);
        }
        return None
    }

    /// Make a new output container with the given WlcOutput.
    /// This is done when a new monitor is added
    pub fn add_output(&mut self, wlc_output: WlcOutput) {
        match self.root.new_child(Container::new_output(wlc_output)) {
            Ok(_) => {},
            Err(e) => error!("Could not add output: {:?}", e),
        }
    }

    /// Make a new workspace container with the given name.
    pub fn add_workspace(&mut self, name: String) {
        if let Some(output) = self.get_active_output() {
            let size = output.get_val().get_geometry()
                .expect("Output did not have a geometry").size;
            let workspace = Container::new_workspace(name.to_string(),
                                                     size.clone());
            match output.new_child(workspace) {
                Ok(workspace) => {
                    trace!("Added workspace {:?}", workspace);
                    let geometry = Geometry {
                        size: size,
                        origin: Point { x: 0, y: 0}
                    };
                    if let Err(e) = workspace.new_child(Container::new_container(geometry)) {
                        error!("Could not add container to workspace: {:?}",e );
                    }
                },
                Err(e) => error!("Could not add workspace: {:?}", e),
            }
        }
    }

    /// Make a new view container with the given WlcView, and adds it to
    /// the active workspace.
    pub fn add_view(&mut self, wlc_view: WlcView) {
        let mut maybe_new_view: *const Node = ptr::null();
        if let Some(current_workspace) = self.get_active_workspace() {
            trace!("Adding view {:?} to {:?}", wlc_view, current_workspace);
            if current_workspace.get_children().len() == 0 {
                let output = self.get_active_output()
                    .expect("Could not get active output");
                let output_size = output.get_val().get_geometry()
                    .expect("Could not get geometry from output").size;
                let mut geometry = wlc_view.get_geometry()
                    .expect("Could not get geometry from wlc view").clone();
                // Ensure that it starts within the bounds
                // Width
                if geometry.origin.x > output_size.w as i32 {
                    geometry.origin.x = output_size.w as i32 ;
                } else if geometry.origin.x < 0 {
                    geometry.origin.x = 0;
                }
                // Height
                if geometry.origin.y > output_size.h as i32 {
                    geometry.origin.y = output_size.h as i32;
                } else if geometry.origin.y < 0 {
                    geometry.origin.y = 0;
                }
                wlc_view.set_geometry(ResizeEdge::empty(), &geometry);
                match current_workspace.new_child(Container::new_container(geometry)) {
                    Ok(_) => {},
                    Err(e) => {
                        error!("Could not add workspace: {:?}", e);
                        return;
                    }
                }
            }
            let container = &mut current_workspace.get_children_mut()[0];
            match container.new_child(Container::new_view(wlc_view)) {
                Ok(view_node) => maybe_new_view = view_node as *const Node,
                Err(e) => {
                    error!("Could not add view to current workspace: {:?}", e);
                    return;
                }
            }
        };
        if ! maybe_new_view.is_null() {
            self.active_container = maybe_new_view as *mut Node;
        }
    }

    /// Remove the view container with the given view
    pub fn remove_view(&self, wlc_view: &WlcView) {
        if let Some(view) = self.root.find_view_by_handle(&wlc_view) {
            let parent = view.get_parent().expect(ERR_BAD_TREE);
            parent.remove_child(view);
        }
    }
}

pub fn try_lock_tree() -> TreeResult {
    trace!("Locking the tree!");
    TREE.try_lock()
}
<commit_msg>Moved tree declarations around, *mut -> *const<commit_after>//! Main module to handle the layout.
//! This is where the i3-specific code is.

use std::sync::{Mutex, MutexGuard, TryLockError};
use std::ptr;

use super::container::{Container, Handle, ContainerType};
use super::node::{Node};
use super::super::rustwlc::{WlcView, WlcOutput, Geometry, Point, ResizeEdge};

pub type TreeErr = TryLockError<MutexGuard<'static, Tree>>;
pub type TreeResult = Result<MutexGuard<'static, Tree>, TreeErr>;

const ERR_BAD_TREE: &'static str = "Layout tree was in an invalid configuration";

pub struct Tree {
    root: Node,
    active_container: *const Node,
}

unsafe impl Send for Tree {}

lazy_static! {
    static ref TREE: Mutex<Tree> = {
        Mutex::new(Tree{
            root: Node::new(Container::new_root()),
            active_container: ptr::null(),
        })
    };
}

impl Tree {

    /// Moves the current active container to a new workspace
    pub fn move_container_to_workspace(&mut self, name: &str) {
        let container: Option<Node> = None;
        if let Some(sub_container) = self.get_active_container() {
            // NOTE Assumes workspace exists, fix this
            // NOTE Should not do this, because floating windows
            // Should do an if let
            // TODO Get the mutable version of that container so this compiles

            /*let container = Some(sub_container.remove_from_parent()
                .expect("Could not remove container, was not part of tree"));
            */
        }
        if let Some(container) = container {
            if let Some(workspace) = self.get_workspace_by_name(name) {
                // Assume workspace has a container
                let output = &mut workspace.get_children_mut()[0];
                output.new_child(container.get_val().clone());
            }
        }
    }

    /// Switch to the workspace with the give name
    pub fn switch_workspace(&mut self, name: &str ) {
        trace!("Switching to workspace {}", name);
        if let Some(old_workspace) = self.get_active_workspace() {
            old_workspace.set_visibility(false);
        }
        if self.active_container.is_null() {
            warn!("Not current active container, cannot switch workspace");
            return;
        }
        let current_workspace: *const Node;
        {
            if let Some(_) = self.get_workspace_by_name(name) {
                trace!("Found workspace {}", name);
            } else {
                trace!("Adding workspace {}", name);
                self.add_workspace(name.to_string());
            }
            let new_current_workspace = self.get_workspace_by_name_mut(name).expect(ERR_BAD_TREE);
            new_current_workspace.set_visibility(true);
            // Set the first view to be focused, so the screen refreshes itself
            if new_current_workspace.get_children()[0].get_children().len() > 0 {
                trace!("Focusing view");
                match new_current_workspace.get_children_mut()[0].get_children_mut()[0]
                    .get_val().get_handle().expect(ERR_BAD_TREE) {
                    Handle::View(view) => view.focus(),
                    _ => {},
                }
            } else {
                WlcView::root().focus();
            }
            // Update the tree's pointer to the currently focused container
            current_workspace = &mut new_current_workspace.get_children_mut()[0] as *const Node;
        }
        self.active_container = current_workspace;
    }

    /// Returns the currently viewed container.
    /// If multiple views are selected, the parent container they share is returned
    pub fn get_active_container(&self) -> Option<&Node> {
        if self.active_container.is_null() {
            None
        } else {
            unsafe {
                Some(&*self.active_container)
            }
        }
    }

    /// Get the monitor (output) that the active container is located on
    pub fn get_active_output(&self) -> Option<&mut Node> {
        if let Some(node) = self.get_active_container() {
            node.get_ancestor_of_type(ContainerType::Output)
        } else {
            None
        }
    }

    /// Get the workspace that the active container is located on
    pub fn get_active_workspace(&self) -> Option<&mut Node> {
        if let Some(container) = self.get_active_container() {
            if let Some(workspace) = container.get_ancestor_of_type(ContainerType::Workspace) {
                return Some(workspace)
            }
        }
        return None
    }

    /// Find the workspace node that has the given name
    pub fn get_workspace_by_name(&mut self, name: &str) -> Option<&mut Node> {
        for child in self.root.get_children_mut()[0].get_children_mut() {
            if child.get_val().get_name().expect(ERR_BAD_TREE) != name {
                continue
            }
            return Some(child);
        }
        return None
    }

    /// Find the workspace node that has the given name, with a mutable reference
    pub fn get_workspace_by_name_mut(&mut self, name: &str) -> Option<&mut Node> {
        for child in self.root.get_children_mut()[0].get_children_mut() {
            if child.get_val().get_name().expect(ERR_BAD_TREE) != name {
                continue
            }
            return Some(child);
        }
        return None
    }

    /// Make a new output container with the given WlcOutput.
    /// This is done when a new monitor is added
    pub fn add_output(&mut self, wlc_output: WlcOutput) {
        match self.root.new_child(Container::new_output(wlc_output)) {
            Ok(_) => {},
            Err(e) => error!("Could not add output: {:?}", e),
        }
    }

    /// Make a new workspace container with the given name.
    pub fn add_workspace(&mut self, name: String) {
        if let Some(output) = self.get_active_output() {
            let size = output.get_val().get_geometry()
                .expect("Output did not have a geometry").size;
            let workspace = Container::new_workspace(name.to_string(),
                                                     size.clone());
            match output.new_child(workspace) {
                Ok(workspace) => {
                    trace!("Added workspace {:?}", workspace);
                    let geometry = Geometry {
                        size: size,
                        origin: Point { x: 0, y: 0}
                    };
                    if let Err(e) = workspace.new_child(Container::new_container(geometry)) {
                        error!("Could not add container to workspace: {:?}",e );
                    }
                },
                Err(e) => error!("Could not add workspace: {:?}", e),
            }
        }
    }

    /// Make a new view container with the given WlcView, and adds it to
    /// the active workspace.
    pub fn add_view(&mut self, wlc_view: WlcView) {
        let mut maybe_new_view: *const Node = ptr::null();
        if let Some(current_workspace) = self.get_active_workspace() {
            trace!("Adding view {:?} to {:?}", wlc_view, current_workspace);
            if current_workspace.get_children().len() == 0 {
                let output = self.get_active_output()
                    .expect("Could not get active output");
                let output_size = output.get_val().get_geometry()
                    .expect("Could not get geometry from output").size;
                let mut geometry = wlc_view.get_geometry()
                    .expect("Could not get geometry from wlc view").clone();
                // Ensure that it starts within the bounds
                // Width
                if geometry.origin.x > output_size.w as i32 {
                    geometry.origin.x = output_size.w as i32 ;
                } else if geometry.origin.x < 0 {
                    geometry.origin.x = 0;
                }
                // Height
                if geometry.origin.y > output_size.h as i32 {
                    geometry.origin.y = output_size.h as i32;
                } else if geometry.origin.y < 0 {
                    geometry.origin.y = 0;
                }
                wlc_view.set_geometry(ResizeEdge::empty(), &geometry);
                match current_workspace.new_child(Container::new_container(geometry)) {
                    Ok(_) => {},
                    Err(e) => {
                        error!("Could not add workspace: {:?}", e);
                        return;
                    }
                }
            }
            let container = &mut current_workspace.get_children_mut()[0];
            match container.new_child(Container::new_view(wlc_view)) {
                Ok(view_node) => maybe_new_view = view_node as *const Node,
                Err(e) => {
                    error!("Could not add view to current workspace: {:?}", e);
                    return;
                }
            }
        };
        if ! maybe_new_view.is_null() {
            self.active_container = maybe_new_view as *const Node;
        }
    }

    /// Remove the view container with the given view
    pub fn remove_view(&self, wlc_view: &WlcView) {
        if let Some(view) = self.root.find_view_by_handle(&wlc_view) {
            let parent = view.get_parent().expect(ERR_BAD_TREE);
            parent.remove_child(view);
        }
    }
}

pub fn try_lock_tree() -> TreeResult {
    trace!("Locking the tree!");
    TREE.try_lock()
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::fs::File;
use std::ops::Drop;
use std::path::PathBuf;
use std::result::Result as RResult;
use std::sync::Arc;
use std::sync::{RwLock, Mutex};

use fs2::FileExt;

use entry::Entry;
use error::StoreError;

/// The Result Type returned by any interaction with the store that could fail
pub type Result<T> = RResult<T, StoreError>;

/// The Index into the Store
pub type StoreId = PathBuf;

/// This Trait allows you to convert various representations to a single one
/// suitable for usage in the Store
trait IntoStoreId {
    fn into_storeid(self) -> StoreId;
}

impl<'a> IntoStoreId for &'a str {
    fn into_storeid(self) -> StoreId {
        PathBuf::from(self)
    }
}

impl<'a> IntoStoreId for &'a String{
    fn into_storeid(self) -> StoreId {
        PathBuf::from(self)
    }
}

impl IntoStoreId for String{
    fn into_storeid(self) -> StoreId {
        PathBuf::from(self)
    }
}

impl IntoStoreId for PathBuf {
    fn into_storeid(self) -> StoreId {
        self
    }
}

impl<'a> IntoStoreId for &'a PathBuf {
    fn into_storeid(self) -> StoreId {
        self.clone()
    }
}

impl<ISI: IntoStoreId> IntoStoreId for (ISI, ISI) {
    fn into_storeid(self) -> StoreId {
        let (first, second) = self;
        let mut res : StoreId = first.into_storeid();
        res.push(second.into_storeid());
        res
    }
}

#[derive(PartialEq)]
enum StoreEntryPresence {
    Present,
    Borrowed
}

/// A store entry, depending on the option type it is either borrowed currently
/// or not.
struct StoreEntry {
    file: File,
    entry: StoreEntryPresence
}

impl StoreEntry {
    /// The entry is currently borrowed, meaning that some thread is currently
    /// mutating it
    fn is_borrowed(&self) -> bool {
        self.entry == StoreEntryPresence::Borrowed
    }

    /// Flush the entry to disk
    fn set_entry(&mut self, entry: Entry) -> Result<()> {
        unimplemented!()
    }

    /// We borrow the entry
    fn get_entry(&mut self) -> Result<Entry> {
        unimplemented!()
    }

    /// We copy the entry
    fn copy_entry(&mut self) -> Result<Entry> {
        unimplemented!()
    }
}

/// The Store itself, through this object one can interact with IMAG's entries
pub struct Store {
    location: PathBuf,

    /**
     * Internal Path->File cache map
     *
     * Caches the files, so they remain flock()ed
     *
     * Could be optimized for a threadsafe HashMap
     */
    entries: Arc<RwLock<HashMap<StoreId, StoreEntry>>>,
}

impl Store {
    /// Creates the Entry at the given location (inside the entry)
    pub fn create(&self, entry: Entry) -> Result<()> {
        unimplemented!();
    }

    /// Borrow a given Entry. When the `FileLockEntry` is either `update`d or
    /// dropped, the new Entry is written to disk
    pub fn retrieve<'a>(&'a self, id: StoreId) -> Result<FileLockEntry<'a>> {
        unimplemented!();
    }

    /// Return the `FileLockEntry` and write to disk
    pub fn update<'a>(&'a self, entry: FileLockEntry<'a>) -> Result<()> {
        self._update(&entry)
    }

    /// Internal method to write to the filesystem store.
    ///
    /// # Assumptions
    /// This method assumes that entry is dropped _right after_ the call, hence
    /// it is not public.
    fn _update<'a>(&'a self, entry: &FileLockEntry<'a>) -> Result<()> {
        unimplemented!();
    }

    /// Retrieve a copy of a given entry, this cannot be used to mutate
    /// the one on disk
    pub fn retrieve_copy(&self, id: StoreId) -> Result<Entry> {
        unimplemented!();
    }

    /// Delete an entry
    pub fn delete(&self, id: StoreId) -> Result<()> {
        unimplemented!();
    }
}

impl Drop for Store {

    /**
     * Unlock all files on drop
     *
     * TODO: Error message when file cannot be unlocked?
     */
    fn drop(&mut self) {
        self.entries.write().unwrap()
            .iter().map(|f| f.1.file.unlock());
    }

}

/// A struct that allows you to borrow an Entry
pub struct FileLockEntry<'a> {
    store: &'a Store,
    entry: Entry,
    key: StoreId,
}

impl<'a> FileLockEntry<'a, > {
    fn new(store: &'a Store, entry: Entry, key: StoreId) -> FileLockEntry<'a> {
        FileLockEntry {
            store: store,
            entry: entry,
            key: key,
        }
    }
}

impl<'a> ::std::ops::Deref for FileLockEntry<'a> {
    type Target = Entry;

    fn deref(&self) -> &Self::Target {
        &self.entry
    }
}

impl<'a> ::std::ops::DerefMut for FileLockEntry<'a> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.entry
    }
}

impl<'a> Drop for FileLockEntry<'a> {
    fn drop(&mut self) {
        self.store._update(self).unwrap()
    }
}

#[cfg(test)]
mod test {
    use std::path::PathBuf;
    use store::{StoreId, IntoStoreId};

    #[test]
    fn into_storeid_trait() {
        let buf = PathBuf::from("abc/def");

        let test = ("abc", "def");
        assert_eq!(buf, test.into_storeid());

        let test = "abc/def";
        assert_eq!(buf, test.into_storeid());

        let test = String::from("abc/def");
        assert_eq!(buf, test.into_storeid());

        let test = PathBuf::from("abc/def");
        assert_eq!(buf, test.into_storeid());
    }
}

<commit_msg>Auto merge of #107 - matthiasbeyer:libimagstore/add-store_new, r=matthiasbeyer<commit_after>use std::collections::HashMap;
use std::fs::File;
use std::ops::Drop;
use std::path::PathBuf;
use std::result::Result as RResult;
use std::sync::Arc;
use std::sync::{RwLock, Mutex};

use fs2::FileExt;

use entry::Entry;
use error::StoreError;

/// The Result Type returned by any interaction with the store that could fail
pub type Result<T> = RResult<T, StoreError>;

/// The Index into the Store
pub type StoreId = PathBuf;

/// This Trait allows you to convert various representations to a single one
/// suitable for usage in the Store
trait IntoStoreId {
    fn into_storeid(self) -> StoreId;
}

impl<'a> IntoStoreId for &'a str {
    fn into_storeid(self) -> StoreId {
        PathBuf::from(self)
    }
}

impl<'a> IntoStoreId for &'a String{
    fn into_storeid(self) -> StoreId {
        PathBuf::from(self)
    }
}

impl IntoStoreId for String{
    fn into_storeid(self) -> StoreId {
        PathBuf::from(self)
    }
}

impl IntoStoreId for PathBuf {
    fn into_storeid(self) -> StoreId {
        self
    }
}

impl<'a> IntoStoreId for &'a PathBuf {
    fn into_storeid(self) -> StoreId {
        self.clone()
    }
}

impl<ISI: IntoStoreId> IntoStoreId for (ISI, ISI) {
    fn into_storeid(self) -> StoreId {
        let (first, second) = self;
        let mut res : StoreId = first.into_storeid();
        res.push(second.into_storeid());
        res
    }
}

#[derive(PartialEq)]
enum StoreEntryPresence {
    Present,
    Borrowed
}

/// A store entry, depending on the option type it is either borrowed currently
/// or not.
struct StoreEntry {
    file: File,
    entry: StoreEntryPresence
}

impl StoreEntry {
    /// The entry is currently borrowed, meaning that some thread is currently
    /// mutating it
    fn is_borrowed(&self) -> bool {
        self.entry == StoreEntryPresence::Borrowed
    }

    /// Flush the entry to disk
    fn set_entry(&mut self, entry: Entry) -> Result<()> {
        unimplemented!()
    }

    /// We borrow the entry
    fn get_entry(&mut self) -> Result<Entry> {
        unimplemented!()
    }

    /// We copy the entry
    fn copy_entry(&mut self) -> Result<Entry> {
        unimplemented!()
    }
}

/// The Store itself, through this object one can interact with IMAG's entries
pub struct Store {
    location: PathBuf,

    /**
     * Internal Path->File cache map
     *
     * Caches the files, so they remain flock()ed
     *
     * Could be optimized for a threadsafe HashMap
     */
    entries: Arc<RwLock<HashMap<StoreId, StoreEntry>>>,
}

impl Store {

    /// Create a new Store object
    pub fn new(location: PathBuf) -> Store {
        Store {
            location: location,
            entries: Arc::new(RwLock::new(HashMap::new())),
        }
    }

    /// Creates the Entry at the given location (inside the entry)
    pub fn create(&self, entry: Entry) -> Result<()> {
        unimplemented!();
    }

    /// Borrow a given Entry. When the `FileLockEntry` is either `update`d or
    /// dropped, the new Entry is written to disk
    pub fn retrieve<'a>(&'a self, id: StoreId) -> Result<FileLockEntry<'a>> {
        unimplemented!();
    }

    /// Return the `FileLockEntry` and write to disk
    pub fn update<'a>(&'a self, entry: FileLockEntry<'a>) -> Result<()> {
        self._update(&entry)
    }

    /// Internal method to write to the filesystem store.
    ///
    /// # Assumptions
    /// This method assumes that entry is dropped _right after_ the call, hence
    /// it is not public.
    fn _update<'a>(&'a self, entry: &FileLockEntry<'a>) -> Result<()> {
        unimplemented!();
    }

    /// Retrieve a copy of a given entry, this cannot be used to mutate
    /// the one on disk
    pub fn retrieve_copy(&self, id: StoreId) -> Result<Entry> {
        unimplemented!();
    }

    /// Delete an entry
    pub fn delete(&self, id: StoreId) -> Result<()> {
        unimplemented!();
    }
}

impl Drop for Store {

    /**
     * Unlock all files on drop
     *
     * TODO: Error message when file cannot be unlocked?
     */
    fn drop(&mut self) {
        self.entries.write().unwrap()
            .iter().map(|f| f.1.file.unlock());
    }

}

/// A struct that allows you to borrow an Entry
pub struct FileLockEntry<'a> {
    store: &'a Store,
    entry: Entry,
    key: StoreId,
}

impl<'a> FileLockEntry<'a, > {
    fn new(store: &'a Store, entry: Entry, key: StoreId) -> FileLockEntry<'a> {
        FileLockEntry {
            store: store,
            entry: entry,
            key: key,
        }
    }
}

impl<'a> ::std::ops::Deref for FileLockEntry<'a> {
    type Target = Entry;

    fn deref(&self) -> &Self::Target {
        &self.entry
    }
}

impl<'a> ::std::ops::DerefMut for FileLockEntry<'a> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.entry
    }
}

impl<'a> Drop for FileLockEntry<'a> {
    fn drop(&mut self) {
        self.store._update(self).unwrap()
    }
}

#[cfg(test)]
mod test {
    use std::path::PathBuf;
    use store::{StoreId, IntoStoreId};

    #[test]
    fn into_storeid_trait() {
        let buf = PathBuf::from("abc/def");

        let test = ("abc", "def");
        assert_eq!(buf, test.into_storeid());

        let test = "abc/def";
        assert_eq!(buf, test.into_storeid());

        let test = String::from("abc/def");
        assert_eq!(buf, test.into_storeid());

        let test = PathBuf::from("abc/def");
        assert_eq!(buf, test.into_storeid());
    }
}

<|endoftext|>


<commit_before>use chrono::naive::time::NaiveTime as ChronoNaiveTime;

use parse::Parse;

pub struct Time {
    hour:   u32,
    minute: u32,
    second: u32,
}

impl Time {

    pub fn new(hour: u32, minute: u32, second: u32) -> Time {
        Time {
            hour: hour,
            minute: minute,
            second: second
        }
    }

    pub fn hour(&self) -> u32 {
        self.hour
    }

    pub fn minute(&self) -> u32 {
        self.minute
    }

    pub fn second(&self) -> u32 {
        self.second
    }

}

impl Into<ChronoNaiveTime> for Time {

    fn into(self) -> ChronoNaiveTime {
        ChronoNaiveTime::from_hms(self.hour, self.minute, self.second)
    }

}

impl Parse for Time {

    fn parse(s: &str) -> Option<Time> {
        use std::str::FromStr;
        use regex::Regex;
        use parse::time_parse_regex;

        lazy_static! {
            static ref R: Regex = Regex::new(time_parse_regex()).unwrap();
        }

        R.captures(s)
            .and_then(|capts| {
                let hour   = capts.name("h").and_then(|o| FromStr::from_str(o).ok());
                let minute = capts.name("m").and_then(|o| FromStr::from_str(o).ok()).unwrap_or(0);
                let second = capts.name("s").and_then(|o| FromStr::from_str(o).ok()).unwrap_or(0);

                if hour.is_none() {
                    debug!("No hour");
                    return None;
                }

                Some(Time::new(hour.unwrap(), minute, second))
            })
    }

}

#[cfg(test)]
mod test {
    use super::Time;
    use parse::Parse;

    #[test]
    fn test_valid() {
        let s = "2016-12-12T20:01:02";
        let t = Time::parse(s);

        assert!(t.is_some());
        let t = t.unwrap();

        assert_eq!(20, t.hour());
        assert_eq!(1, t.minute());
        assert_eq!(2, t.second());
    }

    #[test]
    fn test_valid_without_sec() {
        let s = "2016-12-12T20:01";
        let t = Time::parse(s);

        assert!(t.is_some());
        let t = t.unwrap();

        assert_eq!(20, t.hour());
        assert_eq!(1, t.minute());
        assert_eq!(0, t.second());
    }

    #[test]
    fn test_valid_without_min() {
        let s = "2016-12-12T20";
        let t = Time::parse(s);

        assert!(t.is_some());
        let t = t.unwrap();

        assert_eq!(20, t.hour());
        assert_eq!(0, t.minute());
        assert_eq!(0, t.second());
    }

    #[test]
    fn test_invalid() {
        assert!(Time::parse("2015-12-12T").is_none());
        assert!(Time::parse("2015-12-12T200").is_none());
        assert!(Time::parse("2015-12-12T20-20").is_none());
        assert!(Time::parse("2015-12-12T20:200").is_none());
        assert!(Time::parse("2015-12-12T20:20:200").is_none());
        assert!(Time::parse("2015-12-12T20:20:").is_none());
        assert!(Time::parse("2015-12-12T20:").is_none());
        assert!(Time::parse("2015-12-12T2:20:21").is_none());
        assert!(Time::parse("2015-12-12T2:2:20").is_none());
        assert!(Time::parse("2015-12-12T2:2:2").is_none());
    }

}

<commit_msg>Replace unwrap() by match<commit_after>use chrono::naive::time::NaiveTime as ChronoNaiveTime;

use parse::Parse;

pub struct Time {
    hour:   u32,
    minute: u32,
    second: u32,
}

impl Time {

    pub fn new(hour: u32, minute: u32, second: u32) -> Time {
        Time {
            hour: hour,
            minute: minute,
            second: second
        }
    }

    pub fn hour(&self) -> u32 {
        self.hour
    }

    pub fn minute(&self) -> u32 {
        self.minute
    }

    pub fn second(&self) -> u32 {
        self.second
    }

}

impl Into<ChronoNaiveTime> for Time {

    fn into(self) -> ChronoNaiveTime {
        ChronoNaiveTime::from_hms(self.hour, self.minute, self.second)
    }

}

impl Parse for Time {

    fn parse(s: &str) -> Option<Time> {
        use std::str::FromStr;
        use regex::Regex;
        use parse::time_parse_regex;

        lazy_static! {
            static ref R: Regex = Regex::new(time_parse_regex()).unwrap();
        }

        R.captures(s)
            .and_then(|capts| {
                let minute = capts.name("m").and_then(|o| FromStr::from_str(o).ok()).unwrap_or(0);
                let second = capts.name("s").and_then(|o| FromStr::from_str(o).ok()).unwrap_or(0);
                let hour   = match capts.name("h").and_then(|o| FromStr::from_str(o).ok()) {
                    None => {
                        debug!("No hour");
                        return None;
                    },
                    Some(x) => x,
                };

                Some(Time::new(hour, minute, second))
            })
    }

}

#[cfg(test)]
mod test {
    use super::Time;
    use parse::Parse;

    #[test]
    fn test_valid() {
        let s = "2016-12-12T20:01:02";
        let t = Time::parse(s);

        assert!(t.is_some());
        let t = t.unwrap();

        assert_eq!(20, t.hour());
        assert_eq!(1, t.minute());
        assert_eq!(2, t.second());
    }

    #[test]
    fn test_valid_without_sec() {
        let s = "2016-12-12T20:01";
        let t = Time::parse(s);

        assert!(t.is_some());
        let t = t.unwrap();

        assert_eq!(20, t.hour());
        assert_eq!(1, t.minute());
        assert_eq!(0, t.second());
    }

    #[test]
    fn test_valid_without_min() {
        let s = "2016-12-12T20";
        let t = Time::parse(s);

        assert!(t.is_some());
        let t = t.unwrap();

        assert_eq!(20, t.hour());
        assert_eq!(0, t.minute());
        assert_eq!(0, t.second());
    }

    #[test]
    fn test_invalid() {
        assert!(Time::parse("2015-12-12T").is_none());
        assert!(Time::parse("2015-12-12T200").is_none());
        assert!(Time::parse("2015-12-12T20-20").is_none());
        assert!(Time::parse("2015-12-12T20:200").is_none());
        assert!(Time::parse("2015-12-12T20:20:200").is_none());
        assert!(Time::parse("2015-12-12T20:20:").is_none());
        assert!(Time::parse("2015-12-12T20:").is_none());
        assert!(Time::parse("2015-12-12T2:20:21").is_none());
        assert!(Time::parse("2015-12-12T2:2:20").is_none());
        assert!(Time::parse("2015-12-12T2:2:2").is_none());
    }

}

<|endoftext|>


<commit_before>use std::cell::RefCell;
use std::collections::HashMap;
use std::fs;
use std::fs::{File, OpenOptions};
use std::io;
use std::io::Write;
use std::path::PathBuf;
use std::rc::Rc;
use time::Tm;

use libtiny_ui::*;

#[macro_use]
extern crate log;

#[derive(Clone)]
pub struct Logger {
    inner: Rc<RefCell<LoggerInner>>,
}

#[derive(Debug)]
pub enum LoggerInitError {
    CouldNotCreateDir { dir_path: PathBuf, err: io::Error },
}

impl Logger {
    pub fn new(
        log_dir: PathBuf,
        report_err: Box<dyn Fn(String)>,
    ) -> Result<Logger, LoggerInitError> {
        Ok(Logger {
            inner: Rc::new(RefCell::new(LoggerInner::new(log_dir, report_err)?)),
        })
    }
}

macro_rules! delegate {
    ( $name:ident ( $( $x:ident: $t:ty, )* ) ) => {
        fn $name(&self, $($x: $t,)*) {
            self.inner.borrow_mut().$name( $( $x, )* )
        }
    }
}

impl UI for Logger {
    fn draw(&self) {}
    delegate!(new_server_tab(serv: &str, alias: Option<String>,));
    delegate!(close_server_tab(serv: &str,));
    delegate!(new_chan_tab(serv: &str, chan: &str,));
    delegate!(close_chan_tab(serv: &str, chan: &str,));
    delegate!(close_user_tab(serv: &str, nick: &str,));
    delegate!(add_client_msg(msg: &str, target: &MsgTarget,));
    delegate!(add_msg(msg: &str, ts: Tm, target: &MsgTarget,));
    delegate!(add_err_msg(msg: &str, ts: Tm, target: &MsgTarget,));
    delegate!(add_client_err_msg(msg: &str, target: &MsgTarget,));
    delegate!(clear_nicks(serv: &str,));
    delegate!(set_nick(serv: &str, nick: &str,));
    delegate!(add_privmsg(
        sender: &str,
        msg: &str,
        ts: Tm,
        target: &MsgTarget,
        highlight: bool,
        is_action: bool,
    ));
    delegate!(add_nick(nick: &str, ts: Option<Tm>, target: &MsgTarget,));
    delegate!(remove_nick(nick: &str, ts: Option<Tm>, target: &MsgTarget,));
    delegate!(rename_nick(
        old_nick: &str,
        new_nick: &str,
        ts: Tm,
        target: &MsgTarget,
    ));
    delegate!(set_topic(topic: &str, ts: Tm, serv: &str, chan: &str,));
    delegate!(set_tab_style(style: TabStyle, target: &MsgTarget,));

    // TODO: Maybe just return true?
    fn user_tab_exists(&self, _serv: &str, _nick: &str) -> bool {
        false
    }
}

struct LoggerInner {
    /// Log file directory
    log_dir: PathBuf,

    /// Maps server names to their fds
    servers: HashMap<String, ServerLogs>,

    /// Callback used when reporting errors
    report_err: Box<dyn Fn(String)>,
}

struct ServerLogs {
    fd: File,
    chans: HashMap<String, File>,
    users: HashMap<String, File>,
}

fn print_header(fd: &mut File) -> io::Result<()> {
    writeln!(fd)?;
    writeln!(
        fd,
        "*** Logging started at {}",
        time::strftime("%Y-%m-%d %H:%M:%S", &time::now()).unwrap()
    )?;
    writeln!(fd)
}

macro_rules! report_io_err {
    ( $f:expr, $e:expr ) => {
        match $e {
            Err(err) => {
                debug!("{:?}", err);
                $f(format!("{:?}", err));
                return;
            }
            Ok(ok) => ok,
        }
    };
}

// '/' is valid in channel names but we can't use it in file names, so we replace it with '-'.
// According to RFC 2812 nick names can't contain '/', but we still use this in nicks just to be
// safe. Other special characters mentioned in the RFC ("[]\`^{|}") can be used in file names so we
// don't replace those.
fn replace_forward_slash(path: &str) -> String {
    path.replace('/', "-")
}

fn try_open_log_file(path: &PathBuf, report_err: &dyn Fn(String)) -> Option<File> {
    match OpenOptions::new().create(true).append(true).open(path) {
        Ok(fd) => Some(fd),
        Err(err) => {
            report_err(format!("Couldn't open file {:?}: {}", path, err));
            None
        }
    }
}

impl LoggerInner {
    fn new(
        log_dir: PathBuf,
        report_err: Box<dyn Fn(String)>,
    ) -> Result<LoggerInner, LoggerInitError> {
        if let Err(err) = fs::create_dir_all(&log_dir) {
            if err.kind() != io::ErrorKind::AlreadyExists {
                return Err(LoggerInitError::CouldNotCreateDir {
                    dir_path: log_dir,
                    err,
                });
            }
        }

        Ok(LoggerInner {
            log_dir,
            servers: HashMap::new(),
            report_err,
        })
    }

    fn new_server_tab(&mut self, serv: &str, _alias: Option<String>) {
        let mut path = self.log_dir.clone();
        path.push(&format!("{}.txt", serv));
        debug!("Trying to open log file: {:?}", path);
        if let Some(mut fd) = try_open_log_file(&path, &*self.report_err) {
            report_io_err!(self.report_err, print_header(&mut fd));
            self.servers.insert(
                serv.to_string(),
                ServerLogs {
                    fd,
                    chans: HashMap::new(),
                    users: HashMap::new(),
                },
            );
        }
    }

    fn close_server_tab(&mut self, serv: &str) {
        self.servers.remove(serv);
    }

    fn new_chan_tab(&mut self, serv: &str, chan: &str) {
        match self.servers.get_mut(serv) {
            None => {
                debug!("new_chan_tab: can't find server: {}", serv);
            }
            Some(server) => {
                let mut path = self.log_dir.clone();
                path.push(&format!("{}_{}.txt", serv, replace_forward_slash(chan)));
                debug!("Trying to open log file: {:?}", path);
                if let Some(mut fd) = try_open_log_file(&path, &*self.report_err) {
                    report_io_err!(self.report_err, print_header(&mut fd));
                    server.chans.insert(chan.to_string(), fd);
                }
            }
        }
    }

    fn close_chan_tab(&mut self, serv: &str, chan: &str) {
        match self.servers.get_mut(serv) {
            None => {
                debug!("close_chan_tab: can't find server: {}", serv);
            }
            Some(server) => {
                server.chans.remove(chan);
            }
        }
    }

    fn close_user_tab(&mut self, serv: &str, nick: &str) {
        match self.servers.get_mut(serv) {
            None => {
                debug!("close_user_tab: can't find server: {}", serv);
            }
            Some(server) => {
                server.users.remove(nick);
            }
        }
    }

    fn add_client_msg(&mut self, msg: &str, target: &MsgTarget) {
        let now = now();
        self.apply_to_target(target, |fd: &mut File, report_err: &dyn Fn(String)| {
            report_io_err!(report_err, writeln!(fd, "[{}] [client] {}", now, msg));
        });
    }

    fn add_msg(&mut self, msg: &str, ts: Tm, target: &MsgTarget) {
        self.apply_to_target(target, |fd: &mut File, report_err: &dyn Fn(String)| {
            report_io_err!(report_err, writeln!(fd, "[{}] {}", strf(&ts), msg));
        });
    }

    fn add_err_msg(&self, _msg: &str, _ts: Tm, _target: &MsgTarget) {
        // IRC error messages are ignored
    }

    fn add_client_err_msg(&self, _msg: &str, _target: &MsgTarget) {
        // Ditto with client error messages
    }

    fn clear_nicks(&self, _serv: &str) {
        // Nothing to do here
    }

    fn set_nick(&self, _serv: &str, _nick: &str) {
        // Ditto
    }

    fn add_privmsg(
        &mut self,
        sender: &str,
        msg: &str,
        ts: Tm,
        target: &MsgTarget,
        _highlight: bool,
        is_action: bool,
    ) {
        self.apply_to_target(target, |fd: &mut File, report_err: &dyn Fn(String)| {
            let io_ret = if is_action {
                writeln!(fd, "[{}] {} {}", strf(&ts), sender, msg)
            } else {
                writeln!(fd, "[{}] {}: {}", strf(&ts), sender, msg)
            };
            report_io_err!(report_err, io_ret);
        });
    }

    fn add_nick(&mut self, nick: &str, ts: Option<Tm>, target: &MsgTarget) {
        if let Some(_ts) = ts {
            // This method is only called when a user joins a chan
            self.apply_to_target(target, |fd: &mut File, report_err: &dyn Fn(String)| {
                report_io_err!(
                    report_err,
                    writeln!(fd, "[{}] {} joined the channel.", now(), nick)
                );
            });
        }
    }

    fn remove_nick(&mut self, nick: &str, ts: Option<Tm>, target: &MsgTarget) {
        if let Some(_ts) = ts {
            // TODO: Did the user leave a channel or the server? Currently we can't tell.
            self.apply_to_target(target, |fd: &mut File, report_err: &dyn Fn(String)| {
                report_io_err!(report_err, writeln!(fd, "[{}] {} left.", now(), nick));
            });
        }
    }

    fn rename_nick(&mut self, old_nick: &str, new_nick: &str, ts: Tm, target: &MsgTarget) {
        self.apply_to_target(target, |fd: &mut File, report_err: &dyn Fn(String)| {
            report_io_err!(
                report_err,
                writeln!(
                    fd,
                    "[{}] {} is now known as {}.",
                    strf(&ts),
                    old_nick,
                    new_nick
                )
            );
        });
    }

    fn set_topic(&mut self, topic: &str, ts: Tm, serv: &str, chan: &str) {
        let target = MsgTarget::Chan { serv, chan };
        self.apply_to_target(&target, |fd: &mut File, report_err: &dyn Fn(String)| {
            report_io_err!(
                report_err,
                writeln!(fd, "[{}] Channel topic: {}.", strf(&ts), topic)
            );
        });
    }

    fn set_tab_style(&self, _: TabStyle, _: &MsgTarget) {
        // Nothing to do here
    }

    fn apply_to_target(&mut self, target: &MsgTarget, f: impl Fn(&mut File, &dyn Fn(String))) {
        match *target {
            MsgTarget::Server { serv } => match self.servers.get_mut(serv) {
                None => {
                    debug!("Can't find server: {}", serv);
                }
                Some(ServerLogs { ref mut fd, .. }) => {
                    f(fd, &*self.report_err);
                }
            },
            MsgTarget::Chan { serv, chan } => match self.servers.get_mut(serv) {
                None => {
                    debug!("Can't find server: {}", serv);
                }
                Some(ServerLogs { ref mut chans, .. }) => match chans.get_mut(chan) {
                    None => {
                        debug!("Can't find chan {} in server {}", chan, serv);
                    }
                    Some(fd) => {
                        f(fd, &*self.report_err);
                    }
                },
            },
            MsgTarget::User { serv, nick } => {
                match self.servers.get_mut(serv) {
                    None => {
                        debug!("Can't find server: {}", serv);
                    }
                    Some(ServerLogs { ref mut users, .. }) => {
                        match users.get_mut(nick) {
                            Some(fd) => {
                                f(fd, &*self.report_err);
                            }
                            None => {
                                // We don't have a `new_user_tab` trait method so user log files
                                // are created here
                                let mut path = self.log_dir.clone();
                                path.push(&format!("{}_{}.txt", serv, replace_forward_slash(nick)));
                                debug!("Trying to open log file: {:?}", path);
                                if let Some(mut fd) = try_open_log_file(&path, &*self.report_err) {
                                    report_io_err!(self.report_err, print_header(&mut fd));
                                    f(&mut fd, &*self.report_err);
                                    users.insert(nick.to_owned(), fd);
                                }
                            }
                        }
                    }
                }
            }
            MsgTarget::AllServTabs { serv } => match self.servers.get_mut(serv) {
                None => {
                    debug!("Can't find server: {}", serv);
                }
                Some(ServerLogs {
                    ref mut fd,
                    ref mut chans,
                    ref mut users,
                    ..
                }) => {
                    f(fd, &*self.report_err);
                    for (_, fd) in chans.iter_mut() {
                        f(fd, &*self.report_err);
                    }
                    for (_, fd) in users.iter_mut() {
                        f(fd, &*self.report_err);
                    }
                }
            },
            MsgTarget::CurrentTab => {
                // Probably a cmd error; these are ignored
            }
        }
    }
}

fn now() -> String {
    time::strftime("%H:%M:%S", &time::now()).unwrap()
}

fn strf(tm: &Tm) -> String {
    time::strftime("%H:%M:%S", tm).unwrap()
}
<commit_msg>libtiny_logger: Create channel file on first message if it doesn't exist<commit_after>use std::cell::RefCell;
use std::collections::HashMap;
use std::fs;
use std::fs::{File, OpenOptions};
use std::io;
use std::io::Write;
use std::path::PathBuf;
use std::rc::Rc;
use time::Tm;

use libtiny_ui::*;

#[macro_use]
extern crate log;

#[derive(Clone)]
pub struct Logger {
    inner: Rc<RefCell<LoggerInner>>,
}

#[derive(Debug)]
pub enum LoggerInitError {
    CouldNotCreateDir { dir_path: PathBuf, err: io::Error },
}

impl Logger {
    pub fn new(
        log_dir: PathBuf,
        report_err: Box<dyn Fn(String)>,
    ) -> Result<Logger, LoggerInitError> {
        Ok(Logger {
            inner: Rc::new(RefCell::new(LoggerInner::new(log_dir, report_err)?)),
        })
    }
}

macro_rules! delegate {
    ( $name:ident ( $( $x:ident: $t:ty, )* ) ) => {
        fn $name(&self, $($x: $t,)*) {
            self.inner.borrow_mut().$name( $( $x, )* )
        }
    }
}

impl UI for Logger {
    fn draw(&self) {}
    delegate!(new_server_tab(serv: &str, alias: Option<String>,));
    delegate!(close_server_tab(serv: &str,));
    delegate!(new_chan_tab(serv: &str, chan: &str,));
    delegate!(close_chan_tab(serv: &str, chan: &str,));
    delegate!(close_user_tab(serv: &str, nick: &str,));
    delegate!(add_client_msg(msg: &str, target: &MsgTarget,));
    delegate!(add_msg(msg: &str, ts: Tm, target: &MsgTarget,));
    delegate!(add_err_msg(msg: &str, ts: Tm, target: &MsgTarget,));
    delegate!(add_client_err_msg(msg: &str, target: &MsgTarget,));
    delegate!(clear_nicks(serv: &str,));
    delegate!(set_nick(serv: &str, nick: &str,));
    delegate!(add_privmsg(
        sender: &str,
        msg: &str,
        ts: Tm,
        target: &MsgTarget,
        highlight: bool,
        is_action: bool,
    ));
    delegate!(add_nick(nick: &str, ts: Option<Tm>, target: &MsgTarget,));
    delegate!(remove_nick(nick: &str, ts: Option<Tm>, target: &MsgTarget,));
    delegate!(rename_nick(
        old_nick: &str,
        new_nick: &str,
        ts: Tm,
        target: &MsgTarget,
    ));
    delegate!(set_topic(topic: &str, ts: Tm, serv: &str, chan: &str,));
    delegate!(set_tab_style(style: TabStyle, target: &MsgTarget,));

    // TODO: Maybe just return true?
    fn user_tab_exists(&self, _serv: &str, _nick: &str) -> bool {
        false
    }
}

struct LoggerInner {
    /// Log file directory
    log_dir: PathBuf,

    /// Maps server names to their fds
    servers: HashMap<String, ServerLogs>,

    /// Callback used when reporting errors
    report_err: Box<dyn Fn(String)>,
}

struct ServerLogs {
    fd: File,
    chans: HashMap<String, File>,
    users: HashMap<String, File>,
}

fn print_header(fd: &mut File) -> io::Result<()> {
    writeln!(fd)?;
    writeln!(
        fd,
        "*** Logging started at {}",
        time::strftime("%Y-%m-%d %H:%M:%S", &time::now()).unwrap()
    )?;
    writeln!(fd)
}

macro_rules! report_io_err {
    ( $f:expr, $e:expr ) => {
        match $e {
            Err(err) => {
                debug!("{:?}", err);
                $f(format!("{:?}", err));
                return;
            }
            Ok(ok) => ok,
        }
    };
}

// '/' is valid in channel names but we can't use it in file names, so we replace it with '-'.
// According to RFC 2812 nick names can't contain '/', but we still use this in nicks just to be
// safe. Other special characters mentioned in the RFC ("[]\`^{|}") can be used in file names so we
// don't replace those.
fn replace_forward_slash(path: &str) -> String {
    path.replace('/', "-")
}

fn try_open_log_file(path: &PathBuf, report_err: &dyn Fn(String)) -> Option<File> {
    match OpenOptions::new().create(true).append(true).open(path) {
        Ok(fd) => Some(fd),
        Err(err) => {
            report_err(format!("Couldn't open file {:?}: {}", path, err));
            None
        }
    }
}

impl LoggerInner {
    fn new(
        log_dir: PathBuf,
        report_err: Box<dyn Fn(String)>,
    ) -> Result<LoggerInner, LoggerInitError> {
        if let Err(err) = fs::create_dir_all(&log_dir) {
            if err.kind() != io::ErrorKind::AlreadyExists {
                return Err(LoggerInitError::CouldNotCreateDir {
                    dir_path: log_dir,
                    err,
                });
            }
        }

        Ok(LoggerInner {
            log_dir,
            servers: HashMap::new(),
            report_err,
        })
    }

    fn new_server_tab(&mut self, serv: &str, _alias: Option<String>) {
        let mut path = self.log_dir.clone();
        path.push(&format!("{}.txt", serv));
        debug!("Trying to open log file: {:?}", path);
        if let Some(mut fd) = try_open_log_file(&path, &*self.report_err) {
            report_io_err!(self.report_err, print_header(&mut fd));
            self.servers.insert(
                serv.to_string(),
                ServerLogs {
                    fd,
                    chans: HashMap::new(),
                    users: HashMap::new(),
                },
            );
        }
    }

    fn close_server_tab(&mut self, serv: &str) {
        self.servers.remove(serv);
    }

    fn new_chan_tab(&mut self, serv: &str, chan: &str) {
        match self.servers.get_mut(serv) {
            None => {
                debug!("new_chan_tab: can't find server: {}", serv);
            }
            Some(server) => {
                let mut path = self.log_dir.clone();
                path.push(&format!("{}_{}.txt", serv, replace_forward_slash(chan)));
                debug!("Trying to open log file: {:?}", path);
                if let Some(mut fd) = try_open_log_file(&path, &*self.report_err) {
                    report_io_err!(self.report_err, print_header(&mut fd));
                    server.chans.insert(chan.to_string(), fd);
                }
            }
        }
    }

    fn close_chan_tab(&mut self, serv: &str, chan: &str) {
        match self.servers.get_mut(serv) {
            None => {
                debug!("close_chan_tab: can't find server: {}", serv);
            }
            Some(server) => {
                server.chans.remove(chan);
            }
        }
    }

    fn close_user_tab(&mut self, serv: &str, nick: &str) {
        match self.servers.get_mut(serv) {
            None => {
                debug!("close_user_tab: can't find server: {}", serv);
            }
            Some(server) => {
                server.users.remove(nick);
            }
        }
    }

    fn add_client_msg(&mut self, msg: &str, target: &MsgTarget) {
        let now = now();
        self.apply_to_target(target, |fd: &mut File, report_err: &dyn Fn(String)| {
            report_io_err!(report_err, writeln!(fd, "[{}] [client] {}", now, msg));
        });
    }

    fn add_msg(&mut self, msg: &str, ts: Tm, target: &MsgTarget) {
        self.apply_to_target(target, |fd: &mut File, report_err: &dyn Fn(String)| {
            report_io_err!(report_err, writeln!(fd, "[{}] {}", strf(&ts), msg));
        });
    }

    fn add_err_msg(&self, _msg: &str, _ts: Tm, _target: &MsgTarget) {
        // IRC error messages are ignored
    }

    fn add_client_err_msg(&self, _msg: &str, _target: &MsgTarget) {
        // Ditto with client error messages
    }

    fn clear_nicks(&self, _serv: &str) {
        // Nothing to do here
    }

    fn set_nick(&self, _serv: &str, _nick: &str) {
        // Ditto
    }

    fn add_privmsg(
        &mut self,
        sender: &str,
        msg: &str,
        ts: Tm,
        target: &MsgTarget,
        _highlight: bool,
        is_action: bool,
    ) {
        self.apply_to_target(target, |fd: &mut File, report_err: &dyn Fn(String)| {
            let io_ret = if is_action {
                writeln!(fd, "[{}] {} {}", strf(&ts), sender, msg)
            } else {
                writeln!(fd, "[{}] {}: {}", strf(&ts), sender, msg)
            };
            report_io_err!(report_err, io_ret);
        });
    }

    fn add_nick(&mut self, nick: &str, ts: Option<Tm>, target: &MsgTarget) {
        if let Some(_ts) = ts {
            // This method is only called when a user joins a chan
            self.apply_to_target(target, |fd: &mut File, report_err: &dyn Fn(String)| {
                report_io_err!(
                    report_err,
                    writeln!(fd, "[{}] {} joined the channel.", now(), nick)
                );
            });
        }
    }

    fn remove_nick(&mut self, nick: &str, ts: Option<Tm>, target: &MsgTarget) {
        if let Some(_ts) = ts {
            // TODO: Did the user leave a channel or the server? Currently we can't tell.
            self.apply_to_target(target, |fd: &mut File, report_err: &dyn Fn(String)| {
                report_io_err!(report_err, writeln!(fd, "[{}] {} left.", now(), nick));
            });
        }
    }

    fn rename_nick(&mut self, old_nick: &str, new_nick: &str, ts: Tm, target: &MsgTarget) {
        self.apply_to_target(target, |fd: &mut File, report_err: &dyn Fn(String)| {
            report_io_err!(
                report_err,
                writeln!(
                    fd,
                    "[{}] {} is now known as {}.",
                    strf(&ts),
                    old_nick,
                    new_nick
                )
            );
        });
    }

    fn set_topic(&mut self, topic: &str, ts: Tm, serv: &str, chan: &str) {
        let target = MsgTarget::Chan { serv, chan };
        self.apply_to_target(&target, |fd: &mut File, report_err: &dyn Fn(String)| {
            report_io_err!(
                report_err,
                writeln!(fd, "[{}] Channel topic: {}.", strf(&ts), topic)
            );
        });
    }

    fn set_tab_style(&self, _: TabStyle, _: &MsgTarget) {
        // Nothing to do here
    }

    fn apply_to_target(&mut self, target: &MsgTarget, f: impl Fn(&mut File, &dyn Fn(String))) {
        match *target {
            MsgTarget::Server { serv } => match self.servers.get_mut(serv) {
                None => {
                    debug!("Can't find server: {}", serv);
                }
                Some(ServerLogs { ref mut fd, .. }) => {
                    f(fd, &*self.report_err);
                }
            },
            MsgTarget::Chan { serv, chan } => match self.servers.get_mut(serv) {
                None => {
                    debug!("Can't find server: {}", serv);
                }
                Some(ServerLogs { ref mut chans, .. }) => match chans.get_mut(chan) {
                    None => {
                        // Create a file for the channel. FIXME Code copied from new_chan_tab:
                        // can't reuse it because of borrowchk issues.
                        let mut path = self.log_dir.clone();
                        path.push(&format!("{}_{}.txt", serv, replace_forward_slash(chan)));
                        debug!("Trying to open log file: {:?}", path);
                        if let Some(mut fd) = try_open_log_file(&path, &*self.report_err) {
                            report_io_err!(self.report_err, print_header(&mut fd));
                            f(&mut fd, &*self.report_err);
                            chans.insert(chan.to_string(), fd);
                        }
                    }
                    Some(fd) => {
                        f(fd, &*self.report_err);
                    }
                },
            },
            MsgTarget::User { serv, nick } => {
                match self.servers.get_mut(serv) {
                    None => {
                        debug!("Can't find server: {}", serv);
                    }
                    Some(ServerLogs { ref mut users, .. }) => {
                        match users.get_mut(nick) {
                            Some(fd) => {
                                f(fd, &*self.report_err);
                            }
                            None => {
                                // We don't have a `new_user_tab` trait method so user log files
                                // are created here
                                let mut path = self.log_dir.clone();
                                path.push(&format!("{}_{}.txt", serv, replace_forward_slash(nick)));
                                debug!("Trying to open log file: {:?}", path);
                                if let Some(mut fd) = try_open_log_file(&path, &*self.report_err) {
                                    report_io_err!(self.report_err, print_header(&mut fd));
                                    f(&mut fd, &*self.report_err);
                                    users.insert(nick.to_owned(), fd);
                                }
                            }
                        }
                    }
                }
            }
            MsgTarget::AllServTabs { serv } => match self.servers.get_mut(serv) {
                None => {
                    debug!("Can't find server: {}", serv);
                }
                Some(ServerLogs {
                    ref mut fd,
                    ref mut chans,
                    ref mut users,
                    ..
                }) => {
                    f(fd, &*self.report_err);
                    for (_, fd) in chans.iter_mut() {
                        f(fd, &*self.report_err);
                    }
                    for (_, fd) in users.iter_mut() {
                        f(fd, &*self.report_err);
                    }
                }
            },
            MsgTarget::CurrentTab => {
                // Probably a cmd error; these are ignored
            }
        }
    }
}

fn now() -> String {
    time::strftime("%H:%M:%S", &time::now()).unwrap()
}

fn strf(tm: &Tm) -> String {
    time::strftime("%H:%M:%S", tm).unwrap()
}
<|endoftext|>


<commit_before>// CITA
// Copyright 2016-2017 Cryptape Technologies LLC.

// This program is free software: you can redistribute it
// and/or modify it under the terms of the GNU General Public
// License as published by the Free Software Foundation,
// either version 3 of the License, or (at your option) any
// later version.

// This program is distributed in the hope that it will be
// useful, but WITHOUT ANY WARRANTY; without even the implied
// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE. See the GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

extern crate tx_pool;

use error::ErrorCode;
use libproto::{submodules, topics, factory, communication, Response, TxResponse, Request, BatchRequest, verify_tx_nonce};
use libproto::blockchain::{BlockBody, SignedTransaction, BlockTxs, AccountGasLimit};
use protobuf::{Message, RepeatedField};
use serde_json;

use std::cell::RefCell;
use std::collections::HashSet;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::mpsc::Sender;
use std::thread;
use std::time::SystemTime;
use txwal::Txwal;
use util::H256;
use uuid::Uuid;

pub struct Dispatchtx {
    txs_pool: RefCell<tx_pool::Pool>,
    wal: Txwal,
    filter_wal: Txwal,
    pool_limit: usize,
    data_from_pool: AtomicBool,
    batch_forward_info: BatchForwardInfo,
    response_jsonrpc_cnt: u64,
    start_verify_time: SystemTime,
    add_to_pool_cnt:  u64,
}

pub struct BatchForwardInfo {
    count_per_batch: usize,
    buffer_duration: u32, //in unit of ns
    forward_stamp: SystemTime,
    new_tx_request_buffer: Vec<Request>,
}

impl Dispatchtx {
    pub fn new(package_limit: usize, limit: usize, count_per_batch: usize, buffer_duration: u32) -> Self {
        let batch_forward_info = BatchForwardInfo {
            count_per_batch: count_per_batch,
            buffer_duration: buffer_duration,
            forward_stamp: SystemTime::now(),
            new_tx_request_buffer: Vec::new(),
        };

        let mut dispatch = Dispatchtx {
            txs_pool: RefCell::new(tx_pool::Pool::new(package_limit)),
            wal: Txwal::new("/txwal"),
            filter_wal: Txwal::new("/filterwal"),
            pool_limit: limit,
            data_from_pool: AtomicBool::new(false),
            batch_forward_info: batch_forward_info,
            response_jsonrpc_cnt: 0,
            start_verify_time: SystemTime::now(),
            add_to_pool_cnt: 0,
        };

        let num = dispatch.read_tx_from_wal();
        if num > 0 {
            dispatch.data_from_pool.store(true, Ordering::SeqCst);
        }
        dispatch
    }

    pub fn deal_tx(&mut self, modid: u32, req_id: Vec<u8>, tx_response: TxResponse, tx: &SignedTransaction, mq_pub: Sender<(String, Vec<u8>)>) {
        let mut error_msg: Option<String> = None;
        if !verify_tx_nonce(tx) {
            error_msg = Some(String::from("InvalidNonce"));
        } else if self.tx_flow_control() {
            error_msg = Some(String::from("Busy"));
        } else if !self.add_tx_to_pool(&tx) {
            error_msg = Some(String::from("Dup"));
        }

        if modid == submodules::JSON_RPC {
            let mut response = Response::new();
            response.set_request_id(req_id);

            if error_msg.is_some() {
                response.set_code(ErrorCode::tx_auth_error());
                response.set_error_msg(error_msg.unwrap());
            } else {
                let tx_state = serde_json::to_string(&tx_response).unwrap();
                response.set_tx_state(tx_state);

                let request_id = Uuid::new_v4().as_bytes().to_vec();
                trace!("send auth.tx with request_id {:?}", request_id);
                let mut request = Request::new();
                request.set_un_tx(tx.get_transaction_with_sig().clone());
                request.set_request_id(request_id);
                self.batch_forward_info.new_tx_request_buffer.push(request);

                let count_buffered = self.batch_forward_info.new_tx_request_buffer.len();
                let time_elapsed = self.batch_forward_info.forward_stamp.elapsed().unwrap().subsec_nanos();

                if count_buffered > self.batch_forward_info.count_per_batch || time_elapsed > self.batch_forward_info.buffer_duration {
                    trace!("Going to send new tx batch to peer auth with {} new tx and buffer {} ns", count_buffered, time_elapsed);

                    self.batch_forward_tx_to_peer(mq_pub.clone());
                }
            }

            let msg = factory::create_msg(submodules::AUTH, topics::RESPONSE, communication::MsgType::RESPONSE, response.write_to_bytes().unwrap());
            self.response_jsonrpc_cnt += 1;
            trace!("response new tx {:?}, with response_jsonrpc_cnt = {}", response, self.response_jsonrpc_cnt);
            mq_pub.send(("auth.rpc".to_string(), msg.write_to_bytes().unwrap())).unwrap();
        }
        if 0 == self.add_to_pool_cnt {
            self.start_verify_time = SystemTime::now();
        }
        self.add_to_pool_cnt += 1;
    }

    pub fn deal_txs(&mut self, height: usize, txs: &HashSet<H256>, mq_pub: Sender<(String, Vec<u8>)>, block_gas_limit: u64, account_gas_limit: AccountGasLimit) {
        let mut block_txs = BlockTxs::new();
        let mut body = BlockBody::new();

        trace!("deal_txs inner txs height {} ", txs.len());
        if !txs.is_empty() {
            self.del_txs_from_pool_with_hash(txs);
        }

        let out_txs = self.get_txs_from_pool(height as u64, block_gas_limit, account_gas_limit);
        info!("public block txs height {} with {:?} txs on timestamp: {:?}", height, out_txs.len(), SystemTime::now());
        {
            let time_duration_in_sec = self.start_verify_time.elapsed().unwrap().as_secs();
            if 0 != time_duration_in_sec {
                info!("{} txs have been added into tx_pool, and time cost is {:?}, tps: {:?}", self.add_to_pool_cnt, self.start_verify_time.elapsed().unwrap(), self.add_to_pool_cnt / time_duration_in_sec);
            }
            self.add_to_pool_cnt = 0;
        }

        if !out_txs.is_empty() {
            body.set_transactions(RepeatedField::from_vec(out_txs));
        }
        block_txs.set_height(height as u64);
        block_txs.set_body(body);
        trace!("deal_txs send height {}", height);
        let msg = factory::create_msg(submodules::AUTH, topics::BLOCK_TXS, communication::MsgType::BLOCK_TXS, block_txs.write_to_bytes().unwrap());
        mq_pub.send(("auth.block_txs".to_string(), msg.write_to_bytes().unwrap())).unwrap();
    }

    pub fn wait_timeout_process(&mut self, mq_pub: Sender<(String, Vec<u8>)>) {
        let time_elapsed = self.batch_forward_info.forward_stamp.elapsed().unwrap().subsec_nanos();
        let count_buffered = self.batch_forward_info.new_tx_request_buffer.len();
        if self.batch_forward_info.new_tx_request_buffer.len() > 0 {
            trace!("wait_timeout_process is going to send new tx batch to peer auth with {} new tx and buffer {} ns", count_buffered, time_elapsed);
            self.batch_forward_tx_to_peer(mq_pub);
        }
    }

    pub fn add_tx_to_pool(&self, tx: &SignedTransaction) -> bool {
        //交易放入pool，
        //放入pool完成后，持久化
        let ref mut txs_pool = self.txs_pool.borrow_mut();
        let success = txs_pool.enqueue(tx.clone());
        if success {
            self.wal.write(&tx);
        } else {
            self.filter_wal.write(&tx);
        }
        success
    }

    pub fn get_txs_from_pool(&self, height: u64, block_gas_limit: u64, account_gas_limit: AccountGasLimit) -> Vec<SignedTransaction> {
        if self.data_from_pool.load(Ordering::SeqCst) {
            self.data_from_pool.store(false, Ordering::SeqCst);
            Vec::new()
        } else {
            let ref mut txs_pool = self.txs_pool.borrow_mut();
            let txs = txs_pool.package(height, block_gas_limit, account_gas_limit);
            txs
        }
    }

    pub fn tx_flow_control(&self) -> bool {
        if self.pool_limit == 0 {
            return false;
        }
        let txs_pool = self.txs_pool.borrow();
        if txs_pool.len() > self.pool_limit { true } else { false }
    }

    pub fn del_txs_from_pool_with_hash(&self, txs: &HashSet<H256>) {
        //收到删除通知，从pool中删除vec中的交易
        {
            self.txs_pool.borrow_mut().update_with_hash(txs);
        }
        //改成多线程删除数据
        let mut wal = self.wal.clone();
        let txs = txs.clone();
        thread::spawn(move || for tx in txs {
                          wal.delete_with_hash(&tx);
                      });
    }

    pub fn del_txs_from_pool(&self, txs: Vec<SignedTransaction>) {
        //收到删除通知，从pool中删除vec中的交易
        {
            self.txs_pool.borrow_mut().update(&txs);
        }
        //改成多线程删除数据
        let mut wal = self.wal.clone();
        thread::spawn(move || for tx in txs {
                          wal.delete(&tx);
                      });
    }

    pub fn read_tx_from_wal(&mut self) -> u64 {
        self.wal.read(&mut self.txs_pool.borrow_mut())
    }

    fn batch_forward_tx_to_peer(&mut self, mq_pub: Sender<(String, Vec<u8>)>) {
        trace!("batch_forward_tx_to_peer is going to send {} new tx to peer", self.batch_forward_info.new_tx_request_buffer.len());
        let mut batch_request = BatchRequest::new();
        batch_request.set_new_tx_requests(RepeatedField::from_slice(&self.batch_forward_info.new_tx_request_buffer[..]));

        let request_id = Uuid::new_v4().as_bytes().to_vec();
        let mut request = Request::new();
        request.set_batch_req(batch_request);
        request.set_request_id(request_id);

        let msg = factory::create_msg(submodules::AUTH, topics::REQUEST, communication::MsgType::REQUEST, request.write_to_bytes().unwrap());
        mq_pub.send(("auth.tx".to_string(), msg.write_to_bytes().unwrap())).unwrap();

        self.batch_forward_info.forward_stamp = SystemTime::now();
        self.batch_forward_info.new_tx_request_buffer.clear();
    }
}
<commit_msg>enhancement the precision<commit_after>// CITA
// Copyright 2016-2017 Cryptape Technologies LLC.

// This program is free software: you can redistribute it
// and/or modify it under the terms of the GNU General Public
// License as published by the Free Software Foundation,
// either version 3 of the License, or (at your option) any
// later version.

// This program is distributed in the hope that it will be
// useful, but WITHOUT ANY WARRANTY; without even the implied
// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE. See the GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

extern crate tx_pool;

use error::ErrorCode;
use libproto::{submodules, topics, factory, communication, Response, TxResponse, Request, BatchRequest, verify_tx_nonce};
use libproto::blockchain::{BlockBody, SignedTransaction, BlockTxs, AccountGasLimit};
use protobuf::{Message, RepeatedField};
use serde_json;

use std::cell::RefCell;
use std::collections::HashSet;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::mpsc::Sender;
use std::thread;
use std::time::SystemTime;
use txwal::Txwal;
use util::H256;
use uuid::Uuid;

pub struct Dispatchtx {
    txs_pool: RefCell<tx_pool::Pool>,
    wal: Txwal,
    filter_wal: Txwal,
    pool_limit: usize,
    data_from_pool: AtomicBool,
    batch_forward_info: BatchForwardInfo,
    response_jsonrpc_cnt: u64,
    start_verify_time: SystemTime,
    add_to_pool_cnt:  u64,
}

pub struct BatchForwardInfo {
    count_per_batch: usize,
    buffer_duration: u32, //in unit of ns
    forward_stamp: SystemTime,
    new_tx_request_buffer: Vec<Request>,
}

impl Dispatchtx {
    pub fn new(package_limit: usize, limit: usize, count_per_batch: usize, buffer_duration: u32) -> Self {
        let batch_forward_info = BatchForwardInfo {
            count_per_batch: count_per_batch,
            buffer_duration: buffer_duration,
            forward_stamp: SystemTime::now(),
            new_tx_request_buffer: Vec::new(),
        };

        let mut dispatch = Dispatchtx {
            txs_pool: RefCell::new(tx_pool::Pool::new(package_limit)),
            wal: Txwal::new("/txwal"),
            filter_wal: Txwal::new("/filterwal"),
            pool_limit: limit,
            data_from_pool: AtomicBool::new(false),
            batch_forward_info: batch_forward_info,
            response_jsonrpc_cnt: 0,
            start_verify_time: SystemTime::now(),
            add_to_pool_cnt: 0,
        };

        let num = dispatch.read_tx_from_wal();
        if num > 0 {
            dispatch.data_from_pool.store(true, Ordering::SeqCst);
        }
        dispatch
    }

    pub fn deal_tx(&mut self, modid: u32, req_id: Vec<u8>, tx_response: TxResponse, tx: &SignedTransaction, mq_pub: Sender<(String, Vec<u8>)>) {
        let mut error_msg: Option<String> = None;
        if !verify_tx_nonce(tx) {
            error_msg = Some(String::from("InvalidNonce"));
        } else if self.tx_flow_control() {
            error_msg = Some(String::from("Busy"));
        } else if !self.add_tx_to_pool(&tx) {
            error_msg = Some(String::from("Dup"));
        }

        if modid == submodules::JSON_RPC {
            let mut response = Response::new();
            response.set_request_id(req_id);

            if error_msg.is_some() {
                response.set_code(ErrorCode::tx_auth_error());
                response.set_error_msg(error_msg.unwrap());
            } else {
                let tx_state = serde_json::to_string(&tx_response).unwrap();
                response.set_tx_state(tx_state);

                let request_id = Uuid::new_v4().as_bytes().to_vec();
                trace!("send auth.tx with request_id {:?}", request_id);
                let mut request = Request::new();
                request.set_un_tx(tx.get_transaction_with_sig().clone());
                request.set_request_id(request_id);
                self.batch_forward_info.new_tx_request_buffer.push(request);

                let count_buffered = self.batch_forward_info.new_tx_request_buffer.len();
                let time_elapsed = self.batch_forward_info.forward_stamp.elapsed().unwrap().subsec_nanos();

                if count_buffered > self.batch_forward_info.count_per_batch || time_elapsed > self.batch_forward_info.buffer_duration {
                    trace!("Going to send new tx batch to peer auth with {} new tx and buffer {} ns", count_buffered, time_elapsed);

                    self.batch_forward_tx_to_peer(mq_pub.clone());
                }
            }

            let msg = factory::create_msg(submodules::AUTH, topics::RESPONSE, communication::MsgType::RESPONSE, response.write_to_bytes().unwrap());
            self.response_jsonrpc_cnt += 1;
            trace!("response new tx {:?}, with response_jsonrpc_cnt = {}", response, self.response_jsonrpc_cnt);
            mq_pub.send(("auth.rpc".to_string(), msg.write_to_bytes().unwrap())).unwrap();
        }
        if 0 == self.add_to_pool_cnt {
            self.start_verify_time = SystemTime::now();
        }
        self.add_to_pool_cnt += 1;
    }

    pub fn deal_txs(&mut self, height: usize, txs: &HashSet<H256>, mq_pub: Sender<(String, Vec<u8>)>, block_gas_limit: u64, account_gas_limit: AccountGasLimit) {
        let mut block_txs = BlockTxs::new();
        let mut body = BlockBody::new();

        trace!("deal_txs inner txs height {} ", txs.len());
        if !txs.is_empty() {
            self.del_txs_from_pool_with_hash(txs);
        }

        let out_txs = self.get_txs_from_pool(height as u64, block_gas_limit, account_gas_limit);
        info!("public block txs height {} with {:?} txs on timestamp: {:?}", height, out_txs.len(), SystemTime::now());
        {
            let duration = self.start_verify_time.elapsed().unwrap();
            let time_duration_in_usec = duration.as_secs() * 1_000_000 + (duration.subsec_nanos() / 1_000) as u64;
            if 0 != time_duration_in_usec {
                info!("{} txs have been added into tx_pool, and time cost is {:?}, tps: {:?}", self.add_to_pool_cnt, duration, self.add_to_pool_cnt * 1_000_000 / time_duration_in_usec);
            }
            self.add_to_pool_cnt = 0;
        }

        if !out_txs.is_empty() {
            body.set_transactions(RepeatedField::from_vec(out_txs));
        }
        block_txs.set_height(height as u64);
        block_txs.set_body(body);
        trace!("deal_txs send height {}", height);
        let msg = factory::create_msg(submodules::AUTH, topics::BLOCK_TXS, communication::MsgType::BLOCK_TXS, block_txs.write_to_bytes().unwrap());
        mq_pub.send(("auth.block_txs".to_string(), msg.write_to_bytes().unwrap())).unwrap();
    }

    pub fn wait_timeout_process(&mut self, mq_pub: Sender<(String, Vec<u8>)>) {
        let time_elapsed = self.batch_forward_info.forward_stamp.elapsed().unwrap().subsec_nanos();
        let count_buffered = self.batch_forward_info.new_tx_request_buffer.len();
        if self.batch_forward_info.new_tx_request_buffer.len() > 0 {
            trace!("wait_timeout_process is going to send new tx batch to peer auth with {} new tx and buffer {} ns", count_buffered, time_elapsed);
            self.batch_forward_tx_to_peer(mq_pub);
        }
    }

    pub fn add_tx_to_pool(&self, tx: &SignedTransaction) -> bool {
        //交易放入pool，
        //放入pool完成后，持久化
        let ref mut txs_pool = self.txs_pool.borrow_mut();
        let success = txs_pool.enqueue(tx.clone());
        if success {
            self.wal.write(&tx);
        } else {
            self.filter_wal.write(&tx);
        }
        success
    }

    pub fn get_txs_from_pool(&self, height: u64, block_gas_limit: u64, account_gas_limit: AccountGasLimit) -> Vec<SignedTransaction> {
        if self.data_from_pool.load(Ordering::SeqCst) {
            self.data_from_pool.store(false, Ordering::SeqCst);
            Vec::new()
        } else {
            let ref mut txs_pool = self.txs_pool.borrow_mut();
            let txs = txs_pool.package(height, block_gas_limit, account_gas_limit);
            txs
        }
    }

    pub fn tx_flow_control(&self) -> bool {
        if self.pool_limit == 0 {
            return false;
        }
        let txs_pool = self.txs_pool.borrow();
        if txs_pool.len() > self.pool_limit { true } else { false }
    }

    pub fn del_txs_from_pool_with_hash(&self, txs: &HashSet<H256>) {
        //收到删除通知，从pool中删除vec中的交易
        {
            self.txs_pool.borrow_mut().update_with_hash(txs);
        }
        //改成多线程删除数据
        let mut wal = self.wal.clone();
        let txs = txs.clone();
        thread::spawn(move || for tx in txs {
                          wal.delete_with_hash(&tx);
                      });
    }

    pub fn del_txs_from_pool(&self, txs: Vec<SignedTransaction>) {
        //收到删除通知，从pool中删除vec中的交易
        {
            self.txs_pool.borrow_mut().update(&txs);
        }
        //改成多线程删除数据
        let mut wal = self.wal.clone();
        thread::spawn(move || for tx in txs {
                          wal.delete(&tx);
                      });
    }

    pub fn read_tx_from_wal(&mut self) -> u64 {
        self.wal.read(&mut self.txs_pool.borrow_mut())
    }

    fn batch_forward_tx_to_peer(&mut self, mq_pub: Sender<(String, Vec<u8>)>) {
        trace!("batch_forward_tx_to_peer is going to send {} new tx to peer", self.batch_forward_info.new_tx_request_buffer.len());
        let mut batch_request = BatchRequest::new();
        batch_request.set_new_tx_requests(RepeatedField::from_slice(&self.batch_forward_info.new_tx_request_buffer[..]));

        let request_id = Uuid::new_v4().as_bytes().to_vec();
        let mut request = Request::new();
        request.set_batch_req(batch_request);
        request.set_request_id(request_id);

        let msg = factory::create_msg(submodules::AUTH, topics::REQUEST, communication::MsgType::REQUEST, request.write_to_bytes().unwrap());
        mq_pub.send(("auth.tx".to_string(), msg.write_to_bytes().unwrap())).unwrap();

        self.batch_forward_info.forward_stamp = SystemTime::now();
        self.batch_forward_info.new_tx_request_buffer.clear();
    }
}
<|endoftext|>


<commit_before>use super::*;

impl Editor {
    /// Remove from a given motion (row based), i.e. if the motion given is to another line, all
    /// the lines from the current one to the one defined by the motion are removed. If the motion
    /// defines a position on the same line, only the characters from the current position to the
    /// motion's position are removed.
    pub fn remove_rb<'a>(&mut self, (x, y): (isize, isize)) {
        if y == self.y() as isize {
            let (x, y) = self.bound((x as usize, y as usize));
            // Single line mode
            let (a, b) = if self.x() > x {
                (x, self.x())
             } else {
                (self.x(), x)
            };
            for _ in a..b {
                self.text[y].remove(a);
            }
        } else {
            let (x, y) = self.bound((x as usize, y as usize));
            // Full line mode
            let (a, b) = if self.y() < y {
                (self.y(), y)
            } else {
                (y, self.y())
            };
            for _ in a..(b + 1) {
                if self.text.len() > 1 {
                    self.text.remove(a);
                } else {
                    self.text[0] = VecDeque::new();
                }
            }
        }
    }
}
<commit_msg>Fix the warning about unused field<commit_after>use super::*;

impl Editor {
    /// Remove from a given motion (row based), i.e. if the motion given is to another line, all
    /// the lines from the current one to the one defined by the motion are removed. If the motion
    /// defines a position on the same line, only the characters from the current position to the
    /// motion's position are removed.
    pub fn remove_rb<'a>(&mut self, (x, y): (isize, isize)) {
        if y == self.y() as isize {
            let (x, y) = self.bound((x as usize, y as usize));
            // Single line mode
            let (a, b) = if self.x() > x {
                (x, self.x())
             } else {
                (self.x(), x)
            };
            for _ in a..b {
                self.text[y].remove(a);
            }
        } else {
            let (_, y) = self.bound((x as usize, y as usize));
            // Full line mode
            let (a, b) = if self.y() < y {
                (self.y(), y)
            } else {
                (y, self.y())
            };
            for _ in a..(b + 1) {
                if self.text.len() > 1 {
                    self.text.remove(a);
                } else {
                    self.text[0] = VecDeque::new();
                }
            }
        }
    }
}
<|endoftext|>