use anyhow::Result;
use serde::Deserialize;
use serde::Serialize;
#[cfg(feature = "async_runtime")]
use crate::async_runtime::FutureExt;
#[cfg(feature = "async_runtime")]
use crate::async_runtime::LocalBoxFuture;
use crate::configuration::ConfigKeyMap;
use crate::configuration::ConfigKeyValue;
use crate::configuration::ConfigurationDiagnostic;
use crate::configuration::GlobalConfiguration;
use crate::plugins::PluginInfo;
use super::FileMatchingInfo;
pub trait CancellationToken: Send + Sync + std::fmt::Debug {
fn is_cancelled(&self) -> bool;
#[cfg(feature = "async_runtime")]
fn wait_cancellation(&self) -> LocalBoxFuture<'static, ()>;
}
#[cfg(feature = "async_runtime")]
impl CancellationToken for tokio_util::sync::CancellationToken {
fn is_cancelled(&self) -> bool {
self.is_cancelled()
}
fn wait_cancellation(&self) -> LocalBoxFuture<'static, ()> {
let token = self.clone();
async move { token.cancelled().await }.boxed_local()
}
}
#[derive(Debug)]
pub struct NullCancellationToken;
impl CancellationToken for NullCancellationToken {
fn is_cancelled(&self) -> bool {
false
}
#[cfg(feature = "async_runtime")]
fn wait_cancellation(&self) -> LocalBoxFuture<'static, ()> {
Box::pin(std::future::pending())
}
}
pub type FormatRange = Option<std::ops::Range<usize>>;
#[derive(Debug)]
pub struct CriticalFormatError(pub anyhow::Error);
impl std::fmt::Display for CriticalFormatError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f)
}
}
impl std::error::Error for CriticalFormatError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
self.0.source()
}
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CheckConfigUpdatesMessage {
#[serde(default)]
pub old_version: Option<String>,
pub config: ConfigKeyMap,
}
#[cfg(feature = "process")]
#[derive(Debug)]
pub struct HostFormatRequest {
pub file_path: std::path::PathBuf,
pub file_bytes: Vec<u8>,
pub range: FormatRange,
pub override_config: ConfigKeyMap,
pub token: std::sync::Arc<dyn CancellationToken>,
}
#[cfg(feature = "wasm")]
#[derive(Debug)]
pub struct SyncHostFormatRequest<'a> {
pub file_path: &'a std::path::Path,
pub file_bytes: &'a [u8],
pub range: FormatRange,
pub override_config: &'a ConfigKeyMap,
}
pub type FormatResult = Result<Option<Vec<u8>>>;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RawFormatConfig {
pub plugin: ConfigKeyMap,
pub global: GlobalConfiguration,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct FormatConfigId(u32);
impl std::fmt::Display for FormatConfigId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "${}", self.0)
}
}
impl FormatConfigId {
pub fn from_raw(raw: u32) -> FormatConfigId {
FormatConfigId(raw)
}
pub fn uninitialized() -> FormatConfigId {
FormatConfigId(0)
}
pub fn as_raw(&self) -> u32 {
self.0
}
}
#[cfg(feature = "process")]
pub struct FormatRequest<TConfiguration> {
pub file_path: std::path::PathBuf,
pub file_bytes: Vec<u8>,
pub config_id: FormatConfigId,
pub config: std::sync::Arc<TConfiguration>,
pub range: FormatRange,
pub token: std::sync::Arc<dyn CancellationToken>,
}
#[cfg(feature = "wasm")]
pub struct SyncFormatRequest<'a, TConfiguration> {
pub file_path: &'a std::path::Path,
pub file_bytes: Vec<u8>,
pub config_id: FormatConfigId,
pub config: &'a TConfiguration,
pub range: FormatRange,
pub token: &'a dyn CancellationToken,
}
#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
#[serde(untagged)]
pub enum ConfigChangePathItem {
String(String),
Number(usize),
}
impl From<String> for ConfigChangePathItem {
fn from(value: String) -> Self {
Self::String(value)
}
}
impl From<usize> for ConfigChangePathItem {
fn from(value: usize) -> Self {
Self::Number(value)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ConfigChange {
pub path: Vec<ConfigChangePathItem>,
#[serde(flatten)]
pub kind: ConfigChangeKind,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "kind", content = "value")]
pub enum ConfigChangeKind {
Add(ConfigKeyValue),
Set(ConfigKeyValue),
Remove,
}
#[derive(Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct PluginResolveConfigurationResult<T>
where
T: Clone + Serialize,
{
pub file_matching: FileMatchingInfo,
pub diagnostics: Vec<ConfigurationDiagnostic>,
pub config: T,
}
#[cfg(feature = "process")]
#[crate::async_runtime::async_trait(?Send)]
pub trait AsyncPluginHandler: 'static {
type Configuration: Serialize + Clone + Send + Sync;
fn plugin_info(&self) -> PluginInfo;
fn license_text(&self) -> String;
async fn resolve_config(&self, config: ConfigKeyMap, global_config: GlobalConfiguration) -> PluginResolveConfigurationResult<Self::Configuration>;
async fn check_config_updates(&self, _message: CheckConfigUpdatesMessage) -> Result<Vec<ConfigChange>> {
Ok(Vec::new())
}
async fn format(
&self,
request: FormatRequest<Self::Configuration>,
format_with_host: impl FnMut(HostFormatRequest) -> LocalBoxFuture<'static, FormatResult> + 'static,
) -> FormatResult;
}
#[cfg(feature = "wasm")]
pub trait SyncPluginHandler<TConfiguration: Clone + serde::Serialize> {
fn resolve_config(&mut self, config: ConfigKeyMap, global_config: &GlobalConfiguration) -> PluginResolveConfigurationResult<TConfiguration>;
fn plugin_info(&mut self) -> PluginInfo;
fn license_text(&mut self) -> String;
fn check_config_updates(&self, message: CheckConfigUpdatesMessage) -> Result<Vec<ConfigChange>>;
fn format(&mut self, request: SyncFormatRequest<TConfiguration>, format_with_host: impl FnMut(SyncHostFormatRequest) -> FormatResult) -> FormatResult;
}