blackhole 0.20.2

...to throw your threads into.
Documentation
/*
==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--

Black Hole

Copyright (C) 2019-2020, 2022-2023  Anonymous

There are several releases over multiple years,
they are listed as ranges, such as: "2019-2020".

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser 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 Lesser General Public License for more details.

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

::--::--::--::--::--::--::--::--::--::--::--::--::--::--::--::--
*/

//! # Black Hole
//!
//! _...to throw your threads into._
//!
//! ## Project
//!
//! - Repository: <https://bitbucket.org/de-marco/blackhole>
//! - License: GNU Lesser General Public License, either version 3, or (at your option) any later version.
//! - _This project follows [Semantic Versioning 2.0.0]_
//!
//! ## Features
//!
//! Simple thread manager.
//!
//! ## Design
//!
//! It uses a [channel][std::sync::mpsc] and one job manager thread:
//!
//! - When you throw a new job, it checks for active job count.
//!
//!     + If that count is smaller than limit, it spawns a new runner thread to run that job.
//!     + If that count is equal to limit:
//!
//!         * If job queue is smaller than limit, it pushes the job into the queue.
//!         * Otherwise the job is discarded. An error message is printed to stderr. And the job is returned to you for recovery.
//!
//! - When the runner thread finishes the first job, it contacts job manager to ask for new jobs to run. If there are no new jobs, it finishes
//!   itself and notifies job manager about that.
//! - When job manager receives job-finished-message, it checks job queue to spawn new runner threads, if necessary.
//!
//! ## Examples
//!
//! ```
//! use blackhole::{ActiveLimit, BlackHole, Job};
//!
//! /// # This job takes a `usize`, if it's odd, sends it to Printer
//! struct Handler {
//!     data: usize,
//! }
//!
//! impl Job for Handler {
//!
//!     fn run(&mut self) -> Option<Box<dyn Job>> {
//!         match self.data % 2 {
//!             0 => None,
//!             _ => Some(Box::new(Printer { data: self.data })),
//!         }
//!     }
//!
//! }
//!
//! /// # This job prints data and does nothing else
//! struct Printer {
//!     data: usize,
//! }
//!
//! impl Job for Printer {
//!
//!     fn run(&mut self) -> Option<Box<dyn Job>> {
//!         println!("{}", self.data);
//!         None
//!     }
//!
//! }
//!
//! let active_limit: ActiveLimit = 4;
//! let queue_limit: usize = 10;
//! let black_hole = BlackHole::make_with_active_limit(active_limit, queue_limit).unwrap();
//! for i in 0 .. queue_limit * 2 {
//!     match black_hole.throw(Handler { data: i }) {
//!         Ok(None) => println!("Job is accepted"),
//!         // If job is discarded, it will be sent back here.
//!         Ok(Some(job)) => {
//!             eprintln!("Job is discarded! We'll run it ourselves!");
//!             blackhole::run_to_end(job);
//!         },
//!         Err(err) => {
//!             eprintln!("BlackHole... exploded: {}", err);
//!             break;
//!         },
//!     };
//! }
//! black_hole.escape_on_idle().unwrap();
//! ```
//!
//! [Semantic Versioning 2.0.0]: https://semver.org/spec/v2.0.0.html
//! [std::sync::mpsc]: https://doc.rust-lang.org/std/sync/mpsc/index.html

#![warn(missing_docs)]

// ╔═════════════════╗
// ║   IDENTIFIERS   ║
// ╚═════════════════╝

macro_rules! code_name  { () => { "black-hole" }}
macro_rules! version    { () => { "0.20.2" }}

/// # Crate name
pub const NAME: &str = "Black Hole";

/// # Crate code name
pub const CODE_NAME: &str = code_name!();

/// # ID of this crate
pub const ID: &str = concat!(
    "e64a08f1-fdd25485-2acc4e42-e735d450-9f0f609c-7ffb34ec-fed9b97f-bf66bc0d-",
    "447179f2-7c5d9ff7-69c111e2-a0a2769d-071a4c1d-4a9e6ac0-c37fec16-19aa9e5e",
);

/// # Crate version
pub const VERSION: &str = version!();

/// # Crate release date (year/month/day)
pub const RELEASE_DATE: (u16, u8, u8) = (2023, 1, 24);

/// # Tag, which can be used for logging...
pub const TAG: &str = concat!(code_name!(), "::e64a08f1::", version!());

// ╔════════════════════╗
// ║   IMPLEMENTATION   ║
// ╚════════════════════╝

/// # Wrapper for format!(), which prefixes your optional message with: crate::TAG, module_path!(), line!()
macro_rules! __ {
    ($($arg: tt)+) => {
        format!("[{tag}][{module_path}-{line}] {msg}", tag=crate::TAG, module_path=module_path!(), line=line!(), msg=format!($($arg)+))
    };
    () => {
        format!("[{tag}][{module_path}-{line}] (internal error)", tag=crate::TAG, module_path=module_path!(), line=line!())
    };
}

#[macro_use]
#[allow(unused_macros)]
mod __;
mod io;
mod job;
mod root;

pub use job::*;
pub use root::*;

pub mod version_info;

/// # Result type used in this crate
pub type Result<T> = core::result::Result<T, std::io::Error>;

#[test]
fn test_crate_version() {
    assert_eq!(VERSION, env!("CARGO_PKG_VERSION"));
}