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

ambee/giterated

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

Create `NetworkedSubstack`.

# giterated-protocol - Create `NetworkedSubstack` which will handle all networked operations giterated needs - Add support for `NetworkedSubstack` for both the daemon and client - Pipe everything through but leave APIs temp # `giterated-daemon` - Remove a bunch of random old code, dead code, and files that aren't needed. - Moved all connection handling to `client.rs`, simplified connection logic with new types

Amber - ⁨2⁩ years ago

parent: tbd commit: ⁨202bb12

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