Skip to main content

icydb_core/visitor/
context.rs

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