feat(mavlink): adopt the kaptan-native dialect
This commit is contained in:
1 parent
1c90870634
commit
5f91465e66
87 files changed
+7733
-6850
No files matched your search
@@ -6,7 +6,7 @@ use core::{
|
||||
sync::atomic::{AtomicBool, Ordering},
|
||||
};
|
||||
|
||||
use crate::{Level, LogFlags, LogRecord, LogText};
|
||||
use crate::{Level, LogRecord, LogText};
|
||||
|
||||
const GLOBAL_INGRESS_CAPACITY: usize = 2;
|
||||
static GLOBAL_INGRESS: LogIngress<GLOBAL_INGRESS_CAPACITY> = LogIngress::new();
|
||||
@@ -33,11 +33,11 @@ impl<const N: usize> LogIngress<N> {
|
||||
}
|
||||
}
|
||||
|
||||
fn publish(&self, level: Level, flags: LogFlags, arguments: fmt::Arguments<'_>) {
|
||||
fn publish(&self, level: Level, arguments: fmt::Arguments<'_>) {
|
||||
let message = arguments
|
||||
.as_str()
|
||||
.map_or_else(|| LogText::from_arguments(arguments), LogText::from_message);
|
||||
let record = LogRecord::new(level, flags, message);
|
||||
let record = LogRecord::new(level, message);
|
||||
let _ = self.with_state(|entries, head, len| {
|
||||
if *len < N {
|
||||
entries[(*head + *len) % N] = Some(record);
|
||||
@@ -110,8 +110,8 @@ impl<const N: usize> LogIngress<N> {
|
||||
/// Publishes one macro event to the fixed global ingress.
|
||||
#[doc(hidden)]
|
||||
#[inline]
|
||||
pub fn publish(level: Level, flags: LogFlags, arguments: fmt::Arguments<'_>) {
|
||||
GLOBAL_INGRESS.publish(level, flags, arguments);
|
||||
pub fn publish(level: Level, arguments: fmt::Arguments<'_>) {
|
||||
GLOBAL_INGRESS.publish(level, arguments);
|
||||
}
|
||||
|
||||
/// Visits and removes the oldest pending log event.
|
||||
@@ -128,11 +128,16 @@ mod tests {
|
||||
use core::sync::atomic::Ordering;
|
||||
|
||||
use super::{LogIngress, drain_one};
|
||||
use crate::{Level, LogFlags};
|
||||
use crate::{Level, LogRecord};
|
||||
use std::sync::Mutex;
|
||||
|
||||
static GLOBAL_INGRESS_TEST: Mutex<()> = Mutex::new(());
|
||||
|
||||
fn message(record: &LogRecord) -> &str {
|
||||
let text = record.message();
|
||||
core::str::from_utf8(&text.bytes()[..usize::from(text.byte_length())]).unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn every_macro_produces_the_same_log_record_kind() {
|
||||
let _guard = GLOBAL_INGRESS_TEST
|
||||
@@ -144,7 +149,7 @@ mod tests {
|
||||
assert_eq!(drain_one(|record| record.level()), Some(Level::Info));
|
||||
assert_eq!(
|
||||
drain_one(|record| {
|
||||
assert_eq!(record.message().as_str(), "transfer failed");
|
||||
assert_eq!(message(record), "transfer failed");
|
||||
record.level()
|
||||
}),
|
||||
Some(Level::Error)
|
||||
@@ -154,29 +159,21 @@ mod tests {
|
||||
#[test]
|
||||
fn full_mixed_ingress_keeps_critical_records_and_evicts_best_effort() {
|
||||
let ingress = LogIngress::<2>::new();
|
||||
ingress.publish(Level::Info, LogFlags::NONE, core::format_args!("advance"));
|
||||
ingress.publish(Level::Info, core::format_args!("advance"));
|
||||
assert_eq!(
|
||||
ingress.drain_one(|record| assert_eq!(record.message().as_str(), "advance")),
|
||||
ingress.drain_one(|record| assert_eq!(message(record), "advance")),
|
||||
Some(())
|
||||
);
|
||||
ingress.publish(Level::Info, LogFlags::NONE, core::format_args!("optional"));
|
||||
ingress.publish(
|
||||
Level::Error,
|
||||
LogFlags::NONE,
|
||||
core::format_args!("first error"),
|
||||
);
|
||||
ingress.publish(
|
||||
Level::Error,
|
||||
LogFlags::NONE,
|
||||
core::format_args!("second error"),
|
||||
);
|
||||
ingress.publish(Level::Info, core::format_args!("optional"));
|
||||
ingress.publish(Level::Error, core::format_args!("first error"));
|
||||
ingress.publish(Level::Error, core::format_args!("second error"));
|
||||
|
||||
assert_eq!(
|
||||
ingress.drain_one(|record| assert_eq!(record.message().as_str(), "first error")),
|
||||
ingress.drain_one(|record| assert_eq!(message(record), "first error")),
|
||||
Some(())
|
||||
);
|
||||
assert_eq!(
|
||||
ingress.drain_one(|record| assert_eq!(record.message().as_str(), "second error")),
|
||||
ingress.drain_one(|record| assert_eq!(message(record), "second error")),
|
||||
Some(())
|
||||
);
|
||||
assert!(ingress.drain_one(|_| ()).is_none());
|
||||
@@ -185,28 +182,16 @@ mod tests {
|
||||
#[test]
|
||||
fn full_critical_ingress_keeps_the_earlier_records() {
|
||||
let ingress = LogIngress::<2>::new();
|
||||
ingress.publish(
|
||||
Level::Error,
|
||||
LogFlags::NONE,
|
||||
core::format_args!("first error"),
|
||||
);
|
||||
ingress.publish(
|
||||
Level::Error,
|
||||
LogFlags::NONE,
|
||||
core::format_args!("second error"),
|
||||
);
|
||||
ingress.publish(
|
||||
Level::Error,
|
||||
LogFlags::NONE,
|
||||
core::format_args!("discarded error"),
|
||||
);
|
||||
ingress.publish(Level::Error, core::format_args!("first error"));
|
||||
ingress.publish(Level::Error, core::format_args!("second error"));
|
||||
ingress.publish(Level::Error, core::format_args!("discarded error"));
|
||||
|
||||
assert_eq!(
|
||||
ingress.drain_one(|record| assert_eq!(record.message().as_str(), "first error")),
|
||||
ingress.drain_one(|record| assert_eq!(message(record), "first error")),
|
||||
Some(())
|
||||
);
|
||||
assert_eq!(
|
||||
ingress.drain_one(|record| assert_eq!(record.message().as_str(), "second error")),
|
||||
ingress.drain_one(|record| assert_eq!(message(record), "second error")),
|
||||
Some(())
|
||||
);
|
||||
assert!(ingress.drain_one(|_| ()).is_none());
|
||||
@@ -215,7 +200,7 @@ mod tests {
|
||||
#[test]
|
||||
fn contended_drain_keeps_the_record_for_a_later_attempt() {
|
||||
let ingress = LogIngress::<1>::new();
|
||||
ingress.publish(Level::Info, LogFlags::NONE, core::format_args!("pending"));
|
||||
ingress.publish(Level::Info, core::format_args!("pending"));
|
||||
|
||||
ingress.busy.store(true, Ordering::Relaxed);
|
||||
let mut called = false;
|
||||
@@ -224,7 +209,7 @@ mod tests {
|
||||
ingress.busy.store(false, Ordering::Relaxed);
|
||||
|
||||
assert_eq!(
|
||||
ingress.drain_one(|record| assert_eq!(record.message().as_str(), "pending")),
|
||||
ingress.drain_one(|record| assert_eq!(message(record), "pending")),
|
||||
Some(())
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,21 +1,6 @@
|
||||
//! Bounded priority handling for log records.
|
||||
|
||||
use core::fmt;
|
||||
|
||||
use crate::{DeliveryClass, Level, LevelFilter, LogFlags, LogRecord, LogText};
|
||||
|
||||
/// Outcome from one bounded log admission attempt.
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub enum PublishOutcome {
|
||||
/// The record was retained.
|
||||
Queued,
|
||||
/// The configured level filter excluded the record.
|
||||
Filtered,
|
||||
/// A best-effort record was replaced by newer optional information.
|
||||
ReplacedOldest,
|
||||
/// The critical lane kept its earlier record.
|
||||
RejectedFull,
|
||||
}
|
||||
use crate::{DeliveryClass, LevelFilter, LogRecord};
|
||||
|
||||
/// Result returned after attempting lower-transport admission.
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
@@ -42,19 +27,17 @@ impl<const N: usize> Lane<N> {
|
||||
}
|
||||
}
|
||||
|
||||
fn publish(&mut self, record: LogRecord, replace_oldest: bool) -> PublishOutcome {
|
||||
fn publish(&mut self, record: LogRecord, replace_oldest: bool) {
|
||||
if self.len == N {
|
||||
if replace_oldest {
|
||||
self.entries[self.head] = Some(record);
|
||||
self.head = (self.head + 1) % N;
|
||||
return PublishOutcome::ReplacedOldest;
|
||||
}
|
||||
return PublishOutcome::RejectedFull;
|
||||
return;
|
||||
}
|
||||
let index = (self.head + self.len) % N;
|
||||
self.entries[index] = Some(record);
|
||||
self.len += 1;
|
||||
PublishOutcome::Queued
|
||||
}
|
||||
|
||||
fn peek(&self) -> Option<LogRecord> {
|
||||
@@ -89,30 +72,10 @@ impl<const CRITICAL: usize, const BEST_EFFORT: usize> LogSinkQueue<CRITICAL, BES
|
||||
}
|
||||
}
|
||||
|
||||
/// Immutable sink policy.
|
||||
pub const fn filter(&self) -> LevelFilter {
|
||||
self.filter
|
||||
}
|
||||
|
||||
/// Formats and retains one log event.
|
||||
pub fn publish_arguments(
|
||||
&mut self,
|
||||
level: Level,
|
||||
flags: LogFlags,
|
||||
arguments: fmt::Arguments<'_>,
|
||||
) -> PublishOutcome {
|
||||
self.publish_text(level, flags, LogText::from_arguments(arguments))
|
||||
}
|
||||
|
||||
/// Retains one preformatted log event.
|
||||
pub fn publish_text(&mut self, level: Level, flags: LogFlags, text: LogText) -> PublishOutcome {
|
||||
self.publish_record(LogRecord::new(level, flags, text))
|
||||
}
|
||||
|
||||
/// Applies this sink's filter and retains one record.
|
||||
pub fn publish_record(&mut self, record: LogRecord) -> PublishOutcome {
|
||||
pub fn publish_record(&mut self, record: LogRecord) {
|
||||
if !self.filter.allows(record.level()) {
|
||||
return PublishOutcome::Filtered;
|
||||
return;
|
||||
}
|
||||
match record.delivery() {
|
||||
DeliveryClass::Critical => self.critical.publish(record, false),
|
||||
@@ -145,21 +108,19 @@ impl<const CRITICAL: usize, const BEST_EFFORT: usize> LogSinkQueue<CRITICAL, BES
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{LogSinkQueue, PublishOutcome, TransportAdmission};
|
||||
use crate::{DeliveryClass, Level, LevelFilter, LogFlags, LogText};
|
||||
use super::{LogSinkQueue, TransportAdmission};
|
||||
use crate::{DeliveryClass, Level, LevelFilter, LogRecord, LogText};
|
||||
|
||||
#[test]
|
||||
fn filter_and_priority_apply_only_to_logs() {
|
||||
let mut queue = LogSinkQueue::<1, 1>::new(LevelFilter::Info);
|
||||
assert_eq!(
|
||||
queue.publish_text(Level::Debug, LogFlags::NONE, LogText::from_message("debug")),
|
||||
PublishOutcome::Filtered
|
||||
);
|
||||
assert_eq!(
|
||||
queue.publish_text(Level::Error, LogFlags::NONE, LogText::from_message("error")),
|
||||
PublishOutcome::Queued
|
||||
);
|
||||
assert_eq!(queue.peek().unwrap().delivery(), DeliveryClass::Critical);
|
||||
queue.publish_record(LogRecord::new(Level::Debug, LogText::from_message("debug")));
|
||||
assert!(queue.peek().is_none());
|
||||
queue.publish_record(LogRecord::new(Level::Error, LogText::from_message("error")));
|
||||
assert!(matches!(
|
||||
queue.peek().unwrap().delivery(),
|
||||
DeliveryClass::Critical
|
||||
));
|
||||
assert_eq!(
|
||||
queue.submit_next(|_| (TransportAdmission::Accepted, ())),
|
||||
Some(())
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
//! Compact conventional log levels and filtering.
|
||||
|
||||
/// Severity of one emitted log event.
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
#[repr(u8)]
|
||||
pub enum Level {
|
||||
/// A runtime operation failed.
|
||||
Error = 1,
|
||||
/// Recoverable warning.
|
||||
Warn = 2,
|
||||
/// Fine-grained diagnostic output.
|
||||
Trace = 1,
|
||||
/// Diagnostic output.
|
||||
Debug = 2,
|
||||
/// Informational operation.
|
||||
Info = 3,
|
||||
/// Diagnostic output.
|
||||
Debug = 4,
|
||||
/// Fine-grained diagnostic output.
|
||||
Trace = 5,
|
||||
/// Recoverable warning.
|
||||
Warn = 4,
|
||||
/// A runtime operation failed.
|
||||
Error = 5,
|
||||
}
|
||||
|
||||
impl Level {
|
||||
@@ -29,28 +29,28 @@ impl Level {
|
||||
}
|
||||
}
|
||||
|
||||
/// Conventional maximum log level, including the disabled state.
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, kaptan_config_derive::ConfigEnum)]
|
||||
/// Minimum retained log severity, including the disabled state.
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, kaptan_config_derive::ConfigEnum)]
|
||||
#[repr(u8)]
|
||||
pub enum LevelFilter {
|
||||
/// Disable the sink.
|
||||
Off = 0,
|
||||
/// Accept error events only.
|
||||
Error = 1,
|
||||
/// Accept errors and warnings.
|
||||
Warn = 2,
|
||||
/// Accept through informational events.
|
||||
Info = 3,
|
||||
/// Accept through debug events.
|
||||
Debug = 4,
|
||||
/// Accept every event, including trace.
|
||||
Trace = 5,
|
||||
Trace = 1,
|
||||
/// Accept debug through error events.
|
||||
Debug = 2,
|
||||
/// Accept informational through error events.
|
||||
Info = 3,
|
||||
/// Accept warning and error events.
|
||||
Warn = 4,
|
||||
/// Accept error events only.
|
||||
Error = 5,
|
||||
}
|
||||
|
||||
impl LevelFilter {
|
||||
/// Returns true when this filter accepts the event.
|
||||
pub const fn allows(self, level: Level) -> bool {
|
||||
level as u8 <= self as u8
|
||||
!matches!(self, Self::Off) && level as u8 >= self as u8
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,6 +64,17 @@ mod tests {
|
||||
fn levels_are_one_byte_and_filter_conventionally() {
|
||||
assert_eq!(size_of::<Level>(), 1);
|
||||
assert_eq!(size_of::<LevelFilter>(), 1);
|
||||
assert_eq!(LevelFilter::Off as u8, 0);
|
||||
assert_eq!(LevelFilter::Trace as u8, 1);
|
||||
assert_eq!(LevelFilter::Debug as u8, 2);
|
||||
assert_eq!(LevelFilter::Info as u8, 3);
|
||||
assert_eq!(LevelFilter::Warn as u8, 4);
|
||||
assert_eq!(LevelFilter::Error as u8, 5);
|
||||
assert_eq!(Level::Trace as u8, 1);
|
||||
assert_eq!(Level::Debug as u8, 2);
|
||||
assert_eq!(Level::Info as u8, 3);
|
||||
assert_eq!(Level::Warn as u8, 4);
|
||||
assert_eq!(Level::Error as u8, 5);
|
||||
assert!(!LevelFilter::Off.allows(Level::Error));
|
||||
assert!(LevelFilter::Info.allows(Level::Error));
|
||||
assert!(LevelFilter::Info.allows(Level::Info));
|
||||
|
||||
@@ -14,16 +14,16 @@ extern crate self as kaptan_log;
|
||||
#[cfg(test)]
|
||||
extern crate std;
|
||||
|
||||
pub mod facade;
|
||||
mod facade;
|
||||
mod hub;
|
||||
pub mod level;
|
||||
pub mod report;
|
||||
pub mod text;
|
||||
mod level;
|
||||
mod report;
|
||||
mod text;
|
||||
|
||||
pub use hub::{LogSinkQueue, PublishOutcome, TransportAdmission};
|
||||
pub use hub::{LogSinkQueue, TransportAdmission};
|
||||
pub use level::{Level, LevelFilter};
|
||||
pub use report::{DeliveryClass, LogFlags, LogRecord};
|
||||
pub use text::{LogText, MAXIMUM_LOG_TEXT_BYTES};
|
||||
pub use report::{DeliveryClass, LogRecord};
|
||||
pub use text::LogText;
|
||||
|
||||
pub use facade::drain_one;
|
||||
#[doc(hidden)]
|
||||
@@ -33,11 +33,7 @@ pub use facade::publish as __publish;
|
||||
#[macro_export]
|
||||
macro_rules! debug {
|
||||
($($argument:tt)+) => {
|
||||
$crate::__publish(
|
||||
$crate::Level::Debug,
|
||||
$crate::LogFlags::NONE,
|
||||
core::format_args!($($argument)+),
|
||||
)
|
||||
$crate::__publish($crate::Level::Debug, core::format_args!($($argument)+))
|
||||
};
|
||||
}
|
||||
|
||||
@@ -45,11 +41,7 @@ macro_rules! debug {
|
||||
#[macro_export]
|
||||
macro_rules! info {
|
||||
($($argument:tt)+) => {
|
||||
$crate::__publish(
|
||||
$crate::Level::Info,
|
||||
$crate::LogFlags::NONE,
|
||||
core::format_args!($($argument)+),
|
||||
)
|
||||
$crate::__publish($crate::Level::Info, core::format_args!($($argument)+))
|
||||
};
|
||||
}
|
||||
|
||||
@@ -57,11 +49,7 @@ macro_rules! info {
|
||||
#[macro_export]
|
||||
macro_rules! warn {
|
||||
($($argument:tt)+) => {
|
||||
$crate::__publish(
|
||||
$crate::Level::Warn,
|
||||
$crate::LogFlags::NONE,
|
||||
core::format_args!($($argument)+),
|
||||
)
|
||||
$crate::__publish($crate::Level::Warn, core::format_args!($($argument)+))
|
||||
};
|
||||
}
|
||||
|
||||
@@ -69,22 +57,14 @@ macro_rules! warn {
|
||||
#[macro_export]
|
||||
macro_rules! error {
|
||||
($($argument:tt)+) => {
|
||||
$crate::__publish(
|
||||
$crate::Level::Error,
|
||||
$crate::LogFlags::NONE,
|
||||
core::format_args!($($argument)+),
|
||||
)
|
||||
$crate::__publish($crate::Level::Error, core::format_args!($($argument)+))
|
||||
};
|
||||
}
|
||||
|
||||
/// Emits a trace event, represented as MAVLink Debug plus the trace flag.
|
||||
/// Emits a trace event.
|
||||
#[macro_export]
|
||||
macro_rules! trace {
|
||||
($($argument:tt)+) => {
|
||||
$crate::__publish(
|
||||
$crate::Level::Trace,
|
||||
$crate::LogFlags::NONE,
|
||||
core::format_args!($($argument)+),
|
||||
)
|
||||
$crate::__publish($crate::Level::Trace, core::format_args!($($argument)+))
|
||||
};
|
||||
}
|
||||
@@ -2,30 +2,7 @@
|
||||
|
||||
use crate::{Level, LogText};
|
||||
|
||||
/// Flags belonging exclusively to a log record.
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
#[repr(transparent)]
|
||||
pub struct LogFlags(u8);
|
||||
|
||||
impl LogFlags {
|
||||
/// No optional flags.
|
||||
pub const NONE: Self = Self(0);
|
||||
/// Message text exceeded its fixed capacity.
|
||||
pub const TRUNCATED: Self = Self(1 << 1);
|
||||
|
||||
/// Raw transport bitmask.
|
||||
pub const fn bits(self) -> u8 {
|
||||
self.0
|
||||
}
|
||||
|
||||
/// Adds a bit.
|
||||
pub const fn with(self, other: Self) -> Self {
|
||||
Self(self.0 | other.0)
|
||||
}
|
||||
}
|
||||
|
||||
/// Internal transport priority selected from a conventional log level.
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub enum DeliveryClass {
|
||||
/// Error-level logs.
|
||||
Critical,
|
||||
@@ -37,21 +14,13 @@ pub enum DeliveryClass {
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub struct LogRecord {
|
||||
level: Level,
|
||||
flags: LogFlags,
|
||||
message: LogText,
|
||||
}
|
||||
|
||||
impl LogRecord {
|
||||
/// Creates one bounded log record at the supplied level.
|
||||
pub fn new(level: Level, mut flags: LogFlags, message: LogText) -> Self {
|
||||
if message.is_truncated() {
|
||||
flags = flags.with(LogFlags::TRUNCATED);
|
||||
}
|
||||
Self {
|
||||
level,
|
||||
flags,
|
||||
message,
|
||||
}
|
||||
pub(crate) const fn new(level: Level, message: LogText) -> Self {
|
||||
Self { level, message }
|
||||
}
|
||||
|
||||
/// Conventional log level.
|
||||
@@ -59,11 +28,6 @@ impl LogRecord {
|
||||
self.level
|
||||
}
|
||||
|
||||
/// Log-specific flag bitmask.
|
||||
pub const fn flags(&self) -> LogFlags {
|
||||
self.flags
|
||||
}
|
||||
|
||||
/// Fixed human-readable message.
|
||||
pub const fn message(&self) -> &LogText {
|
||||
&self.message
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
use core::fmt::{self, Write};
|
||||
|
||||
/// Maximum bytes retained in one log message.
|
||||
pub const MAXIMUM_LOG_TEXT_BYTES: usize = 160;
|
||||
pub(crate) const MAXIMUM_LOG_TEXT_BYTES: usize = 160;
|
||||
|
||||
/// Transport-neutral bounded log message.
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
@@ -18,8 +18,7 @@ pub struct LogText {
|
||||
}
|
||||
|
||||
impl LogText {
|
||||
/// Creates empty fixed text storage.
|
||||
pub const fn new() -> Self {
|
||||
const fn new() -> Self {
|
||||
Self {
|
||||
bytes: [0; MAXIMUM_LOG_TEXT_BYTES],
|
||||
length: 0,
|
||||
@@ -28,14 +27,14 @@ impl LogText {
|
||||
}
|
||||
|
||||
/// Formats one message without allocation.
|
||||
pub fn from_arguments(arguments: fmt::Arguments<'_>) -> Self {
|
||||
pub(crate) fn from_arguments(arguments: fmt::Arguments<'_>) -> Self {
|
||||
let mut text = Self::new();
|
||||
let _ = fmt::write(&mut text, arguments);
|
||||
text
|
||||
}
|
||||
|
||||
/// Copies UTF-8 text without splitting a code point at the fixed boundary.
|
||||
pub fn from_message(message: &str) -> Self {
|
||||
pub(crate) fn from_message(message: &str) -> Self {
|
||||
let mut text = Self::new();
|
||||
let _ = text.write_str(message);
|
||||
text
|
||||
@@ -47,32 +46,14 @@ impl LogText {
|
||||
}
|
||||
|
||||
/// Number of meaningful UTF-8 bytes.
|
||||
pub const fn len(&self) -> u8 {
|
||||
pub const fn byte_length(&self) -> u8 {
|
||||
self.length
|
||||
}
|
||||
|
||||
/// Whether this message contains no text.
|
||||
pub const fn is_empty(&self) -> bool {
|
||||
self.length == 0
|
||||
}
|
||||
|
||||
/// Whether the input exceeded fixed storage.
|
||||
pub const fn is_truncated(&self) -> bool {
|
||||
self.truncated != 0
|
||||
}
|
||||
|
||||
/// Returns the valid UTF-8 prefix.
|
||||
pub fn as_str(&self) -> &str {
|
||||
// `write_str` copies only complete code points from an input `&str`.
|
||||
// SAFETY: every populated byte belongs to that valid UTF-8 prefix.
|
||||
unsafe { core::str::from_utf8_unchecked(&self.bytes[..usize::from(self.length)]) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for LogText {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Write for LogText {
|
||||
@@ -108,6 +89,10 @@ mod tests {
|
||||
let input = "x".repeat(MAXIMUM_LOG_TEXT_BYTES - 1) + "é";
|
||||
let text = LogText::from_message(&input);
|
||||
assert!(text.is_truncated());
|
||||
assert_eq!(text.as_str().len(), MAXIMUM_LOG_TEXT_BYTES - 1);
|
||||
let retained = &text.bytes()[..usize::from(text.byte_length())];
|
||||
assert_eq!(
|
||||
core::str::from_utf8(retained).unwrap().len(),
|
||||
MAXIMUM_LOG_TEXT_BYTES - 1
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user