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
67
fork

Configure Feed

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

chore: reorg crates & yeet parakeet-lexgen

Mia aae0e9c6 6f216030

+5 -609
-40
Cargo.lock
··· 2738 2738 ] 2739 2739 2740 2740 [[package]] 2741 - name = "parakeet-lexgen" 2742 - version = "0.1.0" 2743 - dependencies = [ 2744 - "clap", 2745 - "eyre", 2746 - "serde", 2747 - "serde_json", 2748 - "thiserror 2.0.12", 2749 - "walkdir", 2750 - ] 2751 - 2752 - [[package]] 2753 2741 name = "parking_lot" 2754 2742 version = "0.11.2" 2755 2743 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3607 3595 version = "1.0.18" 3608 3596 source = "registry+https://github.com/rust-lang/crates.io-index" 3609 3597 checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" 3610 - 3611 - [[package]] 3612 - name = "same-file" 3613 - version = "1.0.6" 3614 - source = "registry+https://github.com/rust-lang/crates.io-index" 3615 - checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" 3616 - dependencies = [ 3617 - "winapi-util", 3618 - ] 3619 3598 3620 3599 [[package]] 3621 3600 name = "schannel" ··· 4806 4785 checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" 4807 4786 4808 4787 [[package]] 4809 - name = "walkdir" 4810 - version = "2.5.0" 4811 - source = "registry+https://github.com/rust-lang/crates.io-index" 4812 - checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" 4813 - dependencies = [ 4814 - "same-file", 4815 - "winapi-util", 4816 - ] 4817 - 4818 - [[package]] 4819 4788 name = "want" 4820 4789 version = "0.3.1" 4821 4790 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 4993 4962 version = "0.4.0" 4994 4963 source = "registry+https://github.com/rust-lang/crates.io-index" 4995 4964 checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 4996 - 4997 - [[package]] 4998 - name = "winapi-util" 4999 - version = "0.1.9" 5000 - source = "registry+https://github.com/rust-lang/crates.io-index" 5001 - checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" 5002 - dependencies = [ 5003 - "windows-sys 0.59.0", 5004 - ] 5005 4965 5006 4966 [[package]] 5007 4967 name = "winapi-x86_64-pc-windows-gnu"
+1 -10
Cargo.toml
··· 1 1 [workspace] 2 2 resolver = "2" 3 3 4 - members = [ 5 - "consumer", 6 - "dataloader-rs", 7 - "did-resolver", 8 - "lexica", 9 - "parakeet", 10 - "parakeet-db", 11 - "parakeet-index", 12 - "parakeet-lexgen" 13 - ] 4 + members = ["crates/*"]
consumer/Cargo.toml crates/consumer/Cargo.toml
consumer/Dockerfile crates/consumer/Dockerfile
consumer/justfile crates/consumer/justfile
consumer/src/backfill/downloader.rs crates/consumer/src/backfill/downloader.rs
consumer/src/backfill/mod.rs crates/consumer/src/backfill/mod.rs
consumer/src/backfill/repo.rs crates/consumer/src/backfill/repo.rs
consumer/src/backfill/types.rs crates/consumer/src/backfill/types.rs
consumer/src/cmd.rs crates/consumer/src/cmd.rs
consumer/src/config.rs crates/consumer/src/config.rs
consumer/src/db/actor.rs crates/consumer/src/db/actor.rs
consumer/src/db/backfill.rs crates/consumer/src/db/backfill.rs
consumer/src/db/copy.rs crates/consumer/src/db/copy.rs
consumer/src/db/gates.rs crates/consumer/src/db/gates.rs
consumer/src/db/labels.rs crates/consumer/src/db/labels.rs
consumer/src/db/mod.rs crates/consumer/src/db/mod.rs
consumer/src/db/record.rs crates/consumer/src/db/record.rs
consumer/src/db/sql/bookmarks_upsert.sql crates/consumer/src/db/sql/bookmarks_upsert.sql
consumer/src/db/sql/feedgen_upsert.sql crates/consumer/src/db/sql/feedgen_upsert.sql
consumer/src/db/sql/label_copy_upsert.sql crates/consumer/src/db/sql/label_copy_upsert.sql
consumer/src/db/sql/label_defs_upsert.sql crates/consumer/src/db/sql/label_defs_upsert.sql
consumer/src/db/sql/label_service_upsert.sql crates/consumer/src/db/sql/label_service_upsert.sql
consumer/src/db/sql/list_upsert.sql crates/consumer/src/db/sql/list_upsert.sql
consumer/src/db/sql/post_insert.sql crates/consumer/src/db/sql/post_insert.sql
consumer/src/db/sql/postgate_upsert.sql crates/consumer/src/db/sql/postgate_upsert.sql
consumer/src/db/sql/profile_upsert.sql crates/consumer/src/db/sql/profile_upsert.sql
consumer/src/db/sql/starterpack_upsert.sql crates/consumer/src/db/sql/starterpack_upsert.sql
consumer/src/db/sql/status_upsert.sql crates/consumer/src/db/sql/status_upsert.sql
consumer/src/db/sql/threadgate_upsert.sql crates/consumer/src/db/sql/threadgate_upsert.sql
consumer/src/firehose/error.rs crates/consumer/src/firehose/error.rs
consumer/src/firehose/mod.rs crates/consumer/src/firehose/mod.rs
consumer/src/firehose/types.rs crates/consumer/src/firehose/types.rs
consumer/src/indexer/mod.rs crates/consumer/src/indexer/mod.rs
consumer/src/indexer/records.rs crates/consumer/src/indexer/records.rs
consumer/src/indexer/types.rs crates/consumer/src/indexer/types.rs
consumer/src/instrumentation.rs crates/consumer/src/instrumentation.rs
consumer/src/label_indexer/mod.rs crates/consumer/src/label_indexer/mod.rs
consumer/src/main.rs crates/consumer/src/main.rs
consumer/src/utils.rs crates/consumer/src/utils.rs
dataloader-rs/.gitignore crates/dataloader-rs/.gitignore
dataloader-rs/Cargo.toml crates/dataloader-rs/Cargo.toml
dataloader-rs/LICENSE-APACHE crates/dataloader-rs/LICENSE-APACHE
dataloader-rs/LICENSE-MIT crates/dataloader-rs/LICENSE-MIT
dataloader-rs/README.md crates/dataloader-rs/README.md
dataloader-rs/src/async_cached.rs crates/dataloader-rs/src/async_cached.rs
dataloader-rs/src/batch_fn.rs crates/dataloader-rs/src/batch_fn.rs
dataloader-rs/src/cached.rs crates/dataloader-rs/src/cached.rs
dataloader-rs/src/lib.rs crates/dataloader-rs/src/lib.rs
dataloader-rs/src/non_cached.rs crates/dataloader-rs/src/non_cached.rs
dataloader-rs/src/runtime.rs crates/dataloader-rs/src/runtime.rs
did-resolver/Cargo.toml crates/did-resolver/Cargo.toml
did-resolver/src/error.rs crates/did-resolver/src/error.rs
did-resolver/src/lib.rs crates/did-resolver/src/lib.rs
did-resolver/src/types.rs crates/did-resolver/src/types.rs
+1 -1
diesel.toml
··· 2 2 # see https://diesel.rs/guides/configuring-diesel-cli 3 3 4 4 [print_schema] 5 - file = "parakeet-db/src/schema.rs" 5 + file = "crates/parakeet-db/src/schema.rs" 6 6 custom_type_derives = ["diesel::query_builder::QueryId"] 7 7 patch_file = "fk.patch" 8 8
+3 -3
justfile
··· 1 - mod consumer 2 - mod parakeet 3 - mod parakeet-index 1 + mod consumer 'crates/consumer' 2 + mod parakeet 'crates/parakeet' 3 + mod parakeet-index 'crates/parakeet-index' 4 4 5 5 alias run-consumer := consumer::run 6 6 alias run-parakeet := parakeet::run
lexica/Cargo.toml crates/lexica/Cargo.toml
lexica/src/app_bsky/actor.rs crates/lexica/src/app_bsky/actor.rs
lexica/src/app_bsky/bookmark.rs crates/lexica/src/app_bsky/bookmark.rs
lexica/src/app_bsky/embed.rs crates/lexica/src/app_bsky/embed.rs
lexica/src/app_bsky/feed.rs crates/lexica/src/app_bsky/feed.rs
lexica/src/app_bsky/graph.rs crates/lexica/src/app_bsky/graph.rs
lexica/src/app_bsky/labeler.rs crates/lexica/src/app_bsky/labeler.rs
lexica/src/app_bsky/mod.rs crates/lexica/src/app_bsky/mod.rs
lexica/src/app_bsky/richtext.rs crates/lexica/src/app_bsky/richtext.rs
lexica/src/app_bsky/unspecced.rs crates/lexica/src/app_bsky/unspecced.rs
lexica/src/com_atproto/label.rs crates/lexica/src/com_atproto/label.rs
lexica/src/com_atproto/mod.rs crates/lexica/src/com_atproto/mod.rs
lexica/src/com_atproto/moderation.rs crates/lexica/src/com_atproto/moderation.rs
lexica/src/community_lexicon/bookmarks.rs crates/lexica/src/community_lexicon/bookmarks.rs
lexica/src/community_lexicon/mod.rs crates/lexica/src/community_lexicon/mod.rs
lexica/src/lib.rs crates/lexica/src/lib.rs
lexica/src/utils.rs crates/lexica/src/utils.rs
parakeet-db/Cargo.toml crates/parakeet-db/Cargo.toml
parakeet-db/src/lib.rs crates/parakeet-db/src/lib.rs
parakeet-db/src/models.rs crates/parakeet-db/src/models.rs
parakeet-db/src/schema.rs crates/parakeet-db/src/schema.rs
parakeet-db/src/types.rs crates/parakeet-db/src/types.rs
parakeet-index/Cargo.toml crates/parakeet-index/Cargo.toml
parakeet-index/Dockerfile crates/parakeet-index/Dockerfile
parakeet-index/build.rs crates/parakeet-index/build.rs
parakeet-index/justfile crates/parakeet-index/justfile
parakeet-index/proto/parakeet.proto crates/parakeet-index/proto/parakeet.proto
parakeet-index/src/lib.rs crates/parakeet-index/src/lib.rs
parakeet-index/src/main.rs crates/parakeet-index/src/main.rs
parakeet-index/src/server/config.rs crates/parakeet-index/src/server/config.rs
parakeet-index/src/server/db.rs crates/parakeet-index/src/server/db.rs
parakeet-index/src/server/instrumentation.rs crates/parakeet-index/src/server/instrumentation.rs
parakeet-index/src/server/mod.rs crates/parakeet-index/src/server/mod.rs
parakeet-index/src/server/service.rs crates/parakeet-index/src/server/service.rs
parakeet-index/src/server/utils.rs crates/parakeet-index/src/server/utils.rs
-12
parakeet-lexgen/Cargo.toml
··· 1 - [package] 2 - name = "parakeet-lexgen" 3 - version = "0.1.0" 4 - edition = "2021" 5 - 6 - [dependencies] 7 - clap = { version = "4.5.26", features = ["derive"] } 8 - eyre = "0.6.12" 9 - serde = { version = "1.0.217", features = ["derive"] } 10 - serde_json = "1.0.136" 11 - thiserror = "2.0.11" 12 - walkdir = "2.5.0"
-89
parakeet-lexgen/src/main.rs
··· 1 - use clap::Parser; 2 - use std::collections::BTreeMap; 3 - use walkdir::WalkDir; 4 - 5 - mod types; 6 - mod validate; 7 - 8 - #[derive(Debug, Parser)] 9 - struct Cli { 10 - sources: Vec<String>, 11 - #[clap(short, long)] 12 - out: String, 13 - /// Turn off lexicon validation - this may cause Rust compilation to fail on generated types. 14 - #[clap(short, long)] 15 - no_validate: bool, 16 - } 17 - 18 - #[derive(Debug, thiserror::Error)] 19 - enum Error { 20 - #[error("{0}")] 21 - Serde(#[from] serde_json::Error), 22 - #[error("{0}")] 23 - Io(#[from] std::io::Error), 24 - } 25 - 26 - fn main() -> eyre::Result<()> { 27 - let args = Cli::parse(); 28 - 29 - let lexicon_files = args 30 - .sources 31 - .iter() 32 - .flat_map(|source| WalkDir::new(source).into_iter()) 33 - .filter_map(|entry| entry.ok()) 34 - .filter_map(|entry| { 35 - let path = entry.path(); 36 - 37 - // attempt filtering down to json files 38 - // ends_with matching isn't the greatest, but it should get us most of the way 39 - match path.is_file() && entry.file_name().to_string_lossy().ends_with(".json") { 40 - true => Some(path.to_path_buf()), 41 - false => None, 42 - } 43 - }) 44 - .collect::<Vec<_>>(); 45 - 46 - let (parsed, errors) = lexicon_files 47 - .into_iter() 48 - .map(|path| { 49 - let filename = path.display().to_string(); 50 - 51 - let file = 52 - std::fs::File::open(path).map_err(|err| (filename.clone(), Error::from(err)))?; 53 - 54 - let res = serde_json::from_reader::<_, types::Lexicon>(file) 55 - .map_err(|err| (filename, Error::from(err)))?; 56 - 57 - Ok(res) 58 - }) 59 - .map(|v| match v { 60 - Ok(v) => (Some(v), None), 61 - Err(e) => (None, Some(e)), 62 - }) 63 - .unzip::<_, _, Vec<_>, Vec<_>>(); 64 - 65 - // report the errors 66 - for error in errors { 67 - if let Some((filename, error)) = error { 68 - eprintln!("Lexicon {filename} parse failed: {error}"); 69 - } 70 - } 71 - 72 - // pull all the valid lexicon files into a big map so we can run validation 73 - let parsed = parsed 74 - .into_iter() 75 - .filter_map(|maybe_lexicon| match maybe_lexicon { 76 - Some(lexicon) => Some((lexicon.id.clone(), lexicon)), 77 - None => None, 78 - }) 79 - .collect::<BTreeMap<_, _>>(); 80 - 81 - if !args.no_validate { 82 - let failed = validate::validate(&parsed); 83 - for (fail_nsid, fail_err) in failed { 84 - eprintln!("Lexicon {fail_nsid} failed validation: {fail_err:?}"); 85 - } 86 - } 87 - 88 - Ok(()) 89 - }
-225
parakeet-lexgen/src/types.rs
··· 1 - use serde::Deserialize; 2 - use std::collections::BTreeMap; 3 - 4 - #[derive(Debug, Deserialize)] 5 - pub struct Lexicon { 6 - /// Lexicon language version 7 - pub lexicon: i32, 8 - // Lexicon NSID 9 - pub id: String, 10 - pub revision: Option<i32>, 11 - pub description: Option<String>, 12 - pub defs: BTreeMap<String, LexiconDef>, 13 - } 14 - 15 - #[derive(Debug, Deserialize)] 16 - #[serde(tag = "type")] 17 - #[serde(rename_all = "lowercase")] 18 - pub enum LexiconDef { 19 - Query(LexiconQuery), 20 - Procedure(LexiconProcedure), 21 - Subscription(LexiconSubscription), 22 - Record(LexiconRecord), 23 - String(LexiconString), 24 - Array(LexiconArray), 25 - Object(LexiconObject), 26 - Token { description: Option<String> }, 27 - } 28 - 29 - #[derive(Debug, Deserialize)] 30 - #[serde(tag = "type")] 31 - #[serde(rename_all = "kebab-case")] 32 - pub enum LexiconSchema { 33 - Null { 34 - description: Option<String>, 35 - }, 36 - Boolean { 37 - description: Option<String>, 38 - default: Option<bool>, 39 - #[serde(rename = "const")] 40 - constant: Option<bool>, 41 - }, 42 - Integer(LexiconInteger), 43 - String(LexiconString), 44 - Bytes { 45 - description: Option<String>, 46 - #[serde(rename = "maxLength")] 47 - max_length: Option<i32>, 48 - #[serde(rename = "minLength")] 49 - min_length: Option<i32>, 50 - }, 51 - CidLink { 52 - description: Option<String>, 53 - }, 54 - Array(LexiconArray), 55 - Object(LexiconObject), 56 - Blob { 57 - description: Option<String>, 58 - accept: Option<Vec<String>>, 59 - #[serde(rename = "maxSize")] 60 - max_size: Option<i32>, 61 - }, 62 - Ref { 63 - description: Option<String>, 64 - #[serde(rename = "ref")] 65 - ref_to: String, 66 - }, 67 - Union(LexiconUnion), 68 - Unknown { 69 - description: Option<String>, 70 - }, 71 - } 72 - 73 - #[derive(Debug, Deserialize)] 74 - #[serde(rename_all = "lowercase")] 75 - pub enum RKey { 76 - Tid, 77 - Nsid, 78 - Any, 79 - } 80 - 81 - #[derive(Debug, Deserialize)] 82 - #[serde(untagged)] 83 - pub enum RecordKey { 84 - Typed(RKey), 85 - Other(String), 86 - } 87 - 88 - #[derive(Debug, Deserialize)] 89 - #[serde(rename_all = "kebab-case")] 90 - pub enum StringFormats { 91 - AtIdentifier, 92 - AtUri, 93 - Cid, 94 - Datetime, 95 - Did, 96 - Handle, 97 - Nsid, 98 - Tid, 99 - RecordKey, 100 - Uri, 101 - Language, 102 - } 103 - 104 - #[derive(Debug, Deserialize)] 105 - pub struct HttpApiExchange { 106 - pub description: Option<String>, 107 - pub encoding: String, 108 - pub schema: Option<LexiconSchema>, 109 - } 110 - 111 - #[derive(Debug, Deserialize)] 112 - pub struct LexiconError { 113 - pub name: String, 114 - pub description: Option<String>, 115 - } 116 - 117 - #[derive(Debug, Deserialize)] 118 - pub struct LexiconSubMessage { 119 - pub description: Option<String>, 120 - pub schema: Option<LexiconUnion>, 121 - } 122 - 123 - #[derive(Debug, Deserialize)] 124 - pub struct LexiconRecord { 125 - pub description: Option<String>, 126 - pub key: RecordKey, 127 - pub record: LexiconObject, 128 - } 129 - 130 - #[derive(Debug, Deserialize)] 131 - pub struct LexiconQuery { 132 - pub description: Option<String>, 133 - pub parameters: Option<LexiconParams>, 134 - pub output: Option<HttpApiExchange>, 135 - pub errors: Option<Vec<LexiconError>>, 136 - } 137 - 138 - #[derive(Debug, Deserialize)] 139 - pub struct LexiconProcedure { 140 - pub description: Option<String>, 141 - pub parameters: Option<LexiconParams>, 142 - pub output: Option<HttpApiExchange>, 143 - pub input: Option<HttpApiExchange>, 144 - pub errors: Option<Vec<LexiconError>>, 145 - } 146 - 147 - #[derive(Debug, Deserialize)] 148 - pub struct LexiconSubscription { 149 - pub description: Option<String>, 150 - pub parameters: Option<LexiconParams>, 151 - pub message: BTreeMap<String, LexiconSubMessage>, 152 - pub errors: Option<Vec<LexiconError>>, 153 - } 154 - 155 - #[derive(Debug, Deserialize)] 156 - pub struct LexiconInteger { 157 - pub description: Option<String>, 158 - 159 - pub minimum: Option<i64>, 160 - pub maximum: Option<i64>, 161 - #[serde(rename = "enum")] 162 - pub values: Option<Vec<i64>>, 163 - pub default: Option<i64>, 164 - #[serde(rename = "const")] 165 - pub constant: Option<i64>, 166 - } 167 - 168 - #[derive(Debug, Deserialize)] 169 - #[serde(rename_all = "camelCase")] 170 - pub struct LexiconString { 171 - pub description: Option<String>, 172 - 173 - pub format: Option<StringFormats>, 174 - 175 - pub max_length: Option<i32>, 176 - pub min_length: Option<i32>, 177 - pub max_graphemes: Option<i32>, 178 - pub min_graphemes: Option<i32>, 179 - 180 - pub known_values: Option<Vec<String>>, 181 - #[serde(rename = "enum")] 182 - pub values: Option<Vec<String>>, 183 - 184 - pub default: Option<String>, 185 - #[serde(rename = "const")] 186 - pub constant: Option<String>, 187 - } 188 - 189 - #[derive(Debug, Deserialize)] 190 - #[serde(rename_all = "camelCase")] 191 - pub struct LexiconArray { 192 - pub description: Option<String>, 193 - 194 - pub min_length: Option<i32>, 195 - pub max_length: Option<i32>, 196 - pub items: Box<LexiconSchema>, 197 - } 198 - 199 - #[derive(Debug, Deserialize)] 200 - #[serde(tag = "type")] 201 - #[serde(rename = "object")] 202 - pub struct LexiconObject { 203 - pub description: Option<String>, 204 - pub properties: BTreeMap<String, LexiconSchema>, 205 - pub required: Option<Vec<String>>, 206 - pub nullable: Option<Vec<String>>, 207 - } 208 - 209 - #[derive(Debug, Deserialize)] 210 - #[serde(tag = "type")] 211 - #[serde(rename = "params")] 212 - pub struct LexiconParams { 213 - pub description: Option<String>, 214 - pub properties: BTreeMap<String, LexiconSchema>, 215 - } 216 - 217 - #[derive(Debug, Deserialize)] 218 - #[serde(tag = "type")] 219 - #[serde(rename = "union")] 220 - pub struct LexiconUnion { 221 - pub description: Option<String>, 222 - pub refs: Vec<String>, 223 - #[serde(default)] 224 - pub closed: bool, 225 - }
-229
parakeet-lexgen/src/validate.rs
··· 1 - use crate::types::{ 2 - Lexicon, LexiconDef, LexiconObject, LexiconProcedure, LexiconQuery, LexiconSchema, 3 - LexiconString, LexiconSubscription, LexiconUnion, RecordKey, 4 - }; 5 - use std::collections::BTreeMap; 6 - 7 - #[derive(Debug)] 8 - pub enum ValidationError { 9 - InvalidReference(String), 10 - MissingProperty, 11 - MissingReference(String), 12 - RecordKey, 13 - StringConstAndDefault, 14 - } 15 - 16 - /// validates lexicon definition files - returns keys for any that don't 17 - pub fn validate(lexica: &BTreeMap<String, Lexicon>) -> Vec<(String, ValidationError)> { 18 - let mut errors = Vec::new(); 19 - 20 - for (lexicon_id, lexicon) in lexica { 21 - for (name, def) in &lexicon.defs { 22 - let pass = match def { 23 - LexiconDef::Query(query) => validate_query(query, lexica, lexicon_id), 24 - LexiconDef::Procedure(proc) => validate_procedure(proc, lexica, lexicon_id), 25 - LexiconDef::Subscription(sub) => validate_subscription(sub, lexica, lexicon_id), 26 - LexiconDef::Record(rec) => { 27 - // validate the rkey 28 - let rkey_okay = match &rec.key { 29 - // typed is always okay - serde validates 30 - RecordKey::Typed(_) => None, 31 - // we may want to validate literals according to record key syntax but idk? 32 - RecordKey::Other(text) if text.starts_with("literal:") => None, 33 - RecordKey::Other(_) => Some(ValidationError::RecordKey), 34 - }; 35 - 36 - // and the schema def 37 - let obj_okay = validate_object(&rec.record, lexica, lexicon_id); 38 - 39 - rkey_okay.or(obj_okay) 40 - } 41 - LexiconDef::String(lex_str) => validate_string(lex_str, lexica, lexicon_id), 42 - LexiconDef::Array(array) => validate_schema(&array.items, lexica, lexicon_id), 43 - LexiconDef::Object(obj) => validate_object(obj, lexica, lexicon_id), 44 - LexiconDef::Token { .. } => continue, 45 - }; 46 - 47 - if let Some(err) = pass { 48 - errors.push((format!("{lexicon_id}#{name}"), err)); 49 - } 50 - } 51 - } 52 - 53 - errors 54 - } 55 - 56 - fn validate_schema( 57 - schema: &LexiconSchema, 58 - lexica: &BTreeMap<String, Lexicon>, 59 - this_lex: &str, 60 - ) -> Option<ValidationError> { 61 - match schema { 62 - LexiconSchema::String(lex_str) => validate_string(lex_str, lexica, this_lex), 63 - LexiconSchema::Array(array) => validate_schema(&array.items, lexica, this_lex), 64 - LexiconSchema::Object(obj) => validate_object(obj, lexica, this_lex), 65 - LexiconSchema::Ref { ref_to, .. } => lookup(ref_to, this_lex, lexica), 66 - LexiconSchema::Union(union) => validate_union(union, lexica, this_lex), 67 - LexiconSchema::Null { .. } 68 - | LexiconSchema::Boolean { .. } 69 - | LexiconSchema::Integer(_) 70 - | LexiconSchema::Bytes { .. } 71 - | LexiconSchema::CidLink { .. } 72 - | LexiconSchema::Blob { .. } 73 - | LexiconSchema::Unknown { .. } => None, 74 - } 75 - } 76 - 77 - fn validate_query( 78 - query: &LexiconQuery, 79 - lexica: &BTreeMap<String, Lexicon>, 80 - this_lex: &str, 81 - ) -> Option<ValidationError> { 82 - if let Some(params) = &query.parameters { 83 - validate_schema_btree(params.properties.values(), lexica, this_lex)?; 84 - } 85 - if let Some(output) = &query.output { 86 - if let Some(schema) = &output.schema { 87 - validate_schema(schema, lexica, this_lex)?; 88 - } 89 - } 90 - 91 - None 92 - } 93 - 94 - fn validate_procedure( 95 - proc: &LexiconProcedure, 96 - lexica: &BTreeMap<String, Lexicon>, 97 - this_lex: &str, 98 - ) -> Option<ValidationError> { 99 - if let Some(params) = &proc.parameters { 100 - validate_schema_btree(params.properties.values(), lexica, this_lex)?; 101 - } 102 - 103 - if let Some(input) = &proc.input { 104 - if let Some(schema) = &input.schema { 105 - validate_schema(schema, lexica, this_lex)?; 106 - } 107 - } 108 - 109 - if let Some(output) = &proc.output { 110 - if let Some(schema) = &output.schema { 111 - validate_schema(schema, lexica, this_lex)?; 112 - } 113 - } 114 - 115 - None 116 - } 117 - 118 - fn validate_subscription( 119 - sub: &LexiconSubscription, 120 - lexica: &BTreeMap<String, Lexicon>, 121 - this_lex: &str, 122 - ) -> Option<ValidationError> { 123 - if let Some(params) = &sub.parameters { 124 - validate_schema_btree(params.properties.values(), lexica, this_lex)?; 125 - } 126 - 127 - sub.message.values().find_map(|item| { 128 - item.schema 129 - .as_ref() 130 - .map(|union| validate_union(&union, lexica, this_lex)) 131 - .unwrap_or_default() 132 - }) 133 - } 134 - 135 - fn validate_object( 136 - object: &LexiconObject, 137 - lexica: &BTreeMap<String, Lexicon>, 138 - this_lex: &str, 139 - ) -> Option<ValidationError> { 140 - // check that everything in required and nullable exists in properties 141 - if let Some(required) = &object.required { 142 - for key in required { 143 - if !object.properties.contains_key(key) { 144 - return Some(ValidationError::MissingProperty); 145 - } 146 - } 147 - } 148 - if let Some(nullable) = &object.nullable { 149 - for key in nullable { 150 - if !object.properties.contains_key(key) { 151 - return Some(ValidationError::MissingProperty); 152 - } 153 - } 154 - } 155 - 156 - // and now validate properties 157 - validate_schema_btree(object.properties.values(), lexica, this_lex) 158 - } 159 - 160 - fn validate_string( 161 - lex_string: &LexiconString, 162 - lexica: &BTreeMap<String, Lexicon>, 163 - this_lex: &str, 164 - ) -> Option<ValidationError> { 165 - if lex_string.constant.is_some() && lex_string.default.is_some() { 166 - return Some(ValidationError::StringConstAndDefault); 167 - } 168 - 169 - if let Some(known_values) = &lex_string.known_values { 170 - known_values.iter().find_map(|value| { 171 - if value.contains("#") { 172 - lookup(value, this_lex, lexica) 173 - } else { 174 - None 175 - } 176 - }) 177 - } else { 178 - None 179 - } 180 - } 181 - 182 - fn validate_union( 183 - union: &LexiconUnion, 184 - lexica: &BTreeMap<String, Lexicon>, 185 - this_lex: &str, 186 - ) -> Option<ValidationError> { 187 - union 188 - .refs 189 - .iter() 190 - .find_map(|value| lookup(value, this_lex, lexica)) 191 - } 192 - 193 - fn lookup( 194 - reference: &str, 195 - this_lex: &str, 196 - lexica: &BTreeMap<String, Lexicon>, 197 - ) -> Option<ValidationError> { 198 - let reference = if reference.contains("#") { 199 - match reference.strip_prefix("#") { 200 - Some(local_ref) => format!("{this_lex}#{local_ref}"), 201 - None => reference.to_string(), 202 - } 203 - } else { 204 - format!("{}#main", reference) 205 - }; 206 - 207 - if let Some((nsid, name)) = reference.split_once("#") { 208 - match lexica.get(nsid) { 209 - Some(def) => match def.defs.contains_key(name) { 210 - true => None, 211 - false => Some(ValidationError::MissingReference(reference)), 212 - }, 213 - None => Some(ValidationError::MissingReference(reference)), 214 - } 215 - } else { 216 - Some(ValidationError::InvalidReference(reference)) 217 - } 218 - } 219 - 220 - fn validate_schema_btree<'a, T>( 221 - mut schema: T, 222 - lexica: &BTreeMap<String, Lexicon>, 223 - this_lex: &str, 224 - ) -> Option<ValidationError> 225 - where 226 - T: Iterator<Item = &'a LexiconSchema>, 227 - { 228 - schema.find_map(|schema| validate_schema(schema, lexica, this_lex)) 229 - }
parakeet/Cargo.toml crates/parakeet/Cargo.toml
parakeet/Dockerfile crates/parakeet/Dockerfile
parakeet/justfile crates/parakeet/justfile
parakeet/src/cache.rs crates/parakeet/src/cache.rs
parakeet/src/config.rs crates/parakeet/src/config.rs
parakeet/src/db.rs crates/parakeet/src/db.rs
parakeet/src/hydration/embed.rs crates/parakeet/src/hydration/embed.rs
parakeet/src/hydration/feedgen.rs crates/parakeet/src/hydration/feedgen.rs
parakeet/src/hydration/labeler.rs crates/parakeet/src/hydration/labeler.rs
parakeet/src/hydration/list.rs crates/parakeet/src/hydration/list.rs
parakeet/src/hydration/mod.rs crates/parakeet/src/hydration/mod.rs
parakeet/src/hydration/posts.rs crates/parakeet/src/hydration/posts.rs
parakeet/src/hydration/profile.rs crates/parakeet/src/hydration/profile.rs
parakeet/src/hydration/starter_packs.rs crates/parakeet/src/hydration/starter_packs.rs
parakeet/src/instrumentation.rs crates/parakeet/src/instrumentation.rs
parakeet/src/loaders.rs crates/parakeet/src/loaders.rs
parakeet/src/main.rs crates/parakeet/src/main.rs
parakeet/src/sql/list_states.sql crates/parakeet/src/sql/list_states.sql
parakeet/src/sql/post_state.sql crates/parakeet/src/sql/post_state.sql
parakeet/src/sql/profile_state.sql crates/parakeet/src/sql/profile_state.sql
parakeet/src/sql/thread.sql crates/parakeet/src/sql/thread.sql
parakeet/src/sql/thread_branching.sql crates/parakeet/src/sql/thread_branching.sql
parakeet/src/sql/thread_parent.sql crates/parakeet/src/sql/thread_parent.sql
parakeet/src/sql/thread_v2_hidden_children.sql crates/parakeet/src/sql/thread_v2_hidden_children.sql
parakeet/src/xrpc/app_bsky/actor.rs crates/parakeet/src/xrpc/app_bsky/actor.rs
parakeet/src/xrpc/app_bsky/bookmark.rs crates/parakeet/src/xrpc/app_bsky/bookmark.rs
parakeet/src/xrpc/app_bsky/feed/feedgen.rs crates/parakeet/src/xrpc/app_bsky/feed/feedgen.rs
parakeet/src/xrpc/app_bsky/feed/likes.rs crates/parakeet/src/xrpc/app_bsky/feed/likes.rs
parakeet/src/xrpc/app_bsky/feed/mod.rs crates/parakeet/src/xrpc/app_bsky/feed/mod.rs
parakeet/src/xrpc/app_bsky/feed/posts.rs crates/parakeet/src/xrpc/app_bsky/feed/posts.rs
parakeet/src/xrpc/app_bsky/graph/lists.rs crates/parakeet/src/xrpc/app_bsky/graph/lists.rs
parakeet/src/xrpc/app_bsky/graph/mod.rs crates/parakeet/src/xrpc/app_bsky/graph/mod.rs
parakeet/src/xrpc/app_bsky/graph/mutes.rs crates/parakeet/src/xrpc/app_bsky/graph/mutes.rs
parakeet/src/xrpc/app_bsky/graph/relations.rs crates/parakeet/src/xrpc/app_bsky/graph/relations.rs
parakeet/src/xrpc/app_bsky/graph/starter_packs.rs crates/parakeet/src/xrpc/app_bsky/graph/starter_packs.rs
parakeet/src/xrpc/app_bsky/labeler.rs crates/parakeet/src/xrpc/app_bsky/labeler.rs
parakeet/src/xrpc/app_bsky/mod.rs crates/parakeet/src/xrpc/app_bsky/mod.rs
parakeet/src/xrpc/app_bsky/unspecced/mod.rs crates/parakeet/src/xrpc/app_bsky/unspecced/mod.rs
parakeet/src/xrpc/app_bsky/unspecced/thread_v2.rs crates/parakeet/src/xrpc/app_bsky/unspecced/thread_v2.rs
parakeet/src/xrpc/cdn.rs crates/parakeet/src/xrpc/cdn.rs
parakeet/src/xrpc/com_atproto/identity.rs crates/parakeet/src/xrpc/com_atproto/identity.rs
parakeet/src/xrpc/com_atproto/mod.rs crates/parakeet/src/xrpc/com_atproto/mod.rs
parakeet/src/xrpc/com_atproto/repo.rs crates/parakeet/src/xrpc/com_atproto/repo.rs
parakeet/src/xrpc/community_lexicon/bookmarks.rs crates/parakeet/src/xrpc/community_lexicon/bookmarks.rs
parakeet/src/xrpc/community_lexicon/mod.rs crates/parakeet/src/xrpc/community_lexicon/mod.rs
parakeet/src/xrpc/error.rs crates/parakeet/src/xrpc/error.rs
parakeet/src/xrpc/extract.rs crates/parakeet/src/xrpc/extract.rs
parakeet/src/xrpc/jwt.rs crates/parakeet/src/xrpc/jwt.rs
parakeet/src/xrpc/mod.rs crates/parakeet/src/xrpc/mod.rs