#![warn(missing_docs)]
#![deny(missing_debug_implementations, unconditional_recursion)]
#![cfg_attr(all(not(feature = "std"), not(test)), no_std)]
#[cfg(all(not(feature = "std"), not(test)))]
extern crate core as std;
#[cfg(feature = "std")]
use std::error;
use std::fmt;
#[cfg(target_has_atomic = "ptr")]
use std::sync::atomic::{AtomicUsize, Ordering};
#[cfg(not(target_has_atomic = "ptr"))]
use std::cell::Cell;
#[cfg(not(target_has_atomic = "ptr"))]
use std::sync::atomic::Ordering;
#[macro_use]
pub mod macros;
#[doc(hidden)]
pub mod __private_api;
#[cfg(not(target_has_atomic = "ptr"))]
struct AtomicUsize {
v: Cell<usize>,
}
#[cfg(not(target_has_atomic = "ptr"))]
impl AtomicUsize {
const fn new(v: usize) -> AtomicUsize {
AtomicUsize { v: Cell::new(v) }
}
fn load(&self, _order: Ordering) -> usize {
self.v.get()
}
fn store(&self, val: usize, _order: Ordering) {
self.v.set(val)
}
}
#[cfg(not(target_has_atomic = "ptr"))]
unsafe impl Sync for AtomicUsize {}
static mut VLOGGER: &dyn VLog = &NopVLogger;
static STATE: AtomicUsize = AtomicUsize::new(0);
const UNINITIALIZED: usize = 0;
const INITIALIZING: usize = 1;
const INITIALIZED: usize = 2;
static SET_VLOGGER_ERROR: &str = "attempted to set a vlogger after the vlogging system \
was already initialized";
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
enum MaybeStaticStr<'a> {
Static(&'static str),
Borrowed(&'a str),
}
impl<'a> MaybeStaticStr<'a> {
#[inline]
fn get(&self) -> &'a str {
match *self {
MaybeStaticStr::Static(s) => s,
MaybeStaticStr::Borrowed(s) => s,
}
}
}
#[derive(Clone, Debug)]
pub struct Record<'a> {
metadata: Metadata<'a>,
visual: Visual,
color: Color,
size: f64,
args: fmt::Arguments<'a>,
module_path: Option<MaybeStaticStr<'a>>,
file: Option<MaybeStaticStr<'a>>,
line: Option<u32>,
}
impl<'a> Record<'a> {
#[inline]
pub fn builder() -> RecordBuilder<'a> {
RecordBuilder::new()
}
#[inline]
pub fn args(&self) -> &fmt::Arguments<'a> {
&self.args
}
#[inline]
pub fn visual(&self) -> &Visual {
&self.visual
}
#[inline]
pub fn color(&self) -> &Color {
&self.color
}
#[inline]
pub fn size(&self) -> f64 {
self.size
}
#[inline]
pub fn metadata(&self) -> &Metadata<'a> {
&self.metadata
}
#[inline]
pub fn target(&self) -> &'a str {
self.metadata.target()
}
#[inline]
pub fn surface(&self) -> &'a str {
self.metadata.surface()
}
#[inline]
pub fn module_path(&self) -> Option<&'a str> {
self.module_path.map(|s| s.get())
}
#[inline]
pub fn module_path_static(&self) -> Option<&'static str> {
match self.module_path {
Some(MaybeStaticStr::Static(s)) => Some(s),
_ => None,
}
}
#[inline]
pub fn file(&self) -> Option<&'a str> {
self.file.map(|s| s.get())
}
#[inline]
pub fn file_static(&self) -> Option<&'static str> {
match self.file {
Some(MaybeStaticStr::Static(s)) => Some(s),
_ => None,
}
}
#[inline]
pub fn line(&self) -> Option<u32> {
self.line
}
}
#[derive(Debug)]
pub struct RecordBuilder<'a> {
record: Record<'a>,
}
impl<'a> RecordBuilder<'a> {
#[inline]
pub fn new() -> RecordBuilder<'a> {
RecordBuilder {
record: Record {
visual: Visual::Message,
color: Color::Base,
size: 12.0,
args: format_args!(""),
metadata: Metadata::builder().build(),
module_path: None,
file: None,
line: None,
},
}
}
pub fn visual(&mut self, visual: Visual) -> &mut RecordBuilder<'a> {
self.record.visual = visual;
self
}
pub fn color(&mut self, color: Color) -> &mut RecordBuilder<'a> {
self.record.color = color;
self
}
pub fn size(&mut self, size: f64) -> &mut RecordBuilder<'a> {
self.record.size = size;
self
}
#[inline]
pub fn args(&mut self, args: fmt::Arguments<'a>) -> &mut RecordBuilder<'a> {
self.record.args = args;
self
}
#[inline]
pub fn metadata(&mut self, metadata: Metadata<'a>) -> &mut RecordBuilder<'a> {
self.record.metadata = metadata;
self
}
#[inline]
pub fn surface(&mut self, surface: &'a str) -> &mut RecordBuilder<'a> {
self.record.metadata.surface = surface;
self
}
#[inline]
pub fn target(&mut self, target: &'a str) -> &mut RecordBuilder<'a> {
self.record.metadata.target = target;
self
}
#[inline]
pub fn module_path(&mut self, path: Option<&'a str>) -> &mut RecordBuilder<'a> {
self.record.module_path = path.map(MaybeStaticStr::Borrowed);
self
}
#[inline]
pub fn module_path_static(&mut self, path: Option<&'static str>) -> &mut RecordBuilder<'a> {
self.record.module_path = path.map(MaybeStaticStr::Static);
self
}
#[inline]
pub fn file(&mut self, file: Option<&'a str>) -> &mut RecordBuilder<'a> {
self.record.file = file.map(MaybeStaticStr::Borrowed);
self
}
#[inline]
pub fn file_static(&mut self, file: Option<&'static str>) -> &mut RecordBuilder<'a> {
self.record.file = file.map(MaybeStaticStr::Static);
self
}
#[inline]
pub fn line(&mut self, line: Option<u32>) -> &mut RecordBuilder<'a> {
self.record.line = line;
self
}
#[inline]
pub fn build(&self) -> Record<'a> {
self.record.clone()
}
}
impl Default for RecordBuilder<'_> {
fn default() -> Self {
Self::new()
}
}
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
pub struct Metadata<'a> {
surface: &'a str,
target: &'a str,
}
impl<'a> Metadata<'a> {
#[inline]
pub fn builder() -> MetadataBuilder<'a> {
MetadataBuilder::new()
}
#[inline]
pub fn surface(&self) -> &'a str {
self.surface
}
#[inline]
pub fn target(&self) -> &'a str {
self.target
}
}
#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
pub struct MetadataBuilder<'a> {
metadata: Metadata<'a>,
}
impl<'a> MetadataBuilder<'a> {
#[inline]
pub fn new() -> MetadataBuilder<'a> {
MetadataBuilder {
metadata: Metadata {
surface: "",
target: "",
},
}
}
#[inline]
pub fn surface(&mut self, surface: &'a str) -> &mut MetadataBuilder<'a> {
self.metadata.surface = surface;
self
}
#[inline]
pub fn target(&mut self, target: &'a str) -> &mut MetadataBuilder<'a> {
self.metadata.target = target;
self
}
#[inline]
pub fn build(&self) -> Metadata<'a> {
self.metadata.clone()
}
}
impl Default for MetadataBuilder<'_> {
fn default() -> Self {
Self::new()
}
}
#[derive(Clone, Copy, Debug)]
#[non_exhaustive]
pub enum PointStyle {
FilledCircle,
Circle,
DashedCircle,
FilledSquare,
Square,
DashedSquare,
Point,
PointOutline,
PointSquare,
PointSquareOutline,
PointCross,
PointDiamond,
PointDiamondOutline,
}
#[derive(Clone, Copy, Debug)]
#[non_exhaustive]
pub enum LineStyle {
Simple,
Dashed,
Arrow,
InsideHarpoonCCW,
InsideHarpoonCW,
}
#[derive(Clone, Copy, Debug, Default)]
#[repr(u8)]
pub enum TextAlignment {
Left = 0,
Center = 1,
Right = 2,
#[default]
Flexible = 3,
}
#[derive(Clone, Debug, Default)]
pub enum Visual {
#[default]
Message,
Label {
x: f64,
y: f64,
z: f64,
alignment: TextAlignment,
},
Point {
x: f64,
y: f64,
z: f64,
style: PointStyle,
},
Line {
x1: f64,
y1: f64,
z1: f64,
x2: f64,
y2: f64,
z2: f64,
style: LineStyle,
},
}
#[derive(Clone, Copy, Debug, Default)]
#[non_exhaustive]
pub enum Color {
#[default]
Base,
Healthy,
Info,
Warn,
Error,
X,
Y,
Z,
Missing,
Hex(u32),
}
pub trait VLog {
fn enabled(&self, metadata: &Metadata) -> bool;
fn vlog(&self, record: &Record);
fn clear(&self, surface: &str);
fn flush(&self);
}
struct NopVLogger;
impl VLog for NopVLogger {
fn enabled(&self, _: &Metadata) -> bool {
false
}
fn vlog(&self, _: &Record) {}
fn clear(&self, _: &str) {}
fn flush(&self) {}
}
impl<T> VLog for &'_ T
where
T: ?Sized + VLog,
{
fn enabled(&self, metadata: &Metadata) -> bool {
(**self).enabled(metadata)
}
fn vlog(&self, record: &Record) {
(**self).vlog(record);
}
fn clear(&self, surface: &str) {
(**self).clear(surface);
}
fn flush(&self) {
(**self).flush();
}
}
#[cfg(feature = "std")]
impl<T> VLog for std::boxed::Box<T>
where
T: ?Sized + VLog,
{
fn enabled(&self, metadata: &Metadata) -> bool {
self.as_ref().enabled(metadata)
}
fn vlog(&self, record: &Record) {
self.as_ref().vlog(record);
}
fn clear(&self, surface: &str) {
self.as_ref().clear(surface);
}
fn flush(&self) {
self.as_ref().flush();
}
}
#[cfg(feature = "std")]
impl<T> VLog for std::sync::Arc<T>
where
T: ?Sized + VLog,
{
fn enabled(&self, metadata: &Metadata) -> bool {
self.as_ref().enabled(metadata)
}
fn vlog(&self, record: &Record) {
self.as_ref().vlog(record);
}
fn clear(&self, surface: &str) {
self.as_ref().clear(surface);
}
fn flush(&self) {
self.as_ref().flush();
}
}
#[cfg(all(feature = "std", target_has_atomic = "ptr"))]
pub fn set_boxed_vlogger(vlogger: Box<dyn VLog>) -> Result<(), SetVLoggerError> {
set_vlogger_inner(|| Box::leak(vlogger))
}
#[cfg(target_has_atomic = "ptr")]
pub fn set_vlogger(vlogger: &'static dyn VLog) -> Result<(), SetVLoggerError> {
set_vlogger_inner(|| vlogger)
}
#[cfg(target_has_atomic = "ptr")]
fn set_vlogger_inner<F>(make_vlogger: F) -> Result<(), SetVLoggerError>
where
F: FnOnce() -> &'static dyn VLog,
{
match STATE.compare_exchange(
UNINITIALIZED,
INITIALIZING,
Ordering::Acquire,
Ordering::Relaxed,
) {
Ok(UNINITIALIZED) => {
unsafe {
VLOGGER = make_vlogger();
}
STATE.store(INITIALIZED, Ordering::Release);
Ok(())
}
Err(INITIALIZING) => {
while STATE.load(Ordering::Relaxed) == INITIALIZING {
std::hint::spin_loop();
}
Err(SetVLoggerError(()))
}
_ => Err(SetVLoggerError(())),
}
}
pub unsafe fn set_vlogger_racy(vlogger: &'static dyn VLog) -> Result<(), SetVLoggerError> {
match STATE.load(Ordering::Acquire) {
UNINITIALIZED => {
unsafe {
VLOGGER = vlogger;
}
STATE.store(INITIALIZED, Ordering::Release);
Ok(())
}
INITIALIZING => {
unreachable!("set_vlogger_racy must not be used with other initialization functions")
}
_ => Err(SetVLoggerError(())),
}
}
#[allow(missing_copy_implementations)]
#[derive(Debug)]
pub struct SetVLoggerError(());
impl fmt::Display for SetVLoggerError {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.write_str(SET_VLOGGER_ERROR)
}
}
#[cfg(feature = "std")]
impl error::Error for SetVLoggerError {}
pub fn vlogger() -> &'static dyn VLog {
if STATE.load(Ordering::Acquire) != INITIALIZED {
static NOP: NopVLogger = NopVLogger;
&NOP
} else {
unsafe { VLOGGER }
}
}