1#![cfg_attr(not(feature = "std"), no_std)]
23extern crate alloc;
24
25use bincode::de::{BorrowDecoder, Decoder};
26use bincode::enc::Encoder;
27use bincode::error::{DecodeError, EncodeError};
28use bincode::{BorrowDecode, Decode as dDecode, Decode, Encode, Encode as dEncode};
29use compact_str::CompactString;
30use cu29_clock::{PartialCuTimeRange, Tov};
31use serde::{Deserialize, Serialize};
32
33use alloc::boxed::Box;
34use alloc::format;
35use alloc::string::{String, ToString};
36use alloc::vec::Vec;
37#[cfg(not(feature = "std"))]
38use core::error::Error as CoreError;
39use core::fmt::{Debug, Display, Formatter};
40#[cfg(feature = "std")]
41use std::error::Error;
42
43#[cfg(feature = "std")]
45type DynError = dyn std::error::Error + Send + Sync + 'static;
46#[cfg(not(feature = "std"))]
47type DynError = dyn core::error::Error + Send + Sync + 'static;
48
49#[derive(Debug)]
52struct StringError(String);
53
54impl Display for StringError {
55 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
56 write!(f, "{}", self.0)
57 }
58}
59
60#[cfg(feature = "std")]
61impl std::error::Error for StringError {}
62
63#[cfg(not(feature = "std"))]
64impl core::error::Error for StringError {}
65
66pub struct CuError {
72 message: String,
73 cause: Option<Box<DynError>>,
74}
75
76impl Debug for CuError {
78 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
79 f.debug_struct("CuError")
80 .field("message", &self.message)
81 .field("cause", &self.cause.as_ref().map(|e| e.to_string()))
82 .finish()
83 }
84}
85
86impl Clone for CuError {
88 fn clone(&self) -> Self {
89 CuError {
90 message: self.message.clone(),
91 cause: self
92 .cause
93 .as_ref()
94 .map(|e| Box::new(StringError(e.to_string())) as Box<DynError>),
95 }
96 }
97}
98
99impl Serialize for CuError {
101 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
102 where
103 S: serde::Serializer,
104 {
105 use serde::ser::SerializeStruct;
106 let mut state = serializer.serialize_struct("CuError", 2)?;
107 state.serialize_field("message", &self.message)?;
108 state.serialize_field("cause", &self.cause.as_ref().map(|e| e.to_string()))?;
109 state.end()
110 }
111}
112
113impl<'de> Deserialize<'de> for CuError {
115 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
116 where
117 D: serde::Deserializer<'de>,
118 {
119 #[derive(Deserialize)]
120 struct CuErrorHelper {
121 message: String,
122 cause: Option<String>,
123 }
124
125 let helper = CuErrorHelper::deserialize(deserializer)?;
126 Ok(CuError {
127 message: helper.message,
128 cause: helper
129 .cause
130 .map(|s| Box::new(StringError(s)) as Box<DynError>),
131 })
132 }
133}
134
135impl Display for CuError {
136 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
137 let context_str = match &self.cause {
138 Some(c) => c.to_string(),
139 None => "None".to_string(),
140 };
141 write!(f, "{}\n context:{}", self.message, context_str)?;
142 Ok(())
143 }
144}
145
146#[cfg(not(feature = "std"))]
147impl CoreError for CuError {
148 fn source(&self) -> Option<&(dyn CoreError + 'static)> {
149 self.cause
150 .as_deref()
151 .map(|e| e as &(dyn CoreError + 'static))
152 }
153}
154
155#[cfg(feature = "std")]
156impl Error for CuError {
157 fn source(&self) -> Option<&(dyn Error + 'static)> {
158 self.cause.as_deref().map(|e| e as &(dyn Error + 'static))
159 }
160}
161
162impl From<&str> for CuError {
163 fn from(s: &str) -> CuError {
164 CuError {
165 message: s.to_string(),
166 cause: None,
167 }
168 }
169}
170
171impl From<String> for CuError {
172 fn from(s: String) -> CuError {
173 CuError {
174 message: s,
175 cause: None,
176 }
177 }
178}
179
180impl CuError {
181 pub fn new(message_index: usize) -> CuError {
187 CuError {
188 message: format!("[interned:{}]", message_index),
189 cause: None,
190 }
191 }
192
193 #[cfg(feature = "std")]
203 pub fn new_with_cause<E>(message: &str, cause: E) -> CuError
204 where
205 E: std::error::Error + Send + Sync + 'static,
206 {
207 CuError {
208 message: message.to_string(),
209 cause: Some(Box::new(cause)),
210 }
211 }
212
213 #[cfg(not(feature = "std"))]
215 pub fn new_with_cause<E>(message: &str, cause: E) -> CuError
216 where
217 E: core::error::Error + Send + Sync + 'static,
218 {
219 CuError {
220 message: message.to_string(),
221 cause: Some(Box::new(cause)),
222 }
223 }
224
225 pub fn add_cause(mut self, context: &str) -> CuError {
236 self.cause = Some(Box::new(StringError(context.to_string())));
237 self
238 }
239
240 #[cfg(feature = "std")]
250 pub fn with_cause<E>(mut self, cause: E) -> CuError
251 where
252 E: std::error::Error + Send + Sync + 'static,
253 {
254 self.cause = Some(Box::new(cause));
255 self
256 }
257
258 #[cfg(not(feature = "std"))]
260 pub fn with_cause<E>(mut self, cause: E) -> CuError
261 where
262 E: core::error::Error + Send + Sync + 'static,
263 {
264 self.cause = Some(Box::new(cause));
265 self
266 }
267
268 pub fn cause(&self) -> Option<&(dyn core::error::Error + Send + Sync + 'static)> {
270 self.cause.as_deref()
271 }
272
273 pub fn message(&self) -> &str {
275 &self.message
276 }
277}
278
279#[cfg(feature = "std")]
291pub fn with_cause<E>(message: &str, cause: E) -> CuError
292where
293 E: std::error::Error + Send + Sync + 'static,
294{
295 CuError::new_with_cause(message, cause)
296}
297
298#[cfg(not(feature = "std"))]
300pub fn with_cause<E>(message: &str, cause: E) -> CuError
301where
302 E: core::error::Error + Send + Sync + 'static,
303{
304 CuError::new_with_cause(message, cause)
305}
306
307pub type CuResult<T> = Result<T, CuError>;
309
310pub trait WriteStream<E: Encode>: Debug + Send + Sync {
312 fn log(&mut self, obj: &E) -> CuResult<()>;
313 fn flush(&mut self) -> CuResult<()> {
314 Ok(())
315 }
316}
317
318#[derive(dEncode, dDecode, Copy, Clone, Debug, PartialEq)]
320pub enum UnifiedLogType {
321 Empty, StructuredLogLine, CopperList, FrozenTasks, LastEntry, }
327pub trait Metadata: Default + Debug + Clone + Encode + Decode<()> + Serialize {}
329
330impl Metadata for () {}
331
332pub trait CuMsgMetadataTrait {
334 fn process_time(&self) -> PartialCuTimeRange;
336
337 fn status_txt(&self) -> &CuCompactString;
339}
340
341pub trait ErasedCuStampedData {
343 fn payload(&self) -> Option<&dyn erased_serde::Serialize>;
344 fn tov(&self) -> Tov;
345 fn metadata(&self) -> &dyn CuMsgMetadataTrait;
346}
347
348pub trait ErasedCuStampedDataSet {
351 fn cumsgs(&self) -> Vec<&dyn ErasedCuStampedData>;
352}
353
354pub trait MatchingTasks {
356 fn get_all_task_ids() -> &'static [&'static str];
357}
358
359pub trait CopperListTuple:
361 bincode::Encode
362 + bincode::Decode<()>
363 + Debug
364 + Serialize
365 + ErasedCuStampedDataSet
366 + MatchingTasks
367 + Default
368{
369} impl<T> CopperListTuple for T where
373 T: bincode::Encode
374 + bincode::Decode<()>
375 + Debug
376 + Serialize
377 + ErasedCuStampedDataSet
378 + MatchingTasks
379 + Default
380{
381}
382
383pub const COMPACT_STRING_CAPACITY: usize = size_of::<String>();
387
388#[derive(Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
389pub struct CuCompactString(pub CompactString);
390
391impl Encode for CuCompactString {
392 fn encode<E: Encoder>(&self, encoder: &mut E) -> Result<(), EncodeError> {
393 let CuCompactString(compact_string) = self;
394 let bytes = &compact_string.as_bytes();
395 bytes.encode(encoder)
396 }
397}
398
399impl Debug for CuCompactString {
400 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
401 if self.0.is_empty() {
402 return write!(f, "CuCompactString(Empty)");
403 }
404 write!(f, "CuCompactString({})", self.0)
405 }
406}
407
408impl<Context> Decode<Context> for CuCompactString {
409 fn decode<D: Decoder>(decoder: &mut D) -> Result<Self, DecodeError> {
410 let bytes = <Vec<u8> as Decode<D::Context>>::decode(decoder)?; let compact_string =
412 CompactString::from_utf8(bytes).map_err(|e| DecodeError::Utf8 { inner: e })?;
413 Ok(CuCompactString(compact_string))
414 }
415}
416
417impl<'de, Context> BorrowDecode<'de, Context> for CuCompactString {
418 fn borrow_decode<D: BorrowDecoder<'de>>(decoder: &mut D) -> Result<Self, DecodeError> {
419 CuCompactString::decode(decoder)
420 }
421}
422
423#[cfg(feature = "defmt")]
424impl defmt::Format for CuError {
425 fn format(&self, f: defmt::Formatter) {
426 match &self.cause {
427 Some(c) => {
428 let cause_str = c.to_string();
429 defmt::write!(
430 f,
431 "CuError {{ message: {}, cause: {} }}",
432 defmt::Display2Format(&self.message),
433 defmt::Display2Format(&cause_str),
434 )
435 }
436 None => defmt::write!(
437 f,
438 "CuError {{ message: {}, cause: None }}",
439 defmt::Display2Format(&self.message),
440 ),
441 }
442 }
443}
444
445#[cfg(feature = "defmt")]
446impl defmt::Format for CuCompactString {
447 fn format(&self, f: defmt::Formatter) {
448 if self.0.is_empty() {
449 defmt::write!(f, "CuCompactString(Empty)");
450 } else {
451 defmt::write!(f, "CuCompactString({})", defmt::Display2Format(&self.0));
452 }
453 }
454}
455
456#[cfg(test)]
457mod tests {
458 use crate::CuCompactString;
459 use bincode::{config, decode_from_slice, encode_to_vec};
460 use compact_str::CompactString;
461
462 #[test]
463 fn test_cucompactstr_encode_decode_empty() {
464 let cstr = CuCompactString(CompactString::from(""));
465 let config = config::standard();
466 let encoded = encode_to_vec(&cstr, config).expect("Encoding failed");
467 assert_eq!(encoded.len(), 1); let (decoded, _): (CuCompactString, usize) =
469 decode_from_slice(&encoded, config).expect("Decoding failed");
470 assert_eq!(cstr.0, decoded.0);
471 }
472
473 #[test]
474 fn test_cucompactstr_encode_decode_small() {
475 let cstr = CuCompactString(CompactString::from("test"));
476 let config = config::standard();
477 let encoded = encode_to_vec(&cstr, config).expect("Encoding failed");
478 assert_eq!(encoded.len(), 5); let (decoded, _): (CuCompactString, usize) =
480 decode_from_slice(&encoded, config).expect("Decoding failed");
481 assert_eq!(cstr.0, decoded.0);
482 }
483}
484
485#[cfg(all(test, feature = "std"))]
487mod std_tests {
488 use crate::{CuError, with_cause};
489
490 #[test]
491 fn test_cuerror_from_str() {
492 let err = CuError::from("test error");
493 assert_eq!(err.message(), "test error");
494 assert!(err.cause().is_none());
495 }
496
497 #[test]
498 fn test_cuerror_from_string() {
499 let err = CuError::from(String::from("test error"));
500 assert_eq!(err.message(), "test error");
501 assert!(err.cause().is_none());
502 }
503
504 #[test]
505 fn test_cuerror_new_index() {
506 let err = CuError::new(42);
507 assert_eq!(err.message(), "[interned:42]");
508 assert!(err.cause().is_none());
509 }
510
511 #[test]
512 fn test_cuerror_new_with_cause() {
513 let io_err = std::io::Error::other("io error");
514 let err = CuError::new_with_cause("wrapped error", io_err);
515 assert_eq!(err.message(), "wrapped error");
516 assert!(err.cause().is_some());
517 assert!(err.cause().unwrap().to_string().contains("io error"));
518 }
519
520 #[test]
521 fn test_cuerror_add_cause() {
522 let err = CuError::from("base error").add_cause("additional context");
523 assert_eq!(err.message(), "base error");
524 assert!(err.cause().is_some());
525 assert_eq!(err.cause().unwrap().to_string(), "additional context");
526 }
527
528 #[test]
529 fn test_cuerror_with_cause_method() {
530 let io_err = std::io::Error::other("io error");
531 let err = CuError::from("base error").with_cause(io_err);
532 assert_eq!(err.message(), "base error");
533 assert!(err.cause().is_some());
534 }
535
536 #[test]
537 fn test_cuerror_with_cause_free_function() {
538 let io_err = std::io::Error::other("io error");
539 let err = with_cause("wrapped", io_err);
540 assert_eq!(err.message(), "wrapped");
541 assert!(err.cause().is_some());
542 }
543
544 #[test]
545 fn test_cuerror_clone() {
546 let io_err = std::io::Error::other("io error");
547 let err = CuError::new_with_cause("test", io_err);
548 let cloned = err.clone();
549 assert_eq!(err.message(), cloned.message());
550 assert_eq!(
552 err.cause().map(|c| c.to_string()),
553 cloned.cause().map(|c| c.to_string())
554 );
555 }
556
557 #[test]
558 fn test_cuerror_serialize_deserialize_json() {
559 let io_err = std::io::Error::other("io error");
560 let err = CuError::new_with_cause("test", io_err);
561
562 let serialized = serde_json::to_string(&err).unwrap();
563 let deserialized: CuError = serde_json::from_str(&serialized).unwrap();
564
565 assert_eq!(err.message(), deserialized.message());
566 assert!(deserialized.cause().is_some());
568 }
569
570 #[test]
571 fn test_cuerror_serialize_deserialize_no_cause() {
572 let err = CuError::from("simple error");
573
574 let serialized = serde_json::to_string(&err).unwrap();
575 let deserialized: CuError = serde_json::from_str(&serialized).unwrap();
576
577 assert_eq!(err.message(), deserialized.message());
578 assert!(deserialized.cause().is_none());
579 }
580
581 #[test]
582 fn test_cuerror_display() {
583 let err = CuError::from("test error").add_cause("some context");
584 let display = format!("{}", err);
585 assert!(display.contains("test error"));
586 assert!(display.contains("some context"));
587 }
588
589 #[test]
590 fn test_cuerror_debug() {
591 let err = CuError::from("test error").add_cause("some context");
592 let debug = format!("{:?}", err);
593 assert!(debug.contains("test error"));
594 assert!(debug.contains("some context"));
595 }
596}