#![cfg_attr(not(feature = "std"), no_std)]
#[cfg(not(feature = "std"))]
extern crate alloc;
use bincode::de::{BorrowDecoder, Decoder};
use bincode::enc::Encoder;
use bincode::error::{DecodeError, EncodeError};
use bincode::{BorrowDecode, Decode as dDecode, Decode, Encode, Encode as dEncode};
use compact_str::CompactString;
#[cfg(not(feature = "std"))]
use core::error::Error as CoreError;
use cu29_clock::{PartialCuTimeRange, Tov};
use serde::{Deserialize, Serialize};
#[cfg(feature = "std")]
use std::fmt::{Debug, Display, Formatter};
#[cfg(not(feature = "std"))]
use alloc::string::{String, ToString};
#[cfg(not(feature = "std"))]
use alloc::vec::Vec;
#[cfg(not(feature = "std"))]
use core::fmt::{Debug, Display, Formatter};
#[cfg(feature = "std")]
use std::error::Error;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CuError {
message: String,
cause: Option<String>,
}
impl Display for CuError {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
let context_str = match &self.cause {
Some(c) => c.to_string(),
None => "None".to_string(),
};
write!(f, "{}\n context:{}", self.message, context_str)?;
Ok(())
}
}
#[cfg(not(feature = "std"))]
impl CoreError for CuError {}
#[cfg(feature = "std")]
impl Error for CuError {}
impl From<&str> for CuError {
fn from(s: &str) -> CuError {
CuError {
message: s.to_string(),
cause: None,
}
}
}
impl From<String> for CuError {
fn from(s: String) -> CuError {
CuError {
message: s,
cause: None,
}
}
}
impl CuError {
pub fn new_with_cause(message: &str, cause: impl Display) -> CuError {
CuError {
message: message.to_string(),
cause: Some(cause.to_string()),
}
}
pub fn add_cause(mut self, context: &str) -> CuError {
self.cause = Some(context.into());
self
}
}
pub type CuResult<T> = Result<T, CuError>;
pub trait WriteStream<E: Encode>: Debug + Send + Sync {
fn log(&mut self, obj: &E) -> CuResult<()>;
fn flush(&mut self) -> CuResult<()> {
Ok(())
}
}
#[derive(dEncode, dDecode, Copy, Clone, Debug, PartialEq)]
pub enum UnifiedLogType {
Empty, StructuredLogLine, CopperList, FrozenTasks, LastEntry, }
pub trait Metadata: Default + Debug + Clone + Encode + Decode<()> + Serialize {}
impl Metadata for () {}
pub trait CuMsgMetadataTrait {
fn process_time(&self) -> PartialCuTimeRange;
fn status_txt(&self) -> &CuCompactString;
}
pub trait ErasedCuStampedData {
fn payload(&self) -> Option<&dyn erased_serde::Serialize>;
fn tov(&self) -> Tov;
fn metadata(&self) -> &dyn CuMsgMetadataTrait;
}
pub trait ErasedCuStampedDataSet {
fn cumsgs(&self) -> Vec<&dyn ErasedCuStampedData>;
}
pub trait MatchingTasks {
fn get_all_task_ids() -> &'static [&'static str];
}
pub trait CopperListTuple:
bincode::Encode
+ bincode::Decode<()>
+ Debug
+ Serialize
+ ErasedCuStampedDataSet
+ MatchingTasks
+ Default
{
}
impl<T> CopperListTuple for T where
T: bincode::Encode
+ bincode::Decode<()>
+ Debug
+ Serialize
+ ErasedCuStampedDataSet
+ MatchingTasks
+ Default
{
}
pub const COMPACT_STRING_CAPACITY: usize = size_of::<String>();
#[derive(Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
pub struct CuCompactString(pub CompactString);
impl Encode for CuCompactString {
fn encode<E: Encoder>(&self, encoder: &mut E) -> Result<(), EncodeError> {
let CuCompactString(ref compact_string) = self;
let bytes = &compact_string.as_bytes();
bytes.encode(encoder)
}
}
impl Debug for CuCompactString {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
if self.0.is_empty() {
return write!(f, "CuCompactString(Empty)");
}
write!(f, "CuCompactString({})", self.0)
}
}
impl<Context> Decode<Context> for CuCompactString {
fn decode<D: Decoder>(decoder: &mut D) -> Result<Self, DecodeError> {
let bytes = <Vec<u8> as Decode<D::Context>>::decode(decoder)?; let compact_string =
CompactString::from_utf8(bytes).map_err(|e| DecodeError::Utf8 { inner: e })?;
Ok(CuCompactString(compact_string))
}
}
impl<'de, Context> BorrowDecode<'de, Context> for CuCompactString {
fn borrow_decode<D: BorrowDecoder<'de>>(decoder: &mut D) -> Result<Self, DecodeError> {
CuCompactString::decode(decoder)
}
}
#[cfg(feature = "defmt")]
impl defmt::Format for CuError {
fn format(&self, f: defmt::Formatter) {
match &self.cause {
Some(c) => defmt::write!(
f,
"CuError {{ message: {}, cause: {} }}",
defmt::Display2Format(&self.message),
defmt::Display2Format(c),
),
None => defmt::write!(
f,
"CuError {{ message: {}, cause: None }}",
defmt::Display2Format(&self.message),
),
}
}
}
#[cfg(feature = "defmt")]
impl defmt::Format for CuCompactString {
fn format(&self, f: defmt::Formatter) {
if self.0.is_empty() {
defmt::write!(f, "CuCompactString(Empty)");
} else {
defmt::write!(f, "CuCompactString({})", defmt::Display2Format(&self.0));
}
}
}
#[cfg(test)]
mod tests {
use crate::CuCompactString;
use bincode::{config, decode_from_slice, encode_to_vec};
use compact_str::CompactString;
#[test]
fn test_cucompactstr_encode_decode_empty() {
let cstr = CuCompactString(CompactString::from(""));
let config = config::standard();
let encoded = encode_to_vec(&cstr, config).expect("Encoding failed");
assert_eq!(encoded.len(), 1); let (decoded, _): (CuCompactString, usize) =
decode_from_slice(&encoded, config).expect("Decoding failed");
assert_eq!(cstr.0, decoded.0);
}
#[test]
fn test_cucompactstr_encode_decode_small() {
let cstr = CuCompactString(CompactString::from("test"));
let config = config::standard();
let encoded = encode_to_vec(&cstr, config).expect("Encoding failed");
assert_eq!(encoded.len(), 5); let (decoded, _): (CuCompactString, usize) =
decode_from_slice(&encoded, config).expect("Decoding failed");
assert_eq!(cstr.0, decoded.0);
}
}