tangled
alpha
login
or
join now
parakeet.at
/
parakeet
62
fork
atom
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
62
fork
atom
overview
issues
12
pulls
pipelines
refactor: clean vec<option<T>> mess
mia.omg.lol
5 months ago
7cd2ac36
59cee77e
+66
-33
5 changed files
expand all
collapse all
unified
split
parakeet
src
hydration
labeler.rs
posts.rs
starter_packs.rs
xrpc
community_lexicon
bookmarks.rs
parakeet-db
src
models.rs
+54
-13
parakeet-db/src/models.rs
···
137
137
138
138
pub content: String,
139
139
pub facets: Option<serde_json::Value>,
140
140
-
pub languages: Vec<Option<String>>,
141
141
-
pub tags: Vec<Option<String>>,
140
140
+
pub languages: not_null_vec::TextArray,
141
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
151
-
pub mentions: Option<Vec<Option<String>>>,
151
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
239
-
pub detached: Vec<Option<String>>,
240
240
-
pub rules: Vec<Option<String>>,
239
239
+
pub detached: not_null_vec::TextArray,
240
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
255
-
pub hidden_replies: Vec<Option<String>>,
256
256
-
pub allow: Option<Vec<Option<String>>>,
257
257
-
pub allowed_lists: Option<Vec<Option<String>>>,
255
255
+
pub hidden_replies: not_null_vec::TextArray,
256
256
+
pub allow: Option<not_null_vec::TextArray>,
257
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
279
-
pub feeds: Option<Vec<Option<String>>>,
279
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
293
-
pub reasons: Option<Vec<Option<String>>>,
294
294
-
pub subject_types: Option<Vec<Option<String>>>,
295
295
-
pub subject_collections: Option<Vec<Option<String>>>,
293
293
+
pub reasons: Option<not_null_vec::TextArray>,
294
294
+
pub subject_types: Option<not_null_vec::TextArray>,
295
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
405
-
pub tags: Vec<Option<String>>,
405
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
433
+
434
434
+
mod not_null_vec {
435
435
+
use diesel::deserialize::FromSql;
436
436
+
use diesel::pg::Pg;
437
437
+
use diesel::sql_types::{Array, Nullable, Text};
438
438
+
use diesel::{deserialize, FromSqlRow};
439
439
+
use serde::{Deserialize, Serialize};
440
440
+
use std::ops::{Deref, DerefMut};
441
441
+
442
442
+
#[derive(Clone, Debug, Default, Serialize, Deserialize, FromSqlRow)]
443
443
+
#[diesel(sql_type = Array<Nullable<Text>>)]
444
444
+
pub struct TextArray(pub Vec<String>);
445
445
+
446
446
+
impl FromSql<Array<Nullable<Text>>, Pg> for TextArray {
447
447
+
fn from_sql(bytes: diesel::pg::PgValue<'_>) -> deserialize::Result<Self> {
448
448
+
let vec_with_nulls =
449
449
+
<Vec<Option<String>> as FromSql<Array<Nullable<Text>>, Pg>>::from_sql(bytes)?;
450
450
+
Ok(TextArray(vec_with_nulls.into_iter().flatten().collect()))
451
451
+
}
452
452
+
}
453
453
+
454
454
+
impl Deref for TextArray {
455
455
+
type Target = Vec<String>;
456
456
+
457
457
+
fn deref(&self) -> &Self::Target {
458
458
+
&self.0
459
459
+
}
460
460
+
}
461
461
+
462
462
+
impl DerefMut for TextArray {
463
463
+
fn deref_mut(&mut self) -> &mut Self::Target {
464
464
+
&mut self.0
465
465
+
}
466
466
+
}
467
467
+
468
468
+
impl From<TextArray> for Vec<String> {
469
469
+
fn from(v: TextArray) -> Vec<String> {
470
470
+
v.0
471
471
+
}
472
472
+
}
473
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
45
-
v.into_iter()
46
46
-
.flatten()
47
47
-
.filter_map(|v| ReasonType::from_str(&v).ok())
45
45
+
v.iter()
46
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
77
-
v.into_iter()
78
78
-
.flatten()
79
79
-
.filter_map(|v| SubjectType::from_str(&v).ok())
76
76
+
v.iter()
77
77
+
.filter_map(|v| SubjectType::from_str(v).ok())
80
78
.collect()
81
79
});
82
82
-
let subject_collections = labeler
83
83
-
.subject_collections
84
84
-
.map(|v| v.into_iter().flatten().collect());
80
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
92
-
Some(allowed_lists) => allowed_lists.iter().flatten().cloned().collect(),
92
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
109
-
acc.extend(lists.iter().flatten().cloned());
109
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
121
-
.filter_map(|v| v.clone().and_then(|v| lists.get(&v).cloned()))
121
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
99
-
.unwrap_or_default()
100
100
-
.into_iter()
101
101
-
.flatten()
102
102
-
.collect();
103
103
-
let feeds = self.hydrate_feedgens(feeds).await.into_values().collect();
99
99
+
.unwrap_or_default();
100
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
122
-
.flat_map(|feeds| feeds.into_iter().flatten())
119
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
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
63
-
tags: bookmark.tags.into_iter().flatten().collect(),
63
63
+
tags: bookmark.tags.into(),
64
64
created_at: bookmark.created_at,
65
65
})
66
66
.collect();