prodash 23.1.2

A dashboard for visualizing progress of asynchronous and possibly blocking tasks
Documentation
use std::{
    ops::Deref,
    sync::{atomic::AtomicUsize, Arc, Weak},
};

use parking_lot::Mutex;

use crate::{
    messages::{Message, MessageCopyState, MessageRingBuffer},
    progress::{Id, Key, Task},
    tree::{Item, Root},
};

impl Root {
    /// Create a new tree with default configuration.
    ///
    /// As opposed to [Item](./struct.Item.html) instances, this type can be closed and sent
    /// safely across threads.
    pub fn new() -> Arc<Root> {
        Options::default().into()
    }

    /// Returns the maximum amount of messages we can keep before overwriting older ones.
    pub fn messages_capacity(&self) -> usize {
        self.inner.lock().messages.lock().buf.capacity()
    }

    /// Returns the current amount of `Item`s stored in the tree.
    /// **Note** that this is at most a guess as tasks can be added and removed in parallel.
    pub fn num_tasks(&self) -> usize {
        #[cfg(feature = "progress-tree-hp-hashmap")]
        {
            self.inner.lock().tree.len()
        }
        #[cfg(not(feature = "progress-tree-hp-hashmap"))]
        {
            self.inner.lock().tree.len()
        }
    }

    /// Adds a new child `tree::Item`, whose parent is this instance, with the given `name`.
    ///
    /// This builds a hierarchy of `tree::Item`s, each having their own progress.
    /// Use this method to [track progress](./struct.Item.html) of your first tasks.
    pub fn add_child(&self, name: impl Into<String>) -> Item {
        self.inner.lock().add_child(name)
    }

    /// Adds a new child `tree::Item`, whose parent is this instance, with the given `name` and `id`.
    ///
    /// This builds a hierarchy of `tree::Item`s, each having their own progress.
    /// Use this method to [track progress](./struct.Item.html) of your first tasks.
    pub fn add_child_with_id(&self, name: impl Into<String>, id: Id) -> Item {
        self.inner.lock().add_child_with_id(name, id)
    }

    /// Copy the entire progress tree into the given `out` vector, so that
    /// it can be traversed from beginning to end in order of hierarchy.
    pub fn sorted_snapshot(&self, out: &mut Vec<(Key, Task)>) {
        out.clear();
        #[cfg(feature = "progress-tree-hp-hashmap")]
        out.extend(self.inner.lock().tree.iter().map(|r| (*r.key(), r.value().clone())));
        #[cfg(not(feature = "progress-tree-hp-hashmap"))]
        self.inner.lock().tree.extend_to(out);
        out.sort_by_key(|t| t.0);
    }

    /// Copy all messages from the internal ring buffer into the given `out`
    /// vector. Messages are ordered from oldest to newest.
    pub fn copy_messages(&self, out: &mut Vec<Message>) {
        self.inner.lock().messages.lock().copy_all(out);
    }

    /// Copy only new messages from the internal ring buffer into the given `out`
    /// vector. Messages are ordered from oldest to newest.
    pub fn copy_new_messages(&self, out: &mut Vec<Message>, prev: Option<MessageCopyState>) -> MessageCopyState {
        self.inner.lock().messages.lock().copy_new(out, prev)
    }

    /// Duplicate all content and return it.
    ///
    /// This is an expensive operation, whereas `clone()` is not as it is shallow.
    pub fn deep_clone(&self) -> Arc<Root> {
        Arc::new(Root {
            inner: Mutex::new(self.inner.lock().deep_clone()),
        })
    }
}

/// A way to configure new [`tree::Root`](./tree/struct.Root.html) instances
/// ```rust
/// let tree = prodash::tree::root::Options::default().create();
/// let tree2 = prodash::tree::root::Options { message_buffer_capacity: 100, ..Default::default() }.create();
/// ```
#[derive(Clone, Debug)]
pub struct Options {
    /// The amount of [items][Item] the tree can hold without being forced to allocate.
    pub initial_capacity: usize,
    /// The amount of messages we can hold before we start overwriting old ones.
    pub message_buffer_capacity: usize,
}

impl Options {
    /// Create a new [`Root`](./tree/struct.Root.html) instance from the
    /// configuration within.
    pub fn create(self) -> Root {
        self.into()
    }
}

impl Default for Options {
    fn default() -> Self {
        Options {
            initial_capacity: 100,
            message_buffer_capacity: 20,
        }
    }
}

impl From<Options> for Arc<Root> {
    fn from(opts: Options) -> Self {
        Arc::new(opts.into())
    }
}

impl From<Options> for Root {
    fn from(
        Options {
            initial_capacity,
            message_buffer_capacity,
        }: Options,
    ) -> Self {
        Root {
            inner: Mutex::new(Item {
                highest_child_id: 0,
                value: Arc::new(AtomicUsize::default()),
                key: Key::default(),
                tree: Arc::new(crate::tree::HashMap::with_capacity(initial_capacity)),
                messages: Arc::new(Mutex::new(MessageRingBuffer::with_capacity(message_buffer_capacity))),
            }),
        }
    }
}

impl crate::WeakRoot for Weak<Root> {
    type Root = Arc<Root>;

    fn upgrade(&self) -> Option<Self::Root> {
        Weak::upgrade(self)
    }
}

impl crate::Root for Arc<Root> {
    type WeakRoot = Weak<Root>;

    fn messages_capacity(&self) -> usize {
        self.deref().messages_capacity()
    }

    fn num_tasks(&self) -> usize {
        self.deref().num_tasks()
    }

    fn sorted_snapshot(&self, out: &mut Vec<(Key, Task)>) {
        self.deref().sorted_snapshot(out)
    }

    fn copy_messages(&self, out: &mut Vec<Message>) {
        self.deref().copy_messages(out)
    }

    fn copy_new_messages(&self, out: &mut Vec<Message>, prev: Option<MessageCopyState>) -> MessageCopyState {
        self.deref().copy_new_messages(out, prev)
    }

    fn downgrade(&self) -> Self::WeakRoot {
        Arc::downgrade(self)
    }
}