edita_core/
lib.rs

1use std::fmt;
2use std::ops::{Deref, DerefMut};
3
4/// The main editor structure that manages the state and blocks.
5pub struct Editor<Node, State, Input> {
6    /// The internal state of the editor.
7    state: State,
8    /// A collection of blocks that process input nodes.
9    blocks: Vec<Box<dyn Block<Node = Node, State = State, Input = Input>>>,
10    /// An optional fallback block used when no other block accepts input nodes.
11    fallback_block: Option<Box<dyn Block<Node = Node, State = State, Input = Input>>>,
12}
13impl<Node, State, Input> Editor<Node, State, Input> {
14    /// Creates a new `Editor` with the given initial state.
15    pub fn new(state: State) -> Self {
16        Editor {
17            state,
18            blocks: Vec::new(),
19            fallback_block: None,
20        }
21    }
22    /// Executes a command on the editor's state.
23    pub fn command<C: Command<State>>(&mut self, cmd: C) {
24        cmd.execute(&mut self.state)
25    }
26    /// Adds a block to the editor's list of blocks.
27    pub fn add_block<B: Block<Node = Node, State = State, Input = Input> + 'static>(
28        &mut self,
29        block: B,
30    ) {
31        self.blocks.push(Box::new(block))
32    }
33
34    /// Sets the fallback block for the editor.
35    pub fn set_fallback_block(
36        &mut self,
37        block: Box<dyn Block<Node = Node, State = State, Input = Input>>,
38    ) {
39        self.fallback_block = Some(block);
40    }
41}
42
43impl<Node, State, Input> fmt::Debug for Editor<Node, State, Input>
44where
45    State: fmt::Debug,
46{
47    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
48        f.debug_struct("Editor")
49            .field("state", &self.state)
50            .field("blocks", &self.blocks.len())
51            .finish()
52    }
53}
54
55impl<Node, State, Input> Deref for Editor<Node, State, Input> {
56    type Target = State;
57
58    fn deref(&self) -> &Self::Target {
59        &self.state
60    }
61}
62
63impl<Node, State, Input> DerefMut for Editor<Node, State, Input> {
64    fn deref_mut(&mut self) -> &mut Self::Target {
65        &mut self.state
66    }
67}
68/// Trait for defining blocks that process input nodes
69#[allow(unused_variables)]
70pub trait Block {
71    type Input;
72    type Node;
73    type State;
74    /// Called when a block is hooked into an editor.
75    fn hook(&self, editor: &mut Editor<Self::Node, Self::State, Self::Input>) {}
76
77    /// Determines whether the block accepts a specific input node.
78    fn accepts(&self, input: &Self::Input) -> bool {
79        false
80    }
81    /// Parses an input node and produces a node of a different type.
82    fn parse(
83        &self,
84        editor: &Editor<Self::Node, Self::State, Self::Input>,
85        input: &Self::Input,
86    ) -> Self::Node;
87}
88
89
90/// Trait for defining commands that can modify the editor's state.
91pub trait Command<State> {
92    fn execute(&self, state: &mut State);
93}
94
95
96/// Processes input nodes using the editor's blocks and returns parsed nodes.
97///
98/// This function takes an `Editor`, which contains a collection of blocks, and a sequence
99/// of input nodes. It processes each input node using the editor's blocks, and if no block
100/// accepts an input node, it falls back to the editor's fallback block (if provided).
101///
102/// The parsed nodes are collected and returned as a `Vec`.
103pub fn process_nodes<N, S, I, Iter: IntoIterator<Item = I>>(
104    editor: &Editor<N, S, I>,
105    children: Iter,
106) -> Vec<N> {
107    let blocks = &editor.blocks;
108    let mut parsed_nodes = Vec::new();
109    let children: Vec<I> = children.into_iter().collect();
110    for i in 0..children.len() {
111        if let Some(node) = children.get(i) {
112            let mut accepted = false;
113
114            for block in blocks {
115                if block.accepts(node) {
116                    parsed_nodes.push(block.parse(editor, node));
117                    accepted = true;
118                    break;
119                }
120            }
121            // Fallback for nodes not accepted by any block
122            if !accepted && editor.fallback_block.is_some() {
123                parsed_nodes.push(editor.fallback_block.as_ref().unwrap().parse(editor, node));
124            }
125        }
126    }
127
128    parsed_nodes
129}
130
131
132#[cfg(test)]
133mod tests {
134    use super::*;
135
136    // Define a simple state struct for testing
137    #[derive(Debug, PartialEq, Eq)]
138    struct TestState {
139        value: i32,
140    }
141
142    // Define a simple block for testing
143    struct TestBlock;
144
145    impl Block for TestBlock {
146        type Input = i32;
147        type Node = i32;
148        type State = TestState;
149
150        fn accepts(&self, input: &i32) -> bool {
151            *input % 2 == 0
152        }
153
154        fn parse(&self, _editor: &Editor<Self::Node, Self::State, Self::Input>, input: &i32) -> i32 {
155            *input * 2
156        }
157    }
158
159    // Define a simple command for testing
160    struct TestCommand;
161
162    impl Command<TestState> for TestCommand {
163        fn execute(&self, state: &mut TestState) {
164            state.value += 1;
165        }
166    }
167
168    #[test]
169    fn test_editor_new() {
170        let editor: Editor<i32, TestState, i32> = Editor::new(TestState { value: 42 });
171        assert_eq!(editor.state.value, 42);
172    }
173
174    #[test]
175    fn test_editor_command() {
176        let mut editor: Editor<i32, TestState, i32> = Editor::new(TestState { value: 0 });
177        let command = TestCommand;
178        editor.command(command);
179        assert_eq!(editor.state.value, 1);
180    }
181
182    #[test]
183    fn test_editor_add_block() {
184        let mut editor: Editor<i32, TestState, i32> = Editor::new(TestState { value: 0 });
185        let block = TestBlock;
186        editor.add_block(block);
187        let input = 4;
188        let parsed_nodes = process_nodes(&editor, vec![input]);
189        assert_eq!(parsed_nodes, vec![8]); // 4 * 2 = 8
190    }
191
192    #[test]
193    fn test_editor_set_fallback_block() {
194        let mut editor: Editor<i32, TestState, i32> = Editor::new(TestState { value: 0 });
195        let fallback_block = Box::new(TestBlock);
196        editor.set_fallback_block(fallback_block);
197        let input = 3; // Fallback block should handle odd input
198        let parsed_nodes = process_nodes(&editor, vec![input]);
199        assert_eq!(parsed_nodes, vec![6]); // 3 * 2 = 6 (handled by the fallback block)
200    }
201
202    #[test]
203    fn test_process_nodes_no_blocks() {
204        let editor: Editor<i32, TestState, i32> = Editor::new(TestState { value: 0 });
205        let input = 5;
206        let parsed_nodes: Vec<_> = process_nodes(&editor, vec![input]);
207        assert_eq!(parsed_nodes, vec![]); // No blocks to handle the input
208    }
209}