Skip to main content

open_gpui_canvas/tool/
registry.rs

1use std::{error::Error, fmt};
2
3use crate::{CanvasEvent, CanvasToolId, DocumentError};
4use indexmap::IndexMap;
5
6use super::{CanvasToolContext, CanvasToolIntent};
7
8pub trait CanvasToolReducer {
9    fn handle_event(
10        &mut self,
11        context: CanvasToolContext<'_>,
12        event: CanvasEvent,
13    ) -> Result<Vec<CanvasToolIntent>, DocumentError>;
14}
15
16impl<F> CanvasToolReducer for F
17where
18    F: for<'a> FnMut(
19        CanvasToolContext<'a>,
20        CanvasEvent,
21    ) -> Result<Vec<CanvasToolIntent>, DocumentError>,
22{
23    fn handle_event(
24        &mut self,
25        context: CanvasToolContext<'_>,
26        event: CanvasEvent,
27    ) -> Result<Vec<CanvasToolIntent>, DocumentError> {
28        self(context, event)
29    }
30}
31
32#[derive(Default)]
33pub struct CanvasToolRegistry {
34    reducers: IndexMap<CanvasToolId, Box<dyn CanvasToolReducer>>,
35}
36
37impl CanvasToolRegistry {
38    pub fn new() -> Self {
39        Self::default()
40    }
41
42    pub fn insert<T>(
43        &mut self,
44        id: impl Into<CanvasToolId>,
45        reducer: T,
46    ) -> Option<Box<dyn CanvasToolReducer>>
47    where
48        T: CanvasToolReducer + 'static,
49    {
50        self.reducers.insert(id.into(), Box::new(reducer))
51    }
52
53    pub fn insert_boxed(
54        &mut self,
55        id: impl Into<CanvasToolId>,
56        reducer: Box<dyn CanvasToolReducer>,
57    ) -> Option<Box<dyn CanvasToolReducer>> {
58        self.reducers.insert(id.into(), reducer)
59    }
60
61    pub fn remove(&mut self, id: &CanvasToolId) -> Option<Box<dyn CanvasToolReducer>> {
62        self.reducers.shift_remove(id)
63    }
64
65    pub fn contains(&self, id: &CanvasToolId) -> bool {
66        self.reducers.contains_key(id)
67    }
68
69    pub fn reducer_mut(&mut self, id: &CanvasToolId) -> Option<&mut (dyn CanvasToolReducer + '_)> {
70        let reducer = self.reducers.get_mut(id)?;
71        Some(reducer.as_mut())
72    }
73
74    pub fn ids(&self) -> impl Iterator<Item = &CanvasToolId> {
75        self.reducers.keys()
76    }
77
78    pub fn len(&self) -> usize {
79        self.reducers.len()
80    }
81
82    pub fn is_empty(&self) -> bool {
83        self.reducers.is_empty()
84    }
85}
86
87#[derive(Debug, Eq, PartialEq)]
88pub enum CanvasToolRegistryError {
89    MissingTool(CanvasToolId),
90    Document(DocumentError),
91}
92
93impl From<DocumentError> for CanvasToolRegistryError {
94    fn from(value: DocumentError) -> Self {
95        Self::Document(value)
96    }
97}
98
99impl fmt::Display for CanvasToolRegistryError {
100    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
101        match self {
102            Self::MissingTool(id) => write!(f, "canvas custom tool `{id}` is not registered"),
103            Self::Document(error) => fmt::Display::fmt(error, f),
104        }
105    }
106}
107
108impl Error for CanvasToolRegistryError {
109    fn source(&self) -> Option<&(dyn Error + 'static)> {
110        match self {
111            Self::MissingTool(_) => None,
112            Self::Document(error) => Some(error),
113        }
114    }
115}