More restructuring
parent: tbd commit: 10b7b7c
Showing 124 changed files with 4662 insertions and 4609 deletions
Cargo.lock
@@ -689,6 +689,7 @@ name = "giterated-abi" | ||
689 | 689 | version = "0.1.0" |
690 | 690 | dependencies = [ |
691 | 691 | "anyhow", |
692 | "dlopen2", | |
692 | 693 | "giterated-models", |
693 | 694 | ] |
694 | 695 | |
@@ -712,6 +713,13 @@ dependencies = [ | ||
712 | 713 | [[package]] |
713 | 714 | name = "giterated-core" |
714 | 715 | version = "0.1.0" |
716 | dependencies = [ | |
717 | "anyhow", | |
718 | "giterated-abi", | |
719 | "giterated-models", | |
720 | "giterated-static-runtime", | |
721 | "tracing", | |
722 | ] | |
715 | 723 | |
716 | 724 | [[package]] |
717 | 725 | name = "giterated-daemon" |
@@ -843,6 +851,13 @@ dependencies = [ | ||
843 | 851 | [[package]] |
844 | 852 | name = "giterated-runtime" |
845 | 853 | version = "0.1.0" |
854 | dependencies = [ | |
855 | "dlopen2", | |
856 | "giterated-abi", | |
857 | "giterated-core", | |
858 | "giterated-models", | |
859 | "tracing", | |
860 | ] | |
846 | 861 | |
847 | 862 | [[package]] |
848 | 863 | name = "giterated-static-runtime" |
Cargo.toml
@@ -1,9 +1,9 @@ | ||
1 | 1 | [workspace] |
2 | members = [ "giterated-abi", "giterated-core", | |
2 | members = [ "giterated-runtime/giterated-abi", "giterated-core", | |
3 | 3 | "giterated-daemon", |
4 | "giterated-models", | |
4 | "giterated-core/giterated-models", | |
5 | 5 | "giterated-plugin", |
6 | "giterated-macros", | |
6 | "giterated-plugin/giterated-macros", | |
7 | 7 | "giterated-runtime/giterated-static-runtime", |
8 | 8 | "giterated-runtime", |
9 | 9 | "plugins/example-plugin", |
giterated-core/Cargo.toml
@@ -6,3 +6,8 @@ edition = "2021" | ||
6 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html |
7 | 7 | |
8 | 8 | [dependencies] |
9 | giterated-abi = { path = "../giterated-runtime/giterated-abi"} | |
10 | giterated-static-runtime = { path = "../giterated-runtime/giterated-static-runtime"} | |
11 | giterated-models = { path = "giterated-models" } | |
12 | anyhow = "1" | |
13 | tracing = "0.1" |
giterated-core/giterated-models/Cargo.toml
@@ -0,0 +1,41 @@ | ||
1 | [package] | |
2 | name = "giterated-models" | |
3 | version = "0.1.0" | |
4 | authors = ["Amber Kowalski"] | |
5 | edition = "2021" | |
6 | rust-version = "1.70.0" | |
7 | description = "Giterated's Data Models" | |
8 | homepage = "https://giterated.dev/ambee/giterated" | |
9 | repository = "https://giterated.dev/ambee/giterated" | |
10 | license = "MIT OR Apache-2.0" | |
11 | keywords = ["giterated"] | |
12 | ||
13 | # Leave until MVP | |
14 | publish = false | |
15 | ||
16 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | |
17 | ||
18 | [dependencies] | |
19 | tracing = "0.1" | |
20 | serde = { version = "1.0", features = [ "derive" ]} | |
21 | serde_json = "1.0" | |
22 | base64 = "0.21" | |
23 | jsonwebtoken = { version = "9.1", features = ["use_pem"]} | |
24 | rand = "0.8" | |
25 | rsa = {version = "0.9", features = ["sha2"]} | |
26 | semver = {version = "1.0", features = ["serde"]} | |
27 | bincode = "1.3" | |
28 | secrecy = { version = "0.8", features = ["serde"] } | |
29 | thiserror = "1" | |
30 | anyhow = "1" | |
31 | toml = { version = "0.8" } | |
32 | # Git backend | |
33 | git2 = "0.18" | |
34 | chrono = { version = "0.4", features = [ "serde" ] } | |
35 | async-trait = "0.1" | |
36 | url = {version = "2.4", features = ["serde"]} | |
37 | ||
38 | # Git backend | |
39 | sqlx = { version = "0.7", default-features = false, features = [ "macros", "chrono" ] } | |
40 | ||
41 | #uuid = { version = "1.4", features = [ "v4", "serde" ] } |
giterated-core/giterated-models/src/authenticated.rs
@@ -0,0 +1,41 @@ | ||
1 | use serde::{Deserialize, Serialize}; | |
2 | ||
3 | use crate::{instance::Instance, user::User}; | |
4 | ||
5 | #[repr(transparent)] | |
6 | #[derive(Clone, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)] | |
7 | pub struct UserAuthenticationToken(String); | |
8 | ||
9 | impl From<String> for UserAuthenticationToken { | |
10 | fn from(value: String) -> Self { | |
11 | Self(value) | |
12 | } | |
13 | } | |
14 | ||
15 | impl ToString for UserAuthenticationToken { | |
16 | fn to_string(&self) -> String { | |
17 | self.0.clone() | |
18 | } | |
19 | } | |
20 | ||
21 | impl AsRef<str> for UserAuthenticationToken { | |
22 | fn as_ref(&self) -> &str { | |
23 | &self.0 | |
24 | } | |
25 | } | |
26 | ||
27 | #[derive(Clone, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)] | |
28 | pub struct InstanceSignature(pub Vec<u8>); | |
29 | ||
30 | impl AsRef<[u8]> for InstanceSignature { | |
31 | fn as_ref(&self) -> &[u8] { | |
32 | &self.0 | |
33 | } | |
34 | } | |
35 | ||
36 | #[derive(Debug, Serialize, Deserialize)] | |
37 | pub struct UserTokenMetadata { | |
38 | pub user: User, | |
39 | pub generated_for: Instance, | |
40 | pub exp: u64, | |
41 | } |
giterated-core/giterated-models/src/discovery.rs
@@ -0,0 +1,15 @@ | ||
1 | use serde::{Deserialize, Serialize}; | |
2 | ||
3 | use crate::{instance::Instance, repository::Repository}; | |
4 | ||
5 | #[derive(Clone, Hash, PartialEq, Eq, Debug, Serialize, Deserialize)] | |
6 | pub enum DiscoveryItem { | |
7 | Instance { | |
8 | instance: Instance, | |
9 | signature: Vec<u8>, | |
10 | }, | |
11 | Repository { | |
12 | repository: Repository, | |
13 | signature: Vec<u8>, | |
14 | }, | |
15 | } |
giterated-core/giterated-models/src/error.rs
@@ -0,0 +1,97 @@ | ||
1 | use std::fmt::Display; | |
2 | ||
3 | use serde::{Deserialize, Serialize}; | |
4 | ||
5 | #[derive(Debug, thiserror::Error, Deserialize, Serialize, Clone)] | |
6 | pub enum InstanceError { | |
7 | #[error("registration failed")] | |
8 | RegistrationFailure, | |
9 | #[error("authentication failed")] | |
10 | AuthenticationFailed, | |
11 | } | |
12 | ||
13 | #[derive(Debug, thiserror::Error, Serialize, Deserialize, Clone)] | |
14 | pub enum RepositoryError {} | |
15 | ||
16 | #[derive(Debug, thiserror::Error, Deserialize, Serialize, Clone)] | |
17 | pub enum UserError {} | |
18 | ||
19 | #[derive(Debug, thiserror::Error, Serialize, Deserialize, Clone)] | |
20 | pub enum GetValueError { | |
21 | #[error("invalid object")] | |
22 | InvalidObject, | |
23 | } | |
24 | ||
25 | #[derive(Debug, thiserror::Error, Clone)] | |
26 | #[error("unauthorized")] | |
27 | pub struct UnauthorizedError; | |
28 | ||
29 | #[derive(Debug, thiserror::Error)] | |
30 | pub enum OperationError<B> { | |
31 | #[error("the operation was handled but an error occurred")] | |
32 | Operation(#[from] B), | |
33 | #[error("an internal error occurred: {0:?}")] | |
34 | Internal(anyhow::Error), | |
35 | #[error("the operation was unhandled or unrecognized")] | |
36 | Unhandled, | |
37 | } | |
38 | ||
39 | pub trait IntoInternalError<T>: Sized { | |
40 | fn as_internal_error<B>(self) -> Result<T, OperationError<B>>; | |
41 | ||
42 | fn as_internal_error_with_context<B, C>(self, context: C) -> Result<T, OperationError<B>> | |
43 | where | |
44 | C: Display + Send + Sync + 'static, | |
45 | { | |
46 | let internal_error = self.as_internal_error::<B>(); | |
47 | ||
48 | match internal_error { | |
49 | Ok(success) => Ok(success), | |
50 | Err(OperationError::Internal(internal)) => { | |
51 | Err(OperationError::Internal(internal.context(context))) | |
52 | } | |
53 | _ => unreachable!(), | |
54 | } | |
55 | } | |
56 | } | |
57 | ||
58 | impl<E: Into<anyhow::Error>, T> IntoInternalError<T> for Result<T, E> { | |
59 | fn as_internal_error<B>(self) -> Result<T, OperationError<B>> { | |
60 | match self { | |
61 | Ok(success) => Ok(success), | |
62 | Err(err) => Err(OperationError::Internal(err.into())), | |
63 | } | |
64 | } | |
65 | } | |
66 | ||
67 | impl<B> OperationError<B> { | |
68 | pub fn into_network(self) -> NetworkOperationError<B> { | |
69 | match self { | |
70 | OperationError::Operation(operation_error) => { | |
71 | NetworkOperationError::Operation(operation_error) | |
72 | } | |
73 | OperationError::Internal(_) => NetworkOperationError::Internal, | |
74 | OperationError::Unhandled => NetworkOperationError::Unhandled, | |
75 | } | |
76 | } | |
77 | } | |
78 | ||
79 | #[derive(Debug, thiserror::Error)] | |
80 | #[error("an extractor failed with an error {0}")] | |
81 | pub struct ExtractorError<E: Into<anyhow::Error>>(#[from] pub E); | |
82 | ||
83 | impl<T, E: Into<anyhow::Error>> IntoInternalError<T> for ExtractorError<E> { | |
84 | fn as_internal_error<B>(self) -> Result<T, OperationError<B>> { | |
85 | todo!() | |
86 | } | |
87 | } | |
88 | ||
89 | #[derive(Serialize, Deserialize, Debug, thiserror::Error)] | |
90 | pub enum NetworkOperationError<B> { | |
91 | #[error("the operation was handled but an error occurred")] | |
92 | Operation(#[from] B), | |
93 | #[error("internal error")] | |
94 | Internal, | |
95 | #[error("the operation was unhandled or unrecognized")] | |
96 | Unhandled, | |
97 | } |
giterated-core/giterated-models/src/ffi.rs
@@ -0,0 +1,13 @@ | ||
1 | pub trait ToFfi { | |
2 | fn to_ffi_bytes(&self) -> Vec<u8>; | |
3 | } | |
4 | ||
5 | pub trait FromFfi: Sized { | |
6 | fn from_ffi_bytes(bytes: &[u8]) -> Option<Self>; | |
7 | } | |
8 | ||
9 | pub trait FfiLabel { | |
10 | fn prefix(&self) -> &'static str; | |
11 | ||
12 | fn type_label(&self) -> &'static str; | |
13 | } |
giterated-core/giterated-models/src/handshake.rs
@@ -0,0 +1,22 @@ | ||
1 | use semver::Version; | |
2 | use serde::{Deserialize, Serialize}; | |
3 | ||
4 | use crate::instance::Instance; | |
5 | ||
6 | /// Sent by the initiator of a new inter-daemon connection. | |
7 | #[derive(Clone, Debug, Serialize, Deserialize)] | |
8 | pub struct InitiateHandshake { | |
9 | pub version: Version, | |
10 | } | |
11 | ||
12 | /// Sent in response to [`InitiateHandshake`] | |
13 | #[derive(Clone, Debug, Serialize, Deserialize)] | |
14 | pub struct HandshakeResponse { | |
15 | pub identity: Instance, | |
16 | pub version: Version, | |
17 | } | |
18 | ||
19 | #[derive(Clone, Debug, Serialize, Deserialize)] | |
20 | pub struct HandshakeFinalize { | |
21 | pub success: bool, | |
22 | } |
giterated-core/giterated-models/src/instance/mod.rs
@@ -0,0 +1,77 @@ | ||
1 | use std::{fmt::Display, str::FromStr}; | |
2 | ||
3 | use serde::{Deserialize, Serialize}; | |
4 | use thiserror::Error; | |
5 | ||
6 | mod operations; | |
7 | mod values; | |
8 | ||
9 | pub use operations::*; | |
10 | use url::Url; | |
11 | ||
12 | use crate::object::GiteratedObject; | |
13 | ||
14 | pub struct InstanceMeta { | |
15 | pub url: String, | |
16 | pub public_key: String, | |
17 | } | |
18 | ||
19 | /// An instance, defined by the URL it can be reached at. | |
20 | /// | |
21 | /// # Textual Format | |
22 | /// An instance's textual format is its URL. | |
23 | /// | |
24 | /// ## Examples | |
25 | /// For the instance `giterated.dev`, the following [`Instance`] initialization | |
26 | /// would be valid: | |
27 | /// | |
28 | /// ``` | |
29 | /// let instance = Instance { | |
30 | /// url: String::from("giterated.dev") | |
31 | /// }; | |
32 | /// | |
33 | /// // This is correct | |
34 | /// assert_eq!(Instance::from_str("giterated.dev").unwrap(), instance); | |
35 | /// ``` | |
36 | #[derive(Clone, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)] | |
37 | pub struct Instance(pub String); | |
38 | ||
39 | impl GiteratedObject for Instance { | |
40 | fn object_name() -> &'static str { | |
41 | "instance" | |
42 | } | |
43 | ||
44 | fn from_object_str(object_str: &str) -> Result<Self, anyhow::Error> { | |
45 | Ok(Instance::from_str(object_str)?) | |
46 | } | |
47 | ||
48 | fn home_uri(&self) -> String { | |
49 | self.0.clone() | |
50 | } | |
51 | } | |
52 | ||
53 | impl Display for Instance { | |
54 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | |
55 | f.write_str(&self.0.to_string()) | |
56 | } | |
57 | } | |
58 | ||
59 | impl FromStr for Instance { | |
60 | type Err = InstanceParseError; | |
61 | ||
62 | fn from_str(s: &str) -> Result<Self, Self::Err> { | |
63 | let with_protocol = format!("wss://{}", s); | |
64 | ||
65 | if Url::parse(&with_protocol).is_ok() { | |
66 | Ok(Self(s.to_string())) | |
67 | } else { | |
68 | Err(InstanceParseError::InvalidFormat) | |
69 | } | |
70 | } | |
71 | } | |
72 | ||
73 | #[derive(Debug, Error)] | |
74 | pub enum InstanceParseError { | |
75 | #[error("invalid format")] | |
76 | InvalidFormat, | |
77 | } |
giterated-core/giterated-models/src/instance/operations.rs
@@ -0,0 +1,197 @@ | ||
1 | use secrecy::Secret; | |
2 | use serde::{Deserialize, Serialize}; | |
3 | ||
4 | use crate::{ | |
5 | authenticated::UserAuthenticationToken, | |
6 | error::{InstanceError, OperationError}, | |
7 | object::Object, | |
8 | object_backend::ObjectBackend, | |
9 | operation::{GiteratedOperation, OperationState}, | |
10 | repository::{Repository, RepositoryVisibility}, | |
11 | user::{Password, User}, | |
12 | }; | |
13 | ||
14 | use super::Instance; | |
15 | ||
16 | /// An account registration request. | |
17 | /// | |
18 | /// # Authentication | |
19 | /// - Instance Authentication | |
20 | /// - **ONLY ACCEPTED WHEN SAME-INSTANCE** | |
21 | #[derive(Clone, Debug, Serialize, Deserialize)] | |
22 | pub struct RegisterAccountRequest { | |
23 | pub username: String, | |
24 | pub email: Option<String>, | |
25 | pub password: Secret<Password>, | |
26 | } | |
27 | ||
28 | impl GiteratedOperation<Instance> for RegisterAccountRequest { | |
29 | type Success = UserAuthenticationToken; | |
30 | type Failure = InstanceError; | |
31 | } | |
32 | ||
33 | #[derive(Clone, Debug, Serialize, Deserialize)] | |
34 | pub struct RegisterAccountResponse(pub UserAuthenticationToken); | |
35 | ||
36 | /// An authentication token request. | |
37 | /// | |
38 | /// AKA Login Request | |
39 | /// | |
40 | /// # Authentication | |
41 | /// - Instance Authentication | |
42 | /// - Identifies the Instance to issue the token for | |
43 | /// # Authorization | |
44 | /// - Credentials ([`crate::backend::AuthBackend`]-based) | |
45 | /// - Identifies the User account to issue a token for | |
46 | /// - Decrypts user private key to issue to | |
47 | #[derive(Clone, Debug, Serialize, Deserialize)] | |
48 | pub struct AuthenticationTokenRequest { | |
49 | pub instance: Instance, | |
50 | pub username: String, | |
51 | pub password: Secret<Password>, | |
52 | } | |
53 | ||
54 | impl GiteratedOperation<Instance> for AuthenticationTokenRequest { | |
55 | type Success = UserAuthenticationToken; | |
56 | type Failure = InstanceError; | |
57 | } | |
58 | ||
59 | /// An authentication token extension request. | |
60 | /// | |
61 | /// # Authentication | |
62 | /// - Instance Authentication | |
63 | /// - Identifies the Instance to issue the token for | |
64 | /// - User Authentication | |
65 | /// - Authenticates the validity of the token | |
66 | /// # Authorization | |
67 | /// - Token-based | |
68 | /// - Validates authorization using token's authenticity | |
69 | #[derive(Clone, Debug, Serialize, Deserialize)] | |
70 | pub struct TokenExtensionRequest { | |
71 | pub token: UserAuthenticationToken, | |
72 | } | |
73 | ||
74 | impl GiteratedOperation<Instance> for TokenExtensionRequest { | |
75 | type Success = Option<UserAuthenticationToken>; | |
76 | type Failure = InstanceError; | |
77 | } | |
78 | ||
79 | /// A request to create a repository. | |
80 | /// | |
81 | /// # Authentication | |
82 | /// - Instance Authentication | |
83 | /// - Used to validate User token `issued_for` | |
84 | /// - User Authentication | |
85 | /// - Used to source owning user | |
86 | /// - Used to authorize user token against user's instance | |
87 | /// # Authorization | |
88 | /// - Instance Authorization | |
89 | /// - Used to authorize action using User token requiring a correct `issued_for` and valid issuance from user's instance | |
90 | /// - User Authorization | |
91 | /// - Potential User permissions checks | |
92 | #[derive(Clone, Debug, Serialize, Deserialize)] | |
93 | pub struct RepositoryCreateRequest { | |
94 | pub instance: Option<Instance>, | |
95 | pub name: String, | |
96 | pub description: Option<String>, | |
97 | pub visibility: RepositoryVisibility, | |
98 | pub default_branch: String, | |
99 | pub owner: User, | |
100 | } | |
101 | ||
102 | impl GiteratedOperation<Instance> for RepositoryCreateRequest { | |
103 | type Success = Repository; | |
104 | type Failure = InstanceError; | |
105 | } | |
106 | ||
107 | impl<B: ObjectBackend + std::fmt::Debug> Object<Instance, B> { | |
108 | pub async fn register_account( | |
109 | &mut self, | |
110 | email: Option<&str>, | |
111 | username: &str, | |
112 | password: &Secret<Password>, | |
113 | operation_state: &OperationState, | |
114 | ) -> Result<UserAuthenticationToken, OperationError<InstanceError>> { | |
115 | self.request::<RegisterAccountRequest>( | |
116 | RegisterAccountRequest { | |
117 | username: username.to_string(), | |
118 | email: email.map(|s| s.to_string()), | |
119 | password: password.clone(), | |
120 | }, | |
121 | operation_state, | |
122 | ) | |
123 | .await | |
124 | } | |
125 | ||
126 | pub async fn authentication_token( | |
127 | &mut self, | |
128 | username: &str, | |
129 | password: &Secret<Password>, | |
130 | operation_state: &OperationState, | |
131 | ) -> Result<UserAuthenticationToken, OperationError<InstanceError>> { | |
132 | self.request::<AuthenticationTokenRequest>( | |
133 | AuthenticationTokenRequest { | |
134 | instance: self.inner.clone(), | |
135 | username: username.to_string(), | |
136 | password: password.clone(), | |
137 | }, | |
138 | operation_state, | |
139 | ) | |
140 | .await | |
141 | } | |
142 | ||
143 | pub async fn authentication_token_for( | |
144 | &mut self, | |
145 | instance: &Instance, | |
146 | username: &str, | |
147 | password: &Secret<Password>, | |
148 | operation_state: &OperationState, | |
149 | ) -> Result<UserAuthenticationToken, OperationError<InstanceError>> { | |
150 | self.request::<AuthenticationTokenRequest>( | |
151 | AuthenticationTokenRequest { | |
152 | instance: instance.clone(), | |
153 | username: username.to_string(), | |
154 | password: password.clone(), | |
155 | }, | |
156 | operation_state, | |
157 | ) | |
158 | .await | |
159 | } | |
160 | ||
161 | pub async fn token_extension( | |
162 | &mut self, | |
163 | token: &UserAuthenticationToken, | |
164 | operation_state: &OperationState, | |
165 | ) -> Result<Option<UserAuthenticationToken>, OperationError<InstanceError>> { | |
166 | self.request::<TokenExtensionRequest>( | |
167 | TokenExtensionRequest { | |
168 | token: token.clone(), | |
169 | }, | |
170 | operation_state, | |
171 | ) | |
172 | .await | |
173 | } | |
174 | ||
175 | pub async fn create_repository( | |
176 | &mut self, | |
177 | instance: &Instance, | |
178 | name: &str, | |
179 | visibility: &RepositoryVisibility, | |
180 | default_branch: &str, | |
181 | owner: &User, | |
182 | operation_state: &OperationState, | |
183 | ) -> Result<Repository, OperationError<InstanceError>> { | |
184 | self.request::<RepositoryCreateRequest>( | |
185 | RepositoryCreateRequest { | |
186 | instance: Some(instance.clone()), | |
187 | name: name.to_string(), | |
188 | description: None, | |
189 | visibility: visibility.clone(), | |
190 | default_branch: default_branch.to_string(), | |
191 | owner: owner.clone(), | |
192 | }, | |
193 | operation_state, | |
194 | ) | |
195 | .await | |
196 | } | |
197 | } |
giterated-core/giterated-models/src/instance/values.rs
@@ -0,0 +1 @@ | ||
1 |
giterated-core/giterated-models/src/lib.rs
@@ -0,0 +1,15 @@ | ||
1 | pub mod authenticated; | |
2 | pub mod discovery; | |
3 | pub mod error; | |
4 | pub mod ffi; | |
5 | pub mod handshake; | |
6 | pub mod instance; | |
7 | pub mod message; | |
8 | pub mod object; | |
9 | pub mod object_backend; | |
10 | pub mod operation; | |
11 | pub mod repository; | |
12 | pub mod settings; | |
13 | pub mod update; | |
14 | pub mod user; | |
15 | pub mod value; |
giterated-core/giterated-models/src/message.rs
@@ -0,0 +1,70 @@ | ||
1 | use serde::Serialize; | |
2 | ||
3 | use crate::{ | |
4 | object::{GiteratedObject, NetworkAnyObject}, | |
5 | operation::{GiteratedOperation, NetworkAnyOperation}, | |
6 | }; | |
7 | use std::fmt::Debug; | |
8 | ||
9 | #[derive(Serialize)] | |
10 | #[serde(bound(deserialize = "O: GiteratedObject, V: GiteratedOperation<O>"))] | |
11 | pub struct GiteratedMessage<O: GiteratedObject, V: GiteratedOperation<O>> { | |
12 | #[serde(with = "string")] | |
13 | pub object: O, | |
14 | pub operation: String, | |
15 | pub payload: V, | |
16 | } | |
17 | ||
18 | #[allow(unused)] | |
19 | mod string { | |
20 | use std::fmt::Display; | |
21 | use std::str::FromStr; | |
22 | ||
23 | use serde::{de, Deserialize, Deserializer, Serializer}; | |
24 | ||
25 | pub fn serialize<T, S>(value: &T, serializer: S) -> Result<S::Ok, S::Error> | |
26 | where | |
27 | T: Display, | |
28 | S: Serializer, | |
29 | { | |
30 | serializer.collect_str(value) | |
31 | } | |
32 | ||
33 | pub fn deserialize<'de, T, D>(deserializer: D) -> Result<T, D::Error> | |
34 | where | |
35 | T: FromStr, | |
36 | T::Err: Display, | |
37 | D: Deserializer<'de>, | |
38 | { | |
39 | String::deserialize(deserializer)? | |
40 | .parse() | |
41 | .map_err(de::Error::custom) | |
42 | } | |
43 | } | |
44 | ||
45 | impl GiteratedMessage<NetworkAnyObject, NetworkAnyOperation> { | |
46 | pub fn try_into<O: GiteratedObject, V: GiteratedOperation<O>>( | |
47 | &self, | |
48 | ) -> Result<GiteratedMessage<O, V>, ()> { | |
49 | let object = O::from_object_str(&self.object.0).map_err(|_| ())?; | |
50 | let payload = serde_json::from_slice::<V>(&self.payload.0).map_err(|_| ())?; | |
51 | ||
52 | Ok(GiteratedMessage { | |
53 | object, | |
54 | operation: self.operation.clone(), | |
55 | payload, | |
56 | }) | |
57 | } | |
58 | } | |
59 | ||
60 | impl<V: GiteratedOperation<O> + Debug, O: GiteratedObject + Debug> Debug | |
61 | for GiteratedMessage<O, V> | |
62 | { | |
63 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | |
64 | f.debug_struct("GiteratedMessage") | |
65 | .field("object", &self.object) | |
66 | .field("operation", &self.operation) | |
67 | .field("payload", &self.payload) | |
68 | .finish() | |
69 | } | |
70 | } |
giterated-core/giterated-models/src/object.rs
@@ -0,0 +1,132 @@ | ||
1 | use std::{ | |
2 | fmt::{Debug, Display}, | |
3 | str::FromStr, | |
4 | }; | |
5 | ||
6 | use anyhow::Error; | |
7 | ||
8 | use crate::{ | |
9 | error::{GetValueError, OperationError}, | |
10 | object_backend::ObjectBackend, | |
11 | operation::{GiteratedOperation, OperationState}, | |
12 | settings::{GetSetting, GetSettingError, SetSetting, SetSettingError, Setting}, | |
13 | value::{GetValue, GiteratedObjectValue}, | |
14 | }; | |
15 | ||
16 | mod operations; | |
17 | pub use operations::*; | |
18 | ||
19 | #[derive(Debug, Clone)] | |
20 | pub struct Object<O: GiteratedObject, B: ObjectBackend + Send + Clone> { | |
21 | pub(crate) inner: O, | |
22 | pub(crate) backend: B, | |
23 | } | |
24 | ||
25 | impl<B: ObjectBackend + Send + Sync + Clone, O: GiteratedObject> Object<O, B> { | |
26 | pub fn object(&self) -> &O { | |
27 | &self.inner | |
28 | } | |
29 | ||
30 | pub unsafe fn new_unchecked(object: O, backend: B) -> Object<O, B> { | |
31 | Object { | |
32 | inner: object, | |
33 | backend, | |
34 | } | |
35 | } | |
36 | } | |
37 | ||
38 | impl<O: GiteratedObject + Display, B: ObjectBackend + Send + Sync + Clone> Display | |
39 | for Object<O, B> | |
40 | { | |
41 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | |
42 | self.inner.fmt(f) | |
43 | } | |
44 | } | |
45 | ||
46 | pub trait GiteratedObject: Send + Display + FromStr + Sync + Clone { | |
47 | fn object_name() -> &'static str; | |
48 | fn home_uri(&self) -> String; | |
49 | ||
50 | fn from_object_str(object_str: &str) -> Result<Self, Error>; | |
51 | } | |
52 | ||
53 | impl<O: GiteratedObject + Clone + Debug + 'static, B: ObjectBackend> Object<O, B> { | |
54 | pub async fn get<V: GiteratedObjectValue<Object = O> + Send + Debug + 'static>( | |
55 | &mut self, | |
56 | operation_state: &OperationState, | |
57 | ) -> Result<V, OperationError<GetValueError>> { | |
58 | let result = self | |
59 | .request( | |
60 | GetValue { | |
61 | value_name: V::value_name().to_string(), | |
62 | }, | |
63 | operation_state, | |
64 | ) | |
65 | .await?; | |
66 | ||
67 | Ok(serde_json::from_slice(&result).unwrap()) | |
68 | } | |
69 | ||
70 | pub async fn get_setting<S: Setting + Send + Clone + Debug>( | |
71 | &mut self, | |
72 | operation_state: &OperationState, | |
73 | ) -> Result<S, OperationError<GetSettingError>> { | |
74 | self.request( | |
75 | GetSetting { | |
76 | setting_name: S::name().to_string(), | |
77 | }, | |
78 | operation_state, | |
79 | ) | |
80 | .await | |
81 | .map(|success| serde_json::from_value(success).unwrap()) | |
82 | } | |
83 | ||
84 | pub async fn set_setting<S: Setting + Send + Clone + Debug>( | |
85 | &mut self, | |
86 | setting: S, | |
87 | operation_state: &OperationState, | |
88 | ) -> Result<(), OperationError<SetSettingError>> { | |
89 | self.request( | |
90 | SetSetting { | |
91 | setting_name: S::name().to_string(), | |
92 | value: serde_json::to_value(setting).unwrap(), | |
93 | }, | |
94 | operation_state, | |
95 | ) | |
96 | .await | |
97 | } | |
98 | ||
99 | pub async fn request<R: GiteratedOperation<O> + Debug + 'static>( | |
100 | &mut self, | |
101 | request: R, | |
102 | operation_state: &OperationState, | |
103 | ) -> Result<R::Success, OperationError<R::Failure>> | |
104 | where | |
105 | R::Success: Clone, | |
106 | R::Failure: Clone, | |
107 | { | |
108 | self.backend | |
109 | .object_operation( | |
110 | self.inner.clone(), | |
111 | R::operation_name(), | |
112 | request, | |
113 | operation_state, | |
114 | ) | |
115 | .await | |
116 | } | |
117 | } | |
118 | ||
119 | #[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)] | |
120 | pub struct ObjectOperationPair<'s> { | |
121 | pub object_kind: &'s str, | |
122 | pub operation_name: &'s str, | |
123 | } | |
124 | ||
125 | impl<'s> ObjectOperationPair<'s> { | |
126 | pub fn new(object_kind: &'s str, operation_name: &'s str) -> Self { | |
127 | Self { | |
128 | object_kind, | |
129 | operation_name, | |
130 | } | |
131 | } | |
132 | } |
giterated-core/giterated-models/src/object/operations.rs
@@ -0,0 +1,64 @@ | ||
1 | use std::{convert::Infallible, fmt::Display, str::FromStr}; | |
2 | ||
3 | use serde::{Deserialize, Serialize}; | |
4 | ||
5 | use crate::{instance::Instance, operation::GiteratedOperation}; | |
6 | ||
7 | use super::GiteratedObject; | |
8 | ||
9 | #[derive(Debug, Serialize, Deserialize, Clone)] | |
10 | pub struct ObjectRequest(pub String); | |
11 | ||
12 | #[derive(Serialize, Deserialize, Clone, Debug)] | |
13 | pub struct ObjectResponse(pub String); | |
14 | ||
15 | impl GiteratedOperation<Instance> for ObjectRequest { | |
16 | type Success = ObjectResponse; | |
17 | ||
18 | type Failure = ObjectRequestError; | |
19 | ||
20 | fn operation_name() -> &'static str { | |
21 | "object_request" | |
22 | } | |
23 | } | |
24 | ||
25 | #[derive(Debug, Clone, thiserror::Error, Serialize, Deserialize)] | |
26 | pub enum ObjectRequestError { | |
27 | #[error("error decoding the object")] | |
28 | Deserialization(String), | |
29 | #[error("invalid object type")] | |
30 | Invalid, | |
31 | } | |
32 | ||
33 | #[derive(Clone, Debug, Serialize, Deserialize)] | |
34 | #[serde(transparent)] | |
35 | #[repr(transparent)] | |
36 | pub struct NetworkAnyObject(pub String); | |
37 | ||
38 | impl GiteratedObject for NetworkAnyObject { | |
39 | fn object_name() -> &'static str { | |
40 | "any" | |
41 | } | |
42 | ||
43 | fn from_object_str(object_str: &str) -> Result<Self, anyhow::Error> { | |
44 | Ok(Self(object_str.to_string())) | |
45 | } | |
46 | ||
47 | fn home_uri(&self) -> String { | |
48 | todo!() | |
49 | } | |
50 | } | |
51 | ||
52 | impl Display for NetworkAnyObject { | |
53 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | |
54 | f.write_str(&self.0) | |
55 | } | |
56 | } | |
57 | ||
58 | impl FromStr for NetworkAnyObject { | |
59 | type Err = Infallible; | |
60 | ||
61 | fn from_str(s: &str) -> Result<Self, Self::Err> { | |
62 | Ok(Self(s.to_owned())) | |
63 | } | |
64 | } |
giterated-core/giterated-models/src/object_backend.rs
@@ -0,0 +1,29 @@ | ||
1 | use crate::{ | |
2 | error::OperationError, | |
3 | object::{GiteratedObject, Object, ObjectRequestError}, | |
4 | operation::{GiteratedOperation, OperationState}, | |
5 | }; | |
6 | ||
7 | use std::fmt::Debug; | |
8 | ||
9 | #[async_trait::async_trait(?Send)] | |
10 | pub trait ObjectBackend: Sized + Clone + Send { | |
11 | async fn object_operation<O, D>( | |
12 | &self, | |
13 | object: O, | |
14 | operation: &str, | |
15 | payload: D, | |
16 | operation_state: &OperationState, | |
17 | ) -> Result<D::Success, OperationError<D::Failure>> | |
18 | where | |
19 | O: GiteratedObject + Debug + 'static, | |
20 | D: GiteratedOperation<O> + Debug + 'static, | |
21 | D::Success: Clone, | |
22 | D::Failure: Clone; | |
23 | ||
24 | async fn get_object<O: GiteratedObject + Debug + 'static>( | |
25 | &self, | |
26 | object_str: &str, | |
27 | operation_state: &OperationState, | |
28 | ) -> Result<Object<O, Self>, OperationError<ObjectRequestError>>; | |
29 | } |
giterated-core/giterated-models/src/operation.rs
@@ -0,0 +1,42 @@ | ||
1 | use std::{any::type_name, collections::HashMap, fmt::Debug}; | |
2 | ||
3 | use serde::{de::DeserializeOwned, Deserialize, Serialize}; | |
4 | ||
5 | use crate::{ffi::FfiLabel, object::GiteratedObject}; | |
6 | ||
7 | pub trait GiteratedOperation<O: GiteratedObject>: | |
8 | Send + Sync + Serialize + DeserializeOwned | |
9 | { | |
10 | type Success: Serialize + DeserializeOwned + Send; | |
11 | type Failure: Serialize + DeserializeOwned + Send; | |
12 | ||
13 | fn operation_name() -> &'static str { | |
14 | type_name::<Self>() | |
15 | } | |
16 | } | |
17 | ||
18 | #[derive(Clone, Debug, Serialize, Deserialize)] | |
19 | #[serde(transparent)] | |
20 | #[repr(transparent)] | |
21 | pub struct NetworkAnyOperation(pub Vec<u8>); | |
22 | ||
23 | impl<O: GiteratedObject> GiteratedOperation<O> for NetworkAnyOperation { | |
24 | type Success = Vec<u8>; | |
25 | ||
26 | type Failure = Vec<u8>; | |
27 | } | |
28 | ||
29 | #[derive(Clone, Debug, Default)] | |
30 | pub struct OperationState { | |
31 | states: HashMap<String, Vec<u8>>, | |
32 | } | |
33 | ||
34 | impl FfiLabel for OperationState { | |
35 | fn prefix(&self) -> &'static str { | |
36 | "dev.giterated" | |
37 | } | |
38 | ||
39 | fn type_label(&self) -> &'static str { | |
40 | "operation_state" | |
41 | } | |
42 | } |
giterated-core/giterated-models/src/repository/mod.rs
@@ -0,0 +1,475 @@ | ||
1 | use std::fmt::{Display, Formatter}; | |
2 | use std::str::FromStr; | |
3 | ||
4 | use serde::{Deserialize, Serialize}; | |
5 | ||
6 | use crate::object::GiteratedObject; | |
7 | ||
8 | use super::{instance::Instance, user::User}; | |
9 | ||
10 | mod operations; | |
11 | mod settings; | |
12 | mod values; | |
13 | ||
14 | pub use operations::*; | |
15 | pub use settings::*; | |
16 | pub use values::*; | |
17 | ||
18 | /// A repository, defined by the instance it exists on along with | |
19 | /// its owner and name. | |
20 | /// | |
21 | /// # Textual Format | |
22 | /// A repository's textual reference is defined as: | |
23 | /// | |
24 | /// `{owner: User}/{name: String}@{instance: Instance}` | |
25 | /// | |
26 | /// # Examples | |
27 | /// For the repository named `foo` owned by `barson:giterated.dev` on the instance | |
28 | /// `giterated.dev`, the following [`Repository`] initialization would | |
29 | /// be valid: | |
30 | /// | |
31 | /// ``` | |
32 | //# use giterated_models::model::repository::Repository; | |
33 | //# use giterated_models::model::instance::Instance; | |
34 | //# use giterated_models::model::user::User; | |
35 | /// let repository = Repository { | |
36 | /// owner: User::from_str("barson:giterated.dev").unwrap(), | |
37 | /// name: String::from("foo"), | |
38 | /// instance: Instance::from_str("giterated.dev").unwrap() | |
39 | /// }; | |
40 | /// | |
41 | /// // This is correct | |
42 | /// assert_eq!(Repository::from_str("barson:giterated.dev/[email protected]").unwrap(), repository); | |
43 | /// ``` | |
44 | #[derive(Hash, Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] | |
45 | pub struct Repository { | |
46 | pub owner: User, | |
47 | pub name: String, | |
48 | /// Instance the repository is on | |
49 | pub instance: Instance, | |
50 | } | |
51 | ||
52 | impl Repository { | |
53 | pub fn new(instance: Instance, owner: User, name: String) -> Self { | |
54 | Self { | |
55 | owner, | |
56 | name, | |
57 | instance, | |
58 | } | |
59 | } | |
60 | } | |
61 | ||
62 | impl Display for Repository { | |
63 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { | |
64 | f.write_str(&format!("{}/{}@{}", self.owner, self.name, self.instance)) | |
65 | } | |
66 | } | |
67 | ||
68 | impl GiteratedObject for Repository { | |
69 | fn object_name() -> &'static str { | |
70 | "repository" | |
71 | } | |
72 | ||
73 | fn from_object_str(object_str: &str) -> Result<Self, anyhow::Error> { | |
74 | Ok(Repository::from_str(object_str)?) | |
75 | } | |
76 | ||
77 | fn home_uri(&self) -> String { | |
78 | self.instance.home_uri() | |
79 | } | |
80 | } | |
81 | ||
82 | // impl TryFrom<String> for Repository { | |
83 | // type Error = RepositoryParseError; | |
84 | ||
85 | // fn try_from(value: String) -> Result<Self, Self::Error> { | |
86 | // Self::from_str(&value) | |
87 | // } | |
88 | // } | |
89 | ||
90 | impl From<String> for Repository { | |
91 | fn from(value: String) -> Self { | |
92 | Repository::from_str(&value).unwrap() | |
93 | } | |
94 | } | |
95 | ||
96 | impl FromStr for Repository { | |
97 | type Err = RepositoryParseError; | |
98 | ||
99 | fn from_str(s: &str) -> Result<Self, Self::Err> { | |
100 | let mut by_ampersand = s.split('@'); | |
101 | let mut path_split = by_ampersand.next().ok_or(RepositoryParseError)?.split('/'); | |
102 | ||
103 | let instance = Instance::from_str(by_ampersand.next().ok_or(RepositoryParseError)?) | |
104 | .map_err(|_| RepositoryParseError)?; | |
105 | let owner = User::from_str(path_split.next().ok_or(RepositoryParseError)?) | |
106 | .map_err(|_| RepositoryParseError)?; | |
107 | let name = path_split.next().ok_or(RepositoryParseError)?.to_string(); | |
108 | ||
109 | Ok(Self { | |
110 | instance, | |
111 | owner, | |
112 | name, | |
113 | }) | |
114 | } | |
115 | } | |
116 | ||
117 | #[derive(Debug, thiserror::Error)] | |
118 | #[error("no parse!")] | |
119 | pub struct RepositoryParseError; | |
120 | ||
121 | /// Visibility of the repository to the general eye | |
122 | #[derive(PartialEq, Eq, Debug, Hash, Serialize, Deserialize, Clone, sqlx::Type)] | |
123 | #[sqlx(type_name = "visibility", rename_all = "lowercase")] | |
124 | pub enum RepositoryVisibility { | |
125 | Public, | |
126 | Unlisted, | |
127 | Private, | |
128 | } | |
129 | ||
130 | /// Implements [`Display`] for [`RepositoryVisiblity`] using [`Debug`] | |
131 | impl Display for RepositoryVisibility { | |
132 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { | |
133 | write!(f, "{:?}", self) | |
134 | } | |
135 | } | |
136 | ||
137 | #[derive(Clone, Debug, Serialize, Deserialize)] | |
138 | pub struct RepositoryView { | |
139 | /// Name of the repository | |
140 | /// | |
141 | /// This is different than the [`Repository`] name, | |
142 | /// which may be a path. | |
143 | pub name: String, | |
144 | /// Owner of the Repository | |
145 | pub owner: User, | |
146 | /// Repository description | |
147 | pub description: Option<Description>, | |
148 | /// Repository visibility | |
149 | pub visibility: Visibility, | |
150 | /// Default branch of the repository | |
151 | pub default_branch: DefaultBranch, | |
152 | /// Last commit made to the repository | |
153 | pub latest_commit: Option<LatestCommit>, | |
154 | /// Repository statistics | |
155 | pub stats: RepositoryStatistics, | |
156 | /// Revision of the displayed tree | |
157 | pub tree_rev: Option<String>, | |
158 | /// Repository tree | |
159 | pub tree: Vec<RepositoryTreeEntry>, | |
160 | } | |
161 | ||
162 | /// Generic repository statistics | |
163 | #[derive(Clone, Debug, Serialize, Deserialize)] | |
164 | pub struct RepositoryStatistics { | |
165 | /// Amount of commits made to this branch in the repository | |
166 | pub commits: usize, | |
167 | /// Amount of branches the repository has | |
168 | pub branches: usize, | |
169 | /// Amount of tags the repository has | |
170 | pub tags: usize, | |
171 | } | |
172 | ||
173 | /// Repository branch | |
174 | #[derive(Clone, Debug, Serialize, Deserialize)] | |
175 | pub struct RepositoryBranch { | |
176 | /// Full reference name | |
177 | pub name: String, | |
178 | /// Whether the repository is stale or not | |
179 | pub stale: bool, | |
180 | /// The last commit made to the branch | |
181 | pub last_commit: Option<Commit>, | |
182 | } | |
183 | ||
184 | #[derive(Clone, Debug, Serialize, Deserialize)] | |
185 | pub struct RepositoryFile { | |
186 | /// ID of the file | |
187 | pub id: String, | |
188 | /// Content of the file | |
189 | pub content: Vec<u8>, | |
190 | /// If the file is binary or not | |
191 | pub binary: bool, | |
192 | /// File size in bytes | |
193 | pub size: usize, | |
194 | } | |
195 | ||
196 | #[derive(Clone, Debug, Serialize, Deserialize)] | |
197 | pub struct RepositoryDiff { | |
198 | /// "to" side of the diff commit | |
199 | pub new_commit: Commit, | |
200 | /// Total number of files changed | |
201 | pub files_changed: usize, | |
202 | /// Total number of insertions | |
203 | pub insertions: usize, | |
204 | /// Total number of deletions | |
205 | pub deletions: usize, | |
206 | /// List of changed files | |
207 | pub files: Vec<RepositoryDiffFile>, | |
208 | } | |
209 | ||
210 | /// Represents the type of change made to a [`RepositoryDiffFile`] | |
211 | #[derive(Clone, Debug, Serialize, Deserialize)] | |
212 | pub enum RepositoryDiffFileStatus { | |
213 | /// No changes | |
214 | Unmodified, | |
215 | Added, | |
216 | Deleted, | |
217 | /// Content changed between old and new | |
218 | Modified, | |
219 | /// Renamed between old and new | |
220 | Renamed, | |
221 | /// Copied from another old entry | |
222 | Copied, | |
223 | /// Ignored item in workdir | |
224 | Ignored, | |
225 | /// Untracked item in workdir | |
226 | Untracked, | |
227 | /// Type of file changed between old and new | |
228 | Typechange, | |
229 | /// File is unreadable | |
230 | Unreadable, | |
231 | /// File in the index is conflicted | |
232 | Conflicted, | |
233 | } | |
234 | ||
235 | impl From<git2::Delta> for RepositoryDiffFileStatus { | |
236 | fn from(status: git2::Delta) -> Self { | |
237 | match status { | |
238 | git2::Delta::Unmodified => Self::Unmodified, | |
239 | git2::Delta::Added => Self::Added, | |
240 | git2::Delta::Deleted => Self::Deleted, | |
241 | git2::Delta::Modified => Self::Modified, | |
242 | git2::Delta::Renamed => Self::Renamed, | |
243 | git2::Delta::Copied => Self::Copied, | |
244 | git2::Delta::Ignored => Self::Ignored, | |
245 | git2::Delta::Untracked => Self::Untracked, | |
246 | git2::Delta::Typechange => Self::Typechange, | |
247 | git2::Delta::Unreadable => Self::Unreadable, | |
248 | git2::Delta::Conflicted => Self::Conflicted, | |
249 | } | |
250 | } | |
251 | } | |
252 | ||
253 | /// Represents a single file of a diff | |
254 | #[derive(Clone, Debug, Serialize, Deserialize)] | |
255 | pub struct RepositoryDiffFile { | |
256 | /// The type of change made to this file | |
257 | pub status: RepositoryDiffFileStatus, | |
258 | /// "From" side of the diff, can be nonexistent if file for example got added for the first time | |
259 | pub old_file_info: Option<RepositoryDiffFileInfo>, | |
260 | /// "To" side of the diff, can be nonexistent if file got removed | |
261 | pub new_file_info: Option<RepositoryDiffFileInfo>, | |
262 | /// Individual chunks of changes in this file | |
263 | pub chunks: Vec<RepositoryDiffFileChunk>, | |
264 | } | |
265 | ||
266 | /// Represents one side of a file diff [`RepositoryDiffFile`] | |
267 | #[derive(Clone, Debug, Serialize, Deserialize)] | |
268 | pub struct RepositoryDiffFileInfo { | |
269 | /// ID of the file | |
270 | pub id: String, | |
271 | /// Path of the entry relative to the working directory of the repository | |
272 | pub path: String, | |
273 | /// Size in bytes | |
274 | pub size: u64, | |
275 | /// If the file is binary or not | |
276 | pub binary: bool, | |
277 | } | |
278 | ||
279 | /// Represents a single chunk of a file diff [`RepositoryDiffFile`] | |
280 | #[derive(Clone, Debug, Serialize, Deserialize)] | |
281 | pub struct RepositoryDiffFileChunk { | |
282 | /// Header of the chunk | |
283 | pub header: Option<String>, | |
284 | /// Starting line number of the old file | |
285 | pub old_start: u32, | |
286 | /// Number of lines in "from" side of this chunk | |
287 | pub old_lines: u32, | |
288 | /// Starting line number of the new file | |
289 | pub new_start: u32, | |
290 | /// Number of lines in "to" side of this chunk | |
291 | pub new_lines: u32, | |
292 | /// Lines of the chunk | |
293 | pub lines: Vec<RepositoryChunkLine>, | |
294 | } | |
295 | ||
296 | /// Represents the change type of the [`RepositoryChunkLine`], incomplete of what git actually provides. | |
297 | #[derive(Clone, Debug, Serialize, Deserialize)] | |
298 | pub enum RepositoryChunkLineType { | |
299 | Context, | |
300 | Addition, | |
301 | Deletion, | |
302 | } | |
303 | ||
304 | impl From<git2::DiffLineType> for RepositoryChunkLineType { | |
305 | fn from(line_type: git2::DiffLineType) -> Self { | |
306 | match line_type { | |
307 | git2::DiffLineType::Context => Self::Context, | |
308 | git2::DiffLineType::Addition => Self::Addition, | |
309 | git2::DiffLineType::Deletion => Self::Deletion, | |
310 | _ => Self::Context, | |
311 | } | |
312 | } | |
313 | } | |
314 | ||
315 | /// Represents a single line of a [`RepositoryDiffFileChunk`] | |
316 | #[derive(Clone, Debug, Serialize, Deserialize)] | |
317 | pub struct RepositoryChunkLine { | |
318 | /// Type of change the line is | |
319 | pub change_type: RepositoryChunkLineType, | |
320 | /// Content of the line | |
321 | pub content: String, | |
322 | /// Line number in old file | |
323 | pub old_line_num: Option<u32>, | |
324 | /// Line number in new file | |
325 | pub new_line_num: Option<u32>, | |
326 | } | |
327 | ||
328 | #[derive(Debug, Clone, Serialize, Deserialize)] | |
329 | pub enum RepositoryObjectType { | |
330 | Tree, | |
331 | Blob, | |
332 | } | |
333 | ||
334 | /// Stored info for our tree entries | |
335 | #[derive(Debug, Clone, Serialize, Deserialize)] | |
336 | pub struct RepositoryTreeEntry { | |
337 | /// ID of the tree/blob | |
338 | pub id: String, | |
339 | /// Name of the tree/blob | |
340 | pub name: String, | |
341 | /// Type of the tree entry | |
342 | pub object_type: RepositoryObjectType, | |
343 | /// Git supplies us with the mode at all times, and people like it displayed. | |
344 | pub mode: i32, | |
345 | /// File size | |
346 | pub size: Option<usize>, | |
347 | /// Last commit made to the tree/blob | |
348 | pub last_commit: Option<Commit>, | |
349 | } | |
350 | ||
351 | impl RepositoryTreeEntry { | |
352 | // I love you Emilia <3 | |
353 | pub fn new(id: &str, name: &str, object_type: RepositoryObjectType, mode: i32) -> Self { | |
354 | Self { | |
355 | id: id.to_string(), | |
356 | name: name.to_string(), | |
357 | object_type, | |
358 | mode, | |
359 | size: None, | |
360 | last_commit: None, | |
361 | } | |
362 | } | |
363 | } | |
364 | ||
365 | #[derive(Debug, Clone, Serialize, Deserialize)] | |
366 | pub struct RepositoryTreeEntryWithCommit { | |
367 | pub tree_entry: RepositoryTreeEntry, | |
368 | pub commit: Commit, | |
369 | } | |
370 | ||
371 | /// Info about a git commit | |
372 | #[derive(PartialEq, Hash, Eq, Debug, Clone, Serialize, Deserialize)] | |
373 | pub struct Commit { | |
374 | /// Unique commit ID | |
375 | pub oid: String, | |
376 | /// Shortened abbreviated OID | |
377 | /// This starts at the git config's "core.abbrev" length (default 7 characters) and | |
378 | /// iteratively extends to a longer string if that length is ambiguous. The | |
379 | /// result will be unambiguous (at least until new objects are added to the repository). | |
380 | pub short_oid: String, | |
381 | /// First paragraph of the full message | |
382 | pub summary: Option<String>, | |
383 | /// Everything in the full message apart from the first paragraph | |
384 | pub body: Option<String>, | |
385 | /// All commit id's of the parents of this commit | |
386 | pub parents: Vec<String>, | |
387 | /// Who created the commit | |
388 | pub author: CommitSignature, | |
389 | /// Who committed the commit | |
390 | pub committer: CommitSignature, | |
391 | /// Time when the commit happened | |
392 | pub time: chrono::NaiveDateTime, | |
393 | } | |
394 | ||
395 | /// Gets all info from [`git2::Commit`] for easy use | |
396 | impl From<git2::Commit<'_>> for Commit { | |
397 | fn from(commit: git2::Commit<'_>) -> Self { | |
398 | Self { | |
399 | oid: commit.id().to_string(), | |
400 | // This shouldn't ever fail, as we already know the object has an oid. | |
401 | short_oid: commit | |
402 | .as_object() | |
403 | .short_id() | |
404 | .unwrap() | |
405 | .as_str() | |
406 | .unwrap() | |
407 | .to_string(), | |
408 | summary: commit.summary().map(|summary| summary.to_string()), | |
409 | body: commit.body().map(|body| body.to_string()), | |
410 | parents: commit | |
411 | .parents() | |
412 | .map(|parent| parent.id().to_string()) | |
413 | .collect::<Vec<String>>(), | |
414 | author: commit.author().into(), | |
415 | committer: commit.committer().into(), | |
416 | time: chrono::NaiveDateTime::from_timestamp_opt(commit.time().seconds(), 0).unwrap(), | |
417 | } | |
418 | } | |
419 | } | |
420 | ||
421 | /// Git commit signature | |
422 | #[derive(PartialEq, Hash, Eq, Debug, Clone, Serialize, Deserialize)] | |
423 | pub struct CommitSignature { | |
424 | pub name: Option<String>, | |
425 | pub email: Option<String>, | |
426 | pub time: chrono::NaiveDateTime, | |
427 | } | |
428 | ||
429 | /// Converts the signature from git2 into something usable without explicit lifetimes. | |
430 | impl From<git2::Signature<'_>> for CommitSignature { | |
431 | fn from(signature: git2::Signature<'_>) -> Self { | |
432 | Self { | |
433 | name: signature.name().map(|name| name.to_string()), | |
434 | email: signature.email().map(|email| email.to_string()), | |
435 | time: chrono::NaiveDateTime::from_timestamp_opt(signature.when().seconds(), 0).unwrap(), | |
436 | } | |
437 | } | |
438 | } | |
439 | ||
440 | /// The document type of a [`Commit`]'s body | |
441 | #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] | |
442 | pub enum CommitBodyType { | |
443 | Plain, | |
444 | Markdown, | |
445 | } | |
446 | ||
447 | impl Default for CommitBodyType { | |
448 | fn default() -> Self { | |
449 | Self::Plain | |
450 | } | |
451 | } | |
452 | ||
453 | #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] | |
454 | pub struct RepositorySummary { | |
455 | pub repository: Repository, | |
456 | pub owner: User, | |
457 | pub visibility: RepositoryVisibility, | |
458 | pub description: Option<String>, | |
459 | pub last_commit: Option<Commit>, | |
460 | } | |
461 | ||
462 | #[derive(Clone, Debug, Serialize, Deserialize)] | |
463 | pub struct IssueLabel { | |
464 | pub name: String, | |
465 | pub color: String, | |
466 | } | |
467 | ||
468 | #[derive(Clone, Debug, Serialize, Deserialize)] | |
469 | pub struct RepositoryIssue { | |
470 | pub author: User, | |
471 | pub id: u64, | |
472 | pub title: String, | |
473 | pub contents: String, | |
474 | pub labels: Vec<IssueLabel>, | |
475 | } |
giterated-core/giterated-models/src/repository/operations.rs
@@ -0,0 +1,439 @@ | ||
1 | use serde::{Deserialize, Serialize}; | |
2 | ||
3 | use crate::{ | |
4 | error::{OperationError, RepositoryError}, | |
5 | object::Object, | |
6 | object_backend::ObjectBackend, | |
7 | operation::{GiteratedOperation, OperationState}, | |
8 | }; | |
9 | ||
10 | use super::{ | |
11 | Commit, IssueLabel, Repository, RepositoryBranch, RepositoryDiff, RepositoryFile, | |
12 | RepositoryIssue, RepositoryStatistics, RepositoryTreeEntry, RepositoryView, | |
13 | }; | |
14 | ||
15 | /// A request to get a repository's information. | |
16 | /// | |
17 | /// # Authentication | |
18 | /// - Instance Authentication | |
19 | /// - Validate request against the `issued_for` public key | |
20 | /// - Validate User token against the user's instance's public key | |
21 | /// # Authorization | |
22 | /// - User Authorization | |
23 | /// - Potential User permissions checks | |
24 | #[derive(Clone, Debug, Serialize, Deserialize)] | |
25 | pub struct RepositoryInfoRequest { | |
26 | pub extra_metadata: bool, | |
27 | pub rev: Option<String>, | |
28 | pub path: Option<String>, | |
29 | } | |
30 | ||
31 | impl GiteratedOperation<Repository> for RepositoryInfoRequest { | |
32 | type Success = RepositoryView; | |
33 | type Failure = RepositoryError; | |
34 | } | |
35 | ||
36 | /// A request to get a file from a repository using the given id. | |
37 | /// | |
38 | /// # Authentication | |
39 | /// - Instance Authentication | |
40 | /// - Validate request against the `issued_for` public key | |
41 | /// - Validate User token against the user's instance's public key | |
42 | /// # Authorization | |
43 | /// - User Authorization | |
44 | /// - Potential User permissions checks | |
45 | #[derive(Clone, Debug, Serialize, Deserialize)] | |
46 | pub struct RepositoryFileFromIdRequest(pub String); | |
47 | ||
48 | impl GiteratedOperation<Repository> for RepositoryFileFromIdRequest { | |
49 | type Success = RepositoryFile; | |
50 | type Failure = RepositoryError; | |
51 | } | |
52 | ||
53 | /// A request to get a file from a repository using the given relative path. | |
54 | /// Also returns the commit id of the rev. | |
55 | /// | |
56 | /// # Authentication | |
57 | /// - Instance Authentication | |
58 | /// - Validate request against the `issued_for` public key | |
59 | /// - Validate User token against the user's instance's public key | |
60 | /// # Authorization | |
61 | /// - User Authorization | |
62 | /// - Potential User permissions checks | |
63 | #[derive(Clone, Debug, Serialize, Deserialize)] | |
64 | pub struct RepositoryFileFromPathRequest { | |
65 | pub rev: Option<String>, | |
66 | pub path: String, | |
67 | } | |
68 | ||
69 | impl GiteratedOperation<Repository> for RepositoryFileFromPathRequest { | |
70 | type Success = (RepositoryFile, String); | |
71 | type Failure = RepositoryError; | |
72 | } | |
73 | ||
74 | /// A request to get the last commit made to a file, using a starting point and the path. | |
75 | /// | |
76 | /// # Authentication | |
77 | /// - Instance Authentication | |
78 | /// - Validate request against the `issued_for` public key | |
79 | /// - Validate User token against the user's instance's public key | |
80 | /// # Authorization | |
81 | /// - User Authorization | |
82 | /// - Potential User permissions checks | |
83 | #[derive(Clone, Debug, Serialize, Deserialize)] | |
84 | pub struct RepositoryLastCommitOfFileRequest { | |
85 | /// ID of commit to start at | |
86 | pub start_commit: String, | |
87 | // Path of the file | |
88 | pub path: String, | |
89 | } | |
90 | ||
91 | impl GiteratedOperation<Repository> for RepositoryLastCommitOfFileRequest { | |
92 | type Success = Commit; | |
93 | type Failure = RepositoryError; | |
94 | } | |
95 | ||
96 | /// A request to get the commit by the specified id. | |
97 | /// | |
98 | /// # Authentication | |
99 | /// - Instance Authentication | |
100 | /// - Validate request against the `issued_for` public key | |
101 | /// - Validate User token against the user's instance's public key | |
102 | /// # Authorization | |
103 | /// - User Authorization | |
104 | /// - Potential User permissions checks | |
105 | #[derive(Clone, Debug, Serialize, Deserialize)] | |
106 | pub struct RepositoryCommitFromIdRequest(pub String); | |
107 | ||
108 | impl GiteratedOperation<Repository> for RepositoryCommitFromIdRequest { | |
109 | type Success = Commit; | |
110 | type Failure = RepositoryError; | |
111 | } | |
112 | ||
113 | /// A request to get the difference between two repository trees. | |
114 | /// | |
115 | /// # Authentication | |
116 | /// - Instance Authentication | |
117 | /// - Validate request against the `issued_for` public key | |
118 | /// - Validate User token against the user's instance's public key | |
119 | /// # Authorization | |
120 | /// - User Authorization | |
121 | /// - Potential User permissions checks | |
122 | #[derive(Clone, Debug, Serialize, Deserialize)] | |
123 | pub struct RepositoryDiffRequest { | |
124 | pub old_id: String, | |
125 | pub new_id: String, | |
126 | } | |
127 | ||
128 | impl GiteratedOperation<Repository> for RepositoryDiffRequest { | |
129 | type Success = RepositoryDiff; | |
130 | type Failure = RepositoryError; | |
131 | } | |
132 | ||
133 | /// A request to get the difference between two repository trees as a unified git patch. | |
134 | /// | |
135 | /// # Authentication | |
136 | /// - Instance Authentication | |
137 | /// - Validate request against the `issued_for` public key | |
138 | /// - Validate User token against the user's instance's public key | |
139 | /// # Authorization | |
140 | /// - User Authorization | |
141 | /// - Potential User permissions checks | |
142 | #[derive(Clone, Debug, Serialize, Deserialize)] | |
143 | pub struct RepositoryDiffPatchRequest { | |
144 | pub old_id: String, | |
145 | pub new_id: String, | |
146 | } | |
147 | ||
148 | impl GiteratedOperation<Repository> for RepositoryDiffPatchRequest { | |
149 | type Success = String; | |
150 | type Failure = RepositoryError; | |
151 | } | |
152 | ||
153 | /// A request to get the commit before the one with the passed id | |
154 | /// | |
155 | /// # Authentication | |
156 | /// - Instance Authentication | |
157 | /// - Validate request against the `issued_for` public key | |
158 | /// - Validate User token against the user's instance's public key | |
159 | /// # Authorization | |
160 | /// - User Authorization | |
161 | /// - Potential User permissions checks | |
162 | #[derive(Clone, Debug, Serialize, Deserialize)] | |
163 | pub struct RepositoryCommitBeforeRequest(pub String); | |
164 | ||
165 | impl GiteratedOperation<Repository> for RepositoryCommitBeforeRequest { | |
166 | type Success = Commit; | |
167 | type Failure = RepositoryError; | |
168 | } | |
169 | ||
170 | #[derive(Clone, Debug, Serialize, Deserialize)] | |
171 | pub struct RepositoryIssuesCountRequest; | |
172 | ||
173 | impl GiteratedOperation<Repository> for RepositoryIssuesCountRequest { | |
174 | type Success = u64; | |
175 | type Failure = RepositoryError; | |
176 | } | |
177 | ||
178 | /// A request to get a repository's issues count. | |
179 | /// | |
180 | /// # Authentication | |
181 | /// - Instance Authentication | |
182 | /// - Validate request against the `issued_for` public key | |
183 | /// - Validate User token against the user's instance's public key | |
184 | /// # Authorization | |
185 | /// - User Authorization | |
186 | /// - Potential User permissions checks | |
187 | #[derive(Clone, Debug, Serialize, Deserialize)] | |
188 | pub struct RepositoryIssueLabelsRequest; | |
189 | ||
190 | impl GiteratedOperation<Repository> for RepositoryIssueLabelsRequest { | |
191 | type Success = Vec<IssueLabel>; | |
192 | type Failure = RepositoryError; | |
193 | } | |
194 | ||
195 | /// A request to get a repository's issue labels. | |
196 | /// | |
197 | /// # Authentication | |
198 | /// - Instance Authentication | |
199 | /// - Validate request against the `issued_for` public key | |
200 | /// - Validate User token against the user's instance's public key | |
201 | /// # Authorization | |
202 | /// - User Authorization | |
203 | /// - Potential User permissions checks | |
204 | #[derive(Clone, Debug, Serialize, Deserialize)] | |
205 | pub struct RepositoryIssuesRequest; | |
206 | ||
207 | impl GiteratedOperation<Repository> for RepositoryIssuesRequest { | |
208 | type Success = Vec<RepositoryIssue>; | |
209 | type Failure = RepositoryError; | |
210 | } | |
211 | ||
212 | /// A request to inspect the tree of a repository. | |
213 | /// | |
214 | /// # Authentication | |
215 | /// - Instance Authentication | |
216 | /// - Validate request against the `issued_for` public key | |
217 | /// - Validate User token against the user's instance's public key | |
218 | /// # Authorization | |
219 | /// - User Authorization | |
220 | /// - Potential User permissions checks | |
221 | #[derive(Clone, Debug, Serialize, Deserialize)] | |
222 | pub struct RepositoryFileInspectRequest { | |
223 | /// Whether to get extra metadata for every entry (file mode, size and last commit made to it). | |
224 | pub extra_metadata: bool, | |
225 | /// Revision of the repository to get (branch, commit id). | |
226 | pub rev: Option<String>, | |
227 | /// If not given a path, it'll default to the base. | |
228 | pub path: Option<String>, | |
229 | } | |
230 | ||
231 | impl GiteratedOperation<Repository> for RepositoryFileInspectRequest { | |
232 | type Success = Vec<RepositoryTreeEntry>; | |
233 | type Failure = RepositoryError; | |
234 | } | |
235 | ||
236 | /// A request to get the statistics of repository. | |
237 | /// | |
238 | /// # Authentication | |
239 | /// - Instance Authentication | |
240 | /// - Validate request against the `issued_for` public key | |
241 | /// - Validate User token against the user's instance's public key | |
242 | /// # Authorization | |
243 | /// - User Authorization | |
244 | /// - Potential User permissions checks | |
245 | #[derive(Clone, Debug, Serialize, Deserialize)] | |
246 | pub struct RepositoryStatisticsRequest { | |
247 | /// Revision of the repository to get (branch, commit id). | |
248 | pub rev: Option<String>, | |
249 | } | |
250 | ||
251 | impl GiteratedOperation<Repository> for RepositoryStatisticsRequest { | |
252 | type Success = RepositoryStatistics; | |
253 | type Failure = RepositoryError; | |
254 | } | |
255 | ||
256 | /// A request to get a list of branches in the repository. | |
257 | /// Skips over references with invalid UTF-8 names. | |
258 | /// | |
259 | /// # Authentication | |
260 | /// - Instance Authentication | |
261 | /// - Validate request against the `issued_for` public key | |
262 | /// - Validate User token against the user's instance's public key | |
263 | /// # Authorization | |
264 | /// - User Authorization | |
265 | /// - Potential User permissions checks | |
266 | #[derive(Clone, Debug, Serialize, Deserialize)] | |
267 | pub struct RepositoryBranchesRequest; | |
268 | ||
269 | impl GiteratedOperation<Repository> for RepositoryBranchesRequest { | |
270 | type Success = Vec<RepositoryBranch>; | |
271 | type Failure = RepositoryError; | |
272 | } | |
273 | ||
274 | impl<B: ObjectBackend + std::fmt::Debug> Object<Repository, B> { | |
275 | pub async fn info( | |
276 | &mut self, | |
277 | extra_metadata: bool, | |
278 | rev: Option<String>, | |
279 | path: Option<String>, | |
280 | operation_state: &OperationState, | |
281 | ) -> Result<RepositoryView, OperationError<RepositoryError>> { | |
282 | self.request::<RepositoryInfoRequest>( | |
283 | RepositoryInfoRequest { | |
284 | extra_metadata, | |
285 | rev, | |
286 | path, | |
287 | }, | |
288 | operation_state, | |
289 | ) | |
290 | .await | |
291 | } | |
292 | ||
293 | pub async fn file_from_id( | |
294 | &mut self, | |
295 | id: String, | |
296 | operation_state: &OperationState, | |
297 | ) -> Result<RepositoryFile, OperationError<RepositoryError>> { | |
298 | self.request::<RepositoryFileFromIdRequest>( | |
299 | RepositoryFileFromIdRequest(id), | |
300 | operation_state, | |
301 | ) | |
302 | .await | |
303 | } | |
304 | ||
305 | pub async fn file_from_path( | |
306 | &mut self, | |
307 | rev: Option<String>, | |
308 | path: String, | |
309 | operation_state: &OperationState, | |
310 | ) -> Result<(RepositoryFile, String), OperationError<RepositoryError>> { | |
311 | self.request::<RepositoryFileFromPathRequest>( | |
312 | RepositoryFileFromPathRequest { rev, path }, | |
313 | operation_state, | |
314 | ) | |
315 | .await | |
316 | } | |
317 | ||
318 | pub async fn last_commit_of_file( | |
319 | &mut self, | |
320 | start_commit: String, | |
321 | path: String, | |
322 | operation_state: &OperationState, | |
323 | ) -> Result<Commit, OperationError<RepositoryError>> { | |
324 | self.request::<RepositoryLastCommitOfFileRequest>( | |
325 | RepositoryLastCommitOfFileRequest { start_commit, path }, | |
326 | operation_state, | |
327 | ) | |
328 | .await | |
329 | } | |
330 | ||
331 | pub async fn commit_by_id( | |
332 | &mut self, | |
333 | id: String, | |
334 | operation_state: &OperationState, | |
335 | ) -> Result<Commit, OperationError<RepositoryError>> { | |
336 | self.request::<RepositoryCommitFromIdRequest>( | |
337 | RepositoryCommitFromIdRequest(id), | |
338 | operation_state, | |
339 | ) | |
340 | .await | |
341 | } | |
342 | ||
343 | pub async fn diff( | |
344 | &mut self, | |
345 | old_id: String, | |
346 | new_id: String, | |
347 | operation_state: &OperationState, | |
348 | ) -> Result<RepositoryDiff, OperationError<RepositoryError>> { | |
349 | self.request::<RepositoryDiffRequest>( | |
350 | RepositoryDiffRequest { old_id, new_id }, | |
351 | operation_state, | |
352 | ) | |
353 | .await | |
354 | } | |
355 | ||
356 | pub async fn diff_patch( | |
357 | &mut self, | |
358 | old_id: String, | |
359 | new_id: String, | |
360 | operation_state: &OperationState, | |
361 | ) -> Result<String, OperationError<RepositoryError>> { | |
362 | self.request::<RepositoryDiffPatchRequest>( | |
363 | RepositoryDiffPatchRequest { old_id, new_id }, | |
364 | operation_state, | |
365 | ) | |
366 | .await | |
367 | } | |
368 | ||
369 | pub async fn commit_before( | |
370 | &mut self, | |
371 | id: String, | |
372 | operation_state: &OperationState, | |
373 | ) -> Result<Commit, OperationError<RepositoryError>> { | |
374 | self.request::<RepositoryCommitBeforeRequest>( | |
375 | RepositoryCommitBeforeRequest(id), | |
376 | operation_state, | |
377 | ) | |
378 | .await | |
379 | } | |
380 | ||
381 | pub async fn statistics( | |
382 | &mut self, | |
383 | rev: Option<String>, | |
384 | operation_state: &OperationState, | |
385 | ) -> Result<RepositoryStatistics, OperationError<RepositoryError>> { | |
386 | self.request::<RepositoryStatisticsRequest>( | |
387 | RepositoryStatisticsRequest { rev }, | |
388 | operation_state, | |
389 | ) | |
390 | .await | |
391 | } | |
392 | ||
393 | pub async fn branches( | |
394 | &mut self, | |
395 | operation_state: &OperationState, | |
396 | ) -> Result<Vec<RepositoryBranch>, OperationError<RepositoryError>> { | |
397 | self.request::<RepositoryBranchesRequest>(RepositoryBranchesRequest, operation_state) | |
398 | .await | |
399 | } | |
400 | ||
401 | // pub async fn issues_count(&mut self) -> Result<u64, OperationError<RepositoryError>> { | |
402 | // self.request::<RepositoryIssuesCountRequest>(RepositoryIssuesCountRequest) | |
403 | // .await | |
404 | // } | |
405 | ||
406 | pub async fn issue_labels( | |
407 | &mut self, | |
408 | operation_state: &OperationState, | |
409 | ) -> Result<Vec<IssueLabel>, OperationError<RepositoryError>> { | |
410 | self.request::<RepositoryIssueLabelsRequest>(RepositoryIssueLabelsRequest, operation_state) | |
411 | .await | |
412 | } | |
413 | ||
414 | pub async fn issues( | |
415 | &mut self, | |
416 | operation_state: &OperationState, | |
417 | ) -> Result<Vec<RepositoryIssue>, OperationError<RepositoryError>> { | |
418 | self.request::<RepositoryIssuesRequest>(RepositoryIssuesRequest, operation_state) | |
419 | .await | |
420 | } | |
421 | ||
422 | pub async fn inspect_files( | |
423 | &mut self, | |
424 | extra_metadata: bool, | |
425 | rev: Option<&str>, | |
426 | path: Option<&str>, | |
427 | operation_state: &OperationState, | |
428 | ) -> Result<Vec<RepositoryTreeEntry>, OperationError<RepositoryError>> { | |
429 | self.request::<RepositoryFileInspectRequest>( | |
430 | RepositoryFileInspectRequest { | |
431 | extra_metadata, | |
432 | rev: rev.map(|r| r.to_string()), | |
433 | path: path.map(|p| p.to_string()), | |
434 | }, | |
435 | operation_state, | |
436 | ) | |
437 | .await | |
438 | } | |
439 | } |
giterated-core/giterated-models/src/repository/settings.rs
@@ -0,0 +1,28 @@ | ||
1 | use serde::{Deserialize, Serialize}; | |
2 | ||
3 | use crate::{settings::Setting, user::User}; | |
4 | ||
5 | use super::{CommitBodyType, DefaultBranch}; | |
6 | ||
7 | impl Setting for DefaultBranch { | |
8 | fn name() -> &'static str { | |
9 | "default_branch" | |
10 | } | |
11 | } | |
12 | ||
13 | #[derive(Debug, Hash, Clone, PartialEq, Eq, Serialize, Deserialize)] | |
14 | #[repr(transparent)] | |
15 | #[serde(transparent)] | |
16 | pub struct AccessList(pub Vec<User>); | |
17 | ||
18 | impl Setting for AccessList { | |
19 | fn name() -> &'static str { | |
20 | "access_list" | |
21 | } | |
22 | } | |
23 | ||
24 | impl Setting for CommitBodyType { | |
25 | fn name() -> &'static str { | |
26 | "commit_body_type" | |
27 | } | |
28 | } |
giterated-core/giterated-models/src/repository/values.rs
@@ -0,0 +1,99 @@ | ||
1 | use std::fmt::Display; | |
2 | ||
3 | use serde::{Deserialize, Serialize}; | |
4 | ||
5 | use crate::{settings::Setting, value::GiteratedObjectValue}; | |
6 | ||
7 | use super::{Commit, CommitBodyType, Repository, RepositoryVisibility}; | |
8 | ||
9 | // pub struct RepositorySetting<V: GiteratedObjectValue>(pub V); | |
10 | ||
11 | // impl<O: GiteratedObject, V: GiteratedObjectValue<Object = O> + Send> GiteratedOperation<O> | |
12 | // for RepositorySetting<V> | |
13 | // { | |
14 | // fn operation_name(&self) -> &'static str { | |
15 | // "setting_get" | |
16 | // } | |
17 | // type Success = V; | |
18 | // type Failure = GetValueError; | |
19 | // } | |
20 | ||
21 | #[derive(Debug, Hash, Clone, PartialEq, Eq, Serialize, Deserialize)] | |
22 | pub struct Description(pub String); | |
23 | ||
24 | impl Display for Description { | |
25 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | |
26 | f.write_str(&self.0) | |
27 | } | |
28 | } | |
29 | ||
30 | impl GiteratedObjectValue for Description { | |
31 | type Object = Repository; | |
32 | ||
33 | fn value_name() -> &'static str { | |
34 | "description" | |
35 | } | |
36 | } | |
37 | ||
38 | impl Setting for Description { | |
39 | fn name() -> &'static str { | |
40 | "description" | |
41 | } | |
42 | } | |
43 | ||
44 | #[derive(Debug, Hash, Clone, PartialEq, Eq, Serialize, Deserialize)] | |
45 | #[repr(transparent)] | |
46 | #[serde(transparent)] | |
47 | pub struct Visibility(pub RepositoryVisibility); | |
48 | ||
49 | impl GiteratedObjectValue for Visibility { | |
50 | type Object = Repository; | |
51 | ||
52 | fn value_name() -> &'static str { | |
53 | "visibility" | |
54 | } | |
55 | } | |
56 | ||
57 | impl Setting for Visibility { | |
58 | fn name() -> &'static str { | |
59 | "visibility" | |
60 | } | |
61 | } | |
62 | ||
63 | #[derive(Debug, Hash, Clone, PartialEq, Eq, Serialize, Deserialize)] | |
64 | #[serde(transparent)] | |
65 | #[repr(transparent)] | |
66 | pub struct DefaultBranch(pub String); | |
67 | ||
68 | impl Display for DefaultBranch { | |
69 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | |
70 | f.write_str(&self.0) | |
71 | } | |
72 | } | |
73 | ||
74 | impl GiteratedObjectValue for DefaultBranch { | |
75 | type Object = Repository; | |
76 | ||
77 | fn value_name() -> &'static str { | |
78 | "default_branch" | |
79 | } | |
80 | } | |
81 | ||
82 | #[derive(Debug, Hash, Clone, PartialEq, Eq, Serialize, Deserialize)] | |
83 | pub struct LatestCommit(pub Option<Commit>); | |
84 | ||
85 | impl GiteratedObjectValue for LatestCommit { | |
86 | type Object = Repository; | |
87 | ||
88 | fn value_name() -> &'static str { | |
89 | "latest_commit" | |
90 | } | |
91 | } | |
92 | ||
93 | impl GiteratedObjectValue for CommitBodyType { | |
94 | type Object = Repository; | |
95 | ||
96 | fn value_name() -> &'static str { | |
97 | "commit_body_type" | |
98 | } | |
99 | } |
giterated-core/giterated-models/src/settings/mod.rs
@@ -0,0 +1,25 @@ | ||
1 | mod operations; | |
2 | ||
3 | use std::{any::Any, sync::Arc}; | |
4 | ||
5 | pub use operations::*; | |
6 | use serde::{de::DeserializeOwned, Serialize}; | |
7 | ||
8 | pub trait Setting: Serialize + DeserializeOwned + Send + Sync { | |
9 | fn name() -> &'static str; | |
10 | } | |
11 | ||
12 | #[derive(Clone, Copy, Hash, PartialEq, Eq, Debug)] | |
13 | pub struct ObjectSettingPair<'s> { | |
14 | pub object_kind: &'s str, | |
15 | pub setting_name: &'s str, | |
16 | } | |
17 | ||
18 | impl<'s> ObjectSettingPair<'s> { | |
19 | pub fn new(object_kind: &'s str, setting_name: &'s str) -> Self { | |
20 | Self { | |
21 | object_kind, | |
22 | setting_name, | |
23 | } | |
24 | } | |
25 | } |
giterated-core/giterated-models/src/settings/operations.rs
@@ -0,0 +1,46 @@ | ||
1 | use std::fmt::Debug; | |
2 | ||
3 | use serde::{Deserialize, Serialize}; | |
4 | use serde_json::Value; | |
5 | use thiserror::Error; | |
6 | ||
7 | use crate::{object::GiteratedObject, operation::GiteratedOperation}; | |
8 | ||
9 | #[derive(Serialize, Deserialize, Debug, Clone)] | |
10 | pub struct GetSetting { | |
11 | pub setting_name: String, | |
12 | } | |
13 | ||
14 | impl<O: GiteratedObject> GiteratedOperation<O> for GetSetting { | |
15 | fn operation_name() -> &'static str { | |
16 | "get_setting" | |
17 | } | |
18 | ||
19 | type Success = Value; | |
20 | ||
21 | type Failure = GetSettingError; | |
22 | } | |
23 | ||
24 | #[derive(Error, Debug, Serialize, Deserialize, Clone)] | |
25 | pub enum GetSettingError {} | |
26 | #[derive(Serialize, Deserialize, Debug, Clone)] | |
27 | pub struct SetSetting { | |
28 | pub setting_name: String, | |
29 | pub value: Value, | |
30 | } | |
31 | ||
32 | impl<O: GiteratedObject> GiteratedOperation<O> for SetSetting { | |
33 | fn operation_name() -> &'static str { | |
34 | "set_setting" | |
35 | } | |
36 | ||
37 | type Success = (); | |
38 | ||
39 | type Failure = SetSettingError; | |
40 | } | |
41 | ||
42 | #[derive(Error, Debug, Serialize, Deserialize, Clone)] | |
43 | pub enum SetSettingError { | |
44 | #[error("Invalid setting `{0}` on object `{0}`")] | |
45 | InvalidSetting(String, String), | |
46 | } |
giterated-core/giterated-models/src/update/instance.rs
@@ -0,0 +1 @@ | ||
1 |
giterated-core/giterated-models/src/update/mod.rs
@@ -0,0 +1,9 @@ | ||
1 | mod instance; | |
2 | mod repository; | |
3 | mod user; | |
4 | ||
5 | pub enum GiteratedUpdateKind { | |
6 | Instance, | |
7 | Repository, | |
8 | Value, | |
9 | } |
giterated-core/giterated-models/src/update/repository.rs
@@ -0,0 +1 @@ | ||
1 |
giterated-core/giterated-models/src/update/user.rs
@@ -0,0 +1 @@ | ||
1 |
giterated-core/giterated-models/src/user/mod.rs
@@ -0,0 +1,102 @@ | ||
1 | mod operations; | |
2 | mod settings; | |
3 | mod values; | |
4 | ||
5 | use std::{ | |
6 | fmt::{Display, Formatter}, | |
7 | str::FromStr, | |
8 | }; | |
9 | ||
10 | pub use operations::*; | |
11 | use secrecy::{CloneableSecret, DebugSecret, SerializableSecret, Zeroize}; | |
12 | use serde::{Deserialize, Serialize}; | |
13 | ||
14 | pub use values::*; | |
15 | ||
16 | use crate::{instance::Instance, object::GiteratedObject}; | |
17 | ||
18 | /// A user, defined by its username and instance. | |
19 | /// | |
20 | /// # Textual Format | |
21 | /// A user's textual reference is defined as: | |
22 | /// | |
23 | /// `{username: String}:{instance: Instance}` | |
24 | /// | |
25 | /// # Examples | |
26 | /// For the user with the username `barson` and the instance `giterated.dev`, | |
27 | /// the following [`User`] initialization would be valid: | |
28 | /// | |
29 | /// ``` | |
30 | /// let user = User { | |
31 | /// username: String::from("barson"), | |
32 | /// instance: Instance::from_str("giterated.dev").unwrap() | |
33 | /// }; | |
34 | /// | |
35 | /// // This is correct | |
36 | /// assert_eq!(User::from_str("barson:giterated.dev").unwrap(), user); | |
37 | /// ``` | |
38 | #[derive(Clone, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)] | |
39 | pub struct User { | |
40 | pub username: String, | |
41 | pub instance: Instance, | |
42 | } | |
43 | ||
44 | impl GiteratedObject for User { | |
45 | fn object_name() -> &'static str { | |
46 | "user" | |
47 | } | |
48 | ||
49 | fn from_object_str(object_str: &str) -> Result<Self, anyhow::Error> { | |
50 | Ok(User::from_str(object_str)?) | |
51 | } | |
52 | ||
53 | fn home_uri(&self) -> String { | |
54 | self.instance.home_uri() | |
55 | } | |
56 | } | |
57 | ||
58 | impl Display for User { | |
59 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { | |
60 | write!(f, "{}:{}", self.username, self.instance.0) | |
61 | } | |
62 | } | |
63 | ||
64 | impl From<String> for User { | |
65 | fn from(user_string: String) -> Self { | |
66 | User::from_str(&user_string).unwrap() | |
67 | } | |
68 | } | |
69 | ||
70 | impl FromStr for User { | |
71 | type Err = UserParseError; | |
72 | ||
73 | fn from_str(s: &str) -> Result<Self, Self::Err> { | |
74 | if s.contains('/') { | |
75 | return Err(UserParseError); | |
76 | } | |
77 | ||
78 | let mut colon_split = s.split(':'); | |
79 | let username = colon_split.next().ok_or(UserParseError)?.to_string(); | |
80 | let instance = Instance::from_str(colon_split.next().ok_or(UserParseError)?) | |
81 | .map_err(|_| UserParseError)?; | |
82 | ||
83 | Ok(Self { username, instance }) | |
84 | } | |
85 | } | |
86 | ||
87 | #[derive(thiserror::Error, Debug)] | |
88 | #[error("failed to parse user")] | |
89 | pub struct UserParseError; | |
90 | ||
91 | #[derive(Clone, Debug, Serialize, Deserialize)] | |
92 | pub struct Password(pub String); | |
93 | ||
94 | impl Zeroize for Password { | |
95 | fn zeroize(&mut self) { | |
96 | self.0.zeroize() | |
97 | } | |
98 | } | |
99 | ||
100 | impl SerializableSecret for Password {} | |
101 | impl CloneableSecret for Password {} | |
102 | impl DebugSecret for Password {} |
giterated-core/giterated-models/src/user/operations.rs
@@ -0,0 +1,40 @@ | ||
1 | use serde::{Deserialize, Serialize}; | |
2 | ||
3 | use crate::{ | |
4 | error::{OperationError, UserError}, | |
5 | instance::Instance, | |
6 | object::Object, | |
7 | object_backend::ObjectBackend, | |
8 | operation::{GiteratedOperation, OperationState}, | |
9 | repository::RepositorySummary, | |
10 | }; | |
11 | ||
12 | use super::User; | |
13 | ||
14 | #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] | |
15 | pub struct UserRepositoriesRequest { | |
16 | pub instance: Instance, | |
17 | pub user: User, | |
18 | } | |
19 | ||
20 | impl GiteratedOperation<User> for UserRepositoriesRequest { | |
21 | type Success = Vec<RepositorySummary>; | |
22 | type Failure = UserError; | |
23 | } | |
24 | ||
25 | impl<B: ObjectBackend + std::fmt::Debug> Object<User, B> { | |
26 | pub async fn repositories( | |
27 | &mut self, | |
28 | instance: &Instance, | |
29 | operation_state: &OperationState, | |
30 | ) -> Result<Vec<RepositorySummary>, OperationError<UserError>> { | |
31 | self.request::<UserRepositoriesRequest>( | |
32 | UserRepositoriesRequest { | |
33 | instance: instance.clone(), | |
34 | user: self.inner.clone(), | |
35 | }, | |
36 | operation_state, | |
37 | ) | |
38 | .await | |
39 | } | |
40 | } |
giterated-core/giterated-models/src/user/settings.rs
@@ -0,0 +1,15 @@ | ||
1 | use crate::settings::Setting; | |
2 | ||
3 | use super::{Bio, DisplayName}; | |
4 | ||
5 | impl Setting for Bio { | |
6 | fn name() -> &'static str { | |
7 | "bio" | |
8 | } | |
9 | } | |
10 | ||
11 | impl Setting for DisplayName { | |
12 | fn name() -> &'static str { | |
13 | "display_name" | |
14 | } | |
15 | } |
giterated-core/giterated-models/src/user/values.rs
@@ -0,0 +1,41 @@ | ||
1 | use std::fmt::Display; | |
2 | ||
3 | use serde::{Deserialize, Serialize}; | |
4 | ||
5 | use crate::value::GiteratedObjectValue; | |
6 | ||
7 | use super::User; | |
8 | ||
9 | #[derive(Debug, Hash, Clone, PartialEq, Eq, Serialize, Deserialize)] | |
10 | pub struct Bio(pub String); | |
11 | ||
12 | impl GiteratedObjectValue for Bio { | |
13 | type Object = User; | |
14 | ||
15 | fn value_name() -> &'static str { | |
16 | "bio" | |
17 | } | |
18 | } | |
19 | ||
20 | impl Display for Bio { | |
21 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | |
22 | f.write_str(&self.0) | |
23 | } | |
24 | } | |
25 | ||
26 | #[derive(Debug, Hash, Clone, PartialEq, Eq, Serialize, Deserialize)] | |
27 | pub struct DisplayName(pub String); | |
28 | ||
29 | impl Display for DisplayName { | |
30 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | |
31 | f.write_str(&self.0) | |
32 | } | |
33 | } | |
34 | ||
35 | impl GiteratedObjectValue for DisplayName { | |
36 | type Object = User; | |
37 | ||
38 | fn value_name() -> &'static str { | |
39 | "display_name" | |
40 | } | |
41 | } |
giterated-core/giterated-models/src/value.rs
@@ -0,0 +1,55 @@ | ||
1 | use std::{fmt::Debug, marker::PhantomData}; | |
2 | ||
3 | use serde::{de::DeserializeOwned, Deserialize, Serialize}; | |
4 | use serde_json::Value; | |
5 | ||
6 | use crate::{error::GetValueError, object::GiteratedObject, operation::GiteratedOperation}; | |
7 | ||
8 | pub trait GiteratedObjectValue: Send + Sync + Serialize + DeserializeOwned { | |
9 | type Object: GiteratedObject; | |
10 | ||
11 | fn value_name() -> &'static str; | |
12 | } | |
13 | ||
14 | #[derive(Serialize, Deserialize, Debug, Clone)] | |
15 | pub struct GetValue { | |
16 | pub value_name: String, | |
17 | } | |
18 | ||
19 | impl<O: GiteratedObject + Send> GiteratedOperation<O> for GetValue { | |
20 | fn operation_name() -> &'static str { | |
21 | "get_value" | |
22 | } | |
23 | type Success = Vec<u8>; | |
24 | type Failure = GetValueError; | |
25 | } | |
26 | ||
27 | #[derive(Serialize, Deserialize, Debug, Clone)] | |
28 | pub struct GetValueTyped<V: GiteratedObjectValue> { | |
29 | pub ty: PhantomData<V>, | |
30 | } | |
31 | ||
32 | impl<O: GiteratedObject + Send, V: GiteratedObjectValue<Object = O>> GiteratedOperation<O> | |
33 | for GetValueTyped<V> | |
34 | { | |
35 | fn operation_name() -> &'static str { | |
36 | "get_value" | |
37 | } | |
38 | type Success = V; | |
39 | type Failure = GetValueError; | |
40 | } | |
41 | ||
42 | #[derive(Clone, Copy, Hash, PartialEq, Eq, Debug)] | |
43 | pub struct ObjectValuePair<'s> { | |
44 | pub object_kind: &'s str, | |
45 | pub value_name: &'s str, | |
46 | } | |
47 | ||
48 | impl<'s> ObjectValuePair<'s> { | |
49 | pub fn new(object_kind: &'s str, value_name: &'s str) -> Self { | |
50 | Self { | |
51 | object_kind, | |
52 | value_name, | |
53 | } | |
54 | } | |
55 | } |
giterated-core/src/lib.rs
@@ -1,68 +1,70 @@ | ||
1 | mod state; | |
2 | mod types; | |
1 | use giterated_abi::vtable::{runtime::RuntimeHandle, VTable}; | |
2 | ||
3 | pub mod state; | |
4 | pub mod types; | |
3 | 5 | |
4 | 6 | #[derive(Clone)] |
5 | 7 | #[repr(C)] |
6 | 8 | pub struct RuntimeState { |
7 | pub vtable: VTable<RuntimeState>, | |
9 | pub vtable: &'static VTable<RuntimeHandle>, | |
8 | 10 | } |
9 | 11 | |
10 | impl RuntimeState { | |
11 | pub unsafe fn from_static() -> Self { | |
12 | let runtime = giterated_static_runtime::get_runtime_reference(); | |
12 | // impl RuntimeState { | |
13 | // pub unsafe fn from_static() -> Self { | |
14 | // let runtime = giterated_static_runtime::get_runtime_reference(); | |
13 | 15 | |
14 | let runtime = runtime.cast::<Box<Runtime>>().as_ref(); | |
16 | // let runtime = runtime.cast::<Box<Runtime>>().as_ref(); | |
15 | 17 | |
16 | runtime.state() | |
17 | } | |
18 | // runtime.state() | |
19 | // } | |
18 | 20 | |
19 | pub unsafe fn runtime_state() -> PluginState { | |
20 | let runtime = giterated_static_runtime::get_runtime_reference(); | |
21 | // pub unsafe fn runtime_state() -> PluginState { | |
22 | // let runtime = giterated_static_runtime::get_runtime_reference(); | |
21 | 23 | |
22 | PluginState::from_raw_ptr(giterated_static_runtime::get_runtime_reference().as_ptr()) | |
23 | } | |
24 | } | |
24 | // PluginState::from_raw_ptr(giterated_static_runtime::get_runtime_reference().as_ptr()) | |
25 | // } | |
26 | // } | |
25 | 27 | |
26 | #[async_trait::async_trait(?Send)] | |
27 | impl ObjectBackend for RuntimeState { | |
28 | async fn object_operation<O, D>( | |
29 | &self, | |
30 | object: O, | |
31 | _operation: &str, | |
32 | payload: D, | |
33 | _operation_state: &OperationState, | |
34 | ) -> Result<D::Success, OperationError<D::Failure>> | |
35 | where | |
36 | O: GiteratedObject + Debug + 'static, | |
37 | D: GiteratedOperation<O> + Debug + 'static, | |
38 | D::Success: Clone, | |
39 | D::Failure: Clone, | |
40 | { | |
41 | // let _object = AnyObject::new(object); | |
42 | // let _operation = AnyOperation::new(payload); | |
28 | // #[async_trait::async_trait(?Send)] | |
29 | // impl ObjectBackend for RuntimeState { | |
30 | // async fn object_operation<O, D>( | |
31 | // &self, | |
32 | // object: O, | |
33 | // _operation: &str, | |
34 | // payload: D, | |
35 | // _operation_state: &OperationState, | |
36 | // ) -> Result<D::Success, OperationError<D::Failure>> | |
37 | // where | |
38 | // O: GiteratedObject + Debug + 'static, | |
39 | // D: GiteratedOperation<O> + Debug + 'static, | |
40 | // D::Success: Clone, | |
41 | // D::Failure: Clone, | |
42 | // { | |
43 | // // let _object = AnyObject::new(object); | |
44 | // // let _operation = AnyOperation::new(payload); | |
43 | 45 | |
44 | todo!() | |
45 | } | |
46 | // todo!() | |
47 | // } | |
46 | 48 | |
47 | async fn get_object<O: GiteratedObject + Debug + 'static>( | |
48 | &self, | |
49 | object_str: &str, | |
50 | operation_state: &OperationState, | |
51 | ) -> Result<Object<O, Self>, OperationError<ObjectRequestError>> { | |
52 | // let object = unsafe { | |
53 | // (self.vtable.get_object)( | |
54 | // Self::runtime_state(), | |
55 | // object_str, | |
56 | // &mut operation_state.clone(), | |
57 | // ) | |
58 | // }?; | |
49 | // async fn get_object<O: GiteratedObject + Debug + 'static>( | |
50 | // &self, | |
51 | // object_str: &str, | |
52 | // operation_state: &OperationState, | |
53 | // ) -> Result<Object<O, Self>, OperationError<ObjectRequestError>> { | |
54 | // // let object = unsafe { | |
55 | // // (self.vtable.get_object)( | |
56 | // // Self::runtime_state(), | |
57 | // // object_str, | |
58 | // // &mut operation_state.clone(), | |
59 | // // ) | |
60 | // // }?; | |
59 | 61 | |
60 | // let object = unsafe { object.cast::<O>() }; | |
62 | // // let object = unsafe { object.cast::<O>() }; | |
61 | 63 | |
62 | panic!("object casted"); | |
64 | // panic!("object casted"); | |
63 | 65 | |
64 | // Ok(unsafe { Object::new_unchecked(object, self.clone()) }) | |
66 | // // Ok(unsafe { Object::new_unchecked(object, self.clone()) }) | |
65 | 67 | |
66 | todo!() | |
67 | } | |
68 | } | |
68 | // todo!() | |
69 | // } | |
70 | // } |
giterated-core/src/state.rs
@@ -18,6 +18,7 @@ use giterated_abi::prelude::*; | ||
18 | 18 | use giterated_abi::value_ex::FfiValueUntyped; |
19 | 19 | use giterated_abi::vtable::ObjectABI; |
20 | 20 | use giterated_abi::vtable::VTable; |
21 | use giterated_models::error::OperationError; | |
21 | 22 | |
22 | 23 | #[repr(transparent)] |
23 | 24 | pub struct State { |
giterated-core/src/types/mod.rs
@@ -1,3 +1,11 @@ | ||
1 | use std::collections::HashMap; | |
2 | ||
3 | use giterated_abi::vtable::{operation::Operation, Object, Setting, VTable, Value}; | |
4 | use giterated_models::{ | |
5 | object::ObjectOperationPair, settings::ObjectSettingPair, value::ObjectValuePair, | |
6 | }; | |
7 | use tracing::trace; | |
8 | ||
1 | 9 | #[derive(Default, Clone)] |
2 | 10 | pub struct TypeMetadata { |
3 | 11 | pub objects: HashMap<&'static str, &'static VTable<Object>>, |
giterated-daemon/Cargo.toml
@@ -29,7 +29,7 @@ reqwest = "0.11" | ||
29 | 29 | argon2 = "0.5" |
30 | 30 | aes-gcm = "0.10" |
31 | 31 | semver = {version = "1.0", features = ["serde"]} |
32 | giterated-models = { path = "../giterated-models" } | |
32 | giterated-models = { path = "../giterated-core/giterated-models" } | |
33 | 33 | giterated-plugin = { path = "../giterated-plugin" } |
34 | 34 | giterated-protocol = { path = "../plugins/giterated-protocol" } |
35 | 35 | deadpool = "0.9" |
giterated-plugin/Cargo.toml
@@ -10,13 +10,13 @@ dlopen2 = "0.6" | ||
10 | 10 | anyhow = "1" |
11 | 11 | thiserror = "1" |
12 | 12 | tracing = "0.1" |
13 | giterated-models = { path = "../giterated-models" } | |
13 | giterated-models = { path = "../giterated-core/giterated-models" } | |
14 | 14 | giterated-static-runtime = { path = "../giterated-runtime/giterated-static-runtime" } |
15 | giterated-abi = { path = "../giterated-abi" } | |
15 | giterated-abi = { path = "../giterated-runtime/giterated-abi" } | |
16 | 16 | semver = "*" |
17 | 17 | serde_json = "1.0" |
18 | 18 | async-trait = "0.1" |
19 | 19 | serde = "*" |
20 | 20 | futures-util = "0.3.30" |
21 | 21 | tokio = { version = "1.32", features = [ "full" ] } |
22 | giterated-macros = { path = "../giterated-macros" } | |
22 | \ No newline at end of file | |
22 | giterated-macros = { path = "giterated-macros" } | |
22 | \ No newline at end of file |
giterated-plugin/giterated-macros/Cargo.toml
@@ -0,0 +1,11 @@ | ||
1 | [package] | |
2 | name = "giterated-macros" | |
3 | version = "0.1.0" | |
4 | edition = "2021" | |
5 | ||
6 | [lib] | |
7 | proc-macro = true | |
8 | ||
9 | [dependencies] | |
10 | quote = "1" | |
11 | syn = "2" | |
11 | \ No newline at end of file |
giterated-plugin/giterated-macros/src/lib.rs
@@ -0,0 +1,44 @@ | ||
1 | use proc_macro::TokenStream; | |
2 | use quote::quote; | |
3 | use syn::{parse_macro_input, Abi, Attribute, ItemFn}; | |
4 | ||
5 | extern crate proc_macro; | |
6 | ||
7 | #[proc_macro] | |
8 | pub fn plugin(metadata: TokenStream) -> TokenStream { | |
9 | emit_plugin_api().into() | |
10 | } | |
11 | ||
12 | #[proc_macro_attribute] | |
13 | pub fn plugin_init(attribute: TokenStream, item: TokenStream) -> TokenStream { | |
14 | let input = parse_macro_input!(item as ItemFn); | |
15 | ||
16 | let func = input.sig.ident.clone(); | |
17 | ||
18 | quote! { | |
19 | #[doc(hidden)] | |
20 | #[no_mangle] | |
21 | unsafe extern "C" fn __plugin_init() { | |
22 | #func(&mut ::giterated_plugin::local::PluginStackBuilder::new()).unwrap() | |
23 | } | |
24 | ||
25 | #input | |
26 | } | |
27 | .into() | |
28 | } | |
29 | ||
30 | fn emit_plugin_api() -> impl Into<TokenStream> { | |
31 | quote! { | |
32 | #[doc(hidden)] | |
33 | #[no_mangle] | |
34 | unsafe extern "C" fn __load_runtime_vtable(vtable: &'static ::giterated_plugin::abi::vtable::VTable<::giterated_plugin::abi::vtable::runtime::RuntimeHandle>) { | |
35 | todo!() | |
36 | } | |
37 | ||
38 | #[doc(hidden)] | |
39 | #[no_mangle] | |
40 | unsafe extern "C" fn __get_plugin_vtable() -> &'static ::giterated_plugin::abi::vtable::VTable<::giterated_plugin::abi::vtable::plugin::Plugin> { | |
41 | todo!() | |
42 | } | |
43 | } | |
44 | } |
giterated-runtime/Cargo.toml
@@ -6,3 +6,8 @@ edition = "2021" | ||
6 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html |
7 | 7 | |
8 | 8 | [dependencies] |
9 | giterated-abi = { path = "giterated-abi" } | |
10 | dlopen2 = "0.6" | |
11 | giterated-models = { path = "../giterated-core/giterated-models" } | |
12 | tracing = "0.1" | |
13 | giterated-core = { path = "../giterated-core" } |
giterated-runtime/giterated-abi/Cargo.toml
@@ -0,0 +1,11 @@ | ||
1 | [package] | |
2 | name = "giterated-abi" | |
3 | version = "0.1.0" | |
4 | edition = "2021" | |
5 | ||
6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | |
7 | ||
8 | [dependencies] | |
9 | giterated-models = { path = "../../giterated-core/giterated-models"} | |
10 | anyhow = "1" | |
11 | dlopen2 = "0.6" |
giterated-runtime/giterated-abi/README.md
@@ -0,0 +1,25 @@ | ||
1 | # Versioning | |
2 | ||
3 | This versioning documentation specifically refers to the version of the Giterated ABI. The Giterated ABI can, and should, update independently from the rest of the project, as needed. | |
4 | ||
5 | ## Pre 1.0.0 (Now!) | |
6 | ||
7 | You are able to consume `giterated-abi` in your `Cargo.toml` manifest as follows with the expectation nothing should break as `giterated-abi` updates: | |
8 | ``` | |
9 | giterated-abi = "0.1" | |
10 | ``` | |
11 | ||
12 | It is important to still receive patch updates automatically as we may provide shim functionality and better compatibility with future changes in these updates. There may also be security and performance fixes. Furthermore it allows for Cargo to be more precise when selecting the exact dependency version. | |
13 | ||
14 | - Stability is **not** guaranteed. | |
15 | - Breaking changes **can** be made between "minor" semver versions, they **should** be well documented and announced ahead of time. | |
16 | ||
17 | ## Post 1.0.0 (Future) | |
18 | ||
19 | You are able to consume `giterated-abi` in your `Cargo.toml` manifest as follows with the *guarantee* of compatibility. | |
20 | ||
21 | ``` | |
22 | giterated-abi = "1" | |
23 | ``` | |
24 | ||
25 | After 1.0.0 is released, minor updates **must not** contain changes that violate a reasonable understanding of semver. The ABI is intended to be stable and only changed in a backwards-compatible manner beyond 1.0.0. | |
25 | \ No newline at end of file |
giterated-runtime/giterated-abi/src/callback/mod.rs
@@ -0,0 +1,27 @@ | ||
1 | pub mod operation; | |
2 | pub mod setting; | |
3 | pub mod value; | |
4 | ||
5 | use std::marker::PhantomData; | |
6 | ||
7 | pub trait Callback { | |
8 | type CallbackFunc; | |
9 | } | |
10 | ||
11 | #[derive(Copy, Clone)] | |
12 | #[repr(C)] | |
13 | pub struct CallbackPtr<T: Callback> { | |
14 | callback_ptr: *const (), | |
15 | func: T::CallbackFunc, | |
16 | _marker: PhantomData<T>, | |
17 | } | |
18 | ||
19 | impl<T: Callback> CallbackPtr<T> { | |
20 | pub unsafe fn from_raw(data: T, func: T::CallbackFunc) -> Self { | |
21 | todo!() | |
22 | } | |
23 | ||
24 | pub fn func(&self) -> &T::CallbackFunc { | |
25 | &self.func | |
26 | } | |
27 | } |
giterated-runtime/giterated-abi/src/callback/operation.rs
@@ -0,0 +1,140 @@ | ||
1 | use std::future::Future; | |
2 | ||
3 | use giterated_models::{ | |
4 | error::OperationError, object::GiteratedObject, operation::GiteratedOperation, | |
5 | }; | |
6 | ||
7 | use crate::{ | |
8 | result::FfiResult, | |
9 | state::State, | |
10 | value_ex::FfiValueUntyped, | |
11 | vtable::{operation::Operation, Object}, | |
12 | FfiFuture, FfiValueMut, FfiValueRef, | |
13 | }; | |
14 | ||
15 | use super::{Callback, CallbackPtr}; | |
16 | ||
17 | use std::fmt::Debug; | |
18 | ||
19 | pub struct OperationHandlerCallback(FfiValueUntyped); | |
20 | ||
21 | impl Callback for OperationHandlerCallback { | |
22 | type CallbackFunc = unsafe extern "C" fn( | |
23 | CallbackPtr<OperationHandlerCallback>, | |
24 | state: FfiValueMut<State>, | |
25 | object: FfiValueRef<Object>, | |
26 | operation: FfiValueRef<Operation>, | |
27 | ) -> FfiFuture< | |
28 | FfiResult<FfiValueUntyped, OperationError<FfiValueUntyped>>, | |
29 | >; | |
30 | } | |
31 | ||
32 | pub trait IntoPluginOperationHandler<O: GiteratedObject, D: GiteratedOperation<O>, A> { | |
33 | unsafe extern "C" fn handle( | |
34 | callback_ptr: CallbackPtr<OperationHandlerCallback>, | |
35 | state: FfiValueMut<State>, | |
36 | object: FfiValueRef<Object>, | |
37 | operation: FfiValueRef<Operation>, | |
38 | ) -> FfiFuture<FfiResult<FfiValueUntyped, OperationError<FfiValueUntyped>>>; | |
39 | fn callback_ptr(&self) -> CallbackPtr<OperationHandlerCallback>; | |
40 | } | |
41 | ||
42 | impl<F, O, D, Fut> IntoPluginOperationHandler<O, D, ()> for F | |
43 | where | |
44 | Fut: Future<Output = Result<D::Success, OperationError<D::Failure>>> + Send + Sync, | |
45 | F: Fn(O, D) -> Fut + Send + Sync + 'static, | |
46 | O: Debug + GiteratedObject + 'static, | |
47 | D: Debug + GiteratedOperation<O> + 'static, | |
48 | { | |
49 | unsafe extern "C" fn handle( | |
50 | callback: CallbackPtr<OperationHandlerCallback>, | |
51 | state: FfiValueMut<State>, | |
52 | object: FfiValueRef<Object>, | |
53 | operation: FfiValueRef<Operation>, | |
54 | ) -> FfiFuture<FfiResult<FfiValueUntyped, OperationError<FfiValueUntyped>>> { | |
55 | todo!() | |
56 | // let _guard = trace_span!( | |
57 | // "operation handler", | |
58 | // object = type_name::<O>(), | |
59 | // operation = type_name::<D>() | |
60 | // ) | |
61 | // .entered(); | |
62 | // let state = unsafe { state.transmute_ref::<S>() }; | |
63 | ||
64 | // // Since this is Rust code, we know that the AnyObject and AnyOperation are just boxes | |
65 | // let object = unsafe { object.transmute_owned::<O>() }; | |
66 | // let operation = unsafe { operation.transmute_owned::<D>() }; | |
67 | ||
68 | // // Cast the callback ptr to ourselves | |
69 | // let callback: *const F = std::mem::transmute(callback.0); | |
70 | // let callback = callback.as_ref().unwrap(); | |
71 | ||
72 | // let state = state.clone(); | |
73 | // runtime_state.spawn_future(async move { | |
74 | // let result = callback(state, *object, *operation).await; | |
75 | ||
76 | // match result { | |
77 | // Ok(success) => unsafe { | |
78 | // todo!() | |
79 | // // Ok(AnySuccess::from_raw( | |
80 | // // FFIBox::from_box(Box::new(success)).untyped(), | |
81 | // // OperationVTable::new::<O, D>(), | |
82 | // // )) | |
83 | // }, | |
84 | // Err(err) => match err { | |
85 | // OperationError::Operation(_) => todo!(), | |
86 | // OperationError::Internal(_) => todo!(), | |
87 | // OperationError::Unhandled => todo!(), | |
88 | // }, | |
89 | // } | |
90 | // }) | |
91 | } | |
92 | ||
93 | fn callback_ptr(&self) -> CallbackPtr<OperationHandlerCallback> { | |
94 | // unsafe { CallbackPtr::from_raw(self as *const _ as *const ()) } | |
95 | ||
96 | todo!() | |
97 | } | |
98 | } | |
99 | ||
100 | impl<F, O, D, Fut, A1> IntoPluginOperationHandler<O, D, (A1,)> for F | |
101 | where | |
102 | Fut: Future<Output = Result<D::Success, OperationError<D::Failure>>>, | |
103 | F: Fn(O, D, A1) -> Fut, | |
104 | O: Debug + GiteratedObject, | |
105 | D: Debug + GiteratedOperation<O>, | |
106 | { | |
107 | unsafe extern "C" fn handle( | |
108 | _callback_ptr: CallbackPtr<OperationHandlerCallback>, | |
109 | state: FfiValueMut<State>, | |
110 | object: FfiValueRef<Object>, | |
111 | operation: FfiValueRef<Operation>, | |
112 | ) -> FfiFuture<FfiResult<FfiValueUntyped, OperationError<FfiValueUntyped>>> { | |
113 | todo!() | |
114 | } | |
115 | ||
116 | fn callback_ptr(&self) -> CallbackPtr<OperationHandlerCallback> { | |
117 | todo!() | |
118 | } | |
119 | } | |
120 | ||
121 | impl<F, O, D, Fut, A1, A2> IntoPluginOperationHandler<O, D, (A1, A2)> for F | |
122 | where | |
123 | Fut: Future<Output = Result<D::Success, OperationError<D::Failure>>>, | |
124 | F: Fn(O, D, A1, A2) -> Fut, | |
125 | O: Debug + GiteratedObject, | |
126 | D: Debug + GiteratedOperation<O>, | |
127 | { | |
128 | unsafe extern "C" fn handle( | |
129 | _callback_ptr: CallbackPtr<OperationHandlerCallback>, | |
130 | state: FfiValueMut<State>, | |
131 | object: FfiValueRef<Object>, | |
132 | operation: FfiValueRef<Operation>, | |
133 | ) -> FfiFuture<FfiResult<FfiValueUntyped, OperationError<FfiValueUntyped>>> { | |
134 | todo!() | |
135 | } | |
136 | ||
137 | fn callback_ptr(&self) -> CallbackPtr<OperationHandlerCallback> { | |
138 | todo!() | |
139 | } | |
140 | } |
giterated-runtime/giterated-abi/src/callback/setting.rs
@@ -0,0 +1,161 @@ | ||
1 | use std::future::Future; | |
2 | ||
3 | use giterated_models::{error::OperationError, object::GiteratedObject}; | |
4 | ||
5 | use crate::{ | |
6 | state::State, | |
7 | value_ex::FfiValueUntyped, | |
8 | vtable::{Object, Setting}, | |
9 | FfiFuture, FfiValueMut, FfiValueRef, | |
10 | }; | |
11 | ||
12 | use super::{Callback, CallbackPtr}; | |
13 | ||
14 | pub struct SettingGetterCallback(FfiValueUntyped); | |
15 | ||
16 | impl Callback for SettingGetterCallback { | |
17 | type CallbackFunc = unsafe extern "C" fn( | |
18 | CallbackPtr<SettingGetterCallback>, | |
19 | state: FfiValueMut<State>, | |
20 | object: FfiValueRef<Object>, | |
21 | ) -> FfiFuture<Result<Setting, ()>>; | |
22 | } | |
23 | ||
24 | pub trait IntoPluginSettingGetter<O, OS> { | |
25 | unsafe extern "C" fn get_setting( | |
26 | callback_ptr: CallbackPtr<SettingGetterCallback>, | |
27 | state: FfiValueMut<State>, | |
28 | object: FfiValueRef<Object>, | |
29 | ) -> FfiFuture<Result<Setting, ()>>; | |
30 | ||
31 | fn callback_ptr(&self) -> CallbackPtr<SettingGetterCallback> { | |
32 | // unsafe { CallbackPtr::from_raw(self as *const _ as *const ()) } | |
33 | todo!() | |
34 | } | |
35 | } | |
36 | ||
37 | impl<F, O, OS, Fut> IntoPluginSettingGetter<O, OS> for F | |
38 | where | |
39 | Fut: Future<Output = Result<OS, OperationError<anyhow::Error>>> + Send + Sync + 'static, | |
40 | O: GiteratedObject + Send + Sync + 'static, | |
41 | OS: giterated_models::settings::Setting + Send + Sync + 'static, | |
42 | F: Fn(O) -> Fut + Send + Sync + 'static, | |
43 | { | |
44 | unsafe extern "C" fn get_setting( | |
45 | callback: CallbackPtr<SettingGetterCallback>, | |
46 | state: FfiValueMut<State>, | |
47 | mut object: FfiValueRef<Object>, | |
48 | ) -> FfiFuture<Result<Setting, ()>> { | |
49 | // let _guard = trace_span!( | |
50 | // "get_setting handler", | |
51 | // object = O::object_name(), | |
52 | // setting = OS::name() | |
53 | // ) | |
54 | // .entered(); | |
55 | // let state = unsafe { state.transmute_ref::<S>() }; | |
56 | ||
57 | // let object = unsafe { object.transmute_owned::<O>() }; | |
58 | ||
59 | // // Cast the callback ptr to ourselves | |
60 | // let callback: *const F = std::mem::transmute(callback.0); | |
61 | // let callback = callback.as_ref().unwrap(); | |
62 | ||
63 | // let state = state.clone(); | |
64 | // runtime_state.spawn_future(async move { | |
65 | // let result = callback(state, *object).await; | |
66 | ||
67 | // match result { | |
68 | // Ok(success) => unsafe { Ok(NewAnySetting::new(success)) }, | |
69 | // Err(err) => match err { | |
70 | // OperationError::Operation(_) => todo!(), | |
71 | // OperationError::Internal(_) => todo!(), | |
72 | // OperationError::Unhandled => todo!(), | |
73 | // }, | |
74 | // } | |
75 | ||
76 | todo!() | |
77 | // }) | |
78 | } | |
79 | } | |
80 | ||
81 | pub trait IntoPluginSettingSetter<O, OS> { | |
82 | unsafe extern "C" fn set_setting( | |
83 | callback_ptr: CallbackPtr<SettingGetterCallback>, | |
84 | state: FfiValueMut<State>, | |
85 | object: FfiValueRef<Object>, | |
86 | setting: Setting, | |
87 | ) -> FfiFuture<Result<(), ()>>; | |
88 | ||
89 | fn callback_ptr(&self) -> CallbackPtr<SettingGetterCallback> { | |
90 | // unsafe { CallbackPtr::from_raw(self as *const _ as *const ()) } | |
91 | todo!() | |
92 | } | |
93 | } | |
94 | ||
95 | impl<F, O, OS, Fut> IntoPluginSettingSetter<O, OS> for F | |
96 | where | |
97 | Fut: Future<Output = Result<(), OperationError<anyhow::Error>>>, | |
98 | O: GiteratedObject, | |
99 | OS: giterated_models::settings::Setting, | |
100 | F: Fn(O, OS) -> Fut, | |
101 | { | |
102 | unsafe extern "C" fn set_setting( | |
103 | callback: CallbackPtr<SettingGetterCallback>, | |
104 | state: FfiValueMut<State>, | |
105 | mut object: FfiValueRef<Object>, | |
106 | _setting: Setting, | |
107 | ) -> FfiFuture<Result<(), ()>> { | |
108 | // let _guard = trace_span!( | |
109 | // "get_setting handler", | |
110 | // object = O::object_name(), | |
111 | // setting = OS::name() | |
112 | // ) | |
113 | // .entered(); | |
114 | // let _state = unsafe { state.transmute_ref::<S>() }; | |
115 | ||
116 | // let _object = unsafe { object.transmute_owned::<O>() }; | |
117 | ||
118 | // // Cast the callback ptr to ourselves | |
119 | // let callback: *const F = std::mem::transmute(callback.0); | |
120 | // let _callback = callback.as_ref().unwrap(); | |
121 | ||
122 | // let result = callback(state.clone(), *object); | |
123 | ||
124 | // match result { | |
125 | // Ok(setting) => Ok(NewAnySetting::new(setting)), | |
126 | // Err(_) => todo!(), | |
127 | // } | |
128 | todo!() | |
129 | } | |
130 | } | |
131 | ||
132 | pub struct SettingChangeCallback(FfiValueUntyped); | |
133 | ||
134 | impl Callback for SettingChangeCallback { | |
135 | type CallbackFunc = unsafe extern "C" fn( | |
136 | state: FfiValueMut<State>, | |
137 | object: FfiValueRef<Object>, | |
138 | setting_name: &str, | |
139 | new_setting: Setting, | |
140 | ); | |
141 | } | |
142 | ||
143 | pub trait IntoSettingChangeCallback<S, O> { | |
144 | unsafe extern "C" fn setting_changed( | |
145 | state: FfiValueMut<State>, | |
146 | object: FfiValueRef<Object>, | |
147 | setting_name: &str, | |
148 | new_setting: Setting, | |
149 | ); | |
150 | } | |
151 | ||
152 | impl<F, S, O> IntoSettingChangeCallback<S, O> for F { | |
153 | unsafe extern "C" fn setting_changed( | |
154 | state: FfiValueMut<State>, | |
155 | _object: FfiValueRef<Object>, | |
156 | _setting_name: &str, | |
157 | _new_setting: Setting, | |
158 | ) { | |
159 | todo!() | |
160 | } | |
161 | } |
giterated-runtime/giterated-abi/src/callback/value.rs
@@ -0,0 +1,117 @@ | ||
1 | use std::future::Future; | |
2 | ||
3 | use giterated_models::{ | |
4 | error::OperationError, object::GiteratedObject, value::GiteratedObjectValue, | |
5 | }; | |
6 | ||
7 | use crate::{ | |
8 | result::{FfiError, FfiResult}, | |
9 | state::State, | |
10 | vtable::{Object, Value}, | |
11 | FfiFuture, FfiSliceRef, FfiValueMut, FfiValueRef, | |
12 | }; | |
13 | ||
14 | use super::{setting::SettingGetterCallback, Callback, CallbackPtr}; | |
15 | ||
16 | #[derive(Copy, Clone)] | |
17 | pub struct ValueGetterCallback(CallbackPtr<ValueGetterCallback>); | |
18 | ||
19 | impl Callback for ValueGetterCallback { | |
20 | type CallbackFunc = unsafe extern "C" fn( | |
21 | CallbackPtr<ValueGetterCallback>, | |
22 | state: FfiValueMut<State>, | |
23 | object: FfiValueRef<Object>, | |
24 | ) -> FfiFuture<FfiResult<Value, FfiError>>; | |
25 | } | |
26 | ||
27 | pub trait IntoPluginValueGetter<O, V> { | |
28 | unsafe extern "C" fn get_value( | |
29 | callback: CallbackPtr<SettingGetterCallback>, | |
30 | state: FfiValueMut<State>, | |
31 | object: FfiValueRef<Object>, | |
32 | ) -> FfiFuture<FfiResult<Value, FfiError>>; | |
33 | ||
34 | fn callback_ptr(&self) -> CallbackPtr<SettingGetterCallback>; | |
35 | } | |
36 | ||
37 | impl<F, O, V, Fut> IntoPluginValueGetter<O, V> for F | |
38 | where | |
39 | Fut: Future<Output = Result<V, OperationError<anyhow::Error>>> + Send + Sync, | |
40 | O: GiteratedObject + 'static, | |
41 | V: GiteratedObjectValue<Object = O> + Send + Sync + 'static, | |
42 | F: Fn(O) -> Fut + Send + Sync + 'static, | |
43 | { | |
44 | unsafe extern "C" fn get_value( | |
45 | callback: CallbackPtr<SettingGetterCallback>, | |
46 | state: FfiValueMut<State>, | |
47 | mut object: FfiValueRef<Object>, | |
48 | ) -> FfiFuture<FfiResult<Value, FfiError>> { | |
49 | // let _guard = trace_span!( | |
50 | // "get_value handler", | |
51 | // object = O::object_name(), | |
52 | // value = V::value_name() | |
53 | // ) | |
54 | // .entered(); | |
55 | // let state = unsafe { state.transmute_ref::<S>() }; | |
56 | ||
57 | // let object = unsafe { object.transmute_owned::<O>() }; | |
58 | ||
59 | // // Cast the callback ptr to ourselves | |
60 | // let callback: *const F = std::mem::transmute(callback.0); | |
61 | // let callback = callback.as_ref().unwrap(); | |
62 | ||
63 | // let state = state.clone(); | |
64 | // runtime_state.spawn_future(async move { | |
65 | // let result = callback(state, *object).await; | |
66 | ||
67 | // match result { | |
68 | // Ok(success) => unsafe { Ok(NewAnyValue::new(success)) }, | |
69 | // Err(err) => match err { | |
70 | // OperationError::Operation(_) => todo!(), | |
71 | // OperationError::Internal(_) => todo!(), | |
72 | // OperationError::Unhandled => todo!(), | |
73 | // }, | |
74 | // } | |
75 | // }) | |
76 | ||
77 | todo!() | |
78 | } | |
79 | ||
80 | fn callback_ptr(&self) -> CallbackPtr<SettingGetterCallback> { | |
81 | todo!() | |
82 | // unsafe { CallbackPtr::from_raw(self as *const _ as *const ()) } | |
83 | } | |
84 | } | |
85 | ||
86 | pub struct ValueChangeCallback(CallbackPtr<ValueChangeCallback>); | |
87 | ||
88 | impl Callback for ValueChangeCallback { | |
89 | type CallbackFunc = unsafe extern "C" fn( | |
90 | state: FfiValueMut<State>, | |
91 | object: FfiValueRef<Object>, | |
92 | value_name: FfiSliceRef<str>, | |
93 | new_value: Value, | |
94 | ) -> FfiFuture<()>; | |
95 | } | |
96 | ||
97 | pub trait IntoValueChangeCallback<S, O> { | |
98 | unsafe extern "C" fn value_changed( | |
99 | callback: CallbackPtr<ValueChangeCallback>, | |
100 | state: FfiValueMut<State>, | |
101 | object: FfiValueRef<Object>, | |
102 | value_name: FfiSliceRef<str>, | |
103 | new_value: Value, | |
104 | ) -> FfiFuture<()>; | |
105 | } | |
106 | ||
107 | impl<F, S, O> IntoValueChangeCallback<S, O> for F { | |
108 | unsafe extern "C" fn value_changed( | |
109 | callback: CallbackPtr<ValueChangeCallback>, | |
110 | state: FfiValueMut<State>, | |
111 | _object: FfiValueRef<Object>, | |
112 | _value_name: FfiSliceRef<str>, | |
113 | _new_value: Value, | |
114 | ) -> FfiFuture<()> { | |
115 | todo!() | |
116 | } | |
117 | } |
giterated-runtime/giterated-abi/src/future.rs
@@ -0,0 +1,56 @@ | ||
1 | use std::marker::PhantomData; | |
2 | ||
3 | use crate::{FfiValue, FfiValueMut}; | |
4 | ||
5 | #[repr(C)] | |
6 | pub struct FfiFuture<Output> { | |
7 | poll_fn: unsafe extern "C" fn(FfiValueMut<FfiFuture<()>>, FfiValueMut<()>) -> RuntimeFuturePoll, | |
8 | wake_fn: unsafe extern "C" fn(FfiValueMut<FfiFuture<()>>, FfiValueMut<()>), | |
9 | ||
10 | poll_state: FfiValue<()>, | |
11 | pub waker_state: Option<FfiValue<()>>, | |
12 | ||
13 | _output_marker: PhantomData<Output>, | |
14 | } | |
15 | ||
16 | unsafe impl<Output> Send for FfiFuture<Output> where Output: Send {} | |
17 | unsafe impl<Output> Sync for FfiFuture<Output> where Output: Sync {} | |
18 | ||
19 | impl<Output> FfiFuture<Output> { | |
20 | /// Docs here! | |
21 | /// # SAFETY | |
22 | /// TODO :( | |
23 | pub unsafe fn poll(&mut self) -> RuntimeFuturePoll { | |
24 | todo!() | |
25 | } | |
26 | ||
27 | /// Docs here! | |
28 | /// # SAFETY | |
29 | /// TODO :( | |
30 | pub unsafe fn from_raw<PS>( | |
31 | _poll_fn: unsafe extern "C" fn( | |
32 | FfiValueMut<FfiFuture<()>>, | |
33 | FfiValueMut<()>, | |
34 | ) -> RuntimeFuturePoll, | |
35 | _poll_state: PS, | |
36 | ) -> Self { | |
37 | todo!() | |
38 | } | |
39 | ||
40 | /// Docs here! | |
41 | /// # SAFETY | |
42 | /// Very not in progress text :) | |
43 | pub unsafe fn write_waker<WS>( | |
44 | &mut self, | |
45 | _wake_fn: unsafe extern "C" fn(FfiValueMut<FfiFuture<()>>), | |
46 | _waker_state: WS, | |
47 | ) { | |
48 | todo!() | |
49 | } | |
50 | } | |
51 | ||
52 | #[repr(C)] | |
53 | pub enum RuntimeFuturePoll { | |
54 | Ready(FfiValue<()>), | |
55 | Pending, | |
56 | } |
giterated-runtime/giterated-abi/src/heap.rs
@@ -0,0 +1,26 @@ | ||
1 | use std::{mem::MaybeUninit, ptr::drop_in_place}; | |
2 | ||
3 | use crate::{abi_backing::HeapValueBacking, FfiValue}; | |
4 | ||
5 | pub trait HeapPlacable { | |
6 | unsafe extern "C" fn free(value: FfiValue<Self>, taken: bool); | |
7 | } | |
8 | ||
9 | impl<T> HeapPlacable for T { | |
10 | unsafe extern "C" fn free(value: FfiValue<Self>, taken: bool) { | |
11 | if !taken { | |
12 | drop(Box::from_raw(value.inner as *mut HeapValueBacking<T>)) | |
13 | } else { | |
14 | let allocation = Box::from_raw(value.inner as *mut T); | |
15 | ||
16 | // Since we "took" the value, kindly inform the compiler that it can't | |
17 | // treat the value like it exists | |
18 | let allocation_uninit: Box<HeapValueBacking<MaybeUninit<T>>> = | |
19 | unsafe { core::mem::transmute(allocation) }; | |
20 | ||
21 | // Since the compiler has no idea whether the value exists or not, it won't try and | |
22 | // drop it. Success! | |
23 | drop(allocation_uninit); | |
24 | } | |
25 | } | |
26 | } |
giterated-runtime/giterated-abi/src/lib.rs
@@ -0,0 +1,556 @@ | ||
1 | //! Giterated ABI | |
2 | //! # ABI | |
3 | //! | |
4 | //! ## Value ABI | |
5 | //! | |
6 | //! At its core, the Giterated Runtime uses the `extern "C"` ABI. What that means is likely platform specific, and doesn't matter. | |
7 | //! You are intended to compile the Giterated Runtime and Plugins for your local machine, all with a similar idea of what | |
8 | //! your "local machine" is. | |
9 | //! | |
10 | //! Values are passed using the `FFI` type. There are four categories of value that the `FFI` type enables you to pass: | |
11 | //! | |
12 | //! | `FFI` Type Category | Placed Backing? | Owned? | | |
13 | //! |---------------------|-----------------|--------| | |
14 | //! | Slice | Heap/Stack | No | | |
15 | //! | Referenced Slice | Stack | No | | |
16 | //! | Referenced Value | No | No | | |
17 | //! | Owned Value | Heap | Yes | | |
18 | //! | |
19 | //! For an FFI type to have a "placed backing" is for it to have some data structure beyond the data it represents, placed | |
20 | //! somewhere in memory. Some types only require stack placement while some offer both stack and heap placement. | |
21 | //! | |
22 | //! Stack-placed values can be shared by `PinnedRef` and `PinnedMut`, and thus can only be owned by the caller. | |
23 | //! | |
24 | //! Heap-placed values can be shared by `Owned`, `PinnedRef`, and `PinnedMut`. They can be owned by any one consumer, | |
25 | //! When the handle with ownership is `Drop`'d by the sole consumer, it will free the object using the associated `Drop` callback. | |
26 | //! | |
27 | //! ### Safety Intents | |
28 | //! | |
29 | //! This API is designed to simplify interaction with FFI values, and provide a static ABI for those values to be passed. It | |
30 | //! is key to enabling ownership across FFI while ensuring associated dropping and allocation freeing logic is ran. | |
31 | //! | |
32 | //! The contract the developer has to follow is made simpler by this system, and it allows for generic code to be written to | |
33 | //! interact with FFI-given values and pass values using FFI. | |
34 | //! | |
35 | //! ### Stability Guarantees | |
36 | //! | |
37 | //! There are no plans to guarantee stability until 1.0.0. At that point you can expect the ABI to remain stable until the major version | |
38 | //! is incremented again. There will be an appropriate deprecation process and changeover period. | |
39 | //! | |
40 | //! ### Memory Representation | |
41 | //! | |
42 | //! Please check out the source code, sorry if you needed that from the docs! | |
43 | //! | |
44 | //! ## Object, Operation, Setting, Value, Plugin, and Runtime ABIs | |
45 | //! | |
46 | //! The Giterated Runtime uses vtables to accomplish the goal of ensuring maximum compatibility. For every object that is shared | |
47 | //! between plugins, a vtable is used to allow each plugin to provide their own code for interacting with the object. | |
48 | //! | |
49 | //! When objects switch "runtime domains" (e.g. host -> plugin, plugin -> plugin, plugin -> host), their vtable is swapped out | |
50 | //! for the new runtime domain's own vtables. | |
51 | //! | |
52 | //! ### Untyped "Objects" (see above header for list) | |
53 | //! | |
54 | //! Untyped objects, in memory, are represented by a data pointer and a vtable pointer. Exactly like Rust traits. However, to | |
55 | //! prevent small compilation differences and other random garbage from making the interface not perfectly compatible we use | |
56 | //! the local plugin's idea of the vtable for the object at all times. An object that the plugin does not have a vtable for cannot | |
57 | //! be relevant to the plugin. | |
58 | //! | |
59 | //! It is important that the object's base representation in memory remain unchanged between major versions, but the vtables that provide methods for | |
60 | //! that object may be grown. The methods that operate on that object may be changed in an non-breaking fashion, and bugs can be | |
61 | //! fixed. | |
62 | //! | |
63 | //! ## Futures ABI | |
64 | //! | |
65 | //! The Giterated Runtime has an async runtime that allows for futures to be shared and awaited across FFI boundaries while only | |
66 | //! executing the future within the context of the Plugin who is running the underlying future. | |
67 | //! | |
68 | //! Futures are spawned onto the `RuntimeState` with the `RuntimeFuturesExt` trait. This takes a Rust future, boxes it, and | |
69 | //! provides a `RuntimeFuture` handle that can be used to drive the underlying Rust future locally. The `RuntimeFuture` handle | |
70 | //! is thread-safe and can be shared with the callee and `.await`'d directly like any other future. | |
71 | //! | |
72 | //! ### RuntimeFuture | |
73 | //! | |
74 | //! The `RuntimeFuture` mixes a vtable with data to allow for any caller to drive a spawned future. It contains: | |
75 | //! | |
76 | //! - A `poll_fn` which is used to poll the future for `Ready`-ness | |
77 | //! - A `wake_fn` which is used to wake the callee to poll for (expected) `Ready`-ness, it is populated when the `RuntimeFuture` is `await`'d. | |
78 | //! | |
79 | //! When the `RuntimeFuture` is polled, it causes the inner future to also be polled. We provide the inner future with a waker | |
80 | //! that triggers the `RuntimeFuture`'s waker so it is polled again. Breaking character to point out how freaking cool that is. | |
81 | //! | |
82 | //! `RuntimeFuture`s drop the associated inner future as they drop. | |
83 | ||
84 | pub mod callback; | |
85 | mod future; | |
86 | pub mod heap; | |
87 | pub mod model_impl; | |
88 | pub mod plugin; | |
89 | pub mod result; | |
90 | pub mod state; | |
91 | pub mod vtable; | |
92 | use abi_backing::{HeapValueBacking, SliceBacking}; | |
93 | pub use future::{FfiFuture, RuntimeFuturePoll}; | |
94 | use heap::HeapPlacable; | |
95 | use prelude::value_ex::FfiValueUntyped; | |
96 | ||
97 | use std::{ | |
98 | marker::PhantomData, | |
99 | mem::{transmute, MaybeUninit}, | |
100 | ops::{Deref, DerefMut}, | |
101 | }; | |
102 | ||
103 | use abi_types::{Slice, SliceMut, SliceRef, Value, ValueMut, ValueRef}; | |
104 | use guards::{HeapPinnedSlice, HeapPinnedValue, StackPinnedSlice, StackPinnedValue}; | |
105 | ||
106 | #[doc(hidden)] | |
107 | pub mod prelude { | |
108 | pub use crate::Ffi; | |
109 | pub use crate::StackPinned; | |
110 | pub use crate::*; | |
111 | pub use crate::{FfiSlice, FfiSliceRef, FfiValue, FfiValueRef}; | |
112 | } | |
113 | ||
114 | /// Slice Reference | |
115 | /// Heap or Stack Placed | |
116 | pub type FfiSliceRef<T> = Ffi<T, SliceRef>; | |
117 | ||
118 | /// Mutable Slice Reference | |
119 | /// Heap or Stack Placed | |
120 | pub type FfiSliceMut<T> = Ffi<T, SliceMut>; | |
121 | ||
122 | /// Value Reference | |
123 | /// Heap or Stack Placed | |
124 | pub type FfiValueRef<T> = Ffi<T, ValueRef>; | |
125 | ||
126 | /// Mutable Value Reference | |
127 | /// Heap or Stack Placed | |
128 | pub type FfiValueMut<T> = Ffi<T, ValueMut>; | |
129 | ||
130 | /// Owned Value | |
131 | /// Heap Placed | |
132 | pub type FfiValue<T> = Ffi<T, Value>; | |
133 | ||
134 | /// Owned Slice | |
135 | /// Heap Placed | |
136 | pub type FfiSlice<T> = Ffi<T, Slice>; | |
137 | ||
138 | pub mod value_ex { | |
139 | use crate::{abi_types::Value, Ffi}; | |
140 | ||
141 | pub type FfiValueUntyped = Ffi<(), Value>; | |
142 | pub type FfiValueRefUntyped = Ffi<(), Value>; | |
143 | } | |
144 | ||
145 | /// A value passed over FFI, following the Giterated ABI. | |
146 | /// | |
147 | /// The function of the [`Ffi`] type is to take an arbitrary pointer and send it over FFI. | |
148 | /// Both the caller and callee **must** have the same understanding of what the pointer represents. | |
149 | /// The [`Ffi`] type is also used to encode ownership information. | |
150 | /// | |
151 | /// # The Pointer | |
152 | /// The pointer contained within the [`Ffi`] is transmuted based on the provided `ABI` on the | |
153 | /// [`Ffi`] type signature. | |
154 | #[repr(transparent)] | |
155 | pub struct Ffi<T: ?Sized, ABI> { | |
156 | inner: *const (), | |
157 | _type_marker: PhantomData<T>, | |
158 | _abi_marker: PhantomData<ABI>, | |
159 | } | |
160 | ||
161 | impl<T> FfiSlice<T> { | |
162 | #[inline(always)] | |
163 | pub fn pin(&self) -> HeapPinnedSlice<'_, T> { | |
164 | unsafe { HeapPinnedSlice::from_raw(self) } | |
165 | } | |
166 | } | |
167 | ||
168 | impl<T> Deref for FfiSlice<T> { | |
169 | type Target = [T]; | |
170 | ||
171 | #[inline(always)] | |
172 | fn deref(&self) -> &Self::Target { | |
173 | let inner: *const SliceBacking<[T]> = unsafe { transmute(self.inner) }; | |
174 | let backing = unsafe { inner.as_ref().unwrap_unchecked() }; | |
175 | ||
176 | unsafe { | |
177 | core::slice::from_raw_parts( | |
178 | backing.slice as *mut T, | |
179 | usize::try_from(backing.count).unwrap_unchecked(), | |
180 | ) | |
181 | } | |
182 | } | |
183 | } | |
184 | impl<T> DerefMut for FfiSlice<T> { | |
185 | #[inline(always)] | |
186 | fn deref_mut(&mut self) -> &mut Self::Target { | |
187 | let inner: *mut SliceBacking<[T]> = unsafe { transmute(self.inner) }; | |
188 | let backing = unsafe { inner.as_mut().unwrap_unchecked() }; | |
189 | ||
190 | unsafe { | |
191 | core::slice::from_raw_parts_mut( | |
192 | backing.slice as *mut T, | |
193 | usize::try_from(backing.count).unwrap_unchecked(), | |
194 | ) | |
195 | } | |
196 | } | |
197 | } | |
198 | ||
199 | impl<T> FfiSliceRef<T> {} | |
200 | ||
201 | impl<T> Deref for FfiSliceRef<[T]> { | |
202 | type Target = [T]; | |
203 | ||
204 | #[inline(always)] | |
205 | fn deref(&self) -> &Self::Target { | |
206 | let inner: *const SliceBacking<[T]> = unsafe { transmute(self.inner) }; | |
207 | ||
208 | let backing = unsafe { inner.as_ref().unwrap_unchecked() }; | |
209 | ||
210 | unsafe { | |
211 | core::slice::from_raw_parts( | |
212 | backing.slice as *const T, | |
213 | usize::try_from(backing.count).unwrap_unchecked(), | |
214 | ) | |
215 | } | |
216 | } | |
217 | } | |
218 | ||
219 | impl<T> FfiValueRef<T> {} | |
220 | ||
221 | impl<T> Deref for FfiValueRef<T> { | |
222 | type Target = T; | |
223 | ||
224 | #[inline(always)] | |
225 | fn deref(&self) -> &Self::Target { | |
226 | let inner: *const T = unsafe { transmute(self.inner) }; | |
227 | ||
228 | match unsafe { inner.as_ref() } { | |
229 | Some(val) => val, | |
230 | _ => unreachable!(), | |
231 | } | |
232 | } | |
233 | } | |
234 | ||
235 | impl<T> Deref for FfiValueMut<T> { | |
236 | type Target = T; | |
237 | ||
238 | fn deref(&self) -> &Self::Target { | |
239 | let inner: *mut T = unsafe { transmute(self.inner) }; | |
240 | ||
241 | unsafe { inner.as_ref().unwrap_unchecked() } | |
242 | } | |
243 | } | |
244 | impl<T> DerefMut for FfiValueMut<T> { | |
245 | fn deref_mut(&mut self) -> &mut Self::Target { | |
246 | let inner: *mut T = unsafe { transmute(self.inner) }; | |
247 | ||
248 | unsafe { inner.as_mut().unwrap_unchecked() } | |
249 | } | |
250 | } | |
251 | ||
252 | impl<T> std::fmt::Display for FfiValueRef<T> | |
253 | where | |
254 | T: std::fmt::Display, | |
255 | { | |
256 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | |
257 | unsafe { (self.inner as *const T).as_ref().unwrap() }.fmt(f) | |
258 | } | |
259 | } | |
260 | ||
261 | impl<T> FfiValue<T> { | |
262 | pub fn new(value: T) -> Self { | |
263 | let value = Box::new(HeapValueBacking { | |
264 | value, | |
265 | drop_fn: <T as HeapPlacable>::free, | |
266 | }); | |
267 | ||
268 | FfiValue { | |
269 | inner: Box::into_raw(value) as _, | |
270 | _type_marker: PhantomData, | |
271 | _abi_marker: PhantomData, | |
272 | } | |
273 | } | |
274 | ||
275 | pub fn erase_type(self) -> FfiValueUntyped { | |
276 | unsafe { transmute(self) } | |
277 | } | |
278 | ||
279 | pub fn pin(&self) -> HeapPinnedValue<'_, T> { | |
280 | unsafe { HeapPinnedValue::from_raw(self) } | |
281 | } | |
282 | ||
283 | pub fn take(self) -> T { | |
284 | // This all boils down to moving `T` out of the `FfiValue` and dropping the backing | |
285 | // storage for said `FfiValue`. Despite the use of unsafe this is exactly how moving | |
286 | // a value onto the stack works. | |
287 | ||
288 | let inner = self.inner as *mut T; | |
289 | let mut move_target: MaybeUninit<T> = MaybeUninit::zeroed(); | |
290 | ||
291 | unsafe { move_target.as_mut_ptr().copy_from(inner, 1) } | |
292 | ||
293 | let inner_descriptor: *mut HeapValueBacking<T> = unsafe { transmute(self.inner) }; | |
294 | ||
295 | unsafe { (inner_descriptor.as_mut().unwrap_unchecked().drop_fn)(self, true) }; | |
296 | ||
297 | unsafe { move_target.assume_init() } | |
298 | } | |
299 | } | |
300 | ||
301 | impl<T> Deref for FfiValue<T> { | |
302 | type Target = T; | |
303 | ||
304 | #[inline(always)] | |
305 | fn deref(&self) -> &Self::Target { | |
306 | let inner: *const T = unsafe { transmute(self.inner) }; | |
307 | ||
308 | unsafe { inner.as_ref().unwrap_unchecked() } | |
309 | } | |
310 | } | |
311 | impl<T> DerefMut for FfiValue<T> { | |
312 | #[inline(always)] | |
313 | fn deref_mut(&mut self) -> &mut Self::Target { | |
314 | let inner: *mut T = unsafe { transmute(self.inner) }; | |
315 | ||
316 | unsafe { inner.as_mut().unwrap_unchecked() } | |
317 | } | |
318 | } | |
319 | ||
320 | mod abi_backing { | |
321 | use std::{marker::PhantomData, mem::transmute}; | |
322 | ||
323 | use crate::{FfiSlice, FfiValue}; | |
324 | ||
325 | #[repr(C)] | |
326 | pub struct HeapValueBacking<T: Sized> { | |
327 | pub(super) value: T, | |
328 | pub(super) drop_fn: unsafe extern "C" fn(value: FfiValue<T>, taken: bool), | |
329 | } | |
330 | ||
331 | pub struct SliceBacking<T: ?Sized> { | |
332 | pub(crate) count: u64, | |
333 | pub(crate) slice: *const (), | |
334 | _marker: PhantomData<T>, | |
335 | } | |
336 | ||
337 | impl<T: ?Sized> SliceBacking<T> { | |
338 | /// Creates a new slice backing from a raw slice pointer and a count. | |
339 | /// | |
340 | /// # SAFETY | |
341 | /// | |
342 | /// `slice` **must** refer to a valid slice, with a length greater than or equal to the | |
343 | /// value provided as `count`. | |
344 | #[inline(always)] | |
345 | pub(crate) unsafe fn from_raw(count: u64, slice: *const ()) -> Self { | |
346 | Self { | |
347 | count, | |
348 | slice, | |
349 | _marker: PhantomData, | |
350 | } | |
351 | } | |
352 | ||
353 | /// Creates a new slice backing from an [`FfiSlice`]. | |
354 | /// | |
355 | /// # SAFETY | |
356 | /// | |
357 | /// The resultant [`SliceBacking`] **must not** outlive the backing [`FfiSlice`]. | |
358 | #[inline(always)] | |
359 | pub(crate) unsafe fn from_heap(slice: &FfiSlice<T>) -> Self { | |
360 | let heap_backing: *const SliceBacking<T> = unsafe { transmute(slice.inner) }; | |
361 | ||
362 | let heap_backing = unsafe { heap_backing.as_ref().unwrap_unchecked() }; | |
363 | ||
364 | Self { | |
365 | count: heap_backing.count, | |
366 | slice: heap_backing.slice, | |
367 | _marker: PhantomData, | |
368 | } | |
369 | } | |
370 | } | |
371 | } | |
372 | ||
373 | mod guards { | |
374 | use std::marker::PhantomData; | |
375 | ||
376 | use crate::{ | |
377 | abi_backing::SliceBacking, Ffi, FfiSlice, FfiSliceMut, FfiSliceRef, FfiValue, FfiValueMut, | |
378 | FfiValueRef, | |
379 | }; | |
380 | ||
381 | #[repr(transparent)] | |
382 | pub struct StackPinnedSlice<'v, T: ?Sized> { | |
383 | _lifetime: PhantomData<&'v T>, | |
384 | slice: SliceBacking<T>, | |
385 | } | |
386 | ||
387 | impl<'v, T> StackPinnedSlice<'v, T> { | |
388 | #[inline(always)] | |
389 | pub fn as_ref(&self) -> FfiSliceRef<T> { | |
390 | FfiSliceRef { | |
391 | inner: &self.slice as *const _ as *const (), | |
392 | _type_marker: PhantomData, | |
393 | _abi_marker: PhantomData, | |
394 | } | |
395 | } | |
396 | ||
397 | #[inline(always)] | |
398 | pub fn as_mut(&mut self) -> FfiSliceMut<T> { | |
399 | FfiSliceMut { | |
400 | inner: &mut self.slice as *mut _ as *mut (), | |
401 | _type_marker: PhantomData, | |
402 | _abi_marker: PhantomData, | |
403 | } | |
404 | } | |
405 | } | |
406 | ||
407 | impl<'v, T> StackPinnedSlice<'v, T> { | |
408 | /// Creates a stack pinned slice guard from a borrowed slice. | |
409 | /// | |
410 | /// # SAFETY | |
411 | /// This function itself isn't "unsafe" but other code will become unsafe if the `slice` | |
412 | /// becomes invalid or moves. You'd have to violate safety rules somewhere else to do that, | |
413 | /// though. | |
414 | #[inline(always)] | |
415 | pub(crate) unsafe fn from_raw(slice: &'v [T]) -> StackPinnedSlice<'v, T> { | |
416 | Self { | |
417 | _lifetime: PhantomData, | |
418 | slice: SliceBacking::from_raw( | |
419 | u64::try_from(slice.len()).unwrap(), | |
420 | slice.as_ptr() as *const (), | |
421 | ), | |
422 | } | |
423 | } | |
424 | } | |
425 | ||
426 | pub struct StackPinnedValue<'v, T> { | |
427 | value_ref: &'v T, | |
428 | } | |
429 | ||
430 | impl<'v, T> StackPinnedValue<'v, T> { | |
431 | /// Grants a reference to the pinned value. | |
432 | /// | |
433 | /// # SAFETY | |
434 | /// - The granted reference **must not** outlive the lifetime of `&self`. | |
435 | /// - There **must not** be a mutable reference created or mutable dereference performed during the lifetime of the [`FfiValueRef`]. | |
436 | #[inline(always)] | |
437 | pub unsafe fn grant_ref(&self) -> FfiValueRef<T> { | |
438 | Ffi { | |
439 | inner: self.value_ref as *const _ as *const (), | |
440 | _type_marker: PhantomData, | |
441 | _abi_marker: PhantomData, | |
442 | } | |
443 | } | |
444 | } | |
445 | ||
446 | impl<'v, T> StackPinnedValue<'v, T> { | |
447 | #[inline(always)] | |
448 | pub(crate) fn from_raw(value: &'v T) -> Self { | |
449 | Self { value_ref: value } | |
450 | } | |
451 | } | |
452 | ||
453 | pub struct HeapPinnedSlice<'v, T> { | |
454 | _lifetime: PhantomData<&'v T>, | |
455 | slice: SliceBacking<T>, | |
456 | } | |
457 | ||
458 | impl<'v, T> HeapPinnedSlice<'v, T> { | |
459 | /// Creates a pin guard from a heap placed slice. | |
460 | /// | |
461 | /// # SAFETY | |
462 | /// The `slice` **must not** be moved and **must not** have a mutable reference given during the lifetime | |
463 | /// of the returned [`HeapPinnedSlice`] guard. | |
464 | #[inline(always)] | |
465 | pub(crate) unsafe fn from_raw(slice: &'v FfiSlice<T>) -> HeapPinnedSlice<'v, T> { | |
466 | Self { | |
467 | _lifetime: PhantomData, | |
468 | slice: SliceBacking::from_heap(slice), | |
469 | } | |
470 | } | |
471 | ||
472 | pub unsafe fn grant_ref(&self) -> FfiSliceRef<T> { | |
473 | FfiSliceRef { | |
474 | inner: &self.slice as *const _ as *const (), | |
475 | _type_marker: PhantomData, | |
476 | _abi_marker: PhantomData, | |
477 | } | |
478 | } | |
479 | ||
480 | pub unsafe fn grant_mut(&mut self) -> FfiSliceMut<T> { | |
481 | FfiSliceMut { | |
482 | inner: &mut self.slice as *mut _ as *mut (), | |
483 | _type_marker: PhantomData, | |
484 | _abi_marker: PhantomData, | |
485 | } | |
486 | } | |
487 | } | |
488 | ||
489 | #[repr(transparent)] | |
490 | pub struct HeapPinnedValue<'v, T> { | |
491 | value: &'v FfiValue<T>, | |
492 | } | |
493 | ||
494 | impl<'v, T> HeapPinnedValue<'v, T> { | |
495 | #[inline(always)] | |
496 | pub(crate) unsafe fn from_raw(value: &'v FfiValue<T>) -> HeapPinnedValue<'v, T> { | |
497 | Self { value } | |
498 | } | |
499 | ||
500 | #[inline(always)] | |
501 | pub unsafe fn grant_ref(&self) -> FfiValueRef<T> { | |
502 | FfiValueRef { | |
503 | inner: self.value.inner, | |
504 | _type_marker: PhantomData, | |
505 | _abi_marker: PhantomData, | |
506 | } | |
507 | } | |
508 | ||
509 | #[inline(always)] | |
510 | pub unsafe fn grant_mut(&mut self) -> FfiValueMut<T> { | |
511 | FfiValueMut { | |
512 | inner: self.value.inner, | |
513 | _type_marker: PhantomData, | |
514 | _abi_marker: PhantomData, | |
515 | } | |
516 | } | |
517 | } | |
518 | } | |
519 | ||
520 | mod abi_types { | |
521 | pub struct Slice; | |
522 | ||
523 | pub struct SliceRef; | |
524 | ||
525 | pub struct SliceMut; | |
526 | ||
527 | pub struct ValueRef; | |
528 | ||
529 | pub struct ValueMut; | |
530 | ||
531 | pub struct Value; | |
532 | } | |
533 | ||
534 | pub trait StackPinned<'p> { | |
535 | type Pinned: ?Sized + 'p; | |
536 | ||
537 | fn pin(&'p self) -> Self::Pinned; | |
538 | } | |
539 | ||
540 | impl<'p, T: 'p> StackPinned<'p> for [T] { | |
541 | type Pinned = StackPinnedSlice<'p, T>; | |
542 | ||
543 | #[inline(always)] | |
544 | fn pin(&'p self) -> StackPinnedSlice<'p, T> { | |
545 | unsafe { StackPinnedSlice::from_raw(self) } | |
546 | } | |
547 | } | |
548 | ||
549 | impl<'p, T: 'p> StackPinned<'p> for T { | |
550 | type Pinned = StackPinnedValue<'p, T>; | |
551 | ||
552 | #[inline(always)] | |
553 | fn pin(&'p self) -> Self::Pinned { | |
554 | StackPinnedValue::from_raw(self) | |
555 | } | |
556 | } |
giterated-runtime/giterated-abi/src/model_impl/mod.rs
@@ -0,0 +1,104 @@ | ||
1 | use std::ffi::CStr; | |
2 | ||
3 | use giterated_models::{object::GiteratedObject, operation::GiteratedOperation}; | |
4 | ||
5 | use crate::{ | |
6 | result::{FfiError, FfiResult}, | |
7 | value_ex::{FfiValueRefUntyped, FfiValueUntyped}, | |
8 | vtable::{ | |
9 | operation::{IntoOperationVTable, Operation}, | |
10 | IntoObjectVTable, IntoSettingVTable, IntoValueVTable, Object, Setting, | |
11 | }, | |
12 | FfiSlice, FfiSliceRef, FfiValueRef, | |
13 | }; | |
14 | ||
15 | impl<T> IntoObjectVTable for T | |
16 | where | |
17 | T: GiteratedObject, | |
18 | { | |
19 | fn object_kind() -> &'static CStr { | |
20 | todo!() | |
21 | } | |
22 | ||
23 | unsafe extern "C" fn to_str(this: FfiValueRef<Object>) -> FfiSlice<str> { | |
24 | todo!() | |
25 | } | |
26 | ||
27 | unsafe extern "C" fn from_str(from: FfiSliceRef<str>) -> FfiResult<Object, FfiError> { | |
28 | todo!() | |
29 | } | |
30 | ||
31 | unsafe extern "C" fn home_uri(this: FfiValueRef<Object>) -> FfiSlice<str> { | |
32 | todo!() | |
33 | } | |
34 | } | |
35 | ||
36 | impl<T, O> IntoOperationVTable<O> for T | |
37 | where | |
38 | T: GiteratedOperation<O>, | |
39 | O: GiteratedObject, | |
40 | { | |
41 | fn operation_kind() -> &'static CStr { | |
42 | todo!() | |
43 | } | |
44 | ||
45 | unsafe extern "C" fn operation_serialize( | |
46 | this: FfiValueRef<Operation>, | |
47 | ) -> FfiResult<FfiSlice<[u8]>, FfiError> { | |
48 | todo!() | |
49 | } | |
50 | ||
51 | unsafe extern "C" fn operation_deserialize( | |
52 | buffer: FfiSliceRef<[u8]>, | |
53 | ) -> FfiResult<Operation, FfiError> { | |
54 | todo!() | |
55 | } | |
56 | ||
57 | unsafe extern "C" fn success_serialize( | |
58 | success: FfiValueRefUntyped, | |
59 | ) -> FfiResult<FfiSlice<[u8]>, FfiError> { | |
60 | todo!() | |
61 | } | |
62 | ||
63 | unsafe extern "C" fn success_deserialize( | |
64 | buffer: FfiSliceRef<[u8]>, | |
65 | ) -> FfiResult<FfiValueUntyped, FfiError> { | |
66 | todo!() | |
67 | } | |
68 | ||
69 | unsafe extern "C" fn failure_serialize( | |
70 | failure: FfiValueRefUntyped, | |
71 | ) -> FfiResult<FfiSlice<[u8]>, FfiError> { | |
72 | todo!() | |
73 | } | |
74 | ||
75 | unsafe extern "C" fn failure_deserialize( | |
76 | buffer: FfiSliceRef<[u8]>, | |
77 | ) -> FfiResult<FfiValueUntyped, FfiError> { | |
78 | todo!() | |
79 | } | |
80 | } | |
81 | ||
82 | impl<T> IntoValueVTable for T { | |
83 | unsafe extern "C" fn serialize( | |
84 | buffer: FfiSliceRef<[u8]>, | |
85 | ) -> FfiResult<crate::vtable::Value, FfiError> { | |
86 | todo!() | |
87 | } | |
88 | ||
89 | unsafe extern "C" fn deserialize( | |
90 | this: crate::vtable::Value, | |
91 | ) -> FfiResult<FfiSlice<[u8]>, FfiError> { | |
92 | todo!() | |
93 | } | |
94 | } | |
95 | ||
96 | impl<T> IntoSettingVTable for T { | |
97 | unsafe extern "C" fn serialize(this: Setting) -> FfiResult<FfiSlice<[u8]>, FfiError> { | |
98 | todo!() | |
99 | } | |
100 | ||
101 | unsafe extern "C" fn deserialize(buffer: FfiSliceRef<[u8]>) -> FfiResult<Setting, FfiError> { | |
102 | todo!() | |
103 | } | |
104 | } |
giterated-runtime/giterated-abi/src/plugin.rs
@@ -0,0 +1,13 @@ | ||
1 | use dlopen2::wrapper::WrapperApi; | |
2 | ||
3 | use crate::vtable::{ | |
4 | plugin::{Plugin, PluginVTable}, | |
5 | runtime::{RuntimeHandle, RuntimeVTable}, | |
6 | VTable, | |
7 | }; | |
8 | ||
9 | #[derive(WrapperApi)] | |
10 | pub struct GiteratedPluginAbi { | |
11 | __load_runtime_vtable: unsafe extern "C" fn(vtable: &'static VTable<RuntimeHandle>), | |
12 | __get_plugin_vtable: unsafe extern "C" fn() -> &'static VTable<Plugin>, | |
13 | } |
giterated-runtime/giterated-abi/src/result.rs
@@ -0,0 +1,12 @@ | ||
1 | use crate::FfiSlice; | |
2 | ||
3 | #[repr(C)] | |
4 | pub enum FfiResult<T, E> { | |
5 | Ok(T), | |
6 | Err(E), | |
7 | } | |
8 | ||
9 | #[repr(C)] | |
10 | pub struct FfiError { | |
11 | last_error: FfiSlice<str>, | |
12 | } |
giterated-runtime/giterated-abi/src/state.rs
@@ -0,0 +1,136 @@ | ||
1 | use anyhow::Error; | |
2 | ||
3 | pub trait FromOperationState<O, D>: Sized { | |
4 | fn from_operation_state( | |
5 | state: &mut State, | |
6 | object: &O, | |
7 | operation: &D, | |
8 | ) -> Result<Self, OperationError<Error>>; | |
9 | } | |
10 | ||
11 | pub struct StateExtractor<T>(T); | |
12 | ||
13 | impl<T: FromState> FromState for StateExtractor<T> { | |
14 | fn from_state(state: &mut State) -> Result<Self, Error> { | |
15 | todo!() | |
16 | } | |
17 | } | |
18 | ||
19 | use giterated_models::error::OperationError; | |
20 | ||
21 | use crate::{ | |
22 | value_ex::FfiValueUntyped, | |
23 | vtable::{ObjectABI, VTable}, | |
24 | }; | |
25 | ||
26 | #[repr(transparent)] | |
27 | pub struct State { | |
28 | inner: StateHandle, | |
29 | } | |
30 | ||
31 | #[repr(transparent)] | |
32 | struct StateHandle { | |
33 | state: FfiValueUntyped, | |
34 | } | |
35 | ||
36 | #[repr(C)] | |
37 | struct StateItem<T: ?Sized> { | |
38 | /// The pointer to the next item. | |
39 | /// | |
40 | /// `next_item` is most likely always an `FfiValue<StateItem<()>>` and that's how we free them. | |
41 | next_item: *const StateItem<()>, | |
42 | pub state_uuid: u128, | |
43 | pub state: T, | |
44 | } | |
45 | ||
46 | impl Drop for State { | |
47 | fn drop(&mut self) { | |
48 | let state_manager = unsafe { StateManager::new(self) }; | |
49 | ||
50 | for state in state_manager {} | |
51 | } | |
52 | } | |
53 | ||
54 | struct StateManager<'s> { | |
55 | state: &'s mut State, | |
56 | last: Option<StateHandle>, | |
57 | } | |
58 | ||
59 | impl<'s> StateManager<'s> { | |
60 | pub unsafe fn new(handle: &'s mut State) -> Self { | |
61 | todo!() | |
62 | } | |
63 | ||
64 | pub unsafe fn write_state<S: StateUUID>(&mut self, state: S) -> Self { | |
65 | todo!() | |
66 | } | |
67 | ||
68 | pub unsafe fn get_state<S: StateUUID>(&mut self) -> Option<&S> { | |
69 | todo!() | |
70 | } | |
71 | } | |
72 | ||
73 | impl<'s> Iterator for StateManager<'s> { | |
74 | type Item = StateItem<()>; | |
75 | ||
76 | fn next(&mut self) -> Option<StateItem<()>> { | |
77 | todo!() | |
78 | } | |
79 | } | |
80 | ||
81 | pub trait StateUUID { | |
82 | fn uuid() -> u128; | |
83 | ||
84 | fn unsafe_hint_copy() -> Option<bool> { | |
85 | None | |
86 | } | |
87 | } | |
88 | ||
89 | /// State values for the current execution domain. 99.99% of the time this means "plugin-specific" | |
90 | /// | |
91 | /// The remainder 0.01% of the time it refers to the daemon's runtime domain. | |
92 | pub struct DomainState(StateItem<()>); | |
93 | ||
94 | impl StateUUID for DomainState { | |
95 | fn uuid() -> u128 { | |
96 | todo!() | |
97 | } | |
98 | } | |
99 | ||
100 | pub struct RuntimeState(StateItem<&'static VTable<Runtime>>); | |
101 | ||
102 | impl StateUUID for RuntimeState { | |
103 | fn uuid() -> u128 { | |
104 | todo!() | |
105 | } | |
106 | } | |
107 | ||
108 | impl RuntimeState { | |
109 | pub fn queue_insert_state<S: StateUUID>(&mut self, state: S) { | |
110 | todo!() | |
111 | } | |
112 | } | |
113 | ||
114 | pub struct Runtime { | |
115 | pub queue_insert_state: unsafe extern "C" fn(state_uuid: u128, state: FfiValueUntyped), | |
116 | } | |
117 | ||
118 | impl ObjectABI for Runtime { | |
119 | type VTable = Runtime; | |
120 | } | |
121 | ||
122 | pub trait FromState: Sized { | |
123 | fn from_state(state: &mut State) -> Result<Self, Error>; | |
124 | } | |
125 | ||
126 | impl<T: StateUUID> FromState for T { | |
127 | fn from_state(state: &mut State) -> Result<Self, Error> { | |
128 | todo!() | |
129 | } | |
130 | } | |
131 | ||
132 | impl<T: FromState> FromState for Option<T> { | |
133 | fn from_state(state: &mut State) -> Result<Self, Error> { | |
134 | todo!() | |
135 | } | |
136 | } |
giterated-runtime/giterated-abi/src/vtable/mod.rs
@@ -0,0 +1,41 @@ | ||
1 | use std::{marker::PhantomData, mem::transmute, ops::Deref}; | |
2 | ||
3 | pub mod object; | |
4 | pub mod operation; | |
5 | pub mod plugin; | |
6 | pub mod plugin_initialization; | |
7 | pub mod runtime; | |
8 | mod setting; | |
9 | mod value; | |
10 | ||
11 | pub use object::*; | |
12 | pub use setting::*; | |
13 | pub use value::*; | |
14 | ||
15 | pub trait ObjectABI { | |
16 | type VTable; | |
17 | } | |
18 | ||
19 | #[repr(transparent)] | |
20 | pub struct VTable<T: ObjectABI> { | |
21 | _marker: PhantomData<T>, | |
22 | } | |
23 | ||
24 | impl<T: ObjectABI> VTable<T> { | |
25 | /// Creates a new `VTable<T>` reference to a static vtable in memory. | |
26 | /// | |
27 | /// Might be unsafe? It seems like it should be safe... | |
28 | pub const fn new<V>(vtable: &'static V) -> &'static Self { | |
29 | // We're going to transmute the reference to the typed vtable to us | |
30 | // which will probably be fine | |
31 | unsafe { transmute(vtable) } | |
32 | } | |
33 | } | |
34 | ||
35 | impl<T: ObjectABI> Deref for VTable<T> { | |
36 | type Target = T::VTable; | |
37 | ||
38 | fn deref(&self) -> &Self::Target { | |
39 | unsafe { transmute(self) } | |
40 | } | |
41 | } |
giterated-runtime/giterated-abi/src/vtable/object.rs
@@ -0,0 +1,67 @@ | ||
1 | use std::{ffi::CStr, str::FromStr}; | |
2 | ||
3 | use crate::{ | |
4 | result::{FfiError, FfiResult}, | |
5 | value_ex::FfiValueUntyped, | |
6 | FfiSlice, FfiSliceRef, FfiValueRef, | |
7 | }; | |
8 | ||
9 | use super::{ObjectABI, VTable}; | |
10 | ||
11 | #[repr(C)] | |
12 | pub struct Object { | |
13 | inner: FfiValueUntyped, | |
14 | vtable: &'static VTable<Object>, | |
15 | } | |
16 | ||
17 | impl Object { | |
18 | pub fn home_uri(&self) -> String { | |
19 | todo!() | |
20 | } | |
21 | } | |
22 | ||
23 | impl<O: IntoObjectVTable> From<O> for Object { | |
24 | fn from(value: O) -> Self { | |
25 | todo!() | |
26 | } | |
27 | } | |
28 | ||
29 | impl ToString for Object { | |
30 | fn to_string(&self) -> String { | |
31 | todo!() | |
32 | } | |
33 | } | |
34 | ||
35 | impl FromStr for Object { | |
36 | type Err = FfiError; | |
37 | ||
38 | fn from_str(s: &str) -> Result<Self, Self::Err> { | |
39 | todo!() | |
40 | } | |
41 | } | |
42 | ||
43 | impl ObjectABI for Object { | |
44 | type VTable = ObjectVTable; | |
45 | } | |
46 | ||
47 | pub struct ObjectVTable { | |
48 | pub object_kind: &'static CStr, | |
49 | pub to_str: unsafe extern "C" fn(this: FfiValueRef<Object>) -> FfiSlice<str>, | |
50 | pub from_str: unsafe extern "C" fn(from: FfiSliceRef<str>) -> FfiResult<Object, FfiError>, | |
51 | pub home_uri: unsafe extern "C" fn(this: FfiValueRef<Object>) -> FfiSlice<str>, | |
52 | } | |
53 | ||
54 | impl ObjectVTable { | |
55 | pub const fn new<O: IntoObjectVTable>() -> Self { | |
56 | todo!() | |
57 | } | |
58 | } | |
59 | ||
60 | pub trait IntoObjectVTable: Sized { | |
61 | const VTABLE: &'static VTable<Object> = VTable::new(&ObjectVTable::new::<Self>()); | |
62 | ||
63 | fn object_kind() -> &'static CStr; | |
64 | unsafe extern "C" fn to_str(this: FfiValueRef<Object>) -> FfiSlice<str>; | |
65 | unsafe extern "C" fn from_str(from: FfiSliceRef<str>) -> FfiResult<Object, FfiError>; | |
66 | unsafe extern "C" fn home_uri(this: FfiValueRef<Object>) -> FfiSlice<str>; | |
67 | } |
giterated-runtime/giterated-abi/src/vtable/operation.rs
@@ -0,0 +1,103 @@ | ||
1 | use std::ffi::CStr; | |
2 | ||
3 | use crate::{ | |
4 | result::{FfiError, FfiResult}, | |
5 | value_ex::{FfiValueRefUntyped, FfiValueUntyped}, | |
6 | FfiSlice, FfiSliceRef, FfiValue, FfiValueRef, | |
7 | }; | |
8 | ||
9 | use super::{ObjectABI, VTable}; | |
10 | ||
11 | #[repr(C)] | |
12 | pub struct Operation { | |
13 | inner: FfiValueUntyped, | |
14 | vtable: &'static VTable<Operation>, | |
15 | } | |
16 | ||
17 | impl Operation { | |
18 | pub fn operation_kind(&self) -> &'static str { | |
19 | todo!() | |
20 | } | |
21 | ||
22 | pub fn serialize(&self) -> Vec<u8> { | |
23 | todo!() | |
24 | } | |
25 | ||
26 | pub fn deserialize_from<O, T: IntoOperationVTable<O>>(buffer: &[u8]) -> Result<Self, FfiError> { | |
27 | todo!() | |
28 | } | |
29 | } | |
30 | ||
31 | impl ObjectABI for Operation { | |
32 | type VTable = OperationVTable; | |
33 | } | |
34 | ||
35 | pub struct OperationVTable { | |
36 | pub operation_kind: &'static CStr, | |
37 | pub operation_serialize: | |
38 | unsafe extern "C" fn(this: FfiValueRef<Operation>) -> FfiResult<FfiSlice<[u8]>, FfiError>, | |
39 | pub operation_deserialize: | |
40 | unsafe extern "C" fn(buffer: FfiSliceRef<[u8]>) -> FfiResult<Operation, FfiError>, | |
41 | pub success_serialize: | |
42 | unsafe extern "C" fn(success: FfiValueRefUntyped) -> FfiResult<FfiSlice<[u8]>, FfiError>, | |
43 | pub success_deserialize: | |
44 | unsafe extern "C" fn(buffer: FfiSliceRef<[u8]>) -> FfiResult<FfiValueUntyped, FfiError>, | |
45 | pub failure_serialize: | |
46 | unsafe extern "C" fn(failure: FfiValueRefUntyped) -> FfiResult<FfiSlice<[u8]>, FfiError>, | |
47 | pub failure_deserialize: | |
48 | unsafe extern "C" fn(buffer: FfiSliceRef<[u8]>) -> FfiResult<FfiValueUntyped, FfiError>, | |
49 | } | |
50 | ||
51 | impl OperationVTable { | |
52 | pub const fn new<O, T: IntoOperationVTable<O>>() -> Self { | |
53 | todo!() | |
54 | } | |
55 | ||
56 | pub fn serialize(&self, operation: Operation) -> Result<Vec<u8>, FfiError> { | |
57 | todo!() | |
58 | } | |
59 | ||
60 | pub fn deserialize(&self, buffer: &[u8]) -> Result<Operation, FfiError> { | |
61 | todo!() | |
62 | } | |
63 | ||
64 | pub fn serialize_success(&self, success: FfiValueUntyped) -> Result<Vec<u8>, FfiError> { | |
65 | todo!() | |
66 | } | |
67 | ||
68 | pub fn deserialize_success(&self, buffer: &[u8]) -> Result<FfiValueUntyped, FfiError> { | |
69 | todo!() | |
70 | } | |
71 | ||
72 | pub fn serialize_failure(&self, failure: FfiValueUntyped) -> Result<Vec<u8>, FfiError> { | |
73 | todo!() | |
74 | } | |
75 | ||
76 | pub fn deserialize_failure(&self, buffer: &[u8]) -> Result<FfiValueUntyped, FfiError> { | |
77 | todo!() | |
78 | } | |
79 | } | |
80 | ||
81 | pub trait IntoOperationVTable<O>: Sized { | |
82 | const VTABLE: &'static VTable<Operation> = VTable::new(&OperationVTable::new::<O, Self>()); | |
83 | ||
84 | fn operation_kind() -> &'static CStr; | |
85 | unsafe extern "C" fn operation_serialize( | |
86 | this: FfiValueRef<Operation>, | |
87 | ) -> FfiResult<FfiSlice<[u8]>, FfiError>; | |
88 | unsafe extern "C" fn operation_deserialize( | |
89 | buffer: FfiSliceRef<[u8]>, | |
90 | ) -> FfiResult<Operation, FfiError>; | |
91 | unsafe extern "C" fn success_serialize( | |
92 | success: FfiValueRefUntyped, | |
93 | ) -> FfiResult<FfiSlice<[u8]>, FfiError>; | |
94 | unsafe extern "C" fn success_deserialize( | |
95 | buffer: FfiSliceRef<[u8]>, | |
96 | ) -> FfiResult<FfiValueUntyped, FfiError>; | |
97 | unsafe extern "C" fn failure_serialize( | |
98 | failure: FfiValueRefUntyped, | |
99 | ) -> FfiResult<FfiSlice<[u8]>, FfiError>; | |
100 | unsafe extern "C" fn failure_deserialize( | |
101 | buffer: FfiSliceRef<[u8]>, | |
102 | ) -> FfiResult<FfiValueUntyped, FfiError>; | |
103 | } |
giterated-runtime/giterated-abi/src/vtable/plugin.rs
@@ -0,0 +1,25 @@ | ||
1 | use crate::{value_ex::FfiValueRefUntyped, FfiSliceRef, FfiValueRef}; | |
2 | ||
3 | use super::ObjectABI; | |
4 | ||
5 | pub struct Plugin {} | |
6 | ||
7 | impl ObjectABI for Plugin { | |
8 | type VTable = PluginVTable; | |
9 | } | |
10 | ||
11 | pub struct PluginVTable { | |
12 | pub plugin_name: unsafe extern "C" fn() -> FfiSliceRef<str>, | |
13 | pub plugin_version: unsafe extern "C" fn() -> FfiSliceRef<str>, | |
14 | pub type_metadata: unsafe extern "C" fn() -> FfiValueRefUntyped, | |
15 | } | |
16 | ||
17 | impl PluginVTable { | |
18 | pub fn plugin_name(&self) -> &'static str { | |
19 | todo!() | |
20 | } | |
21 | ||
22 | pub fn type_metadata(&self) -> FfiValueRefUntyped { | |
23 | todo!() | |
24 | } | |
25 | } |
giterated-runtime/giterated-abi/src/vtable/plugin_initialization.rs
@@ -0,0 +1,65 @@ | ||
1 | use crate::{ | |
2 | callback::{ | |
3 | operation::OperationHandlerCallback, setting::SettingGetterCallback, | |
4 | value::ValueGetterCallback, CallbackPtr, | |
5 | }, | |
6 | FfiValueMut, | |
7 | }; | |
8 | ||
9 | use super::{operation::Operation, Object, ObjectABI, Setting, VTable, Value}; | |
10 | ||
11 | #[repr(C)] | |
12 | pub struct HostVTable {} | |
13 | ||
14 | #[repr(C)] | |
15 | #[derive(Clone, Copy)] | |
16 | pub struct InitializationVTable { | |
17 | pub register_object: | |
18 | unsafe extern "C" fn(state: FfiValueMut<()>, &'static str, &'static VTable<Object>), | |
19 | pub register_operation: unsafe extern "C" fn( | |
20 | state: FfiValueMut<()>, | |
21 | &'static str, | |
22 | &'static str, | |
23 | &'static VTable<Operation>, | |
24 | ), | |
25 | pub register_setting: unsafe extern "C" fn( | |
26 | state: FfiValueMut<()>, | |
27 | &'static str, | |
28 | &'static str, | |
29 | &'static VTable<Setting>, | |
30 | ), | |
31 | pub register_value: unsafe extern "C" fn( | |
32 | state: FfiValueMut<()>, | |
33 | &'static str, | |
34 | &'static str, | |
35 | &'static VTable<Value>, | |
36 | ), | |
37 | ||
38 | pub operation_handler: unsafe extern "C" fn( | |
39 | state: FfiValueMut<()>, | |
40 | &'static str, | |
41 | &'static str, | |
42 | CallbackPtr<OperationHandlerCallback>, | |
43 | ), | |
44 | ||
45 | pub value_getter: unsafe extern "C" fn( | |
46 | state: FfiValueMut<()>, | |
47 | &'static str, | |
48 | &'static str, | |
49 | CallbackPtr<ValueGetterCallback>, | |
50 | ), | |
51 | ||
52 | pub setting_getter: unsafe extern "C" fn( | |
53 | state: FfiValueMut<()>, | |
54 | &'static str, | |
55 | &'static str, | |
56 | CallbackPtr<SettingGetterCallback>, | |
57 | ), | |
58 | } | |
59 | ||
60 | impl ObjectABI for InitializationVTable { | |
61 | type VTable = InitializationVTable; | |
62 | } | |
63 | ||
64 | unsafe impl Sync for InitializationVTable {} | |
65 | unsafe impl Send for InitializationVTable {} |
giterated-runtime/giterated-abi/src/vtable/runtime.rs
@@ -0,0 +1,52 @@ | ||
1 | use giterated_models::{error::OperationError, object::ObjectRequestError}; | |
2 | ||
3 | use crate::{ | |
4 | result::{FfiError, FfiResult}, | |
5 | state::State, | |
6 | value_ex::FfiValueUntyped, | |
7 | FfiFuture, FfiSliceRef, FfiValueMut, | |
8 | }; | |
9 | ||
10 | use super::{Object, ObjectABI}; | |
11 | ||
12 | pub struct RuntimeHandle; | |
13 | ||
14 | impl ObjectABI for RuntimeHandle { | |
15 | type VTable = RuntimeVTable; | |
16 | } | |
17 | ||
18 | #[derive(Clone, Copy)] | |
19 | pub struct RuntimeVTable { | |
20 | pub(crate) handle_fn: unsafe extern "C" fn( | |
21 | FfiSliceRef<str>, | |
22 | FfiSliceRef<str>, | |
23 | FfiSliceRef<str>, | |
24 | FfiSliceRef<[u8]>, | |
25 | FfiSliceRef<[u8]>, | |
26 | ) -> FfiFuture< | |
27 | FfiResult<FfiValueUntyped, OperationError<FfiError>>, | |
28 | >, | |
29 | pub(crate) get_object: | |
30 | unsafe extern "C" fn( | |
31 | &str, | |
32 | state: FfiValueMut<State>, | |
33 | ) -> FfiResult<Object, OperationError<ObjectRequestError>>, | |
34 | } | |
35 | ||
36 | unsafe impl Send for RuntimeVTable {} | |
37 | unsafe impl Sync for RuntimeVTable {} | |
38 | ||
39 | pub trait IntoRuntimeVtable { | |
40 | unsafe extern "C" fn handle( | |
41 | object_kind: FfiSliceRef<str>, | |
42 | operation_name: FfiSliceRef<str>, | |
43 | object: FfiSliceRef<str>, | |
44 | operation_payload: FfiSliceRef<[u8]>, | |
45 | operation_state: FfiSliceRef<[u8]>, | |
46 | ) -> FfiFuture<FfiResult<FfiValueUntyped, OperationError<FfiError>>>; | |
47 | ||
48 | unsafe extern "C" fn get_object( | |
49 | object_str: &str, | |
50 | operation_state: FfiValueMut<State>, | |
51 | ) -> FfiResult<Object, OperationError<ObjectRequestError>>; | |
52 | } |
giterated-runtime/giterated-abi/src/vtable/setting.rs
@@ -0,0 +1,53 @@ | ||
1 | use crate::{ | |
2 | result::{FfiError, FfiResult}, | |
3 | value_ex::FfiValueUntyped, | |
4 | FfiSlice, FfiSliceRef, | |
5 | }; | |
6 | ||
7 | use super::{ObjectABI, VTable}; | |
8 | ||
9 | #[repr(C)] | |
10 | pub struct Setting { | |
11 | inner: FfiValueUntyped, | |
12 | vtable: &'static VTable<Setting>, | |
13 | } | |
14 | ||
15 | // impl<T: IntoSettingVTable> From<T> for Setting { | |
16 | // fn from(value: T) -> Self { | |
17 | // todo!() | |
18 | // } | |
19 | // } | |
20 | ||
21 | impl ObjectABI for Setting { | |
22 | type VTable = SettingVTable; | |
23 | } | |
24 | ||
25 | pub struct SettingVTable { | |
26 | pub serialize: unsafe extern "C" fn(this: Setting) -> FfiResult<FfiSlice<[u8]>, FfiError>, | |
27 | pub deserialize: | |
28 | unsafe extern "C" fn(buffer: FfiSliceRef<[u8]>) -> FfiResult<Setting, FfiError>, | |
29 | } | |
30 | ||
31 | impl SettingVTable { | |
32 | pub const fn new<S: IntoSettingVTable>() -> Self { | |
33 | Self { | |
34 | serialize: S::serialize, | |
35 | deserialize: S::deserialize, | |
36 | } | |
37 | } | |
38 | ||
39 | pub fn serialize(&self, setting: Setting) -> Result<Vec<u8>, FfiError> { | |
40 | todo!() | |
41 | } | |
42 | ||
43 | pub fn deserialize(&self, buffer: &[u8]) -> Result<Setting, FfiError> { | |
44 | todo!() | |
45 | } | |
46 | } | |
47 | ||
48 | pub trait IntoSettingVTable: Sized { | |
49 | const VTABLE: &'static VTable<Setting> = VTable::new(&SettingVTable::new::<Self>()); | |
50 | ||
51 | unsafe extern "C" fn serialize(this: Setting) -> FfiResult<FfiSlice<[u8]>, FfiError>; | |
52 | unsafe extern "C" fn deserialize(buffer: FfiSliceRef<[u8]>) -> FfiResult<Setting, FfiError>; | |
53 | } |
giterated-runtime/giterated-abi/src/vtable/value.rs
@@ -0,0 +1,52 @@ | ||
1 | use crate::{ | |
2 | result::{FfiError, FfiResult}, | |
3 | value_ex::FfiValueUntyped, | |
4 | FfiSlice, FfiSliceRef, | |
5 | }; | |
6 | ||
7 | use super::{ObjectABI, VTable}; | |
8 | ||
9 | #[repr(C)] | |
10 | pub struct Value { | |
11 | inner: FfiValueUntyped, | |
12 | vtable: &'static VTable<Value>, | |
13 | } | |
14 | ||
15 | impl Value { | |
16 | pub fn from<T: IntoValueVTable>(value: T) -> Self { | |
17 | todo!() | |
18 | } | |
19 | } | |
20 | ||
21 | impl ObjectABI for Value { | |
22 | type VTable = ValueVTable; | |
23 | } | |
24 | ||
25 | pub struct ValueVTable { | |
26 | pub serialize: unsafe extern "C" fn(buffer: FfiSliceRef<[u8]>) -> FfiResult<Value, FfiError>, | |
27 | pub deserialize: unsafe extern "C" fn(this: Value) -> FfiResult<FfiSlice<[u8]>, FfiError>, | |
28 | } | |
29 | ||
30 | impl ValueVTable { | |
31 | pub const fn new<V: IntoValueVTable>() -> Self { | |
32 | Self { | |
33 | serialize: V::serialize, | |
34 | deserialize: V::deserialize, | |
35 | } | |
36 | } | |
37 | ||
38 | pub fn serialize(&self, value: &Value) -> Result<Vec<u8>, FfiError> { | |
39 | todo!() | |
40 | } | |
41 | ||
42 | pub fn deserialize(&self, buffer: &[u8]) -> Result<Value, FfiError> { | |
43 | todo!() | |
44 | } | |
45 | } | |
46 | ||
47 | pub trait IntoValueVTable: Sized { | |
48 | const VTABLE: &'static VTable<Value> = VTable::new(&ValueVTable::new::<Self>()); | |
49 | ||
50 | unsafe extern "C" fn serialize(buffer: FfiSliceRef<[u8]>) -> FfiResult<Value, FfiError>; | |
51 | unsafe extern "C" fn deserialize(this: Value) -> FfiResult<FfiSlice<[u8]>, FfiError>; | |
52 | } |
giterated-runtime/src/lib.rs
@@ -1,50 +1,55 @@ | ||
1 | mod operation_walker; | |
2 | pub mod plugin; | |
3 | ||
4 | use std::{collections::HashMap, sync::Arc}; | |
5 | ||
6 | use dlopen2::wrapper::Container; | |
7 | use giterated_abi::{ | |
8 | callback::{ | |
9 | operation::OperationHandlerCallback, | |
10 | setting::{SettingChangeCallback, SettingGetterCallback}, | |
11 | value::{ValueChangeCallback, ValueGetterCallback}, | |
12 | CallbackPtr, | |
13 | }, | |
14 | plugin::GiteratedPluginAbi, | |
15 | vtable::{ | |
16 | operation::Operation, plugin::Plugin, runtime::RuntimeHandle, Object, Setting, VTable, | |
17 | Value, | |
18 | }, | |
19 | }; | |
20 | use giterated_core::types::TypeMetadata; | |
21 | use giterated_models::{ | |
22 | error::OperationError, | |
23 | object::{GiteratedObject, ObjectOperationPair}, | |
24 | operation::GiteratedOperation, | |
25 | settings::ObjectSettingPair, | |
26 | value::ObjectValuePair, | |
27 | }; | |
28 | use operation_walker::OperationHandlerRules; | |
29 | use plugin::initialization::PluginInitializationState; | |
30 | use tracing::{debug, debug_span, trace, trace_span, warn}; | |
31 | ||
1 | 32 | pub struct Runtime { |
2 | plugins: Vec<PluginHandle>, | |
33 | plugins: Vec<RuntimePlugin>, | |
3 | 34 | handlers: RuntimeHandlers, |
4 | 35 | } |
5 | 36 | |
6 | pub struct FfiRuntimeMetadata { | |
7 | runtime: PluginState, | |
37 | #[derive(Clone)] | |
38 | pub struct RuntimePlugin { | |
39 | vtable: &'static VTable<Plugin>, | |
40 | library: Arc<Container<GiteratedPluginAbi>>, | |
8 | 41 | } |
9 | 42 | |
10 | impl IntoRuntimeVtable for Runtime { | |
11 | unsafe extern "C" fn handle( | |
12 | _this: PluginState, | |
13 | _object_kind: FfiSliceRef<str>, | |
14 | _operation_name: FfiSliceRef<str>, | |
15 | _object: FfiSliceRef<str>, | |
16 | _operation_payload: FfiSliceRef<[u8]>, | |
17 | _operation_state: FfiSliceRef<[u8]>, | |
18 | ) -> RuntimeFuture<FfiResult<FfiValueUntyped, OperationError<giterated_abi::result::FfiError>>> | |
19 | { | |
43 | impl RuntimePlugin { | |
44 | pub fn name(&self) -> &'static str { | |
20 | 45 | todo!() |
21 | 46 | } |
22 | 47 | |
23 | unsafe extern "C" fn get_object( | |
24 | this: PluginState, | |
25 | object_str: &str, | |
26 | operation_state: *mut OperationState, | |
27 | ) -> FfiResult<Object, OperationError<giterated_models::object::ObjectRequestError>> { | |
28 | let runtime_state = unsafe { RuntimeState::from_static() }; | |
29 | ||
30 | let type_metada = runtime_state | |
31 | .vtable | |
32 | .type_metadata | |
33 | .as_ref() | |
34 | .unwrap_or_else(|| { | |
35 | let runtime = this.transmute_ref::<Runtime>(); | |
36 | ||
37 | &runtime.plugins.first().unwrap().domain_metadata | |
38 | }); | |
39 | ||
40 | // for (object_type, object_vtable) in &type_metada.objects { | |
41 | // if let Ok(object) = (object_vtable.from_str)(object_str) { | |
42 | // return Ok(object); | |
43 | // } | |
44 | // } | |
45 | ||
46 | // Err(OperationError::Operation(ObjectRequestError::Invalid)) | |
48 | pub fn version(&self) -> &'static str { | |
49 | todo!() | |
50 | } | |
47 | 51 | |
52 | pub fn metadata(&self) -> &TypeMetadata { | |
48 | 53 | todo!() |
49 | 54 | } |
50 | 55 | } |
@@ -57,23 +62,16 @@ impl Runtime { | ||
57 | 62 | }) |
58 | 63 | } |
59 | 64 | |
60 | pub fn state(self: &Box<Self>) -> RuntimeState { | |
61 | RuntimeState { | |
62 | vtable: RuntimeVTable { | |
63 | runtime: PluginState::from(self), | |
64 | handle_fn: <Runtime as IntoRuntimeVtable>::handle, | |
65 | get_object: <Runtime as IntoRuntimeVtable>::get_object, | |
66 | type_metadata: unsafe { TypeMetadata::from_static() }, | |
67 | }, | |
68 | } | |
65 | pub fn state(self: &Box<Self>) -> RuntimeHandle { | |
66 | RuntimeHandle | |
69 | 67 | } |
70 | 68 | |
71 | 69 | pub fn insert_plugin( |
72 | 70 | &mut self, |
73 | mut plugin: PluginHandle, | |
71 | mut plugin: RuntimePlugin, | |
74 | 72 | mut initialization: PluginInitializationState, |
75 | 73 | ) { |
76 | let _guard = debug_span!("inserting plugin", meta = debug(&plugin.meta)).entered(); | |
74 | let _guard = debug_span!("inserting plugin", meta = debug(&plugin.name())).entered(); | |
77 | 75 | |
78 | 76 | for (pair, callback) in initialization.operation_handlers.drain() { |
79 | 77 | let _guard = |
@@ -127,7 +125,8 @@ impl Runtime { | ||
127 | 125 | } |
128 | 126 | |
129 | 127 | pub fn init(self: Box<Self>) { |
130 | unsafe { giterated_static_runtime::initialize_runtime(Box::into_raw(self).cast::<()>()) } | |
128 | todo!() | |
129 | // unsafe { giterated_static_runtime::initialize_runtime(Box::into_raw(self).cast::<()>()) } | |
131 | 130 | } |
132 | 131 | |
133 | 132 | // pub async fn handle( |
@@ -154,6 +153,99 @@ impl Runtime { | ||
154 | 153 | } |
155 | 154 | } |
156 | 155 | |
156 | pub struct RuntimeDomain { | |
157 | plugin: RuntimePlugin, | |
158 | } | |
159 | ||
160 | impl RuntimeDomain { | |
161 | pub fn from_plugin(plugin: &RuntimePlugin) -> Self { | |
162 | Self { | |
163 | plugin: plugin.clone(), | |
164 | } | |
165 | } | |
166 | ||
167 | pub fn object_vtable(&self, object_kind: &str) -> Option<&'static VTable<Object>> { | |
168 | self.plugin.metadata().objects.get(object_kind).copied() | |
169 | } | |
170 | ||
171 | pub fn operation_vtable( | |
172 | &self, | |
173 | object_kind: &str, | |
174 | operation_name: &str, | |
175 | ) -> Option<&'static VTable<Operation>> { | |
176 | self.plugin | |
177 | .metadata() | |
178 | .operations | |
179 | .get(&ObjectOperationPair::new(object_kind, operation_name)) | |
180 | .copied() | |
181 | } | |
182 | ||
183 | pub fn setting_vtable( | |
184 | &self, | |
185 | object_kind: &str, | |
186 | setting_name: &str, | |
187 | ) -> Option<&'static VTable<Setting>> { | |
188 | self.plugin | |
189 | .metadata() | |
190 | .settings | |
191 | .get(&ObjectSettingPair::new(object_kind, setting_name)) | |
192 | .copied() | |
193 | } | |
194 | ||
195 | pub fn value_vtable( | |
196 | &self, | |
197 | object_kind: &str, | |
198 | value_name: &str, | |
199 | ) -> Option<&'static VTable<Value>> { | |
200 | self.plugin | |
201 | .metadata() | |
202 | .values | |
203 | .get(&ObjectValuePair::new(object_kind, value_name)) | |
204 | .copied() | |
205 | } | |
206 | } | |
207 | ||
208 | #[repr(C)] | |
209 | pub struct FFIPluginMeta { | |
210 | pub name: *const u8, | |
211 | pub name_len: usize, | |
212 | pub version: *const u8, | |
213 | pub version_len: usize, | |
214 | } | |
215 | ||
216 | // pub struct RuntimePlugin { | |
217 | // handle: PluginHandle, | |
218 | // type_metadata: Arc<TypeMetadata>, | |
219 | // } | |
220 | ||
221 | impl RuntimePlugin { | |
222 | // pub fn plugin_meta(&self) -> PluginMeta { | |
223 | // let meta = unsafe { self.handle.raw.plugin_meta() }; | |
224 | ||
225 | // let name = unsafe { std::slice::from_raw_parts(meta.name, meta.name_len) }; | |
226 | // let version = unsafe { std::slice::from_raw_parts(meta.version, meta.version_len) }; | |
227 | ||
228 | // let name = std::str::from_utf8(name).unwrap(); | |
229 | // let version = std::str::from_utf8(version).unwrap(); | |
230 | ||
231 | // PluginMeta { | |
232 | // name: String::from(name), | |
233 | // version: Version::parse(version).unwrap(), | |
234 | // } | |
235 | // } | |
236 | } | |
237 | ||
238 | // #[derive(WrapperApi)] | |
239 | // pub struct GiteratedPluginApi { | |
240 | // plugin_meta: unsafe extern "C" fn() -> FFIPluginMeta, | |
241 | // load_host_vtable: unsafe extern "C" fn(vtable: &HostVTable), | |
242 | // load_initialization_vtable: unsafe extern "C" fn(vtable: &'static VTable<Initialization>), | |
243 | // initialize: unsafe extern "C" fn(runtime_state: *const RuntimeState) -> PluginState, | |
244 | // initialize_registration: unsafe extern "C" fn( | |
245 | // init_state: *mut PluginInitializationState, | |
246 | // ) -> *mut PluginInitializationState, | |
247 | // load_type_metadata: unsafe extern "C" fn(metadata: *mut ()), | |
248 | // } | |
157 | 249 | #[derive(Default)] |
158 | 250 | pub struct RuntimeHandlers { |
159 | 251 | operation_handlers: HashMap< |
@@ -288,107 +380,3 @@ impl RuntimeHandlers { | ||
288 | 380 | OperationHandlerRules::new(object_kind, operation_name, self) |
289 | 381 | } |
290 | 382 | } |
291 | ||
292 | pub struct RuntimeDomain { | |
293 | plugin: PluginHandle, | |
294 | } | |
295 | ||
296 | impl RuntimeDomain { | |
297 | pub fn from_plugin(plugin: &PluginHandle) -> Self { | |
298 | Self { | |
299 | plugin: plugin.clone(), | |
300 | } | |
301 | } | |
302 | ||
303 | pub fn object_vtable(&self, object_kind: &str) -> Option<&'static VTable<Object>> { | |
304 | self.plugin | |
305 | .domain_metadata | |
306 | .objects | |
307 | .get(object_kind) | |
308 | .copied() | |
309 | } | |
310 | ||
311 | pub fn operation_vtable( | |
312 | &self, | |
313 | object_kind: &str, | |
314 | operation_name: &str, | |
315 | ) -> Option<&'static VTable<Operation>> { | |
316 | self.plugin | |
317 | .domain_metadata | |
318 | .operations | |
319 | .get(&ObjectOperationPair::new(object_kind, operation_name)) | |
320 | .copied() | |
321 | } | |
322 | ||
323 | pub fn setting_vtable( | |
324 | &self, | |
325 | object_kind: &str, | |
326 | setting_name: &str, | |
327 | ) -> Option<&'static VTable<Setting>> { | |
328 | self.plugin | |
329 | .domain_metadata | |
330 | .settings | |
331 | .get(&ObjectSettingPair::new(object_kind, setting_name)) | |
332 | .copied() | |
333 | } | |
334 | ||
335 | pub fn value_vtable( | |
336 | &self, | |
337 | object_kind: &str, | |
338 | value_name: &str, | |
339 | ) -> Option<&'static VTable<Value>> { | |
340 | self.plugin | |
341 | .domain_metadata | |
342 | .values | |
343 | .get(&ObjectValuePair::new(object_kind, value_name)) | |
344 | .copied() | |
345 | } | |
346 | } | |
347 | ||
348 | #[derive(Clone, Debug)] | |
349 | pub struct PluginMeta { | |
350 | pub name: String, | |
351 | pub version: Version, | |
352 | } | |
353 | ||
354 | #[repr(C)] | |
355 | pub struct FFIPluginMeta { | |
356 | pub name: *const u8, | |
357 | pub name_len: usize, | |
358 | pub version: *const u8, | |
359 | pub version_len: usize, | |
360 | } | |
361 | ||
362 | pub struct RuntimePlugin { | |
363 | handle: PluginHandle, | |
364 | type_metadata: Arc<TypeMetadata>, | |
365 | } | |
366 | ||
367 | impl RuntimePlugin { | |
368 | pub fn plugin_meta(&self) -> PluginMeta { | |
369 | let meta = unsafe { self.handle.raw.plugin_meta() }; | |
370 | ||
371 | let name = unsafe { std::slice::from_raw_parts(meta.name, meta.name_len) }; | |
372 | let version = unsafe { std::slice::from_raw_parts(meta.version, meta.version_len) }; | |
373 | ||
374 | let name = std::str::from_utf8(name).unwrap(); | |
375 | let version = std::str::from_utf8(version).unwrap(); | |
376 | ||
377 | PluginMeta { | |
378 | name: String::from(name), | |
379 | version: Version::parse(version).unwrap(), | |
380 | } | |
381 | } | |
382 | } | |
383 | ||
384 | #[derive(WrapperApi)] | |
385 | pub struct GiteratedPluginApi { | |
386 | plugin_meta: unsafe extern "C" fn() -> FFIPluginMeta, | |
387 | load_host_vtable: unsafe extern "C" fn(vtable: &HostVTable), | |
388 | load_initialization_vtable: unsafe extern "C" fn(vtable: &'static VTable<Initialization>), | |
389 | initialize: unsafe extern "C" fn(runtime_state: *const RuntimeState) -> PluginState, | |
390 | initialize_registration: unsafe extern "C" fn( | |
391 | init_state: *mut PluginInitializationState, | |
392 | ) -> *mut PluginInitializationState, | |
393 | load_type_metadata: unsafe extern "C" fn(metadata: *mut ()), | |
394 | } |
giterated-runtime/src/operation_walker.rs
@@ -0,0 +1,270 @@ | ||
1 | use giterated_abi::{result::FfiError, value_ex::FfiValueUntyped, vtable::runtime::RuntimeHandle}; | |
2 | use giterated_models::{ | |
3 | error::OperationError, object::ObjectOperationPair, operation::GiteratedOperation, | |
4 | settings::GetSetting, value::GetValue, | |
5 | }; | |
6 | ||
7 | use tracing::{debug_span, trace, trace_span}; | |
8 | ||
9 | use super::{ObjectSettingPair, ObjectValuePair, RuntimeHandlers}; | |
10 | ||
11 | /// A wrapper for operation handling that enforces handling rules. | |
12 | /// | |
13 | /// # Handler Resolution | |
14 | /// In order, handler resolution will be attempted as follows: | |
15 | /// | |
16 | /// | Index | object_kind | operation_kind | Special Case? | | |
17 | /// |-------|-------------|-----------------|---------------| | |
18 | /// | 1 | `any` | `typed` | No | | |
19 | /// | 2 | `typed` | `any` | No | | |
20 | /// | 3 | `any` | `any` | No | | |
21 | /// | 4 | `any` | `GetValue` | ⚠️ Yes ⚠️ | | |
22 | /// | 5 | `any` | `GetSetting` | ⚠️ Yes ⚠️ | | |
23 | /// | 6 | `any` | `SetSetting` | ⚠️ Yes ⚠️ | | |
24 | /// | 7 | `any` | `ObjectRequest` | ⚠️ Yes ⚠️ | | |
25 | /// | 8 | `typed` | `typed` | No | | |
26 | pub struct OperationHandlerRules<'a> { | |
27 | object_kind: &'a str, | |
28 | operation_name: &'a str, | |
29 | handlers: &'a RuntimeHandlers, | |
30 | } | |
31 | ||
32 | impl<'o> OperationHandlerRules<'o> { | |
33 | pub fn new( | |
34 | object_kind: &'o str, | |
35 | operation_name: &'o str, | |
36 | handlers: &'o RuntimeHandlers, | |
37 | ) -> Self { | |
38 | Self { | |
39 | object_kind, | |
40 | operation_name, | |
41 | handlers, | |
42 | } | |
43 | } | |
44 | ||
45 | pub async fn handle( | |
46 | &self, | |
47 | runtime: &RuntimeHandle, | |
48 | object: &str, | |
49 | operation_payload: &[u8], | |
50 | ) -> Result<FfiValueUntyped, OperationError<FfiError>> { | |
51 | // object_kind: `any` | |
52 | // operation_kind: `typed` | |
53 | if let Some(_handler) = self | |
54 | .handlers | |
55 | .operation_handlers | |
56 | .get(&ObjectOperationPair::new("any", self.operation_name)) | |
57 | { | |
58 | todo!() | |
59 | } | |
60 | ||
61 | // object_kind: `typed` | |
62 | // operation_kind: `any` | |
63 | if let Some(_handler) = self | |
64 | .handlers | |
65 | .operation_handlers | |
66 | .get(&ObjectOperationPair::new(self.object_kind, "any")) | |
67 | {} | |
68 | ||
69 | // object_kind: `any` | |
70 | // operation_kind: `any` | |
71 | if let Some(_handler) = self | |
72 | .handlers | |
73 | .operation_handlers | |
74 | .get(&ObjectOperationPair::new("any", "any")) | |
75 | {} | |
76 | ||
77 | // ⚠️ Special Case ⚠️ | |
78 | // object_kind: `any` | |
79 | // operation_kind: `GetValue` | |
80 | if self.operation_name == "get_value" { | |
81 | let operation: GetValue = todo!(); | |
82 | let _guard = trace_span!( | |
83 | "get_value handler resolving", | |
84 | object = self.object_kind, | |
85 | value = operation.value_name | |
86 | ) | |
87 | .entered(); | |
88 | ||
89 | if let Some((domain, callback)) = self.handlers.value_getters.get( | |
90 | &ObjectValuePair::new(self.object_kind, &operation.value_name), | |
91 | ) { | |
92 | trace_span!( | |
93 | "get_value handler.", | |
94 | object = self.object_kind, | |
95 | value_name = operation.value_name | |
96 | ); | |
97 | ||
98 | let object_vtable = domain | |
99 | .object_vtable(self.object_kind) | |
100 | .ok_or_else(|| OperationError::Unhandled)?; | |
101 | trace!("Resolved object vtable for {}", self.object_kind); | |
102 | ||
103 | let _value_vtable = domain | |
104 | .value_vtable(self.object_kind, &operation.value_name) | |
105 | .ok_or_else(|| OperationError::Unhandled)?; | |
106 | trace!( | |
107 | "Resolved value vtable for {}::{}", | |
108 | self.object_kind, | |
109 | operation.value_name | |
110 | ); | |
111 | ||
112 | // let object = unsafe { (object_vtable.from_str)(object) } | |
113 | // .map_err(|_| OperationError::Internal(anyhow::anyhow!("yikes!")))?; | |
114 | ||
115 | // let _guard = debug_span!("get_value handler"); | |
116 | ||
117 | // let result = unsafe { | |
118 | // (callback.func)( | |
119 | // callback.callback_ptr, | |
120 | // runtime_state, | |
121 | // &domain.plugin.state, | |
122 | // object, | |
123 | // ) | |
124 | // } | |
125 | // .await; | |
126 | ||
127 | // match result { | |
128 | // Ok(value) => return Ok(value.into()), | |
129 | // Err(_err) => todo!(), | |
130 | // } | |
131 | ||
132 | todo!() | |
133 | } else { | |
134 | trace!("Failed to resolve handler."); | |
135 | } | |
136 | } | |
137 | ||
138 | // ⚠️ Special Case ⚠️ | |
139 | // object_kind: `any` | |
140 | // operation_kind: `GetSetting` | |
141 | if self.operation_name == "get_setting" { | |
142 | let operation: GetSetting = todo!(); | |
143 | let _guard = trace_span!( | |
144 | "get_setting handler resolving", | |
145 | object = self.object_kind, | |
146 | setting = operation.setting_name | |
147 | ) | |
148 | .entered(); | |
149 | ||
150 | if let Some((domain, callback)) = self.handlers.setting_getters.get( | |
151 | &ObjectSettingPair::new(self.object_kind, &operation.setting_name), | |
152 | ) { | |
153 | trace_span!( | |
154 | "get_setting handler.", | |
155 | object = self.object_kind, | |
156 | setting_name = operation.setting_name | |
157 | ); | |
158 | ||
159 | let object_vtable = domain | |
160 | .object_vtable(self.object_kind) | |
161 | .ok_or_else(|| OperationError::Unhandled)?; | |
162 | trace!("Resolved object vtable for {}", self.object_kind); | |
163 | ||
164 | let _setting_vtable = domain | |
165 | .setting_vtable(self.object_kind, &operation.setting_name) | |
166 | .ok_or_else(|| OperationError::Unhandled)?; | |
167 | trace!("Resolved setting vtable for {}", operation.setting_name); | |
168 | ||
169 | todo!() | |
170 | ||
171 | // let object = unsafe { (object_vtable.from_str)(object) } | |
172 | // .map_err(|_| OperationError::Internal(anyhow::anyhow!("yikes!")))?; | |
173 | ||
174 | // let _guard = debug_span!("get_value handler"); | |
175 | ||
176 | // let result = unsafe { | |
177 | // (callback.func())( | |
178 | // callback.callback_ptr, | |
179 | // runtime_state, | |
180 | // &domain.plugin.state, | |
181 | // object, | |
182 | // ) | |
183 | // } | |
184 | // .await; | |
185 | ||
186 | // match result { | |
187 | // Ok(value) => { | |
188 | // let vtable = unsafe { (value.vtable.get_setting_vtable)() }; | |
189 | // let return_value: Value = serde_json::from_slice(unsafe { | |
190 | // (value.vtable.serialize)(value).unwrap().as_ref() | |
191 | // }) | |
192 | // .unwrap(); | |
193 | ||
194 | // todo!() | |
195 | ||
196 | // // return Ok(unsafe { | |
197 | // // AnySuccess::from_raw( | |
198 | // // FFIBox::from_box(Box::new(return_value)).untyped(), | |
199 | // // vtable, | |
200 | // // ) | |
201 | // // }); | |
202 | // } | |
203 | // Err(_err) => todo!(), | |
204 | // } | |
205 | } else { | |
206 | trace!("Failed to resolve handler."); | |
207 | } | |
208 | } | |
209 | ||
210 | // ⚠️ Special Case ⚠️ | |
211 | // object_kind: `any` | |
212 | // operation_kind: `SetSetting` | |
213 | self.operation_name == "set_setting"; | |
214 | ||
215 | // ⚠️ Special Case ⚠️ | |
216 | // object_kind: `any` | |
217 | // operation_kind: `ObjectRequest` | |
218 | self.operation_name == "object_request"; | |
219 | ||
220 | // object_kind: `typed` | |
221 | // operation_kind: `typed` | |
222 | if let Some((domain, handler)) = | |
223 | self.handlers | |
224 | .operation_handlers | |
225 | .get(&ObjectOperationPair::new( | |
226 | self.object_kind, | |
227 | self.operation_name, | |
228 | )) | |
229 | { | |
230 | let _guard = trace_span!("typed_typed handler resolved").entered(); | |
231 | ||
232 | let object_vtable = domain | |
233 | .object_vtable(self.object_kind) | |
234 | .ok_or_else(|| OperationError::Unhandled)?; | |
235 | trace!("Resolved object vtable for {}", self.object_kind); | |
236 | ||
237 | let operation_vtable = domain | |
238 | .operation_vtable(self.object_kind, self.operation_name) | |
239 | .ok_or_else(|| OperationError::Unhandled)?; | |
240 | trace!( | |
241 | "Resolved operation vtable for {}::{}", | |
242 | self.object_kind, | |
243 | self.operation_name | |
244 | ); | |
245 | ||
246 | todo!() | |
247 | ||
248 | // let object = unsafe { (object_vtable.from_str)(object) } | |
249 | // .map_err(|_| OperationError::Internal(anyhow::anyhow!("yikes!")))?; | |
250 | // let operation = unsafe { (operation_vtable.deserialize)(operation_payload) } | |
251 | // .map_err(|_| OperationError::Internal(anyhow::anyhow!("yikes!")))?; | |
252 | // trace!("Parsed operation data"); | |
253 | ||
254 | // let _guard = debug_span!("calling handler").entered(); | |
255 | // let result = unsafe { | |
256 | // (handler.func)( | |
257 | // handler.callback_ptr, | |
258 | // runtime_state, | |
259 | // &domain.plugin.state, | |
260 | // object, | |
261 | // operation, | |
262 | // ) | |
263 | // }; | |
264 | ||
265 | // return result.await; | |
266 | } | |
267 | ||
268 | Err(OperationError::Unhandled) | |
269 | } | |
270 | } |
giterated-runtime/src/plugin/initialization.rs
@@ -0,0 +1,19 @@ | ||
1 | use std::collections::HashMap; | |
2 | ||
3 | use giterated_abi::callback::{ | |
4 | operation::OperationHandlerCallback, setting::SettingGetterCallback, | |
5 | value::ValueGetterCallback, CallbackPtr, | |
6 | }; | |
7 | use giterated_core::types::TypeMetadata; | |
8 | use giterated_models::{ | |
9 | object::ObjectOperationPair, settings::ObjectSettingPair, value::ObjectValuePair, | |
10 | }; | |
11 | ||
12 | #[derive(Default)] | |
13 | pub struct PluginInitializationState { | |
14 | pub type_metadata: TypeMetadata, | |
15 | pub operation_handlers: | |
16 | HashMap<ObjectOperationPair<'static>, CallbackPtr<OperationHandlerCallback>>, | |
17 | pub value_getters: HashMap<ObjectValuePair<'static>, CallbackPtr<ValueGetterCallback>>, | |
18 | pub setting_getters: HashMap<ObjectSettingPair<'static>, CallbackPtr<SettingGetterCallback>>, | |
19 | } |
giterated-runtime/src/plugin/mod.rs
@@ -0,0 +1 @@ | ||
1 | pub mod initialization; |
plugins/example-plugin/Cargo.toml
@@ -15,7 +15,7 @@ giterated-plugin = { path = "../../giterated-plugin" } | ||
15 | 15 | giterated-static-runtime = { path = "../../giterated-runtime/giterated-static-runtime" } |
16 | 16 | dlopen2 = "0.6" |
17 | 17 | tracing-subscriber = "0.3" |
18 | giterated-models = { path = "../../giterated-models" } | |
18 | giterated-models = { path = "../../giterated-core/giterated-models" } | |
19 | 19 | tracing = "0.1" |
20 | 20 | serde_json = "1.0" |
21 | 21 | anyhow = "1" |
plugins/example-plugin/src/lib.rs
@@ -33,13 +33,9 @@ impl StateUUID for PluginState { | ||
33 | 33 | /// build the plugin's stack. |
34 | 34 | #[plugin::init] |
35 | 35 | pub fn init(builder: &mut PluginStackBuilder) -> Result<(), Error> { |
36 | builder | |
37 | .insert_state(PluginState); | |
38 | builder | |
39 | .object::<Instance>() | |
40 | .object::<User>(); | |
41 | builder | |
42 | .setting_getter(setting_getter); | |
36 | builder.insert_state(PluginState); | |
37 | builder.object::<Instance>().object::<User>(); | |
38 | builder.setting_getter(setting_getter); | |
43 | 39 | |
44 | 40 | Ok(()) |
45 | 41 | } |
plugins/giterated-backend/Cargo.toml
@@ -7,7 +7,7 @@ edition = "2021" | ||
7 | 7 | |
8 | 8 | [dependencies] |
9 | 9 | giterated-plugin = { path = "../../giterated-plugin" } |
10 | giterated-models = { path = "../../giterated-models" } | |
10 | giterated-models = { path = "../../giterated-core/giterated-models" } | |
11 | 11 | serde = { version = "1.0", features = [ "derive" ]} |
12 | 12 | anyhow = "1" |
13 | 13 | thiserror = "1" |
plugins/giterated-issues/Cargo.toml
@@ -12,7 +12,7 @@ crate-type = ["rlib", "dylib"] | ||
12 | 12 | |
13 | 13 | [dependencies] |
14 | 14 | giterated-plugin = { path = "../../giterated-plugin" } |
15 | giterated-models = { path = "../../giterated-models" } | |
15 | giterated-models = { path = "../../giterated-core/giterated-models" } | |
16 | 16 | serde = { version = "1.0", features = [ "derive" ]} |
17 | 17 | anyhow = "1" |
18 | 18 | thiserror = "1" |
plugins/giterated-protocol/Cargo.toml
@@ -12,7 +12,7 @@ crate-type = ["dylib", "rlib"] | ||
12 | 12 | |
13 | 13 | [dependencies] |
14 | 14 | giterated-plugin = { path = "../../giterated-plugin" } |
15 | giterated-models = { path = "../../giterated-models" } | |
15 | giterated-models = { path = "../../giterated-core/giterated-models" } | |
16 | 16 | serde = { version = "1.0", features = [ "derive" ]} |
17 | 17 | anyhow = "1" |
18 | 18 | toml = { version = "0.8" } |