Skip to main content

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::new(msg));
15    }
16
17    pub fn issue_at(&mut self, seg: PathSegment, msg: impl Into<String>) {
18        self.add_issue_at(seg, Issue::new(msg));
19    }
20}
21
22/// VisitorContext that pins all issues to a single path segment.
23pub struct ScopedContext<'a> {
24    ctx: &'a mut dyn VisitorContext,
25    seg: PathSegment,
26}
27
28impl<'a> ScopedContext<'a> {
29    #[must_use]
30    pub fn new(ctx: &'a mut dyn VisitorContext, seg: PathSegment) -> Self {
31        Self { ctx, seg }
32    }
33}
34
35impl VisitorContext for ScopedContext<'_> {
36    fn add_issue(&mut self, issue: Issue) {
37        self.ctx.add_issue_at(self.seg.clone(), issue);
38    }
39
40    fn add_issue_at(&mut self, _seg: PathSegment, issue: Issue) {
41        self.ctx.add_issue_at(self.seg.clone(), issue);
42    }
43}
44
45///
46/// Issue
47///
48
49#[derive(Clone, Debug, Default)]
50pub struct Issue {
51    message: String,
52}
53
54impl Issue {
55    #[must_use]
56    pub fn new(message: impl Into<String>) -> Self {
57        Self {
58            message: message.into(),
59        }
60    }
61
62    #[must_use]
63    pub fn message(&self) -> &str {
64        &self.message
65    }
66
67    #[must_use]
68    pub fn into_message(self) -> String {
69        self.message
70    }
71}
72
73///
74/// PathSegment
75///
76
77#[derive(Clone, Debug)]
78pub enum PathSegment {
79    Empty,
80    Field(&'static str),
81    Index(usize),
82}
83
84impl From<&'static str> for PathSegment {
85    fn from(s: &'static str) -> Self {
86        Self::Field(s)
87    }
88}
89
90impl From<usize> for PathSegment {
91    fn from(i: usize) -> Self {
92        Self::Index(i)
93    }
94}
95
96impl From<Option<&'static str>> for PathSegment {
97    fn from(opt: Option<&'static str>) -> Self {
98        match opt {
99            Some(s) if !s.is_empty() => Self::Field(s),
100            _ => Self::Empty,
101        }
102    }
103}