use std::{
borrow::{Borrow, Cow},
cell::RefCell,
time::SystemTime,
};
use crate::{Level, SourceLocation};
#[derive(Clone, Debug)]
pub struct Record<'a> {
logger_name: Option<&'a str>,
payload: Cow<'a, str>,
inner: Cow<'a, RecordInner>,
}
#[derive(Clone, Debug)]
struct RecordInner {
level: Level,
source_location: Option<SourceLocation>,
time: SystemTime,
tid: u64,
}
impl<'a> Record<'a> {
#[must_use]
pub(crate) fn new<S>(level: Level, payload: S) -> Record<'a>
where
S: Into<Cow<'a, str>>,
{
Record {
logger_name: None,
payload: payload.into(),
inner: Cow::Owned(RecordInner {
level,
source_location: None,
time: SystemTime::now(),
tid: get_current_tid(),
}),
}
}
#[must_use]
pub(crate) fn builder<S>(level: Level, payload: S) -> RecordBuilder<'a>
where
S: Into<Cow<'a, str>>,
{
RecordBuilder::new(level, payload)
}
#[must_use]
pub fn to_owned(&self) -> RecordOwned {
RecordOwned {
logger_name: self.logger_name.map(|n| n.into()),
payload: self.payload.to_string(),
inner: self.inner.clone().into_owned(),
}
}
#[must_use]
pub fn logger_name(&self) -> Option<&'a str> {
self.logger_name
}
#[must_use]
pub fn level(&self) -> Level {
self.inner.level
}
#[must_use]
pub fn payload(&self) -> &str {
self.payload.borrow()
}
#[must_use]
pub fn source_location(&self) -> Option<&SourceLocation> {
self.inner.source_location.as_ref()
}
#[must_use]
pub fn time(&self) -> SystemTime {
self.inner.time
}
#[must_use]
pub(crate) fn tid(&self) -> u64 {
self.inner.tid
}
#[cfg(feature = "log")]
#[must_use]
pub(crate) fn from_log_crate_record(
logger: &'a crate::Logger,
record: &log::Record,
time: SystemTime,
) -> Self {
let args = record.args();
Self {
logger_name: logger.name(),
payload: match args.as_str() {
Some(literal_str) => literal_str.into(),
None => args.to_string().into(),
},
inner: Cow::Owned(RecordInner {
level: record.level().into(),
source_location: SourceLocation::from_log_crate_record(record),
time,
tid: get_current_tid(),
}),
}
}
#[cfg(test)]
pub(crate) fn set_time(&mut self, new: SystemTime) {
self.inner.to_mut().time = new;
}
}
#[derive(Clone, Debug)]
pub struct RecordOwned {
logger_name: Option<String>,
payload: String,
inner: RecordInner,
}
impl RecordOwned {
#[doc(hidden)]
pub const __SIZE_OF: usize = std::mem::size_of::<Self>();
#[must_use]
pub fn as_ref(&self) -> Record {
Record {
logger_name: self.logger_name.as_deref(),
payload: Cow::Borrowed(&self.payload),
inner: Cow::Borrowed(&self.inner),
}
}
#[must_use]
pub fn logger_name(&self) -> Option<&str> {
self.logger_name.as_deref()
}
#[must_use]
pub fn level(&self) -> Level {
self.inner.level
}
#[must_use]
pub fn payload(&self) -> &str {
self.payload.borrow()
}
#[must_use]
pub fn source_location(&self) -> Option<&SourceLocation> {
self.inner.source_location.as_ref()
}
#[must_use]
pub fn time(&self) -> SystemTime {
self.inner.time
}
}
#[derive(Clone, Debug)]
pub(crate) struct RecordBuilder<'a> {
record: Record<'a>,
}
impl<'a> RecordBuilder<'a> {
#[must_use]
pub(crate) fn new<S>(level: Level, payload: S) -> Self
where
S: Into<Cow<'a, str>>,
{
Self {
record: Record::new(level, payload),
}
}
#[must_use]
pub(crate) fn logger_name(mut self, logger_name: &'a str) -> Self {
self.record.logger_name = Some(logger_name);
self
}
#[must_use]
pub(crate) fn source_location(mut self, srcloc: Option<SourceLocation>) -> Self {
self.record.inner.to_mut().source_location = srcloc;
self
}
#[must_use]
pub(crate) fn build(self) -> Record<'a> {
self.record
}
}
fn get_current_tid() -> u64 {
#[cfg(target_os = "linux")]
#[must_use]
fn get_current_tid_inner() -> u64 {
let tid = unsafe { libc::syscall(libc::SYS_gettid) };
tid as u64
}
#[cfg(any(target_os = "macos", target_os = "ios"))]
#[must_use]
fn get_current_tid_inner() -> u64 {
let mut tid = 0;
unsafe { libc::pthread_threadid_np(0, &mut tid) };
tid
}
#[cfg(target_os = "windows")]
#[must_use]
fn get_current_tid_inner() -> u64 {
let tid = unsafe { winapi::um::processthreadsapi::GetCurrentThreadId() };
tid as u64
}
thread_local! {
static TID: RefCell<Option<u64>> = RefCell::new(None);
}
TID.with(|tid| *tid.borrow_mut().get_or_insert_with(get_current_tid_inner))
}