don't
5
fork

Configure Feed

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

feat: update lexicons

* Remove tedious lifetimes on XRPC input types.
* Update lexicon definitions to match upstream.

Signed-off-by: tjh <x@tjh.dev>

tjh 8db169fc 881cf715

+283 -233
+1 -3
crates/knot/src/lib.rs
··· 74 74 75 75 #[cfg(test)] 76 76 mod tests { 77 - use std::borrow::Cow; 78 - 79 77 use atproto::{Did, tid::Tid}; 80 78 use auth::jwt::Claims; 81 79 ··· 260 262 261 263 // Generate the body of the 'sh.tangled.repo.create' request. 262 264 let create = lexicon::sh_tangled::repo::create::Input { 263 - rkey: Cow::Borrowed(&rkey), 265 + rkey: rkey.to_string(), 264 266 default_branch: Some("main".into()), 265 267 source: None, 266 268 };
+1 -1
crates/knot/src/model/convert.rs
··· 82 82 }; 83 83 84 84 tree::TreeEntry { 85 - mode: Cow::Borrowed(mode_string), 85 + mode: mode_string, 86 86 name, 87 87 ..Default::default() 88 88 }
+1 -1
crates/knot/src/model/repository.rs
··· 398 398 ) -> XrpcResult<Json<tree::Output>> { 399 399 let (tip, immutable) = self.resolve_revspec(params.rev.as_deref())?; 400 400 let dotdot = params.path.clone().and_then(|mut path| { 401 - path.to_mut().pop(); 401 + path.pop(); 402 402 match path.as_os_str().is_empty() { 403 403 true => None, 404 404 false => Some(path),
+1 -1
crates/knot/src/public/xrpc/sh_tangled/repo/impl_branches.rs
··· 12 12 #[tracing::instrument(target = "sh_tangled::repo::branches", skip(knot, repository), err)] 13 13 pub async fn handle( 14 14 State(knot): State<Knot>, 15 - XrpcQuery(params): XrpcQuery<Input<'static>>, 15 + XrpcQuery(params): XrpcQuery<Input>, 16 16 repository: TangledRepository, 17 17 ) -> XrpcResult<Json<Output>> { 18 18 knot.pool()
+1 -1
crates/knot/src/public/xrpc/sh_tangled/repo/impl_compare.rs
··· 12 12 #[tracing::instrument(target = "sh_tangled::repo::compare", skip(knot, repository), err)] 13 13 pub async fn handle( 14 14 State(knot): State<Knot>, 15 - XrpcQuery(params): XrpcQuery<Input<'static>>, 15 + XrpcQuery(params): XrpcQuery<Input>, 16 16 repository: TangledRepository, 17 17 ) -> XrpcResult<Json<Output>> { 18 18 knot.pool()
+1 -1
crates/knot/src/public/xrpc/sh_tangled/repo/impl_create.rs
··· 29 29 pub async fn handle( 30 30 State(knot): State<Knot>, 31 31 authorization: Authorization<CreateVerification>, 32 - Json(params): Json<Input<'static>>, 32 + Json(params): Json<Input>, 33 33 ) -> XrpcResult<()> { 34 34 use crate::services::rbac::{Action, Policy, PolicyResult::*, RepositoryCreatePolicy}; 35 35
+1 -1
crates/knot/src/public/xrpc/sh_tangled/repo/impl_diff.rs
··· 12 12 #[tracing::instrument(target = "sh_tangled::repo::diff", skip(knot, repository), err)] 13 13 pub async fn handle( 14 14 State(knot): State<Knot>, 15 - XrpcQuery(params): XrpcQuery<Input<'static>>, 15 + XrpcQuery(params): XrpcQuery<Input>, 16 16 repository: TangledRepository, 17 17 ) -> XrpcResult<Json<Output>> { 18 18 knot.pool()
+1 -1
crates/knot/src/public/xrpc/sh_tangled/repo/impl_get_default_branch.rs
··· 16 16 )] 17 17 pub async fn handle( 18 18 State(knot): State<Knot>, 19 - XrpcQuery(params): XrpcQuery<Input<'static>>, 19 + XrpcQuery(params): XrpcQuery<Input>, 20 20 repository: TangledRepository, 21 21 ) -> XrpcResult<Json<Output>> { 22 22 knot.pool()
+1 -1
crates/knot/src/public/xrpc/sh_tangled/repo/impl_languages.rs
··· 12 12 #[tracing::instrument(target = "sh_tangled::repo::languages", skip(knot, repository), err)] 13 13 pub async fn handle( 14 14 State(knot): State<Knot>, 15 - XrpcQuery(params): XrpcQuery<Input<'static>>, 15 + XrpcQuery(params): XrpcQuery<Input>, 16 16 repository: TangledRepository, 17 17 ) -> XrpcResult<Json<Output>> { 18 18 knot.pool()
+1 -1
crates/knot/src/public/xrpc/sh_tangled/repo/impl_log.rs
··· 12 12 #[tracing::instrument(target = "sh_tangled::repo::log", skip(knot, repository), err)] 13 13 pub async fn handle( 14 14 State(knot): State<Knot>, 15 - XrpcQuery(params): XrpcQuery<Input<'static>>, 15 + XrpcQuery(params): XrpcQuery<Input>, 16 16 repository: TangledRepository, 17 17 ) -> XrpcResult<Json<Output>> { 18 18 knot.pool()
+1 -1
crates/knot/src/public/xrpc/sh_tangled/repo/impl_tags.rs
··· 12 12 #[tracing::instrument(target = "sh_tangled::repo::tags", skip(knot, repository), err)] 13 13 pub async fn handle( 14 14 State(knot): State<Knot>, 15 - XrpcQuery(params): XrpcQuery<Input<'static>>, 15 + XrpcQuery(params): XrpcQuery<Input>, 16 16 repository: TangledRepository, 17 17 ) -> XrpcResult<Json<Output>> { 18 18 knot.pool()
+1 -1
crates/knot/src/public/xrpc/sh_tangled/repo/impl_tree.rs
··· 12 12 #[tracing::instrument(target = "sh_tangled::repo::tree", skip(knot, repository), err)] 13 13 pub async fn handle( 14 14 State(knot): State<Knot>, 15 - XrpcQuery(params): XrpcQuery<Input<'static>>, 15 + XrpcQuery(params): XrpcQuery<Input>, 16 16 repository: TangledRepository, 17 17 ) -> XrpcResult<Json<Output>> { 18 18 let cloned_knot = knot.clone();
+2 -2
crates/knot/src/services/atrepo.rs
··· 28 28 mut callback: F, 29 29 ) -> Result<(), Error<E>> 30 30 where 31 - F: AsyncFnMut(&[list_records::Record<'_>]) -> Result<(), E> + Send + 'static, 31 + F: AsyncFnMut(&[list_records::Record]) -> Result<(), E> + Send + 'static, 32 32 { 33 33 use url::Url; 34 34 ··· 73 73 complete = parsed 74 74 .records 75 75 .last() 76 - .is_none_or(|last| last.uri.rkey == cursor.as_deref()); 76 + .is_none_or(|last| Some(last.uri.rkey.as_str()) == cursor.as_deref()); 77 77 78 78 callback(&parsed.records).await.map_err(Error::Callback)?; 79 79 }
+7 -2
crates/knot/src/services/jetstream.rs
··· 345 345 )); 346 346 }; 347 347 348 - let repo_did = coll.repo.did().unwrap(); 349 - let repo_rkey = coll.repo.rkey.unwrap(); 348 + let repo_did = coll.repo.authority(); 349 + let repo_rkey = coll.repo.rkey(); 350 + if coll.repo.collection() != "sh.tangled.repo" { 351 + return Err(anyhow::anyhow!( 352 + "repo parameter in should refer to a 'sh.tangled.repo'" 353 + )); 354 + } 350 355 351 356 let policy = AddCollaboratorPolicy; 352 357 let repository = RepositoryRef::new(repo_did, repo_rkey);
+13 -25
crates/knot/src/services/seed.rs
··· 1 1 use atproto::{Did, tid::Tid}; 2 - use lexicon::sh_tangled::{knot::Member, repo::Repo}; 2 + use lexicon::{ 3 + com::atproto::repo::list_records::Record, 4 + sh_tangled::{knot::Member, repo::Repo}, 5 + }; 3 6 4 7 use crate::{model::Knot, services::atrepo, types::RecordKey}; 5 8 ··· 20 17 { 21 18 let knot = knot.clone(); 22 19 async move |records| { 23 - for record in records { 24 - let rkey = record 25 - .uri 26 - .rkey 27 - .ok_or(anyhow::anyhow!("record uri missing rkey"))?; 28 - 29 - let Ok(member) = serde_json::from_str::<Member>(record.value.get()) else { 20 + for Record { uri, cid, value } in records { 21 + let Ok(member) = serde_json::from_str::<Member>(value.get()) else { 30 22 continue; 31 23 }; 32 24 33 - knot.add_member(rkey, &rev, &record.cid, &member).await?; 25 + knot.add_member(uri.rkey(), &rev, &cid, &member).await?; 34 26 } 35 27 Ok(()) 36 28 } ··· 48 50 { 49 51 let knot = knot.clone(); 50 52 async move |records| { 51 - for record in records { 52 - let rkey = record 53 - .uri 54 - .rkey 55 - .ok_or(anyhow::anyhow!("record uri missing rkey"))?; 56 - 57 - let Ok(public_key) = serde_json::from_str(record.value.get()) else { 53 + for Record { uri, cid, value } in records { 54 + let Ok(public_key) = serde_json::from_str(value.get()) else { 58 55 continue; 59 56 }; 60 57 61 58 knot.database() 62 - .upsert_public_key(&did, rkey, &rev, &record.cid, &public_key) 59 + .upsert_public_key(&did, uri.rkey(), &rev, &cid, &public_key) 63 60 .await?; 64 61 65 - tracing::info!(?did, ?rkey, "new public key"); 62 + tracing::info!(?did, ?uri, "new public key"); 66 63 } 67 64 68 65 Ok(()) ··· 81 88 let knot = knot.clone(); 82 89 async move |records| { 83 90 for record in records { 84 - let rkey = record 85 - .uri 86 - .rkey 87 - .ok_or(anyhow::anyhow!("record uri missing rkey"))?; 88 - 89 91 let Ok(repo) = serde_json::from_str::<Repo>(record.value.get()) else { 90 - tracing::error!(?record, "error parsing record"); 92 + tracing::error!(value = ?record.value, "error parsing record value"); 91 93 continue; 92 94 }; 93 95 ··· 96 108 continue; 97 109 } 98 110 99 - tracing::info!(?did, ?rkey, name = %repo.name, "new repository"); 111 + tracing::info!(?did, uri= ?record.uri, name = %repo.name, "new repository"); 100 112 } 101 113 102 114 Ok(())
+8 -12
crates/knot/src/types.rs
··· 1 1 use core::fmt; 2 2 3 - use atproto::Did; 3 + use atproto::{Did, RecordUri}; 4 4 use lexicon::com::atproto::repo::list_records::Record; 5 5 6 6 pub mod push_certificate; ··· 34 34 35 35 impl core::error::Error for FromRecordError {} 36 36 37 - impl<'a> TryFrom<&'a Record<'a>> for RecordKey<'a> { 37 + impl<'a> TryFrom<&'a Record> for RecordKey<'a> { 38 38 type Error = FromRecordError; 39 - fn try_from(value: &'a Record<'a>) -> Result<Self, Self::Error> { 40 - let did = value 41 - .uri 42 - .did() 43 - .ok_or("'uri' does not have a did authority")?; 44 - let collection = value 45 - .uri 46 - .collection_str() 47 - .ok_or("'uri' does not declare a collection")?; 48 - let rkey = value.uri.rkey.ok_or("'uri' does not declare an rkey")?; 39 + fn try_from(value: &'a Record) -> Result<Self, Self::Error> { 40 + let RecordUri { 41 + authority: did, 42 + collection, 43 + rkey, 44 + } = &value.uri; 49 45 50 46 Ok(Self { 51 47 did,
+15 -22
crates/lexicon/src/com/atproto/repo.rs
··· 5 5 //! 6 6 //! <https://docs.bsky.app/docs/api/com-atproto-repo-list-records> 7 7 //! 8 - use atproto::aturi::AtUri; 9 - use serde::{Deserialize, Serialize}; 10 - use serde_json::value::RawValue; 11 - use std::borrow::Cow; 8 + use atproto::RecordUri; 12 9 13 - #[derive(Debug, Deserialize, Serialize)] 14 - pub struct Input<'a> { 10 + #[derive(Debug, serde::Deserialize, serde::Serialize)] 11 + pub struct Input { 15 12 /// The handle or DID of the repo. 16 - #[serde(borrow)] 17 - pub repo: Cow<'a, str>, 13 + pub repo: String, 18 14 19 15 /// The NSID of the record type. 20 - #[serde(borrow)] 21 - pub collection: Cow<'a, str>, 16 + pub collection: String, 22 17 23 18 /// The number of records to return. 24 19 /// ··· 21 26 #[serde(skip_serializing_if = "Option::is_none")] 22 27 pub limit: Option<usize>, 23 28 24 - #[serde(borrow, skip_serializing_if = "Option::is_none")] 25 - pub cursor: Option<Cow<'a, str>>, 29 + #[serde(skip_serializing_if = "Option::is_none")] 30 + pub cursor: Option<String>, 26 31 27 32 /// Flag to reverse the order of the returned records. 28 33 #[serde(default)] 29 34 pub reverse: bool, 30 35 } 31 36 32 - #[derive(Debug, Deserialize, Serialize)] 33 - pub struct Output<'a> { 34 - #[serde(borrow)] 35 - pub cursor: Option<&'a str>, 37 + #[derive(Debug, serde::Deserialize, serde::Serialize)] 38 + pub struct Output { 39 + pub cursor: Option<String>, 36 40 37 - pub records: Vec<Record<'a>>, 41 + pub records: Vec<Record>, 38 42 } 39 43 40 - #[derive(Debug, Deserialize, Serialize)] 41 - pub struct Record<'a> { 42 - #[serde(borrow)] 43 - pub uri: AtUri<'a>, 44 + #[derive(Debug, serde::Deserialize, serde::Serialize)] 45 + pub struct Record { 46 + pub uri: RecordUri, 44 47 45 48 pub cid: String, 46 49 47 - pub value: &'a RawValue, 50 + pub value: Box<serde_json::value::RawValue>, 48 51 } 49 52 }
+2 -2
crates/lexicon/src/extra/objectid.rs
··· 11 11 /// 12 12 /// This only exists because knotserver uses both representations. 13 13 /// 14 - #[derive(Clone, PartialEq, Eq)] 14 + #[derive(Clone, Hash, PartialEq, Eq)] 15 15 pub struct ObjectId<E = Hex> { 16 16 inner: gix_hash::ObjectId, 17 17 enc: PhantomData<E>, 18 18 } 19 19 20 20 impl<E> ObjectId<E> { 21 - #[must_use] 21 + #[must_use] 22 22 pub const fn id(&self) -> gix_hash::ObjectId { 23 23 self.inner 24 24 }
+3 -3
crates/lexicon/src/lib.rs
··· 19 19 #[serde(rename = "sh.tangled.feed.reaction")] 20 20 FeedReaction(sh_tangled::feed::Reaction<'a>), 21 21 #[serde(rename = "sh.tangled.feed.star")] 22 - FeedStar(sh_tangled::feed::Star<'a>), 22 + FeedStar(sh_tangled::feed::Star), 23 23 #[serde(rename = "sh.tangled.graph.follow")] 24 24 GraphFollow(sh_tangled::graph::Follow<'a>), 25 25 #[serde(rename = "sh.tangled.knot")] ··· 42 42 RepoPull(sh_tangled::repo::Pull<'a>), 43 43 #[serde(rename = "sh.tangled.repo.pull.comment")] 44 44 RepoPullComment(sh_tangled::repo::pull::Comment<'a>), 45 - #[serde(rename = "sh.tangled.repo.pull.state")] 46 - RepoPullstate(sh_tangled::repo::pull::State<'a>), 45 + #[serde(rename = "sh.tangled.repo.pull.status")] 46 + RepoPullstate(sh_tangled::repo::pull::Status<'a>), 47 47 #[serde(rename = "sh.tangled.spindle")] 48 48 Spindle(sh_tangled::spindle::Spindle), 49 49 #[serde(rename = "sh.tangled.spindle.member")]
+48 -2
crates/lexicon/src/sh_tangled/actor.rs
··· 1 1 //! 2 - //! <https://tangled.org/@tangled.org/core/tree/master/lexicons/actor> 2 + //! <https://tangled.org/tangled.org/core/tree/master/lexicons/actor> 3 3 //! 4 4 use serde::{Deserialize, Serialize}; 5 5 use std::borrow::Cow; 6 6 7 7 /// `sh.tangled.actor.profile` record. 8 8 /// 9 - /// <https://tangled.org/@tangled.org/core/blob/master/lexicons/actor/profile.json> 9 + /// <https://tangled.org/tangled.org/core/blob/master/lexicons/actor/profile.json> 10 10 #[derive(Debug, Hash, PartialEq, Eq, Deserialize, Serialize)] 11 11 #[serde(rename_all = "camelCase")] 12 12 pub struct Profile<'a> { ··· 43 43 OpenIssueCount, 44 44 ClosedIssueCount, 45 45 RepositoryCount, 46 + } 47 + 48 + #[test] 49 + fn can_deserialize_profile() -> Result<(), serde_json::Error> { 50 + #[derive(serde::Deserialize)] 51 + #[serde(tag = "$type")] 52 + #[allow(unused)] 53 + enum Lexicon<'a> { 54 + #[serde(rename = "sh.tangled.actor.profile")] 55 + Profile(#[serde(borrow)] Profile<'a>), 56 + } 57 + 58 + fn check(s: &str) -> Result<Lexicon<'_>, serde_json::Error> { 59 + serde_json::from_str(s) 60 + } 61 + 62 + check( 63 + r#"{ 64 + "$type": "sh.tangled.actor.profile", 65 + "links": [ 66 + "", 67 + "", 68 + "", 69 + "", 70 + "" 71 + ], 72 + "stats": [ 73 + "open-pull-request-count", 74 + "merged-pull-request-count" 75 + ], 76 + "bluesky": true, 77 + "location": "", 78 + "pronouns": "", 79 + "description": "🦀🦀🦀", 80 + "pinnedRepositories": [ 81 + "at://did:plc:65gha4t3avpfpzmvpbwovss7/sh.tangled.repo/3lyfsjdjwpa22", 82 + "at://did:plc:65gha4t3avpfpzmvpbwovss7/sh.tangled.repo/3ln3mfrahwk22", 83 + "at://did:plc:65gha4t3avpfpzmvpbwovss7/sh.tangled.repo/3lmz4rgwyxm22", 84 + "at://did:plc:65gha4t3avpfpzmvpbwovss7/sh.tangled.repo/3m24udbjajf22", 85 + "at://did:plc:65gha4t3avpfpzmvpbwovss7/sh.tangled.repo/3lztqm3elpo22", 86 + "" 87 + ] 88 + }"#, 89 + )?; 90 + 91 + Ok(()) 46 92 }
+4 -5
crates/lexicon/src/sh_tangled/feed.rs
··· 1 1 //! 2 2 //! <https://tangled.org/@tangled.org/core/tree/master/lexicons/feed> 3 3 //! 4 + use atproto::RecordUri; 4 5 use serde::{Deserialize, Serialize}; 5 6 use std::borrow::Cow; 6 7 use time::OffsetDateTime; ··· 18 17 #[serde(borrow)] 19 18 pub reaction: Cow<'a, str>, 20 19 21 - // @TODO This should be an at-uri. 22 - pub subject: Cow<'a, str>, 20 + pub subject: Cow<'a, RecordUri>, 23 21 } 24 22 25 23 /// `sh.tangled.feed.star` record. ··· 26 26 /// <https://tangled.org/@tangled.org/core/blob/master/lexicons/feed/star.json> 27 27 #[derive(Debug, Hash, PartialEq, Eq, Deserialize, Serialize)] 28 28 #[serde(rename_all = "camelCase")] 29 - pub struct Star<'a> { 29 + pub struct Star { 30 30 #[serde(alias = "created", with = "time::serde::rfc3339")] 31 31 pub created_at: OffsetDateTime, 32 32 33 33 // @TODO This should be an at-uri. 34 - #[serde(borrow)] 35 - pub subject: Cow<'a, str>, 34 + pub subject: RecordUri, 36 35 }
+40 -8
crates/lexicon/src/sh_tangled/graph.rs
··· 1 1 //! 2 - //! <https://tangled.org/@tangled.org/core/tree/master/lexicons/graph> 2 + //! <https://tangled.org/tangled.org/core/tree/master/lexicons/graph> 3 3 //! 4 - use serde::{Deserialize, Serialize}; 5 4 use std::borrow::Cow; 6 - use time::OffsetDateTime; 7 5 8 6 /// `sh.tangled.graph.follow` record. 9 7 /// 10 - /// <https://tangled.org/@tangled.org/core/blob/master/lexicons/graph/follow.json> 11 - #[derive(Debug, Hash, PartialEq, Eq, Deserialize, Serialize)] 8 + /// See: <https://tangled.org/tangled.org/core/blob/master/lexicons/graph/follow.json> 9 + /// 10 + #[derive(Debug, Hash, PartialEq, Eq, serde::Deserialize, serde::Serialize)] 12 11 #[serde(rename_all = "camelCase")] 13 12 pub struct Follow<'a> { 14 - #[serde(borrow)] 15 - pub subject: Cow<'a, str>, 13 + #[serde(borrow, with = "atproto::serde::cow_did")] 14 + pub subject: Cow<'a, atproto::Did>, 16 15 17 16 #[serde(with = "time::serde::rfc3339")] 18 - pub created_at: OffsetDateTime, 17 + pub created_at: time::OffsetDateTime, 18 + } 19 + 20 + #[test] 21 + fn can_deserialize_follow() -> Result<(), serde_json::Error> { 22 + #[derive(serde::Deserialize)] 23 + #[serde(tag = "$type")] 24 + #[allow(unused)] 25 + enum Lexicon<'a> { 26 + #[serde(rename = "sh.tangled.graph.follow")] 27 + Follow(#[serde(borrow)] Follow<'a>), 28 + } 29 + 30 + fn check(s: &str) -> Result<Lexicon<'_>, serde_json::Error> { 31 + serde_json::from_str(s) 32 + } 33 + 34 + check( 35 + r#"{ 36 + "$type": "sh.tangled.graph.follow", 37 + "subject": "did:plc:wshs7t2adsemcrrd4snkeqli", 38 + "createdAt":"2025-04-17T12:37:42Z" 39 + }"#, 40 + )?; 41 + 42 + check( 43 + r#"{ 44 + "$type": "sh.tangled.graph.follow", 45 + "subject": "did:plc:xasnlahkri4ewmbuzly2rlc5", 46 + "createdAt": "2026-01-16T14:39:26+02:00" 47 + }"#, 48 + )?; 49 + 50 + Ok(()) 19 51 }
+28 -19
crates/lexicon/src/sh_tangled/repo.rs
··· 16 16 pub mod tags; 17 17 pub mod tree; 18 18 19 - use atproto::{Did, aturi::AtUri}; 19 + use atproto::{Did, OwnedDid, RecordUri}; 20 20 use serde::{Deserialize, Serialize}; 21 21 use std::borrow::Cow; 22 22 use time::OffsetDateTime; 23 + 24 + use crate::extra::objectid::ObjectId; 23 25 24 26 /// `sh.tangled.repo.issue` record. 25 27 /// ··· 33 31 pub subject: &'a Did, 34 32 35 33 /// Domain that this member now belongs to 36 - pub repo: AtUri<'a>, 34 + pub repo: RecordUri, 37 35 38 36 #[serde(with = "time::serde::rfc3339")] 39 37 pub created_at: OffsetDateTime, ··· 45 43 #[derive(Debug, Hash, PartialEq, Eq, Deserialize, Serialize)] 46 44 #[serde(rename_all = "camelCase")] 47 45 pub struct Issue<'a> { 48 - #[serde(borrow)] 49 - pub repo: Cow<'a, str>, 46 + pub repo: RecordUri, 50 47 48 + #[serde(borrow)] 51 49 pub title: Cow<'a, str>, 50 + 51 + #[serde(with = "time::serde::rfc3339")] 52 + pub created_at: OffsetDateTime, 52 53 53 54 #[serde(skip_serializing_if = "Option::is_none")] 54 55 pub body: Option<Cow<'a, str>>, 55 56 56 - #[serde(with = "time::serde::rfc3339")] 57 - pub created_at: OffsetDateTime, 57 + #[serde(skip_serializing_if = "Vec::is_empty")] 58 + pub mentions: Vec<OwnedDid>, 59 + 60 + #[serde(skip_serializing_if = "Vec::is_empty")] 61 + pub references: Vec<RecordUri>, 58 62 } 59 63 60 64 /// `sh.tangled.repo` record. ··· 93 85 pub source: Option<Cow<'a, str>>, 94 86 95 87 /// List of labels that this repo subscribes to 96 - // @NOTE These should be at-uris. 97 88 #[serde(default, skip_serializing_if = "Vec::is_empty")] 98 - pub labels: Vec<Cow<'a, str>>, 89 + pub labels: Vec<RecordUri>, 99 90 100 91 #[serde(with = "time::serde::rfc3339")] 101 92 #[serde(alias = "addedAt")] ··· 122 115 123 116 #[serde(with = "time::serde::rfc3339")] 124 117 pub created_at: OffsetDateTime, 118 + 119 + #[serde(skip_serializing_if = "Vec::is_empty")] 120 + pub mentions: Vec<OwnedDid>, 121 + 122 + #[serde(skip_serializing_if = "Vec::is_empty")] 123 + pub references: Vec<RecordUri>, 125 124 } 126 125 127 126 #[derive(Debug, Hash, PartialEq, Eq, Deserialize, Serialize)] 128 - #[serde(rename_all = "camelCase")] 129 127 pub struct PullTarget<'a> { 130 - // @NOTE This should be an at-uri. 131 - #[serde(borrow)] 132 - pub repo: Option<Cow<'a, str>>, 128 + pub repo: RecordUri, 133 129 130 + #[serde(borrow)] 134 131 pub branch: Cow<'a, str>, 135 132 } 136 133 137 134 #[derive(Debug, Hash, PartialEq, Eq, Deserialize, Serialize)] 138 - #[serde(rename_all = "camelCase")] 139 135 pub struct PullSource<'a> { 140 136 #[serde(borrow)] 141 - pub sha: Cow<'a, str>, 142 - 143 - // @NOTE This should be an at-uri. 144 - #[serde(skip_serializing_if = "Option::is_none")] 145 - pub repo: Option<Cow<'a, str>>, 146 - 147 137 pub branch: Cow<'a, str>, 138 + 139 + pub sha: ObjectId, 140 + 141 + #[serde(skip_serializing_if = "Option::is_none")] 142 + pub repo: Option<RecordUri>, 148 143 } 149 144 150 145 /// Lexicon sub-types.
+11 -14
crates/lexicon/src/sh_tangled/repo/branches.rs
··· 1 1 //! 2 2 //! <https://tangled.org/@tangled.org/core/blob/master/lexicons/repo/branches.json> 3 3 //! 4 - use std::borrow::Cow; 5 - 6 - use serde::Deserialize; 7 4 8 5 const LIMIT_MIN: u16 = 1; 9 6 const LIMIT_MAX: u16 = 100; ··· 8 11 9 12 /// Parameters for the `sh.tangled.repo.branches` query. 10 13 /// 11 - /// <https://tangled.org/@tangled.org/core/blob/master/lexicons/repo/branches.json> 12 - #[derive(Debug, Deserialize)] 14 + /// <https://tangled.org/tangled.org/core/blob/master/lexicons/repo/branches.json> 15 + #[derive(Debug, serde::Deserialize)] 13 16 #[serde(try_from = "UnvalidatedInput")] 14 - pub struct Input<'a> { 17 + pub struct Input { 15 18 /// Repository identifier in the format `did:plc:.../repoName` 16 - pub repo: Cow<'a, str>, 19 + pub repo: String, 17 20 18 21 /// Maximum number of branches to return. 19 22 /// ··· 22 25 pub limit: u16, 23 26 24 27 /// Pagination cursor 25 - pub cursor: Option<Cow<'a, str>>, 28 + pub cursor: Option<String>, 26 29 } 27 30 28 - #[derive(Deserialize)] 29 - struct UnvalidatedInput<'a> { 30 - repo: Cow<'a, str>, 31 + #[derive(serde::Deserialize)] 32 + struct UnvalidatedInput { 33 + repo: String, 31 34 limit: Option<u16>, 32 - cursor: Option<Cow<'a, str>>, 35 + cursor: Option<String>, 33 36 } 34 37 35 - impl<'a> TryFrom<UnvalidatedInput<'a>> for Input<'a> { 38 + impl TryFrom<UnvalidatedInput> for Input { 36 39 type Error = &'static str; 37 40 38 - fn try_from(value: UnvalidatedInput<'a>) -> Result<Self, Self::Error> { 41 + fn try_from(value: UnvalidatedInput) -> Result<Self, Self::Error> { 39 42 let UnvalidatedInput { 40 43 repo, 41 44 limit,
+6 -9
crates/lexicon/src/sh_tangled/repo/compare.rs
··· 1 1 //! 2 - //! <https://tangled.org/@tangled.org/core/blob/master/lexicons/repo/compare.json> 2 + //! <https://tangled.org/tangled.org/core/blob/master/lexicons/repo/compare.json> 3 3 //! 4 - use std::borrow::Cow; 5 - 6 - use serde::Deserialize; 7 4 8 5 /// Parameters for the `sh.tangled.repo.compare` query. 9 6 /// 10 7 /// <https://tangled.org/@tangled.org/core/blob/master/lexicons/repo/compare.json> 11 - #[derive(Debug, Deserialize)] 12 - pub struct Input<'a> { 8 + #[derive(Debug, serde::Deserialize)] 9 + pub struct Input { 13 10 /// Repository identifier in the format `did:plc:.../repoName` 14 - pub repo: Cow<'a, str>, 11 + pub repo: String, 15 12 16 13 /// First revision (commit, branch, or tag) 17 - pub rev1: Cow<'a, str>, 14 + pub rev1: String, 18 15 19 16 /// Second revision (commit, branch, or tag) 20 - pub rev2: Cow<'a, str>, 17 + pub rev2: String, 21 18 }
+6 -7
crates/lexicon/src/sh_tangled/repo/create.rs
··· 1 1 //! 2 - //! <https://tangled.org/@tangled.org/core/blob/master/lexicons/repo/create.json> 2 + //! <https://tangled.org/tangled.org/core/blob/master/lexicons/repo/create.json> 3 3 //! 4 - use std::borrow::Cow; 5 4 6 5 use serde::{Deserialize, Serialize}; 7 6 8 7 /// Parameters for the `sh.tangled.repo.create` procedure. 9 8 /// 10 - /// <https://tangled.org/@tangled.org/core/blob/master/lexicons/repo/create.json> 9 + /// <https://tangled.org/tangled.org/core/blob/master/lexicons/repo/create.json> 11 10 #[derive(Debug, Deserialize, Serialize)] 12 11 #[serde(rename_all = "camelCase")] 13 - pub struct Input<'a> { 12 + pub struct Input { 14 13 /// Record key of the repository record. 15 - pub rkey: Cow<'a, str>, 14 + pub rkey: String, 16 15 17 16 /// Default branch to push to. 18 17 #[serde(skip_serializing_if = "Option::is_none")] 19 - pub default_branch: Option<Cow<'a, str>>, 18 + pub default_branch: Option<String>, 20 19 21 20 /// A source URL to clone from , populate this when forking or importing a repository. 22 21 #[serde(skip_serializing_if = "Option::is_none")] 23 - pub source: Option<Cow<'a, str>>, 22 + pub source: Option<String>, 24 23 } 25 24 26 25 #[cfg(test)]
+5 -8
crates/lexicon/src/sh_tangled/repo/diff.rs
··· 1 1 //! 2 - //! <https://tangled.org/@tangled.org/core/blob/master/lexicons/repo/diff.json> 2 + //! <https://tangled.org/tangled.org/core/blob/master/lexicons/repo/diff.json> 3 3 //! 4 - use std::borrow::Cow; 5 4 6 - use serde::Deserialize; 7 - 8 - #[derive(Debug, Deserialize)] 9 - pub struct Input<'a> { 5 + #[derive(Debug, serde::Deserialize)] 6 + pub struct Input { 10 7 /// Repository identifier in the format `did:plc:.../repoName` 11 - pub repo: Cow<'a, str>, 8 + pub repo: String, 12 9 13 10 /// Git reference (branch, tag, or commit SHA) 14 11 #[serde(rename = "ref")] 15 - pub rev: Cow<'a, str>, 12 + pub rev: String, 16 13 }
+2 -4
crates/lexicon/src/sh_tangled/repo/get_default_branch.rs
··· 1 - use std::borrow::Cow; 2 - 3 1 use crate::extra::objectid::{Hex, ObjectId}; 4 2 use serde::{Deserialize, Serialize}; 5 3 use time::OffsetDateTime; ··· 5 7 use super::refs; 6 8 7 9 #[derive(Debug, Deserialize)] 8 - pub struct Input<'a> { 10 + pub struct Input { 9 11 /// Repository identifier in the format `did:plc:.../repoName` 10 - pub repo: Cow<'a, str>, 12 + pub repo: String, 11 13 } 12 14 13 15 /// Response type for the `sh.tangled.repo.getDefaultBranch` query.
+10 -3
crates/lexicon/src/sh_tangled/repo/issue.rs
··· 1 1 //! 2 2 //! <https://tangled.org/@tangled.org/core/tree/master/lexicons/issue> 3 3 //! 4 + use atproto::{OwnedDid, RecordUri}; 4 5 use serde::{Deserialize, Serialize}; 5 6 use std::borrow::Cow; 6 7 use time::OffsetDateTime; ··· 21 20 pub created_at: OffsetDateTime, 22 21 23 22 #[serde(skip_serializing_if = "Option::is_none")] 24 - pub reply_to: Option<Cow<'a, str>>, 23 + pub reply_to: Option<RecordUri>, 24 + 25 + #[serde(skip_serializing_if = "Vec::is_empty")] 26 + pub mentions: Vec<OwnedDid>, 27 + 28 + #[serde(skip_serializing_if = "Vec::is_empty")] 29 + pub references: Vec<RecordUri>, 25 30 } 26 31 27 32 /// `sh.tangled.repo.issue.state` record. ··· 36 29 #[derive(Debug, Hash, PartialEq, Eq, Deserialize, Serialize)] 37 30 #[serde(rename_all = "camelCase")] 38 31 pub struct State<'a> { 39 - #[serde(borrow)] 40 - pub issue: Cow<'a, str>, 32 + pub issue: RecordUri, 41 33 42 34 /// State of the issue. 35 + #[serde(borrow)] 43 36 pub state: Cow<'a, str>, 44 37 }
+7 -10
crates/lexicon/src/sh_tangled/repo/languages.rs
··· 1 1 //! 2 - //! <https://tangled.org/@tangled.org/core/blob/master/lexicons/repo/languages.json> 2 + //! <https://tangled.org/tangled.org/core/blob/master/lexicons/repo/languages.json> 3 3 //! 4 - use std::borrow::Cow; 5 4 6 - use serde::{Deserialize, Serialize}; 7 - 8 - #[derive(Debug, Deserialize)] 9 - pub struct Input<'a> { 5 + #[derive(Debug, serde::Deserialize)] 6 + pub struct Input { 10 7 /// Respository identifier in format `did:plc:.../repoName` 11 - pub repo: Cow<'a, str>, 8 + pub repo: String, 12 9 13 10 /// Git reference (branch, tag, or commit SHA) 14 11 #[serde(rename = "ref")] 15 - pub rev: Cow<'a, str>, 12 + pub rev: String, 16 13 } 17 14 18 - #[derive(Debug, Default, Serialize)] 15 + #[derive(Debug, Default, serde::Serialize)] 19 16 #[serde(rename_all = "camelCase")] 20 17 pub struct Output { 21 18 /// The git reference used ··· 31 34 pub total_files: Option<u64>, 32 35 } 33 36 34 - #[derive(Debug, Serialize)] 37 + #[derive(Debug, serde::Serialize)] 35 38 #[serde(rename_all = "camelCase")] 36 39 pub struct Language { 37 40 /// Programming language name
+10 -12
crates/lexicon/src/sh_tangled/repo/log.rs
··· 1 1 //! 2 - //! <https://tangled.org/@tangled.org/core/blob/master/lexicons/repo/log.json> 2 + //! <https://tangled.org/tangled.org/core/blob/master/lexicons/repo/log.json> 3 3 //! 4 - use std::{borrow::Cow, path::PathBuf}; 5 - 6 - use serde::Deserialize; 4 + use std::path::PathBuf; 7 5 8 6 const LIMIT_MIN: u16 = 1; 9 7 const LIMIT_MAX: u16 = 100; 10 8 const LIMIT_DEFAULT: u16 = 50; 11 9 12 10 /// Parameters for the `sh.tangled.repo.log` query. 13 - #[derive(Debug, Deserialize)] 11 + #[derive(Debug, serde::Deserialize)] 14 12 #[serde(try_from = "UnvalidatedInput")] 15 - pub struct Input<'a> { 13 + pub struct Input { 16 14 /// Repository identifier in the format `did:plc:.../repoName` 17 - pub repo: Cow<'a, str>, 15 + pub repo: String, 18 16 19 17 /// Git reference (branch, tag, or commit SHA) 20 18 #[serde(rename = "ref")] ··· 35 37 pub cursor: usize, 36 38 } 37 39 38 - #[derive(Deserialize)] 39 - struct UnvalidatedInput<'a> { 40 - repo: Cow<'a, str>, 40 + #[derive(serde::Deserialize)] 41 + struct UnvalidatedInput { 42 + repo: String, 41 43 #[serde(rename = "ref")] 42 44 rev: Option<String>, 43 45 path: Option<PathBuf>, ··· 46 48 cursor: usize, 47 49 } 48 50 49 - impl<'a> TryFrom<UnvalidatedInput<'a>> for Input<'a> { 51 + impl TryFrom<UnvalidatedInput> for Input { 50 52 type Error = &'static str; 51 53 52 - fn try_from(value: UnvalidatedInput<'a>) -> Result<Self, Self::Error> { 54 + fn try_from(value: UnvalidatedInput) -> Result<Self, Self::Error> { 53 55 let UnvalidatedInput { 54 56 repo, 55 57 rev,
+17 -14
crates/lexicon/src/sh_tangled/repo/pull.rs
··· 1 1 //! 2 - //! <https://tangled.org/@tangled.org/core/tree/master/lexicons/pulls> 2 + //! <https://tangled.org/tangled.org/core/tree/master/lexicons/pulls> 3 3 //! 4 - use serde::{Deserialize, Serialize}; 5 4 use std::borrow::Cow; 6 - use time::OffsetDateTime; 5 + 6 + use serde::{Deserialize, Serialize}; 7 7 8 8 /// `sh.tangled.repo.pull.comment` record. 9 9 /// 10 - /// <https://tangled.org/@tangled.org/core/blob/master/lexicons/pulls/comment.json> 10 + /// See: <https://tangled.org/tangled.org/core/blob/master/lexicons/pulls/comment.json> 11 11 #[derive(Debug, Hash, PartialEq, Eq, Deserialize, Serialize)] 12 12 #[serde(rename_all = "camelCase")] 13 13 pub struct Comment<'a> { 14 - // @NOTE This should be an at-uri. 15 - #[serde(borrow)] 16 - pub pull: Cow<'a, str>, 14 + pub pull: atproto::RecordUri, 17 15 16 + #[serde(borrow)] 18 17 pub body: Cow<'a, str>, 19 18 20 19 #[serde(with = "time::serde::rfc3339")] 21 - pub created_at: OffsetDateTime, 20 + pub created_at: time::OffsetDateTime, 21 + 22 + #[serde(skip_serializing_if = "Vec::is_empty")] 23 + pub mentions: Vec<atproto::OwnedDid>, 24 + 25 + #[serde(skip_serializing_if = "Vec::is_empty")] 26 + pub references: Vec<atproto::RecordUri>, 22 27 } 23 28 24 29 /// `sh.tangled.repo.pull.state` record. 25 30 /// 26 - /// <https://tangled.org/@tangled.org/core/blob/master/lexicons/pulls/state.json> 31 + /// See: <https://tangled.org/tangled.org/core/blob/master/lexicons/pulls/state.json> 27 32 #[derive(Debug, Hash, PartialEq, Eq, Deserialize, Serialize)] 28 - #[serde(rename_all = "camelCase")] 29 - pub struct State<'a> { 30 - // @NOTE This should be an at-uri. 31 - #[serde(borrow)] 32 - pub pull: Cow<'a, str>, 33 + pub struct Status<'a> { 34 + pub pull: atproto::RecordUri, 33 35 36 + #[serde(borrow)] 34 37 pub status: Cow<'a, str>, 35 38 }
+10 -13
crates/lexicon/src/sh_tangled/repo/tags.rs
··· 1 1 //! 2 2 //! <https://tangled.org/@tangled.org/core/blob/master/lexicons/repo/tags.json> 3 3 //! 4 - use std::borrow::Cow; 5 - 6 - use serde::Deserialize; 7 4 8 5 const LIMIT_MIN: u16 = 1; 9 6 const LIMIT_MAX: u16 = 100; ··· 9 12 /// Parameters for the `sh.tangled.repo.tags` query. 10 13 /// 11 14 /// <https://tangled.org/@tangled.org/core/blob/master/lexicons/repo/tags.json> 12 - #[derive(Debug, Deserialize)] 15 + #[derive(Debug, serde::Deserialize)] 13 16 #[serde(try_from = "UnvalidatedInput")] 14 - pub struct Input<'a> { 17 + pub struct Input { 15 18 /// Repository identifier in the format `did:plc:.../repoName` 16 - pub repo: Cow<'a, str>, 19 + pub repo: String, 17 20 18 21 /// Maximum number of commits to return. 19 22 /// ··· 22 25 pub limit: u16, 23 26 24 27 /// Pagination cursor 25 - pub cursor: Option<Cow<'a, str>>, 28 + pub cursor: Option<String>, 26 29 } 27 30 28 - #[derive(Deserialize)] 29 - struct UnvalidatedInput<'a> { 30 - repo: Cow<'a, str>, 31 + #[derive(serde::Deserialize)] 32 + struct UnvalidatedInput { 33 + repo: String, 31 34 limit: Option<u16>, 32 - cursor: Option<Cow<'a, str>>, 35 + cursor: Option<String>, 33 36 } 34 37 35 - impl<'a> TryFrom<UnvalidatedInput<'a>> for Input<'a> { 38 + impl TryFrom<UnvalidatedInput> for Input { 36 39 type Error = &'static str; 37 40 38 - fn try_from(value: UnvalidatedInput<'a>) -> Result<Self, Self::Error> { 41 + fn try_from(value: UnvalidatedInput) -> Result<Self, Self::Error> { 39 42 let UnvalidatedInput { 40 43 repo, 41 44 limit,
+14 -20
crates/lexicon/src/sh_tangled/repo/tree.rs
··· 1 1 //! 2 - //! <https://tangled.org/@tangled.org/core/blob/master/lexicons/repo/tree.json> 2 + //! <https://tangled.org/tangled.org/core/blob/master/lexicons/repo/tree.json> 3 3 //! 4 - use std::{ 5 - borrow::Cow, 6 - path::{Path, PathBuf}, 7 - }; 8 - 9 - use serde::{Deserialize, Serialize}; 10 - use time::OffsetDateTime; 4 + use std::path::PathBuf; 11 5 12 6 use crate::extra::objectid::ObjectId; 13 7 14 8 /// Parameters for the `sh.tangled.repo.tree` query. 15 9 /// 16 - /// <https://tangled.org/@tangled.org/core/blob/master/lexicons/repo/tree.json> 17 - #[derive(Debug, Deserialize)] 18 - pub struct Input<'a> { 10 + /// <https://tangled.org/tangled.org/core/blob/master/lexicons/repo/tree.json> 11 + #[derive(Debug, serde::Deserialize)] 12 + pub struct Input { 19 13 /// Repository identifier in the format `did:plc:.../repoName` 20 - pub repo: Cow<'a, str>, 14 + pub repo: String, 21 15 22 16 /// Git reference (branch, tag, or commit SHA) 23 17 #[serde(rename = "ref")] 24 - pub rev: Option<Cow<'a, str>>, 18 + pub rev: Option<String>, 25 19 26 20 /// Path within the repository tree 27 - pub path: Option<Cow<'a, Path>>, 21 + pub path: Option<PathBuf>, 28 22 } 29 23 30 24 /// Output of `sh.tangled.repo.tree` query. 31 - #[derive(Debug, Serialize)] 25 + #[derive(Debug, serde::Serialize)] 32 26 pub struct Output { 33 27 /// The git reference used 34 28 #[serde(rename = "ref")] ··· 43 49 pub files: Vec<TreeEntry>, 44 50 } 45 51 46 - #[derive(Debug, Default, Serialize)] 52 + #[derive(Debug, Default, serde::Serialize)] 47 53 pub struct TreeEntry { 48 54 /// Relative file or directory name 49 55 pub name: String, 50 56 51 57 /// File mode 52 - pub mode: Cow<'static, str>, 58 + pub mode: &'static str, 53 59 54 60 /// File size in bytes 55 61 pub size: i64, ··· 57 63 pub last_commit: Option<LastCommit>, 58 64 } 59 65 60 - #[derive(Debug, Serialize)] 66 + #[derive(Debug, serde::Serialize)] 61 67 pub struct Readme { 62 68 /// Contents of the readme file 63 69 pub filename: String, ··· 66 72 pub contents: String, 67 73 } 68 74 69 - #[derive(Debug, Serialize)] 75 + #[derive(Debug, serde::Serialize)] 70 76 pub struct LastCommit { 71 77 /// Commit hash 72 78 pub hash: ObjectId, ··· 76 82 77 83 /// Commit timestamp 78 84 #[serde(with = "time::serde::rfc3339")] 79 - pub when: OffsetDateTime, 85 + pub when: time::OffsetDateTime, 80 86 }
+3 -3
crates/lexicon/src/sh_tangled/spindle.rs
··· 19 19 #[derive(Debug, Hash, PartialEq, Eq, Deserialize, Serialize)] 20 20 #[serde(rename_all = "camelCase")] 21 21 pub struct Member<'a> { 22 - #[serde(borrow)] 23 - pub subject: &'a Did, 22 + #[serde(borrow, with = "atproto::serde::cow_did")] 23 + pub subject: Cow<'a, Did>, 24 24 25 - /// Spindle instance that the subject is not a member of. 25 + /// Spindle instance that the subject is now a member of. 26 26 pub instance: Cow<'a, str>, 27 27 28 28 #[serde(with = "time::serde::rfc3339")]