spacegate_config/service/fs/
model.rs1use std::{
2 collections::HashMap,
3 hash::{DefaultHasher, Hasher},
4};
5
6use serde::{Deserialize, Serialize};
7use serde_json::Value;
8use spacegate_model::{constants::DEFAULT_API_PORT, ConfigItem, PluginInstanceId, PluginInstanceMap, PluginInstanceName, SgGateway, SgHttpRoute};
9
10#[derive(Debug, Serialize, Deserialize, Clone)]
11#[serde(untagged)]
12pub enum FsAsmPluginConfig {
13 Anon { uid: String, code: String, spec: Value },
14 Named { name: String, code: String },
15 Mono { code: String },
16}
17
18#[derive(Debug, Serialize, Deserialize, Clone)]
19#[serde(untagged)]
20pub(crate) enum FsAsmPluginConfigMaybeUninitialized {
21 Anon { uid: Option<String>, code: String, spec: Value },
22 Named { name: String, code: String },
23 Mono { code: String },
24}
25
26#[derive(Debug, Serialize, Deserialize, Clone)]
27
28pub struct FsNamedPluginConfig {
29 pub name: String,
30 pub code: String,
31 pub spec: Value,
32}
33
34#[derive(Debug, Serialize, Deserialize, Clone)]
35
36pub struct FsMonoPluginConfig {
37 pub code: String,
38 pub spec: Value,
39}
40
41#[derive(Debug, Serialize, Deserialize, Clone)]
42
43pub struct FsAnonPluginConfig {
44 pub code: String,
45 pub spec: Value,
46 pub uid: u64,
47}
48
49#[derive(Debug, Serialize, Deserialize, Clone)]
50#[serde(default)]
51pub struct MainFileConfig<P = FsAsmPluginConfig> {
52 pub gateways: Vec<MainFileConfigItem<P>>,
54 pub plugins: PluginConfigs,
55 pub api_port: u16,
56}
57
58impl<P> Default for MainFileConfig<P> {
59 fn default() -> Self {
60 MainFileConfig {
61 gateways: Default::default(),
62 plugins: Default::default(),
63 api_port: DEFAULT_API_PORT,
64 }
65 }
66}
67
68#[derive(Debug, Serialize, Deserialize, Clone)]
69#[serde(default)]
70pub struct MainFileConfigItem<P = FsAsmPluginConfig> {
71 #[serde(flatten)]
72 pub gateway: SgGateway<P>,
73 pub routes: Vec<SgHttpRoute<P>>,
74}
75impl<P> Default for MainFileConfigItem<P> {
76 fn default() -> Self {
77 MainFileConfigItem {
78 gateway: Default::default(),
79 routes: Default::default(),
80 }
81 }
82}
83impl<P> From<ConfigItem<P>> for MainFileConfigItem<P> {
84 fn from(value: ConfigItem<P>) -> Self {
85 MainFileConfigItem {
86 gateway: value.gateway,
87 routes: value.routes.into_values().collect(),
88 }
89 }
90}
91
92impl<P> From<MainFileConfigItem<P>> for ConfigItem<P> {
93 fn from(val: MainFileConfigItem<P>) -> Self {
94 ConfigItem {
95 gateway: val.gateway,
96 routes: val.routes.into_iter().map(|route| (route.route_name.clone(), route)).collect(),
97 }
98 }
99}
100
101impl<P> MainFileConfigItem<P> {
102 pub fn map_plugins<F, T>(self, mut f: F) -> MainFileConfigItem<T>
103 where
104 F: FnMut(P) -> T,
105 {
106 MainFileConfigItem {
107 gateway: self.gateway.map_plugins(&mut f),
108 routes: self.routes.into_iter().map(|route| route.map_plugins(&mut f)).collect(),
109 }
110 }
111}
112
113#[derive(Default, Debug, Serialize, Deserialize, Clone)]
114#[serde(default)]
115pub struct PluginConfigs {
116 pub named: Vec<FsNamedPluginConfig>,
117 pub mono: Vec<FsMonoPluginConfig>,
118}
119
120impl MainFileConfig<FsAsmPluginConfigMaybeUninitialized> {
121 pub fn initialize_uid(self) -> MainFileConfig<FsAsmPluginConfig> {
122 let mut hasher = DefaultHasher::new();
123 let mut set_uid = move |p: FsAsmPluginConfigMaybeUninitialized| match p {
124 FsAsmPluginConfigMaybeUninitialized::Anon { uid, code, spec } => {
125 let uid = if let Some(uid) = uid {
126 hasher.write(uid.as_bytes());
127 uid
128 } else {
129 hasher.write(code.as_bytes());
130 hasher.write(spec.to_string().as_bytes());
131 format!("{:016x}", hasher.finish())
132 };
133 FsAsmPluginConfig::Anon { uid, code, spec }
134 }
135 FsAsmPluginConfigMaybeUninitialized::Named { name, code } => FsAsmPluginConfig::Named { name, code },
136 FsAsmPluginConfigMaybeUninitialized::Mono { code } => FsAsmPluginConfig::Mono { code },
137 };
138 let gateways = self.gateways.into_iter().map(|item| item.map_plugins(&mut set_uid)).collect();
139 MainFileConfig {
140 gateways,
141 plugins: self.plugins,
142 api_port: self.api_port,
143 }
144 }
145}
146
147impl MainFileConfig<FsAsmPluginConfig> {
148 pub fn into_model_config(self) -> spacegate_model::Config {
149 let mut plugins = PluginInstanceMap::default();
150 for named in self.plugins.named {
151 let id = PluginInstanceId {
152 code: named.code.into(),
153 name: spacegate_model::PluginInstanceName::Named { name: named.name },
154 };
155 plugins.insert(id.clone(), named.spec);
156 }
157 for mono in self.plugins.mono {
158 let id = PluginInstanceId {
159 code: mono.code.into(),
160 name: spacegate_model::PluginInstanceName::Mono {},
161 };
162 plugins.insert(id.clone(), mono.spec);
163 }
164 let mut collect_plugin = |p: FsAsmPluginConfig| match p {
165 FsAsmPluginConfig::Anon { uid, code, spec } => {
166 let id = PluginInstanceId {
167 code: code.into(),
168 name: spacegate_model::PluginInstanceName::Anon { uid },
169 };
170 plugins.insert(id.clone(), spec);
171 id
172 }
173 FsAsmPluginConfig::Named { name, code } => PluginInstanceId {
174 code: code.into(),
175 name: spacegate_model::PluginInstanceName::Named { name },
176 },
177 FsAsmPluginConfig::Mono { code } => PluginInstanceId {
178 code: code.into(),
179 name: spacegate_model::PluginInstanceName::Mono {},
180 },
181 };
182 let gateways = self
183 .gateways
184 .into_iter()
185 .map(|item| {
186 let config_item: ConfigItem = item.map_plugins(&mut collect_plugin).into();
187 (config_item.gateway.name.clone(), config_item)
188 })
189 .collect();
190 spacegate_model::Config {
191 gateways,
192 plugins,
193 api_port: Some(self.api_port),
194 }
195 }
196}
197
198impl From<spacegate_model::Config> for MainFileConfig<FsAsmPluginConfig> {
199 fn from(value: spacegate_model::Config) -> Self {
200 let mut plugins = PluginConfigs::default();
201 let mut anon_plugins = HashMap::new();
202 for (id, spec) in value.plugins.into_inner() {
203 match id.name {
204 PluginInstanceName::Anon { uid } => {
205 anon_plugins.insert(uid, spec);
206 }
207 PluginInstanceName::Named { name } => plugins.named.push(FsNamedPluginConfig { name, code: id.code.into(), spec }),
208 PluginInstanceName::Mono {} => plugins.mono.push(FsMonoPluginConfig { code: id.code.into(), spec }),
209 }
210 }
211 let mut map = |id: PluginInstanceId| match id.name {
212 PluginInstanceName::Anon { uid } => {
213 if let Some(spec) = anon_plugins.remove(&uid) {
214 FsAsmPluginConfig::Anon { uid, code: id.code.into(), spec }
215 } else {
216 FsAsmPluginConfig::Anon {
217 uid,
218 code: id.code.into(),
219 spec: Default::default(),
220 }
221 }
222 }
223 PluginInstanceName::Named { name } => FsAsmPluginConfig::Named { name, code: id.code.into() },
224 PluginInstanceName::Mono {} => FsAsmPluginConfig::Mono { code: id.code.into() },
225 };
226 let gateways = value.gateways.into_values().map(|item| <MainFileConfigItem<FsAsmPluginConfig>>::from(item.map_plugins(&mut map))).collect();
227 MainFileConfig {
228 gateways,
229 plugins,
230 api_port: value.api_port.unwrap_or(DEFAULT_API_PORT),
231 }
232 }
233}