icydb_core/visitor/
context.rs

1///
2/// VisitorContext
3/// Narrow interface exposed to visitors for reporting non-fatal issues.
4/// Implemented by adapters via a short-lived context object.
5///
6
7pub trait VisitorContext {
8    fn add_issue(&mut self, issue: Issue);
9    fn add_issue_at(&mut self, seg: PathSegment, issue: Issue);
10}
11
12impl dyn VisitorContext + '_ {
13    pub fn issue(&mut self, msg: impl Into<String>) {
14        self.add_issue(Issue {
15            message: msg.into(),
16        });
17    }
18
19    pub fn issue_at(&mut self, seg: PathSegment, msg: impl Into<String>) {
20        self.add_issue_at(
21            seg,
22            Issue {
23                message: msg.into(),
24            },
25        );
26    }
27}
28
29/// VisitorContext that pins all issues to a single path segment.
30pub struct ScopedContext<'a> {
31    ctx: &'a mut dyn VisitorContext,
32    seg: PathSegment,
33}
34
35impl<'a> ScopedContext<'a> {
36    #[must_use]
37    pub fn new(ctx: &'a mut dyn VisitorContext, seg: PathSegment) -> Self {
38        Self { ctx, seg }
39    }
40}
41
42impl VisitorContext for ScopedContext<'_> {
43    fn add_issue(&mut self, issue: Issue) {
44        self.ctx.add_issue_at(self.seg.clone(), issue);
45    }
46
47    fn add_issue_at(&mut self, _seg: PathSegment, issue: Issue) {
48        self.ctx.add_issue_at(self.seg.clone(), issue);
49    }
50}
51
52///
53/// Issue
54///
55
56#[derive(Clone, Debug, Default)]
57pub struct Issue {
58    pub message: String,
59}
60
61///
62/// PathSegment
63///
64
65#[derive(Clone, Debug)]
66pub enum PathSegment {
67    Empty,
68    Field(&'static str),
69    Index(usize),
70}
71
72impl From<&'static str> for PathSegment {
73    fn from(s: &'static str) -> Self {
74        Self::Field(s)
75    }
76}
77
78impl From<usize> for PathSegment {
79    fn from(i: usize) -> Self {
80        Self::Index(i)
81    }
82}
83
84impl From<Option<&'static str>> for PathSegment {
85    fn from(opt: Option<&'static str>) -> Self {
86        match opt {
87            Some(s) if !s.is_empty() => Self::Field(s),
88            _ => Self::Empty,
89        }
90    }
91}