Parakeet is a Rust-based Bluesky AppServer aiming to implement most of the functionality required to support the Bluesky client
appview atproto bluesky rust appserver
66
fork

Configure Feed

Select the types of activity you want to include in your feed.

refactor: clean vec<option<T>> mess

Mia 7cd2ac36 59cee77e

+66 -33
+54 -13
parakeet-db/src/models.rs
··· 137 137 138 138 pub content: String, 139 139 pub facets: Option<serde_json::Value>, 140 - pub languages: Vec<Option<String>>, 141 - pub tags: Vec<Option<String>>, 140 + pub languages: not_null_vec::TextArray, 141 + pub tags: not_null_vec::TextArray, 142 142 143 143 pub parent_uri: Option<String>, 144 144 pub parent_cid: Option<String>, ··· 148 148 pub embed: Option<String>, 149 149 pub embed_subtype: Option<String>, 150 150 151 - pub mentions: Option<Vec<Option<String>>>, 151 + pub mentions: Option<not_null_vec::TextArray>, 152 152 pub violates_threadgate: bool, 153 153 154 154 pub created_at: DateTime<Utc>, ··· 236 236 pub cid: String, 237 237 pub post_uri: String, 238 238 239 - pub detached: Vec<Option<String>>, 240 - pub rules: Vec<Option<String>>, 239 + pub detached: not_null_vec::TextArray, 240 + pub rules: not_null_vec::TextArray, 241 241 242 242 pub created_at: DateTime<Utc>, 243 243 pub indexed_at: NaiveDateTime, ··· 252 252 pub cid: String, 253 253 pub post_uri: String, 254 254 255 - pub hidden_replies: Vec<Option<String>>, 256 - pub allow: Option<Vec<Option<String>>>, 257 - pub allowed_lists: Option<Vec<Option<String>>>, 255 + pub hidden_replies: not_null_vec::TextArray, 256 + pub allow: Option<not_null_vec::TextArray>, 257 + pub allowed_lists: Option<not_null_vec::TextArray>, 258 258 259 259 pub record: serde_json::Value, 260 260 ··· 276 276 pub description: Option<String>, 277 277 pub description_facets: Option<serde_json::Value>, 278 278 pub list: String, 279 - pub feeds: Option<Vec<Option<String>>>, 279 + pub feeds: Option<not_null_vec::TextArray>, 280 280 281 281 pub created_at: DateTime<Utc>, 282 282 pub indexed_at: NaiveDateTime, ··· 290 290 pub did: String, 291 291 pub cid: String, 292 292 293 - pub reasons: Option<Vec<Option<String>>>, 294 - pub subject_types: Option<Vec<Option<String>>>, 295 - pub subject_collections: Option<Vec<Option<String>>>, 293 + pub reasons: Option<not_null_vec::TextArray>, 294 + pub subject_types: Option<not_null_vec::TextArray>, 295 + pub subject_collections: Option<not_null_vec::TextArray>, 296 296 297 297 pub created_at: NaiveDateTime, 298 298 pub indexed_at: NaiveDateTime, ··· 402 402 pub subject: String, 403 403 pub subject_cid: Option<String>, 404 404 pub subject_type: String, 405 - pub tags: Vec<Option<String>>, 405 + pub tags: not_null_vec::TextArray, 406 406 pub created_at: DateTime<Utc>, 407 407 } 408 408 ··· 430 430 pub typ: String, 431 431 pub sort_at: DateTime<Utc>, 432 432 } 433 + 434 + mod not_null_vec { 435 + use diesel::deserialize::FromSql; 436 + use diesel::pg::Pg; 437 + use diesel::sql_types::{Array, Nullable, Text}; 438 + use diesel::{deserialize, FromSqlRow}; 439 + use serde::{Deserialize, Serialize}; 440 + use std::ops::{Deref, DerefMut}; 441 + 442 + #[derive(Clone, Debug, Default, Serialize, Deserialize, FromSqlRow)] 443 + #[diesel(sql_type = Array<Nullable<Text>>)] 444 + pub struct TextArray(pub Vec<String>); 445 + 446 + impl FromSql<Array<Nullable<Text>>, Pg> for TextArray { 447 + fn from_sql(bytes: diesel::pg::PgValue<'_>) -> deserialize::Result<Self> { 448 + let vec_with_nulls = 449 + <Vec<Option<String>> as FromSql<Array<Nullable<Text>>, Pg>>::from_sql(bytes)?; 450 + Ok(TextArray(vec_with_nulls.into_iter().flatten().collect())) 451 + } 452 + } 453 + 454 + impl Deref for TextArray { 455 + type Target = Vec<String>; 456 + 457 + fn deref(&self) -> &Self::Target { 458 + &self.0 459 + } 460 + } 461 + 462 + impl DerefMut for TextArray { 463 + fn deref_mut(&mut self) -> &mut Self::Target { 464 + &mut self.0 465 + } 466 + } 467 + 468 + impl From<TextArray> for Vec<String> { 469 + fn from(v: TextArray) -> Vec<String> { 470 + v.0 471 + } 472 + } 473 + }
+5 -9
parakeet/src/hydration/labeler.rs
··· 42 42 likes: Option<i32>, 43 43 ) -> LabelerViewDetailed { 44 44 let reason_types = labeler.reasons.map(|v| { 45 - v.into_iter() 46 - .flatten() 47 - .filter_map(|v| ReasonType::from_str(&v).ok()) 45 + v.iter() 46 + .filter_map(|v| ReasonType::from_str(v).ok()) 48 47 .collect() 49 48 }); 50 49 ··· 74 73 }) 75 74 .collect(); 76 75 let subject_types = labeler.subject_types.map(|v| { 77 - v.into_iter() 78 - .flatten() 79 - .filter_map(|v| SubjectType::from_str(&v).ok()) 76 + v.iter() 77 + .filter_map(|v| SubjectType::from_str(v).ok()) 80 78 .collect() 81 79 }); 82 - let subject_collections = labeler 83 - .subject_collections 84 - .map(|v| v.into_iter().flatten().collect()); 80 + let subject_collections = labeler.subject_collections.map(Vec::from); 85 81 86 82 LabelerViewDetailed { 87 83 uri: format!("at://{}/app.bsky.labeler.service/self", labeler.did),
+3 -3
parakeet/src/hydration/posts.rs
··· 89 89 let threadgate = threadgate?; 90 90 91 91 let lists = match threadgate.allowed_lists.as_ref() { 92 - Some(allowed_lists) => allowed_lists.iter().flatten().cloned().collect(), 92 + Some(allowed_lists) => allowed_lists.clone().into(), 93 93 None => Vec::new(), 94 94 }; 95 95 let lists = self.hydrate_lists_basic(lists).await; ··· 106 106 ) -> HashMap<String, ThreadgateView> { 107 107 let lists = threadgates.iter().fold(Vec::new(), |mut acc, c| { 108 108 if let Some(lists) = &c.allowed_lists { 109 - acc.extend(lists.iter().flatten().cloned()); 109 + acc.extend(lists.clone().0); 110 110 } 111 111 acc 112 112 }); ··· 118 118 let this_lists = match &threadgate.allowed_lists { 119 119 Some(allowed_lists) => allowed_lists 120 120 .iter() 121 - .filter_map(|v| v.clone().and_then(|v| lists.get(&v).cloned())) 121 + .filter_map(|v| lists.get(v).cloned()) 122 122 .collect(), 123 123 None => Vec::new(), 124 124 };
+3 -7
parakeet/src/hydration/starter_packs.rs
··· 96 96 let feeds = sp 97 97 .feeds 98 98 .clone() 99 - .unwrap_or_default() 100 - .into_iter() 101 - .flatten() 102 - .collect(); 103 - let feeds = self.hydrate_feedgens(feeds).await.into_values().collect(); 99 + .unwrap_or_default(); 100 + let feeds = self.hydrate_feedgens(feeds.into()).await.into_values().collect(); 104 101 105 102 Some(build_spview(sp, creator, labels, list, feeds)) 106 103 } ··· 119 116 let feeds = packs 120 117 .values() 121 118 .filter_map(|pack| pack.feeds.clone()) 122 - .flat_map(|feeds| feeds.into_iter().flatten()) 119 + .flat_map(Vec::from) 123 120 .collect(); 124 121 125 122 let creators = self.hydrate_profiles_basic(creators).await; ··· 133 130 let list = lists.get(&pack.list).cloned(); 134 131 let feeds = pack.feeds.as_ref().map(|v| { 135 132 v.iter() 136 - .flatten() 137 133 .filter_map(|feed| feeds.get(feed).cloned()) 138 134 .collect() 139 135 });
+1 -1
parakeet/src/xrpc/community_lexicon/bookmarks.rs
··· 60 60 .into_iter() 61 61 .map(|bookmark| Bookmark { 62 62 subject: bookmark.subject, 63 - tags: bookmark.tags.into_iter().flatten().collect(), 63 + tags: bookmark.tags.into(), 64 64 created_at: bookmark.created_at, 65 65 }) 66 66 .collect();