Skip to main content

orion_error/core/
context.rs

1#[cfg(all(feature = "log", not(feature = "tracing")))]
2use log::{debug, error, info, trace, warn};
3use std::{
4    fmt::Display,
5    ops::{Deref, DerefMut},
6    path::{Path, PathBuf},
7};
8
9use super::metadata::{ErrorMetadata, MetadataValue};
10#[derive(Debug, Clone, PartialEq, Default)]
11#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
12pub enum OperationResult {
13    Suc,
14    #[default]
15    Fail,
16    Cancel,
17}
18
19// 使用编译期模块路径作为默认日志 target,以提升可读性
20const DEFAULT_MOD_PATH: &str = module_path!();
21
22/// 在调用处展开 `module_path!()`,便于自动日志输出正确的模块路径。
23#[macro_export]
24macro_rules! op_context {
25    ($target:expr) => {
26        $crate::OperationContext::want($target).with_mod_path(module_path!())
27    };
28}
29
30#[derive(Debug, Clone, PartialEq)]
31#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
32pub struct OperationContext {
33    context: CallContext,
34    result: OperationResult,
35    exit_log: bool,
36    mod_path: String,
37    target: Option<String>,
38    #[cfg_attr(feature = "serde", serde(default))]
39    path: Vec<String>,
40    #[cfg_attr(feature = "serde", serde(default))]
41    #[cfg_attr(
42        feature = "serde",
43        serde(skip_serializing_if = "ErrorMetadata::is_empty")
44    )]
45    metadata: ErrorMetadata,
46}
47impl Default for OperationContext {
48    fn default() -> Self {
49        Self {
50            context: CallContext::default(),
51            target: None,
52            path: Vec::new(),
53            result: OperationResult::Fail,
54            exit_log: false,
55            mod_path: DEFAULT_MOD_PATH.into(),
56            metadata: ErrorMetadata::default(),
57        }
58    }
59}
60pub type WithContext = OperationContext;
61impl From<CallContext> for OperationContext {
62    fn from(value: CallContext) -> Self {
63        OperationContext {
64            context: value,
65            result: OperationResult::Fail,
66            target: None,
67            path: Vec::new(),
68            exit_log: false,
69            mod_path: DEFAULT_MOD_PATH.into(),
70            metadata: ErrorMetadata::default(),
71        }
72    }
73}
74
75impl Drop for OperationContext {
76    fn drop(&mut self) {
77        if !self.exit_log {
78            return;
79        }
80
81        #[cfg(feature = "tracing")]
82        {
83            let ctx = self.format_context();
84            match self.result() {
85                OperationResult::Suc => {
86                    tracing::info!(
87                        target: "domain",
88                        mod_path = %self.mod_path,
89                        "suc! {ctx}"
90                    )
91                }
92                OperationResult::Fail => {
93                    tracing::error!(
94                        target: "domain",
95                        mod_path = %self.mod_path,
96                        "fail! {ctx}"
97                    )
98                }
99                OperationResult::Cancel => {
100                    tracing::warn!(
101                        target: "domain",
102                        mod_path = %self.mod_path,
103                        "cancel! {ctx}"
104                    )
105                }
106            }
107        }
108
109        #[cfg(all(feature = "log", not(feature = "tracing")))]
110        {
111            match self.result() {
112                OperationResult::Suc => {
113                    info!(target: self.mod_path.as_str(), "suc! {}", self.format_context());
114                }
115                OperationResult::Fail => {
116                    error!(target: self.mod_path.as_str(), "fail! {}", self.format_context());
117                }
118                OperationResult::Cancel => {
119                    warn!(target: self.mod_path.as_str(), "cancel! {}", self.format_context());
120                }
121            }
122        }
123    }
124}
125
126impl Display for OperationContext {
127    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
128        if let Some(target) = &self.target {
129            writeln!(f, "want: {target}")?;
130        }
131        if let Some(path) = self.path_string() {
132            if self.target.as_deref() != Some(path.as_str()) {
133                writeln!(f, "path: {path}")?;
134            }
135        }
136        for (i, (k, v)) in self.context().items.iter().enumerate() {
137            writeln!(f, "{}. {k}: {v} ", i + 1)?;
138        }
139        Ok(())
140    }
141}
142pub trait ContextRecord<S1, S2> {
143    fn record(&mut self, key: S1, val: S2);
144}
145
146impl<S1> ContextRecord<S1, String> for OperationContext
147where
148    S1: Into<String>,
149{
150    fn record(&mut self, key: S1, val: String) {
151        self.context.items.push((key.into(), val));
152    }
153}
154
155impl<S1> ContextRecord<S1, &str> for OperationContext
156where
157    S1: Into<String>,
158{
159    fn record(&mut self, key: S1, val: &str) {
160        self.context.items.push((key.into(), val.into()));
161    }
162}
163
164// Wrapper type for path values to avoid conflicts
165
166impl<S1> ContextRecord<S1, &PathBuf> for OperationContext
167where
168    S1: Into<String>,
169{
170    fn record(&mut self, key: S1, val: &PathBuf) {
171        self.context
172            .items
173            .push((key.into(), format!("{}", val.display())));
174    }
175}
176impl<S1> ContextRecord<S1, &Path> for OperationContext
177where
178    S1: Into<String>,
179{
180    fn record(&mut self, key: S1, val: &Path) {
181        self.context
182            .items
183            .push((key.into(), format!("{}", val.display())));
184    }
185}
186
187impl OperationContext {
188    pub fn context(&self) -> &CallContext {
189        &self.context
190    }
191
192    pub fn result(&self) -> &OperationResult {
193        &self.result
194    }
195
196    pub fn exit_log(&self) -> &bool {
197        &self.exit_log
198    }
199
200    pub fn mod_path(&self) -> &String {
201        &self.mod_path
202    }
203
204    pub fn target(&self) -> &Option<String> {
205        &self.target
206    }
207
208    pub fn path(&self) -> &[String] {
209        &self.path
210    }
211
212    pub fn metadata(&self) -> &ErrorMetadata {
213        &self.metadata
214    }
215
216    pub fn new() -> Self {
217        Self {
218            target: None,
219            path: Vec::new(),
220            context: CallContext::default(),
221            result: OperationResult::Fail,
222            exit_log: false,
223            mod_path: DEFAULT_MOD_PATH.into(),
224            metadata: ErrorMetadata::default(),
225        }
226    }
227    pub fn want<S: Into<String>>(target: S) -> Self {
228        let target = target.into();
229        Self {
230            target: Some(target.clone()),
231            path: vec![target],
232            context: CallContext::default(),
233            result: OperationResult::Fail,
234            exit_log: false,
235            mod_path: DEFAULT_MOD_PATH.into(),
236            metadata: ErrorMetadata::default(),
237        }
238    }
239    #[deprecated(since = "0.5.4", note = "use with_auto_log")]
240    pub fn with_exit_log(mut self) -> Self {
241        self.exit_log = true;
242        self
243    }
244    pub fn with_auto_log(mut self) -> Self {
245        self.exit_log = true;
246        self
247    }
248    pub fn with_mod_path<S: Into<String>>(mut self, path: S) -> Self {
249        self.mod_path = path.into();
250        self
251    }
252    #[deprecated(since = "0.5.4", note = "use record")]
253    pub fn with<S1: Into<String>, S2: Into<String>>(&mut self, key: S1, val: S2) {
254        self.context.items.push((key.into(), val.into()));
255    }
256
257    #[deprecated(since = "0.5.4", note = "use record")]
258    pub fn with_path<S1: Into<String>, S2: Into<PathBuf>>(&mut self, key: S1, val: S2) {
259        self.context
260            .items
261            .push((key.into(), format!("{}", val.into().display())));
262    }
263
264    pub fn with_want<S: Into<String>>(&mut self, target: S) {
265        let target = target.into();
266        if target.is_empty() {
267            return;
268        }
269
270        if let Some(root) = &self.target {
271            if self.path.is_empty() {
272                self.path.push(root.clone());
273            }
274            if self.path.last() != Some(&target) {
275                self.path.push(target);
276            }
277        } else {
278            self.target = Some(target.clone());
279            self.path.push(target);
280        }
281    }
282    /// 别名:设置目标资源/操作名;首次设置根 want,后续调用仅追加 path
283    pub fn set_target<S: Into<String>>(&mut self, target: S) {
284        self.with_want(target)
285    }
286
287    pub fn path_string(&self) -> Option<String> {
288        if self.path.is_empty() {
289            None
290        } else {
291            Some(self.path.join(" / "))
292        }
293    }
294
295    pub fn record_meta<K, V>(&mut self, key: K, value: V)
296    where
297        K: Into<String>,
298        V: Into<MetadataValue>,
299    {
300        self.metadata.insert(key, value);
301    }
302
303    pub fn with_meta<K, V>(mut self, key: K, value: V) -> Self
304    where
305        K: Into<String>,
306        V: Into<MetadataValue>,
307    {
308        self.record_meta(key, value);
309        self
310    }
311
312    pub fn mark_suc(&mut self) {
313        self.result = OperationResult::Suc;
314    }
315    pub fn mark_cancel(&mut self) {
316        self.result = OperationResult::Cancel;
317    }
318
319    /// 格式化上下文信息,用于日志输出
320    #[cfg_attr(not(any(feature = "log", feature = "tracing")), allow(dead_code))]
321    fn format_context(&self) -> String {
322        let want = self.target.clone().unwrap_or_default();
323        let path = self.path_string().unwrap_or_default();
324        let head = match (want.is_empty(), path.is_empty() || path == want) {
325            (true, true) => String::new(),
326            (false, true) => format!("want={want}"),
327            (false, false) => format!("want={want} path={path}"),
328            (true, false) => format!("path={path}"),
329        };
330        if self.context.items.is_empty() {
331            return head;
332        }
333        if head.is_empty() {
334            let body = self.context.to_string();
335            body.strip_prefix('\n').unwrap_or(&body).to_string()
336        } else {
337            format!("{head}: {}", self.context)
338        }
339    }
340
341    /// 创建作用域 guard,默认为失败状态,需显式 `mark_success()`
342    pub fn scope(&mut self) -> OperationScope<'_> {
343        OperationScope {
344            ctx: self,
345            mark_success: false,
346        }
347    }
348
349    /// 创建作用域 guard,在作用域结束时自动标记成功
350    pub fn scoped_success(&mut self) -> OperationScope<'_> {
351        OperationScope {
352            ctx: self,
353            mark_success: true,
354        }
355    }
356
357    /// 记录日志信息,在无错误情况下也可以提供有价值的上下文信息
358    /// 注意:需要启用 `log` 或 `tracing` 特性
359    #[cfg(feature = "tracing")]
360    pub fn info<S: AsRef<str>>(&self, message: S) {
361        tracing::info!(
362            target: "domain",
363            mod_path = %self.mod_path,
364            "{}: {}",
365            self.format_context(),
366            message.as_ref()
367        );
368    }
369    #[cfg(all(feature = "log", not(feature = "tracing")))]
370    pub fn info<S: AsRef<str>>(&self, message: S) {
371        info!(target: self.mod_path.as_str(), "{}: {}", self.format_context(), message.as_ref());
372    }
373    #[cfg(not(any(feature = "log", feature = "tracing")))]
374    pub fn info<S: AsRef<str>>(&self, _message: S) {}
375
376    #[cfg(feature = "tracing")]
377    pub fn debug<S: AsRef<str>>(&self, message: S) {
378        tracing::debug!(
379            target: "domain",
380            mod_path = %self.mod_path,
381            "{}: {}",
382            self.format_context(),
383            message.as_ref()
384        );
385    }
386    #[cfg(all(feature = "log", not(feature = "tracing")))]
387    pub fn debug<S: AsRef<str>>(&self, message: S) {
388        debug!( target: self.mod_path.as_str(), "{}: {}", self.format_context(), message.as_ref());
389    }
390    #[cfg(not(any(feature = "log", feature = "tracing")))]
391    pub fn debug<S: AsRef<str>>(&self, _message: S) {}
392
393    #[cfg(feature = "tracing")]
394    pub fn warn<S: AsRef<str>>(&self, message: S) {
395        tracing::warn!(
396            target: "domain",
397            mod_path = %self.mod_path,
398            "{}: {}",
399            self.format_context(),
400            message.as_ref()
401        );
402    }
403    #[cfg(all(feature = "log", not(feature = "tracing")))]
404    pub fn warn<S: AsRef<str>>(&self, message: S) {
405        warn!( target: self.mod_path.as_str(), "{}: {}", self.format_context(), message.as_ref());
406    }
407    #[cfg(not(any(feature = "log", feature = "tracing")))]
408    pub fn warn<S: AsRef<str>>(&self, _message: S) {}
409
410    #[cfg(feature = "tracing")]
411    pub fn error<S: AsRef<str>>(&self, message: S) {
412        tracing::error!(
413            target: "domain",
414            mod_path = %self.mod_path,
415            "{}: {}",
416            self.format_context(),
417            message.as_ref()
418        );
419    }
420    #[cfg(all(feature = "log", not(feature = "tracing")))]
421    pub fn error<S: AsRef<str>>(&self, message: S) {
422        error!(target: self.mod_path.as_str(), "{}: {}", self.format_context(), message.as_ref());
423    }
424    #[cfg(not(any(feature = "log", feature = "tracing")))]
425    pub fn error<S: AsRef<str>>(&self, _message: S) {}
426
427    #[cfg(feature = "tracing")]
428    pub fn trace<S: AsRef<str>>(&self, message: S) {
429        tracing::trace!(
430            target: "domain",
431            mod_path = %self.mod_path,
432            "{}: {}",
433            self.format_context(),
434            message.as_ref()
435        );
436    }
437    #[cfg(all(feature = "log", not(feature = "tracing")))]
438    pub fn trace<S: AsRef<str>>(&self, message: S) {
439        trace!( target: self.mod_path.as_str(), "{}: {}", self.format_context(), message.as_ref());
440    }
441    #[cfg(not(any(feature = "log", feature = "tracing")))]
442    pub fn trace<S: AsRef<str>>(&self, _message: S) {}
443
444    /// 与文档示例一致的别名方法(调用上面的同名方法)
445    pub fn log_info<S: AsRef<str>>(&self, message: S) {
446        self.info(message)
447    }
448    pub fn log_debug<S: AsRef<str>>(&self, message: S) {
449        self.debug(message)
450    }
451    pub fn log_warn<S: AsRef<str>>(&self, message: S) {
452        self.warn(message)
453    }
454    pub fn log_error<S: AsRef<str>>(&self, message: S) {
455        self.error(message)
456    }
457    pub fn log_trace<S: AsRef<str>>(&self, message: S) {
458        self.trace(message)
459    }
460
461    pub(crate) fn context_mut_for_report(&mut self) -> &mut CallContext {
462        &mut self.context
463    }
464
465    pub(crate) fn replace_target_for_report(&mut self, target: Option<String>) {
466        self.target = target;
467    }
468
469    pub(crate) fn replace_path_for_report(&mut self, path: Vec<String>) {
470        self.path = path;
471    }
472
473    pub(crate) fn replace_metadata_for_report(&mut self, metadata: ErrorMetadata) {
474        self.metadata = metadata;
475    }
476}
477
478pub struct OperationScope<'a> {
479    ctx: &'a mut OperationContext,
480    mark_success: bool,
481}
482
483impl<'a> OperationScope<'a> {
484    /// 显式标记成功
485    pub fn mark_success(&mut self) {
486        self.mark_success = true;
487    }
488
489    /// 保持失败状态(默认行为)
490    pub fn mark_failure(&mut self) {
491        self.mark_success = false;
492    }
493
494    /// 标记为取消并阻止成功写入
495    pub fn cancel(&mut self) {
496        self.ctx.mark_cancel();
497        self.mark_success = false;
498    }
499}
500
501impl<'a> Deref for OperationScope<'a> {
502    type Target = OperationContext;
503
504    fn deref(&self) -> &Self::Target {
505        self.ctx
506    }
507}
508
509impl<'a> DerefMut for OperationScope<'a> {
510    fn deref_mut(&mut self) -> &mut Self::Target {
511        self.ctx
512    }
513}
514
515impl Drop for OperationScope<'_> {
516    fn drop(&mut self) {
517        if self.mark_success {
518            self.ctx.mark_suc();
519        }
520    }
521}
522
523impl From<String> for OperationContext {
524    fn from(value: String) -> Self {
525        Self {
526            target: None,
527            path: Vec::new(),
528            context: CallContext::from(("key", value.to_string())),
529            result: OperationResult::Fail,
530            exit_log: false,
531            mod_path: DEFAULT_MOD_PATH.into(),
532            metadata: ErrorMetadata::default(),
533        }
534    }
535}
536
537impl From<&PathBuf> for OperationContext {
538    fn from(value: &PathBuf) -> Self {
539        Self {
540            target: None,
541            path: Vec::new(),
542            context: CallContext::from(("path", format!("{}", value.display()))),
543            result: OperationResult::Fail,
544            exit_log: false,
545            mod_path: DEFAULT_MOD_PATH.into(),
546            metadata: ErrorMetadata::default(),
547        }
548    }
549}
550
551impl From<&Path> for OperationContext {
552    fn from(value: &Path) -> Self {
553        Self {
554            target: None,
555            path: Vec::new(),
556            context: CallContext::from(("path", format!("{}", value.display()))),
557            result: OperationResult::Fail,
558            exit_log: false,
559            mod_path: DEFAULT_MOD_PATH.into(),
560            metadata: ErrorMetadata::default(),
561        }
562    }
563}
564
565impl From<&str> for OperationContext {
566    fn from(value: &str) -> Self {
567        Self {
568            target: None,
569            path: Vec::new(),
570            context: CallContext::from(("key", value.to_string())),
571            result: OperationResult::Fail,
572            exit_log: false,
573            mod_path: DEFAULT_MOD_PATH.into(),
574            metadata: ErrorMetadata::default(),
575        }
576    }
577}
578
579impl From<(&str, &str)> for OperationContext {
580    fn from(value: (&str, &str)) -> Self {
581        Self {
582            target: None,
583            path: Vec::new(),
584            context: CallContext::from((value.0, value.1)),
585            result: OperationResult::Fail,
586            exit_log: false,
587            mod_path: DEFAULT_MOD_PATH.into(),
588            metadata: ErrorMetadata::default(),
589        }
590    }
591}
592
593impl From<(&str, String)> for OperationContext {
594    fn from(value: (&str, String)) -> Self {
595        Self {
596            target: None,
597            path: Vec::new(),
598            context: CallContext::from((value.0, value.1)),
599            result: OperationResult::Fail,
600            exit_log: false,
601            mod_path: DEFAULT_MOD_PATH.into(),
602            metadata: ErrorMetadata::default(),
603        }
604    }
605}
606// Marker trait to exclude types that are already covered by other implementations
607trait NotAsRefStr: AsRef<Path> {}
608
609// Implement for concrete path types but not for &str
610impl NotAsRefStr for PathBuf {}
611impl NotAsRefStr for Path {}
612impl<T: AsRef<Path> + ?Sized> NotAsRefStr for &T where T: NotAsRefStr {}
613
614impl<V: AsRef<Path>> From<(&str, V)> for OperationContext
615where
616    V: NotAsRefStr,
617{
618    fn from(value: (&str, V)) -> Self {
619        Self {
620            target: None,
621            path: Vec::new(),
622            context: CallContext {
623                items: vec![(
624                    value.0.to_string(),
625                    format!("{}", value.1.as_ref().display()),
626                )],
627            },
628            result: OperationResult::Fail,
629            exit_log: false,
630            mod_path: DEFAULT_MOD_PATH.into(),
631            metadata: ErrorMetadata::default(),
632        }
633    }
634}
635
636impl From<(String, String)> for OperationContext {
637    fn from(value: (String, String)) -> Self {
638        Self {
639            target: None,
640            path: Vec::new(),
641            context: CallContext::from((value.0, value.1)),
642            result: OperationResult::Fail,
643            exit_log: false,
644            mod_path: DEFAULT_MOD_PATH.into(),
645            metadata: ErrorMetadata::default(),
646        }
647    }
648}
649
650impl From<&OperationContext> for OperationContext {
651    fn from(value: &OperationContext) -> Self {
652        value.clone()
653    }
654}
655
656#[derive(Default, Debug, Clone, PartialEq)]
657#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
658pub struct CallContext {
659    pub items: Vec<(String, String)>,
660}
661
662impl<K: AsRef<str>, V: AsRef<str>> From<(K, V)> for CallContext {
663    fn from(value: (K, V)) -> Self {
664        Self {
665            items: vec![(value.0.as_ref().to_string(), value.1.as_ref().to_string())],
666        }
667    }
668}
669
670pub trait ContextAdd<T> {
671    fn add_context(&mut self, val: T);
672}
673
674impl<K: Into<String>> ContextAdd<(K, String)> for OperationContext {
675    fn add_context(&mut self, val: (K, String)) {
676        self.record(val.0.into(), val.1);
677    }
678}
679impl<K: Into<String>> ContextAdd<(K, &String)> for OperationContext {
680    fn add_context(&mut self, val: (K, &String)) {
681        self.record(val.0.into(), val.1.clone());
682    }
683}
684impl<K: Into<String>> ContextAdd<(K, &str)> for OperationContext {
685    fn add_context(&mut self, val: (K, &str)) {
686        self.record(val.0.into(), val.1.to_string());
687    }
688}
689
690impl<K: Into<String>> ContextAdd<(K, &PathBuf)> for OperationContext {
691    fn add_context(&mut self, val: (K, &PathBuf)) {
692        self.record(val.0.into(), format!("{}", val.1.display()));
693    }
694}
695impl<K: Into<String>> ContextAdd<(K, &Path)> for OperationContext {
696    fn add_context(&mut self, val: (K, &Path)) {
697        self.record(val.0.into(), format!("{}", val.1.display()));
698    }
699}
700
701impl Display for CallContext {
702    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
703        if !self.items.is_empty() {
704            writeln!(f, "\ncall context:")?;
705        }
706        for (k, v) in &self.items {
707            writeln!(f, "\t{k} : {v}")?;
708        }
709        Ok(())
710    }
711}
712
713#[cfg(test)]
714mod tests {
715    use super::*;
716    use std::path::PathBuf;
717
718    #[test]
719    fn test_op_context_macro_sets_callsite_mod_path() {
720        let ctx = crate::op_context!("macro_target");
721        assert_eq!(*ctx.target(), Some("macro_target".to_string()));
722        assert_eq!(ctx.mod_path().as_str(), module_path!());
723    }
724
725    #[test]
726    fn test_withcontext_new() {
727        let ctx = OperationContext::new();
728        assert!(ctx.target.is_none());
729        assert_eq!(ctx.context().items.len(), 0);
730    }
731
732    #[test]
733    fn test_withcontext_want() {
734        let ctx = OperationContext::want("test_target");
735        assert_eq!(*ctx.target(), Some("test_target".to_string()));
736        assert_eq!(ctx.path(), &["test_target".to_string()]);
737        assert_eq!(ctx.context().items.len(), 0);
738    }
739
740    #[test]
741    fn test_withcontext_with() {
742        let mut ctx = OperationContext::new();
743        ctx.record("key1", "value1");
744        ctx.record("key2", "value2");
745
746        assert_eq!(ctx.context().items.len(), 2);
747        assert_eq!(
748            ctx.context().items[0],
749            ("key1".to_string(), "value1".to_string())
750        );
751        assert_eq!(
752            ctx.context().items[1],
753            ("key2".to_string(), "value2".to_string())
754        );
755    }
756
757    #[test]
758    fn test_withcontext_with_path() {
759        let mut ctx = OperationContext::new();
760        let path = PathBuf::from("/test/path");
761        ctx.record("file_path", &path);
762
763        assert_eq!(ctx.context().items.len(), 1);
764        assert!(ctx.context().items[0].1.contains("/test/path"));
765    }
766
767    #[test]
768    fn test_withcontext_with_want() {
769        let mut ctx = OperationContext::new();
770        ctx.with_want("new_target");
771
772        assert_eq!(*ctx.target(), Some("new_target".to_string()));
773        assert_eq!(ctx.path(), &["new_target".to_string()]);
774    }
775
776    #[test]
777    fn test_withcontext_with_want_appends_path() {
778        let mut ctx = OperationContext::want("place_order");
779        ctx.with_want("read_order_payload");
780        ctx.with_want("parse_order");
781
782        assert_eq!(*ctx.target(), Some("place_order".to_string()));
783        assert_eq!(
784            ctx.path(),
785            &[
786                "place_order".to_string(),
787                "read_order_payload".to_string(),
788                "parse_order".to_string()
789            ]
790        );
791        assert_eq!(
792            ctx.path_string().as_deref(),
793            Some("place_order / read_order_payload / parse_order")
794        );
795    }
796
797    #[test]
798    fn test_errcontext_from_string() {
799        let ctx = CallContext::from(("key".to_string(), "test_string".to_string()));
800        assert_eq!(ctx.items.len(), 1);
801        assert_eq!(ctx.items[0], ("key".to_string(), "test_string".to_string()));
802    }
803
804    #[test]
805    fn test_errcontext_from_str() {
806        let ctx = CallContext::from(("key", "test_str"));
807        assert_eq!(ctx.items.len(), 1);
808        assert_eq!(ctx.items[0], ("key".to_string(), "test_str".to_string()));
809    }
810
811    #[test]
812    fn test_errcontext_from_string_pair() {
813        let ctx = CallContext::from(("key1".to_string(), "value1".to_string()));
814        assert_eq!(ctx.items.len(), 1);
815        assert_eq!(ctx.items[0], ("key1".to_string(), "value1".to_string()));
816    }
817
818    #[test]
819    fn test_errcontext_from_str_pair() {
820        let ctx = CallContext::from(("key1", "value1"));
821        assert_eq!(ctx.items.len(), 1);
822        assert_eq!(ctx.items[0], ("key1".to_string(), "value1".to_string()));
823    }
824
825    #[test]
826    fn test_errcontext_from_mixed_pair() {
827        let ctx = CallContext::from(("key1", "value1".to_string()));
828        assert_eq!(ctx.items.len(), 1);
829        assert_eq!(ctx.items[0], ("key1".to_string(), "value1".to_string()));
830    }
831
832    #[test]
833    fn test_errcontext_default() {
834        let ctx = CallContext::default();
835        assert_eq!(ctx.items.len(), 0);
836    }
837
838    #[test]
839    fn test_errcontext_display_single() {
840        let ctx = CallContext::from(("key", "test"));
841        let display = format!("{ctx}");
842        assert!(display.contains("call context:"));
843        assert!(display.contains("key : test"));
844    }
845
846    #[test]
847    fn test_errcontext_display_multiple() {
848        let mut ctx = CallContext::default();
849        ctx.items.push(("key1".to_string(), "value1".to_string()));
850        ctx.items.push(("key2".to_string(), "value2".to_string()));
851        let display = format!("{ctx}");
852        assert!(display.contains("call context:"));
853        assert!(display.contains("key1 : value1"));
854        assert!(display.contains("key2 : value2"));
855    }
856
857    #[test]
858    fn test_errcontext_display_empty() {
859        let ctx = CallContext::default();
860        let display = format!("{ctx}");
861        assert_eq!(display, "");
862    }
863
864    #[test]
865    fn test_withcontext_from_string() {
866        let ctx = OperationContext::from("test_string".to_string());
867        assert!(ctx.target.is_none());
868        assert_eq!(ctx.context().items.len(), 1);
869        assert_eq!(
870            ctx.context().items[0],
871            ("key".to_string(), "test_string".to_string())
872        );
873    }
874
875    #[test]
876    fn test_withcontext_from_str() {
877        let ctx = OperationContext::from("test_str".to_string());
878        assert!(ctx.target.is_none());
879        assert_eq!(ctx.context().items.len(), 1);
880        assert_eq!(
881            ctx.context().items[0],
882            ("key".to_string(), "test_str".to_string())
883        );
884    }
885
886    #[test]
887    fn test_withcontext_from_pathbuf() {
888        let path = PathBuf::from("/test/path");
889        let ctx = OperationContext::from(&path);
890        assert!(ctx.target.is_none());
891        assert_eq!(ctx.context().items.len(), 1);
892        assert!(ctx.context().items[0].1.contains("/test/path"));
893    }
894
895    #[test]
896    fn test_withcontext_from_path() {
897        let path = "/test/path";
898        let ctx = OperationContext::from(path);
899        assert!(ctx.target.is_none());
900        assert_eq!(ctx.context().items.len(), 1);
901        assert!(ctx.context().items[0].1.contains("/test/path"));
902    }
903
904    #[test]
905    fn test_withcontext_from_string_pair() {
906        let ctx = OperationContext::from(("key1".to_string(), "value1".to_string()));
907        assert!(ctx.target.is_none());
908        assert_eq!(ctx.context().items.len(), 1);
909        assert_eq!(
910            ctx.context().items[0],
911            ("key1".to_string(), "value1".to_string())
912        );
913    }
914
915    #[test]
916    fn test_withcontext_from_str_pair() {
917        let ctx = OperationContext::from(("key1", "value1"));
918        assert!(ctx.target.is_none());
919        assert_eq!(ctx.context().items.len(), 1);
920        assert_eq!(
921            ctx.context().items[0],
922            ("key1".to_string(), "value1".to_string())
923        );
924    }
925
926    #[test]
927    fn test_withcontext_from_mixed_pair() {
928        let ctx = OperationContext::from(("key1", "value1".to_string()));
929        assert!(ctx.target.is_none());
930        assert_eq!(ctx.context().items.len(), 1);
931        assert_eq!(
932            ctx.context().items[0],
933            ("key1".to_string(), "value1".to_string())
934        );
935    }
936
937    #[test]
938    fn test_withcontext_from_path_pair() {
939        let path = PathBuf::from("/test/path");
940        let ctx = OperationContext::from(("file", path.to_string_lossy().as_ref()));
941        assert!(ctx.target.is_none());
942        assert_eq!(ctx.context().items.len(), 1);
943        assert!(ctx.context().items[0].0.contains("file"));
944        assert!(ctx.context().items[0].1.contains("/test/path"));
945    }
946
947    #[test]
948    fn test_withcontext_display_with_target() {
949        let mut ctx = OperationContext::want("test_target");
950        ctx.record("key1", "value1");
951        let display = format!("{ctx}");
952        assert!(display.contains("want: test_target"));
953        assert!(display.contains("1. key1: value1"));
954    }
955
956    #[test]
957    fn test_withcontext_display_without_target() {
958        let mut ctx = OperationContext::new();
959        ctx.record("key1", "value1");
960        let display = format!("{ctx}");
961        assert!(!display.contains("target:"));
962        assert!(display.contains("1. key1: value1"));
963    }
964
965    #[test]
966    fn test_withcontext_from_errcontext() {
967        let err_ctx = CallContext::from(("key1", "value1"));
968        let ctx = OperationContext::from(err_ctx);
969        assert!(ctx.target.is_none());
970        assert_eq!(ctx.context().items.len(), 1);
971        assert_eq!(
972            ctx.context().items[0],
973            ("key1".to_string(), "value1".to_string())
974        );
975    }
976
977    #[test]
978    fn test_withcontext_from_withcontext() {
979        let mut ctx1 = OperationContext::want("target1");
980        ctx1.record("key1", "value1");
981        ctx1.with_want("step1");
982        let ctx2 = OperationContext::from(&ctx1);
983        assert_eq!(*ctx2.target(), Some("target1".to_string()));
984        assert_eq!(ctx2.path(), &["target1".to_string(), "step1".to_string()]);
985        assert_eq!(ctx2.context().items.len(), 1);
986        assert_eq!(
987            ctx2.context().items[0],
988            ("key1".to_string(), "value1".to_string())
989        );
990    }
991
992    #[test]
993    fn test_withcontext_from_str_path_pair() {
994        let path = PathBuf::from("/test/path");
995        let ctx = OperationContext::from(("file", &path));
996        assert_eq!(ctx.context().items.len(), 1);
997        assert_eq!(ctx.context().items[0].0, "file");
998        assert!(ctx.context().items[0].1.contains("/test/path"));
999    }
1000
1001    #[test]
1002    fn test_withcontext_from_str_pathbuf_pair() {
1003        let path = PathBuf::from("/test/pathbuf");
1004        let ctx = OperationContext::from(("file", path));
1005        assert_eq!(ctx.context().items.len(), 1);
1006        assert_eq!(ctx.context().items[0].0, "file");
1007        assert!(ctx.context().items[0].1.contains("/test/pathbuf"));
1008    }
1009
1010    // ContextAdd trait tests are commented out due to trait implementation issues
1011    // These tests will be revisited when the ContextAdd trait is properly implemented
1012
1013    #[test]
1014    fn test_withcontext_edge_cases() {
1015        let ctx1 = OperationContext::from("".to_string());
1016        assert_eq!(ctx1.context().items.len(), 1);
1017        assert_eq!(ctx1.context().items[0], ("key".to_string(), "".to_string()));
1018
1019        let ctx2 = OperationContext::from(("".to_string(), "".to_string()));
1020        assert_eq!(ctx2.context().items.len(), 1);
1021        assert_eq!(ctx2.context().items[0], ("".to_string(), "".to_string()));
1022    }
1023
1024    #[test]
1025    fn test_errcontext_equality() {
1026        let ctx1 = CallContext::from(("key1", "value1"));
1027        let ctx2 = CallContext::from(("key1", "value1"));
1028        let ctx3 = CallContext::from(("key1", "value2"));
1029
1030        assert_eq!(ctx1, ctx2);
1031        assert_ne!(ctx1, ctx3);
1032    }
1033
1034    #[test]
1035    fn test_withcontext_equality() {
1036        let ctx1 = OperationContext::from(("key1", "value1"));
1037        let ctx2 = OperationContext::from(("key1", "value1"));
1038        let ctx3 = OperationContext::from(("key1", "value2"));
1039
1040        assert_eq!(ctx1, ctx2);
1041        assert_ne!(ctx1, ctx3);
1042    }
1043
1044    #[test]
1045    fn test_withcontext_clone() {
1046        let mut ctx = OperationContext::want("target");
1047        ctx.record("key", "value");
1048
1049        let cloned = ctx.clone();
1050        assert_eq!(ctx.target(), cloned.target());
1051        assert_eq!(ctx.context().items.len(), cloned.context().items.len());
1052        assert_eq!(ctx.context().items[0], cloned.context().items[0]);
1053    }
1054
1055    #[test]
1056    fn test_withcontext_with_types() {
1057        let mut ctx = OperationContext::new();
1058
1059        // 测试各种类型转换
1060        ctx.record("string_key", "string_value");
1061        ctx.record("string_key", 42.to_string()); // 数字转字符串
1062        ctx.record("bool_key", true.to_string()); // 布尔转字符串
1063
1064        assert_eq!(ctx.context().items.len(), 3);
1065
1066        // 验证最后一个添加的值
1067        assert_eq!(
1068            ctx.context().items[2],
1069            ("bool_key".to_string(), "true".to_string())
1070        );
1071    }
1072
1073    #[test]
1074    fn test_mark_suc() {
1075        let mut ctx = OperationContext::new();
1076        assert!(ctx.result == OperationResult::Fail);
1077
1078        ctx.mark_suc();
1079        assert!(ctx.result == OperationResult::Suc);
1080    }
1081
1082    #[test]
1083    fn test_with_exit_log() {
1084        let ctx = OperationContext::new().with_auto_log();
1085        assert!(ctx.exit_log);
1086
1087        let ctx2 = OperationContext::want("test").with_auto_log();
1088        assert!(ctx2.exit_log);
1089        assert_eq!(*ctx2.target(), Some("test".to_string()));
1090    }
1091
1092    #[test]
1093    fn test_scope_marks_success() {
1094        let mut ctx = OperationContext::want("scope_success");
1095        {
1096            let _scope = ctx.scoped_success();
1097        }
1098        assert!(matches!(ctx.result(), OperationResult::Suc));
1099    }
1100
1101    #[test]
1102    fn test_scope_preserves_failure() {
1103        let mut ctx = OperationContext::want("scope_fail");
1104        {
1105            let mut scope = ctx.scoped_success();
1106            scope.mark_failure();
1107        }
1108        assert!(matches!(ctx.result(), OperationResult::Fail));
1109    }
1110
1111    #[test]
1112    fn test_scope_cancel() {
1113        let mut ctx = OperationContext::want("scope_cancel");
1114        {
1115            let mut scope = ctx.scoped_success();
1116            scope.cancel();
1117        }
1118        assert!(matches!(ctx.result(), OperationResult::Cancel));
1119    }
1120
1121    #[test]
1122    fn test_format_context_with_target() {
1123        let mut ctx = OperationContext::want("test_target");
1124        ctx.record("key1", "value1");
1125
1126        let formatted = ctx.format_context();
1127        assert_eq!(
1128            formatted,
1129            "want=test_target: \ncall context:\n\tkey1 : value1\n"
1130        );
1131    }
1132
1133    #[test]
1134    fn test_format_context_without_target() {
1135        let mut ctx = OperationContext::new();
1136        ctx.record("key1", "value1");
1137
1138        let formatted = ctx.format_context();
1139        assert_eq!(formatted, "call context:\n\tkey1 : value1\n");
1140    }
1141
1142    #[test]
1143    fn test_format_context_empty() {
1144        let ctx = OperationContext::new();
1145        let formatted = ctx.format_context();
1146        assert_eq!(formatted, "");
1147    }
1148
1149    #[test]
1150    fn test_format_context_with_target_only() {
1151        let ctx = OperationContext::want("test_target");
1152        let formatted = ctx.format_context();
1153        assert_eq!(formatted, "want=test_target");
1154    }
1155
1156    #[test]
1157    fn test_format_context_with_path() {
1158        let mut ctx = OperationContext::want("place_order");
1159        ctx.with_want("read_order_payload");
1160        ctx.record("order_id", "42");
1161
1162        let formatted = ctx.format_context();
1163        assert_eq!(
1164            formatted,
1165            "want=place_order path=place_order / read_order_payload: \ncall context:\n\torder_id : 42\n"
1166        );
1167    }
1168
1169    #[test]
1170    fn test_logging_methods() {
1171        let ctx = OperationContext::want("test_target");
1172
1173        // 这些方法主要测试它们不会panic,实际日志输出需要日志框架支持
1174        ctx.info("info message");
1175        ctx.debug("debug message");
1176        ctx.warn("warn message");
1177        ctx.error("error message");
1178        ctx.trace("trace message");
1179    }
1180
1181    #[test]
1182    fn test_logging_methods_with_empty_context() {
1183        let ctx = OperationContext::new();
1184
1185        // 测试空上下文时的日志方法
1186        ctx.info("info message");
1187        ctx.debug("debug message");
1188        ctx.warn("warn message");
1189        ctx.error("error message");
1190        ctx.trace("trace message");
1191    }
1192
1193    #[test]
1194    fn test_context_add_trait() {
1195        let mut ctx = OperationContext::new();
1196
1197        // 测试ContextAdd trait的实现
1198        ctx.add_context(("key1", "value1"));
1199        ctx.add_context(("key2", "value2"));
1200
1201        assert_eq!(ctx.context().items.len(), 2);
1202        assert_eq!(
1203            ctx.context().items[0],
1204            ("key1".to_string(), "value1".to_string())
1205        );
1206        assert_eq!(
1207            ctx.context().items[1],
1208            ("key2".to_string(), "value2".to_string())
1209        );
1210    }
1211
1212    #[test]
1213    fn test_drop_trait_with_success() {
1214        {
1215            let mut ctx = OperationContext::want("test_drop").with_auto_log();
1216            ctx.record("operation", "test");
1217            ctx.mark_suc(); // 标记为成功
1218                            // ctx 在这里离开作用域,会触发Drop trait
1219        }
1220        // 注意:Drop trait的日志输出需要日志框架配置才能看到
1221        // 这里主要测试Drop trait不会panic
1222    }
1223
1224    #[test]
1225    fn test_drop_trait_with_failure() {
1226        {
1227            let mut ctx = OperationContext::want("test_drop_fail").with_auto_log();
1228            ctx.record("operation", "test_fail");
1229            // 不调用mark_suc,保持is_suc = false
1230            // ctx 在这里离开作用域,会触发Drop trait
1231        }
1232        // 注意:Drop trait的日志输出需要日志框架配置才能看到
1233        // 这里主要测试Drop trait不会panic
1234    }
1235
1236    #[test]
1237    fn test_drop_trait_without_exit_log() {
1238        {
1239            let mut ctx = OperationContext::want("test_no_log");
1240            ctx.record("operation", "no_log");
1241            ctx.mark_suc();
1242            // exit_log = false,不会触发日志输出
1243            // ctx 在这里离开作用域,Drop trait应该什么都不做
1244        }
1245        // 测试通过即可
1246    }
1247
1248    #[test]
1249    fn test_complex_context_scenario() {
1250        // 模拟一个复杂的操作场景
1251        let mut ctx = OperationContext::want("user_registration").with_auto_log();
1252
1253        // 添加各种上下文信息
1254        ctx.record("user_id", "12345");
1255        ctx.record("email", "test@example.com");
1256        ctx.record("role", "user");
1257
1258        // 记录各种级别的日志
1259        ctx.info("开始用户注册流程");
1260        ctx.debug("验证用户输入");
1261        ctx.warn("检测到潜在的安全风险");
1262
1263        // 模拟操作成功
1264        ctx.mark_suc();
1265        ctx.info("用户注册成功");
1266
1267        // 验证上下文状态
1268        assert!(ctx.result == OperationResult::Suc);
1269        assert!(ctx.exit_log);
1270        assert_eq!(*ctx.target(), Some("user_registration".to_string()));
1271        assert_eq!(ctx.context().items.len(), 3);
1272
1273        // 验证format_context输出
1274        let formatted = ctx.format_context();
1275        assert!(formatted.contains("user_registration"));
1276        assert!(formatted.contains("user_id"));
1277        assert!(formatted.contains("email"));
1278        assert!(formatted.contains("role"));
1279    }
1280
1281    #[cfg(feature = "serde")]
1282    #[test]
1283    fn test_context_serialization() {
1284        let mut ctx = OperationContext::want("serialization_test");
1285        ctx.with_want("inner_step");
1286        ctx.record("key1", "value1");
1287        ctx.record("key2", "value2");
1288
1289        // 测试序列化
1290        let serialized = serde_json::to_string(&ctx).expect("序列化失败");
1291        assert!(serialized.contains("serialization_test"));
1292        assert!(serialized.contains("inner_step"));
1293        assert!(serialized.contains("key1"));
1294        assert!(serialized.contains("value1"));
1295
1296        // 测试反序列化
1297        let deserialized: OperationContext =
1298            serde_json::from_str(&serialized).expect("反序列化失败");
1299        assert_eq!(ctx, deserialized);
1300    }
1301
1302    #[test]
1303    fn test_context_with_special_characters() {
1304        let mut ctx = OperationContext::new();
1305
1306        // 测试特殊字符
1307        ctx.record("key_with_spaces", "value with spaces");
1308        ctx.record("key_with_unicode", "值包含中文");
1309        ctx.record("key_with_symbols", "value@#$%^&*()");
1310
1311        assert_eq!(ctx.context().items.len(), 3);
1312        assert_eq!(
1313            ctx.context().items[0],
1314            (
1315                "key_with_spaces".to_string(),
1316                "value with spaces".to_string()
1317            )
1318        );
1319        assert_eq!(
1320            ctx.context().items[1],
1321            ("key_with_unicode".to_string(), "值包含中文".to_string())
1322        );
1323        assert_eq!(
1324            ctx.context().items[2],
1325            ("key_with_symbols".to_string(), "value@#$%^&*()".to_string())
1326        );
1327
1328        // 测试显示
1329        let display = format!("{ctx}");
1330        assert!(display.contains("key_with_spaces"));
1331        assert!(display.contains("值包含中文"));
1332        assert!(display.contains("value@#$%^&*()"));
1333    }
1334
1335    #[test]
1336    fn test_context_builder_pattern() {
1337        // 测试构建器模式的使用
1338        let ctx = OperationContext::want("builder_test").with_auto_log();
1339
1340        assert_eq!(*ctx.target(), Some("builder_test".to_string()));
1341        assert_eq!(ctx.path(), &["builder_test".to_string()]);
1342        assert!(ctx.exit_log);
1343    }
1344
1345    #[test]
1346    fn test_context_multiple_with_calls() {
1347        let mut ctx = OperationContext::new();
1348
1349        // 多次调用with方法
1350        ctx.record("key1", "value1");
1351        ctx.record("key2", "value2");
1352        ctx.record("key3", "value3");
1353        ctx.record("key1", "new_value1"); // 覆盖key1
1354
1355        // 注意:当前实现允许重复的key,这是预期的行为
1356        assert_eq!(ctx.context().items.len(), 4);
1357        assert_eq!(
1358            ctx.context().items[0],
1359            ("key1".to_string(), "value1".to_string())
1360        );
1361        assert_eq!(
1362            ctx.context().items[3],
1363            ("key1".to_string(), "new_value1".to_string())
1364        );
1365    }
1366
1367    #[test]
1368    fn test_context_from_various_types() {
1369        // 测试从各种类型创建OperationContext
1370        let ctx1 = OperationContext::from("simple_string");
1371        assert_eq!(
1372            ctx1.context().items[0],
1373            ("key".to_string(), "simple_string".to_string())
1374        );
1375
1376        let ctx2 = OperationContext::from(("custom_key", "custom_value"));
1377        assert_eq!(
1378            ctx2.context().items[0],
1379            ("custom_key".to_string(), "custom_value".to_string())
1380        );
1381
1382        let path = PathBuf::from("/test/path/file.txt");
1383        let ctx3 = OperationContext::from(&path);
1384        assert!(ctx3.context().items[0].0.contains("path"));
1385        assert!(ctx3.context().items[0].1.contains("/test/path/file.txt"));
1386    }
1387
1388    // ContextTake trait 测试用例
1389    #[test]
1390    fn test_context_take_with_string_types() {
1391        let mut ctx = OperationContext::new();
1392
1393        // 测试字符串类型的ContextTake实现
1394        ctx.record("string_key", "string_value");
1395        ctx.record("string_key2", String::from("string_value2"));
1396        ctx.record(String::from("string_key3"), "string_value3");
1397        ctx.record(String::from("string_key4"), String::from("string_value4"));
1398
1399        assert_eq!(ctx.context().items.len(), 4);
1400        assert_eq!(
1401            ctx.context().items[0],
1402            ("string_key".to_string(), "string_value".to_string())
1403        );
1404        assert_eq!(
1405            ctx.context().items[1],
1406            ("string_key2".to_string(), "string_value2".to_string())
1407        );
1408        assert_eq!(
1409            ctx.context().items[2],
1410            ("string_key3".to_string(), "string_value3".to_string())
1411        );
1412        assert_eq!(
1413            ctx.context().items[3],
1414            ("string_key4".to_string(), "string_value4".to_string())
1415        );
1416    }
1417
1418    #[test]
1419    fn test_context_take_with_numeric_types() {
1420        let mut ctx = OperationContext::new();
1421
1422        // 测试数字类型的ContextTake实现(需要转换为字符串)
1423        ctx.record("int_key", 42.to_string());
1424        ctx.record("float_key", 3.24.to_string());
1425        ctx.record("bool_key", true.to_string());
1426
1427        assert_eq!(ctx.context().items.len(), 3);
1428        assert_eq!(
1429            ctx.context().items[0],
1430            ("int_key".to_string(), "42".to_string())
1431        );
1432        assert_eq!(
1433            ctx.context().items[1],
1434            ("float_key".to_string(), "3.24".to_string())
1435        );
1436        assert_eq!(
1437            ctx.context().items[2],
1438            ("bool_key".to_string(), "true".to_string())
1439        );
1440    }
1441
1442    #[test]
1443    fn test_context_take_with_path_context() {
1444        let mut ctx = OperationContext::new();
1445
1446        // 测试PathContext包装类型的ContextTake实现
1447        let path1 = PathBuf::from("/test/path1.txt");
1448        let path2 = Path::new("/test/path2.txt");
1449
1450        ctx.record("file1", &path1);
1451        ctx.record("file2", path2);
1452
1453        assert_eq!(ctx.context().items.len(), 2);
1454        assert_eq!(ctx.context().items[0].0, "file1");
1455        assert!(ctx.context().items[0].1.contains("/test/path1.txt"));
1456        assert_eq!(ctx.context().items[1].0, "file2");
1457        assert!(ctx.context().items[1].1.contains("/test/path2.txt"));
1458    }
1459
1460    #[test]
1461    fn test_context_take_mixed_types() {
1462        let mut ctx = OperationContext::new();
1463
1464        // 测试混合使用字符串和PathContext类型
1465        ctx.record("name", "test_user");
1466        ctx.record("age", 25.to_string());
1467        ctx.record("config_file", &PathBuf::from("/etc/config.toml"));
1468        ctx.record("status", "active");
1469
1470        assert_eq!(ctx.context().items.len(), 4);
1471        assert_eq!(
1472            ctx.context().items[0],
1473            ("name".to_string(), "test_user".to_string())
1474        );
1475        assert_eq!(
1476            ctx.context().items[1],
1477            ("age".to_string(), "25".to_string())
1478        );
1479        assert_eq!(ctx.context().items[2].0, "config_file");
1480        assert!(ctx.context().items[2].1.contains("/etc/config.toml"));
1481        assert_eq!(
1482            ctx.context().items[3],
1483            ("status".to_string(), "active".to_string())
1484        );
1485    }
1486
1487    #[test]
1488    fn test_context_take_edge_cases() {
1489        let mut ctx = OperationContext::new();
1490
1491        // 测试边界情况
1492        ctx.record("", ""); // 空字符串
1493        ctx.record("empty_value", ""); // 空值
1494        ctx.record("", "empty_key"); // 空键
1495        ctx.record("special_chars", "@#$%^&*()"); // 特殊字符
1496        ctx.record("unicode", "测试中文字符"); // Unicode字符
1497
1498        assert_eq!(ctx.context().items.len(), 5);
1499        assert_eq!(ctx.context().items[0], ("".to_string(), "".to_string()));
1500        assert_eq!(
1501            ctx.context().items[1],
1502            ("empty_value".to_string(), "".to_string())
1503        );
1504        assert_eq!(
1505            ctx.context().items[2],
1506            ("".to_string(), "empty_key".to_string())
1507        );
1508        assert_eq!(
1509            ctx.context().items[3],
1510            ("special_chars".to_string(), "@#$%^&*()".to_string())
1511        );
1512        assert_eq!(
1513            ctx.context().items[4],
1514            ("unicode".to_string(), "测试中文字符".to_string())
1515        );
1516    }
1517
1518    #[test]
1519    fn test_context_take_multiple_calls() {
1520        let mut ctx = OperationContext::new();
1521
1522        // 测试多次调用take方法
1523        ctx.record("key1", "value1");
1524        ctx.record("key2", "value2");
1525        ctx.record("key1", "new_value1"); // 覆盖key1
1526        ctx.record("key3", &PathBuf::from("/path/file.txt"));
1527        ctx.record("key2", &PathBuf::from("/path/file2.txt")); // 覆盖key2
1528
1529        // 注意:当前实现允许重复的key,这是预期的行为
1530        assert_eq!(ctx.context().items.len(), 5);
1531        assert_eq!(
1532            ctx.context().items[0],
1533            ("key1".to_string(), "value1".to_string())
1534        );
1535        assert_eq!(
1536            ctx.context().items[1],
1537            ("key2".to_string(), "value2".to_string())
1538        );
1539        assert_eq!(
1540            ctx.context().items[2],
1541            ("key1".to_string(), "new_value1".to_string())
1542        );
1543        assert_eq!(ctx.context().items[3].0, "key3");
1544        assert!(ctx.context().items[3].1.contains("/path/file.txt"));
1545        assert_eq!(ctx.context().items[4].0, "key2");
1546        assert!(ctx.context().items[4].1.contains("/path/file2.txt"));
1547    }
1548
1549    #[test]
1550    fn test_context_take_with_existing_context() {
1551        // 创建一个已有上下文的OperationContext
1552        let mut ctx = OperationContext::from(("existing_key", "existing_value"));
1553
1554        // 使用ContextTake添加更多上下文
1555        ctx.record("new_key1", "new_value1");
1556        ctx.record("new_key2", &PathBuf::from("/new/path.txt"));
1557
1558        assert_eq!(ctx.context().items.len(), 3);
1559        assert_eq!(
1560            ctx.context().items[0],
1561            ("existing_key".to_string(), "existing_value".to_string())
1562        );
1563        assert_eq!(
1564            ctx.context().items[1],
1565            ("new_key1".to_string(), "new_value1".to_string())
1566        );
1567        assert_eq!(ctx.context().items[2].0, "new_key2");
1568        assert!(ctx.context().items[2].1.contains("/new/path.txt"));
1569    }
1570
1571    #[test]
1572    fn test_context_metadata_records_values() {
1573        let ctx = OperationContext::want("load")
1574            .with_meta("config.kind", "wpsrc")
1575            .with_meta("parse.line", 1u32)
1576            .with_meta("parse.strict", true);
1577
1578        assert_eq!(ctx.metadata().get_str("config.kind"), Some("wpsrc"));
1579        assert!(ctx.metadata().as_map().contains_key("parse.line"));
1580        assert!(ctx.metadata().as_map().contains_key("parse.strict"));
1581    }
1582
1583    #[test]
1584    fn test_context_metadata_duplicate_key_overwrites() {
1585        let ctx = OperationContext::new()
1586            .with_meta("config.kind", "sink_route")
1587            .with_meta("config.kind", "sink_defaults");
1588
1589        assert_eq!(ctx.metadata().get_str("config.kind"), Some("sink_defaults"));
1590    }
1591
1592    #[test]
1593    fn test_context_metadata_ignores_empty_key() {
1594        let result = std::panic::catch_unwind(|| OperationContext::new().with_meta("", "ignored"));
1595
1596        if cfg!(debug_assertions) {
1597            assert!(result.is_err());
1598        } else {
1599            let ctx = result.expect("release build should ignore empty metadata key");
1600            assert!(ctx.metadata().is_empty());
1601        }
1602    }
1603
1604    #[cfg(feature = "serde")]
1605    #[test]
1606    fn test_context_serialization_skips_empty_metadata_and_reads_missing_field() {
1607        let ctx = OperationContext::want("serialization_test");
1608
1609        let serialized = serde_json::to_value(&ctx).expect("序列化失败");
1610        assert!(!serialized
1611            .as_object()
1612            .expect("object")
1613            .contains_key("metadata"));
1614
1615        let deserialized: OperationContext =
1616            serde_json::from_value(serialized).expect("反序列化失败");
1617        assert!(deserialized.metadata().is_empty());
1618    }
1619
1620    #[cfg(feature = "serde")]
1621    #[test]
1622    fn test_context_serialization_preserves_metadata() {
1623        let ctx = OperationContext::want("serialization_test")
1624            .with_meta("config.kind", "sink_defaults")
1625            .with_meta("parse.line", 3u32);
1626
1627        let serialized = serde_json::to_value(&ctx).expect("序列化失败");
1628        assert_eq!(
1629            serialized["metadata"]["config.kind"],
1630            serde_json::Value::String("sink_defaults".to_string())
1631        );
1632        assert_eq!(serialized["metadata"]["parse.line"], serde_json::json!(3));
1633
1634        let deserialized: OperationContext =
1635            serde_json::from_value(serialized).expect("反序列化失败");
1636        assert_eq!(
1637            deserialized.metadata().get_str("config.kind"),
1638            Some("sink_defaults")
1639        );
1640    }
1641}