Skip to main content

satteri_plugin_api/
context.rs

1use crate::commands::{Command, NewNode};
2use crate::data::{DataMap, DataValue, TypedDataMap};
3use satteri_arena::Arena;
4
5/// Context passed to Rust plugin visitor methods and before/after hooks.
6pub struct PluginContext<'a> {
7    arena: &'a Arena,
8    pub(crate) data_map: &'a mut DataMap,
9    pub(crate) typed_data: &'a mut TypedDataMap,
10    commands: Vec<Command>,
11    diagnostics: Vec<Diagnostic>,
12}
13
14#[derive(Debug, Clone)]
15pub struct Diagnostic {
16    pub message: String,
17    pub node_id: Option<u32>,
18    pub severity: Severity,
19}
20
21#[derive(Debug, Clone, Copy, PartialEq, Eq)]
22pub enum Severity {
23    Error,
24    Warning,
25    Info,
26}
27
28impl<'a> PluginContext<'a> {
29    pub(crate) fn new(
30        arena: &'a Arena,
31        data_map: &'a mut DataMap,
32        typed_data: &'a mut TypedDataMap,
33    ) -> Self {
34        Self {
35            arena,
36            data_map,
37            typed_data,
38            commands: Vec::new(),
39            diagnostics: Vec::new(),
40        }
41    }
42
43    pub fn arena(&self) -> &Arena {
44        self.arena
45    }
46
47    /// Extract all text content from a subtree (depth-first concatenation)
48    pub fn extract_text(&self, node_id: u32) -> String {
49        use satteri_ast::mdast::codec::decode_string_ref_data;
50        use satteri_ast::mdast::MdastNodeType;
51        let node = self.arena.get_node(node_id);
52        if node.node_type == MdastNodeType::Text as u8
53            || node.node_type == MdastNodeType::InlineCode as u8
54        {
55            let data = self.arena.get_type_data(node_id);
56            if !data.is_empty() {
57                let string_ref = decode_string_ref_data(data);
58                return self.arena.get_str(string_ref).to_string();
59            }
60            return String::new();
61        }
62        let children = self.arena.get_children(node_id).to_vec();
63        children
64            .iter()
65            .map(|&child_id| self.extract_text(child_id))
66            .collect::<Vec<_>>()
67            .join("")
68    }
69
70    pub fn set_data(&mut self, node_id: u32, key: &str, value: DataValue) {
71        self.data_map.set(node_id, key, value);
72    }
73
74    pub fn get_data(&self, node_id: u32, key: &str) -> Option<&DataValue> {
75        self.data_map.get(node_id, key)
76    }
77
78    pub fn set_typed_data<T: std::any::Any + Send + Sync>(&mut self, node_id: u32, value: T) {
79        self.typed_data.set(node_id, value);
80    }
81
82    pub fn get_typed_data<T: std::any::Any + Send + Sync>(&self, node_id: u32) -> Option<&T> {
83        self.typed_data.get(node_id)
84    }
85
86    pub fn replace_node(&mut self, node_id: u32, new_node: NewNode) {
87        self.commands.push(Command::Replace { node_id, new_node });
88    }
89
90    pub fn remove_node(&mut self, node_id: u32) {
91        self.commands.push(Command::Remove { node_id });
92    }
93
94    pub fn insert_before(&mut self, node_id: u32, new_node: NewNode) {
95        self.commands
96            .push(Command::InsertBefore { node_id, new_node });
97    }
98
99    pub fn insert_after(&mut self, node_id: u32, new_node: NewNode) {
100        self.commands
101            .push(Command::InsertAfter { node_id, new_node });
102    }
103
104    pub fn wrap_node(&mut self, node_id: u32, parent_node: NewNode) {
105        self.commands.push(Command::Wrap {
106            node_id,
107            parent_node,
108        });
109    }
110
111    pub fn prepend_child(&mut self, node_id: u32, child_node: NewNode) {
112        self.commands.push(Command::PrependChild {
113            node_id,
114            child_node,
115        });
116    }
117
118    pub fn append_child(&mut self, node_id: u32, child_node: NewNode) {
119        self.commands.push(Command::AppendChild {
120            node_id,
121            child_node,
122        });
123    }
124
125    pub fn report(&mut self, message: impl Into<String>, node_id: Option<u32>, severity: Severity) {
126        self.diagnostics.push(Diagnostic {
127            message: message.into(),
128            node_id,
129            severity,
130        });
131    }
132
133    pub fn error(&mut self, message: impl Into<String>, node_id: Option<u32>) {
134        self.report(message, node_id, Severity::Error);
135    }
136
137    pub fn warn(&mut self, message: impl Into<String>, node_id: Option<u32>) {
138        self.report(message, node_id, Severity::Warning);
139    }
140
141    pub(crate) fn take_commands(self) -> (Vec<Command>, Vec<Diagnostic>) {
142        (self.commands, self.diagnostics)
143    }
144}