1use serde::Deserialize;
2use serde::Serialize;
3
4#[cfg(feature = "async_runtime")]
5use crate::async_runtime::FutureExt;
6#[cfg(feature = "async_runtime")]
7use crate::async_runtime::LocalBoxFuture;
8
9use crate::configuration::ConfigKeyMap;
10use crate::configuration::ConfigKeyValue;
11use crate::configuration::ConfigurationDiagnostic;
12use crate::configuration::GlobalConfiguration;
13use crate::plugins::PluginInfo;
14
15use super::FileMatchingInfo;
16
17pub trait CancellationToken: Send + Sync + std::fmt::Debug {
18 fn is_cancelled(&self) -> bool;
19 #[cfg(feature = "async_runtime")]
20 fn wait_cancellation(&self) -> LocalBoxFuture<'static, ()>;
21}
22
23#[cfg(feature = "async_runtime")]
24impl CancellationToken for tokio_util::sync::CancellationToken {
25 fn is_cancelled(&self) -> bool {
26 self.is_cancelled()
27 }
28
29 fn wait_cancellation(&self) -> LocalBoxFuture<'static, ()> {
30 let token = self.clone();
31 async move { token.cancelled().await }.boxed_local()
32 }
33}
34
35#[derive(Debug)]
37pub struct NullCancellationToken;
38
39impl CancellationToken for NullCancellationToken {
40 fn is_cancelled(&self) -> bool {
41 false
42 }
43
44 #[cfg(feature = "async_runtime")]
45 fn wait_cancellation(&self) -> LocalBoxFuture<'static, ()> {
46 Box::pin(std::future::pending())
48 }
49}
50
51pub type FormatRange = Option<std::ops::Range<usize>>;
52
53#[derive(Debug, thiserror::Error)]
59#[error(transparent)]
60pub struct FormatError(Box<dyn std::error::Error + Send + Sync + 'static>);
61
62impl FormatError {
63 pub fn new(error: impl Into<Box<dyn std::error::Error + Send + Sync + 'static>>) -> Self {
66 FormatError(error.into())
67 }
68
69 pub fn downcast_ref<E: std::error::Error + 'static>(&self) -> Option<&E> {
72 self.0.downcast_ref::<E>()
73 }
74}
75
76pub fn error_to_string(err: &(dyn std::error::Error + 'static)) -> String {
80 const MAX_DEPTH: usize = 100;
83 let mut result = err.to_string();
84 let mut source = err.source();
85 for _ in 0..MAX_DEPTH {
86 let Some(err) = source else { break };
87 result.push_str(": ");
88 result.push_str(&err.to_string());
89 source = err.source();
90 }
91 result
92}
93
94macro_rules! impl_format_error_from {
95 ($($t:ty),* $(,)?) => {
96 $(
97 impl From<$t> for FormatError {
98 fn from(error: $t) -> Self {
99 FormatError(error.into())
100 }
101 }
102 )*
103 };
104}
105
106impl_format_error_from!(
107 String,
108 &str,
109 Box<dyn std::error::Error + Send + Sync + 'static>,
110 std::io::Error,
111 std::string::FromUtf8Error,
112 CriticalFormatError,
113);
114
115#[cfg(feature = "serde_json")]
116impl_format_error_from!(serde_json::Error);
117
118#[cfg(feature = "async_runtime")]
119impl_format_error_from!(tokio::task::JoinError, tokio::sync::oneshot::error::RecvError);
120
121#[derive(Debug, thiserror::Error)]
126#[error(transparent)]
127pub struct CriticalFormatError(pub FormatError);
128
129#[derive(Debug, Serialize, Deserialize)]
130#[serde(rename_all = "camelCase")]
131pub struct CheckConfigUpdatesMessage {
132 #[serde(default)]
134 pub old_version: Option<String>,
135 pub config: ConfigKeyMap,
136}
137
138#[cfg(feature = "process")]
139#[derive(Debug)]
140pub struct HostFormatRequest {
141 pub file_path: std::path::PathBuf,
142 pub file_bytes: Vec<u8>,
143 pub range: FormatRange,
145 pub override_config: ConfigKeyMap,
146 pub token: std::sync::Arc<dyn CancellationToken>,
147}
148
149#[cfg(feature = "wasm")]
150#[derive(Debug)]
151pub struct SyncHostFormatRequest<'a> {
152 pub file_path: &'a std::path::Path,
153 pub file_bytes: &'a [u8],
154 pub range: FormatRange,
156 pub override_config: &'a ConfigKeyMap,
157}
158
159pub type FormatResult = Result<Option<Vec<u8>>, FormatError>;
163
164#[derive(Debug, Clone, Serialize, Deserialize)]
165pub struct RawFormatConfig {
166 pub plugin: ConfigKeyMap,
167 pub global: GlobalConfiguration,
168}
169
170#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
172pub struct FormatConfigId(u32);
173
174impl std::fmt::Display for FormatConfigId {
175 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
176 write!(f, "${}", self.0)
177 }
178}
179
180impl FormatConfigId {
181 pub fn from_raw(raw: u32) -> FormatConfigId {
182 FormatConfigId(raw)
183 }
184
185 pub fn uninitialized() -> FormatConfigId {
186 FormatConfigId(0)
187 }
188
189 pub fn as_raw(&self) -> u32 {
190 self.0
191 }
192}
193
194#[cfg(feature = "process")]
195pub struct FormatRequest<TConfiguration> {
196 pub file_path: std::path::PathBuf,
197 pub file_bytes: Vec<u8>,
198 pub config_id: FormatConfigId,
199 pub config: std::sync::Arc<TConfiguration>,
200 pub range: FormatRange,
202 pub token: std::sync::Arc<dyn CancellationToken>,
203}
204
205#[cfg(feature = "wasm")]
206pub struct SyncFormatRequest<'a, TConfiguration> {
207 pub file_path: &'a std::path::Path,
208 pub file_bytes: Vec<u8>,
209 pub config_id: FormatConfigId,
210 pub config: &'a TConfiguration,
211 pub range: FormatRange,
213 pub token: &'a dyn CancellationToken,
214}
215
216#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
217#[serde(untagged)]
218pub enum ConfigChangePathItem {
219 String(String),
221 Number(usize),
223}
224
225impl From<String> for ConfigChangePathItem {
226 fn from(value: String) -> Self {
227 Self::String(value)
228 }
229}
230
231impl From<usize> for ConfigChangePathItem {
232 fn from(value: usize) -> Self {
233 Self::Number(value)
234 }
235}
236
237#[derive(Debug, Clone, Serialize, Deserialize)]
238#[serde(rename_all = "camelCase")]
239pub struct ConfigChange {
240 pub path: Vec<ConfigChangePathItem>,
242 #[serde(flatten)]
243 pub kind: ConfigChangeKind,
244}
245
246#[derive(Debug, Clone, Serialize, Deserialize)]
247#[serde(tag = "kind", content = "value")]
248pub enum ConfigChangeKind {
249 Add(ConfigKeyValue),
251 Set(ConfigKeyValue),
253 Remove,
255}
256
257#[derive(Clone, Serialize)]
258#[serde(rename_all = "camelCase")]
259pub struct PluginResolveConfigurationResult<T>
260where
261 T: Clone + Serialize,
262{
263 pub file_matching: FileMatchingInfo,
265
266 pub diagnostics: Vec<ConfigurationDiagnostic>,
268
269 pub config: T,
272}
273
274#[cfg(feature = "process")]
276#[crate::async_runtime::async_trait(?Send)]
277pub trait AsyncPluginHandler: 'static {
278 type Configuration: Serialize + Clone + Send + Sync;
279
280 fn plugin_info(&self) -> PluginInfo;
282 fn license_text(&self) -> String;
284 async fn resolve_config(&self, config: ConfigKeyMap, global_config: GlobalConfiguration) -> PluginResolveConfigurationResult<Self::Configuration>;
286 async fn check_config_updates(&self, _message: CheckConfigUpdatesMessage) -> Result<Vec<ConfigChange>, FormatError> {
289 Ok(Vec::new())
290 }
291 async fn format(
293 &self,
294 request: FormatRequest<Self::Configuration>,
295 format_with_host: impl FnMut(HostFormatRequest) -> LocalBoxFuture<'static, FormatResult> + 'static,
296 ) -> FormatResult;
297}
298
299#[cfg(feature = "wasm")]
301pub trait SyncPluginHandler<TConfiguration: Clone + serde::Serialize> {
302 fn resolve_config(&mut self, config: ConfigKeyMap, global_config: &GlobalConfiguration) -> PluginResolveConfigurationResult<TConfiguration>;
304 fn plugin_info(&mut self) -> PluginInfo;
306 fn license_text(&mut self) -> String;
308 fn check_config_updates(&self, message: CheckConfigUpdatesMessage) -> Result<Vec<ConfigChange>, FormatError>;
311 fn format(&mut self, request: SyncFormatRequest<TConfiguration>, format_with_host: impl FnMut(SyncHostFormatRequest) -> FormatResult) -> FormatResult;
313}