openzeppelin_relayer/config/config_file/
plugin.rs1use std::collections::HashSet;
2
3use crate::config::ConfigFileError;
4use serde::{Deserialize, Serialize};
5use serde_json::Map;
6
7const PLUGIN_FILE_TYPE: &str = ".ts";
11const PLUGIN_LANG: &str = "typescript";
12
13#[derive(Debug, Serialize, Deserialize, Clone)]
14pub struct PluginFileConfig {
15 pub id: String,
16 pub path: String,
17 pub timeout: Option<u64>,
18 #[serde(default)]
19 pub emit_logs: bool,
20 #[serde(default)]
21 pub emit_traces: bool,
22 #[serde(default)]
24 pub raw_response: bool,
25 #[serde(default)]
27 pub allow_get_invocation: bool,
28 pub config: Option<Map<String, serde_json::Value>>,
30 #[serde(default)]
31 pub forward_logs: bool,
32}
33
34pub struct PluginsFileConfig {
35 pub plugins: Vec<PluginFileConfig>,
36}
37
38impl PluginsFileConfig {
39 pub fn new(plugins: Vec<PluginFileConfig>) -> Self {
40 Self { plugins }
41 }
42
43 pub fn validate(&self) -> Result<(), ConfigFileError> {
44 let mut ids = HashSet::new();
45 for plugin in &self.plugins {
46 if !ids.insert(plugin.id.clone()) {
47 return Err(ConfigFileError::DuplicateId(plugin.id.clone()));
48 }
49
50 if plugin.id.is_empty() {
51 return Err(ConfigFileError::MissingField("id".into()));
52 }
53
54 if plugin.path.is_empty() {
55 return Err(ConfigFileError::MissingField("path".into()));
56 }
57
58 if let Some(timeout) = plugin.timeout {
60 if timeout == 0 {
61 return Err(ConfigFileError::InvalidTimeout(timeout));
62 }
63 }
64
65 if !plugin.path.ends_with(PLUGIN_FILE_TYPE) {
66 return Err(ConfigFileError::InvalidFormat(format!(
67 "Plugin path must be a {PLUGIN_LANG} file (ends with '{PLUGIN_FILE_TYPE}')"
68 )));
69 }
70 }
71
72 Ok(())
73 }
74}