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