1use std::{
68 collections::HashMap,
69 ffi::CString,
70 ptr,
71 sync::{Arc, OnceLock, RwLock, atomic::AtomicBool},
72};
73
74use thiserror::Error;
75use tokio::sync::broadcast;
76
77#[rustfmt::skip]
78#[allow(clippy::all,
79 dead_code,
80 warnings,
81 clippy::arithmetic_side_effects,
82 clippy::indexing_slicing,
83)]
84pub use dlt_sys::{DLT_ID_SIZE, DltContext, DltContextData};
85
86#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
94pub enum DltLogLevel {
95 Default,
97 Off,
99 Fatal,
101 Error,
103 Warn,
105 Info,
107 Debug,
109 Verbose,
111}
112
113impl From<i32> for DltLogLevel {
114 fn from(value: i32) -> Self {
115 match value {
116 dlt_sys::DltLogLevelType_DLT_LOG_OFF => DltLogLevel::Off,
117 dlt_sys::DltLogLevelType_DLT_LOG_FATAL => DltLogLevel::Fatal,
118 dlt_sys::DltLogLevelType_DLT_LOG_ERROR => DltLogLevel::Error,
119 dlt_sys::DltLogLevelType_DLT_LOG_WARN => DltLogLevel::Warn,
120 dlt_sys::DltLogLevelType_DLT_LOG_INFO => DltLogLevel::Info,
121 dlt_sys::DltLogLevelType_DLT_LOG_DEBUG => DltLogLevel::Debug,
122 dlt_sys::DltLogLevelType_DLT_LOG_VERBOSE => DltLogLevel::Verbose,
123 _ => DltLogLevel::Default,
124 }
125 }
126}
127
128impl From<DltLogLevel> for i32 {
129 fn from(value: DltLogLevel) -> Self {
130 match value {
131 DltLogLevel::Default => dlt_sys::DltLogLevelType_DLT_LOG_DEFAULT,
132 DltLogLevel::Off => dlt_sys::DltLogLevelType_DLT_LOG_OFF,
133 DltLogLevel::Fatal => dlt_sys::DltLogLevelType_DLT_LOG_FATAL,
134 DltLogLevel::Error => dlt_sys::DltLogLevelType_DLT_LOG_ERROR,
135 DltLogLevel::Warn => dlt_sys::DltLogLevelType_DLT_LOG_WARN,
136 DltLogLevel::Info => dlt_sys::DltLogLevelType_DLT_LOG_INFO,
137 DltLogLevel::Debug => dlt_sys::DltLogLevelType_DLT_LOG_DEBUG,
138 DltLogLevel::Verbose => dlt_sys::DltLogLevelType_DLT_LOG_VERBOSE,
139 }
140 }
141}
142
143#[derive(Error, Debug, Clone, PartialEq, Eq)]
145pub enum DltError {
146 #[error("Data cannot be converted to a DLT compatible string: {0}")]
147 InvalidString(String),
148 #[error("Failed to register DLT context")]
149 ContextRegistrationFailed(String),
150 #[error("Failed to register DLT application")]
151 ApplicationRegistrationFailed(String),
152 #[error("Failed to register a log event change listener")]
153 LogLevelListenerRegistrationFailed(String),
154 #[error("A pointer or memory is invalid")]
155 InvalidMemory,
156 #[error("Failed to acquire a lock")]
157 BadLock,
158 #[error("Input value is invalid")]
159 InvalidInput,
160}
161
162#[derive(Error, Debug, Clone, Copy, PartialEq, Eq)]
164pub enum DltSysError {
165 #[cfg(feature = "trace_load_ctrl")]
166 #[error("DLT load exceeded")]
167 LoadExceeded,
169
170 #[error("DLT file size error")]
171 FileSizeError,
172
173 #[error("DLT logging disabled")]
174 LoggingDisabled,
175
176 #[error("DLT user buffer full")]
177 UserBufferFull,
178
179 #[error("DLT wrong parameter")]
180 WrongParameter,
181
182 #[error("DLT buffer full")]
183 BufferFull,
184
185 #[error("DLT pipe full")]
186 PipeFull,
187
188 #[error("DLT pipe error")]
189 PipeError,
190
191 #[error("DLT general error")]
192 Error,
193
194 #[error("DLT unknown error")]
195 Unknown,
196}
197
198impl DltSysError {
199 fn from_return_code(code: i32) -> Result<(), Self> {
200 #[allow(unreachable_patterns)]
201 match code {
202 dlt_sys::DltReturnValue_DLT_RETURN_TRUE | dlt_sys::DltReturnValue_DLT_RETURN_OK => {
203 Ok(())
204 }
205 dlt_sys::DltReturnValue_DLT_RETURN_ERROR => Err(DltSysError::Error),
206 dlt_sys::DltReturnValue_DLT_RETURN_PIPE_ERROR => Err(DltSysError::PipeError),
207 dlt_sys::DltReturnValue_DLT_RETURN_PIPE_FULL => Err(DltSysError::PipeFull),
208 dlt_sys::DltReturnValue_DLT_RETURN_BUFFER_FULL => Err(DltSysError::BufferFull),
209 dlt_sys::DltReturnValue_DLT_RETURN_WRONG_PARAMETER => Err(DltSysError::WrongParameter),
210 dlt_sys::DltReturnValue_DLT_RETURN_USER_BUFFER_FULL => Err(DltSysError::UserBufferFull),
211 dlt_sys::DltReturnValue_DLT_RETURN_LOGGING_DISABLED => {
212 Err(DltSysError::LoggingDisabled)
213 }
214 dlt_sys::DltReturnValue_DLT_RETURN_FILESZERR => Err(DltSysError::FileSizeError),
215 #[cfg(feature = "trace_load_ctrl")]
216 dlt_sys::DltReturnValue_DLT_RETURN_LOAD_EXCEEDED => Err(DltSysError::LoadExceeded),
217 _ => Err(DltSysError::Unknown),
218 }
219 }
220}
221
222pub const DLT_ID_SIZE_USIZE: usize = DLT_ID_SIZE as usize;
224
225#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
230pub struct DltId {
231 bytes: [u8; DLT_ID_SIZE_USIZE],
232 len: usize,
233}
234
235impl DltId {
236 pub fn new(bytes: &[u8]) -> Result<Self, DltError> {
261 let len = bytes.len();
263 if bytes.is_empty() || len > DLT_ID_SIZE_USIZE {
264 return Err(DltError::InvalidInput);
265 }
266
267 if !bytes.is_ascii() {
269 return Err(DltError::InvalidInput);
270 }
271
272 let mut padded = [0u8; DLT_ID_SIZE_USIZE];
273 #[allow(clippy::indexing_slicing)]
275 padded[..len].copy_from_slice(&bytes[..len]);
276
277 Ok(Self { bytes: padded, len })
278 }
279
280 pub fn from_str_clamped(id: &str) -> Result<Self, DltError> {
293 if id.is_empty() {
294 return Err(DltError::InvalidInput);
295 }
296 let bytes = id.as_bytes();
297 let len = bytes.len().clamp(1, DLT_ID_SIZE_USIZE);
298
299 DltId::new(bytes.get(0..len).ok_or(DltError::InvalidInput)?)
300 }
301
302 pub fn as_str(&self) -> Result<&str, DltError> {
308 let slice = self
309 .bytes
310 .get(..self.len)
311 .ok_or_else(|| DltError::InvalidString("Invalid length".to_string()))?;
312 let s = std::str::from_utf8(slice).map_err(|e| DltError::InvalidString(e.to_string()))?;
313 Ok(s)
314 }
315}
316
317impl TryFrom<&str> for DltId {
319 type Error = DltError;
320
321 fn try_from(value: &str) -> Result<Self, Self::Error> {
322 let bytes = value.as_bytes();
323 if bytes.is_empty() || bytes.len() > DLT_ID_SIZE_USIZE {
324 return Err(DltError::InvalidInput);
325 }
326 let mut padded = [0u8; DLT_ID_SIZE_USIZE];
327 padded
328 .get_mut(..bytes.len())
329 .ok_or(DltError::InvalidInput)?
330 .copy_from_slice(bytes);
331 Ok(DltId {
332 bytes: padded,
333 len: bytes.len(),
334 })
335 }
336}
337
338#[derive(Debug, Clone, Copy, PartialEq, Eq)]
346pub enum DltTraceStatus {
347 Default,
349 Off,
351 On,
353}
354
355impl From<i32> for DltTraceStatus {
356 fn from(value: i32) -> Self {
357 match value {
358 dlt_sys::DltTraceStatusType_DLT_TRACE_STATUS_OFF => DltTraceStatus::Off,
359 dlt_sys::DltTraceStatusType_DLT_TRACE_STATUS_ON => DltTraceStatus::On,
360 _ => DltTraceStatus::Default,
361 }
362 }
363}
364
365impl From<DltTraceStatus> for i32 {
366 fn from(value: DltTraceStatus) -> Self {
367 match value {
368 DltTraceStatus::Default => dlt_sys::DltTraceStatusType_DLT_TRACE_STATUS_DEFAULT,
369 DltTraceStatus::Off => dlt_sys::DltTraceStatusType_DLT_TRACE_STATUS_OFF,
370 DltTraceStatus::On => dlt_sys::DltTraceStatusType_DLT_TRACE_STATUS_ON,
371 }
372 }
373}
374
375#[derive(Debug, Clone, Copy)]
382pub struct LogLevelChangedEvent {
383 pub context_id: DltId,
385 pub log_level: DltLogLevel,
387 pub trace_status: DltTraceStatus,
389}
390
391struct LogLevelChangedBroadcaster {
392 sender: broadcast::Sender<LogLevelChangedEvent>,
393 receiver: broadcast::Receiver<LogLevelChangedEvent>,
394}
395
396static CALLBACK_REGISTRY: OnceLock<RwLock<HashMap<DltId, LogLevelChangedBroadcaster>>> =
398 OnceLock::new();
399
400static APP_REGISTERED: AtomicBool = AtomicBool::new(false);
401
402unsafe extern "C" fn internal_log_level_callback(
404 context_id: *mut std::os::raw::c_char,
405 log_level: u8,
406 trace_status: u8,
407) {
408 if context_id.is_null() {
409 return;
410 }
411
412 let Some(registry) = CALLBACK_REGISTRY.get() else {
413 return;
414 };
415
416 let id = unsafe {
417 let mut ctx_id = [0u8; DLT_ID_SIZE_USIZE];
418 ptr::copy(
419 context_id.cast::<u8>(),
420 ctx_id.as_mut_ptr(),
421 DLT_ID_SIZE_USIZE,
422 );
423 match DltId::new(&ctx_id) {
424 Ok(id) => id,
425 Err(_) => return, }
427 };
428
429 let Ok(lock) = registry.read() else {
430 return;
431 };
432
433 let Some(broadcaster) = lock.get(&id) else {
434 return;
435 };
436
437 let event = LogLevelChangedEvent {
438 context_id: id,
439 log_level: DltLogLevel::from(i32::from(log_level)),
440 trace_status: DltTraceStatus::from(i32::from(trace_status)),
441 };
442
443 let _ = broadcaster.sender.send(event);
444}
445
446struct DltApplicationHandle {
452 _private: (),
453}
454
455impl Drop for DltApplicationHandle {
456 fn drop(&mut self) {
457 unsafe {
458 dlt_sys::unregisterApplicationFlushBufferedLogs();
460 dlt_sys::dltFree();
461 APP_REGISTERED.store(false, std::sync::atomic::Ordering::SeqCst);
462 }
463 }
464}
465
466pub struct DltApplication {
476 inner: Arc<DltApplicationHandle>,
477}
478
479impl DltApplication {
480 pub fn register(app_id: &DltId, app_description: &str) -> Result<Self, DltError> {
490 if APP_REGISTERED
491 .compare_exchange(
492 false,
493 true,
494 std::sync::atomic::Ordering::SeqCst,
495 std::sync::atomic::Ordering::SeqCst,
496 )
497 .is_err()
498 {
499 return Err(DltError::ApplicationRegistrationFailed(
500 "An application is already registered in this process".to_string(),
501 ));
502 }
503
504 let app_id_str = app_id.as_str()?;
505 let app_id_c = CString::new(app_id_str).map_err(|_| {
506 DltError::InvalidString("App id could not be converted to string".to_owned())
507 })?;
508 let app_desc_c = CString::new(app_description).map_err(|_| {
509 DltError::InvalidString("Context id could not be converted to string".to_owned())
510 })?;
511
512 unsafe {
513 let ret = dlt_sys::registerApplication(app_id_c.as_ptr(), app_desc_c.as_ptr());
514 DltSysError::from_return_code(ret).map_err(|_| {
515 DltError::ApplicationRegistrationFailed(format!(
516 "Failed to register application: {ret}"
517 ))
518 })?;
519 }
520 Ok(DltApplication {
521 inner: Arc::new(DltApplicationHandle { _private: () }),
522 })
523 }
524
525 pub fn create_context(
533 &self,
534 context_id: &DltId,
535 context_description: &str,
536 ) -> Result<DltContextHandle, DltError> {
537 DltContextHandle::new(context_id, context_description, Arc::clone(&self.inner))
538 }
539}
540
541impl Clone for DltApplication {
542 fn clone(&self) -> Self {
543 Self {
544 inner: Arc::clone(&self.inner),
545 }
546 }
547}
548
549unsafe impl Send for DltApplication {}
551unsafe impl Sync for DltApplication {}
552
553pub struct DltContextHandle {
558 context: *mut DltContext,
559 _app: Arc<DltApplicationHandle>,
560}
561
562impl DltContextHandle {
563 fn new(
568 context_id: &DltId,
569 context_description: &str,
570 app: Arc<DltApplicationHandle>,
571 ) -> Result<Self, DltError> {
572 let context_id_str = context_id.as_str()?;
573 let ctx_id_c = CString::new(context_id_str)
574 .map_err(|_| DltError::InvalidString("Context ID is not a valid string".to_owned()))?;
575 let ctx_desc_c = CString::new(context_description).map_err(|_| {
576 DltError::InvalidString("Context description is not a valid string".to_owned())
577 })?;
578
579 unsafe {
580 let mut context = Box::new(std::mem::zeroed::<DltContext>());
581 let rv =
582 dlt_sys::registerContext(ctx_id_c.as_ptr(), ctx_desc_c.as_ptr(), context.as_mut());
583 DltSysError::from_return_code(rv)
584 .map_err(|e| DltError::ContextRegistrationFailed(format!("{e}")))?;
585
586 Ok(DltContextHandle {
587 context: Box::into_raw(context),
588 _app: app,
589 })
590 }
591 }
592
593 fn raw_context(&self) -> Result<DltContext, DltError> {
594 let context = unsafe {
595 if self.context.is_null() {
596 return Err(DltError::ContextRegistrationFailed(
597 "Context pointer is null".to_string(),
598 ));
599 }
600 *self.context
601 };
602 Ok(context)
603 }
604
605 pub fn context_id(&self) -> Result<DltId, DltError> {
609 let ctx_id = unsafe {
610 #[allow(clippy::useless_transmute)]
614 std::mem::transmute::<[std::os::raw::c_char; 4], [u8; 4]>(self.raw_context()?.contextID)
615 };
616 DltId::new(&ctx_id)
617 }
618
619 #[must_use]
623 pub fn trace_status(&self) -> DltTraceStatus {
624 self.raw_context()
625 .ok()
626 .and_then(|rc| {
627 if rc.log_level_ptr.is_null() {
628 None
629 } else {
630 Some(DltTraceStatus::from(i32::from(unsafe {
631 *rc.trace_status_ptr
632 })))
633 }
634 })
635 .unwrap_or(DltTraceStatus::Default)
636 }
637
638 #[must_use]
640 pub fn log_level(&self) -> DltLogLevel {
641 self.raw_context()
642 .ok()
643 .and_then(|rc| {
644 if rc.log_level_ptr.is_null() {
645 None
646 } else {
647 Some(DltLogLevel::from(i32::from(unsafe { *rc.log_level_ptr })))
648 }
649 })
650 .unwrap_or(DltLogLevel::Default)
651 }
652
653 pub fn log(&self, log_level: DltLogLevel, message: &str) -> Result<(), DltSysError> {
658 let msg_c = CString::new(message).map_err(|_| DltSysError::WrongParameter)?;
659
660 unsafe {
661 let ret = dlt_sys::logDlt(self.context, log_level.into(), msg_c.as_ptr());
662 DltSysError::from_return_code(ret)
663 }
664 }
665
666 pub fn log_write_start_custom_timestamp(
674 &self,
675 log_level: DltLogLevel,
676 timestamp_microseconds: u64,
677 ) -> Result<DltLogWriter, DltSysError> {
678 let mut log_writer = self.log_write_start(log_level)?;
679 let timestamp =
681 u32::try_from(timestamp_microseconds / 100).map_err(|_| DltSysError::WrongParameter)?;
682 log_writer.log_data.use_timestamp = dlt_sys::DltTimestampType_DLT_USER_TIMESTAMP;
683 log_writer.log_data.user_timestamp = timestamp;
684 Ok(log_writer)
685 }
686
687 pub fn log_write_start(&self, log_level: DltLogLevel) -> Result<DltLogWriter, DltSysError> {
692 let mut log_data = DltContextData::default();
693
694 unsafe {
695 let ret =
696 dlt_sys::dltUserLogWriteStart(self.context, &raw mut log_data, log_level.into());
697
698 DltSysError::from_return_code(ret)?;
699 Ok(DltLogWriter { log_data })
700 }
701 }
702
703 pub fn register_log_level_changed_listener(
711 &self,
712 ) -> Result<broadcast::Receiver<LogLevelChangedEvent>, DltError> {
713 let rwlock = CALLBACK_REGISTRY.get_or_init(|| RwLock::new(HashMap::new()));
714 let mut guard = rwlock.write().map_err(|_| DltError::BadLock)?;
715 let ctx_id = self.context_id()?;
716
717 if let Some(broadcaster) = guard.get_mut(&ctx_id) {
718 Ok(broadcaster.receiver.resubscribe())
719 } else {
720 unsafe {
721 let ret = dlt_sys::registerLogLevelChangedCallback(
722 self.context,
723 Some(internal_log_level_callback),
724 );
725 DltSysError::from_return_code(ret)
726 .map_err(|e| DltError::LogLevelListenerRegistrationFailed(format!("{e}")))?;
727 }
728 let (tx, rx) = broadcast::channel(5);
729 let rx_clone = rx.resubscribe();
730 guard.insert(
731 ctx_id,
732 LogLevelChangedBroadcaster {
733 sender: tx,
734 receiver: rx,
735 },
736 );
737 Ok(rx_clone)
738 }
739 }
740}
741
742impl Drop for DltContextHandle {
743 fn drop(&mut self) {
744 let context_id = self.context_id();
745 if let Some(lock) = CALLBACK_REGISTRY.get()
746 && let Ok(mut guard) = lock.write()
747 && let Ok(ctx_id) = context_id
748 {
749 guard.remove(&ctx_id);
750 }
751
752 unsafe {
753 dlt_sys::unregisterContext(self.context);
754 let _ = Box::from_raw(self.context);
757 }
758 }
759}
760
761unsafe impl Send for DltContextHandle {}
763unsafe impl Sync for DltContextHandle {}
764
765pub struct DltLogWriter {
806 log_data: DltContextData,
807}
808
809impl DltLogWriter {
810 pub fn write_string(&mut self, text: &str) -> Result<&mut Self, DltSysError> {
815 let text_c = CString::new(text).map_err(|_| DltSysError::WrongParameter)?;
816
817 unsafe {
818 let ret = dlt_sys::dltUserLogWriteString(&raw mut self.log_data, text_c.as_ptr());
819 DltSysError::from_return_code(ret)?;
820 }
821
822 Ok(self)
823 }
824
825 pub fn write_u32(&mut self, value: u32) -> Result<&mut Self, DltSysError> {
830 unsafe {
831 let ret = dlt_sys::dltUserLogWriteUint(&raw mut self.log_data, value);
832 DltSysError::from_return_code(ret)?;
833 }
834
835 Ok(self)
836 }
837
838 pub fn write_i32(&mut self, value: i32) -> Result<&mut Self, DltSysError> {
843 unsafe {
844 let ret = dlt_sys::dltUserLogWriteInt(&raw mut self.log_data, value);
845 DltSysError::from_return_code(ret)?;
846 }
847
848 Ok(self)
849 }
850
851 pub fn write_uint64(&mut self, value: u64) -> Result<&mut Self, DltSysError> {
856 unsafe {
857 let ret = dlt_sys::dltUserLogWriteUint64(&raw mut self.log_data, value);
858 DltSysError::from_return_code(ret)?;
859 }
860
861 Ok(self)
862 }
863
864 pub fn write_int64(&mut self, value: i64) -> Result<&mut Self, DltSysError> {
869 unsafe {
870 let ret = dlt_sys::dltUserLogWriteInt64(&raw mut self.log_data, value);
871 DltSysError::from_return_code(ret)?;
872 }
873
874 Ok(self)
875 }
876
877 pub fn write_float32(&mut self, value: f32) -> Result<&mut Self, DltSysError> {
882 unsafe {
883 let ret = dlt_sys::dltUserLogWriteFloat32(&raw mut self.log_data, value);
884 DltSysError::from_return_code(ret)?;
885 }
886
887 Ok(self)
888 }
889
890 pub fn write_float64(&mut self, value: f64) -> Result<&mut Self, DltSysError> {
895 unsafe {
896 let ret = dlt_sys::dltUserLogWriteFloat64(&raw mut self.log_data, value);
897 DltSysError::from_return_code(ret)?;
898 }
899
900 Ok(self)
901 }
902
903 pub fn write_bool(&mut self, value: bool) -> Result<&mut Self, DltSysError> {
908 unsafe {
909 let ret = dlt_sys::dltUserLogWriteBool(&raw mut self.log_data, u8::from(value));
910 DltSysError::from_return_code(ret)?;
911 }
912
913 Ok(self)
914 }
915
916 pub fn finish(mut self) -> Result<(), DltSysError> {
924 let ret = unsafe { dlt_sys::dltUserLogWriteFinish(&raw mut self.log_data) };
925 std::mem::forget(self);
927 DltSysError::from_return_code(ret)
928 }
929}
930
931impl Drop for DltLogWriter {
932 fn drop(&mut self) {
933 unsafe {
935 let _ = dlt_sys::dltUserLogWriteFinish(&raw mut self.log_data);
936 }
937 }
938}
939
940#[cfg(test)]
941mod tests {
942 use super::*;
943
944 #[test]
945 fn test_dlt_error_from_return_code() {
946 assert!(DltSysError::from_return_code(0).is_ok());
947 assert!(DltSysError::from_return_code(1).is_ok());
948 assert_eq!(DltSysError::from_return_code(-1), Err(DltSysError::Error));
949 assert_eq!(
950 DltSysError::from_return_code(-5),
951 Err(DltSysError::WrongParameter)
952 );
953 }
954
955 #[test]
956 fn test_dlt_id_creation() {
957 let short_id = DltId::new(b"A").unwrap();
959 assert_eq!(short_id.as_str().unwrap(), "A");
960
961 let app_id = DltId::new(b"APP").unwrap();
963 assert_eq!(app_id.as_str().unwrap(), "APP");
964
965 let ctx_id = DltId::new(b"CTX").unwrap();
966 assert_eq!(ctx_id.as_str().unwrap(), "CTX");
967
968 let full_id = DltId::new(b"ABCD").unwrap();
970 assert_eq!(full_id.as_str().unwrap(), "ABCD");
971 }
972
973 #[test]
974 fn test_dlt_id_too_long() {
975 let result = DltId::new(b"TOOLONG");
976 assert_eq!(result.unwrap_err(), DltError::InvalidInput);
977 }
978
979 #[test]
980 fn test_dlt_id_empty() {
981 let result = DltId::new(b"");
982 assert_eq!(result.unwrap_err(), DltError::InvalidInput);
983 }
984
985 #[test]
986 fn test_dlt_id_non_ascii() {
987 let result = DltId::new(b"\xFF\xFE");
988 assert_eq!(result.unwrap_err(), DltError::InvalidInput);
989 }
990
991 #[test]
992 fn test_dlt_id_equality() {
993 let id1 = DltId::new(b"APP").unwrap();
994 let id2 = DltId::new(b"APP").unwrap();
995 let id3 = DltId::new(b"CTX").unwrap();
996
997 assert_eq!(id1, id2);
998 assert_ne!(id1, id3);
999
1000 let id4 = DltId::new(b"A").unwrap();
1002 assert_ne!(id1, id4);
1003 }
1004
1005 #[test]
1006 fn test_dlt_id_try_from_str() {
1007 let id = DltId::try_from("APP").unwrap();
1008 assert_eq!(id.as_str().unwrap(), "APP");
1009
1010 let long_id_result = DltId::try_from("TOOLONG");
1011 assert_eq!(long_id_result.unwrap_err(), DltError::InvalidInput);
1012
1013 let empty_id_result = DltId::try_from("");
1014 assert_eq!(empty_id_result.unwrap_err(), DltError::InvalidInput);
1015 }
1016}