Lexicons and codegen, tiny testbed for oauth in cli crate

+521 -91
+65 -2
Cargo.lock
··· 1970 1971 [[package]] 1972 name = "n0-future" 1973 - version = "0.1.2" 1974 source = "registry+https://github.com/rust-lang/crates.io-index" 1975 - checksum = "399e11dc3b0e8d9d65b27170d22f5d779d52d9bed888db70d7e0c2c7ce3dfc52" 1976 dependencies = [ 1977 "cfg_aliases", 1978 "derive_more", ··· 2667 ] 2668 2669 [[package]] 2670 name = "serde_urlencoded" 2671 version = "0.7.1" 2672 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3058 ] 3059 3060 [[package]] 3061 name = "tower" 3062 version = "0.5.2" 3063 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3381 "clap", 3382 "esquema-codegen", 3383 "hickory-resolver", 3384 "n0-future", 3385 "weaver-common", 3386 "weaver-workspace-hack", 3387 ] ··· 3415 "serde_json", 3416 "thiserror 2.0.12", 3417 "tokio", 3418 "tracing", 3419 "weaver-workspace-hack", 3420 ] ··· 3816 version = "0.53.0" 3817 source = "registry+https://github.com/rust-lang/crates.io-index" 3818 checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" 3819 3820 [[package]] 3821 name = "winreg"
··· 1970 1971 [[package]] 1972 name = "n0-future" 1973 + version = "0.1.3" 1974 source = "registry+https://github.com/rust-lang/crates.io-index" 1975 + checksum = "7bb0e5d99e681ab3c938842b96fcb41bf8a7bb4bfdb11ccbd653a7e83e06c794" 1976 dependencies = [ 1977 "cfg_aliases", 1978 "derive_more", ··· 2667 ] 2668 2669 [[package]] 2670 + name = "serde_spanned" 2671 + version = "0.6.8" 2672 + source = "registry+https://github.com/rust-lang/crates.io-index" 2673 + checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" 2674 + dependencies = [ 2675 + "serde", 2676 + ] 2677 + 2678 + [[package]] 2679 name = "serde_urlencoded" 2680 version = "0.7.1" 2681 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3067 ] 3068 3069 [[package]] 3070 + name = "toml" 3071 + version = "0.8.22" 3072 + source = "registry+https://github.com/rust-lang/crates.io-index" 3073 + checksum = "05ae329d1f08c4d17a59bed7ff5b5a769d062e64a62d34a3261b219e62cd5aae" 3074 + dependencies = [ 3075 + "serde", 3076 + "serde_spanned", 3077 + "toml_datetime", 3078 + "toml_edit", 3079 + ] 3080 + 3081 + [[package]] 3082 + name = "toml_datetime" 3083 + version = "0.6.9" 3084 + source = "registry+https://github.com/rust-lang/crates.io-index" 3085 + checksum = "3da5db5a963e24bc68be8b17b6fa82814bb22ee8660f192bb182771d498f09a3" 3086 + dependencies = [ 3087 + "serde", 3088 + ] 3089 + 3090 + [[package]] 3091 + name = "toml_edit" 3092 + version = "0.22.26" 3093 + source = "registry+https://github.com/rust-lang/crates.io-index" 3094 + checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e" 3095 + dependencies = [ 3096 + "indexmap 2.9.0", 3097 + "serde", 3098 + "serde_spanned", 3099 + "toml_datetime", 3100 + "toml_write", 3101 + "winnow", 3102 + ] 3103 + 3104 + [[package]] 3105 + name = "toml_write" 3106 + version = "0.1.1" 3107 + source = "registry+https://github.com/rust-lang/crates.io-index" 3108 + checksum = "bfb942dfe1d8e29a7ee7fcbde5bd2b9a25fb89aa70caea2eba3bee836ff41076" 3109 + 3110 + [[package]] 3111 name = "tower" 3112 version = "0.5.2" 3113 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3431 "clap", 3432 "esquema-codegen", 3433 "hickory-resolver", 3434 + "miette", 3435 "n0-future", 3436 + "serde_html_form", 3437 + "tokio", 3438 "weaver-common", 3439 "weaver-workspace-hack", 3440 ] ··· 3468 "serde_json", 3469 "thiserror 2.0.12", 3470 "tokio", 3471 + "toml", 3472 "tracing", 3473 "weaver-workspace-hack", 3474 ] ··· 3870 version = "0.53.0" 3871 source = "registry+https://github.com/rust-lang/crates.io-index" 3872 checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" 3873 + 3874 + [[package]] 3875 + name = "winnow" 3876 + version = "0.7.10" 3877 + source = "registry+https://github.com/rust-lang/crates.io-index" 3878 + checksum = "c06928c8748d81b05c9be96aad92e1b6ff01833332f281e8cfca3be4b35fc9ec" 3879 + dependencies = [ 3880 + "memchr", 3881 + ] 3882 3883 [[package]] 3884 name = "winreg"
+1 -1
Cargo.toml
··· 33 thiserror = "2.0" 34 syntect = "5.2.0" 35 jane-eyre = "0.6.12" 36 - n0-future = "=0.1.2" 37 tracing = { version = "0.1.41", default-features = false, features = ["std"] } 38 lexicon_cid = { package = "cid", version = "0.10.1", features = [ 39 "serde-codec",
··· 33 thiserror = "2.0" 34 syntect = "5.2.0" 35 jane-eyre = "0.6.12" 36 + n0-future = "=0.1.3" 37 tracing = { version = "0.1.41", default-features = false, features = ["std"] } 38 lexicon_cid = { package = "cid", version = "0.10.1", features = [ 39 "serde-codec",
+6
crates/weaver-cli/Cargo.toml
··· 10 n0-future = { workspace = true } 11 weaver-common = { path = "../weaver-common" } 12 weaver-workspace-hack = { version = "0.1", path = "../weaver-workspace-hack" } 13 14 hickory-resolver = "0.24.1" 15 esquema-codegen.workspace = true ··· 20 atrium-oauth = "0.1.1" 21 atrium-xrpc-client = "0.5.13" 22 atrium-lex = { workspace = true }
··· 10 n0-future = { workspace = true } 11 weaver-common = { path = "../weaver-common" } 12 weaver-workspace-hack = { version = "0.1", path = "../weaver-workspace-hack" } 13 + miette = { workspace = true, features = ["fancy"] } 14 15 hickory-resolver = "0.24.1" 16 esquema-codegen.workspace = true ··· 21 atrium-oauth = "0.1.1" 22 atrium-xrpc-client = "0.5.13" 23 atrium-lex = { workspace = true } 24 + 25 + serde_html_form = "0.2.7" 26 + 27 + # temp for testing 28 + tokio = { version = "1.45.0", features = ["full"] }
+57 -2
crates/weaver-cli/src/main.rs
··· 1 - //! A cli which will echo every input argument to stdout 2 3 - fn main() {}
··· 1 + use atrium_api::agent::Agent; 2 + use atrium_api::xrpc::http::Uri; 3 + use atrium_oauth::AuthorizeOptions; 4 + use atrium_oauth::KnownScope; 5 + use atrium_oauth::Scope; 6 + use std::{ 7 + error, 8 + io::{BufRead, Write, stdin, stdout}, 9 + sync::Arc, 10 + }; 11 + 12 + #[tokio::main] 13 + async fn main() -> Result<(), Box<dyn error::Error>> { 14 + let client = weaver_common::oauth::default_oauth_client("http://127.0.0.1/callback")?; 15 + println!( 16 + "Authorization url: {}", 17 + client 18 + .authorize( 19 + std::env::var("PDS_URL").unwrap_or(String::from("https://atproto.systems")), 20 + AuthorizeOptions { 21 + scopes: vec![ 22 + Scope::Known(KnownScope::Atproto), 23 + Scope::Known(KnownScope::TransitionGeneric) 24 + ], 25 + ..Default::default() 26 + } 27 + ) 28 + .await? 29 + ); 30 + 31 + print!("Redirected url: "); 32 + stdout().lock().flush()?; 33 + let mut url = String::new(); 34 + stdin().lock().read_line(&mut url)?; 35 36 + let uri = url.trim().parse::<Uri>()?; 37 + let params = serde_html_form::from_str(uri.query().unwrap())?; 38 + let (session, _) = client.callback(params).await?; 39 + let agent = Agent::new(session); 40 + let output = agent 41 + .api 42 + .app 43 + .bsky 44 + .feed 45 + .get_timeline( 46 + atrium_api::app::bsky::feed::get_timeline::ParametersData { 47 + algorithm: None, 48 + cursor: None, 49 + limit: 3.try_into().ok(), 50 + } 51 + .into(), 52 + ) 53 + .await?; 54 + for feed in &output.feed { 55 + println!("{feed:?}"); 56 + } 57 + Ok(()) 58 + }
+2 -1
crates/weaver-common/Cargo.toml
··· 41 ] } 42 43 44 - miette = { workspace = true, features = ["fancy"] } 45 owo-colors = { workspace = true } 46 thiserror = { workspace = true } 47 tracing = { workspace = true } 48 hickory-resolver = "0.24.1" 49 50 51
··· 41 ] } 42 43 44 + miette = { workspace = true } 45 owo-colors = { workspace = true } 46 thiserror = { workspace = true } 47 tracing = { workspace = true } 48 hickory-resolver = "0.24.1" 49 + toml = "0.8.22" 50 51 52
crates/weaver-common/src/client.rs

This is a binary file and will not be displayed.

+118
crates/weaver-common/src/config.rs
···
··· 1 + use atrium_api::agent::atp_agent::AtpSession; 2 + use miette::Result; 3 + use miette::miette; 4 + use serde::{Deserialize, Serialize}; 5 + 6 + use std::future::Future; 7 + use std::path::Path; 8 + use std::path::PathBuf; 9 + 10 + #[derive(Debug, Clone, Serialize, Deserialize)] 11 + pub struct Config { 12 + /// The base URL for the XRPC endpoint. 13 + pub endpoint: String, 14 + /// The session data. 15 + pub session: Option<AtpSession>, 16 + /// The labelers header values. 17 + pub labelers_header: Option<Vec<String>>, 18 + /// The proxy header for service proxying. 19 + pub proxy_header: Option<String>, 20 + } 21 + 22 + impl Config { 23 + /// Loads the configuration from the provided loader. 24 + pub async fn load(loader: &impl Loader) -> Result<Self> { 25 + loader 26 + .load() 27 + .await 28 + .map_err(|_| miette!("Failed to load configuration")) 29 + } 30 + /// Saves the configuration using the provided saver. 31 + pub async fn save(&self, saver: &impl Saver) -> Result<()> { 32 + saver 33 + .save(self) 34 + .await 35 + .map_err(|_| miette!("Failed to save configuration")) 36 + } 37 + } 38 + 39 + impl Default for Config { 40 + /// Creates a new default configuration. 41 + /// 42 + /// The default configuration uses the base URL `https://bsky.social`. 43 + fn default() -> Self { 44 + Self { 45 + endpoint: "https://atproto.systems".to_owned(), 46 + session: None, 47 + labelers_header: None, 48 + proxy_header: None, 49 + } 50 + } 51 + } 52 + 53 + /// The trait for loading configuration data. 54 + pub trait Loader { 55 + /// Loads the configuration data. 56 + fn load( 57 + &self, 58 + ) -> impl Future< 59 + Output = core::result::Result<Config, Box<dyn std::error::Error + Send + Sync + 'static>>, 60 + > + Send; 61 + } 62 + 63 + /// The trait for saving configuration data. 64 + pub trait Saver { 65 + /// Saves the configuration data. 66 + fn save( 67 + &self, 68 + config: &Config, 69 + ) -> impl Future< 70 + Output = core::result::Result<(), Box<dyn std::error::Error + Send + Sync + 'static>>, 71 + > + Send; 72 + } 73 + 74 + /// An implementation of [`Loader`] and [`Saver`] that reads and writes a configuration file. 75 + pub struct FileStore { 76 + path: PathBuf, 77 + } 78 + 79 + impl FileStore { 80 + /// Create a new [`FileStore`] with the given path. 81 + /// 82 + /// This `FileStore` will read and write to the file at the given path. 83 + /// [`Config`] data will be serialized and deserialized using the file extension. 84 + /// By default, this supports only `.json` files. 85 + pub fn new(path: impl AsRef<Path>) -> Self { 86 + Self { 87 + path: path.as_ref().to_path_buf(), 88 + } 89 + } 90 + } 91 + 92 + impl Loader for FileStore { 93 + async fn load( 94 + &self, 95 + ) -> core::result::Result<Config, Box<dyn std::error::Error + Send + Sync + 'static>> { 96 + match self.path.extension().and_then(|ext| ext.to_str()) { 97 + Some("json") => Ok(serde_json::from_str(&std::fs::read_to_string(&self.path)?)?), 98 + Some("toml") => Ok(toml::from_str(&std::fs::read_to_string(&self.path)?)?), 99 + _ => Err(miette!("Unsupported file format").into()), 100 + } 101 + } 102 + } 103 + 104 + impl Saver for FileStore { 105 + async fn save( 106 + &self, 107 + config: &Config, 108 + ) -> core::result::Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> { 109 + match self.path.extension().and_then(|ext| ext.to_str()) { 110 + Some("json") => Ok(std::fs::write( 111 + &self.path, 112 + serde_json::to_string_pretty(config)?, 113 + )?), 114 + Some("toml") => Ok(std::fs::write(&self.path, toml::to_string_pretty(config)?)?), 115 + _ => Err(miette!("Unsupported file format").into()), 116 + } 117 + } 118 + }
+16
crates/weaver-common/src/lexicons/record.rs
··· 69 LexiconsShWeaverNotebookAuthors( 70 Box<crate::lexicons::sh::weaver::notebook::authors::Record>, 71 ), 72 #[serde(rename = "sh.weaver.notebook.entry")] 73 LexiconsShWeaverNotebookEntry( 74 Box<crate::lexicons::sh::weaver::notebook::entry::Record>, ··· 310 record_data: crate::lexicons::sh::weaver::notebook::authors::RecordData, 311 ) -> Self { 312 KnownRecord::LexiconsShWeaverNotebookAuthors(Box::new(record_data.into())) 313 } 314 } 315 impl From<crate::lexicons::sh::weaver::notebook::entry::Record> for KnownRecord {
··· 69 LexiconsShWeaverNotebookAuthors( 70 Box<crate::lexicons::sh::weaver::notebook::authors::Record>, 71 ), 72 + #[serde(rename = "sh.weaver.notebook.book")] 73 + LexiconsShWeaverNotebookBook( 74 + Box<crate::lexicons::sh::weaver::notebook::book::Record>, 75 + ), 76 #[serde(rename = "sh.weaver.notebook.entry")] 77 LexiconsShWeaverNotebookEntry( 78 Box<crate::lexicons::sh::weaver::notebook::entry::Record>, ··· 314 record_data: crate::lexicons::sh::weaver::notebook::authors::RecordData, 315 ) -> Self { 316 KnownRecord::LexiconsShWeaverNotebookAuthors(Box::new(record_data.into())) 317 + } 318 + } 319 + impl From<crate::lexicons::sh::weaver::notebook::book::Record> for KnownRecord { 320 + fn from(record: crate::lexicons::sh::weaver::notebook::book::Record) -> Self { 321 + KnownRecord::LexiconsShWeaverNotebookBook(Box::new(record)) 322 + } 323 + } 324 + impl From<crate::lexicons::sh::weaver::notebook::book::RecordData> for KnownRecord { 325 + fn from( 326 + record_data: crate::lexicons::sh::weaver::notebook::book::RecordData, 327 + ) -> Self { 328 + KnownRecord::LexiconsShWeaverNotebookBook(Box::new(record_data.into())) 329 } 330 } 331 impl From<crate::lexicons::sh::weaver::notebook::entry::Record> for KnownRecord {
+5
crates/weaver-common/src/lexicons/sh/weaver/actor/defs.rs
··· 17 pub indexed_at: core::option::Option<atrium_api::types::string::Datetime>, 18 #[serde(skip_serializing_if = "core::option::Option::is_none")] 19 pub labels: core::option::Option<Vec<crate::com::atproto::label::defs::Label>>, 20 } 21 pub type ProfileView = atrium_api::types::Object<ProfileViewData>;
··· 17 pub indexed_at: core::option::Option<atrium_api::types::string::Datetime>, 18 #[serde(skip_serializing_if = "core::option::Option::is_none")] 19 pub labels: core::option::Option<Vec<crate::com::atproto::label::defs::Label>>, 20 + #[serde(skip_serializing_if = "core::option::Option::is_none")] 21 + pub links: core::option::Option<Vec<String>>, 22 + ///Free-form location text. 23 + #[serde(skip_serializing_if = "core::option::Option::is_none")] 24 + pub location: core::option::Option<String>, 25 } 26 pub type ProfileView = atrium_api::types::Object<ProfileViewData>;
+7
crates/weaver-common/src/lexicons/sh/weaver/notebook.rs
··· 1 // @generated - This file is generated by esquema-codegen (forked from atrium-codegen). DO NOT EDIT. 2 //!Definitions for the `sh.weaver.notebook` namespace. 3 pub mod authors; 4 pub mod defs; 5 pub mod entry; 6 #[derive(Debug)] ··· 8 impl atrium_api::types::Collection for Authors { 9 const NSID: &'static str = "sh.weaver.notebook.authors"; 10 type Record = authors::Record; 11 } 12 #[derive(Debug)] 13 pub struct Entry;
··· 1 // @generated - This file is generated by esquema-codegen (forked from atrium-codegen). DO NOT EDIT. 2 //!Definitions for the `sh.weaver.notebook` namespace. 3 pub mod authors; 4 + pub mod book; 5 pub mod defs; 6 pub mod entry; 7 #[derive(Debug)] ··· 9 impl atrium_api::types::Collection for Authors { 10 const NSID: &'static str = "sh.weaver.notebook.authors"; 11 type Record = authors::Record; 12 + } 13 + #[derive(Debug)] 14 + pub struct Book; 15 + impl atrium_api::types::Collection for Book { 16 + const NSID: &'static str = "sh.weaver.notebook.book"; 17 + type Record = book::Record; 18 } 19 #[derive(Debug)] 20 pub struct Entry;
+8 -4
crates/weaver-common/src/lexicons/sh/weaver/notebook/authors.rs
··· 21 #[serde(skip_serializing_if = "core::option::Option::is_none")] 22 pub index: core::option::Option<i64>, 23 #[serde(skip_serializing_if = "core::option::Option::is_none")] 24 - pub profile: core::option::Option<atrium_api::types::Union<AuthorListItemProfileRefs>>, 25 } 26 pub type AuthorListItem = atrium_api::types::Object<AuthorListItemData>; 27 #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] 28 #[serde(tag = "$type")] 29 pub enum AuthorListItemProfileRefs { 30 #[serde(rename = "app.bsky.actor.defs#profileViewBasic")] 31 - AppBskyActorDefsProfileViewBasic(Box<crate::app::bsky::actor::defs::ProfileViewBasic>), 32 - #[serde(rename = "sh.weaver.actor.profile")] 33 - ShWeaverActorProfileMain(Box<crate::sh::weaver::actor::profile::Record>), 34 }
··· 21 #[serde(skip_serializing_if = "core::option::Option::is_none")] 22 pub index: core::option::Option<i64>, 23 #[serde(skip_serializing_if = "core::option::Option::is_none")] 24 + pub profile: core::option::Option< 25 + atrium_api::types::Union<AuthorListItemProfileRefs>, 26 + >, 27 } 28 pub type AuthorListItem = atrium_api::types::Object<AuthorListItemData>; 29 #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] 30 #[serde(tag = "$type")] 31 pub enum AuthorListItemProfileRefs { 32 #[serde(rename = "app.bsky.actor.defs#profileViewBasic")] 33 + AppBskyActorDefsProfileViewBasic( 34 + Box<crate::app::bsky::actor::defs::ProfileViewBasic>, 35 + ), 36 + #[serde(rename = "sh.weaver.actor.defs#profileView")] 37 + ShWeaverActorDefsProfileView(Box<crate::sh::weaver::actor::defs::ProfileView>), 38 }
+19
crates/weaver-common/src/lexicons/sh/weaver/notebook/book.rs
···
··· 1 + // @generated - This file is generated by esquema-codegen (forked from atrium-codegen). DO NOT EDIT. 2 + //!Definitions for the `sh.weaver.notebook.book` namespace. 3 + use atrium_api::types::TryFromUnknown; 4 + #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] 5 + #[serde(rename_all = "camelCase")] 6 + pub struct RecordData { 7 + pub authors: crate::sh::weaver::notebook::defs::AuthorListView, 8 + pub cid: atrium_api::types::string::Cid, 9 + #[serde(skip_serializing_if = "core::option::Option::is_none")] 10 + pub created_at: core::option::Option<atrium_api::types::string::Datetime>, 11 + pub entry_list: Vec<crate::sh::weaver::notebook::defs::BookEntryView>, 12 + pub uri: String, 13 + } 14 + pub type Record = atrium_api::types::Object<RecordData>; 15 + impl From<atrium_api::types::Unknown> for RecordData { 16 + fn from(value: atrium_api::types::Unknown) -> Self { 17 + Self::try_from_unknown(value).unwrap() 18 + } 19 + }
+36
crates/weaver-common/src/lexicons/sh/weaver/notebook/defs.rs
··· 2 //!Definitions for the `sh.weaver.notebook.defs` namespace. 3 #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] 4 #[serde(rename_all = "camelCase")] 5 pub struct EntryViewData { 6 pub cid: atrium_api::types::string::Cid, 7 pub uri: String, 8 } 9 pub type EntryView = atrium_api::types::Object<EntryViewData>;
··· 2 //!Definitions for the `sh.weaver.notebook.defs` namespace. 3 #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] 4 #[serde(rename_all = "camelCase")] 5 + pub struct AuthorListViewData { 6 + #[serde(skip_serializing_if = "core::option::Option::is_none")] 7 + pub cid: core::option::Option<atrium_api::types::string::Cid>, 8 + pub index: i64, 9 + #[serde(skip_serializing_if = "core::option::Option::is_none")] 10 + pub record: core::option::Option<atrium_api::types::Unknown>, 11 + #[serde(skip_serializing_if = "core::option::Option::is_none")] 12 + pub uri: core::option::Option<String>, 13 + } 14 + pub type AuthorListView = atrium_api::types::Object<AuthorListViewData>; 15 + #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] 16 + #[serde(rename_all = "camelCase")] 17 + pub struct BookEntryRefData { 18 + pub entry: EntryView, 19 + } 20 + pub type BookEntryRef = atrium_api::types::Object<BookEntryRefData>; 21 + ///An ordered entry in a Weaver notebook. 22 + #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] 23 + #[serde(rename_all = "camelCase")] 24 + pub struct BookEntryViewData { 25 + pub entry: EntryView, 26 + pub index: i64, 27 + #[serde(skip_serializing_if = "core::option::Option::is_none")] 28 + pub next: core::option::Option<BookEntryRef>, 29 + #[serde(skip_serializing_if = "core::option::Option::is_none")] 30 + pub prev: core::option::Option<BookEntryRef>, 31 + } 32 + pub type BookEntryView = atrium_api::types::Object<BookEntryViewData>; 33 + #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] 34 + #[serde(rename_all = "camelCase")] 35 pub struct EntryViewData { 36 + #[serde(skip_serializing_if = "core::option::Option::is_none")] 37 + pub authors: core::option::Option<AuthorListView>, 38 pub cid: atrium_api::types::string::Cid, 39 + pub indexed_at: atrium_api::types::string::Datetime, 40 + pub record: atrium_api::types::Unknown, 41 + #[serde(skip_serializing_if = "core::option::Option::is_none")] 42 + pub rendered_view: core::option::Option<RenderedView>, 43 pub uri: String, 44 } 45 pub type EntryView = atrium_api::types::Object<EntryViewData>;
+3 -32
crates/weaver-common/src/lib.rs
··· 1 pub mod error; 2 pub mod lexicons; 3 pub mod oauth; 4 pub use lexicons::*; 5 6 - use atrium_identity::handle::DnsTxtResolver; 7 - 8 pub use crate::error::{Error, IoError, ParseError, SerDeError}; 9 10 /// Canonical Cow for us, thanks Amos 11 pub use merde::CowStr; 12 - 13 - use hickory_resolver::TokioAsyncResolver; 14 15 /// too many cows, so we have conversions 16 pub fn mcow_to_cow(cow: CowStr<'_>) -> std::borrow::Cow<'_, str> { ··· 36 markdown_weaver::CowStr::Inlined(s) => std::borrow::Cow::Owned(s.as_ref().to_owned()), 37 } 38 } 39 - 40 - pub struct HickoryDnsTxtResolver { 41 - resolver: TokioAsyncResolver, 42 - } 43 - 44 - impl Default for HickoryDnsTxtResolver { 45 - fn default() -> Self { 46 - Self { 47 - resolver: TokioAsyncResolver::tokio_from_system_conf() 48 - .expect("failed to create resolver"), 49 - } 50 - } 51 - } 52 - 53 - impl DnsTxtResolver for HickoryDnsTxtResolver { 54 - async fn resolve( 55 - &self, 56 - query: &str, 57 - ) -> core::result::Result<Vec<String>, Box<dyn std::error::Error + Send + Sync + 'static>> { 58 - Ok(self 59 - .resolver 60 - .txt_lookup(query) 61 - .await? 62 - .iter() 63 - .map(|txt| txt.to_string()) 64 - .collect()) 65 - } 66 - }
··· 1 + pub mod client; 2 + pub mod config; 3 pub mod error; 4 pub mod lexicons; 5 pub mod oauth; 6 + pub mod resolver; 7 pub use lexicons::*; 8 9 pub use crate::error::{Error, IoError, ParseError, SerDeError}; 10 11 /// Canonical Cow for us, thanks Amos 12 pub use merde::CowStr; 13 14 /// too many cows, so we have conversions 15 pub fn mcow_to_cow(cow: CowStr<'_>) -> std::borrow::Cow<'_, str> { ··· 35 markdown_weaver::CowStr::Inlined(s) => std::borrow::Cow::Owned(s.as_ref().to_owned()), 36 } 37 }
+1 -1
crates/weaver-common/src/oauth.rs
··· 15 16 use std::sync::Arc; 17 18 - use crate::HickoryDnsTxtResolver; 19 20 pub fn default_oauth_client( 21 url: impl AsRef<str>,
··· 15 16 use std::sync::Arc; 17 18 + use crate::resolver::HickoryDnsTxtResolver; 19 20 pub fn default_oauth_client( 21 url: impl AsRef<str>,
+30
crates/weaver-common/src/resolver.rs
···
··· 1 + use atrium_identity::handle::DnsTxtResolver; 2 + use hickory_resolver::TokioAsyncResolver; 3 + 4 + pub struct HickoryDnsTxtResolver { 5 + resolver: TokioAsyncResolver, 6 + } 7 + 8 + impl Default for HickoryDnsTxtResolver { 9 + fn default() -> Self { 10 + Self { 11 + resolver: TokioAsyncResolver::tokio_from_system_conf() 12 + .expect("failed to create resolver"), 13 + } 14 + } 15 + } 16 + 17 + impl DnsTxtResolver for HickoryDnsTxtResolver { 18 + async fn resolve( 19 + &self, 20 + query: &str, 21 + ) -> core::result::Result<Vec<String>, Box<dyn std::error::Error + Send + Sync + 'static>> { 22 + Ok(self 23 + .resolver 24 + .txt_lookup(query) 25 + .await? 26 + .iter() 27 + .map(|txt| txt.to_string()) 28 + .collect()) 29 + } 30 + }
+12
docs/oauth/client-metadata.json
···
··· 1 + { 2 + "client_id": "https://appview.weaver.sh/oauth/client-metadata.json", 3 + "application_type": "native", 4 + "client_name": "Weaver Appview", 5 + "client_uri": "https://appview.weaver.sh", 6 + "dpop_bound_access_tokens": true, 7 + "grant_types": ["authorization_code", "refresh_token"], 8 + "redirect_uris": ["http://127.0.0.1:8080/oauth/callback"], 9 + "response_types": ["code"], 10 + "scope": "atproto transition:generic", 11 + "token_endpoint_auth_method": "none" 12 + }
+1
flake.nix
··· 256 weaver-server = flake-utils.lib.mkApp { 257 drv = weaver-server; 258 }; 259 }; 260 261 devShells.default = craneLib.devShell {
··· 256 weaver-server = flake-utils.lib.mkApp { 257 drv = weaver-server; 258 }; 259 + 260 }; 261 262 devShells.default = craneLib.devShell {
+2
lexicon-codegen.sh
··· 2 3 4 #cargo install esquema-cli --locked --git https://github.com/fatfingers23/esquema.git 5 mkdir -p ./target/lexicons 6 cp -r ./lexicons ./target/lexicons 7 cp -r ./atproto/lexicons ./target/lexicons 8
··· 2 3 4 #cargo install esquema-cli --locked --git https://github.com/fatfingers23/esquema.git 5 + rm -rf ./target/lexicons 6 mkdir -p ./target/lexicons 7 + 8 cp -r ./lexicons ./target/lexicons 9 cp -r ./atproto/lexicons ./target/lexicons 10
lexicons/sh.tangled/actor.profile.json lexicons/sh/tangled/actor.profile.json
+16 -1
lexicons/sh.weaver/actor/defs.json lexicons/sh/weaver/actor/defs.json
··· 19 "maxLength": 2560 20 }, 21 "avatar": { "type": "string", "format": "uri" }, 22 - 23 "indexedAt": { "type": "string", "format": "datetime" }, 24 "createdAt": { "type": "string", "format": "datetime" }, 25
··· 19 "maxLength": 2560 20 }, 21 "avatar": { "type": "string", "format": "uri" }, 22 + "location": { 23 + "type": "string", 24 + "description": "Free-form location text.", 25 + "maxGraphemes": 40, 26 + "maxLength": 400 27 + }, 28 + "links": { 29 + "type": "array", 30 + "minLength": 0, 31 + "maxLength": 5, 32 + "items": { 33 + "type": "string", 34 + "description": "Any URI, intended for social profiles or websites, can be used to link DIDs/AT-URIs too.", 35 + "format": "uri" 36 + } 37 + }, 38 "indexedAt": { "type": "string", "format": "datetime" }, 39 "createdAt": { "type": "string", "format": "datetime" }, 40
lexicons/sh.weaver/actor/profile.json lexicons/sh/weaver/actor/profile.json
lexicons/sh.weaver/embed/defs.json lexicons/sh/weaver/embed/defs.json
lexicons/sh.weaver/embed/external.json lexicons/sh/weaver/embed/external.json
lexicons/sh.weaver/embed/images.json lexicons/sh/weaver/embed/images.json
lexicons/sh.weaver/embed/recordWithMedia.json lexicons/sh/weaver/embed/recordWithMedia.json
lexicons/sh.weaver/embed/records.json lexicons/sh/weaver/embed/records.json
lexicons/sh.weaver/embed/video.json lexicons/sh/weaver/embed/video.json
+1 -1
lexicons/sh.weaver/notebook/authors.json lexicons/sh/weaver/notebook/authors.json
··· 28 "properties": { 29 "profile": { 30 "type": "union", 31 - "refs": ["app.bsky.actor.defs#profileViewBasic", "sh.weaver.actor.profile", "sh.weaver.actor.profile"] 32 }, 33 "index": { "type": "integer" } 34 }
··· 28 "properties": { 29 "profile": { 30 "type": "union", 31 + "refs": ["app.bsky.actor.defs#profileViewBasic", "sh.weaver.actor.defs#profileView"] 32 }, 33 "index": { "type": "integer" } 34 }
-46
lexicons/sh.weaver/notebook/defs.json
··· 1 - { 2 - "lexicon": 1, 3 - "id": "sh.weaver.notebook.defs", 4 - "defs": { 5 - "entryView": { 6 - "type": "object", 7 - "required": ["uri", "cid", "author", "record", "indexedAt"], 8 - "authors": { 9 - "type": "array", 10 - "items": { 11 - "type": "union", 12 - "refs": ["app.bsky.actor.defs#profileViewBasic"] 13 - } 14 - }, 15 - "properties": { 16 - "uri": { "type": "string", "format": "at-uri" }, 17 - "cid": { "type": "string", "format": "cid" } 18 - }, 19 - 20 - "record": { "type": "unknown" }, 21 - "renderedView": { 22 - "type": "ref", 23 - "ref": "#renderedView" 24 - }, 25 - "indexedAt": { "type": "string", "format": "datetime" } 26 - }, 27 - 28 - "renderedView": { 29 - "type": "object", 30 - "required": ["html"], 31 - "properties": { 32 - "html": { 33 - "type": "blob", 34 - "accept": ["text/html"], 35 - "maxSize": 1000000 36 - }, 37 - "css": { 38 - "type": "blob", 39 - "accept": ["text/css"], 40 - "maxSize": 1000000 41 - } 42 - }, 43 - "description": "View of a rendered and cached notebook entry" 44 - } 45 - } 46 - }
···
lexicons/sh.weaver/notebook/entry.json lexicons/sh/weaver/notebook/entry.json
+31
lexicons/sh/weaver/notebook/book.json
···
··· 1 + { 2 + "lexicon": 1, 3 + "id": "sh.weaver.notebook.book", 4 + "defs": { 5 + "main": { 6 + "type": "record", 7 + "description": "Authors of a Weaver notebook.", 8 + "key": "tid", 9 + "record": { 10 + "type": "object", 11 + "required": ["cid", "uri", "authors", "entryList"], 12 + "properties": { 13 + "uri": { "type": "string", "format": "at-uri" }, 14 + "cid": { "type": "string", "format": "cid" }, 15 + "authors": { 16 + "type": "ref", 17 + "ref": "sh.weaver.notebook.defs#authorListView" 18 + }, 19 + "entryList": { 20 + "type": "array", 21 + "items": { 22 + "type": "ref", 23 + "ref": "sh.weaver.notebook.defs#bookEntryView" 24 + } 25 + }, 26 + "createdAt": { "type": "string", "format": "datetime" } 27 + } 28 + } 29 + } 30 + } 31 + }
+84
lexicons/sh/weaver/notebook/defs.json
···
··· 1 + { 2 + "lexicon": 1, 3 + "id": "sh.weaver.notebook.defs", 4 + "defs": { 5 + "entryView": { 6 + "type": "object", 7 + "required": ["uri", "cid", "author", "record", "indexedAt"], 8 + 9 + "properties": { 10 + "uri": { "type": "string", "format": "at-uri" }, 11 + "cid": { "type": "string", "format": "cid" }, 12 + "authors": { 13 + "type": "ref", 14 + "ref": "#authorListView" 15 + }, 16 + "record": { "type": "unknown" }, 17 + "renderedView": { 18 + "type": "ref", 19 + "ref": "#renderedView" 20 + }, 21 + "indexedAt": { "type": "string", "format": "datetime" } 22 + } 23 + }, 24 + 25 + "renderedView": { 26 + "type": "object", 27 + "required": ["html"], 28 + "properties": { 29 + "html": { 30 + "type": "blob", 31 + "accept": ["text/html"], 32 + "maxSize": 1000000 33 + }, 34 + "css": { 35 + "type": "blob", 36 + "accept": ["text/css"], 37 + "maxSize": 1000000 38 + } 39 + }, 40 + "description": "View of a rendered and cached notebook entry" 41 + }, 42 + "authorListView": { 43 + "type": "object", 44 + "required": ["profile", "index"], 45 + "properties": { 46 + "uri": { "type": "string", "format": "at-uri" }, 47 + "cid": { "type": "string", "format": "cid" }, 48 + "record": { "type": "unknown" }, 49 + "index": { "type": "integer" } 50 + } 51 + }, 52 + 53 + "bookEntryView": { 54 + "type": "object", 55 + "description": "An ordered entry in a Weaver notebook.", 56 + "required": ["entry", "index"], 57 + "properties": { 58 + "entry": { 59 + "type": "ref", 60 + "ref": "#entryView" 61 + }, 62 + "index": { "type": "integer" }, 63 + "next": { 64 + "type": "ref", 65 + "ref": "#bookEntryRef" 66 + }, 67 + "prev": { 68 + "type": "ref", 69 + "ref": "#bookEntryRef" 70 + } 71 + } 72 + }, 73 + "bookEntryRef": { 74 + "type": "object", 75 + "required": ["entry"], 76 + "properties": { 77 + "entry": { 78 + "type": "ref", 79 + "ref": "#entryView" 80 + } 81 + } 82 + } 83 + } 84 + }