use async_trait::async_trait;
use std::sync::Arc;
use crate::error::StateResult;
use crate::plugin::{PluginConfig, PluginMetadata};
use crate::resource::Resource;
use crate::state::{State, StateConfig};
use crate::transaction::Transaction;
#[async_trait]
pub trait PluginTrait: Send + Sync + Debug {
fn metadata(&self) -> PluginMetadata;
fn config(&self) -> PluginConfig {
PluginConfig {
enabled: true,
priority: 0,
settings: std::collections::HashMap::new(),
}
}
async fn append_transaction(
&self,
_: &[Arc<Transaction>],
_: &Arc<State>,
_: &Arc<State>,
) -> StateResult<Option<Transaction>> {
Ok(None)
}
async fn filter_transaction(
&self,
_: &Transaction,
_: &State,
) -> bool {
true
}
}
#[async_trait]
pub trait StateField: Send + Sync + Debug {
type Value: Resource;
async fn init(
&self,
config: &StateConfig,
instance: &State,
) -> Arc<Self::Value>;
async fn apply(
&self,
tr: &Transaction,
value: Arc<Self::Value>,
old_state: &State,
new_state: &State,
) -> Arc<Self::Value>;
fn serialize(
&self,
_value: &Arc<Self::Value>,
) -> Option<Vec<u8>> {
None
}
fn deserialize(
&self,
_data: &[u8],
) -> Option<Arc<Self::Value>> {
None
}
}
#[async_trait]
pub trait ErasedStateField: Send + Sync + Debug {
async fn init_erased(
&self,
config: &StateConfig,
instance: &State,
) -> Arc<dyn Resource>;
async fn apply_erased(
&self,
tr: &Transaction,
value: Arc<dyn Resource>,
old_state: &State,
new_state: &State,
) -> Arc<dyn Resource>;
fn serialize_erased(
&self,
value: Arc<dyn Resource>,
) -> Option<Vec<u8>>;
fn deserialize_erased(
&self,
data: &[u8],
) -> Option<Arc<dyn Resource>>;
}
#[async_trait]
impl<T: StateField + 'static> ErasedStateField for T {
#[cfg_attr(feature = "dev-tracing", tracing::instrument(skip(self, config, instance), fields(
crate_name = "state",
state_field_type = std::any::type_name::<T>(),
value_type = std::any::type_name::<T::Value>()
)))]
async fn init_erased(
&self,
config: &StateConfig,
instance: &State,
) -> Arc<dyn Resource> {
let value = self.init(config, instance).await;
value as Arc<dyn Resource>
}
#[cfg_attr(feature = "dev-tracing", tracing::instrument(skip(self, tr, value, old_state, new_state), fields(
crate_name = "state",
state_field_type = std::any::type_name::<T>(),
value_type = std::any::type_name::<T::Value>(),
tr_id = %tr.id
)))]
async fn apply_erased(
&self,
tr: &Transaction,
value: Arc<dyn Resource>,
old_state: &State,
new_state: &State,
) -> Arc<dyn Resource> {
if let Some(typed_value) = value.downcast_arc::<T::Value>() {
let new_value =
self.apply(tr, typed_value.clone(), old_state, new_state).await;
new_value as Arc<dyn Resource>
} else {
tracing::warn!(
"StateField 类型不匹配,期望 {},跳过应用",
std::any::type_name::<T::Value>()
);
value
}
}
#[cfg_attr(feature = "dev-tracing", tracing::instrument(skip(self, value), fields(
crate_name = "state",
state_field_type = std::any::type_name::<T>(),
value_type = std::any::type_name::<T::Value>()
)))]
fn serialize_erased(
&self,
value: Arc<dyn Resource>,
) -> Option<Vec<u8>> {
if let Some(typed_value) = value.downcast_arc::<T::Value>() {
let result = self.serialize(typed_value);
#[cfg(feature = "dev-tracing")]
if let Some(ref data) = result {
tracing::debug!(serialized_size = data.len(), "序列化成功");
}
result
} else {
None
}
}
#[cfg_attr(feature = "dev-tracing", tracing::instrument(skip(self, data), fields(
crate_name = "state",
state_field_type = std::any::type_name::<T>(),
value_type = std::any::type_name::<T::Value>(),
data_size = data.len()
)))]
fn deserialize_erased(
&self,
data: &[u8],
) -> Option<Arc<dyn Resource>> {
let result = self.deserialize(data).map(|v| v as Arc<dyn Resource>);
#[cfg(feature = "dev-tracing")]
if result.is_some() {
tracing::debug!("反序列化成功");
}
result
}
}
#[derive(Clone, Debug)]
pub struct PluginSpec {
pub state_field: Option<Arc<dyn ErasedStateField>>,
pub tr: Arc<dyn PluginTrait>,
}
impl PluginSpec {
#[cfg_attr(feature = "dev-tracing", tracing::instrument(skip(self, tr, state), fields(
crate_name = "state",
plugin_name = %self.tr.metadata().name,
tr_id = %tr.id
)))]
async fn filter_transaction(
&self,
tr: &Transaction,
state: &State,
) -> bool {
let filter = &self.tr;
let result = filter.filter_transaction(tr, state).await;
#[cfg(feature = "dev-tracing")]
tracing::debug!(allowed = result, "过滤结果");
result
}
#[cfg_attr(feature = "dev-tracing", tracing::instrument(skip(self, trs, old_state, new_state), fields(
crate_name = "state",
plugin_name = %self.tr.metadata().name,
tr_count = trs.len()
)))]
async fn append_transaction(
&self,
trs: &[Arc<Transaction>],
old_state: &Arc<State>,
new_state: &Arc<State>,
) -> StateResult<Option<Transaction>> {
let tr = self.tr.append_transaction(trs, old_state, new_state).await?;
if let Some(mut tr) = tr {
let _ = tr.commit(); #[cfg(feature = "dev-tracing")]
tracing::debug!(step_count = tr.steps.len(), "追加事务成功");
Ok(Some(tr))
} else {
#[cfg(feature = "dev-tracing")]
tracing::debug!("无需追加事务");
Ok(None)
}
}
}
#[derive(Clone, Debug)]
pub struct Plugin {
pub spec: PluginSpec,
pub key: String,
}
impl Plugin {
pub fn new(spec: PluginSpec) -> Self {
let key = spec.tr.metadata().name.clone();
Plugin { spec, key }
}
pub fn get_name(&self) -> &str {
&self.key
}
pub fn get_metadata(&self) -> PluginMetadata {
self.spec.tr.metadata()
}
pub fn get_config(&self) -> PluginConfig {
self.spec.tr.config()
}
pub fn get_state(
&self,
state: &State,
) -> Option<Arc<dyn Resource>> {
state.get_field(&self.key)
}
#[cfg_attr(feature = "dev-tracing", tracing::instrument(skip(self, tr, state), fields(
crate_name = "state",
plugin_key = %self.key,
tr_id = %tr.id
)))]
pub async fn apply_filter_transaction(
&self,
tr: &Transaction,
state: &State,
) -> bool {
self.spec.filter_transaction(tr, state).await
}
#[cfg_attr(feature = "dev-tracing", tracing::instrument(skip(self, trs, old_state, new_state), fields(
crate_name = "state",
plugin_key = %self.key,
tr_count = trs.len()
)))]
pub async fn apply_append_transaction(
&self,
trs: &[Arc<Transaction>],
old_state: &Arc<State>,
new_state: &Arc<State>,
) -> StateResult<Option<Transaction>> {
self.spec.append_transaction(trs, old_state, new_state).await
}
}
use std::fmt::Debug;