JavaScript is disabled, refresh for a better experience. ambee/giterated

ambee/giterated

Git repository hosting, collaboration, and discovery for the Fediverse.

Re-expose Operation State in generics.

This is the worst code I have ever written. I hate the way this changes everything. ugh.

Amber - ⁨2⁩ years ago

parent: tbd commit: ⁨90db3e2

⁨giterated-protocol/src/substack.rs⁩ - ⁨7864⁩ bytes
Raw
1 use std::{fmt::Display, str::FromStr, sync::Arc};
2
3 use giterated_stack::{
4 models::{Error, GiteratedObject, GiteratedOperation, IntoInternalError, OperationError},
5 AnyFailure, AnyObject, AnyOperation, AnySuccess, GiteratedStack, GiteratedStackInner,
6 ObjectOperationPair, OperationState, StackOperationState, SubstackBuilder,
7 };
8 use serde::{Deserialize, Serialize};
9 use tracing::{trace, warn};
10
11 use crate::NetworkOperationState;
12
13 /// A Giterated substack that attempts to resolve with a remote, networked Giterated Daemon.
14 ///
15 /// # Usage
16 ///
17 /// Convert the [`NetworkedSubstack`] into a [`SubStackBuilder<NetworkedSubstack>`] and merge it with
18 /// a runtime.
19 ///
20 /// ```
21 /// let mut runtime = GiteratedStack::default();
22 ///
23 /// let network_substack = NetworkedSubstack::default();
24 ///
25 /// runtime.merge_builder(network_substack.into_substack());
26 /// ```
27 ///
28 /// To handle messages that are sourced from the network, use [`NetworkedObject`] and [`NetworkedOperation`].
29 ///
30 /// These are wrappers around the raw payloads from the network. The return payload from handling [`NetworkedOperation`] is then
31 /// sent back to the requester.
32 ///
33 /// ```
34 /// // Start with a network payload
35 /// let network_payload: AuthenticatedPayload = { todo!() };
36 ///
37 /// let networked_object = runtime.get_object::<NetworkedObject>(network_payload.object).await?;
38 /// let operation_name = payload.operation;
39 /// let networked_operation = NetworkedOperation(payload);
40 ///
41 /// // Operation state depends on the authentication in the payload, it
42 /// // isn't relevant here.
43 /// let operation_state = StackOperationState::default();
44 ///
45 /// let result = networked_object.request(networked_operation, &operation_state);
46 ///
47 /// // `result` is Result<Vec<u8>, OperationError<Vec<u8>> which is also the type that
48 /// // giterated's networked protocol uses for responses, so you can send it directly.
49 /// ```
50 ///
51 /// TODO: The above docs are 100% false about the network protocol type
52 #[derive(Clone)]
53 pub struct NetworkedSubstack {
54 home_uri: Option<String>,
55 }
56
57 impl Default for NetworkedSubstack {
58 fn default() -> Self {
59 Self { home_uri: None }
60 }
61 }
62
63 impl NetworkedSubstack {
64 pub fn into_server_substack(self) -> SubstackBuilder<Self, StackOperationState> {
65 let mut stack = SubstackBuilder::new(self);
66
67 stack.operation(handle_network_operation::<StackOperationState>);
68
69 // TODO: optional
70 stack.dynamic_operation(try_handle_with_remote);
71
72 stack
73 }
74
75 pub fn into_client_substack(self) -> SubstackBuilder<Self, NetworkOperationState> {
76 let mut stack = SubstackBuilder::new(self);
77
78 // TODO: optional
79 stack.dynamic_operation(try_handle_with_remote);
80
81 stack
82 }
83 }
84
85 pub async fn handle_network_operation<OS: Send + Sync + Clone + 'static>(
86 object: NetworkedObject,
87 operation: NetworkedOperation,
88 _state: NetworkedSubstack,
89 OperationState(operation_state): OperationState<OS>,
90 stack: GiteratedStack<OS>,
91 ) -> Result<Vec<u8>, OperationError<Vec<u8>>> {
92 trace!("Handle network operation");
93 let mut result = None;
94
95 for (_, object_meta) in &stack.inner.metadata.objects {
96 if let Ok(object) = (object_meta.from_str)(&object.0) {
97 // TODO: This is definitely going to resolve us
98 result = Some((object, object_meta));
99 break;
100 }
101 }
102
103 let (object, object_meta) = result.ok_or_else(|| OperationError::Unhandled)?;
104
105 trace!(
106 "Resolved object type {} for network operation.",
107 object_meta.name
108 );
109
110 let operation_meta = stack
111 .inner
112 .metadata
113 .operations
114 .get(&ObjectOperationPair {
115 object_name: &object_meta.name,
116 operation_name: &operation.name,
117 })
118 .ok_or_else(|| OperationError::Unhandled)?;
119
120 trace!(
121 "Resolved operation {}::{} for network operation.",
122 object_meta.name,
123 operation_meta.name
124 );
125
126 let operation = (operation_meta.deserialize)(&operation.payload)
127 .as_internal_error_with_context(format!(
128 "deserializing object operation {}::{}",
129 object_meta.name, operation_meta.name
130 ))?;
131
132 trace!(
133 "Deserialized operation {}::{} for network operation.",
134 object_meta.name,
135 operation_meta.name
136 );
137
138 let result = stack
139 .new_operation_func(object, operation, operation_state)
140 .await;
141
142 match result {
143 Ok(success) => Ok((operation_meta.serialize_success)(success)
144 .as_internal_error_with_context(format!(
145 "serializing success for object operation {}::{}",
146 object_meta.name, operation_meta.name
147 ))?),
148 Err(err) => Err(match err {
149 OperationError::Operation(failure) => OperationError::Operation(
150 (operation_meta.serialize_error)(failure).as_internal_error_with_context(
151 format!(
152 "serializing error for object operation {}::{}",
153 object_meta.name, operation_meta.name
154 ),
155 )?,
156 ),
157 OperationError::Internal(internal) => OperationError::Internal(internal),
158 OperationError::Unhandled => OperationError::Unhandled,
159 }),
160 }
161 }
162
163 #[derive(Clone, Debug, Serialize, Deserialize)]
164 pub struct NetworkedObject(pub String);
165
166 impl FromStr for NetworkedObject {
167 type Err = ();
168
169 fn from_str(_s: &str) -> Result<Self, Self::Err> {
170 todo!()
171 }
172 }
173
174 impl Display for NetworkedObject {
175 fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
176 todo!()
177 }
178 }
179
180 impl GiteratedObject for NetworkedObject {
181 fn object_name() -> &'static str {
182 todo!()
183 }
184
185 fn from_object_str(_object_str: &str) -> Result<Self, Error> {
186 todo!()
187 }
188 }
189
190 #[derive(Clone, Debug, Serialize, Deserialize)]
191 pub struct NetworkedOperation {
192 name: String,
193 payload: Vec<u8>,
194 }
195
196 impl NetworkedOperation {
197 pub fn new(_name: String, _payload: Vec<u8>) -> Self {
198 todo!()
199 }
200 }
201
202 impl GiteratedOperation<NetworkedObject> for NetworkedOperation {
203 type Success = Vec<u8>;
204
205 type Failure = Vec<u8>;
206
207 fn operation_name() -> &'static str {
208 "network_operation"
209 }
210 }
211
212 /// Handler which will attempt to resolve any operation that doesn't resolve locally
213 /// against a remote instance.
214 pub async fn try_handle_with_remote(
215 object: AnyObject,
216 operation: AnyOperation,
217 state: NetworkedSubstack,
218 _operation_state: StackOperationState,
219 stack: Arc<GiteratedStackInner>,
220 ) -> Result<AnySuccess, OperationError<AnyFailure>> {
221 trace!(
222 "Try handling object operation {}::{} with remote",
223 object.kind(),
224 operation.kind().operation_name
225 );
226 // TODO:
227 // Ideally we support pass-through on object types that aren't used locally.
228 // For now, we aren't worrying about that.
229 let object_meta = stack
230 .metadata
231 .objects
232 .get(object.kind())
233 .ok_or_else(|| OperationError::Unhandled)?;
234
235 let _operation_meta = stack
236 .metadata
237 .operations
238 .get(&operation.kind())
239 .ok_or_else(|| OperationError::Unhandled)?;
240
241 let object_home_uri = (object_meta.home_uri)(object.clone());
242
243 if let Some(home_uri) = state.home_uri {
244 if home_uri == object_home_uri {
245 // This isn't a remote request, requests aren't supposed to hit this layer
246 // if they're not remote.
247 warn!("Try handling object operation {}::{}, resolved object home uri as local home uri. This is a bug.", object.kind(),
248 operation.kind().operation_name);
249
250 return Err(OperationError::Unhandled);
251 }
252 }
253
254 // Blah blah connect and do the stuff
255
256 todo!()
257 }
258