Base protocol refactor complete
parent: tbd commit: 079d544
Showing 32 changed files with 649 insertions and 653 deletions
Cargo.lock
@@ -350,6 +350,41 @@ dependencies = [ | ||
350 | 350 | ] |
351 | 351 | |
352 | 352 | [[package]] |
353 | name = "darling" | |
354 | version = "0.20.3" | |
355 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
356 | checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e" | |
357 | dependencies = [ | |
358 | "darling_core", | |
359 | "darling_macro", | |
360 | ] | |
361 | ||
362 | [[package]] | |
363 | name = "darling_core" | |
364 | version = "0.20.3" | |
365 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
366 | checksum = "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621" | |
367 | dependencies = [ | |
368 | "fnv", | |
369 | "ident_case", | |
370 | "proc-macro2", | |
371 | "quote", | |
372 | "strsim", | |
373 | "syn 2.0.29", | |
374 | ] | |
375 | ||
376 | [[package]] | |
377 | name = "darling_macro" | |
378 | version = "0.20.3" | |
379 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
380 | checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" | |
381 | dependencies = [ | |
382 | "darling_core", | |
383 | "quote", | |
384 | "syn 2.0.29", | |
385 | ] | |
386 | ||
387 | [[package]] | |
353 | 388 | name = "data-encoding" |
354 | 389 | version = "2.4.0" |
355 | 390 | source = "registry+https://github.com/rust-lang/crates.io-index" |
@@ -390,6 +425,9 @@ name = "deranged" | ||
390 | 425 | version = "0.3.8" |
391 | 426 | source = "registry+https://github.com/rust-lang/crates.io-index" |
392 | 427 | checksum = "f2696e8a945f658fd14dc3b87242e6b80cd0f36ff04ea560fa39082368847946" |
428 | dependencies = [ | |
429 | "serde", | |
430 | ] | |
393 | 431 | |
394 | 432 | [[package]] |
395 | 433 | name = "digest" |
@@ -729,6 +767,7 @@ dependencies = [ | ||
729 | 767 | "semver", |
730 | 768 | "serde", |
731 | 769 | "serde_json", |
770 | "serde_with", | |
732 | 771 | "sqlx", |
733 | 772 | "thiserror", |
734 | 773 | "toml", |
@@ -922,6 +961,12 @@ dependencies = [ | ||
922 | 961 | ] |
923 | 962 | |
924 | 963 | [[package]] |
964 | name = "ident_case" | |
965 | version = "1.0.1" | |
966 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
967 | checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" | |
968 | ||
969 | [[package]] | |
925 | 970 | name = "idna" |
926 | 971 | version = "0.4.0" |
927 | 972 | source = "registry+https://github.com/rust-lang/crates.io-index" |
@@ -939,6 +984,7 @@ checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" | ||
939 | 984 | dependencies = [ |
940 | 985 | "autocfg", |
941 | 986 | "hashbrown 0.12.3", |
987 | "serde", | |
942 | 988 | ] |
943 | 989 | |
944 | 990 | [[package]] |
@@ -949,6 +995,7 @@ checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" | ||
949 | 995 | dependencies = [ |
950 | 996 | "equivalent", |
951 | 997 | "hashbrown 0.14.0", |
998 | "serde", | |
952 | 999 | ] |
953 | 1000 | |
954 | 1001 | [[package]] |
@@ -1799,6 +1846,35 @@ dependencies = [ | ||
1799 | 1846 | ] |
1800 | 1847 | |
1801 | 1848 | [[package]] |
1849 | name = "serde_with" | |
1850 | version = "3.3.0" | |
1851 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1852 | checksum = "1ca3b16a3d82c4088f343b7480a93550b3eabe1a358569c2dfe38bbcead07237" | |
1853 | dependencies = [ | |
1854 | "base64 0.21.3", | |
1855 | "chrono", | |
1856 | "hex", | |
1857 | "indexmap 1.9.3", | |
1858 | "indexmap 2.0.0", | |
1859 | "serde", | |
1860 | "serde_json", | |
1861 | "serde_with_macros", | |
1862 | "time 0.3.28", | |
1863 | ] | |
1864 | ||
1865 | [[package]] | |
1866 | name = "serde_with_macros" | |
1867 | version = "3.3.0" | |
1868 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1869 | checksum = "2e6be15c453eb305019bfa438b1593c731f36a289a7853f7707ee29e870b3b3c" | |
1870 | dependencies = [ | |
1871 | "darling", | |
1872 | "proc-macro2", | |
1873 | "quote", | |
1874 | "syn 2.0.29", | |
1875 | ] | |
1876 | ||
1877 | [[package]] | |
1802 | 1878 | name = "sha1" |
1803 | 1879 | version = "0.10.5" |
1804 | 1880 | source = "registry+https://github.com/rust-lang/crates.io-index" |
@@ -2142,6 +2218,12 @@ dependencies = [ | ||
2142 | 2218 | ] |
2143 | 2219 | |
2144 | 2220 | [[package]] |
2221 | name = "strsim" | |
2222 | version = "0.10.0" | |
2223 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
2224 | checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" | |
2225 | ||
2226 | [[package]] | |
2145 | 2227 | name = "subtle" |
2146 | 2228 | version = "2.5.0" |
2147 | 2229 | source = "registry+https://github.com/rust-lang/crates.io-index" |
giterated-daemon/migrations/20230913232601_repository_metadata.sql
@@ -0,0 +1,8 @@ | ||
1 | CREATE TABLE IF NOT EXISTS repository_metadata | |
2 | ( | |
3 | repository TEXT NOT NULL, | |
4 | name TEXT NOT NULL, | |
5 | value TEXT NOT NULL | |
6 | ); | |
7 | ||
8 | CREATE UNIQUE INDEX unique_per_repository_metadata ON repository_metadata (repository, name); | |
8 | \ No newline at end of file |
giterated-daemon/migrations/20230913232625_user_metadata.sql
@@ -0,0 +1,17 @@ | ||
1 | CREATE TABLE IF NOT EXISTS user_metadata | |
2 | ( | |
3 | username TEXT NOT NULL, | |
4 | name TEXT NOT NULL, | |
5 | value TEXT NOT NULL | |
6 | ); | |
7 | ||
8 | CREATE UNIQUE INDEX unique_per_user_metadata ON user_metadata (username, name); | |
9 | ||
10 | CREATE TABLE IF NOT EXISTS user_settings | |
11 | ( | |
12 | username TEXT NOT NULL, | |
13 | name TEXT NOT NULL, | |
14 | value TEXT NOT NULL | |
15 | ); | |
16 | ||
17 | CREATE UNIQUE INDEX unique_per_user_settings ON user_settings (username, name); | |
17 | \ No newline at end of file |
giterated-daemon/src/backend/git.rs
@@ -9,7 +9,8 @@ use giterated_models::{ | ||
9 | 9 | Commit, IssueLabel, Repository, RepositoryIssue, RepositorySummary, |
10 | 10 | RepositoryTreeEntry, RepositoryVisibility, |
11 | 11 | }, |
12 | user::User, | |
12 | settings::{AnySetting, RepositoryDescription, RepositoryVisibilitySetting, Setting}, | |
13 | user::{User, UserParseError}, | |
13 | 14 | }, |
14 | 15 | operation::{ |
15 | 16 | instance::RepositoryCreateRequest, |
@@ -18,12 +19,18 @@ use giterated_models::{ | ||
18 | 19 | RepositoryIssuesCountRequest, RepositoryIssuesRequest, |
19 | 20 | }, |
20 | 21 | }, |
22 | values::AnyValue, | |
21 | 23 | }; |
24 | use serde_json::Value; | |
22 | 25 | use sqlx::{Either, PgPool}; |
23 | use std::path::{Path, PathBuf}; | |
26 | use std::{ | |
27 | path::{Path, PathBuf}, | |
28 | sync::Arc, | |
29 | }; | |
24 | 30 | use thiserror::Error; |
31 | use tokio::sync::Mutex; | |
25 | 32 | |
26 | use super::{IssuesBackend, RepositoryBackend}; | |
33 | use super::{IssuesBackend, MetadataBackend, RepositoryBackend}; | |
27 | 34 | |
28 | 35 | // TODO: Handle this |
29 | 36 | //region database structures |
@@ -98,6 +105,7 @@ pub struct GitBackend { | ||
98 | 105 | pub pg_pool: PgPool, |
99 | 106 | pub repository_folder: String, |
100 | 107 | pub instance: Instance, |
108 | pub settings_provider: Arc<Mutex<dyn MetadataBackend + Send>>, | |
101 | 109 | } |
102 | 110 | |
103 | 111 | impl GitBackend { |
@@ -105,11 +113,13 @@ impl GitBackend { | ||
105 | 113 | pg_pool: &PgPool, |
106 | 114 | repository_folder: &str, |
107 | 115 | instance: impl ToOwned<Owned = Instance>, |
116 | settings_provider: Arc<Mutex<dyn MetadataBackend + Send>>, | |
108 | 117 | ) -> Self { |
109 | 118 | Self { |
110 | 119 | pg_pool: pg_pool.clone(), |
111 | 120 | repository_folder: repository_folder.to_string(), |
112 | 121 | instance: instance.to_owned(), |
122 | settings_provider, | |
113 | 123 | } |
114 | 124 | } |
115 | 125 | |
@@ -286,6 +296,52 @@ impl RepositoryBackend for GitBackend { | ||
286 | 296 | } |
287 | 297 | } |
288 | 298 | |
299 | async fn get_value( | |
300 | &mut self, | |
301 | repository: &Repository, | |
302 | name: &str, | |
303 | ) -> Result<AnyValue<Repository>, Error> { | |
304 | Ok(match name { | |
305 | "description" => unsafe { | |
306 | AnyValue::from_raw( | |
307 | self.get_setting(repository, RepositoryDescription::name()) | |
308 | .await? | |
309 | .0, | |
310 | ) | |
311 | }, | |
312 | "visibility" => unsafe { | |
313 | AnyValue::from_raw( | |
314 | self.get_setting(repository, RepositoryVisibilitySetting::name()) | |
315 | .await? | |
316 | .0, | |
317 | ) | |
318 | }, | |
319 | _ => { | |
320 | return Err(UserParseError.into()); | |
321 | } | |
322 | }) | |
323 | } | |
324 | async fn get_setting( | |
325 | &mut self, | |
326 | repository: &Repository, | |
327 | name: &str, | |
328 | ) -> Result<AnySetting, Error> { | |
329 | let mut provider = self.settings_provider.lock().await; | |
330 | ||
331 | Ok(provider.repository_get(repository, name).await?) | |
332 | } | |
333 | async fn write_setting( | |
334 | &mut self, | |
335 | repository: &Repository, | |
336 | name: &str, | |
337 | setting: &Value, | |
338 | ) -> Result<(), Error> { | |
339 | let mut provider = self.settings_provider.lock().await; | |
340 | ||
341 | provider | |
342 | .repository_write(repository, name, AnySetting(setting.clone())) | |
343 | .await | |
344 | } | |
289 | 345 | // async fn repository_info( |
290 | 346 | // &mut self, |
291 | 347 | // requester: Option<&User>, |
@@ -519,3 +575,10 @@ impl IssuesBackend for GitBackend { | ||
519 | 575 | todo!() |
520 | 576 | } |
521 | 577 | } |
578 | ||
579 | #[derive(Debug, sqlx::FromRow)] | |
580 | struct RepositoryMetadata { | |
581 | pub repository: String, | |
582 | pub name: String, | |
583 | pub value: String, | |
584 | } |
giterated-daemon/src/backend/mod.rs
@@ -46,6 +46,18 @@ pub trait RepositoryBackend { | ||
46 | 46 | requester: Option<&User>, |
47 | 47 | user: &User, |
48 | 48 | ) -> Result<Vec<RepositorySummary>, Error>; |
49 | async fn get_value( | |
50 | &mut self, | |
51 | user: &Repository, | |
52 | name: &str, | |
53 | ) -> Result<AnyValue<Repository>, Error>; | |
54 | async fn get_setting(&mut self, user: &Repository, name: &str) -> Result<AnySetting, Error>; | |
55 | async fn write_setting( | |
56 | &mut self, | |
57 | user: &Repository, | |
58 | name: &str, | |
59 | setting: &Value, | |
60 | ) -> Result<(), Error>; | |
49 | 61 | async fn exists(&mut self, repository: &Repository) -> Result<bool, Error>; |
50 | 62 | } |
51 | 63 | |
@@ -95,18 +107,23 @@ pub trait UserBackend: AuthBackend { | ||
95 | 107 | } |
96 | 108 | |
97 | 109 | #[async_trait::async_trait] |
98 | pub trait SettingsBackend: Send + Sync { | |
99 | async fn user_get(&mut self, user: &User) -> Result<Vec<(String, Value)>, Error>; | |
100 | async fn user_write(&mut self, user: &User, settings: &[(String, String)]) | |
101 | -> Result<(), Error>; | |
102 | ||
110 | pub trait MetadataBackend { | |
111 | async fn user_get(&mut self, user: &User, name: &str) -> Result<AnySetting, Error>; | |
112 | async fn user_write( | |
113 | &mut self, | |
114 | user: &User, | |
115 | name: &str, | |
116 | setting: AnySetting, | |
117 | ) -> Result<(), Error>; | |
103 | 118 | async fn repository_get( |
104 | 119 | &mut self, |
105 | 120 | repository: &Repository, |
106 | ) -> Result<Vec<(String, Value)>, Error>; | |
121 | name: &str, | |
122 | ) -> Result<AnySetting, Error>; | |
107 | 123 | async fn repository_write( |
108 | 124 | &mut self, |
109 | 125 | repository: &Repository, |
110 | settings: &[(String, String)], | |
126 | name: &str, | |
127 | setting: AnySetting, | |
111 | 128 | ) -> Result<(), Error>; |
112 | 129 | } |
giterated-daemon/src/backend/settings.rs
@@ -1,98 +1,84 @@ | ||
1 | 1 | use anyhow::Error; |
2 | use futures_util::StreamExt; | |
3 | use giterated_models::model::{repository::Repository, user::User}; | |
4 | use serde_json::Value; | |
5 | use sqlx::{Either, PgPool}; | |
6 | 2 | |
7 | use super::SettingsBackend; | |
3 | use giterated_models::model::{repository::Repository, settings::AnySetting, user::User}; | |
4 | ||
5 | use sqlx::PgPool; | |
6 | ||
7 | use super::MetadataBackend; | |
8 | 8 | |
9 | 9 | pub struct DatabaseSettings { |
10 | 10 | pub pg_pool: PgPool, |
11 | 11 | } |
12 | 12 | |
13 | 13 | #[async_trait::async_trait] |
14 | impl SettingsBackend for DatabaseSettings { | |
15 | async fn user_get(&mut self, user: &User) -> Result<Vec<(String, Value)>, Error> { | |
16 | let settings = sqlx::query_as!( | |
17 | UserSettingRow, | |
18 | r#"SELECT * FROM user_settings WHERE username = $1"#, | |
19 | user.username | |
20 | ) | |
21 | .fetch_many(&self.pg_pool) | |
22 | .filter_map(|result| async move { | |
23 | if let Ok(Either::Right(row)) = result { | |
24 | Some(row) | |
25 | } else { | |
26 | None | |
27 | } | |
28 | }) | |
29 | .filter_map(|row| async move { | |
30 | if let Ok(value) = serde_json::from_str(&row.value) { | |
31 | Some((row.name, value)) | |
32 | } else { | |
33 | None | |
34 | } | |
35 | }) | |
36 | .collect::<Vec<_>>() | |
37 | .await; | |
38 | ||
39 | Ok(settings) | |
14 | impl MetadataBackend for DatabaseSettings { | |
15 | async fn user_get(&mut self, _user: &User, _name: &str) -> Result<AnySetting, Error> { | |
16 | todo!() | |
40 | 17 | } |
41 | 18 | async fn user_write( |
42 | 19 | &mut self, |
43 | user: &User, | |
44 | settings: &[(String, String)], | |
20 | _user: &User, | |
21 | _name: &str, | |
22 | _value: AnySetting, | |
45 | 23 | ) -> Result<(), Error> { |
46 | for (name, value) in settings { | |
47 | sqlx::query!("INSERT INTO user_settings VALUES ($1, $2, $3) ON CONFLICT (username, name) DO UPDATE SET value = $3", | |
48 | user.username, name, value) | |
49 | .execute(&self.pg_pool).await?; | |
50 | } | |
24 | // for (name, value) in settings { | |
25 | // sqlx::query!("INSERT INTO user_settings VALUES ($1, $2, $3) ON CONFLICT (username, name) DO UPDATE SET value = $3", | |
26 | // user.username, name, value) | |
27 | // .execute(&self.pg_pool).await?; | |
28 | // } | |
51 | 29 | |
52 | Ok(()) | |
30 | // Ok(()) | |
31 | ||
32 | todo!() | |
53 | 33 | } |
54 | 34 | |
55 | 35 | async fn repository_get( |
56 | 36 | &mut self, |
57 | repository: &Repository, | |
58 | ) -> Result<Vec<(String, Value)>, Error> { | |
59 | let settings = sqlx::query_as!( | |
60 | RepositorySettingRow, | |
61 | r#"SELECT * FROM repository_settings WHERE repository = $1"#, | |
62 | repository.to_string() | |
63 | ) | |
64 | .fetch_many(&self.pg_pool) | |
65 | .filter_map(|result| async move { | |
66 | if let Ok(Either::Right(row)) = result { | |
67 | Some(row) | |
68 | } else { | |
69 | None | |
70 | } | |
71 | }) | |
72 | .filter_map(|row| async move { | |
73 | if let Ok(value) = serde_json::from_str(&row.value) { | |
74 | Some((row.name, value)) | |
75 | } else { | |
76 | None | |
77 | } | |
78 | }) | |
79 | .collect::<Vec<_>>() | |
80 | .await; | |
37 | _repository: &Repository, | |
38 | _name: &str, | |
39 | ) -> Result<AnySetting, Error> { | |
40 | // let settings = sqlx::query_as!( | |
41 | // RepositorySettingRow, | |
42 | // r#"SELECT * FROM repository_settings WHERE repository = $1"#, | |
43 | // repository.to_string() | |
44 | // ) | |
45 | // .fetch_many(&self.pg_pool) | |
46 | // .filter_map(|result| async move { | |
47 | // if let Ok(Either::Right(row)) = result { | |
48 | // Some(row) | |
49 | // } else { | |
50 | // None | |
51 | // } | |
52 | // }) | |
53 | // .filter_map(|row| async move { | |
54 | // if let Ok(value) = serde_json::from_str(&row.value) { | |
55 | // Some((row.name, value)) | |
56 | // } else { | |
57 | // None | |
58 | // } | |
59 | // }) | |
60 | // .collect::<Vec<_>>() | |
61 | // .await; | |
62 | ||
63 | // Ok(settings) | |
81 | 64 | |
82 | Ok(settings) | |
65 | todo!() | |
83 | 66 | } |
84 | 67 | async fn repository_write( |
85 | 68 | &mut self, |
86 | repository: &Repository, | |
87 | settings: &[(String, String)], | |
69 | _repository: &Repository, | |
70 | _name: &str, | |
71 | _value: AnySetting, | |
88 | 72 | ) -> Result<(), Error> { |
89 | for (name, value) in settings { | |
90 | sqlx::query!("INSERT INTO repository_settings VALUES ($1, $2, $3) ON CONFLICT (repository, name) DO UPDATE SET value = $3", | |
91 | repository.to_string(), name, value) | |
92 | .execute(&self.pg_pool).await?; | |
93 | } | |
73 | // for (name, value) in settings { | |
74 | // sqlx::query!("INSERT INTO repository_settings VALUES ($1, $2, $3) ON CONFLICT (repository, name) DO UPDATE SET value = $3", | |
75 | // repository.to_string(), name, value) | |
76 | // .execute(&self.pg_pool).await?; | |
77 | // } | |
78 | ||
79 | // Ok(()) | |
94 | 80 | |
95 | Ok(()) | |
81 | todo!() | |
96 | 82 | } |
97 | 83 | } |
98 | 84 |
giterated-daemon/src/backend/user.rs
@@ -7,8 +7,10 @@ use argon2::{password_hash::SaltString, Argon2, PasswordHash, PasswordHasher, Pa | ||
7 | 7 | use base64::{engine::general_purpose::STANDARD, Engine as _}; |
8 | 8 | use giterated_models::{ |
9 | 9 | model::{ |
10 | authenticated::UserAuthenticationToken, instance::Instance, settings::AnySetting, | |
11 | user::User, | |
10 | authenticated::UserAuthenticationToken, | |
11 | instance::Instance, | |
12 | settings::{AnySetting, Setting, UserBio, UserDisplayName}, | |
13 | user::{User, UserParseError}, | |
12 | 14 | }, |
13 | 15 | operation::instance::{AuthenticationTokenRequest, RegisterAccountRequest}, |
14 | 16 | values::AnyValue, |
@@ -26,13 +28,13 @@ use tokio::sync::Mutex; | ||
26 | 28 | |
27 | 29 | use crate::authentication::AuthenticationTokenGranter; |
28 | 30 | |
29 | use super::{AuthBackend, SettingsBackend, UserBackend}; | |
31 | use super::{AuthBackend, MetadataBackend, UserBackend}; | |
30 | 32 | |
31 | 33 | pub struct UserAuth { |
32 | 34 | pub pg_pool: PgPool, |
33 | 35 | pub this_instance: Instance, |
34 | 36 | pub auth_granter: Arc<Mutex<AuthenticationTokenGranter>>, |
35 | pub settings_provider: Arc<Mutex<dyn SettingsBackend>>, | |
37 | pub settings_provider: Arc<Mutex<dyn MetadataBackend + Send>>, | |
36 | 38 | } |
37 | 39 | |
38 | 40 | impl UserAuth { |
@@ -40,7 +42,7 @@ impl UserAuth { | ||
40 | 42 | pool: PgPool, |
41 | 43 | this_instance: &Instance, |
42 | 44 | granter: Arc<Mutex<AuthenticationTokenGranter>>, |
43 | settings_provider: Arc<Mutex<dyn SettingsBackend>>, | |
45 | settings_provider: Arc<Mutex<dyn MetadataBackend + Send>>, | |
44 | 46 | ) -> Self { |
45 | 47 | Self { |
46 | 48 | pg_pool: pool, |
@@ -53,20 +55,38 @@ impl UserAuth { | ||
53 | 55 | |
54 | 56 | #[async_trait::async_trait] |
55 | 57 | impl UserBackend for UserAuth { |
56 | async fn get_value(&mut self, _user: &User, _name: &str) -> Result<AnyValue<User>, Error> { | |
57 | todo!() | |
58 | async fn get_value(&mut self, user: &User, name: &str) -> Result<AnyValue<User>, Error> { | |
59 | Ok(match name { | |
60 | "display_name" => unsafe { | |
61 | AnyValue::from_raw(self.get_setting(user, UserDisplayName::name()).await?.0) | |
62 | }, | |
63 | "bio" => unsafe { | |
64 | AnyValue::from_raw(self.get_setting(user, UserBio::name()).await?.0) | |
65 | }, | |
66 | _ => { | |
67 | return Err(UserParseError.into()); | |
68 | } | |
69 | }) | |
58 | 70 | } |
59 | async fn get_setting(&mut self, _user: &User, _name: &str) -> Result<AnySetting, Error> { | |
60 | todo!() | |
71 | async fn get_setting(&mut self, user: &User, name: &str) -> Result<AnySetting, Error> { | |
72 | let mut provider = self.settings_provider.lock().await; | |
73 | ||
74 | Ok(provider.user_get(user, name).await?) | |
61 | 75 | } |
76 | ||
62 | 77 | async fn write_setting( |
63 | 78 | &mut self, |
64 | _user: &User, | |
65 | _name: &str, | |
66 | _setting: &Value, | |
79 | user: &User, | |
80 | name: &str, | |
81 | setting: &Value, | |
67 | 82 | ) -> Result<(), Error> { |
68 | todo!() | |
83 | let mut provider = self.settings_provider.lock().await; | |
84 | ||
85 | provider | |
86 | .user_write(user, name, AnySetting(setting.clone())) | |
87 | .await | |
69 | 88 | } |
89 | ||
70 | 90 | async fn exists(&mut self, user: &User) -> Result<bool, Error> { |
71 | 91 | Ok(sqlx::query_as!( |
72 | 92 | UserRow, |
@@ -210,6 +230,13 @@ struct UserRow { | ||
210 | 230 | pub enc_private_key: Vec<u8>, |
211 | 231 | } |
212 | 232 | |
233 | #[derive(Debug, sqlx::FromRow)] | |
234 | struct UserValue { | |
235 | pub username: String, | |
236 | pub name: String, | |
237 | pub value: String, | |
238 | } | |
239 | ||
213 | 240 | #[derive(Debug, thiserror::Error)] |
214 | 241 | pub enum AuthenticationError { |
215 | 242 | #[error("invalid password")] |
giterated-daemon/src/connection/wrapper.rs
@@ -18,7 +18,7 @@ use toml::Table; | ||
18 | 18 | |
19 | 19 | use crate::{ |
20 | 20 | authentication::AuthenticationTokenGranter, |
21 | backend::{RepositoryBackend, SettingsBackend, UserBackend}, | |
21 | backend::{MetadataBackend, RepositoryBackend, UserBackend}, | |
22 | 22 | database_backend::Foobackend, |
23 | 23 | federation::connections::InstanceConnections, |
24 | 24 | keys::PublicKeyCache, |
@@ -32,7 +32,7 @@ pub async fn connection_wrapper( | ||
32 | 32 | repository_backend: Arc<Mutex<dyn RepositoryBackend + Send>>, |
33 | 33 | user_backend: Arc<Mutex<dyn UserBackend + Send>>, |
34 | 34 | auth_granter: Arc<Mutex<AuthenticationTokenGranter>>, |
35 | settings_backend: Arc<Mutex<dyn SettingsBackend>>, | |
35 | settings_backend: Arc<Mutex<dyn MetadataBackend + Send>>, | |
36 | 36 | addr: SocketAddr, |
37 | 37 | instance: impl ToOwned<Owned = Instance>, |
38 | 38 | instance_connections: Arc<Mutex<InstanceConnections>>, |
@@ -210,7 +210,7 @@ pub struct ConnectionState { | ||
210 | 210 | pub repository_backend: Arc<Mutex<dyn RepositoryBackend + Send>>, |
211 | 211 | pub user_backend: Arc<Mutex<dyn UserBackend + Send>>, |
212 | 212 | pub auth_granter: Arc<Mutex<AuthenticationTokenGranter>>, |
213 | pub settings_backend: Arc<Mutex<dyn SettingsBackend>>, | |
213 | pub settings_backend: Arc<Mutex<dyn MetadataBackend + Send>>, | |
214 | 214 | pub addr: SocketAddr, |
215 | 215 | pub instance: Instance, |
216 | 216 | pub handshaked: Arc<AtomicBool>, |
giterated-daemon/src/database_backend/handler.rs
@@ -117,16 +117,6 @@ impl<S: Send + Sync + Clone + 'static> OperationWrapper<S> { | ||
117 | 117 | } |
118 | 118 | } |
119 | 119 | |
120 | fn test_operation( | |
121 | _object: &User, | |
122 | _operation: SetSetting<AnySetting>, | |
123 | _state: (), | |
124 | ) -> Pin<Box<dyn Future<Output = Result<(), OperationError<SetSettingError>>> + Send + 'static>> { | |
125 | todo!() | |
126 | } | |
127 | ||
128 | fn foo() {} | |
129 | ||
130 | 120 | pub fn user_get_value( |
131 | 121 | object: &User, |
132 | 122 | operation: GetValue<AnyValue<User>>, |
@@ -147,43 +137,98 @@ pub fn user_get_value( | ||
147 | 137 | } |
148 | 138 | |
149 | 139 | pub fn user_get_setting( |
150 | _object: &User, | |
151 | _operation: GetSetting<AnySetting>, | |
152 | _state: DatabaseBackend, | |
140 | object: &User, | |
141 | operation: GetSetting<AnySetting>, | |
142 | state: DatabaseBackend, | |
153 | 143 | ) -> BoxFuture<'static, Result<AnySetting, OperationError<GetSettingError>>> { |
154 | todo!() | |
144 | let object = object.clone(); | |
145 | ||
146 | async move { | |
147 | let mut user_backend = state.user_backend.lock().await; | |
148 | let value = user_backend | |
149 | .get_setting(&object, &operation.setting_name) | |
150 | .await | |
151 | .map_err(|e| OperationError::Internal(e.to_string()))?; | |
152 | ||
153 | Ok(value) | |
154 | } | |
155 | .boxed() | |
155 | 156 | } |
156 | 157 | |
157 | 158 | pub fn user_set_setting( |
158 | _object: &User, | |
159 | _operation: SetSetting<AnySetting>, | |
160 | _state: DatabaseBackend, | |
159 | object: &User, | |
160 | operation: SetSetting<AnySetting>, | |
161 | state: DatabaseBackend, | |
161 | 162 | ) -> BoxFuture<'static, Result<(), OperationError<SetSettingError>>> { |
162 | todo!() | |
163 | let object = object.clone(); | |
164 | ||
165 | async move { | |
166 | let mut user_backend = state.user_backend.lock().await; | |
167 | let value = user_backend | |
168 | .write_setting(&object, &operation.setting_name, &operation.value.0) | |
169 | .await | |
170 | .map_err(|e| OperationError::Internal(e.to_string()))?; | |
171 | ||
172 | Ok(value) | |
173 | } | |
174 | .boxed() | |
163 | 175 | } |
164 | 176 | |
165 | 177 | pub fn repository_get_value( |
166 | _object: &Repository, | |
167 | _operation: GetValue<AnyValue<Repository>>, | |
168 | _state: DatabaseBackend, | |
178 | object: &Repository, | |
179 | operation: GetValue<AnyValue<Repository>>, | |
180 | state: DatabaseBackend, | |
169 | 181 | ) -> BoxFuture<'static, Result<AnyValue<Repository>, OperationError<GetValueError>>> { |
170 | todo!() | |
182 | let object = object.clone(); | |
183 | ||
184 | async move { | |
185 | let mut repository_backend = state.repository_backend.lock().await; | |
186 | let value = repository_backend | |
187 | .get_value(&object, &operation.value_name) | |
188 | .await | |
189 | .map_err(|e| OperationError::Internal(e.to_string()))?; | |
190 | ||
191 | Ok(value) | |
192 | } | |
193 | .boxed() | |
171 | 194 | } |
172 | 195 | |
173 | 196 | pub fn repository_get_setting( |
174 | _object: &Repository, | |
175 | _operation: GetSetting<AnySetting>, | |
176 | _state: DatabaseBackend, | |
197 | object: &Repository, | |
198 | operation: GetSetting<AnySetting>, | |
199 | state: DatabaseBackend, | |
177 | 200 | ) -> BoxFuture<'static, Result<AnySetting, OperationError<GetSettingError>>> { |
178 | todo!() | |
201 | let object = object.clone(); | |
202 | ||
203 | async move { | |
204 | let mut repository_backend = state.repository_backend.lock().await; | |
205 | let value = repository_backend | |
206 | .get_setting(&object, &operation.setting_name) | |
207 | .await | |
208 | .map_err(|e| OperationError::Internal(e.to_string()))?; | |
209 | ||
210 | Ok(value) | |
211 | } | |
212 | .boxed() | |
179 | 213 | } |
180 | 214 | |
181 | 215 | pub fn repository_set_setting( |
182 | _object: &Repository, | |
183 | _operation: SetSetting<AnySetting>, | |
184 | _state: DatabaseBackend, | |
216 | object: &Repository, | |
217 | operation: SetSetting<AnySetting>, | |
218 | state: DatabaseBackend, | |
185 | 219 | ) -> BoxFuture<'static, Result<(), OperationError<SetSettingError>>> { |
186 | todo!() | |
220 | let object = object.clone(); | |
221 | ||
222 | async move { | |
223 | let mut repository_backend = state.repository_backend.lock().await; | |
224 | let value = repository_backend | |
225 | .write_setting(&object, &operation.setting_name, &operation.value.0) | |
226 | .await | |
227 | .map_err(|e| OperationError::Internal(e.to_string()))?; | |
228 | ||
229 | Ok(value) | |
230 | } | |
231 | .boxed() | |
187 | 232 | } |
188 | 233 | |
189 | 234 | pub struct OperationHandlers<S: Send + Sync + Clone> { |
@@ -225,14 +270,13 @@ impl<S: Send + Sync + Clone + 'static> OperationHandlers<S> { | ||
225 | 270 | ) -> Result<Vec<u8>, OperationError<Vec<u8>>> { |
226 | 271 | if let Some(handler) = self.operations.get_mut(operation_name) { |
227 | 272 | handler |
228 | .handle( | |
229 | AnyObject(serde_json::to_string(object).unwrap()), | |
230 | operation, | |
231 | state, | |
232 | ) | |
273 | .handle(AnyObject(object.to_string()), operation, state) | |
233 | 274 | .await |
234 | 275 | } else { |
235 | panic!() | |
276 | Err(OperationError::Internal(format!( | |
277 | "unknown operation: {}", | |
278 | operation_name | |
279 | ))) | |
236 | 280 | } |
237 | 281 | } |
238 | 282 | } |
giterated-daemon/src/database_backend/mod.rs
@@ -2,7 +2,6 @@ pub mod handler; | ||
2 | 2 | |
3 | 3 | use std::{str::FromStr, sync::Arc}; |
4 | 4 | |
5 | use futures_util::TryFutureExt; | |
6 | 5 | use giterated_models::{ |
7 | 6 | error::OperationError, |
8 | 7 | model::{instance::Instance, repository::Repository, user::User}, |
@@ -63,9 +62,8 @@ impl ObjectBackend for DatabaseBackend { | ||
63 | 62 | ) -> Result<D::Success, OperationError<D::Failure>> { |
64 | 63 | let serialized = |
65 | 64 | serde_json::to_value(operation).map_err(|e| OperationError::Internal(e.to_string()))?; |
66 | let object_name = object.to_string(); | |
67 | ||
68 | if let Ok(user) = User::from_str(&object_name) { | |
65 | let object = object.to_string(); | |
66 | if let Ok(user) = User::from_str(&object) { | |
69 | 67 | let mut handler = OperationHandlers::default(); |
70 | 68 | |
71 | 69 | handler |
@@ -93,7 +91,7 @@ impl ObjectBackend for DatabaseBackend { | ||
93 | 91 | )), |
94 | 92 | }, |
95 | 93 | } |
96 | } else if let Ok(_repository) = Repository::from_str(&object_name) { | |
94 | } else if let Ok(repository) = Repository::from_str(&object) { | |
97 | 95 | let mut handler = OperationHandlers::default(); |
98 | 96 | |
99 | 97 | handler |
@@ -101,9 +99,27 @@ impl ObjectBackend for DatabaseBackend { | ||
101 | 99 | .insert(repository_get_setting) |
102 | 100 | .insert(repository_set_setting); |
103 | 101 | |
104 | // handler.handle(&repository, D::operation_name(), bincode::deserialize(&serialized).unwrap(), DatabaseBackendState).await; | |
105 | todo!() | |
106 | } else if Instance::from_str(&object_name).is_ok() { | |
102 | match handler | |
103 | .handle( | |
104 | &repository, | |
105 | D::operation_name(), | |
106 | serde_json::from_value(serialized.clone()).unwrap(), | |
107 | self.clone(), | |
108 | ) | |
109 | .await | |
110 | { | |
111 | Ok(result) => Ok(serde_json::from_slice(&result) | |
112 | .map_err(|e| OperationError::Internal(e.to_string()))?), | |
113 | Err(err) => match err { | |
114 | OperationError::Internal(internal) => Err(OperationError::Internal(internal)), | |
115 | OperationError::Unhandled => Err(OperationError::Unhandled), | |
116 | OperationError::Operation(err) => Err(OperationError::Operation( | |
117 | serde_json::from_slice(&err) | |
118 | .map_err(|e| OperationError::Internal(e.to_string()))?, | |
119 | )), | |
120 | }, | |
121 | } | |
122 | } else if serde_json::from_str::<Instance>(&object).is_ok() { | |
107 | 123 | Err(OperationError::Unhandled) |
108 | 124 | } else { |
109 | 125 | Err(OperationError::Unhandled) |
@@ -159,10 +175,16 @@ impl ObjectBackend for DatabaseBackend { | ||
159 | 175 | } |
160 | 176 | } |
161 | 177 | |
178 | // These tests verify that the essential handling of the database backend is | |
179 | // functional and correct. | |
180 | #[cfg(test)] | |
162 | 181 | mod test { |
163 | 182 | use std::{str::FromStr, sync::Arc}; |
164 | 183 | |
165 | 184 | use anyhow::Error; |
185 | use giterated_models::model::settings::UserDisplayName; | |
186 | use giterated_models::operation::ObjectBackend; | |
187 | use giterated_models::values::repository::Description; | |
166 | 188 | use giterated_models::{ |
167 | 189 | model::{ |
168 | 190 | authenticated::UserAuthenticationToken, |
@@ -176,10 +198,11 @@ mod test { | ||
176 | 198 | AuthenticationTokenRequest, RegisterAccountRequest, RepositoryCreateRequest, |
177 | 199 | }, |
178 | 200 | repository::RepositoryFileInspectRequest, |
179 | GiteratedObjectValue, ObjectBackend, | |
201 | GiteratedObjectValue, | |
180 | 202 | }, |
181 | 203 | values::{user::DisplayName, AnyValue}, |
182 | 204 | }; |
205 | ||
183 | 206 | use serde_json::Value; |
184 | 207 | use tokio::sync::Mutex; |
185 | 208 | |
@@ -199,7 +222,10 @@ mod test { | ||
199 | 222 | .unwrap()) |
200 | 223 | } |
201 | 224 | async fn get_setting(&mut self, _user: &User, _name: &str) -> Result<AnySetting, Error> { |
202 | todo!() | |
225 | Ok(serde_json::from_slice( | |
226 | &serde_json::to_vec(&DisplayName(String::from("test"))).unwrap(), | |
227 | ) | |
228 | .unwrap()) | |
203 | 229 | } |
204 | 230 | async fn write_setting( |
205 | 231 | &mut self, |
@@ -207,7 +233,7 @@ mod test { | ||
207 | 233 | _name: &str, |
208 | 234 | _setting: &Value, |
209 | 235 | ) -> Result<(), Error> { |
210 | todo!() | |
236 | Ok(()) | |
211 | 237 | } |
212 | 238 | async fn exists(&mut self, user: &User) -> Result<bool, Error> { |
213 | 239 | Ok(user == &User::from_str("test_user:test.giterated.dev").unwrap()) |
@@ -257,8 +283,43 @@ mod test { | ||
257 | 283 | ) -> Result<Vec<RepositorySummary>, Error> { |
258 | 284 | todo!() |
259 | 285 | } |
260 | async fn exists(&mut self, _repository: &Repository) -> Result<bool, Error> { | |
261 | todo!() | |
286 | ||
287 | async fn get_value( | |
288 | &mut self, | |
289 | _repository: &Repository, | |
290 | _name: &str, | |
291 | ) -> Result<AnyValue<Repository>, Error> { | |
292 | Ok(serde_json::from_slice( | |
293 | &serde_json::to_vec(&Description(String::from("test"))).unwrap(), | |
294 | ) | |
295 | .unwrap()) | |
296 | } | |
297 | async fn get_setting( | |
298 | &mut self, | |
299 | _repository: &Repository, | |
300 | _name: &str, | |
301 | ) -> Result<AnySetting, Error> { | |
302 | Ok(serde_json::from_slice( | |
303 | &serde_json::to_vec(&Description(String::from("test"))).unwrap(), | |
304 | ) | |
305 | .unwrap()) | |
306 | } | |
307 | async fn write_setting( | |
308 | &mut self, | |
309 | _repository: &Repository, | |
310 | _name: &str, | |
311 | _setting: &Value, | |
312 | ) -> Result<(), Error> { | |
313 | Ok(()) | |
314 | } | |
315 | ||
316 | async fn exists(&mut self, repository: &Repository) -> Result<bool, Error> { | |
317 | // Ok(true) | |
318 | Ok(repository | |
319 | == &Repository::from_str( | |
320 | "test_user:test.giterated.dev/[email protected]", | |
321 | ) | |
322 | .unwrap()) | |
262 | 323 | } |
263 | 324 | } |
264 | 325 | |
@@ -283,4 +344,77 @@ mod test { | ||
283 | 344 | .await |
284 | 345 | .expect("object value should have been returned"); |
285 | 346 | } |
347 | ||
348 | #[tokio::test] | |
349 | async fn test_user_get_setting() { | |
350 | let backend = test_backend(); | |
351 | ||
352 | let mut user = backend | |
353 | .get_object::<User>("test_user:test.giterated.dev") | |
354 | .await | |
355 | .expect("object should have been returned"); | |
356 | ||
357 | user.get_setting::<UserDisplayName>() | |
358 | .await | |
359 | .expect("object value should have been returned"); | |
360 | } | |
361 | ||
362 | #[tokio::test] | |
363 | async fn test_user_set_setting() { | |
364 | let backend = test_backend(); | |
365 | ||
366 | let mut user = backend | |
367 | .get_object::<User>("test_user:test.giterated.dev") | |
368 | .await | |
369 | .expect("object should have been returned"); | |
370 | ||
371 | user.set_setting::<UserDisplayName>(UserDisplayName(String::from("test"))) | |
372 | .await | |
373 | .expect("object value should have been returned"); | |
374 | } | |
375 | ||
376 | #[tokio::test] | |
377 | async fn test_respository_get() { | |
378 | let backend = test_backend(); | |
379 | ||
380 | let mut repository = backend | |
381 | .get_object::<Repository>("test_user:test.giterated.dev/[email protected]") | |
382 | .await | |
383 | .expect("object should have been returned"); | |
384 | ||
385 | repository | |
386 | .get::<Description>() | |
387 | .await | |
388 | .expect("object value should have been returned"); | |
389 | } | |
390 | ||
391 | #[tokio::test] | |
392 | async fn test_repository_get_setting() { | |
393 | let backend = test_backend(); | |
394 | ||
395 | let mut repository = backend | |
396 | .get_object::<Repository>("test_user:test.giterated.dev/[email protected]") | |
397 | .await | |
398 | .expect("object should have been returned"); | |
399 | ||
400 | repository | |
401 | .get_setting::<Description>() | |
402 | .await | |
403 | .expect("object value should have been returned"); | |
404 | } | |
405 | ||
406 | #[tokio::test] | |
407 | async fn test_repository_set_setting() { | |
408 | let backend = test_backend(); | |
409 | ||
410 | let mut repository = backend | |
411 | .get_object::<Repository>("test_user:test.giterated.dev/[email protected]") | |
412 | .await | |
413 | .expect("object should have been returned"); | |
414 | ||
415 | repository | |
416 | .set_setting::<Description>(Description(String::from("test"))) | |
417 | .await | |
418 | .expect("object value should have been returned"); | |
419 | } | |
286 | 420 | } |
giterated-daemon/src/main.rs
@@ -62,6 +62,7 @@ async fn main() -> Result<(), Error> { | ||
62 | 62 | ), |
63 | 63 | instance: Instance::from_str(config["giterated"]["instance"].as_str().unwrap()) |
64 | 64 | .unwrap(), |
65 | settings_provider: settings.clone(), | |
65 | 66 | })); |
66 | 67 | |
67 | 68 | let token_granter = Arc::new(Mutex::new(AuthenticationTokenGranter { |
giterated-models/Cargo.toml
@@ -23,6 +23,7 @@ toml = { version = "0.7" } | ||
23 | 23 | git2 = "0.17" |
24 | 24 | chrono = { version = "0.4", features = [ "serde" ] } |
25 | 25 | async-trait = "0.1" |
26 | serde_with = "3.3.0" | |
26 | 27 | |
27 | 28 | # Git backend |
28 | 29 | sqlx = { version = "0.7", default-features = false, features = [ "macros", "chrono" ] } |
giterated-models/src/model/instance.rs
@@ -1,4 +1,4 @@ | ||
1 | use std::str::FromStr; | |
1 | use std::{fmt::Display, str::FromStr}; | |
2 | 2 | |
3 | 3 | use serde::{Deserialize, Serialize}; |
4 | 4 | use thiserror::Error; |
@@ -42,9 +42,9 @@ impl GiteratedObject for Instance { | ||
42 | 42 | } |
43 | 43 | } |
44 | 44 | |
45 | impl ToString for Instance { | |
46 | fn to_string(&self) -> String { | |
47 | self.url.clone() | |
45 | impl Display for Instance { | |
46 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | |
47 | f.write_str(&self.url) | |
48 | 48 | } |
49 | 49 | } |
50 | 50 |
giterated-models/src/model/repository.rs
@@ -21,6 +21,9 @@ use super::{instance::Instance, user::User}; | ||
21 | 21 | /// be valid: |
22 | 22 | /// |
23 | 23 | /// ``` |
24 | //# use giterated_models::model::repository::Repository; | |
25 | //# use giterated_models::model::instance::Instance; | |
26 | //# use giterated_models::model::user::User; | |
24 | 27 | /// let repository = Repository { |
25 | 28 | /// owner: User::from_str("barson:giterated.dev").unwrap(), |
26 | 29 | /// name: String::from("foo"), |
@@ -38,24 +41,24 @@ pub struct Repository { | ||
38 | 41 | pub instance: Instance, |
39 | 42 | } |
40 | 43 | |
44 | impl Display for Repository { | |
45 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { | |
46 | f.write_str(&format!("{}/{}@{}", self.owner, self.name, self.instance)) | |
47 | } | |
48 | } | |
49 | ||
41 | 50 | impl GiteratedObject for Repository { |
42 | 51 | fn object_name() -> &'static str { |
43 | 52 | "repository" |
44 | 53 | } |
45 | 54 | |
46 | 55 | fn from_object_str(object_str: &str) -> Result<Self, anyhow::Error> { |
47 | Ok(Repository::from_str(object_str).unwrap()) | |
48 | } | |
49 | } | |
50 | ||
51 | impl ToString for Repository { | |
52 | fn to_string(&self) -> String { | |
53 | format!("{}/{}@{}", self.owner, self.name, self.instance.to_string()) | |
56 | Ok(Repository::from_str(object_str)?) | |
54 | 57 | } |
55 | 58 | } |
56 | 59 | |
57 | 60 | impl TryFrom<String> for Repository { |
58 | type Error = (); | |
61 | type Error = RepositoryParseError; | |
59 | 62 | |
60 | 63 | fn try_from(value: String) -> Result<Self, Self::Error> { |
61 | 64 | Self::from_str(&value) |
@@ -63,7 +66,7 @@ impl TryFrom<String> for Repository { | ||
63 | 66 | } |
64 | 67 | |
65 | 68 | impl FromStr for Repository { |
66 | type Err = (); | |
69 | type Err = RepositoryParseError; | |
67 | 70 | |
68 | 71 | fn from_str(s: &str) -> Result<Self, Self::Err> { |
69 | 72 | let mut by_ampersand = s.split('@'); |
@@ -81,6 +84,9 @@ impl FromStr for Repository { | ||
81 | 84 | } |
82 | 85 | } |
83 | 86 | |
87 | #[derive(Debug, thiserror::Error)] | |
88 | pub enum RepositoryParseError {} | |
89 | ||
84 | 90 | /// Visibility of the repository to the general eye |
85 | 91 | #[derive(PartialEq, Eq, Debug, Hash, Serialize, Deserialize, Clone, sqlx::Type)] |
86 | 92 | #[sqlx(type_name = "visibility", rename_all = "lowercase")] |
giterated-models/src/model/settings.rs
@@ -6,7 +6,7 @@ pub trait Setting: Serialize + DeserializeOwned { | ||
6 | 6 | } |
7 | 7 | |
8 | 8 | #[derive(Debug, Clone, Serialize, Deserialize)] |
9 | pub struct AnySetting(Value); | |
9 | pub struct AnySetting(pub Value); | |
10 | 10 | |
11 | 11 | impl Setting for AnySetting { |
12 | 12 | fn name() -> &'static str { |
@@ -23,7 +23,7 @@ impl Setting for UserBio { | ||
23 | 23 | } |
24 | 24 | } |
25 | 25 | |
26 | #[derive(Debug, Serialize, Deserialize)] | |
26 | #[derive(Debug, Serialize, Deserialize, Clone)] | |
27 | 27 | pub struct UserDisplayName(pub String); |
28 | 28 | |
29 | 29 | impl Setting for UserDisplayName { |
@@ -40,3 +40,21 @@ impl Setting for UserDisplayImage { | ||
40 | 40 | "Profile Image" |
41 | 41 | } |
42 | 42 | } |
43 | ||
44 | #[derive(Debug, Serialize, Deserialize)] | |
45 | pub struct RepositoryDescription(pub String); | |
46 | ||
47 | impl Setting for RepositoryDescription { | |
48 | fn name() -> &'static str { | |
49 | "Repository Description" | |
50 | } | |
51 | } | |
52 | ||
53 | #[derive(Debug, Serialize, Deserialize)] | |
54 | pub struct RepositoryVisibilitySetting(pub String); | |
55 | ||
56 | impl Setting for RepositoryVisibilitySetting { | |
57 | fn name() -> &'static str { | |
58 | "Repository Visibility" | |
59 | } | |
60 | } |
giterated-models/src/model/user.rs
@@ -57,9 +57,13 @@ impl From<String> for User { | ||
57 | 57 | } |
58 | 58 | |
59 | 59 | impl FromStr for User { |
60 | type Err = (); | |
60 | type Err = UserParseError; | |
61 | 61 | |
62 | 62 | fn from_str(s: &str) -> Result<Self, Self::Err> { |
63 | if s.contains('/') { | |
64 | return Err(UserParseError); | |
65 | } | |
66 | ||
63 | 67 | let mut colon_split = s.split(':'); |
64 | 68 | let username = colon_split.next().unwrap().to_string(); |
65 | 69 | let instance = Instance::from_str(colon_split.next().unwrap()).unwrap(); |
@@ -68,6 +72,10 @@ impl FromStr for User { | ||
68 | 72 | } |
69 | 73 | } |
70 | 74 | |
75 | #[derive(thiserror::Error, Debug)] | |
76 | #[error("failed to parse user")] | |
77 | pub struct UserParseError; | |
78 | ||
71 | 79 | #[derive(Clone, Debug, Serialize, Deserialize)] |
72 | 80 | pub struct Password(pub String); |
73 | 81 |
giterated-models/src/operation/mod.rs
@@ -1,4 +1,10 @@ | ||
1 | use std::{any::type_name, fmt::Debug, marker::PhantomData}; | |
1 | use std::{ | |
2 | any::type_name, | |
3 | convert::Infallible, | |
4 | fmt::{Debug, Display}, | |
5 | marker::PhantomData, | |
6 | str::FromStr, | |
7 | }; | |
2 | 8 | |
3 | 9 | use anyhow::Error; |
4 | 10 | use serde::{de::DeserializeOwned, Deserialize, Serialize}; |
@@ -14,7 +20,7 @@ pub mod instance; | ||
14 | 20 | pub mod repository; |
15 | 21 | pub mod user; |
16 | 22 | |
17 | pub trait GiteratedObject: Send + Serialize + DeserializeOwned + ToString { | |
23 | pub trait GiteratedObject: Send + Display + FromStr { | |
18 | 24 | fn object_name() -> &'static str; |
19 | 25 | |
20 | 26 | fn from_object_str(object_str: &str) -> Result<Self, Error>; |
@@ -121,11 +127,38 @@ impl<O: GiteratedObject + Send, V: GiteratedObjectValue<Object = O> + Send> Gite | ||
121 | 127 | #[derive(Serialize)] |
122 | 128 | #[serde(bound(deserialize = "O: GiteratedObject, V: GiteratedOperation<O>"))] |
123 | 129 | pub struct GiteratedMessage<O: GiteratedObject, V: GiteratedOperation<O>> { |
130 | #[serde(with = "string")] | |
124 | 131 | pub object: O, |
125 | 132 | pub operation: String, |
126 | 133 | pub payload: V, |
127 | 134 | } |
128 | 135 | |
136 | mod string { | |
137 | use std::fmt::Display; | |
138 | use std::str::FromStr; | |
139 | ||
140 | use serde::{de, Deserialize, Deserializer, Serializer}; | |
141 | ||
142 | pub fn serialize<T, S>(value: &T, serializer: S) -> Result<S::Ok, S::Error> | |
143 | where | |
144 | T: Display, | |
145 | S: Serializer, | |
146 | { | |
147 | serializer.collect_str(value) | |
148 | } | |
149 | ||
150 | pub fn deserialize<'de, T, D>(deserializer: D) -> Result<T, D::Error> | |
151 | where | |
152 | T: FromStr, | |
153 | T::Err: Display, | |
154 | D: Deserializer<'de>, | |
155 | { | |
156 | String::deserialize(deserializer)? | |
157 | .parse() | |
158 | .map_err(de::Error::custom) | |
159 | } | |
160 | } | |
161 | ||
129 | 162 | impl GiteratedMessage<AnyObject, AnyOperation> { |
130 | 163 | pub fn try_into<O: GiteratedObject, V: GiteratedOperation<O>>( |
131 | 164 | &self, |
@@ -187,9 +220,17 @@ impl GiteratedObject for AnyObject { | ||
187 | 220 | } |
188 | 221 | } |
189 | 222 | |
190 | impl ToString for AnyObject { | |
191 | fn to_string(&self) -> String { | |
192 | self.0.to_string() | |
223 | impl Display for AnyObject { | |
224 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | |
225 | f.write_str(&self.0) | |
226 | } | |
227 | } | |
228 | ||
229 | impl FromStr for AnyObject { | |
230 | type Err = Infallible; | |
231 | ||
232 | fn from_str(s: &str) -> Result<Self, Self::Err> { | |
233 | Ok(Self(s.to_owned())) | |
193 | 234 | } |
194 | 235 | } |
195 | 236 |
giterated-models/src/values/mod.rs
@@ -39,6 +39,10 @@ pub struct GetSetting<S: Setting + std::fmt::Debug + Clone> { | ||
39 | 39 | impl<O: GiteratedObject, S: Setting + Send + DeserializeOwned + Debug + Clone> GiteratedOperation<O> |
40 | 40 | for GetSetting<S> |
41 | 41 | { |
42 | fn operation_name() -> &'static str { | |
43 | "get_setting" | |
44 | } | |
45 | ||
42 | 46 | type Success = S; |
43 | 47 | |
44 | 48 | type Failure = GetSettingError; |
@@ -54,6 +58,10 @@ pub struct SetSetting<S: Setting> { | ||
54 | 58 | } |
55 | 59 | |
56 | 60 | impl<O: GiteratedObject, S: Setting + Send> GiteratedOperation<O> for SetSetting<S> { |
61 | fn operation_name() -> &'static str { | |
62 | "set_setting" | |
63 | } | |
64 | ||
57 | 65 | type Success = (); |
58 | 66 | |
59 | 67 | type Failure = SetSettingError; |
@@ -70,6 +78,15 @@ pub struct AnyValue<O> { | ||
70 | 78 | _marker: PhantomData<O>, |
71 | 79 | } |
72 | 80 | |
81 | impl<O: GiteratedObject> AnyValue<O> { | |
82 | pub unsafe fn from_raw(value: Value) -> Self { | |
83 | Self { | |
84 | value, | |
85 | _marker: Default::default(), | |
86 | } | |
87 | } | |
88 | } | |
89 | ||
73 | 90 | impl<O: GiteratedObject> GiteratedObjectValue for AnyValue<O> { |
74 | 91 | type Object = O; |
75 | 92 |
giterated-models/src/values/repository.rs
@@ -1,7 +1,10 @@ | ||
1 | 1 | use serde::{Deserialize, Serialize}; |
2 | 2 | |
3 | 3 | use crate::{ |
4 | model::repository::{Repository, RepositoryVisibility}, | |
4 | model::{ | |
5 | repository::{Repository, RepositoryVisibility}, | |
6 | settings::Setting, | |
7 | }, | |
5 | 8 | operation::GiteratedObjectValue, |
6 | 9 | }; |
7 | 10 | |
@@ -28,6 +31,12 @@ impl GiteratedObjectValue for Description { | ||
28 | 31 | } |
29 | 32 | } |
30 | 33 | |
34 | impl Setting for Description { | |
35 | fn name() -> &'static str { | |
36 | "description" | |
37 | } | |
38 | } | |
39 | ||
31 | 40 | #[derive(Debug, Hash, Clone, PartialEq, Eq, Serialize, Deserialize)] |
32 | 41 | pub struct Visibility(pub RepositoryVisibility); |
33 | 42 |
migrations/20230913232554_repository_metadata.sql
@@ -0,0 +1 @@ | ||
1 | -- Add migration script here |