A better Rust ATProto crate

refactored codegen to be more navigable, fixed a number of bugs

Orual c08fa921 0cbdaf71

+4355 -4663
+103 -15
Cargo.lock
··· 1761 1761 "bytes", 1762 1762 "clap", 1763 1763 "http", 1764 - "jacquard-api", 1765 - "jacquard-common", 1766 - "jacquard-derive", 1767 - "jacquard-identity", 1764 + "jacquard-api 0.4.1", 1765 + "jacquard-common 0.4.0", 1766 + "jacquard-derive 0.4.0", 1767 + "jacquard-identity 0.4.0", 1768 1768 "jacquard-oauth", 1769 1769 "jose-jwk", 1770 1770 "miette", ··· 1786 1786 [[package]] 1787 1787 name = "jacquard-api" 1788 1788 version = "0.4.0" 1789 + source = "git+https://tangled.org/@nonbinary.computer/jacquard#0cbdaf71e0721122b354892bb8ae49aa3ffcc9bc" 1789 1790 dependencies = [ 1790 1791 "bon", 1791 1792 "bytes", 1792 - "jacquard-common", 1793 - "jacquard-derive", 1793 + "jacquard-common 0.4.0 (git+https://tangled.org/@nonbinary.computer/jacquard)", 1794 + "jacquard-derive 0.4.0 (git+https://tangled.org/@nonbinary.computer/jacquard)", 1795 + "miette", 1796 + "serde", 1797 + "thiserror 2.0.17", 1798 + ] 1799 + 1800 + [[package]] 1801 + name = "jacquard-api" 1802 + version = "0.4.1" 1803 + dependencies = [ 1804 + "bon", 1805 + "bytes", 1806 + "jacquard-common 0.4.0", 1807 + "jacquard-derive 0.4.0", 1794 1808 "miette", 1795 1809 "serde", 1796 1810 "thiserror 2.0.17", ··· 1805 1819 "axum-test", 1806 1820 "bytes", 1807 1821 "jacquard", 1808 - "jacquard-common", 1822 + "jacquard-common 0.4.0", 1809 1823 "miette", 1810 1824 "serde", 1811 1825 "serde_html_form", ··· 1856 1870 ] 1857 1871 1858 1872 [[package]] 1873 + name = "jacquard-common" 1874 + version = "0.4.0" 1875 + source = "git+https://tangled.org/@nonbinary.computer/jacquard#0cbdaf71e0721122b354892bb8ae49aa3ffcc9bc" 1876 + dependencies = [ 1877 + "async-trait", 1878 + "base64 0.22.1", 1879 + "bon", 1880 + "bytes", 1881 + "chrono", 1882 + "cid", 1883 + "http", 1884 + "ipld-core", 1885 + "langtag", 1886 + "miette", 1887 + "multibase", 1888 + "multihash", 1889 + "num-traits", 1890 + "ouroboros", 1891 + "rand 0.9.2", 1892 + "regex", 1893 + "reqwest", 1894 + "serde", 1895 + "serde_html_form", 1896 + "serde_ipld_dagcbor", 1897 + "serde_json", 1898 + "serde_with", 1899 + "smol_str", 1900 + "thiserror 2.0.17", 1901 + "tokio", 1902 + "trait-variant", 1903 + "url", 1904 + ] 1905 + 1906 + [[package]] 1859 1907 name = "jacquard-derive" 1860 1908 version = "0.4.0" 1861 1909 dependencies = [ 1862 1910 "heck 0.5.0", 1863 1911 "itertools", 1864 - "jacquard-common", 1912 + "jacquard-common 0.4.0", 1913 + "prettyplease", 1914 + "proc-macro2", 1915 + "quote", 1916 + "serde", 1917 + "serde_json", 1918 + "serde_repr", 1919 + "serde_with", 1920 + "syn 2.0.106", 1921 + ] 1922 + 1923 + [[package]] 1924 + name = "jacquard-derive" 1925 + version = "0.4.0" 1926 + source = "git+https://tangled.org/@nonbinary.computer/jacquard#0cbdaf71e0721122b354892bb8ae49aa3ffcc9bc" 1927 + dependencies = [ 1928 + "heck 0.5.0", 1929 + "itertools", 1865 1930 "prettyplease", 1866 1931 "proc-macro2", 1867 1932 "quote", ··· 1881 1946 "bytes", 1882 1947 "hickory-resolver", 1883 1948 "http", 1884 - "jacquard-api", 1885 - "jacquard-common", 1949 + "jacquard-api 0.4.1", 1950 + "jacquard-common 0.4.0", 1951 + "miette", 1952 + "percent-encoding", 1953 + "reqwest", 1954 + "serde", 1955 + "serde_html_form", 1956 + "serde_json", 1957 + "thiserror 2.0.17", 1958 + "tokio", 1959 + "url", 1960 + "urlencoding", 1961 + ] 1962 + 1963 + [[package]] 1964 + name = "jacquard-identity" 1965 + version = "0.4.0" 1966 + source = "git+https://tangled.org/@nonbinary.computer/jacquard#0cbdaf71e0721122b354892bb8ae49aa3ffcc9bc" 1967 + dependencies = [ 1968 + "async-trait", 1969 + "bon", 1970 + "bytes", 1971 + "http", 1972 + "jacquard-api 0.4.0", 1973 + "jacquard-common 0.4.0 (git+https://tangled.org/@nonbinary.computer/jacquard)", 1886 1974 "miette", 1887 1975 "percent-encoding", 1888 1976 "reqwest", ··· 1904 1992 "glob", 1905 1993 "heck 0.5.0", 1906 1994 "itertools", 1907 - "jacquard-api", 1908 - "jacquard-common", 1909 - "jacquard-identity", 1995 + "jacquard-api 0.4.0", 1996 + "jacquard-common 0.4.0 (git+https://tangled.org/@nonbinary.computer/jacquard)", 1997 + "jacquard-identity 0.4.0 (git+https://tangled.org/@nonbinary.computer/jacquard)", 1910 1998 "kdl", 1911 1999 "miette", 1912 2000 "prettyplease", ··· 1935 2023 "dashmap", 1936 2024 "elliptic-curve", 1937 2025 "http", 1938 - "jacquard-common", 1939 - "jacquard-identity", 2026 + "jacquard-common 0.4.0", 2027 + "jacquard-identity 0.4.0", 1940 2028 "jose-jwa", 1941 2029 "jose-jwk", 1942 2030 "miette",
+7 -6
crates/jacquard-api/Cargo.toml
··· 2 2 name = "jacquard-api" 3 3 description = "Generated AT Protocol API bindings for Jacquard" 4 4 edition.workspace = true 5 - version = "0.4.0" 5 + version = "0.4.1" 6 6 authors.workspace = true 7 7 repository.workspace = true 8 8 keywords.workspace = true ··· 35 35 # --- generated --- 36 36 # Generated namespace features 37 37 app_blebbit = [] 38 - app_bsky = ["com_atproto"] 38 + app_bsky = [] 39 39 app_ocho = [] 40 40 beauty_cybernetic = [] 41 41 blog_pckt = [] ··· 44 44 blue_linkat = [] 45 45 blue_zio = [] 46 46 buzz_bookhive = [] 47 - chat_bsky = ["app_bsky"] 47 + chat_bsky = [] 48 48 club_stellz = [] 49 49 com_atproto = [] 50 50 com_bad_example = [] ··· 56 56 dev_fudgeu = [] 57 57 dev_ocbwoy3 = [] 58 58 dev_regnault = [] 59 + events_smokesignal = [] 59 60 fyi_unravel = [] 60 61 garden_lexicon = [] 61 62 moe_karashiiro = [] 62 63 my_skylights = [] 63 64 net_aftertheinter = [] 64 - net_anisota = ["app_bsky", "com_atproto"] 65 + net_anisota = ["app_bsky"] 65 66 net_bnewbold = [] 66 67 net_mmatt = [] 67 68 network_slices = [] 68 69 org_devcon = [] 69 70 org_robocracy = [] 70 71 place_atwork = [] 71 - place_stream = [] 72 + place_stream = ["app_bsky"] 72 73 pub_leaflet = [] 73 74 sh_tangled = [] 74 75 sh_weaver = ["app_bsky"] 75 76 social_clippr = [] 76 - social_grain = ["com_atproto"] 77 + social_grain = [] 77 78 social_pmsky = [] 78 79 social_psky = [] 79 80 tools_ozone = ["chat_bsky", "com_atproto"]
+26
crates/jacquard-api/lexicons/win_tomo-x_pushat_allow.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "win.tomo-x.pushat.allow", 4 + "defs": { 5 + "main": { 6 + "type": "record", 7 + "description": "allow service to push. key must be did", 8 + "key": "any", 9 + "record": { 10 + "type": "object", 11 + "required": [ 12 + "createdAt" 13 + ], 14 + "properties": { 15 + "config": { 16 + "type": "unknown" 17 + }, 18 + "createdAt": { 19 + "type": "string", 20 + "format": "datetime" 21 + } 22 + } 23 + } 24 + } 25 + } 26 + }
+63
crates/jacquard-api/lexicons/win_tomo-x_pushat_defs.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "win.tomo-x.pushat.defs", 4 + "defs": { 5 + "deviceList": { 6 + "type": "array", 7 + "items": { 8 + "type": "ref", 9 + "ref": "#deviceListItem" 10 + } 11 + }, 12 + "deviceListItem": { 13 + "type": "object", 14 + "required": [ 15 + "name", 16 + "id", 17 + "current" 18 + ], 19 + "properties": { 20 + "current": { 21 + "type": "boolean", 22 + "default": false 23 + }, 24 + "id": { 25 + "type": "string", 26 + "format": "tid" 27 + }, 28 + "name": { 29 + "type": "string", 30 + "maxLength": 300, 31 + "maxGraphemes": 30 32 + } 33 + } 34 + }, 35 + "notifyBody": { 36 + "type": "object", 37 + "required": [ 38 + "title", 39 + "body" 40 + ], 41 + "properties": { 42 + "body": { 43 + "type": "string", 44 + "description": "Body text of the notification." 45 + }, 46 + "icon": { 47 + "type": "string", 48 + "description": "The URI of the icon displayed in the notification.", 49 + "format": "uri" 50 + }, 51 + "link": { 52 + "type": "string", 53 + "description": "Experimental — do not use. The URI to open when the notification is clicked.", 54 + "format": "uri" 55 + }, 56 + "title": { 57 + "type": "string", 58 + "description": "Title text of the notification." 59 + } 60 + } 61 + } 62 + } 63 + }
+10 -1
crates/jacquard-api/lexicons/win_tomo-x_pushat_pushNotify.json
··· 19 19 }, 20 20 "target": { 21 21 "type": "string", 22 + "description": "The DID of the target user to whom the notification will be sent.", 22 23 "format": "did" 23 24 } 24 25 } ··· 30 31 "type": "object", 31 32 "properties": {} 32 33 } 33 - } 34 + }, 35 + "errors": [ 36 + { 37 + "name": "ServiceNotAllowedError" 38 + }, 39 + { 40 + "name": "DeviceNotFoundError" 41 + } 42 + ] 34 43 } 35 44 } 36 45 }
+72 -21
crates/jacquard-api/src/app_bsky/actor.rs
··· 396 396 #[serde(skip_serializing_if = "std::option::Option::is_none")] 397 397 #[serde(borrow)] 398 398 pub postgate_embedding_rules: std::option::Option< 399 - Vec<jacquard_common::types::value::Data<'a>>, 399 + Vec<crate::app_bsky::feed::postgate::DisableRule<'a>>, 400 400 >, 401 401 ///Matches threadgate record. List of rules defining who can reply to this users posts. If value is an empty array, no one can reply. If value is undefined, anyone can reply. 402 402 #[serde(skip_serializing_if = "std::option::Option::is_none")] 403 403 #[serde(borrow)] 404 404 pub threadgate_allow_rules: std::option::Option< 405 - Vec<jacquard_common::types::value::Data<'a>>, 405 + Vec<PostInteractionSettingsPrefThreadgateAllowRulesItem<'a>>, 406 406 >, 407 407 } 408 408 409 - pub type Preferences<'a> = Vec<jacquard_common::types::value::Data<'a>>; 409 + #[jacquard_derive::open_union] 410 + #[derive( 411 + serde::Serialize, 412 + serde::Deserialize, 413 + Debug, 414 + Clone, 415 + PartialEq, 416 + Eq, 417 + jacquard_derive::IntoStatic 418 + )] 419 + #[serde(tag = "$type")] 420 + #[serde(bound(deserialize = "'de: 'a"))] 421 + pub enum PostInteractionSettingsPrefThreadgateAllowRulesItem<'a> { 422 + #[serde(rename = "app.bsky.feed.threadgate#mentionRule")] 423 + ThreadgateMentionRule(Box<crate::app_bsky::feed::threadgate::MentionRule<'a>>), 424 + #[serde(rename = "app.bsky.feed.threadgate#followerRule")] 425 + ThreadgateFollowerRule(Box<crate::app_bsky::feed::threadgate::FollowerRule<'a>>), 426 + #[serde(rename = "app.bsky.feed.threadgate#followingRule")] 427 + ThreadgateFollowingRule(Box<crate::app_bsky::feed::threadgate::FollowingRule<'a>>), 428 + #[serde(rename = "app.bsky.feed.threadgate#listRule")] 429 + ThreadgateListRule(Box<crate::app_bsky::feed::threadgate::ListRule<'a>>), 430 + } 431 + 432 + #[jacquard_derive::open_union] 433 + #[derive( 434 + serde::Serialize, 435 + serde::Deserialize, 436 + Debug, 437 + Clone, 438 + PartialEq, 439 + Eq, 440 + jacquard_derive::IntoStatic 441 + )] 442 + #[serde(tag = "$type")] 443 + #[serde(bound(deserialize = "'de: 'a"))] 444 + pub enum PreferencesItem<'a> { 445 + #[serde(rename = "app.bsky.actor.defs#adultContentPref")] 446 + AdultContentPref(Box<crate::app_bsky::actor::AdultContentPref<'a>>), 447 + #[serde(rename = "app.bsky.actor.defs#contentLabelPref")] 448 + ContentLabelPref(Box<crate::app_bsky::actor::ContentLabelPref<'a>>), 449 + #[serde(rename = "app.bsky.actor.defs#savedFeedsPref")] 450 + SavedFeedsPref(Box<crate::app_bsky::actor::SavedFeedsPref<'a>>), 451 + #[serde(rename = "app.bsky.actor.defs#savedFeedsPrefV2")] 452 + SavedFeedsPrefV2(Box<crate::app_bsky::actor::SavedFeedsPrefV2<'a>>), 453 + #[serde(rename = "app.bsky.actor.defs#personalDetailsPref")] 454 + PersonalDetailsPref(Box<crate::app_bsky::actor::PersonalDetailsPref<'a>>), 455 + #[serde(rename = "app.bsky.actor.defs#feedViewPref")] 456 + FeedViewPref(Box<crate::app_bsky::actor::FeedViewPref<'a>>), 457 + #[serde(rename = "app.bsky.actor.defs#threadViewPref")] 458 + ThreadViewPref(Box<crate::app_bsky::actor::ThreadViewPref<'a>>), 459 + #[serde(rename = "app.bsky.actor.defs#interestsPref")] 460 + InterestsPref(Box<crate::app_bsky::actor::InterestsPref<'a>>), 461 + #[serde(rename = "app.bsky.actor.defs#mutedWordsPref")] 462 + MutedWordsPref(Box<crate::app_bsky::actor::MutedWordsPref<'a>>), 463 + #[serde(rename = "app.bsky.actor.defs#hiddenPostsPref")] 464 + HiddenPostsPref(Box<crate::app_bsky::actor::HiddenPostsPref<'a>>), 465 + #[serde(rename = "app.bsky.actor.defs#bskyAppStatePref")] 466 + BskyAppStatePref(Box<crate::app_bsky::actor::BskyAppStatePref<'a>>), 467 + #[serde(rename = "app.bsky.actor.defs#labelersPref")] 468 + LabelersPref(Box<crate::app_bsky::actor::LabelersPref<'a>>), 469 + #[serde(rename = "app.bsky.actor.defs#postInteractionSettingsPref")] 470 + PostInteractionSettingsPref( 471 + Box<crate::app_bsky::actor::PostInteractionSettingsPref<'a>>, 472 + ), 473 + #[serde(rename = "app.bsky.actor.defs#verificationPrefs")] 474 + VerificationPrefs(Box<crate::app_bsky::actor::VerificationPrefs<'a>>), 475 + } 476 + 477 + pub type Preferences<'a> = Vec<PreferencesItem<'a>>; 410 478 #[jacquard_derive::lexicon] 411 479 #[derive( 412 480 serde::Serialize, ··· 705 773 ///An optional embed associated with the status. 706 774 #[serde(skip_serializing_if = "std::option::Option::is_none")] 707 775 #[serde(borrow)] 708 - pub embed: std::option::Option<StatusViewRecordEmbed<'a>>, 776 + pub embed: std::option::Option<crate::app_bsky::embed::external::View<'a>>, 709 777 ///The date when this status will expire. The application might choose to no longer return the status after expiration. 710 778 #[serde(skip_serializing_if = "std::option::Option::is_none")] 711 779 pub expires_at: std::option::Option<jacquard_common::types::string::Datetime>, ··· 717 785 ///The status for the account. 718 786 #[serde(borrow)] 719 787 pub status: jacquard_common::CowStr<'a>, 720 - } 721 - 722 - #[jacquard_derive::open_union] 723 - #[derive( 724 - serde::Serialize, 725 - serde::Deserialize, 726 - Debug, 727 - Clone, 728 - PartialEq, 729 - Eq, 730 - jacquard_derive::IntoStatic 731 - )] 732 - #[serde(tag = "$type")] 733 - #[serde(bound(deserialize = "'de: 'a"))] 734 - pub enum StatusViewRecordEmbed<'a> { 735 - #[serde(rename = "app.bsky.embed.external#view")] 736 - ExternalView(Box<crate::app_bsky::embed::external::View<'a>>), 737 788 } 738 789 739 790 #[jacquard_derive::lexicon]
+1 -18
crates/jacquard-api/src/app_bsky/actor/profile.rs
··· 43 43 ///Self-label values, specific to the Bluesky application, on the overall account. 44 44 #[serde(skip_serializing_if = "std::option::Option::is_none")] 45 45 #[serde(borrow)] 46 - pub labels: std::option::Option<ProfileRecordLabels<'a>>, 46 + pub labels: std::option::Option<crate::com_atproto::label::SelfLabels<'a>>, 47 47 #[serde(skip_serializing_if = "std::option::Option::is_none")] 48 48 #[serde(borrow)] 49 49 pub pinned_post: std::option::Option< ··· 56 56 #[serde(skip_serializing_if = "std::option::Option::is_none")] 57 57 #[serde(borrow)] 58 58 pub website: std::option::Option<jacquard_common::types::string::Uri<'a>>, 59 - } 60 - 61 - #[jacquard_derive::open_union] 62 - #[derive( 63 - serde::Serialize, 64 - serde::Deserialize, 65 - Debug, 66 - Clone, 67 - PartialEq, 68 - Eq, 69 - jacquard_derive::IntoStatic 70 - )] 71 - #[serde(tag = "$type")] 72 - #[serde(bound(deserialize = "'de: 'a"))] 73 - pub enum ProfileRecordLabels<'a> { 74 - #[serde(rename = "com.atproto.label.defs#selfLabels")] 75 - DefsSelfLabels(Box<crate::com_atproto::label::SelfLabels<'a>>), 76 59 } 77 60 78 61 impl jacquard_common::types::collection::Collection for Profile<'_> {
+1 -18
crates/jacquard-api/src/app_bsky/actor/status.rs
··· 43 43 ///An optional embed associated with the status. 44 44 #[serde(skip_serializing_if = "std::option::Option::is_none")] 45 45 #[serde(borrow)] 46 - pub embed: std::option::Option<StatusRecordEmbed<'a>>, 46 + pub embed: std::option::Option<crate::app_bsky::embed::external::ExternalRecord<'a>>, 47 47 ///The status for the account. 48 48 #[serde(borrow)] 49 49 pub status: jacquard_common::CowStr<'a>, 50 - } 51 - 52 - #[jacquard_derive::open_union] 53 - #[derive( 54 - serde::Serialize, 55 - serde::Deserialize, 56 - Debug, 57 - Clone, 58 - PartialEq, 59 - Eq, 60 - jacquard_derive::IntoStatic 61 - )] 62 - #[serde(tag = "$type")] 63 - #[serde(bound(deserialize = "'de: 'a"))] 64 - pub enum StatusRecordEmbed<'a> { 65 - #[serde(rename = "app.bsky.embed.external")] 66 - External(Box<crate::app_bsky::embed::external::ExternalRecord<'a>>), 67 50 } 68 51 69 52 impl jacquard_common::types::collection::Collection for Status<'_> {
+5 -5
crates/jacquard-api/src/app_bsky/bookmark.rs
··· 42 42 #[serde(skip_serializing_if = "std::option::Option::is_none")] 43 43 pub created_at: std::option::Option<jacquard_common::types::string::Datetime>, 44 44 #[serde(borrow)] 45 - pub item: BookmarkViewRecordItem<'a>, 45 + pub item: BookmarkViewItem<'a>, 46 46 ///A strong ref to the bookmarked record. 47 47 #[serde(borrow)] 48 48 pub subject: crate::com_atproto::repo::strong_ref::StrongRef<'a>, ··· 60 60 )] 61 61 #[serde(tag = "$type")] 62 62 #[serde(bound(deserialize = "'de: 'a"))] 63 - pub enum BookmarkViewRecordItem<'a> { 63 + pub enum BookmarkViewItem<'a> { 64 64 #[serde(rename = "app.bsky.feed.defs#blockedPost")] 65 - DefsBlockedPost(Box<crate::app_bsky::feed::BlockedPost<'a>>), 65 + BlockedPost(Box<crate::app_bsky::feed::BlockedPost<'a>>), 66 66 #[serde(rename = "app.bsky.feed.defs#notFoundPost")] 67 - DefsNotFoundPost(Box<crate::app_bsky::feed::NotFoundPost<'a>>), 67 + NotFoundPost(Box<crate::app_bsky::feed::NotFoundPost<'a>>), 68 68 #[serde(rename = "app.bsky.feed.defs#postView")] 69 - DefsPostView(Box<crate::app_bsky::feed::PostView<'a>>), 69 + PostView(Box<crate::app_bsky::feed::PostView<'a>>), 70 70 }
+2 -15
crates/jacquard-api/src/app_bsky/bookmark/create_bookmark.rs
··· 41 41 PartialEq, 42 42 Eq, 43 43 thiserror::Error, 44 - miette::Diagnostic 44 + miette::Diagnostic, 45 + jacquard_derive::IntoStatic 45 46 )] 46 47 #[serde(tag = "error", content = "message")] 47 48 #[serde(bound(deserialize = "'de: 'a"))] ··· 62 63 Ok(()) 63 64 } 64 65 Self::Unknown(err) => write!(f, "Unknown error: {:?}", err), 65 - } 66 - } 67 - } 68 - 69 - impl jacquard_common::IntoStatic for CreateBookmarkError<'_> { 70 - type Output = CreateBookmarkError<'static>; 71 - fn into_static(self) -> Self::Output { 72 - match self { 73 - CreateBookmarkError::UnsupportedCollection(v) => { 74 - CreateBookmarkError::UnsupportedCollection(v.into_static()) 75 - } 76 - CreateBookmarkError::Unknown(v) => { 77 - CreateBookmarkError::Unknown(v.into_static()) 78 - } 79 66 } 80 67 } 81 68 }
+2 -15
crates/jacquard-api/src/app_bsky/bookmark/delete_bookmark.rs
··· 39 39 PartialEq, 40 40 Eq, 41 41 thiserror::Error, 42 - miette::Diagnostic 42 + miette::Diagnostic, 43 + jacquard_derive::IntoStatic 43 44 )] 44 45 #[serde(tag = "error", content = "message")] 45 46 #[serde(bound(deserialize = "'de: 'a"))] ··· 60 61 Ok(()) 61 62 } 62 63 Self::Unknown(err) => write!(f, "Unknown error: {:?}", err), 63 - } 64 - } 65 - } 66 - 67 - impl jacquard_common::IntoStatic for DeleteBookmarkError<'_> { 68 - type Output = DeleteBookmarkError<'static>; 69 - fn into_static(self) -> Self::Output { 70 - match self { 71 - DeleteBookmarkError::UnsupportedCollection(v) => { 72 - DeleteBookmarkError::UnsupportedCollection(v.into_static()) 73 - } 74 - DeleteBookmarkError::Unknown(v) => { 75 - DeleteBookmarkError::Unknown(v.into_static()) 76 - } 77 64 } 78 65 } 79 66 }
+40 -7
crates/jacquard-api/src/app_bsky/embed/record.rs
··· 34 34 #[serde(rename_all = "camelCase")] 35 35 pub struct View<'a> { 36 36 #[serde(borrow)] 37 - pub record: ViewRecordRecord<'a>, 37 + pub record: ViewUnionRecord<'a>, 38 38 } 39 39 40 40 #[jacquard_derive::open_union] ··· 49 49 )] 50 50 #[serde(tag = "$type")] 51 51 #[serde(bound(deserialize = "'de: 'a"))] 52 - pub enum ViewRecordRecord<'a> { 52 + pub enum ViewUnionRecord<'a> { 53 + #[serde(rename = "app.bsky.embed.record#viewRecord")] 54 + ViewRecord(Box<crate::app_bsky::embed::record::ViewRecord<'a>>), 55 + #[serde(rename = "app.bsky.embed.record#viewNotFound")] 56 + ViewNotFound(Box<crate::app_bsky::embed::record::ViewNotFound<'a>>), 57 + #[serde(rename = "app.bsky.embed.record#viewBlocked")] 58 + ViewBlocked(Box<crate::app_bsky::embed::record::ViewBlocked<'a>>), 59 + #[serde(rename = "app.bsky.embed.record#viewDetached")] 60 + ViewDetached(Box<crate::app_bsky::embed::record::ViewDetached<'a>>), 53 61 #[serde(rename = "app.bsky.feed.defs#generatorView")] 54 - DefsGeneratorView(Box<crate::app_bsky::feed::GeneratorView<'a>>), 62 + GeneratorView(Box<crate::app_bsky::feed::GeneratorView<'a>>), 55 63 #[serde(rename = "app.bsky.graph.defs#listView")] 56 - DefsListView(Box<crate::app_bsky::graph::ListView<'a>>), 64 + ListView(Box<crate::app_bsky::graph::ListView<'a>>), 57 65 #[serde(rename = "app.bsky.labeler.defs#labelerView")] 58 - DefsLabelerView(Box<crate::app_bsky::labeler::LabelerView<'a>>), 66 + LabelerView(Box<crate::app_bsky::labeler::LabelerView<'a>>), 59 67 #[serde(rename = "app.bsky.graph.defs#starterPackViewBasic")] 60 - DefsStarterPackViewBasic(Box<crate::app_bsky::graph::StarterPackViewBasic<'a>>), 68 + StarterPackViewBasic(Box<crate::app_bsky::graph::StarterPackViewBasic<'a>>), 61 69 } 62 70 63 71 #[jacquard_derive::lexicon] ··· 131 139 pub cid: jacquard_common::types::string::Cid<'a>, 132 140 #[serde(skip_serializing_if = "std::option::Option::is_none")] 133 141 #[serde(borrow)] 134 - pub embeds: std::option::Option<Vec<jacquard_common::types::value::Data<'a>>>, 142 + pub embeds: std::option::Option<Vec<ViewRecordEmbedsItem<'a>>>, 135 143 pub indexed_at: jacquard_common::types::string::Datetime, 136 144 #[serde(skip_serializing_if = "std::option::Option::is_none")] 137 145 #[serde(borrow)] ··· 149 157 ///The record data itself. 150 158 #[serde(borrow)] 151 159 pub value: jacquard_common::types::value::Data<'a>, 160 + } 161 + 162 + #[jacquard_derive::open_union] 163 + #[derive( 164 + serde::Serialize, 165 + serde::Deserialize, 166 + Debug, 167 + Clone, 168 + PartialEq, 169 + Eq, 170 + jacquard_derive::IntoStatic 171 + )] 172 + #[serde(tag = "$type")] 173 + #[serde(bound(deserialize = "'de: 'a"))] 174 + pub enum ViewRecordEmbedsItem<'a> { 175 + #[serde(rename = "app.bsky.embed.images#view")] 176 + ImagesView(Box<crate::app_bsky::embed::images::View<'a>>), 177 + #[serde(rename = "app.bsky.embed.video#view")] 178 + VideoView(Box<crate::app_bsky::embed::video::View<'a>>), 179 + #[serde(rename = "app.bsky.embed.external#view")] 180 + ExternalView(Box<crate::app_bsky::embed::external::View<'a>>), 181 + #[serde(rename = "app.bsky.embed.record#view")] 182 + View(Box<crate::app_bsky::embed::record::View<'a>>), 183 + #[serde(rename = "app.bsky.embed.recordWithMedia#view")] 184 + RecordWithMediaView(Box<crate::app_bsky::embed::record_with_media::View<'a>>), 152 185 }
+4 -4
crates/jacquard-api/src/app_bsky/embed/record_with_media.rs
··· 18 18 #[serde(rename_all = "camelCase")] 19 19 pub struct RecordWithMedia<'a> { 20 20 #[serde(borrow)] 21 - pub media: RecordWithMediaRecordMedia<'a>, 21 + pub media: RecordWithMediaMedia<'a>, 22 22 #[serde(borrow)] 23 23 pub record: crate::app_bsky::embed::record::Record<'a>, 24 24 } ··· 35 35 )] 36 36 #[serde(tag = "$type")] 37 37 #[serde(bound(deserialize = "'de: 'a"))] 38 - pub enum RecordWithMediaRecordMedia<'a> { 38 + pub enum RecordWithMediaMedia<'a> { 39 39 #[serde(rename = "app.bsky.embed.images")] 40 40 Images(Box<crate::app_bsky::embed::images::Images<'a>>), 41 41 #[serde(rename = "app.bsky.embed.video")] ··· 57 57 #[serde(rename_all = "camelCase")] 58 58 pub struct View<'a> { 59 59 #[serde(borrow)] 60 - pub media: ViewRecordMedia<'a>, 60 + pub media: ViewMedia<'a>, 61 61 #[serde(borrow)] 62 62 pub record: crate::app_bsky::embed::record::View<'a>, 63 63 } ··· 74 74 )] 75 75 #[serde(tag = "$type")] 76 76 #[serde(bound(deserialize = "'de: 'a"))] 77 - pub enum ViewRecordMedia<'a> { 77 + pub enum ViewMedia<'a> { 78 78 #[serde(rename = "app.bsky.embed.images#view")] 79 79 ImagesView(Box<crate::app_bsky::embed::images::View<'a>>), 80 80 #[serde(rename = "app.bsky.embed.video#view")]
+70 -13
crates/jacquard-api/src/app_bsky/feed.rs
··· 196 196 pub post: crate::app_bsky::feed::PostView<'a>, 197 197 #[serde(skip_serializing_if = "std::option::Option::is_none")] 198 198 #[serde(borrow)] 199 - pub reason: std::option::Option<FeedViewPostRecordReason<'a>>, 199 + pub reason: std::option::Option<FeedViewPostReason<'a>>, 200 200 #[serde(skip_serializing_if = "std::option::Option::is_none")] 201 201 #[serde(borrow)] 202 202 pub reply: std::option::Option<crate::app_bsky::feed::ReplyRef<'a>>, ··· 218 218 )] 219 219 #[serde(tag = "$type")] 220 220 #[serde(bound(deserialize = "'de: 'a"))] 221 - pub enum FeedViewPostRecordReason<'a> {} 221 + pub enum FeedViewPostReason<'a> { 222 + #[serde(rename = "app.bsky.feed.defs#reasonRepost")] 223 + ReasonRepost(Box<crate::app_bsky::feed::ReasonRepost<'a>>), 224 + #[serde(rename = "app.bsky.feed.defs#reasonPin")] 225 + ReasonPin(Box<crate::app_bsky::feed::ReasonPin<'a>>), 226 + } 227 + 222 228 #[jacquard_derive::lexicon] 223 229 #[derive( 224 230 serde::Serialize, ··· 458 464 pub cid: jacquard_common::types::string::Cid<'a>, 459 465 #[serde(skip_serializing_if = "std::option::Option::is_none")] 460 466 #[serde(borrow)] 461 - pub embed: std::option::Option<PostViewRecordEmbed<'a>>, 467 + pub embed: std::option::Option<PostViewEmbed<'a>>, 462 468 pub indexed_at: jacquard_common::types::string::Datetime, 463 469 #[serde(skip_serializing_if = "std::option::Option::is_none")] 464 470 #[serde(borrow)] ··· 495 501 )] 496 502 #[serde(tag = "$type")] 497 503 #[serde(bound(deserialize = "'de: 'a"))] 498 - pub enum PostViewRecordEmbed<'a> { 504 + pub enum PostViewEmbed<'a> { 499 505 #[serde(rename = "app.bsky.embed.images#view")] 500 506 ImagesView(Box<crate::app_bsky::embed::images::View<'a>>), 501 507 #[serde(rename = "app.bsky.embed.video#view")] ··· 562 568 crate::app_bsky::actor::ProfileViewBasic<'a>, 563 569 >, 564 570 #[serde(borrow)] 565 - pub parent: ReplyRefRecordParent<'a>, 571 + pub parent: ReplyRefParent<'a>, 566 572 #[serde(borrow)] 567 - pub root: ReplyRefRecordRoot<'a>, 573 + pub root: ReplyRefRoot<'a>, 568 574 } 569 575 570 576 #[jacquard_derive::open_union] ··· 579 585 )] 580 586 #[serde(tag = "$type")] 581 587 #[serde(bound(deserialize = "'de: 'a"))] 582 - pub enum ReplyRefRecordParent<'a> {} 588 + pub enum ReplyRefParent<'a> { 589 + #[serde(rename = "app.bsky.feed.defs#postView")] 590 + PostView(Box<crate::app_bsky::feed::PostView<'a>>), 591 + #[serde(rename = "app.bsky.feed.defs#notFoundPost")] 592 + NotFoundPost(Box<crate::app_bsky::feed::NotFoundPost<'a>>), 593 + #[serde(rename = "app.bsky.feed.defs#blockedPost")] 594 + BlockedPost(Box<crate::app_bsky::feed::BlockedPost<'a>>), 595 + } 596 + 583 597 #[jacquard_derive::open_union] 584 598 #[derive( 585 599 serde::Serialize, ··· 592 606 )] 593 607 #[serde(tag = "$type")] 594 608 #[serde(bound(deserialize = "'de: 'a"))] 595 - pub enum ReplyRefRecordRoot<'a> {} 609 + pub enum ReplyRefRoot<'a> { 610 + #[serde(rename = "app.bsky.feed.defs#postView")] 611 + PostView(Box<crate::app_bsky::feed::PostView<'a>>), 612 + #[serde(rename = "app.bsky.feed.defs#notFoundPost")] 613 + NotFoundPost(Box<crate::app_bsky::feed::NotFoundPost<'a>>), 614 + #[serde(rename = "app.bsky.feed.defs#blockedPost")] 615 + BlockedPost(Box<crate::app_bsky::feed::BlockedPost<'a>>), 616 + } 617 + 596 618 ///Request that less content like the given feed item be shown in the feed 597 619 #[derive( 598 620 serde::Serialize, ··· 649 671 pub post: jacquard_common::types::string::AtUri<'a>, 650 672 #[serde(skip_serializing_if = "std::option::Option::is_none")] 651 673 #[serde(borrow)] 652 - pub reason: std::option::Option<SkeletonFeedPostRecordReason<'a>>, 674 + pub reason: std::option::Option<SkeletonFeedPostReason<'a>>, 653 675 } 654 676 655 677 #[jacquard_derive::open_union] ··· 664 686 )] 665 687 #[serde(tag = "$type")] 666 688 #[serde(bound(deserialize = "'de: 'a"))] 667 - pub enum SkeletonFeedPostRecordReason<'a> {} 689 + pub enum SkeletonFeedPostReason<'a> { 690 + #[serde(rename = "app.bsky.feed.defs#skeletonReasonRepost")] 691 + SkeletonReasonRepost(Box<crate::app_bsky::feed::SkeletonReasonRepost<'a>>), 692 + #[serde(rename = "app.bsky.feed.defs#skeletonReasonPin")] 693 + SkeletonReasonPin(Box<crate::app_bsky::feed::SkeletonReasonPin<'a>>), 694 + } 695 + 668 696 #[jacquard_derive::lexicon] 669 697 #[derive( 670 698 serde::Serialize, ··· 725 753 pub struct ThreadViewPost<'a> { 726 754 #[serde(skip_serializing_if = "std::option::Option::is_none")] 727 755 #[serde(borrow)] 728 - pub parent: std::option::Option<ThreadViewPostRecordParent<'a>>, 756 + pub parent: std::option::Option<ThreadViewPostParent<'a>>, 729 757 #[serde(borrow)] 730 758 pub post: crate::app_bsky::feed::PostView<'a>, 731 759 #[serde(skip_serializing_if = "std::option::Option::is_none")] 732 760 #[serde(borrow)] 733 - pub replies: std::option::Option<Vec<jacquard_common::types::value::Data<'a>>>, 761 + pub replies: std::option::Option<Vec<ThreadViewPostRepliesItem<'a>>>, 734 762 #[serde(skip_serializing_if = "std::option::Option::is_none")] 735 763 #[serde(borrow)] 736 764 pub thread_context: std::option::Option<crate::app_bsky::feed::ThreadContext<'a>>, ··· 748 776 )] 749 777 #[serde(tag = "$type")] 750 778 #[serde(bound(deserialize = "'de: 'a"))] 751 - pub enum ThreadViewPostRecordParent<'a> {} 779 + pub enum ThreadViewPostParent<'a> { 780 + #[serde(rename = "app.bsky.feed.defs#threadViewPost")] 781 + ThreadViewPost(Box<crate::app_bsky::feed::ThreadViewPost<'a>>), 782 + #[serde(rename = "app.bsky.feed.defs#notFoundPost")] 783 + NotFoundPost(Box<crate::app_bsky::feed::NotFoundPost<'a>>), 784 + #[serde(rename = "app.bsky.feed.defs#blockedPost")] 785 + BlockedPost(Box<crate::app_bsky::feed::BlockedPost<'a>>), 786 + } 787 + 788 + #[jacquard_derive::open_union] 789 + #[derive( 790 + serde::Serialize, 791 + serde::Deserialize, 792 + Debug, 793 + Clone, 794 + PartialEq, 795 + Eq, 796 + jacquard_derive::IntoStatic 797 + )] 798 + #[serde(tag = "$type")] 799 + #[serde(bound(deserialize = "'de: 'a"))] 800 + pub enum ThreadViewPostRepliesItem<'a> { 801 + #[serde(rename = "app.bsky.feed.defs#threadViewPost")] 802 + ThreadViewPost(Box<crate::app_bsky::feed::ThreadViewPost<'a>>), 803 + #[serde(rename = "app.bsky.feed.defs#notFoundPost")] 804 + NotFoundPost(Box<crate::app_bsky::feed::NotFoundPost<'a>>), 805 + #[serde(rename = "app.bsky.feed.defs#blockedPost")] 806 + BlockedPost(Box<crate::app_bsky::feed::BlockedPost<'a>>), 807 + } 808 + 752 809 #[jacquard_derive::lexicon] 753 810 #[derive( 754 811 serde::Serialize,
+1 -18
crates/jacquard-api/src/app_bsky/feed/generator.rs
··· 43 43 ///Self-label values 44 44 #[serde(skip_serializing_if = "std::option::Option::is_none")] 45 45 #[serde(borrow)] 46 - pub labels: std::option::Option<GeneratorRecordLabels<'a>>, 47 - } 48 - 49 - #[jacquard_derive::open_union] 50 - #[derive( 51 - serde::Serialize, 52 - serde::Deserialize, 53 - Debug, 54 - Clone, 55 - PartialEq, 56 - Eq, 57 - jacquard_derive::IntoStatic 58 - )] 59 - #[serde(tag = "$type")] 60 - #[serde(bound(deserialize = "'de: 'a"))] 61 - pub enum GeneratorRecordLabels<'a> { 62 - #[serde(rename = "com.atproto.label.defs#selfLabels")] 63 - DefsSelfLabels(Box<crate::com_atproto::label::SelfLabels<'a>>), 46 + pub labels: std::option::Option<crate::com_atproto::label::SelfLabels<'a>>, 64 47 } 65 48 66 49 impl jacquard_common::types::collection::Collection for Generator<'_> {
+2 -18
crates/jacquard-api/src/app_bsky/feed/get_actor_likes.rs
··· 57 57 PartialEq, 58 58 Eq, 59 59 thiserror::Error, 60 - miette::Diagnostic 60 + miette::Diagnostic, 61 + jacquard_derive::IntoStatic 61 62 )] 62 63 #[serde(tag = "error", content = "message")] 63 64 #[serde(bound(deserialize = "'de: 'a"))] ··· 86 87 Ok(()) 87 88 } 88 89 Self::Unknown(err) => write!(f, "Unknown error: {:?}", err), 89 - } 90 - } 91 - } 92 - 93 - impl jacquard_common::IntoStatic for GetActorLikesError<'_> { 94 - type Output = GetActorLikesError<'static>; 95 - fn into_static(self) -> Self::Output { 96 - match self { 97 - GetActorLikesError::BlockedActor(v) => { 98 - GetActorLikesError::BlockedActor(v.into_static()) 99 - } 100 - GetActorLikesError::BlockedByActor(v) => { 101 - GetActorLikesError::BlockedByActor(v.into_static()) 102 - } 103 - GetActorLikesError::Unknown(v) => { 104 - GetActorLikesError::Unknown(v.into_static()) 105 - } 106 90 } 107 91 } 108 92 }
+2 -18
crates/jacquard-api/src/app_bsky/feed/get_author_feed.rs
··· 65 65 PartialEq, 66 66 Eq, 67 67 thiserror::Error, 68 - miette::Diagnostic 68 + miette::Diagnostic, 69 + jacquard_derive::IntoStatic 69 70 )] 70 71 #[serde(tag = "error", content = "message")] 71 72 #[serde(bound(deserialize = "'de: 'a"))] ··· 94 95 Ok(()) 95 96 } 96 97 Self::Unknown(err) => write!(f, "Unknown error: {:?}", err), 97 - } 98 - } 99 - } 100 - 101 - impl jacquard_common::IntoStatic for GetAuthorFeedError<'_> { 102 - type Output = GetAuthorFeedError<'static>; 103 - fn into_static(self) -> Self::Output { 104 - match self { 105 - GetAuthorFeedError::BlockedActor(v) => { 106 - GetAuthorFeedError::BlockedActor(v.into_static()) 107 - } 108 - GetAuthorFeedError::BlockedByActor(v) => { 109 - GetAuthorFeedError::BlockedByActor(v.into_static()) 110 - } 111 - GetAuthorFeedError::Unknown(v) => { 112 - GetAuthorFeedError::Unknown(v.into_static()) 113 - } 114 98 } 115 99 } 116 100 }
+2 -11
crates/jacquard-api/src/app_bsky/feed/get_feed.rs
··· 57 57 PartialEq, 58 58 Eq, 59 59 thiserror::Error, 60 - miette::Diagnostic 60 + miette::Diagnostic, 61 + jacquard_derive::IntoStatic 61 62 )] 62 63 #[serde(tag = "error", content = "message")] 63 64 #[serde(bound(deserialize = "'de: 'a"))] ··· 77 78 Ok(()) 78 79 } 79 80 Self::Unknown(err) => write!(f, "Unknown error: {:?}", err), 80 - } 81 - } 82 - } 83 - 84 - impl jacquard_common::IntoStatic for GetFeedError<'_> { 85 - type Output = GetFeedError<'static>; 86 - fn into_static(self) -> Self::Output { 87 - match self { 88 - GetFeedError::UnknownFeed(v) => GetFeedError::UnknownFeed(v.into_static()), 89 - GetFeedError::Unknown(v) => GetFeedError::Unknown(v.into_static()), 90 81 } 91 82 } 92 83 }
+2 -15
crates/jacquard-api/src/app_bsky/feed/get_feed_skeleton.rs
··· 61 61 PartialEq, 62 62 Eq, 63 63 thiserror::Error, 64 - miette::Diagnostic 64 + miette::Diagnostic, 65 + jacquard_derive::IntoStatic 65 66 )] 66 67 #[serde(tag = "error", content = "message")] 67 68 #[serde(bound(deserialize = "'de: 'a"))] ··· 81 82 Ok(()) 82 83 } 83 84 Self::Unknown(err) => write!(f, "Unknown error: {:?}", err), 84 - } 85 - } 86 - } 87 - 88 - impl jacquard_common::IntoStatic for GetFeedSkeletonError<'_> { 89 - type Output = GetFeedSkeletonError<'static>; 90 - fn into_static(self) -> Self::Output { 91 - match self { 92 - GetFeedSkeletonError::UnknownFeed(v) => { 93 - GetFeedSkeletonError::UnknownFeed(v.into_static()) 94 - } 95 - GetFeedSkeletonError::Unknown(v) => { 96 - GetFeedSkeletonError::Unknown(v.into_static()) 97 - } 98 85 } 99 86 } 100 87 }
+2 -13
crates/jacquard-api/src/app_bsky/feed/get_list_feed.rs
··· 57 57 PartialEq, 58 58 Eq, 59 59 thiserror::Error, 60 - miette::Diagnostic 60 + miette::Diagnostic, 61 + jacquard_derive::IntoStatic 61 62 )] 62 63 #[serde(tag = "error", content = "message")] 63 64 #[serde(bound(deserialize = "'de: 'a"))] ··· 77 78 Ok(()) 78 79 } 79 80 Self::Unknown(err) => write!(f, "Unknown error: {:?}", err), 80 - } 81 - } 82 - } 83 - 84 - impl jacquard_common::IntoStatic for GetListFeedError<'_> { 85 - type Output = GetListFeedError<'static>; 86 - fn into_static(self) -> Self::Output { 87 - match self { 88 - GetListFeedError::UnknownList(v) => { 89 - GetListFeedError::UnknownList(v.into_static()) 90 - } 91 - GetListFeedError::Unknown(v) => GetListFeedError::Unknown(v.into_static()), 92 81 } 93 82 } 94 83 }
+7 -20
crates/jacquard-api/src/app_bsky/feed/get_post_thread.rs
··· 41 41 #[serde(rename_all = "camelCase")] 42 42 pub struct GetPostThreadOutput<'a> { 43 43 #[serde(borrow)] 44 - pub thread: GetPostThreadOutputRecordThread<'a>, 44 + pub thread: GetPostThreadOutputThread<'a>, 45 45 #[serde(skip_serializing_if = "std::option::Option::is_none")] 46 46 #[serde(borrow)] 47 47 pub threadgate: std::option::Option<crate::app_bsky::feed::ThreadgateView<'a>>, ··· 59 59 )] 60 60 #[serde(tag = "$type")] 61 61 #[serde(bound(deserialize = "'de: 'a"))] 62 - pub enum GetPostThreadOutputRecordThread<'a> { 62 + pub enum GetPostThreadOutputThread<'a> { 63 63 #[serde(rename = "app.bsky.feed.defs#threadViewPost")] 64 - DefsThreadViewPost(Box<crate::app_bsky::feed::ThreadViewPost<'a>>), 64 + ThreadViewPost(Box<crate::app_bsky::feed::ThreadViewPost<'a>>), 65 65 #[serde(rename = "app.bsky.feed.defs#notFoundPost")] 66 - DefsNotFoundPost(Box<crate::app_bsky::feed::NotFoundPost<'a>>), 66 + NotFoundPost(Box<crate::app_bsky::feed::NotFoundPost<'a>>), 67 67 #[serde(rename = "app.bsky.feed.defs#blockedPost")] 68 - DefsBlockedPost(Box<crate::app_bsky::feed::BlockedPost<'a>>), 68 + BlockedPost(Box<crate::app_bsky::feed::BlockedPost<'a>>), 69 69 } 70 70 71 71 #[jacquard_derive::open_union] ··· 77 77 PartialEq, 78 78 Eq, 79 79 thiserror::Error, 80 - miette::Diagnostic 80 + miette::Diagnostic, 81 + jacquard_derive::IntoStatic 81 82 )] 82 83 #[serde(tag = "error", content = "message")] 83 84 #[serde(bound(deserialize = "'de: 'a"))] ··· 97 98 Ok(()) 98 99 } 99 100 Self::Unknown(err) => write!(f, "Unknown error: {:?}", err), 100 - } 101 - } 102 - } 103 - 104 - impl jacquard_common::IntoStatic for GetPostThreadError<'_> { 105 - type Output = GetPostThreadError<'static>; 106 - fn into_static(self) -> Self::Output { 107 - match self { 108 - GetPostThreadError::NotFound(v) => { 109 - GetPostThreadError::NotFound(v.into_static()) 110 - } 111 - GetPostThreadError::Unknown(v) => { 112 - GetPostThreadError::Unknown(v.into_static()) 113 - } 114 101 } 115 102 } 116 103 }
+3 -20
crates/jacquard-api/src/app_bsky/feed/post.rs
··· 44 44 pub created_at: jacquard_common::types::string::Datetime, 45 45 #[serde(skip_serializing_if = "std::option::Option::is_none")] 46 46 #[serde(borrow)] 47 - pub embed: std::option::Option<PostRecordEmbed<'a>>, 47 + pub embed: std::option::Option<PostEmbed<'a>>, 48 48 ///DEPRECATED: replaced by app.bsky.richtext.facet. 49 49 #[serde(skip_serializing_if = "std::option::Option::is_none")] 50 50 #[serde(borrow)] ··· 56 56 ///Self-label values for this post. Effectively content warnings. 57 57 #[serde(skip_serializing_if = "std::option::Option::is_none")] 58 58 #[serde(borrow)] 59 - pub labels: std::option::Option<PostRecordLabels<'a>>, 59 + pub labels: std::option::Option<crate::com_atproto::label::SelfLabels<'a>>, 60 60 ///Indicates human language of post primary text content. 61 61 #[serde(skip_serializing_if = "std::option::Option::is_none")] 62 62 pub langs: std::option::Option<Vec<jacquard_common::types::string::Language>>, ··· 84 84 )] 85 85 #[serde(tag = "$type")] 86 86 #[serde(bound(deserialize = "'de: 'a"))] 87 - pub enum PostRecordEmbed<'a> { 87 + pub enum PostEmbed<'a> { 88 88 #[serde(rename = "app.bsky.embed.images")] 89 89 Images(Box<crate::app_bsky::embed::images::Images<'a>>), 90 90 #[serde(rename = "app.bsky.embed.video")] ··· 95 95 Record(Box<crate::app_bsky::embed::record::Record<'a>>), 96 96 #[serde(rename = "app.bsky.embed.recordWithMedia")] 97 97 RecordWithMedia(Box<crate::app_bsky::embed::record_with_media::RecordWithMedia<'a>>), 98 - } 99 - 100 - #[jacquard_derive::open_union] 101 - #[derive( 102 - serde::Serialize, 103 - serde::Deserialize, 104 - Debug, 105 - Clone, 106 - PartialEq, 107 - Eq, 108 - jacquard_derive::IntoStatic 109 - )] 110 - #[serde(tag = "$type")] 111 - #[serde(bound(deserialize = "'de: 'a"))] 112 - pub enum PostRecordLabels<'a> { 113 - #[serde(rename = "com.atproto.label.defs#selfLabels")] 114 - DefsSelfLabels(Box<crate::com_atproto::label::SelfLabels<'a>>), 115 98 } 116 99 117 100 impl jacquard_common::types::collection::Collection for Post<'_> {
+1 -1
crates/jacquard-api/src/app_bsky/feed/postgate.rs
··· 42 42 #[serde(skip_serializing_if = "std::option::Option::is_none")] 43 43 #[serde(borrow)] 44 44 pub embedding_rules: std::option::Option< 45 - Vec<jacquard_common::types::value::Data<'a>>, 45 + Vec<crate::app_bsky::feed::postgate::DisableRule<'a>>, 46 46 >, 47 47 ///Reference (AT-URI) to the post record. 48 48 #[serde(borrow)]
+2 -13
crates/jacquard-api/src/app_bsky/feed/search_posts.rs
··· 92 92 PartialEq, 93 93 Eq, 94 94 thiserror::Error, 95 - miette::Diagnostic 95 + miette::Diagnostic, 96 + jacquard_derive::IntoStatic 96 97 )] 97 98 #[serde(tag = "error", content = "message")] 98 99 #[serde(bound(deserialize = "'de: 'a"))] ··· 112 113 Ok(()) 113 114 } 114 115 Self::Unknown(err) => write!(f, "Unknown error: {:?}", err), 115 - } 116 - } 117 - } 118 - 119 - impl jacquard_common::IntoStatic for SearchPostsError<'_> { 120 - type Output = SearchPostsError<'static>; 121 - fn into_static(self) -> Self::Output { 122 - match self { 123 - SearchPostsError::BadQueryString(v) => { 124 - SearchPostsError::BadQueryString(v.into_static()) 125 - } 126 - SearchPostsError::Unknown(v) => SearchPostsError::Unknown(v.into_static()), 127 116 } 128 117 } 129 118 }
+24 -1
crates/jacquard-api/src/app_bsky/feed/threadgate.rs
··· 64 64 ///List of rules defining who can reply to this post. If value is an empty array, no one can reply. If value is undefined, anyone can reply. 65 65 #[serde(skip_serializing_if = "std::option::Option::is_none")] 66 66 #[serde(borrow)] 67 - pub allow: std::option::Option<Vec<jacquard_common::types::value::Data<'a>>>, 67 + pub allow: std::option::Option<Vec<ThreadgateAllowItem<'a>>>, 68 68 pub created_at: jacquard_common::types::string::Datetime, 69 69 ///List of hidden reply URIs. 70 70 #[serde(skip_serializing_if = "std::option::Option::is_none")] ··· 75 75 ///Reference (AT-URI) to the post record. 76 76 #[serde(borrow)] 77 77 pub post: jacquard_common::types::string::AtUri<'a>, 78 + } 79 + 80 + #[jacquard_derive::open_union] 81 + #[derive( 82 + serde::Serialize, 83 + serde::Deserialize, 84 + Debug, 85 + Clone, 86 + PartialEq, 87 + Eq, 88 + jacquard_derive::IntoStatic 89 + )] 90 + #[serde(tag = "$type")] 91 + #[serde(bound(deserialize = "'de: 'a"))] 92 + pub enum ThreadgateAllowItem<'a> { 93 + #[serde(rename = "app.bsky.feed.threadgate#mentionRule")] 94 + MentionRule(Box<crate::app_bsky::feed::threadgate::MentionRule<'a>>), 95 + #[serde(rename = "app.bsky.feed.threadgate#followerRule")] 96 + FollowerRule(Box<crate::app_bsky::feed::threadgate::FollowerRule<'a>>), 97 + #[serde(rename = "app.bsky.feed.threadgate#followingRule")] 98 + FollowingRule(Box<crate::app_bsky::feed::threadgate::FollowingRule<'a>>), 99 + #[serde(rename = "app.bsky.feed.threadgate#listRule")] 100 + ListRule(Box<crate::app_bsky::feed::threadgate::ListRule<'a>>), 78 101 } 79 102 80 103 impl jacquard_common::types::collection::Collection for Threadgate<'_> {
+22 -16
crates/jacquard-api/src/app_bsky/graph/get_relationships.rs
··· 43 43 #[serde(borrow)] 44 44 pub actor: std::option::Option<jacquard_common::types::string::Did<'a>>, 45 45 #[serde(borrow)] 46 - pub relationships: Vec<jacquard_common::types::value::Data<'a>>, 46 + pub relationships: Vec<GetRelationshipsOutputRelationshipsItem<'a>>, 47 + } 48 + 49 + #[jacquard_derive::open_union] 50 + #[derive( 51 + serde::Serialize, 52 + serde::Deserialize, 53 + Debug, 54 + Clone, 55 + PartialEq, 56 + Eq, 57 + jacquard_derive::IntoStatic 58 + )] 59 + #[serde(tag = "$type")] 60 + #[serde(bound(deserialize = "'de: 'a"))] 61 + pub enum GetRelationshipsOutputRelationshipsItem<'a> { 62 + #[serde(rename = "app.bsky.graph.defs#relationship")] 63 + Relationship(Box<crate::app_bsky::graph::Relationship<'a>>), 64 + #[serde(rename = "app.bsky.graph.defs#notFoundActor")] 65 + NotFoundActor(Box<crate::app_bsky::graph::NotFoundActor<'a>>), 47 66 } 48 67 49 68 #[jacquard_derive::open_union] ··· 55 74 PartialEq, 56 75 Eq, 57 76 thiserror::Error, 58 - miette::Diagnostic 77 + miette::Diagnostic, 78 + jacquard_derive::IntoStatic 59 79 )] 60 80 #[serde(tag = "error", content = "message")] 61 81 #[serde(bound(deserialize = "'de: 'a"))] ··· 76 96 Ok(()) 77 97 } 78 98 Self::Unknown(err) => write!(f, "Unknown error: {:?}", err), 79 - } 80 - } 81 - } 82 - 83 - impl jacquard_common::IntoStatic for GetRelationshipsError<'_> { 84 - type Output = GetRelationshipsError<'static>; 85 - fn into_static(self) -> Self::Output { 86 - match self { 87 - GetRelationshipsError::ActorNotFound(v) => { 88 - GetRelationshipsError::ActorNotFound(v.into_static()) 89 - } 90 - GetRelationshipsError::Unknown(v) => { 91 - GetRelationshipsError::Unknown(v.into_static()) 92 - } 93 99 } 94 100 } 95 101 }
+1 -18
crates/jacquard-api/src/app_bsky/graph/list.rs
··· 32 32 >, 33 33 #[serde(skip_serializing_if = "std::option::Option::is_none")] 34 34 #[serde(borrow)] 35 - pub labels: std::option::Option<ListRecordLabels<'a>>, 35 + pub labels: std::option::Option<crate::com_atproto::label::SelfLabels<'a>>, 36 36 ///Display name for list; can not be empty. 37 37 #[serde(borrow)] 38 38 pub name: jacquard_common::CowStr<'a>, 39 39 ///Defines the purpose of the list (aka, moderation-oriented or curration-oriented) 40 40 #[serde(borrow)] 41 41 pub purpose: crate::app_bsky::graph::ListPurpose<'a>, 42 - } 43 - 44 - #[jacquard_derive::open_union] 45 - #[derive( 46 - serde::Serialize, 47 - serde::Deserialize, 48 - Debug, 49 - Clone, 50 - PartialEq, 51 - Eq, 52 - jacquard_derive::IntoStatic 53 - )] 54 - #[serde(tag = "$type")] 55 - #[serde(bound(deserialize = "'de: 'a"))] 56 - pub enum ListRecordLabels<'a> { 57 - #[serde(rename = "com.atproto.label.defs#selfLabels")] 58 - DefsSelfLabels(Box<crate::com_atproto::label::SelfLabels<'a>>), 59 42 } 60 43 61 44 impl jacquard_common::types::collection::Collection for List<'_> {
+20 -1
crates/jacquard-api/src/app_bsky/labeler/get_services.rs
··· 38 38 #[serde(rename_all = "camelCase")] 39 39 pub struct GetServicesOutput<'a> { 40 40 #[serde(borrow)] 41 - pub views: Vec<jacquard_common::types::value::Data<'a>>, 41 + pub views: Vec<GetServicesOutputViewsItem<'a>>, 42 + } 43 + 44 + #[jacquard_derive::open_union] 45 + #[derive( 46 + serde::Serialize, 47 + serde::Deserialize, 48 + Debug, 49 + Clone, 50 + PartialEq, 51 + Eq, 52 + jacquard_derive::IntoStatic 53 + )] 54 + #[serde(tag = "$type")] 55 + #[serde(bound(deserialize = "'de: 'a"))] 56 + pub enum GetServicesOutputViewsItem<'a> { 57 + #[serde(rename = "app.bsky.labeler.defs#labelerView")] 58 + LabelerView(Box<crate::app_bsky::labeler::LabelerView<'a>>), 59 + #[serde(rename = "app.bsky.labeler.defs#labelerViewDetailed")] 60 + LabelerViewDetailed(Box<crate::app_bsky::labeler::LabelerViewDetailed<'a>>), 42 61 } 43 62 44 63 ///Response type for
+1 -18
crates/jacquard-api/src/app_bsky/labeler/service.rs
··· 21 21 pub created_at: jacquard_common::types::string::Datetime, 22 22 #[serde(skip_serializing_if = "std::option::Option::is_none")] 23 23 #[serde(borrow)] 24 - pub labels: std::option::Option<ServiceRecordLabels<'a>>, 24 + pub labels: std::option::Option<crate::com_atproto::label::SelfLabels<'a>>, 25 25 #[serde(borrow)] 26 26 pub policies: crate::app_bsky::labeler::LabelerPolicies<'a>, 27 27 ///The set of report reason 'codes' which are in-scope for this service to review and action. These usually align to policy categories. If not defined (distinct from empty array), all reason types are allowed. ··· 42 42 pub subject_types: std::option::Option< 43 43 Vec<crate::com_atproto::moderation::SubjectType<'a>>, 44 44 >, 45 - } 46 - 47 - #[jacquard_derive::open_union] 48 - #[derive( 49 - serde::Serialize, 50 - serde::Deserialize, 51 - Debug, 52 - Clone, 53 - PartialEq, 54 - Eq, 55 - jacquard_derive::IntoStatic 56 - )] 57 - #[serde(tag = "$type")] 58 - #[serde(bound(deserialize = "'de: 'a"))] 59 - pub enum ServiceRecordLabels<'a> { 60 - #[serde(rename = "com.atproto.label.defs#selfLabels")] 61 - DefsSelfLabels(Box<crate::com_atproto::label::SelfLabels<'a>>), 62 45 } 63 46 64 47 impl jacquard_common::types::collection::Collection for Service<'_> {
+22 -1
crates/jacquard-api/src/app_bsky/richtext/facet.rs
··· 53 53 #[serde(rename_all = "camelCase")] 54 54 pub struct Facet<'a> { 55 55 #[serde(borrow)] 56 - pub features: Vec<jacquard_common::types::value::Data<'a>>, 56 + pub features: Vec<FacetFeaturesItem<'a>>, 57 57 #[serde(borrow)] 58 58 pub index: crate::app_bsky::richtext::facet::ByteSlice<'a>, 59 + } 60 + 61 + #[jacquard_derive::open_union] 62 + #[derive( 63 + serde::Serialize, 64 + serde::Deserialize, 65 + Debug, 66 + Clone, 67 + PartialEq, 68 + Eq, 69 + jacquard_derive::IntoStatic 70 + )] 71 + #[serde(tag = "$type")] 72 + #[serde(bound(deserialize = "'de: 'a"))] 73 + pub enum FacetFeaturesItem<'a> { 74 + #[serde(rename = "app.bsky.richtext.facet#mention")] 75 + Mention(Box<crate::app_bsky::richtext::facet::Mention<'a>>), 76 + #[serde(rename = "app.bsky.richtext.facet#link")] 77 + Link(Box<crate::app_bsky::richtext::facet::Link<'a>>), 78 + #[serde(rename = "app.bsky.richtext.facet#tag")] 79 + Tag(Box<crate::app_bsky::richtext::facet::Tag<'a>>), 59 80 } 60 81 61 82 ///Facet feature for mention of another account. The text is usually a handle, including a '@' prefix, but the facet reference is a DID.
+1 -18
crates/jacquard-api/src/app_bsky/unspecced/get_post_thread_other_v2.rs
··· 85 85 #[serde(borrow)] 86 86 pub uri: jacquard_common::types::string::AtUri<'a>, 87 87 #[serde(borrow)] 88 - pub value: ThreadItemRecordValue<'a>, 89 - } 90 - 91 - #[jacquard_derive::open_union] 92 - #[derive( 93 - serde::Serialize, 94 - serde::Deserialize, 95 - Debug, 96 - Clone, 97 - PartialEq, 98 - Eq, 99 - jacquard_derive::IntoStatic 100 - )] 101 - #[serde(tag = "$type")] 102 - #[serde(bound(deserialize = "'de: 'a"))] 103 - pub enum ThreadItemRecordValue<'a> { 104 - #[serde(rename = "app.bsky.unspecced.defs#threadItemPost")] 105 - DefsThreadItemPost(Box<crate::app_bsky::unspecced::ThreadItemPost<'a>>), 88 + pub value: crate::app_bsky::unspecced::ThreadItemPost<'a>, 106 89 }
+6 -6
crates/jacquard-api/src/app_bsky/unspecced/get_post_thread_v2.rs
··· 104 104 #[serde(borrow)] 105 105 pub uri: jacquard_common::types::string::AtUri<'a>, 106 106 #[serde(borrow)] 107 - pub value: ThreadItemRecordValue<'a>, 107 + pub value: ThreadItemValue<'a>, 108 108 } 109 109 110 110 #[jacquard_derive::open_union] ··· 119 119 )] 120 120 #[serde(tag = "$type")] 121 121 #[serde(bound(deserialize = "'de: 'a"))] 122 - pub enum ThreadItemRecordValue<'a> { 122 + pub enum ThreadItemValue<'a> { 123 123 #[serde(rename = "app.bsky.unspecced.defs#threadItemPost")] 124 - DefsThreadItemPost(Box<crate::app_bsky::unspecced::ThreadItemPost<'a>>), 124 + ThreadItemPost(Box<crate::app_bsky::unspecced::ThreadItemPost<'a>>), 125 125 #[serde(rename = "app.bsky.unspecced.defs#threadItemNoUnauthenticated")] 126 - DefsThreadItemNoUnauthenticated( 126 + ThreadItemNoUnauthenticated( 127 127 Box<crate::app_bsky::unspecced::ThreadItemNoUnauthenticated<'a>>, 128 128 ), 129 129 #[serde(rename = "app.bsky.unspecced.defs#threadItemNotFound")] 130 - DefsThreadItemNotFound(Box<crate::app_bsky::unspecced::ThreadItemNotFound<'a>>), 130 + ThreadItemNotFound(Box<crate::app_bsky::unspecced::ThreadItemNotFound<'a>>), 131 131 #[serde(rename = "app.bsky.unspecced.defs#threadItemBlocked")] 132 - DefsThreadItemBlocked(Box<crate::app_bsky::unspecced::ThreadItemBlocked<'a>>), 132 + ThreadItemBlocked(Box<crate::app_bsky::unspecced::ThreadItemBlocked<'a>>), 133 133 }
+2 -21
crates/jacquard-api/src/app_bsky/unspecced/init_age_assurance.rs
··· 66 66 PartialEq, 67 67 Eq, 68 68 thiserror::Error, 69 - miette::Diagnostic 69 + miette::Diagnostic, 70 + jacquard_derive::IntoStatic 70 71 )] 71 72 #[serde(tag = "error", content = "message")] 72 73 #[serde(bound(deserialize = "'de: 'a"))] ··· 104 105 Ok(()) 105 106 } 106 107 Self::Unknown(err) => write!(f, "Unknown error: {:?}", err), 107 - } 108 - } 109 - } 110 - 111 - impl jacquard_common::IntoStatic for InitAgeAssuranceError<'_> { 112 - type Output = InitAgeAssuranceError<'static>; 113 - fn into_static(self) -> Self::Output { 114 - match self { 115 - InitAgeAssuranceError::InvalidEmail(v) => { 116 - InitAgeAssuranceError::InvalidEmail(v.into_static()) 117 - } 118 - InitAgeAssuranceError::DidTooLong(v) => { 119 - InitAgeAssuranceError::DidTooLong(v.into_static()) 120 - } 121 - InitAgeAssuranceError::InvalidInitiation(v) => { 122 - InitAgeAssuranceError::InvalidInitiation(v.into_static()) 123 - } 124 - InitAgeAssuranceError::Unknown(v) => { 125 - InitAgeAssuranceError::Unknown(v.into_static()) 126 - } 127 108 } 128 109 } 129 110 }
+2 -15
crates/jacquard-api/src/app_bsky/unspecced/search_actors_skeleton.rs
··· 66 66 PartialEq, 67 67 Eq, 68 68 thiserror::Error, 69 - miette::Diagnostic 69 + miette::Diagnostic, 70 + jacquard_derive::IntoStatic 70 71 )] 71 72 #[serde(tag = "error", content = "message")] 72 73 #[serde(bound(deserialize = "'de: 'a"))] ··· 86 87 Ok(()) 87 88 } 88 89 Self::Unknown(err) => write!(f, "Unknown error: {:?}", err), 89 - } 90 - } 91 - } 92 - 93 - impl jacquard_common::IntoStatic for SearchActorsSkeletonError<'_> { 94 - type Output = SearchActorsSkeletonError<'static>; 95 - fn into_static(self) -> Self::Output { 96 - match self { 97 - SearchActorsSkeletonError::BadQueryString(v) => { 98 - SearchActorsSkeletonError::BadQueryString(v.into_static()) 99 - } 100 - SearchActorsSkeletonError::Unknown(v) => { 101 - SearchActorsSkeletonError::Unknown(v.into_static()) 102 - } 103 90 } 104 91 } 105 92 }
+2 -15
crates/jacquard-api/src/app_bsky/unspecced/search_posts_skeleton.rs
··· 95 95 PartialEq, 96 96 Eq, 97 97 thiserror::Error, 98 - miette::Diagnostic 98 + miette::Diagnostic, 99 + jacquard_derive::IntoStatic 99 100 )] 100 101 #[serde(tag = "error", content = "message")] 101 102 #[serde(bound(deserialize = "'de: 'a"))] ··· 115 116 Ok(()) 116 117 } 117 118 Self::Unknown(err) => write!(f, "Unknown error: {:?}", err), 118 - } 119 - } 120 - } 121 - 122 - impl jacquard_common::IntoStatic for SearchPostsSkeletonError<'_> { 123 - type Output = SearchPostsSkeletonError<'static>; 124 - fn into_static(self) -> Self::Output { 125 - match self { 126 - SearchPostsSkeletonError::BadQueryString(v) => { 127 - SearchPostsSkeletonError::BadQueryString(v.into_static()) 128 - } 129 - SearchPostsSkeletonError::Unknown(v) => { 130 - SearchPostsSkeletonError::Unknown(v.into_static()) 131 - } 132 119 } 133 120 } 134 121 }
+2 -15
crates/jacquard-api/src/app_bsky/unspecced/search_starter_packs_skeleton.rs
··· 64 64 PartialEq, 65 65 Eq, 66 66 thiserror::Error, 67 - miette::Diagnostic 67 + miette::Diagnostic, 68 + jacquard_derive::IntoStatic 68 69 )] 69 70 #[serde(tag = "error", content = "message")] 70 71 #[serde(bound(deserialize = "'de: 'a"))] ··· 84 85 Ok(()) 85 86 } 86 87 Self::Unknown(err) => write!(f, "Unknown error: {:?}", err), 87 - } 88 - } 89 - } 90 - 91 - impl jacquard_common::IntoStatic for SearchStarterPacksSkeletonError<'_> { 92 - type Output = SearchStarterPacksSkeletonError<'static>; 93 - fn into_static(self) -> Self::Output { 94 - match self { 95 - SearchStarterPacksSkeletonError::BadQueryString(v) => { 96 - SearchStarterPacksSkeletonError::BadQueryString(v.into_static()) 97 - } 98 - SearchStarterPacksSkeletonError::Unknown(v) => { 99 - SearchStarterPacksSkeletonError::Unknown(v.into_static()) 100 - } 101 88 } 102 89 } 103 90 }
+2 -19
crates/jacquard-api/src/app_ocho/auth/update_email.rs
··· 47 47 PartialEq, 48 48 Eq, 49 49 thiserror::Error, 50 - miette::Diagnostic 50 + miette::Diagnostic, 51 + jacquard_derive::IntoStatic 51 52 )] 52 53 #[serde(tag = "error", content = "message")] 53 54 #[serde(bound(deserialize = "'de: 'a"))] ··· 85 86 Ok(()) 86 87 } 87 88 Self::Unknown(err) => write!(f, "Unknown error: {:?}", err), 88 - } 89 - } 90 - } 91 - 92 - impl jacquard_common::IntoStatic for UpdateEmailError<'_> { 93 - type Output = UpdateEmailError<'static>; 94 - fn into_static(self) -> Self::Output { 95 - match self { 96 - UpdateEmailError::ExpiredToken(v) => { 97 - UpdateEmailError::ExpiredToken(v.into_static()) 98 - } 99 - UpdateEmailError::InvalidToken(v) => { 100 - UpdateEmailError::InvalidToken(v.into_static()) 101 - } 102 - UpdateEmailError::TokenRequired(v) => { 103 - UpdateEmailError::TokenRequired(v.into_static()) 104 - } 105 - UpdateEmailError::Unknown(v) => UpdateEmailError::Unknown(v.into_static()), 106 89 } 107 90 } 108 91 }
+20 -1
crates/jacquard-api/src/app_ocho/plugin.rs
··· 327 327 pub expo_go: crate::app_ocho::plugin::ExpoGo<'a>, 328 328 } 329 329 330 - pub type Plugin<'a> = Vec<jacquard_common::types::value::Data<'a>>; 330 + #[jacquard_derive::open_union] 331 + #[derive( 332 + serde::Serialize, 333 + serde::Deserialize, 334 + Debug, 335 + Clone, 336 + PartialEq, 337 + Eq, 338 + jacquard_derive::IntoStatic 339 + )] 340 + #[serde(tag = "$type")] 341 + #[serde(bound(deserialize = "'de: 'a"))] 342 + pub enum PluginItem<'a> { 343 + #[serde(rename = "app.ocho.plugin.defs#stringId")] 344 + StringId(Box<crate::app_ocho::plugin::StringId<'a>>), 345 + #[serde(rename = "app.ocho.plugin.defs#pluginConfig")] 346 + PluginConfig(Box<crate::app_ocho::plugin::PluginConfig<'a>>), 347 + } 348 + 349 + pub type Plugin<'a> = Vec<PluginItem<'a>>; 331 350 pub type PluginConfig<'a> = jacquard_common::types::value::Data<'a>; 332 351 ///A string identifier for a plugin, used to reference it in the app. 333 352 pub type StringId<'a> = jacquard_common::CowStr<'a>;
+2 -14
crates/jacquard-api/src/app_ocho/state/get_config.rs
··· 37 37 PartialEq, 38 38 Eq, 39 39 thiserror::Error, 40 - miette::Diagnostic 40 + miette::Diagnostic, 41 + jacquard_derive::IntoStatic 41 42 )] 42 43 #[serde(tag = "error", content = "message")] 43 44 #[serde(bound(deserialize = "'de: 'a"))] ··· 66 67 Ok(()) 67 68 } 68 69 Self::Unknown(err) => write!(f, "Unknown error: {:?}", err), 69 - } 70 - } 71 - } 72 - 73 - impl jacquard_common::IntoStatic for GetConfigError<'_> { 74 - type Output = GetConfigError<'static>; 75 - fn into_static(self) -> Self::Output { 76 - match self { 77 - GetConfigError::InvalidId(v) => GetConfigError::InvalidId(v.into_static()), 78 - GetConfigError::InvalidServiceAuth(v) => { 79 - GetConfigError::InvalidServiceAuth(v.into_static()) 80 - } 81 - GetConfigError::Unknown(v) => GetConfigError::Unknown(v.into_static()), 82 70 } 83 71 } 84 72 }
+1 -14
crates/jacquard-api/src/blog_pckt/post.rs
··· 18 18 #[serde(rename_all = "camelCase")] 19 19 pub struct Post<'a> { 20 20 #[serde(borrow)] 21 - pub blocks: PostRecordBlocks<'a>, 21 + pub blocks: jacquard_common::types::value::Data<'a>, 22 22 #[serde(borrow)] 23 23 pub blog: crate::com_atproto::repo::strong_ref::StrongRef<'a>, 24 24 #[serde(skip_serializing_if = "std::option::Option::is_none")] ··· 43 43 pub url: jacquard_common::types::string::Uri<'a>, 44 44 } 45 45 46 - #[jacquard_derive::open_union] 47 - #[derive( 48 - serde::Serialize, 49 - serde::Deserialize, 50 - Debug, 51 - Clone, 52 - PartialEq, 53 - Eq, 54 - jacquard_derive::IntoStatic 55 - )] 56 - #[serde(tag = "$type")] 57 - #[serde(bound(deserialize = "'de: 'a"))] 58 - pub enum PostRecordBlocks<'a> {} 59 46 impl jacquard_common::types::collection::Collection for Post<'_> { 60 47 const NSID: &'static str = "blog.pckt.post"; 61 48 }
+1 -18
crates/jacquard-api/src/blog_pckt/publication.rs
··· 42 42 ///Theme configuration as an open union; accepts blog.pckt.theme and future types. 43 43 #[serde(skip_serializing_if = "std::option::Option::is_none")] 44 44 #[serde(borrow)] 45 - pub theme: std::option::Option<PublicationRecordTheme<'a>>, 45 + pub theme: std::option::Option<crate::blog_pckt::theme::Theme<'a>>, 46 46 ///Timestamp when the publication was last updated (optional) 47 47 #[serde(skip_serializing_if = "std::option::Option::is_none")] 48 48 pub updated_at: std::option::Option<jacquard_common::types::string::Datetime>, 49 - } 50 - 51 - #[jacquard_derive::open_union] 52 - #[derive( 53 - serde::Serialize, 54 - serde::Deserialize, 55 - Debug, 56 - Clone, 57 - PartialEq, 58 - Eq, 59 - jacquard_derive::IntoStatic 60 - )] 61 - #[serde(tag = "$type")] 62 - #[serde(bound(deserialize = "'de: 'a"))] 63 - pub enum PublicationRecordTheme<'a> { 64 - #[serde(rename = "blog.pckt.theme")] 65 - Theme(Box<crate::blog_pckt::theme::Theme<'a>>), 66 49 } 67 50 68 51 impl jacquard_common::types::collection::Collection for Publication<'_> {
+53 -62
crates/jacquard-api/src/chat_bsky/convo.rs
··· 39 39 pub id: jacquard_common::CowStr<'a>, 40 40 #[serde(skip_serializing_if = "std::option::Option::is_none")] 41 41 #[serde(borrow)] 42 - pub last_message: std::option::Option<ConvoViewRecordLastMessage<'a>>, 42 + pub last_message: std::option::Option<ConvoViewLastMessage<'a>>, 43 43 #[serde(skip_serializing_if = "std::option::Option::is_none")] 44 44 #[serde(borrow)] 45 - pub last_reaction: std::option::Option<ConvoViewRecordLastReaction<'a>>, 45 + pub last_reaction: std::option::Option< 46 + crate::chat_bsky::convo::MessageAndReactionView<'a>, 47 + >, 46 48 #[serde(borrow)] 47 49 pub members: Vec<crate::chat_bsky::actor::ProfileViewBasic<'a>>, 48 50 pub muted: bool, ··· 66 68 )] 67 69 #[serde(tag = "$type")] 68 70 #[serde(bound(deserialize = "'de: 'a"))] 69 - pub enum ConvoViewRecordLastMessage<'a> {} 70 - #[jacquard_derive::open_union] 71 - #[derive( 72 - serde::Serialize, 73 - serde::Deserialize, 74 - Debug, 75 - Clone, 76 - PartialEq, 77 - Eq, 78 - jacquard_derive::IntoStatic 79 - )] 80 - #[serde(tag = "$type")] 81 - #[serde(bound(deserialize = "'de: 'a"))] 82 - pub enum ConvoViewRecordLastReaction<'a> {} 71 + pub enum ConvoViewLastMessage<'a> { 72 + #[serde(rename = "chat.bsky.convo.defs#messageView")] 73 + MessageView(Box<crate::chat_bsky::convo::MessageView<'a>>), 74 + #[serde(rename = "chat.bsky.convo.defs#deletedMessageView")] 75 + DeletedMessageView(Box<crate::chat_bsky::convo::DeletedMessageView<'a>>), 76 + } 77 + 83 78 #[jacquard_derive::lexicon] 84 79 #[derive( 85 80 serde::Serialize, ··· 134 129 #[serde(borrow)] 135 130 pub convo_id: jacquard_common::CowStr<'a>, 136 131 #[serde(borrow)] 137 - pub message: LogAddReactionRecordMessage<'a>, 132 + pub message: LogAddReactionMessage<'a>, 138 133 #[serde(borrow)] 139 134 pub reaction: crate::chat_bsky::convo::ReactionView<'a>, 140 135 #[serde(borrow)] ··· 153 148 )] 154 149 #[serde(tag = "$type")] 155 150 #[serde(bound(deserialize = "'de: 'a"))] 156 - pub enum LogAddReactionRecordMessage<'a> {} 151 + pub enum LogAddReactionMessage<'a> { 152 + #[serde(rename = "chat.bsky.convo.defs#messageView")] 153 + MessageView(Box<crate::chat_bsky::convo::MessageView<'a>>), 154 + #[serde(rename = "chat.bsky.convo.defs#deletedMessageView")] 155 + DeletedMessageView(Box<crate::chat_bsky::convo::DeletedMessageView<'a>>), 156 + } 157 + 157 158 #[jacquard_derive::lexicon] 158 159 #[derive( 159 160 serde::Serialize, ··· 187 188 #[serde(borrow)] 188 189 pub convo_id: jacquard_common::CowStr<'a>, 189 190 #[serde(borrow)] 190 - pub message: LogCreateMessageRecordMessage<'a>, 191 + pub message: LogCreateMessageMessage<'a>, 191 192 #[serde(borrow)] 192 193 pub rev: jacquard_common::CowStr<'a>, 193 194 } ··· 204 205 )] 205 206 #[serde(tag = "$type")] 206 207 #[serde(bound(deserialize = "'de: 'a"))] 207 - pub enum LogCreateMessageRecordMessage<'a> {} 208 + pub enum LogCreateMessageMessage<'a> { 209 + #[serde(rename = "chat.bsky.convo.defs#messageView")] 210 + MessageView(Box<crate::chat_bsky::convo::MessageView<'a>>), 211 + #[serde(rename = "chat.bsky.convo.defs#deletedMessageView")] 212 + DeletedMessageView(Box<crate::chat_bsky::convo::DeletedMessageView<'a>>), 213 + } 214 + 208 215 #[jacquard_derive::lexicon] 209 216 #[derive( 210 217 serde::Serialize, ··· 220 227 #[serde(borrow)] 221 228 pub convo_id: jacquard_common::CowStr<'a>, 222 229 #[serde(borrow)] 223 - pub message: LogDeleteMessageRecordMessage<'a>, 230 + pub message: LogDeleteMessageMessage<'a>, 224 231 #[serde(borrow)] 225 232 pub rev: jacquard_common::CowStr<'a>, 226 233 } ··· 237 244 )] 238 245 #[serde(tag = "$type")] 239 246 #[serde(bound(deserialize = "'de: 'a"))] 240 - pub enum LogDeleteMessageRecordMessage<'a> {} 247 + pub enum LogDeleteMessageMessage<'a> { 248 + #[serde(rename = "chat.bsky.convo.defs#messageView")] 249 + MessageView(Box<crate::chat_bsky::convo::MessageView<'a>>), 250 + #[serde(rename = "chat.bsky.convo.defs#deletedMessageView")] 251 + DeletedMessageView(Box<crate::chat_bsky::convo::DeletedMessageView<'a>>), 252 + } 253 + 241 254 #[jacquard_derive::lexicon] 242 255 #[derive( 243 256 serde::Serialize, ··· 289 302 #[serde(borrow)] 290 303 pub convo_id: jacquard_common::CowStr<'a>, 291 304 #[serde(borrow)] 292 - pub message: LogReadMessageRecordMessage<'a>, 305 + pub message: LogReadMessageMessage<'a>, 293 306 #[serde(borrow)] 294 307 pub rev: jacquard_common::CowStr<'a>, 295 308 } ··· 306 319 )] 307 320 #[serde(tag = "$type")] 308 321 #[serde(bound(deserialize = "'de: 'a"))] 309 - pub enum LogReadMessageRecordMessage<'a> {} 322 + pub enum LogReadMessageMessage<'a> { 323 + #[serde(rename = "chat.bsky.convo.defs#messageView")] 324 + MessageView(Box<crate::chat_bsky::convo::MessageView<'a>>), 325 + #[serde(rename = "chat.bsky.convo.defs#deletedMessageView")] 326 + DeletedMessageView(Box<crate::chat_bsky::convo::DeletedMessageView<'a>>), 327 + } 328 + 310 329 #[jacquard_derive::lexicon] 311 330 #[derive( 312 331 serde::Serialize, ··· 322 341 #[serde(borrow)] 323 342 pub convo_id: jacquard_common::CowStr<'a>, 324 343 #[serde(borrow)] 325 - pub message: LogRemoveReactionRecordMessage<'a>, 344 + pub message: LogRemoveReactionMessage<'a>, 326 345 #[serde(borrow)] 327 346 pub reaction: crate::chat_bsky::convo::ReactionView<'a>, 328 347 #[serde(borrow)] ··· 341 360 )] 342 361 #[serde(tag = "$type")] 343 362 #[serde(bound(deserialize = "'de: 'a"))] 344 - pub enum LogRemoveReactionRecordMessage<'a> {} 363 + pub enum LogRemoveReactionMessage<'a> { 364 + #[serde(rename = "chat.bsky.convo.defs#messageView")] 365 + MessageView(Box<crate::chat_bsky::convo::MessageView<'a>>), 366 + #[serde(rename = "chat.bsky.convo.defs#deletedMessageView")] 367 + DeletedMessageView(Box<crate::chat_bsky::convo::DeletedMessageView<'a>>), 368 + } 369 + 345 370 #[jacquard_derive::lexicon] 346 371 #[derive( 347 372 serde::Serialize, ··· 392 417 pub struct MessageInput<'a> { 393 418 #[serde(skip_serializing_if = "std::option::Option::is_none")] 394 419 #[serde(borrow)] 395 - pub embed: std::option::Option<MessageInputRecordEmbed<'a>>, 420 + pub embed: std::option::Option<crate::app_bsky::embed::record::Record<'a>>, 396 421 ///Annotations of text (mentions, URLs, hashtags, etc) 397 422 #[serde(skip_serializing_if = "std::option::Option::is_none")] 398 423 #[serde(borrow)] ··· 401 426 pub text: jacquard_common::CowStr<'a>, 402 427 } 403 428 404 - #[jacquard_derive::open_union] 405 - #[derive( 406 - serde::Serialize, 407 - serde::Deserialize, 408 - Debug, 409 - Clone, 410 - PartialEq, 411 - Eq, 412 - jacquard_derive::IntoStatic 413 - )] 414 - #[serde(tag = "$type")] 415 - #[serde(bound(deserialize = "'de: 'a"))] 416 - pub enum MessageInputRecordEmbed<'a> { 417 - #[serde(rename = "app.bsky.embed.record")] 418 - Record(Box<crate::app_bsky::embed::record::Record<'a>>), 419 - } 420 - 421 429 #[jacquard_derive::lexicon] 422 430 #[derive( 423 431 serde::Serialize, ··· 452 460 pub struct MessageView<'a> { 453 461 #[serde(skip_serializing_if = "std::option::Option::is_none")] 454 462 #[serde(borrow)] 455 - pub embed: std::option::Option<MessageViewRecordEmbed<'a>>, 463 + pub embed: std::option::Option<crate::app_bsky::embed::record::View<'a>>, 456 464 ///Annotations of text (mentions, URLs, hashtags, etc) 457 465 #[serde(skip_serializing_if = "std::option::Option::is_none")] 458 466 #[serde(borrow)] ··· 470 478 pub sent_at: jacquard_common::types::string::Datetime, 471 479 #[serde(borrow)] 472 480 pub text: jacquard_common::CowStr<'a>, 473 - } 474 - 475 - #[jacquard_derive::open_union] 476 - #[derive( 477 - serde::Serialize, 478 - serde::Deserialize, 479 - Debug, 480 - Clone, 481 - PartialEq, 482 - Eq, 483 - jacquard_derive::IntoStatic 484 - )] 485 - #[serde(tag = "$type")] 486 - #[serde(bound(deserialize = "'de: 'a"))] 487 - pub enum MessageViewRecordEmbed<'a> { 488 - #[serde(rename = "app.bsky.embed.record#view")] 489 - RecordView(Box<crate::app_bsky::embed::record::View<'a>>), 490 481 } 491 482 492 483 #[jacquard_derive::lexicon]
+2 -19
crates/jacquard-api/src/chat_bsky/convo/add_reaction.rs
··· 62 62 PartialEq, 63 63 Eq, 64 64 thiserror::Error, 65 - miette::Diagnostic 65 + miette::Diagnostic, 66 + jacquard_derive::IntoStatic 66 67 )] 67 68 #[serde(tag = "error", content = "message")] 68 69 #[serde(bound(deserialize = "'de: 'a"))] ··· 103 104 Ok(()) 104 105 } 105 106 Self::Unknown(err) => write!(f, "Unknown error: {:?}", err), 106 - } 107 - } 108 - } 109 - 110 - impl jacquard_common::IntoStatic for AddReactionError<'_> { 111 - type Output = AddReactionError<'static>; 112 - fn into_static(self) -> Self::Output { 113 - match self { 114 - AddReactionError::ReactionMessageDeleted(v) => { 115 - AddReactionError::ReactionMessageDeleted(v.into_static()) 116 - } 117 - AddReactionError::ReactionLimitReached(v) => { 118 - AddReactionError::ReactionLimitReached(v.into_static()) 119 - } 120 - AddReactionError::ReactionInvalidValue(v) => { 121 - AddReactionError::ReactionInvalidValue(v.into_static()) 122 - } 123 - AddReactionError::Unknown(v) => AddReactionError::Unknown(v.into_static()), 124 107 } 125 108 } 126 109 }
+36 -1
crates/jacquard-api/src/chat_bsky/convo/get_log.rs
··· 40 40 #[serde(borrow)] 41 41 pub cursor: std::option::Option<jacquard_common::CowStr<'a>>, 42 42 #[serde(borrow)] 43 - pub logs: Vec<jacquard_common::types::value::Data<'a>>, 43 + pub logs: Vec<GetLogOutputLogsItem<'a>>, 44 + } 45 + 46 + #[jacquard_derive::open_union] 47 + #[derive( 48 + serde::Serialize, 49 + serde::Deserialize, 50 + Debug, 51 + Clone, 52 + PartialEq, 53 + Eq, 54 + jacquard_derive::IntoStatic 55 + )] 56 + #[serde(tag = "$type")] 57 + #[serde(bound(deserialize = "'de: 'a"))] 58 + pub enum GetLogOutputLogsItem<'a> { 59 + #[serde(rename = "chat.bsky.convo.defs#logBeginConvo")] 60 + LogBeginConvo(Box<crate::chat_bsky::convo::LogBeginConvo<'a>>), 61 + #[serde(rename = "chat.bsky.convo.defs#logAcceptConvo")] 62 + LogAcceptConvo(Box<crate::chat_bsky::convo::LogAcceptConvo<'a>>), 63 + #[serde(rename = "chat.bsky.convo.defs#logLeaveConvo")] 64 + LogLeaveConvo(Box<crate::chat_bsky::convo::LogLeaveConvo<'a>>), 65 + #[serde(rename = "chat.bsky.convo.defs#logMuteConvo")] 66 + LogMuteConvo(Box<crate::chat_bsky::convo::LogMuteConvo<'a>>), 67 + #[serde(rename = "chat.bsky.convo.defs#logUnmuteConvo")] 68 + LogUnmuteConvo(Box<crate::chat_bsky::convo::LogUnmuteConvo<'a>>), 69 + #[serde(rename = "chat.bsky.convo.defs#logCreateMessage")] 70 + LogCreateMessage(Box<crate::chat_bsky::convo::LogCreateMessage<'a>>), 71 + #[serde(rename = "chat.bsky.convo.defs#logDeleteMessage")] 72 + LogDeleteMessage(Box<crate::chat_bsky::convo::LogDeleteMessage<'a>>), 73 + #[serde(rename = "chat.bsky.convo.defs#logReadMessage")] 74 + LogReadMessage(Box<crate::chat_bsky::convo::LogReadMessage<'a>>), 75 + #[serde(rename = "chat.bsky.convo.defs#logAddReaction")] 76 + LogAddReaction(Box<crate::chat_bsky::convo::LogAddReaction<'a>>), 77 + #[serde(rename = "chat.bsky.convo.defs#logRemoveReaction")] 78 + LogRemoveReaction(Box<crate::chat_bsky::convo::LogRemoveReaction<'a>>), 44 79 } 45 80 46 81 ///Response type for
+20 -1
crates/jacquard-api/src/chat_bsky/convo/get_messages.rs
··· 46 46 #[serde(borrow)] 47 47 pub cursor: std::option::Option<jacquard_common::CowStr<'a>>, 48 48 #[serde(borrow)] 49 - pub messages: Vec<jacquard_common::types::value::Data<'a>>, 49 + pub messages: Vec<GetMessagesOutputMessagesItem<'a>>, 50 + } 51 + 52 + #[jacquard_derive::open_union] 53 + #[derive( 54 + serde::Serialize, 55 + serde::Deserialize, 56 + Debug, 57 + Clone, 58 + PartialEq, 59 + Eq, 60 + jacquard_derive::IntoStatic 61 + )] 62 + #[serde(tag = "$type")] 63 + #[serde(bound(deserialize = "'de: 'a"))] 64 + pub enum GetMessagesOutputMessagesItem<'a> { 65 + #[serde(rename = "chat.bsky.convo.defs#messageView")] 66 + MessageView(Box<crate::chat_bsky::convo::MessageView<'a>>), 67 + #[serde(rename = "chat.bsky.convo.defs#deletedMessageView")] 68 + DeletedMessageView(Box<crate::chat_bsky::convo::DeletedMessageView<'a>>), 50 69 } 51 70 52 71 ///Response type for
+2 -18
crates/jacquard-api/src/chat_bsky/convo/remove_reaction.rs
··· 62 62 PartialEq, 63 63 Eq, 64 64 thiserror::Error, 65 - miette::Diagnostic 65 + miette::Diagnostic, 66 + jacquard_derive::IntoStatic 66 67 )] 67 68 #[serde(tag = "error", content = "message")] 68 69 #[serde(bound(deserialize = "'de: 'a"))] ··· 93 94 Ok(()) 94 95 } 95 96 Self::Unknown(err) => write!(f, "Unknown error: {:?}", err), 96 - } 97 - } 98 - } 99 - 100 - impl jacquard_common::IntoStatic for RemoveReactionError<'_> { 101 - type Output = RemoveReactionError<'static>; 102 - fn into_static(self) -> Self::Output { 103 - match self { 104 - RemoveReactionError::ReactionMessageDeleted(v) => { 105 - RemoveReactionError::ReactionMessageDeleted(v.into_static()) 106 - } 107 - RemoveReactionError::ReactionInvalidValue(v) => { 108 - RemoveReactionError::ReactionInvalidValue(v.into_static()) 109 - } 110 - RemoveReactionError::Unknown(v) => { 111 - RemoveReactionError::Unknown(v.into_static()) 112 - } 113 97 } 114 98 } 115 99 }
+20 -1
crates/jacquard-api/src/chat_bsky/moderation/get_message_context.rs
··· 46 46 #[serde(rename_all = "camelCase")] 47 47 pub struct GetMessageContextOutput<'a> { 48 48 #[serde(borrow)] 49 - pub messages: Vec<jacquard_common::types::value::Data<'a>>, 49 + pub messages: Vec<GetMessageContextOutputMessagesItem<'a>>, 50 + } 51 + 52 + #[jacquard_derive::open_union] 53 + #[derive( 54 + serde::Serialize, 55 + serde::Deserialize, 56 + Debug, 57 + Clone, 58 + PartialEq, 59 + Eq, 60 + jacquard_derive::IntoStatic 61 + )] 62 + #[serde(tag = "$type")] 63 + #[serde(bound(deserialize = "'de: 'a"))] 64 + pub enum GetMessageContextOutputMessagesItem<'a> { 65 + #[serde(rename = "chat.bsky.convo.defs#messageView")] 66 + MessageView(Box<crate::chat_bsky::convo::MessageView<'a>>), 67 + #[serde(rename = "chat.bsky.convo.defs#deletedMessageView")] 68 + DeletedMessageView(Box<crate::chat_bsky::convo::DeletedMessageView<'a>>), 50 69 } 51 70 52 71 ///Response type for
+4 -4
crates/jacquard-api/src/com_atproto/admin/get_subject_status.rs
··· 45 45 #[serde(borrow)] 46 46 pub deactivated: std::option::Option<crate::com_atproto::admin::StatusAttr<'a>>, 47 47 #[serde(borrow)] 48 - pub subject: GetSubjectStatusOutputRecordSubject<'a>, 48 + pub subject: GetSubjectStatusOutputSubject<'a>, 49 49 #[serde(skip_serializing_if = "std::option::Option::is_none")] 50 50 #[serde(borrow)] 51 51 pub takedown: std::option::Option<crate::com_atproto::admin::StatusAttr<'a>>, ··· 63 63 )] 64 64 #[serde(tag = "$type")] 65 65 #[serde(bound(deserialize = "'de: 'a"))] 66 - pub enum GetSubjectStatusOutputRecordSubject<'a> { 66 + pub enum GetSubjectStatusOutputSubject<'a> { 67 67 #[serde(rename = "com.atproto.admin.defs#repoRef")] 68 - DefsRepoRef(Box<crate::com_atproto::admin::RepoRef<'a>>), 68 + RepoRef(Box<crate::com_atproto::admin::RepoRef<'a>>), 69 69 #[serde(rename = "com.atproto.repo.strongRef")] 70 70 StrongRef(Box<crate::com_atproto::repo::strong_ref::StrongRef<'a>>), 71 71 #[serde(rename = "com.atproto.admin.defs#repoBlobRef")] 72 - DefsRepoBlobRef(Box<crate::com_atproto::admin::RepoBlobRef<'a>>), 72 + RepoBlobRef(Box<crate::com_atproto::admin::RepoBlobRef<'a>>), 73 73 } 74 74 75 75 ///Response type for
+8 -8
crates/jacquard-api/src/com_atproto/admin/update_subject_status.rs
··· 23 23 #[serde(borrow)] 24 24 pub deactivated: std::option::Option<crate::com_atproto::admin::StatusAttr<'a>>, 25 25 #[serde(borrow)] 26 - pub subject: UpdateSubjectStatusRecordSubject<'a>, 26 + pub subject: UpdateSubjectStatusSubject<'a>, 27 27 #[serde(skip_serializing_if = "std::option::Option::is_none")] 28 28 #[serde(borrow)] 29 29 pub takedown: std::option::Option<crate::com_atproto::admin::StatusAttr<'a>>, ··· 48 48 )] 49 49 #[serde(tag = "$type")] 50 50 #[serde(bound(deserialize = "'de: 'a"))] 51 - pub enum UpdateSubjectStatusRecordSubject<'a> { 51 + pub enum UpdateSubjectStatusSubject<'a> { 52 52 #[serde(rename = "com.atproto.admin.defs#repoRef")] 53 - DefsRepoRef(Box<crate::com_atproto::admin::RepoRef<'a>>), 53 + RepoRef(Box<crate::com_atproto::admin::RepoRef<'a>>), 54 54 #[serde(rename = "com.atproto.repo.strongRef")] 55 55 StrongRef(Box<crate::com_atproto::repo::strong_ref::StrongRef<'a>>), 56 56 #[serde(rename = "com.atproto.admin.defs#repoBlobRef")] 57 - DefsRepoBlobRef(Box<crate::com_atproto::admin::RepoBlobRef<'a>>), 57 + RepoBlobRef(Box<crate::com_atproto::admin::RepoBlobRef<'a>>), 58 58 } 59 59 60 60 #[jacquard_derive::lexicon] ··· 70 70 #[serde(rename_all = "camelCase")] 71 71 pub struct UpdateSubjectStatusOutput<'a> { 72 72 #[serde(borrow)] 73 - pub subject: UpdateSubjectStatusOutputRecordSubject<'a>, 73 + pub subject: UpdateSubjectStatusOutputSubject<'a>, 74 74 #[serde(skip_serializing_if = "std::option::Option::is_none")] 75 75 #[serde(borrow)] 76 76 pub takedown: std::option::Option<crate::com_atproto::admin::StatusAttr<'a>>, ··· 88 88 )] 89 89 #[serde(tag = "$type")] 90 90 #[serde(bound(deserialize = "'de: 'a"))] 91 - pub enum UpdateSubjectStatusOutputRecordSubject<'a> { 91 + pub enum UpdateSubjectStatusOutputSubject<'a> { 92 92 #[serde(rename = "com.atproto.admin.defs#repoRef")] 93 - DefsRepoRef(Box<crate::com_atproto::admin::RepoRef<'a>>), 93 + RepoRef(Box<crate::com_atproto::admin::RepoRef<'a>>), 94 94 #[serde(rename = "com.atproto.repo.strongRef")] 95 95 StrongRef(Box<crate::com_atproto::repo::strong_ref::StrongRef<'a>>), 96 96 #[serde(rename = "com.atproto.admin.defs#repoBlobRef")] 97 - DefsRepoBlobRef(Box<crate::com_atproto::admin::RepoBlobRef<'a>>), 97 + RepoBlobRef(Box<crate::com_atproto::admin::RepoBlobRef<'a>>), 98 98 } 99 99 100 100 ///Response type for
+2 -21
crates/jacquard-api/src/com_atproto/identity/refresh_identity.rs
··· 56 56 PartialEq, 57 57 Eq, 58 58 thiserror::Error, 59 - miette::Diagnostic 59 + miette::Diagnostic, 60 + jacquard_derive::IntoStatic 60 61 )] 61 62 #[serde(tag = "error", content = "message")] 62 63 #[serde(bound(deserialize = "'de: 'a"))] ··· 97 98 Ok(()) 98 99 } 99 100 Self::Unknown(err) => write!(f, "Unknown error: {:?}", err), 100 - } 101 - } 102 - } 103 - 104 - impl jacquard_common::IntoStatic for RefreshIdentityError<'_> { 105 - type Output = RefreshIdentityError<'static>; 106 - fn into_static(self) -> Self::Output { 107 - match self { 108 - RefreshIdentityError::HandleNotFound(v) => { 109 - RefreshIdentityError::HandleNotFound(v.into_static()) 110 - } 111 - RefreshIdentityError::DidNotFound(v) => { 112 - RefreshIdentityError::DidNotFound(v.into_static()) 113 - } 114 - RefreshIdentityError::DidDeactivated(v) => { 115 - RefreshIdentityError::DidDeactivated(v.into_static()) 116 - } 117 - RefreshIdentityError::Unknown(v) => { 118 - RefreshIdentityError::Unknown(v.into_static()) 119 - } 120 101 } 121 102 } 122 103 }
+2 -16
crates/jacquard-api/src/com_atproto/identity/resolve_did.rs
··· 48 48 PartialEq, 49 49 Eq, 50 50 thiserror::Error, 51 - miette::Diagnostic 51 + miette::Diagnostic, 52 + jacquard_derive::IntoStatic 52 53 )] 53 54 #[serde(tag = "error", content = "message")] 54 55 #[serde(bound(deserialize = "'de: 'a"))] ··· 79 80 Ok(()) 80 81 } 81 82 Self::Unknown(err) => write!(f, "Unknown error: {:?}", err), 82 - } 83 - } 84 - } 85 - 86 - impl jacquard_common::IntoStatic for ResolveDidError<'_> { 87 - type Output = ResolveDidError<'static>; 88 - fn into_static(self) -> Self::Output { 89 - match self { 90 - ResolveDidError::DidNotFound(v) => { 91 - ResolveDidError::DidNotFound(v.into_static()) 92 - } 93 - ResolveDidError::DidDeactivated(v) => { 94 - ResolveDidError::DidDeactivated(v.into_static()) 95 - } 96 - ResolveDidError::Unknown(v) => ResolveDidError::Unknown(v.into_static()), 97 83 } 98 84 } 99 85 }
+2 -15
crates/jacquard-api/src/com_atproto/identity/resolve_handle.rs
··· 47 47 PartialEq, 48 48 Eq, 49 49 thiserror::Error, 50 - miette::Diagnostic 50 + miette::Diagnostic, 51 + jacquard_derive::IntoStatic 51 52 )] 52 53 #[serde(tag = "error", content = "message")] 53 54 #[serde(bound(deserialize = "'de: 'a"))] ··· 68 69 Ok(()) 69 70 } 70 71 Self::Unknown(err) => write!(f, "Unknown error: {:?}", err), 71 - } 72 - } 73 - } 74 - 75 - impl jacquard_common::IntoStatic for ResolveHandleError<'_> { 76 - type Output = ResolveHandleError<'static>; 77 - fn into_static(self) -> Self::Output { 78 - match self { 79 - ResolveHandleError::HandleNotFound(v) => { 80 - ResolveHandleError::HandleNotFound(v.into_static()) 81 - } 82 - ResolveHandleError::Unknown(v) => { 83 - ResolveHandleError::Unknown(v.into_static()) 84 - } 85 72 } 86 73 } 87 74 }
+2 -21
crates/jacquard-api/src/com_atproto/identity/resolve_identity.rs
··· 48 48 PartialEq, 49 49 Eq, 50 50 thiserror::Error, 51 - miette::Diagnostic 51 + miette::Diagnostic, 52 + jacquard_derive::IntoStatic 52 53 )] 53 54 #[serde(tag = "error", content = "message")] 54 55 #[serde(bound(deserialize = "'de: 'a"))] ··· 89 90 Ok(()) 90 91 } 91 92 Self::Unknown(err) => write!(f, "Unknown error: {:?}", err), 92 - } 93 - } 94 - } 95 - 96 - impl jacquard_common::IntoStatic for ResolveIdentityError<'_> { 97 - type Output = ResolveIdentityError<'static>; 98 - fn into_static(self) -> Self::Output { 99 - match self { 100 - ResolveIdentityError::HandleNotFound(v) => { 101 - ResolveIdentityError::HandleNotFound(v.into_static()) 102 - } 103 - ResolveIdentityError::DidNotFound(v) => { 104 - ResolveIdentityError::DidNotFound(v.into_static()) 105 - } 106 - ResolveIdentityError::DidDeactivated(v) => { 107 - ResolveIdentityError::DidDeactivated(v.into_static()) 108 - } 109 - ResolveIdentityError::Unknown(v) => { 110 - ResolveIdentityError::Unknown(v.into_static()) 111 - } 112 93 } 113 94 } 114 95 }
+2 -15
crates/jacquard-api/src/com_atproto/label/subscribe_labels.rs
··· 86 86 PartialEq, 87 87 Eq, 88 88 thiserror::Error, 89 - miette::Diagnostic 89 + miette::Diagnostic, 90 + jacquard_derive::IntoStatic 90 91 )] 91 92 #[serde(tag = "error", content = "message")] 92 93 #[serde(bound(deserialize = "'de: 'a"))] ··· 106 107 Ok(()) 107 108 } 108 109 Self::Unknown(err) => write!(f, "Unknown error: {:?}", err), 109 - } 110 - } 111 - } 112 - 113 - impl jacquard_common::IntoStatic for SubscribeLabelsError<'_> { 114 - type Output = SubscribeLabelsError<'static>; 115 - fn into_static(self) -> Self::Output { 116 - match self { 117 - SubscribeLabelsError::FutureCursor(v) => { 118 - SubscribeLabelsError::FutureCursor(v.into_static()) 119 - } 120 - SubscribeLabelsError::Unknown(v) => { 121 - SubscribeLabelsError::Unknown(v.into_static()) 122 - } 123 110 } 124 111 } 125 112 }
+6 -6
crates/jacquard-api/src/com_atproto/moderation/create_report.rs
··· 31 31 #[serde(borrow)] 32 32 pub reason_type: crate::com_atproto::moderation::ReasonType<'a>, 33 33 #[serde(borrow)] 34 - pub subject: CreateReportRecordSubject<'a>, 34 + pub subject: CreateReportSubject<'a>, 35 35 #[serde(flatten)] 36 36 #[serde(borrow)] 37 37 #[builder(default)] ··· 53 53 )] 54 54 #[serde(tag = "$type")] 55 55 #[serde(bound(deserialize = "'de: 'a"))] 56 - pub enum CreateReportRecordSubject<'a> { 56 + pub enum CreateReportSubject<'a> { 57 57 #[serde(rename = "com.atproto.admin.defs#repoRef")] 58 - DefsRepoRef(Box<crate::com_atproto::admin::RepoRef<'a>>), 58 + RepoRef(Box<crate::com_atproto::admin::RepoRef<'a>>), 59 59 #[serde(rename = "com.atproto.repo.strongRef")] 60 60 StrongRef(Box<crate::com_atproto::repo::strong_ref::StrongRef<'a>>), 61 61 } ··· 82 82 #[serde(borrow)] 83 83 pub reported_by: jacquard_common::types::string::Did<'a>, 84 84 #[serde(borrow)] 85 - pub subject: CreateReportOutputRecordSubject<'a>, 85 + pub subject: CreateReportOutputSubject<'a>, 86 86 } 87 87 88 88 #[jacquard_derive::open_union] ··· 97 97 )] 98 98 #[serde(tag = "$type")] 99 99 #[serde(bound(deserialize = "'de: 'a"))] 100 - pub enum CreateReportOutputRecordSubject<'a> { 100 + pub enum CreateReportOutputSubject<'a> { 101 101 #[serde(rename = "com.atproto.admin.defs#repoRef")] 102 - DefsRepoRef(Box<crate::com_atproto::admin::RepoRef<'a>>), 102 + RepoRef(Box<crate::com_atproto::admin::RepoRef<'a>>), 103 103 #[serde(rename = "com.atproto.repo.strongRef")] 104 104 StrongRef(Box<crate::com_atproto::repo::strong_ref::StrongRef<'a>>), 105 105 }
+44 -15
crates/jacquard-api/src/com_atproto/repo/apply_writes.rs
··· 111 111 #[serde(skip_serializing_if = "std::option::Option::is_none")] 112 112 pub validate: std::option::Option<bool>, 113 113 #[serde(borrow)] 114 - pub writes: Vec<jacquard_common::types::value::Data<'a>>, 114 + pub writes: Vec<ApplyWritesWritesItem<'a>>, 115 115 #[serde(flatten)] 116 116 #[serde(borrow)] 117 117 #[builder(default)] ··· 121 121 >, 122 122 } 123 123 124 + #[derive( 125 + serde::Serialize, 126 + serde::Deserialize, 127 + Debug, 128 + Clone, 129 + PartialEq, 130 + Eq, 131 + jacquard_derive::IntoStatic 132 + )] 133 + #[serde(tag = "$type")] 134 + #[serde(bound(deserialize = "'de: 'a"))] 135 + pub enum ApplyWritesWritesItem<'a> { 136 + #[serde(rename = "com.atproto.repo.applyWrites#create")] 137 + Create(Box<crate::com_atproto::repo::apply_writes::Create<'a>>), 138 + #[serde(rename = "com.atproto.repo.applyWrites#update")] 139 + Update(Box<crate::com_atproto::repo::apply_writes::Update<'a>>), 140 + #[serde(rename = "com.atproto.repo.applyWrites#delete")] 141 + Delete(Box<crate::com_atproto::repo::apply_writes::Delete<'a>>), 142 + } 143 + 124 144 #[jacquard_derive::lexicon] 125 145 #[derive( 126 146 serde::Serialize, ··· 138 158 pub commit: std::option::Option<crate::com_atproto::repo::CommitMeta<'a>>, 139 159 #[serde(skip_serializing_if = "std::option::Option::is_none")] 140 160 #[serde(borrow)] 141 - pub results: std::option::Option<Vec<jacquard_common::types::value::Data<'a>>>, 161 + pub results: std::option::Option<Vec<ApplyWritesOutputResultsItem<'a>>>, 162 + } 163 + 164 + #[derive( 165 + serde::Serialize, 166 + serde::Deserialize, 167 + Debug, 168 + Clone, 169 + PartialEq, 170 + Eq, 171 + jacquard_derive::IntoStatic 172 + )] 173 + #[serde(tag = "$type")] 174 + #[serde(bound(deserialize = "'de: 'a"))] 175 + pub enum ApplyWritesOutputResultsItem<'a> { 176 + #[serde(rename = "com.atproto.repo.applyWrites#createResult")] 177 + CreateResult(Box<crate::com_atproto::repo::apply_writes::CreateResult<'a>>), 178 + #[serde(rename = "com.atproto.repo.applyWrites#updateResult")] 179 + UpdateResult(Box<crate::com_atproto::repo::apply_writes::UpdateResult<'a>>), 180 + #[serde(rename = "com.atproto.repo.applyWrites#deleteResult")] 181 + DeleteResult(Box<crate::com_atproto::repo::apply_writes::DeleteResult<'a>>), 142 182 } 143 183 144 184 #[jacquard_derive::open_union] ··· 150 190 PartialEq, 151 191 Eq, 152 192 thiserror::Error, 153 - miette::Diagnostic 193 + miette::Diagnostic, 194 + jacquard_derive::IntoStatic 154 195 )] 155 196 #[serde(tag = "error", content = "message")] 156 197 #[serde(bound(deserialize = "'de: 'a"))] ··· 171 212 Ok(()) 172 213 } 173 214 Self::Unknown(err) => write!(f, "Unknown error: {:?}", err), 174 - } 175 - } 176 - } 177 - 178 - impl jacquard_common::IntoStatic for ApplyWritesError<'_> { 179 - type Output = ApplyWritesError<'static>; 180 - fn into_static(self) -> Self::Output { 181 - match self { 182 - ApplyWritesError::InvalidSwap(v) => { 183 - ApplyWritesError::InvalidSwap(v.into_static()) 184 - } 185 - ApplyWritesError::Unknown(v) => ApplyWritesError::Unknown(v.into_static()), 186 215 } 187 216 } 188 217 }
+2 -13
crates/jacquard-api/src/com_atproto/repo/create_record.rs
··· 85 85 PartialEq, 86 86 Eq, 87 87 thiserror::Error, 88 - miette::Diagnostic 88 + miette::Diagnostic, 89 + jacquard_derive::IntoStatic 89 90 )] 90 91 #[serde(tag = "error", content = "message")] 91 92 #[serde(bound(deserialize = "'de: 'a"))] ··· 106 107 Ok(()) 107 108 } 108 109 Self::Unknown(err) => write!(f, "Unknown error: {:?}", err), 109 - } 110 - } 111 - } 112 - 113 - impl jacquard_common::IntoStatic for CreateRecordError<'_> { 114 - type Output = CreateRecordError<'static>; 115 - fn into_static(self) -> Self::Output { 116 - match self { 117 - CreateRecordError::InvalidSwap(v) => { 118 - CreateRecordError::InvalidSwap(v.into_static()) 119 - } 120 - CreateRecordError::Unknown(v) => CreateRecordError::Unknown(v.into_static()), 121 110 } 122 111 } 123 112 }
+2 -13
crates/jacquard-api/src/com_atproto/repo/delete_record.rs
··· 73 73 PartialEq, 74 74 Eq, 75 75 thiserror::Error, 76 - miette::Diagnostic 76 + miette::Diagnostic, 77 + jacquard_derive::IntoStatic 77 78 )] 78 79 #[serde(tag = "error", content = "message")] 79 80 #[serde(bound(deserialize = "'de: 'a"))] ··· 93 94 Ok(()) 94 95 } 95 96 Self::Unknown(err) => write!(f, "Unknown error: {:?}", err), 96 - } 97 - } 98 - } 99 - 100 - impl jacquard_common::IntoStatic for DeleteRecordError<'_> { 101 - type Output = DeleteRecordError<'static>; 102 - fn into_static(self) -> Self::Output { 103 - match self { 104 - DeleteRecordError::InvalidSwap(v) => { 105 - DeleteRecordError::InvalidSwap(v.into_static()) 106 - } 107 - DeleteRecordError::Unknown(v) => DeleteRecordError::Unknown(v.into_static()), 108 97 } 109 98 } 110 99 }
+2 -13
crates/jacquard-api/src/com_atproto/repo/get_record.rs
··· 61 61 PartialEq, 62 62 Eq, 63 63 thiserror::Error, 64 - miette::Diagnostic 64 + miette::Diagnostic, 65 + jacquard_derive::IntoStatic 65 66 )] 66 67 #[serde(tag = "error", content = "message")] 67 68 #[serde(bound(deserialize = "'de: 'a"))] ··· 81 82 Ok(()) 82 83 } 83 84 Self::Unknown(err) => write!(f, "Unknown error: {:?}", err), 84 - } 85 - } 86 - } 87 - 88 - impl jacquard_common::IntoStatic for GetRecordError<'_> { 89 - type Output = GetRecordError<'static>; 90 - fn into_static(self) -> Self::Output { 91 - match self { 92 - GetRecordError::RecordNotFound(v) => { 93 - GetRecordError::RecordNotFound(v.into_static()) 94 - } 95 - GetRecordError::Unknown(v) => GetRecordError::Unknown(v.into_static()), 96 85 } 97 86 } 98 87 }
+2 -13
crates/jacquard-api/src/com_atproto/repo/put_record.rs
··· 86 86 PartialEq, 87 87 Eq, 88 88 thiserror::Error, 89 - miette::Diagnostic 89 + miette::Diagnostic, 90 + jacquard_derive::IntoStatic 90 91 )] 91 92 #[serde(tag = "error", content = "message")] 92 93 #[serde(bound(deserialize = "'de: 'a"))] ··· 106 107 Ok(()) 107 108 } 108 109 Self::Unknown(err) => write!(f, "Unknown error: {:?}", err), 109 - } 110 - } 111 - } 112 - 113 - impl jacquard_common::IntoStatic for PutRecordError<'_> { 114 - type Output = PutRecordError<'static>; 115 - fn into_static(self) -> Self::Output { 116 - match self { 117 - PutRecordError::InvalidSwap(v) => { 118 - PutRecordError::InvalidSwap(v.into_static()) 119 - } 120 - PutRecordError::Unknown(v) => PutRecordError::Unknown(v.into_static()), 121 110 } 122 111 } 123 112 }
+2 -22
crates/jacquard-api/src/com_atproto/server/confirm_email.rs
··· 43 43 PartialEq, 44 44 Eq, 45 45 thiserror::Error, 46 - miette::Diagnostic 46 + miette::Diagnostic, 47 + jacquard_derive::IntoStatic 47 48 )] 48 49 #[serde(tag = "error", content = "message")] 49 50 #[serde(bound(deserialize = "'de: 'a"))] ··· 90 91 Ok(()) 91 92 } 92 93 Self::Unknown(err) => write!(f, "Unknown error: {:?}", err), 93 - } 94 - } 95 - } 96 - 97 - impl jacquard_common::IntoStatic for ConfirmEmailError<'_> { 98 - type Output = ConfirmEmailError<'static>; 99 - fn into_static(self) -> Self::Output { 100 - match self { 101 - ConfirmEmailError::AccountNotFound(v) => { 102 - ConfirmEmailError::AccountNotFound(v.into_static()) 103 - } 104 - ConfirmEmailError::ExpiredToken(v) => { 105 - ConfirmEmailError::ExpiredToken(v.into_static()) 106 - } 107 - ConfirmEmailError::InvalidToken(v) => { 108 - ConfirmEmailError::InvalidToken(v.into_static()) 109 - } 110 - ConfirmEmailError::InvalidEmail(v) => { 111 - ConfirmEmailError::InvalidEmail(v.into_static()) 112 - } 113 - ConfirmEmailError::Unknown(v) => ConfirmEmailError::Unknown(v.into_static()), 114 94 } 115 95 } 116 96 }
+2 -33
crates/jacquard-api/src/com_atproto/server/create_account.rs
··· 101 101 PartialEq, 102 102 Eq, 103 103 thiserror::Error, 104 - miette::Diagnostic 104 + miette::Diagnostic, 105 + jacquard_derive::IntoStatic 105 106 )] 106 107 #[serde(tag = "error", content = "message")] 107 108 #[serde(bound(deserialize = "'de: 'a"))] ··· 175 176 Ok(()) 176 177 } 177 178 Self::Unknown(err) => write!(f, "Unknown error: {:?}", err), 178 - } 179 - } 180 - } 181 - 182 - impl jacquard_common::IntoStatic for CreateAccountError<'_> { 183 - type Output = CreateAccountError<'static>; 184 - fn into_static(self) -> Self::Output { 185 - match self { 186 - CreateAccountError::InvalidHandle(v) => { 187 - CreateAccountError::InvalidHandle(v.into_static()) 188 - } 189 - CreateAccountError::InvalidPassword(v) => { 190 - CreateAccountError::InvalidPassword(v.into_static()) 191 - } 192 - CreateAccountError::InvalidInviteCode(v) => { 193 - CreateAccountError::InvalidInviteCode(v.into_static()) 194 - } 195 - CreateAccountError::HandleNotAvailable(v) => { 196 - CreateAccountError::HandleNotAvailable(v.into_static()) 197 - } 198 - CreateAccountError::UnsupportedDomain(v) => { 199 - CreateAccountError::UnsupportedDomain(v.into_static()) 200 - } 201 - CreateAccountError::UnresolvableDid(v) => { 202 - CreateAccountError::UnresolvableDid(v.into_static()) 203 - } 204 - CreateAccountError::IncompatibleDidDoc(v) => { 205 - CreateAccountError::IncompatibleDidDoc(v.into_static()) 206 - } 207 - CreateAccountError::Unknown(v) => { 208 - CreateAccountError::Unknown(v.into_static()) 209 - } 210 179 } 211 180 } 212 181 }
+2 -15
crates/jacquard-api/src/com_atproto/server/create_app_password.rs
··· 82 82 PartialEq, 83 83 Eq, 84 84 thiserror::Error, 85 - miette::Diagnostic 85 + miette::Diagnostic, 86 + jacquard_derive::IntoStatic 86 87 )] 87 88 #[serde(tag = "error", content = "message")] 88 89 #[serde(bound(deserialize = "'de: 'a"))] ··· 102 103 Ok(()) 103 104 } 104 105 Self::Unknown(err) => write!(f, "Unknown error: {:?}", err), 105 - } 106 - } 107 - } 108 - 109 - impl jacquard_common::IntoStatic for CreateAppPasswordError<'_> { 110 - type Output = CreateAppPasswordError<'static>; 111 - fn into_static(self) -> Self::Output { 112 - match self { 113 - CreateAppPasswordError::AccountTakedown(v) => { 114 - CreateAppPasswordError::AccountTakedown(v.into_static()) 115 - } 116 - CreateAppPasswordError::Unknown(v) => { 117 - CreateAppPasswordError::Unknown(v.into_static()) 118 - } 119 106 } 120 107 } 121 108 }
+2 -18
crates/jacquard-api/src/com_atproto/server/create_session.rs
··· 89 89 PartialEq, 90 90 Eq, 91 91 thiserror::Error, 92 - miette::Diagnostic 92 + miette::Diagnostic, 93 + jacquard_derive::IntoStatic 93 94 )] 94 95 #[serde(tag = "error", content = "message")] 95 96 #[serde(bound(deserialize = "'de: 'a"))] ··· 118 119 Ok(()) 119 120 } 120 121 Self::Unknown(err) => write!(f, "Unknown error: {:?}", err), 121 - } 122 - } 123 - } 124 - 125 - impl jacquard_common::IntoStatic for CreateSessionError<'_> { 126 - type Output = CreateSessionError<'static>; 127 - fn into_static(self) -> Self::Output { 128 - match self { 129 - CreateSessionError::AccountTakedown(v) => { 130 - CreateSessionError::AccountTakedown(v.into_static()) 131 - } 132 - CreateSessionError::AuthFactorTokenRequired(v) => { 133 - CreateSessionError::AuthFactorTokenRequired(v.into_static()) 134 - } 135 - CreateSessionError::Unknown(v) => { 136 - CreateSessionError::Unknown(v.into_static()) 137 - } 138 122 } 139 123 } 140 124 }
+2 -18
crates/jacquard-api/src/com_atproto/server/delete_account.rs
··· 45 45 PartialEq, 46 46 Eq, 47 47 thiserror::Error, 48 - miette::Diagnostic 48 + miette::Diagnostic, 49 + jacquard_derive::IntoStatic 49 50 )] 50 51 #[serde(tag = "error", content = "message")] 51 52 #[serde(bound(deserialize = "'de: 'a"))] ··· 74 75 Ok(()) 75 76 } 76 77 Self::Unknown(err) => write!(f, "Unknown error: {:?}", err), 77 - } 78 - } 79 - } 80 - 81 - impl jacquard_common::IntoStatic for DeleteAccountError<'_> { 82 - type Output = DeleteAccountError<'static>; 83 - fn into_static(self) -> Self::Output { 84 - match self { 85 - DeleteAccountError::ExpiredToken(v) => { 86 - DeleteAccountError::ExpiredToken(v.into_static()) 87 - } 88 - DeleteAccountError::InvalidToken(v) => { 89 - DeleteAccountError::InvalidToken(v.into_static()) 90 - } 91 - DeleteAccountError::Unknown(v) => { 92 - DeleteAccountError::Unknown(v.into_static()) 93 - } 94 78 } 95 79 } 96 80 }
+2 -15
crates/jacquard-api/src/com_atproto/server/get_account_invite_codes.rs
··· 51 51 PartialEq, 52 52 Eq, 53 53 thiserror::Error, 54 - miette::Diagnostic 54 + miette::Diagnostic, 55 + jacquard_derive::IntoStatic 55 56 )] 56 57 #[serde(tag = "error", content = "message")] 57 58 #[serde(bound(deserialize = "'de: 'a"))] ··· 71 72 Ok(()) 72 73 } 73 74 Self::Unknown(err) => write!(f, "Unknown error: {:?}", err), 74 - } 75 - } 76 - } 77 - 78 - impl jacquard_common::IntoStatic for GetAccountInviteCodesError<'_> { 79 - type Output = GetAccountInviteCodesError<'static>; 80 - fn into_static(self) -> Self::Output { 81 - match self { 82 - GetAccountInviteCodesError::DuplicateCreate(v) => { 83 - GetAccountInviteCodesError::DuplicateCreate(v.into_static()) 84 - } 85 - GetAccountInviteCodesError::Unknown(v) => { 86 - GetAccountInviteCodesError::Unknown(v.into_static()) 87 - } 88 75 } 89 76 } 90 77 }
+2 -15
crates/jacquard-api/src/com_atproto/server/get_service_auth.rs
··· 52 52 PartialEq, 53 53 Eq, 54 54 thiserror::Error, 55 - miette::Diagnostic 55 + miette::Diagnostic, 56 + jacquard_derive::IntoStatic 56 57 )] 57 58 #[serde(tag = "error", content = "message")] 58 59 #[serde(bound(deserialize = "'de: 'a"))] ··· 73 74 Ok(()) 74 75 } 75 76 Self::Unknown(err) => write!(f, "Unknown error: {:?}", err), 76 - } 77 - } 78 - } 79 - 80 - impl jacquard_common::IntoStatic for GetServiceAuthError<'_> { 81 - type Output = GetServiceAuthError<'static>; 82 - fn into_static(self) -> Self::Output { 83 - match self { 84 - GetServiceAuthError::BadExpiration(v) => { 85 - GetServiceAuthError::BadExpiration(v.into_static()) 86 - } 87 - GetServiceAuthError::Unknown(v) => { 88 - GetServiceAuthError::Unknown(v.into_static()) 89 - } 90 77 } 91 78 } 92 79 }
+2 -15
crates/jacquard-api/src/com_atproto/server/list_app_passwords.rs
··· 49 49 PartialEq, 50 50 Eq, 51 51 thiserror::Error, 52 - miette::Diagnostic 52 + miette::Diagnostic, 53 + jacquard_derive::IntoStatic 53 54 )] 54 55 #[serde(tag = "error", content = "message")] 55 56 #[serde(bound(deserialize = "'de: 'a"))] ··· 69 70 Ok(()) 70 71 } 71 72 Self::Unknown(err) => write!(f, "Unknown error: {:?}", err), 72 - } 73 - } 74 - } 75 - 76 - impl jacquard_common::IntoStatic for ListAppPasswordsError<'_> { 77 - type Output = ListAppPasswordsError<'static>; 78 - fn into_static(self) -> Self::Output { 79 - match self { 80 - ListAppPasswordsError::AccountTakedown(v) => { 81 - ListAppPasswordsError::AccountTakedown(v.into_static()) 82 - } 83 - ListAppPasswordsError::Unknown(v) => { 84 - ListAppPasswordsError::Unknown(v.into_static()) 85 - } 86 73 } 87 74 } 88 75 }
+2 -15
crates/jacquard-api/src/com_atproto/server/refresh_session.rs
··· 45 45 PartialEq, 46 46 Eq, 47 47 thiserror::Error, 48 - miette::Diagnostic 48 + miette::Diagnostic, 49 + jacquard_derive::IntoStatic 49 50 )] 50 51 #[serde(tag = "error", content = "message")] 51 52 #[serde(bound(deserialize = "'de: 'a"))] ··· 65 66 Ok(()) 66 67 } 67 68 Self::Unknown(err) => write!(f, "Unknown error: {:?}", err), 68 - } 69 - } 70 - } 71 - 72 - impl jacquard_common::IntoStatic for RefreshSessionError<'_> { 73 - type Output = RefreshSessionError<'static>; 74 - fn into_static(self) -> Self::Output { 75 - match self { 76 - RefreshSessionError::AccountTakedown(v) => { 77 - RefreshSessionError::AccountTakedown(v.into_static()) 78 - } 79 - RefreshSessionError::Unknown(v) => { 80 - RefreshSessionError::Unknown(v.into_static()) 81 - } 82 69 } 83 70 } 84 71 }
+2 -18
crates/jacquard-api/src/com_atproto/server/reset_password.rs
··· 43 43 PartialEq, 44 44 Eq, 45 45 thiserror::Error, 46 - miette::Diagnostic 46 + miette::Diagnostic, 47 + jacquard_derive::IntoStatic 47 48 )] 48 49 #[serde(tag = "error", content = "message")] 49 50 #[serde(bound(deserialize = "'de: 'a"))] ··· 72 73 Ok(()) 73 74 } 74 75 Self::Unknown(err) => write!(f, "Unknown error: {:?}", err), 75 - } 76 - } 77 - } 78 - 79 - impl jacquard_common::IntoStatic for ResetPasswordError<'_> { 80 - type Output = ResetPasswordError<'static>; 81 - fn into_static(self) -> Self::Output { 82 - match self { 83 - ResetPasswordError::ExpiredToken(v) => { 84 - ResetPasswordError::ExpiredToken(v.into_static()) 85 - } 86 - ResetPasswordError::InvalidToken(v) => { 87 - ResetPasswordError::InvalidToken(v.into_static()) 88 - } 89 - ResetPasswordError::Unknown(v) => { 90 - ResetPasswordError::Unknown(v.into_static()) 91 - } 92 76 } 93 77 } 94 78 }
+2 -19
crates/jacquard-api/src/com_atproto/server/update_email.rs
··· 47 47 PartialEq, 48 48 Eq, 49 49 thiserror::Error, 50 - miette::Diagnostic 50 + miette::Diagnostic, 51 + jacquard_derive::IntoStatic 51 52 )] 52 53 #[serde(tag = "error", content = "message")] 53 54 #[serde(bound(deserialize = "'de: 'a"))] ··· 85 86 Ok(()) 86 87 } 87 88 Self::Unknown(err) => write!(f, "Unknown error: {:?}", err), 88 - } 89 - } 90 - } 91 - 92 - impl jacquard_common::IntoStatic for UpdateEmailError<'_> { 93 - type Output = UpdateEmailError<'static>; 94 - fn into_static(self) -> Self::Output { 95 - match self { 96 - UpdateEmailError::ExpiredToken(v) => { 97 - UpdateEmailError::ExpiredToken(v.into_static()) 98 - } 99 - UpdateEmailError::InvalidToken(v) => { 100 - UpdateEmailError::InvalidToken(v.into_static()) 101 - } 102 - UpdateEmailError::TokenRequired(v) => { 103 - UpdateEmailError::TokenRequired(v.into_static()) 104 - } 105 - UpdateEmailError::Unknown(v) => UpdateEmailError::Unknown(v.into_static()), 106 89 } 107 90 } 108 91 }
+2 -21
crates/jacquard-api/src/com_atproto/sync/get_blob.rs
··· 45 45 PartialEq, 46 46 Eq, 47 47 thiserror::Error, 48 - miette::Diagnostic 48 + miette::Diagnostic, 49 + jacquard_derive::IntoStatic 49 50 )] 50 51 #[serde(tag = "error", content = "message")] 51 52 #[serde(bound(deserialize = "'de: 'a"))] ··· 101 102 Ok(()) 102 103 } 103 104 Self::Unknown(err) => write!(f, "Unknown error: {:?}", err), 104 - } 105 - } 106 - } 107 - 108 - impl jacquard_common::IntoStatic for GetBlobError<'_> { 109 - type Output = GetBlobError<'static>; 110 - fn into_static(self) -> Self::Output { 111 - match self { 112 - GetBlobError::BlobNotFound(v) => GetBlobError::BlobNotFound(v.into_static()), 113 - GetBlobError::RepoNotFound(v) => GetBlobError::RepoNotFound(v.into_static()), 114 - GetBlobError::RepoTakendown(v) => { 115 - GetBlobError::RepoTakendown(v.into_static()) 116 - } 117 - GetBlobError::RepoSuspended(v) => { 118 - GetBlobError::RepoSuspended(v.into_static()) 119 - } 120 - GetBlobError::RepoDeactivated(v) => { 121 - GetBlobError::RepoDeactivated(v.into_static()) 122 - } 123 - GetBlobError::Unknown(v) => GetBlobError::Unknown(v.into_static()), 124 105 } 125 106 } 126 107 }
+2 -25
crates/jacquard-api/src/com_atproto/sync/get_blocks.rs
··· 45 45 PartialEq, 46 46 Eq, 47 47 thiserror::Error, 48 - miette::Diagnostic 48 + miette::Diagnostic, 49 + jacquard_derive::IntoStatic 49 50 )] 50 51 #[serde(tag = "error", content = "message")] 51 52 #[serde(bound(deserialize = "'de: 'a"))] ··· 101 102 Ok(()) 102 103 } 103 104 Self::Unknown(err) => write!(f, "Unknown error: {:?}", err), 104 - } 105 - } 106 - } 107 - 108 - impl jacquard_common::IntoStatic for GetBlocksError<'_> { 109 - type Output = GetBlocksError<'static>; 110 - fn into_static(self) -> Self::Output { 111 - match self { 112 - GetBlocksError::BlockNotFound(v) => { 113 - GetBlocksError::BlockNotFound(v.into_static()) 114 - } 115 - GetBlocksError::RepoNotFound(v) => { 116 - GetBlocksError::RepoNotFound(v.into_static()) 117 - } 118 - GetBlocksError::RepoTakendown(v) => { 119 - GetBlocksError::RepoTakendown(v.into_static()) 120 - } 121 - GetBlocksError::RepoSuspended(v) => { 122 - GetBlocksError::RepoSuspended(v.into_static()) 123 - } 124 - GetBlocksError::RepoDeactivated(v) => { 125 - GetBlocksError::RepoDeactivated(v.into_static()) 126 - } 127 - GetBlocksError::Unknown(v) => GetBlocksError::Unknown(v.into_static()), 128 105 } 129 106 } 130 107 }
+2 -11
crates/jacquard-api/src/com_atproto/sync/get_head.rs
··· 47 47 PartialEq, 48 48 Eq, 49 49 thiserror::Error, 50 - miette::Diagnostic 50 + miette::Diagnostic, 51 + jacquard_derive::IntoStatic 51 52 )] 52 53 #[serde(tag = "error", content = "message")] 53 54 #[serde(bound(deserialize = "'de: 'a"))] ··· 67 68 Ok(()) 68 69 } 69 70 Self::Unknown(err) => write!(f, "Unknown error: {:?}", err), 70 - } 71 - } 72 - } 73 - 74 - impl jacquard_common::IntoStatic for GetHeadError<'_> { 75 - type Output = GetHeadError<'static>; 76 - fn into_static(self) -> Self::Output { 77 - match self { 78 - GetHeadError::HeadNotFound(v) => GetHeadError::HeadNotFound(v.into_static()), 79 - GetHeadError::Unknown(v) => GetHeadError::Unknown(v.into_static()), 80 71 } 81 72 } 82 73 }
+2 -15
crates/jacquard-api/src/com_atproto/sync/get_host_status.rs
··· 57 57 PartialEq, 58 58 Eq, 59 59 thiserror::Error, 60 - miette::Diagnostic 60 + miette::Diagnostic, 61 + jacquard_derive::IntoStatic 61 62 )] 62 63 #[serde(tag = "error", content = "message")] 63 64 #[serde(bound(deserialize = "'de: 'a"))] ··· 77 78 Ok(()) 78 79 } 79 80 Self::Unknown(err) => write!(f, "Unknown error: {:?}", err), 80 - } 81 - } 82 - } 83 - 84 - impl jacquard_common::IntoStatic for GetHostStatusError<'_> { 85 - type Output = GetHostStatusError<'static>; 86 - fn into_static(self) -> Self::Output { 87 - match self { 88 - GetHostStatusError::HostNotFound(v) => { 89 - GetHostStatusError::HostNotFound(v.into_static()) 90 - } 91 - GetHostStatusError::Unknown(v) => { 92 - GetHostStatusError::Unknown(v.into_static()) 93 - } 94 81 } 95 82 } 96 83 }
+2 -24
crates/jacquard-api/src/com_atproto/sync/get_latest_commit.rs
··· 48 48 PartialEq, 49 49 Eq, 50 50 thiserror::Error, 51 - miette::Diagnostic 51 + miette::Diagnostic, 52 + jacquard_derive::IntoStatic 52 53 )] 53 54 #[serde(tag = "error", content = "message")] 54 55 #[serde(bound(deserialize = "'de: 'a"))] ··· 95 96 Ok(()) 96 97 } 97 98 Self::Unknown(err) => write!(f, "Unknown error: {:?}", err), 98 - } 99 - } 100 - } 101 - 102 - impl jacquard_common::IntoStatic for GetLatestCommitError<'_> { 103 - type Output = GetLatestCommitError<'static>; 104 - fn into_static(self) -> Self::Output { 105 - match self { 106 - GetLatestCommitError::RepoNotFound(v) => { 107 - GetLatestCommitError::RepoNotFound(v.into_static()) 108 - } 109 - GetLatestCommitError::RepoTakendown(v) => { 110 - GetLatestCommitError::RepoTakendown(v.into_static()) 111 - } 112 - GetLatestCommitError::RepoSuspended(v) => { 113 - GetLatestCommitError::RepoSuspended(v.into_static()) 114 - } 115 - GetLatestCommitError::RepoDeactivated(v) => { 116 - GetLatestCommitError::RepoDeactivated(v.into_static()) 117 - } 118 - GetLatestCommitError::Unknown(v) => { 119 - GetLatestCommitError::Unknown(v.into_static()) 120 - } 121 99 } 122 100 } 123 101 }
+2 -25
crates/jacquard-api/src/com_atproto/sync/get_record.rs
··· 49 49 PartialEq, 50 50 Eq, 51 51 thiserror::Error, 52 - miette::Diagnostic 52 + miette::Diagnostic, 53 + jacquard_derive::IntoStatic 53 54 )] 54 55 #[serde(tag = "error", content = "message")] 55 56 #[serde(bound(deserialize = "'de: 'a"))] ··· 105 106 Ok(()) 106 107 } 107 108 Self::Unknown(err) => write!(f, "Unknown error: {:?}", err), 108 - } 109 - } 110 - } 111 - 112 - impl jacquard_common::IntoStatic for GetRecordError<'_> { 113 - type Output = GetRecordError<'static>; 114 - fn into_static(self) -> Self::Output { 115 - match self { 116 - GetRecordError::RecordNotFound(v) => { 117 - GetRecordError::RecordNotFound(v.into_static()) 118 - } 119 - GetRecordError::RepoNotFound(v) => { 120 - GetRecordError::RepoNotFound(v.into_static()) 121 - } 122 - GetRecordError::RepoTakendown(v) => { 123 - GetRecordError::RepoTakendown(v.into_static()) 124 - } 125 - GetRecordError::RepoSuspended(v) => { 126 - GetRecordError::RepoSuspended(v.into_static()) 127 - } 128 - GetRecordError::RepoDeactivated(v) => { 129 - GetRecordError::RepoDeactivated(v.into_static()) 130 - } 131 - GetRecordError::Unknown(v) => GetRecordError::Unknown(v.into_static()), 132 109 } 133 110 } 134 111 }
+2 -20
crates/jacquard-api/src/com_atproto/sync/get_repo.rs
··· 45 45 PartialEq, 46 46 Eq, 47 47 thiserror::Error, 48 - miette::Diagnostic 48 + miette::Diagnostic, 49 + jacquard_derive::IntoStatic 49 50 )] 50 51 #[serde(tag = "error", content = "message")] 51 52 #[serde(bound(deserialize = "'de: 'a"))] ··· 92 93 Ok(()) 93 94 } 94 95 Self::Unknown(err) => write!(f, "Unknown error: {:?}", err), 95 - } 96 - } 97 - } 98 - 99 - impl jacquard_common::IntoStatic for GetRepoError<'_> { 100 - type Output = GetRepoError<'static>; 101 - fn into_static(self) -> Self::Output { 102 - match self { 103 - GetRepoError::RepoNotFound(v) => GetRepoError::RepoNotFound(v.into_static()), 104 - GetRepoError::RepoTakendown(v) => { 105 - GetRepoError::RepoTakendown(v.into_static()) 106 - } 107 - GetRepoError::RepoSuspended(v) => { 108 - GetRepoError::RepoSuspended(v.into_static()) 109 - } 110 - GetRepoError::RepoDeactivated(v) => { 111 - GetRepoError::RepoDeactivated(v.into_static()) 112 - } 113 - GetRepoError::Unknown(v) => GetRepoError::Unknown(v.into_static()), 114 96 } 115 97 } 116 98 }
+2 -15
crates/jacquard-api/src/com_atproto/sync/get_repo_status.rs
··· 55 55 PartialEq, 56 56 Eq, 57 57 thiserror::Error, 58 - miette::Diagnostic 58 + miette::Diagnostic, 59 + jacquard_derive::IntoStatic 59 60 )] 60 61 #[serde(tag = "error", content = "message")] 61 62 #[serde(bound(deserialize = "'de: 'a"))] ··· 75 76 Ok(()) 76 77 } 77 78 Self::Unknown(err) => write!(f, "Unknown error: {:?}", err), 78 - } 79 - } 80 - } 81 - 82 - impl jacquard_common::IntoStatic for GetRepoStatusError<'_> { 83 - type Output = GetRepoStatusError<'static>; 84 - fn into_static(self) -> Self::Output { 85 - match self { 86 - GetRepoStatusError::RepoNotFound(v) => { 87 - GetRepoStatusError::RepoNotFound(v.into_static()) 88 - } 89 - GetRepoStatusError::Unknown(v) => { 90 - GetRepoStatusError::Unknown(v.into_static()) 91 - } 92 79 } 93 80 } 94 81 }
+2 -22
crates/jacquard-api/src/com_atproto/sync/list_blobs.rs
··· 59 59 PartialEq, 60 60 Eq, 61 61 thiserror::Error, 62 - miette::Diagnostic 62 + miette::Diagnostic, 63 + jacquard_derive::IntoStatic 63 64 )] 64 65 #[serde(tag = "error", content = "message")] 65 66 #[serde(bound(deserialize = "'de: 'a"))] ··· 106 107 Ok(()) 107 108 } 108 109 Self::Unknown(err) => write!(f, "Unknown error: {:?}", err), 109 - } 110 - } 111 - } 112 - 113 - impl jacquard_common::IntoStatic for ListBlobsError<'_> { 114 - type Output = ListBlobsError<'static>; 115 - fn into_static(self) -> Self::Output { 116 - match self { 117 - ListBlobsError::RepoNotFound(v) => { 118 - ListBlobsError::RepoNotFound(v.into_static()) 119 - } 120 - ListBlobsError::RepoTakendown(v) => { 121 - ListBlobsError::RepoTakendown(v.into_static()) 122 - } 123 - ListBlobsError::RepoSuspended(v) => { 124 - ListBlobsError::RepoSuspended(v.into_static()) 125 - } 126 - ListBlobsError::RepoDeactivated(v) => { 127 - ListBlobsError::RepoDeactivated(v.into_static()) 128 - } 129 - ListBlobsError::Unknown(v) => ListBlobsError::Unknown(v.into_static()), 130 110 } 131 111 } 132 112 }
+2 -13
crates/jacquard-api/src/com_atproto/sync/request_crawl.rs
··· 41 41 PartialEq, 42 42 Eq, 43 43 thiserror::Error, 44 - miette::Diagnostic 44 + miette::Diagnostic, 45 + jacquard_derive::IntoStatic 45 46 )] 46 47 #[serde(tag = "error", content = "message")] 47 48 #[serde(bound(deserialize = "'de: 'a"))] ··· 61 62 Ok(()) 62 63 } 63 64 Self::Unknown(err) => write!(f, "Unknown error: {:?}", err), 64 - } 65 - } 66 - } 67 - 68 - impl jacquard_common::IntoStatic for RequestCrawlError<'_> { 69 - type Output = RequestCrawlError<'static>; 70 - fn into_static(self) -> Self::Output { 71 - match self { 72 - RequestCrawlError::HostBanned(v) => { 73 - RequestCrawlError::HostBanned(v.into_static()) 74 - } 75 - RequestCrawlError::Unknown(v) => RequestCrawlError::Unknown(v.into_static()), 76 65 } 77 66 } 78 67 }
+2 -18
crates/jacquard-api/src/com_atproto/sync/subscribe_repos.rs
··· 166 166 PartialEq, 167 167 Eq, 168 168 thiserror::Error, 169 - miette::Diagnostic 169 + miette::Diagnostic, 170 + jacquard_derive::IntoStatic 170 171 )] 171 172 #[serde(tag = "error", content = "message")] 172 173 #[serde(bound(deserialize = "'de: 'a"))] ··· 196 197 Ok(()) 197 198 } 198 199 Self::Unknown(err) => write!(f, "Unknown error: {:?}", err), 199 - } 200 - } 201 - } 202 - 203 - impl jacquard_common::IntoStatic for SubscribeReposError<'_> { 204 - type Output = SubscribeReposError<'static>; 205 - fn into_static(self) -> Self::Output { 206 - match self { 207 - SubscribeReposError::FutureCursor(v) => { 208 - SubscribeReposError::FutureCursor(v.into_static()) 209 - } 210 - SubscribeReposError::ConsumerTooSlow(v) => { 211 - SubscribeReposError::ConsumerTooSlow(v.into_static()) 212 - } 213 - SubscribeReposError::Unknown(v) => { 214 - SubscribeReposError::Unknown(v.into_static()) 215 - } 216 200 } 217 201 } 218 202 }
+14 -17
crates/jacquard-api/src/com_atproto/temp/check_handle_availability.rs
··· 44 44 #[serde(borrow)] 45 45 pub handle: jacquard_common::types::string::Handle<'a>, 46 46 #[serde(borrow)] 47 - pub result: CheckHandleAvailabilityOutputRecordResult<'a>, 47 + pub result: CheckHandleAvailabilityOutputResult<'a>, 48 48 } 49 49 50 50 #[jacquard_derive::open_union] ··· 59 59 )] 60 60 #[serde(tag = "$type")] 61 61 #[serde(bound(deserialize = "'de: 'a"))] 62 - pub enum CheckHandleAvailabilityOutputRecordResult<'a> {} 62 + pub enum CheckHandleAvailabilityOutputResult<'a> { 63 + #[serde(rename = "com.atproto.temp.checkHandleAvailability#resultAvailable")] 64 + ResultAvailable( 65 + Box<crate::com_atproto::temp::check_handle_availability::ResultAvailable<'a>>, 66 + ), 67 + #[serde(rename = "com.atproto.temp.checkHandleAvailability#resultUnavailable")] 68 + ResultUnavailable( 69 + Box<crate::com_atproto::temp::check_handle_availability::ResultUnavailable<'a>>, 70 + ), 71 + } 72 + 63 73 #[jacquard_derive::open_union] 64 74 #[derive( 65 75 serde::Serialize, ··· 69 79 PartialEq, 70 80 Eq, 71 81 thiserror::Error, 72 - miette::Diagnostic 82 + miette::Diagnostic, 83 + jacquard_derive::IntoStatic 73 84 )] 74 85 #[serde(tag = "error", content = "message")] 75 86 #[serde(bound(deserialize = "'de: 'a"))] ··· 90 101 Ok(()) 91 102 } 92 103 Self::Unknown(err) => write!(f, "Unknown error: {:?}", err), 93 - } 94 - } 95 - } 96 - 97 - impl jacquard_common::IntoStatic for CheckHandleAvailabilityError<'_> { 98 - type Output = CheckHandleAvailabilityError<'static>; 99 - fn into_static(self) -> Self::Output { 100 - match self { 101 - CheckHandleAvailabilityError::InvalidEmail(v) => { 102 - CheckHandleAvailabilityError::InvalidEmail(v.into_static()) 103 - } 104 - CheckHandleAvailabilityError::Unknown(v) => { 105 - CheckHandleAvailabilityError::Unknown(v.into_static()) 106 - } 107 104 } 108 105 } 109 106 }
+2 -15
crates/jacquard-api/src/com_atproto/temp/dereference_scope.rs
··· 49 49 PartialEq, 50 50 Eq, 51 51 thiserror::Error, 52 - miette::Diagnostic 52 + miette::Diagnostic, 53 + jacquard_derive::IntoStatic 53 54 )] 54 55 #[serde(tag = "error", content = "message")] 55 56 #[serde(bound(deserialize = "'de: 'a"))] ··· 70 71 Ok(()) 71 72 } 72 73 Self::Unknown(err) => write!(f, "Unknown error: {:?}", err), 73 - } 74 - } 75 - } 76 - 77 - impl jacquard_common::IntoStatic for DereferenceScopeError<'_> { 78 - type Output = DereferenceScopeError<'static>; 79 - fn into_static(self) -> Self::Output { 80 - match self { 81 - DereferenceScopeError::InvalidScopeReference(v) => { 82 - DereferenceScopeError::InvalidScopeReference(v.into_static()) 83 - } 84 - DereferenceScopeError::Unknown(v) => { 85 - DereferenceScopeError::Unknown(v.into_static()) 86 - } 87 74 } 88 75 } 89 76 }
+2 -2
crates/jacquard-api/src/com_shinolabs/pinksea/get_oekaki.rs
··· 40 40 #[serde(borrow)] 41 41 pub children: Vec<crate::com_shinolabs::pinksea::app_view_defs::HydratedOekaki<'a>>, 42 42 #[serde(borrow)] 43 - pub parent: GetOekakiOutputRecordParent<'a>, 43 + pub parent: GetOekakiOutputParent<'a>, 44 44 } 45 45 46 46 #[jacquard_derive::open_union] ··· 55 55 )] 56 56 #[serde(tag = "$type")] 57 57 #[serde(bound(deserialize = "'de: 'a"))] 58 - pub enum GetOekakiOutputRecordParent<'a> { 58 + pub enum GetOekakiOutputParent<'a> { 59 59 #[serde(rename = "com.shinolabs.pinksea.appViewDefs#hydratedOekaki")] 60 60 AppViewDefsHydratedOekaki( 61 61 Box<crate::com_shinolabs::pinksea::app_view_defs::HydratedOekaki<'a>>,
+2 -15
crates/jacquard-api/src/com_whtwnd/blog/get_entry_metadata_by_name.rs
··· 55 55 PartialEq, 56 56 Eq, 57 57 thiserror::Error, 58 - miette::Diagnostic 58 + miette::Diagnostic, 59 + jacquard_derive::IntoStatic 59 60 )] 60 61 #[serde(tag = "error", content = "message")] 61 62 #[serde(bound(deserialize = "'de: 'a"))] ··· 76 77 Ok(()) 77 78 } 78 79 Self::Unknown(err) => write!(f, "Unknown error: {:?}", err), 79 - } 80 - } 81 - } 82 - 83 - impl jacquard_common::IntoStatic for GetEntryMetadataByNameError<'_> { 84 - type Output = GetEntryMetadataByNameError<'static>; 85 - fn into_static(self) -> Self::Output { 86 - match self { 87 - GetEntryMetadataByNameError::NotFound(v) => { 88 - GetEntryMetadataByNameError::NotFound(v.into_static()) 89 - } 90 - GetEntryMetadataByNameError::Unknown(v) => { 91 - GetEntryMetadataByNameError::Unknown(v.into_static()) 92 - } 93 80 } 94 81 } 95 82 }
+2 -12
crates/jacquard-api/src/com_whtwnd/blog/notify_of_new_entry.rs
··· 51 51 PartialEq, 52 52 Eq, 53 53 thiserror::Error, 54 - miette::Diagnostic 54 + miette::Diagnostic, 55 + jacquard_derive::IntoStatic 55 56 )] 56 57 #[serde(tag = "error", content = "message")] 57 58 #[serde(bound(deserialize = "'de: 'a"))] ··· 60 61 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 61 62 match self { 62 63 Self::Unknown(err) => write!(f, "Unknown error: {:?}", err), 63 - } 64 - } 65 - } 66 - 67 - impl jacquard_common::IntoStatic for NotifyOfNewEntryError<'_> { 68 - type Output = NotifyOfNewEntryError<'static>; 69 - fn into_static(self) -> Self::Output { 70 - match self { 71 - NotifyOfNewEntryError::Unknown(v) => { 72 - NotifyOfNewEntryError::Unknown(v.into_static()) 73 - } 74 64 } 75 65 } 76 66 }
+26 -1
crates/jacquard-api/src/community_lexicon/calendar/event.rs
··· 84 84 ///The locations where the event takes place. 85 85 #[serde(skip_serializing_if = "std::option::Option::is_none")] 86 86 #[serde(borrow)] 87 - pub locations: std::option::Option<Vec<jacquard_common::types::value::Data<'a>>>, 87 + pub locations: std::option::Option<Vec<EventLocationsItem<'a>>>, 88 88 ///The attendance mode of the event. 89 89 #[serde(skip_serializing_if = "std::option::Option::is_none")] 90 90 #[serde(borrow)] ··· 107 107 pub uris: std::option::Option< 108 108 Vec<crate::community_lexicon::calendar::event::Uri<'a>>, 109 109 >, 110 + } 111 + 112 + #[jacquard_derive::open_union] 113 + #[derive( 114 + serde::Serialize, 115 + serde::Deserialize, 116 + Debug, 117 + Clone, 118 + PartialEq, 119 + Eq, 120 + jacquard_derive::IntoStatic 121 + )] 122 + #[serde(tag = "$type")] 123 + #[serde(bound(deserialize = "'de: 'a"))] 124 + pub enum EventLocationsItem<'a> { 125 + #[serde(rename = "community.lexicon.calendar.event#uri")] 126 + Uri(Box<crate::community_lexicon::calendar::event::Uri<'a>>), 127 + #[serde(rename = "community.lexicon.location.address")] 128 + Address(Box<crate::community_lexicon::location::address::Address<'a>>), 129 + #[serde(rename = "community.lexicon.location.fsq")] 130 + Fsq(Box<crate::community_lexicon::location::fsq::Fsq<'a>>), 131 + #[serde(rename = "community.lexicon.location.geo")] 132 + Geo(Box<crate::community_lexicon::location::geo::Geo<'a>>), 133 + #[serde(rename = "community.lexicon.location.hthree")] 134 + Hthree(Box<crate::community_lexicon::location::hthree::Hthree<'a>>), 110 135 } 111 136 112 137 impl jacquard_common::types::collection::Collection for Event<'_> {
+6
crates/jacquard-api/src/events_smokesignal.rs
··· 1 + // @generated by jacquard-lexicon. DO NOT EDIT. 2 + // 3 + // This file was automatically generated from Lexicon schemas. 4 + // Any manual changes will be overwritten on the next regeneration. 5 + 6 + pub mod automation;
+6
crates/jacquard-api/src/events_smokesignal/automation.rs
··· 1 + // @generated by jacquard-lexicon. DO NOT EDIT. 2 + // 3 + // This file was automatically generated from Lexicon schemas. 4 + // Any manual changes will be overwritten on the next regeneration. 5 + 6 + pub mod invoke_webhook;
+78
crates/jacquard-api/src/events_smokesignal/automation/invoke_webhook.rs
··· 1 + // @generated by jacquard-lexicon. DO NOT EDIT. 2 + // 3 + // Lexicon: events.smokesignal.automation.InvokeWebhook 4 + // 5 + // This file was automatically generated from Lexicon schemas. 6 + // Any manual changes will be overwritten on the next regeneration. 7 + 8 + #[jacquard_derive::lexicon] 9 + #[derive( 10 + serde::Serialize, 11 + serde::Deserialize, 12 + Debug, 13 + Clone, 14 + PartialEq, 15 + Eq, 16 + bon::Builder, 17 + jacquard_derive::IntoStatic 18 + )] 19 + #[serde(rename_all = "camelCase")] 20 + #[builder(start_fn = new)] 21 + pub struct InvokeWebhook<'a> { 22 + #[serde(borrow)] 23 + pub context: InvokeWebhookContext<'a>, 24 + #[serde(borrow)] 25 + #[builder(into)] 26 + pub event: jacquard_common::CowStr<'a>, 27 + #[serde(borrow)] 28 + pub record: InvokeWebhookRecord<'a>, 29 + #[serde(flatten)] 30 + #[serde(borrow)] 31 + #[builder(default)] 32 + pub extra_data: ::std::collections::BTreeMap< 33 + ::jacquard_common::smol_str::SmolStr, 34 + ::jacquard_common::types::value::Data<'a>, 35 + >, 36 + } 37 + 38 + #[jacquard_derive::lexicon] 39 + #[derive( 40 + serde::Serialize, 41 + serde::Deserialize, 42 + Debug, 43 + Clone, 44 + PartialEq, 45 + Eq, 46 + jacquard_derive::IntoStatic 47 + )] 48 + #[serde(rename_all = "camelCase")] 49 + pub struct InvokeWebhookOutput<'a> {} 50 + ///Response type for 51 + ///events.smokesignal.automation.InvokeWebhook 52 + pub struct InvokeWebhookResponse; 53 + impl jacquard_common::xrpc::XrpcResp for InvokeWebhookResponse { 54 + const NSID: &'static str = "events.smokesignal.automation.InvokeWebhook"; 55 + const ENCODING: &'static str = "application/json"; 56 + type Output<'de> = InvokeWebhookOutput<'de>; 57 + type Err<'de> = jacquard_common::xrpc::GenericError<'de>; 58 + } 59 + 60 + impl<'de> jacquard_common::xrpc::XrpcRequest<'de> for InvokeWebhook<'de> { 61 + const NSID: &'static str = "events.smokesignal.automation.InvokeWebhook"; 62 + const METHOD: jacquard_common::xrpc::XrpcMethod = jacquard_common::xrpc::XrpcMethod::Procedure( 63 + "application/json", 64 + ); 65 + type Response = InvokeWebhookResponse; 66 + } 67 + 68 + ///Endpoint type for 69 + ///events.smokesignal.automation.InvokeWebhook 70 + pub struct InvokeWebhookRequest; 71 + impl jacquard_common::xrpc::XrpcEndpoint for InvokeWebhookRequest { 72 + const PATH: &'static str = "/xrpc/events.smokesignal.automation.InvokeWebhook"; 73 + const METHOD: jacquard_common::xrpc::XrpcMethod = jacquard_common::xrpc::XrpcMethod::Procedure( 74 + "application/json", 75 + ); 76 + type Request<'de> = InvokeWebhook<'de>; 77 + type Response = InvokeWebhookResponse; 78 + }
+4 -1
crates/jacquard-api/src/lib.rs
··· 69 69 #[cfg(feature = "dev_regnault")] 70 70 pub mod dev_regnault; 71 71 72 + #[cfg(feature = "events_smokesignal")] 73 + pub mod events_smokesignal; 74 + 72 75 #[cfg(feature = "fyi_unravel")] 73 76 pub mod fyi_unravel; 74 77 ··· 145 148 pub mod us_polhem; 146 149 147 150 #[cfg(feature = "win_tomo_x")] 148 - pub mod win_tomo_x; 151 + pub mod win_tomo_x;
+11 -9
crates/jacquard-api/src/my_skylights/list_item.rs
··· 37 37 pub struct Builtin<'a> { 38 38 #[serde(skip_serializing_if = "std::option::Option::is_none")] 39 39 #[serde(borrow)] 40 - pub r#type: std::option::Option<BuiltinRecordType<'a>>, 40 + pub r#type: std::option::Option<BuiltinType<'a>>, 41 41 } 42 42 43 43 #[jacquard_derive::open_union] ··· 52 52 )] 53 53 #[serde(tag = "$type")] 54 54 #[serde(bound(deserialize = "'de: 'a"))] 55 - pub enum BuiltinRecordType<'a> { 55 + pub enum BuiltinType<'a> { 56 56 #[serde(rename = "my.skylights.listItem#inProgress")] 57 - ListItemInProgress(Box<crate::my_skylights::list_item::InProgress>), 57 + InProgress(Box<crate::my_skylights::list_item::InProgress>), 58 58 #[serde(rename = "my.skylights.listItem#queue")] 59 - ListItemQueue(Box<crate::my_skylights::list_item::Queue>), 59 + Queue(Box<crate::my_skylights::list_item::Queue>), 60 60 #[serde(rename = "my.skylights.listItem#abandoned")] 61 - ListItemAbandoned(Box<crate::my_skylights::list_item::Abandoned>), 61 + Abandoned(Box<crate::my_skylights::list_item::Abandoned>), 62 62 #[serde(rename = "my.skylights.listItem#owned")] 63 - ListItemOwned(Box<crate::my_skylights::list_item::Owned>), 63 + Owned(Box<crate::my_skylights::list_item::Owned>), 64 64 #[serde(rename = "my.skylights.listItem#wishlist")] 65 - ListItemWishlist(Box<crate::my_skylights::list_item::Wishlist>), 65 + Wishlist(Box<crate::my_skylights::list_item::Wishlist>), 66 66 } 67 67 68 68 ///User is currently reading/watching/... the item ··· 100 100 #[serde(borrow)] 101 101 pub item: std::option::Option<crate::my_skylights::Item<'a>>, 102 102 #[serde(borrow)] 103 - pub list: ListItemRecordList<'a>, 103 + pub list: ListItemList<'a>, 104 104 #[serde(skip_serializing_if = "std::option::Option::is_none")] 105 105 #[serde(borrow)] 106 106 pub note: std::option::Option<jacquard_common::CowStr<'a>>, ··· 120 120 )] 121 121 #[serde(tag = "$type")] 122 122 #[serde(bound(deserialize = "'de: 'a"))] 123 - pub enum ListItemRecordList<'a> { 123 + pub enum ListItemList<'a> { 124 124 #[serde(rename = "my.skylights.list")] 125 125 List(Box<crate::my_skylights::list::List<'a>>), 126 + #[serde(rename = "my.skylights.listItem#builtin")] 127 + Builtin(Box<crate::my_skylights::list_item::Builtin<'a>>), 126 128 } 127 129 128 130 ///User owns the item
+3 -20
crates/jacquard-api/src/net_anisota/feed/draft.rs
··· 22 22 pub created_at: jacquard_common::types::string::Datetime, 23 23 #[serde(skip_serializing_if = "std::option::Option::is_none")] 24 24 #[serde(borrow)] 25 - pub embed: std::option::Option<DraftRecordEmbed<'a>>, 25 + pub embed: std::option::Option<DraftEmbed<'a>>, 26 26 ///Annotations of text (mentions, URLs, hashtags, etc) 27 27 #[serde(skip_serializing_if = "std::option::Option::is_none")] 28 28 #[serde(borrow)] ··· 30 30 ///Self-label values for this post. Effectively content warnings. 31 31 #[serde(skip_serializing_if = "std::option::Option::is_none")] 32 32 #[serde(borrow)] 33 - pub labels: std::option::Option<DraftRecordLabels<'a>>, 33 + pub labels: std::option::Option<crate::com_atproto::label::SelfLabels<'a>>, 34 34 ///Indicates human language of post primary text content. 35 35 #[serde(skip_serializing_if = "std::option::Option::is_none")] 36 36 pub langs: std::option::Option<Vec<jacquard_common::types::string::Language>>, ··· 61 61 )] 62 62 #[serde(tag = "$type")] 63 63 #[serde(bound(deserialize = "'de: 'a"))] 64 - pub enum DraftRecordEmbed<'a> { 64 + pub enum DraftEmbed<'a> { 65 65 #[serde(rename = "app.bsky.embed.images")] 66 66 Images(Box<crate::app_bsky::embed::images::Images<'a>>), 67 67 #[serde(rename = "app.bsky.embed.video")] ··· 72 72 Record(Box<crate::app_bsky::embed::record::Record<'a>>), 73 73 #[serde(rename = "app.bsky.embed.recordWithMedia")] 74 74 RecordWithMedia(Box<crate::app_bsky::embed::record_with_media::RecordWithMedia<'a>>), 75 - } 76 - 77 - #[jacquard_derive::open_union] 78 - #[derive( 79 - serde::Serialize, 80 - serde::Deserialize, 81 - Debug, 82 - Clone, 83 - PartialEq, 84 - Eq, 85 - jacquard_derive::IntoStatic 86 - )] 87 - #[serde(tag = "$type")] 88 - #[serde(bound(deserialize = "'de: 'a"))] 89 - pub enum DraftRecordLabels<'a> { 90 - #[serde(rename = "com.atproto.label.defs#selfLabels")] 91 - DefsSelfLabels(Box<crate::com_atproto::label::SelfLabels<'a>>), 92 75 } 93 76 94 77 impl jacquard_common::types::collection::Collection for Draft<'_> {
+3 -20
crates/jacquard-api/src/net_anisota/feed/post.rs
··· 22 22 pub created_at: jacquard_common::types::string::Datetime, 23 23 #[serde(skip_serializing_if = "std::option::Option::is_none")] 24 24 #[serde(borrow)] 25 - pub embed: std::option::Option<PostRecordEmbed<'a>>, 25 + pub embed: std::option::Option<PostEmbed<'a>>, 26 26 ///Annotations of text (mentions, URLs, hashtags, etc) 27 27 #[serde(skip_serializing_if = "std::option::Option::is_none")] 28 28 #[serde(borrow)] ··· 30 30 ///Self-label values for this post. Effectively content warnings. 31 31 #[serde(skip_serializing_if = "std::option::Option::is_none")] 32 32 #[serde(borrow)] 33 - pub labels: std::option::Option<PostRecordLabels<'a>>, 33 + pub labels: std::option::Option<crate::com_atproto::label::SelfLabels<'a>>, 34 34 ///Indicates human language of post primary text content. 35 35 #[serde(skip_serializing_if = "std::option::Option::is_none")] 36 36 pub langs: std::option::Option<Vec<jacquard_common::types::string::Language>>, ··· 58 58 )] 59 59 #[serde(tag = "$type")] 60 60 #[serde(bound(deserialize = "'de: 'a"))] 61 - pub enum PostRecordEmbed<'a> { 61 + pub enum PostEmbed<'a> { 62 62 #[serde(rename = "app.bsky.embed.images")] 63 63 Images(Box<crate::app_bsky::embed::images::Images<'a>>), 64 64 #[serde(rename = "app.bsky.embed.video")] ··· 69 69 Record(Box<crate::app_bsky::embed::record::Record<'a>>), 70 70 #[serde(rename = "app.bsky.embed.recordWithMedia")] 71 71 RecordWithMedia(Box<crate::app_bsky::embed::record_with_media::RecordWithMedia<'a>>), 72 - } 73 - 74 - #[jacquard_derive::open_union] 75 - #[derive( 76 - serde::Serialize, 77 - serde::Deserialize, 78 - Debug, 79 - Clone, 80 - PartialEq, 81 - Eq, 82 - jacquard_derive::IntoStatic 83 - )] 84 - #[serde(tag = "$type")] 85 - #[serde(bound(deserialize = "'de: 'a"))] 86 - pub enum PostRecordLabels<'a> { 87 - #[serde(rename = "com.atproto.label.defs#selfLabels")] 88 - DefsSelfLabels(Box<crate::com_atproto::label::SelfLabels<'a>>), 89 72 } 90 73 91 74 impl jacquard_common::types::collection::Collection for Post<'_> {
+3 -1
crates/jacquard-api/src/place_atwork/endorsement.rs
··· 29 29 ///Verified signatures from endorsement proofs (strong references). 30 30 #[serde(skip_serializing_if = "std::option::Option::is_none")] 31 31 #[serde(borrow)] 32 - pub signatures: std::option::Option<Vec<jacquard_common::types::value::Data<'a>>>, 32 + pub signatures: std::option::Option< 33 + Vec<crate::com_atproto::repo::strong_ref::StrongRef<'a>>, 34 + >, 33 35 ///The endorsement text content. 34 36 #[serde(borrow)] 35 37 pub text: jacquard_common::CowStr<'a>,
+2 -19
crates/jacquard-api/src/place_atwork/get_listing.rs
··· 58 58 PartialEq, 59 59 Eq, 60 60 thiserror::Error, 61 - miette::Diagnostic 61 + miette::Diagnostic, 62 + jacquard_derive::IntoStatic 62 63 )] 63 64 #[serde(tag = "error", content = "message")] 64 65 #[serde(bound(deserialize = "'de: 'a"))] ··· 99 100 Ok(()) 100 101 } 101 102 Self::Unknown(err) => write!(f, "Unknown error: {:?}", err), 102 - } 103 - } 104 - } 105 - 106 - impl jacquard_common::IntoStatic for GetListingError<'_> { 107 - type Output = GetListingError<'static>; 108 - fn into_static(self) -> Self::Output { 109 - match self { 110 - GetListingError::ListingNotFound(v) => { 111 - GetListingError::ListingNotFound(v.into_static()) 112 - } 113 - GetListingError::ListingParseFailed(v) => { 114 - GetListingError::ListingParseFailed(v.into_static()) 115 - } 116 - GetListingError::ListingFetchFailed(v) => { 117 - GetListingError::ListingFetchFailed(v.into_static()) 118 - } 119 - GetListingError::Unknown(v) => GetListingError::Unknown(v.into_static()), 120 103 } 121 104 } 122 105 }
+3 -1
crates/jacquard-api/src/place_atwork/listing.rs
··· 36 36 ///Locations that are relevant to the job listing. 37 37 #[serde(skip_serializing_if = "std::option::Option::is_none")] 38 38 #[serde(borrow)] 39 - pub locations: std::option::Option<Vec<jacquard_common::types::value::Data<'a>>>, 39 + pub locations: std::option::Option< 40 + Vec<crate::community_lexicon::location::hthree::Hthree<'a>>, 41 + >, 40 42 ///Client-declared timestamp when the job listing expires. 41 43 pub not_after: jacquard_common::types::string::Datetime, 42 44 ///Client-declared timestamp when the job listing becomes visible.
+2 -15
crates/jacquard-api/src/place_atwork/search_listings.rs
··· 73 73 PartialEq, 74 74 Eq, 75 75 thiserror::Error, 76 - miette::Diagnostic 76 + miette::Diagnostic, 77 + jacquard_derive::IntoStatic 77 78 )] 78 79 #[serde(tag = "error", content = "message")] 79 80 #[serde(bound(deserialize = "'de: 'a"))] ··· 94 95 Ok(()) 95 96 } 96 97 Self::Unknown(err) => write!(f, "Unknown error: {:?}", err), 97 - } 98 - } 99 - } 100 - 101 - impl jacquard_common::IntoStatic for SearchListingsError<'_> { 102 - type Output = SearchListingsError<'static>; 103 - fn into_static(self) -> Self::Output { 104 - match self { 105 - SearchListingsError::SearchFailed(v) => { 106 - SearchListingsError::SearchFailed(v.into_static()) 107 - } 108 - SearchListingsError::Unknown(v) => { 109 - SearchListingsError::Unknown(v.into_static()) 110 - } 111 98 } 112 99 } 113 100 }
+3 -3
crates/jacquard-api/src/place_stream/chat.rs
··· 38 38 pub record: jacquard_common::types::value::Data<'a>, 39 39 #[serde(skip_serializing_if = "std::option::Option::is_none")] 40 40 #[serde(borrow)] 41 - pub reply_to: std::option::Option<MessageViewRecordReplyTo<'a>>, 41 + pub reply_to: std::option::Option<MessageViewReplyTo<'a>>, 42 42 #[serde(borrow)] 43 43 pub uri: jacquard_common::types::string::AtUri<'a>, 44 44 } ··· 55 55 )] 56 56 #[serde(tag = "$type")] 57 57 #[serde(bound(deserialize = "'de: 'a"))] 58 - pub enum MessageViewRecordReplyTo<'a> { 58 + pub enum MessageViewReplyTo<'a> { 59 59 #[serde(rename = "place.stream.chat.defs#messageView")] 60 - DefsMessageView(Box<crate::place_stream::chat::MessageView<'a>>), 60 + MessageView(Box<crate::place_stream::chat::MessageView<'a>>), 61 61 }
+2 -15
crates/jacquard-api/src/place_stream/live/get_profile_card.rs
··· 44 44 PartialEq, 45 45 Eq, 46 46 thiserror::Error, 47 - miette::Diagnostic 47 + miette::Diagnostic, 48 + jacquard_derive::IntoStatic 48 49 )] 49 50 #[serde(tag = "error", content = "message")] 50 51 #[serde(bound(deserialize = "'de: 'a"))] ··· 64 65 Ok(()) 65 66 } 66 67 Self::Unknown(err) => write!(f, "Unknown error: {:?}", err), 67 - } 68 - } 69 - } 70 - 71 - impl jacquard_common::IntoStatic for GetProfileCardError<'_> { 72 - type Output = GetProfileCardError<'static>; 73 - fn into_static(self) -> Self::Output { 74 - match self { 75 - GetProfileCardError::RepoNotFound(v) => { 76 - GetProfileCardError::RepoNotFound(v.into_static()) 77 - } 78 - GetProfileCardError::Unknown(v) => { 79 - GetProfileCardError::Unknown(v.into_static()) 80 - } 81 68 } 82 69 } 83 70 }
+10 -6
crates/jacquard-api/src/place_stream/livestream.rs
··· 90 90 #[serde(rename_all = "camelCase")] 91 91 pub struct StreamplaceAnything<'a> { 92 92 #[serde(borrow)] 93 - pub livestream: StreamplaceAnythingRecordLivestream<'a>, 93 + pub livestream: StreamplaceAnythingLivestream<'a>, 94 94 } 95 95 96 96 #[jacquard_derive::open_union] ··· 105 105 )] 106 106 #[serde(tag = "$type")] 107 107 #[serde(bound(deserialize = "'de: 'a"))] 108 - pub enum StreamplaceAnythingRecordLivestream<'a> { 108 + pub enum StreamplaceAnythingLivestream<'a> { 109 + #[serde(rename = "place.stream.livestream#livestreamView")] 110 + LivestreamView(Box<crate::place_stream::livestream::LivestreamView<'a>>), 111 + #[serde(rename = "place.stream.livestream#viewerCount")] 112 + ViewerCount(Box<crate::place_stream::livestream::ViewerCount<'a>>), 109 113 #[serde(rename = "place.stream.defs#blockView")] 110 - DefsBlockView(Box<crate::place_stream::BlockView<'a>>), 114 + BlockView(Box<crate::place_stream::BlockView<'a>>), 111 115 #[serde(rename = "place.stream.defs#renditions")] 112 - DefsRenditions(Box<crate::place_stream::Renditions<'a>>), 116 + Renditions(Box<crate::place_stream::Renditions<'a>>), 113 117 #[serde(rename = "place.stream.defs#rendition")] 114 - DefsRendition(Box<crate::place_stream::Rendition<'a>>), 118 + Rendition(Box<crate::place_stream::Rendition<'a>>), 115 119 #[serde(rename = "place.stream.chat.defs#messageView")] 116 - DefsMessageView(Box<crate::place_stream::chat::MessageView<'a>>), 120 + MessageView(Box<crate::place_stream::chat::MessageView<'a>>), 117 121 } 118 122 119 123 #[jacquard_derive::lexicon]
+20 -1
crates/jacquard-api/src/place_stream/richtext/facet.rs
··· 19 19 #[serde(rename_all = "camelCase")] 20 20 pub struct Facet<'a> { 21 21 #[serde(borrow)] 22 - pub features: Vec<jacquard_common::types::value::Data<'a>>, 22 + pub features: Vec<FacetFeaturesItem<'a>>, 23 23 #[serde(borrow)] 24 24 pub index: crate::app_bsky::richtext::facet::ByteSlice<'a>, 25 + } 26 + 27 + #[jacquard_derive::open_union] 28 + #[derive( 29 + serde::Serialize, 30 + serde::Deserialize, 31 + Debug, 32 + Clone, 33 + PartialEq, 34 + Eq, 35 + jacquard_derive::IntoStatic 36 + )] 37 + #[serde(tag = "$type")] 38 + #[serde(bound(deserialize = "'de: 'a"))] 39 + pub enum FacetFeaturesItem<'a> { 40 + #[serde(rename = "app.bsky.richtext.facet#mention")] 41 + FacetMention(Box<crate::app_bsky::richtext::facet::Mention<'a>>), 42 + #[serde(rename = "app.bsky.richtext.facet#link")] 43 + FacetLink(Box<crate::app_bsky::richtext::facet::Link<'a>>), 25 44 }
+2 -21
crates/jacquard-api/src/place_stream/server/create_webhook.rs
··· 86 86 PartialEq, 87 87 Eq, 88 88 thiserror::Error, 89 - miette::Diagnostic 89 + miette::Diagnostic, 90 + jacquard_derive::IntoStatic 90 91 )] 91 92 #[serde(tag = "error", content = "message")] 92 93 #[serde(bound(deserialize = "'de: 'a"))] ··· 127 128 Ok(()) 128 129 } 129 130 Self::Unknown(err) => write!(f, "Unknown error: {:?}", err), 130 - } 131 - } 132 - } 133 - 134 - impl jacquard_common::IntoStatic for CreateWebhookError<'_> { 135 - type Output = CreateWebhookError<'static>; 136 - fn into_static(self) -> Self::Output { 137 - match self { 138 - CreateWebhookError::InvalidUrl(v) => { 139 - CreateWebhookError::InvalidUrl(v.into_static()) 140 - } 141 - CreateWebhookError::DuplicateWebhook(v) => { 142 - CreateWebhookError::DuplicateWebhook(v.into_static()) 143 - } 144 - CreateWebhookError::TooManyWebhooks(v) => { 145 - CreateWebhookError::TooManyWebhooks(v.into_static()) 146 - } 147 - CreateWebhookError::Unknown(v) => { 148 - CreateWebhookError::Unknown(v.into_static()) 149 - } 150 131 } 151 132 } 152 133 }
+2 -18
crates/jacquard-api/src/place_stream/server/delete_webhook.rs
··· 57 57 PartialEq, 58 58 Eq, 59 59 thiserror::Error, 60 - miette::Diagnostic 60 + miette::Diagnostic, 61 + jacquard_derive::IntoStatic 61 62 )] 62 63 #[serde(tag = "error", content = "message")] 63 64 #[serde(bound(deserialize = "'de: 'a"))] ··· 88 89 Ok(()) 89 90 } 90 91 Self::Unknown(err) => write!(f, "Unknown error: {:?}", err), 91 - } 92 - } 93 - } 94 - 95 - impl jacquard_common::IntoStatic for DeleteWebhookError<'_> { 96 - type Output = DeleteWebhookError<'static>; 97 - fn into_static(self) -> Self::Output { 98 - match self { 99 - DeleteWebhookError::WebhookNotFound(v) => { 100 - DeleteWebhookError::WebhookNotFound(v.into_static()) 101 - } 102 - DeleteWebhookError::Unauthorized(v) => { 103 - DeleteWebhookError::Unauthorized(v.into_static()) 104 - } 105 - DeleteWebhookError::Unknown(v) => { 106 - DeleteWebhookError::Unknown(v.into_static()) 107 - } 108 92 } 109 93 } 110 94 }
+2 -16
crates/jacquard-api/src/place_stream/server/get_webhook.rs
··· 48 48 PartialEq, 49 49 Eq, 50 50 thiserror::Error, 51 - miette::Diagnostic 51 + miette::Diagnostic, 52 + jacquard_derive::IntoStatic 52 53 )] 53 54 #[serde(tag = "error", content = "message")] 54 55 #[serde(bound(deserialize = "'de: 'a"))] ··· 79 80 Ok(()) 80 81 } 81 82 Self::Unknown(err) => write!(f, "Unknown error: {:?}", err), 82 - } 83 - } 84 - } 85 - 86 - impl jacquard_common::IntoStatic for GetWebhookError<'_> { 87 - type Output = GetWebhookError<'static>; 88 - fn into_static(self) -> Self::Output { 89 - match self { 90 - GetWebhookError::WebhookNotFound(v) => { 91 - GetWebhookError::WebhookNotFound(v.into_static()) 92 - } 93 - GetWebhookError::Unauthorized(v) => { 94 - GetWebhookError::Unauthorized(v.into_static()) 95 - } 96 - GetWebhookError::Unknown(v) => GetWebhookError::Unknown(v.into_static()), 97 83 } 98 84 } 99 85 }
+2 -13
crates/jacquard-api/src/place_stream/server/list_webhooks.rs
··· 62 62 PartialEq, 63 63 Eq, 64 64 thiserror::Error, 65 - miette::Diagnostic 65 + miette::Diagnostic, 66 + jacquard_derive::IntoStatic 66 67 )] 67 68 #[serde(tag = "error", content = "message")] 68 69 #[serde(bound(deserialize = "'de: 'a"))] ··· 83 84 Ok(()) 84 85 } 85 86 Self::Unknown(err) => write!(f, "Unknown error: {:?}", err), 86 - } 87 - } 88 - } 89 - 90 - impl jacquard_common::IntoStatic for ListWebhooksError<'_> { 91 - type Output = ListWebhooksError<'static>; 92 - fn into_static(self) -> Self::Output { 93 - match self { 94 - ListWebhooksError::InvalidCursor(v) => { 95 - ListWebhooksError::InvalidCursor(v.into_static()) 96 - } 97 - ListWebhooksError::Unknown(v) => ListWebhooksError::Unknown(v.into_static()), 98 87 } 99 88 } 100 89 }
+2 -24
crates/jacquard-api/src/place_stream/server/update_webhook.rs
··· 92 92 PartialEq, 93 93 Eq, 94 94 thiserror::Error, 95 - miette::Diagnostic 95 + miette::Diagnostic, 96 + jacquard_derive::IntoStatic 96 97 )] 97 98 #[serde(tag = "error", content = "message")] 98 99 #[serde(bound(deserialize = "'de: 'a"))] ··· 143 144 Ok(()) 144 145 } 145 146 Self::Unknown(err) => write!(f, "Unknown error: {:?}", err), 146 - } 147 - } 148 - } 149 - 150 - impl jacquard_common::IntoStatic for UpdateWebhookError<'_> { 151 - type Output = UpdateWebhookError<'static>; 152 - fn into_static(self) -> Self::Output { 153 - match self { 154 - UpdateWebhookError::WebhookNotFound(v) => { 155 - UpdateWebhookError::WebhookNotFound(v.into_static()) 156 - } 157 - UpdateWebhookError::Unauthorized(v) => { 158 - UpdateWebhookError::Unauthorized(v.into_static()) 159 - } 160 - UpdateWebhookError::InvalidUrl(v) => { 161 - UpdateWebhookError::InvalidUrl(v.into_static()) 162 - } 163 - UpdateWebhookError::DuplicateWebhook(v) => { 164 - UpdateWebhookError::DuplicateWebhook(v.into_static()) 165 - } 166 - UpdateWebhookError::Unknown(v) => { 167 - UpdateWebhookError::Unknown(v.into_static()) 168 - } 169 147 } 170 148 } 171 149 }
+2 -2
crates/jacquard-api/src/pub_leaflet/blocks/unordered_list.rs
··· 23 23 Vec<crate::pub_leaflet::blocks::unordered_list::ListItem<'a>>, 24 24 >, 25 25 #[serde(borrow)] 26 - pub content: ListItemRecordContent<'a>, 26 + pub content: ListItemContent<'a>, 27 27 } 28 28 29 29 #[jacquard_derive::open_union] ··· 38 38 )] 39 39 #[serde(tag = "$type")] 40 40 #[serde(bound(deserialize = "'de: 'a"))] 41 - pub enum ListItemRecordContent<'a> { 41 + pub enum ListItemContent<'a> { 42 42 #[serde(rename = "pub.leaflet.blocks.text")] 43 43 Text(Box<crate::pub_leaflet::blocks::text::Text<'a>>), 44 44 #[serde(rename = "pub.leaflet.blocks.header")]
+3 -14
crates/jacquard-api/src/pub_leaflet/comment.rs
··· 38 38 pub struct Comment<'a> { 39 39 #[serde(skip_serializing_if = "std::option::Option::is_none")] 40 40 #[serde(borrow)] 41 - pub attachment: std::option::Option<CommentRecordAttachment<'a>>, 41 + pub attachment: std::option::Option< 42 + crate::pub_leaflet::comment::LinearDocumentQuote<'a>, 43 + >, 42 44 pub created_at: jacquard_common::types::string::Datetime, 43 45 #[serde(skip_serializing_if = "std::option::Option::is_none")] 44 46 #[serde(borrow)] ··· 52 54 pub subject: jacquard_common::types::string::AtUri<'a>, 53 55 } 54 56 55 - #[jacquard_derive::open_union] 56 - #[derive( 57 - serde::Serialize, 58 - serde::Deserialize, 59 - Debug, 60 - Clone, 61 - PartialEq, 62 - Eq, 63 - jacquard_derive::IntoStatic 64 - )] 65 - #[serde(tag = "$type")] 66 - #[serde(bound(deserialize = "'de: 'a"))] 67 - pub enum CommentRecordAttachment<'a> {} 68 57 impl jacquard_common::types::collection::Collection for Comment<'_> { 69 58 const NSID: &'static str = "pub.leaflet.comment"; 70 59 }
+1 -1
crates/jacquard-api/src/pub_leaflet/document.rs
··· 24 24 #[serde(borrow)] 25 25 pub description: std::option::Option<jacquard_common::CowStr<'a>>, 26 26 #[serde(borrow)] 27 - pub pages: Vec<jacquard_common::types::value::Data<'a>>, 27 + pub pages: Vec<crate::pub_leaflet::pages::linear_document::LinearDocument<'a>>, 28 28 #[serde(skip_serializing_if = "std::option::Option::is_none")] 29 29 #[serde(borrow)] 30 30 pub post_ref: std::option::Option<
+2 -2
crates/jacquard-api/src/pub_leaflet/pages/linear_document.rs
··· 21 21 #[serde(borrow)] 22 22 pub alignment: std::option::Option<jacquard_common::CowStr<'a>>, 23 23 #[serde(borrow)] 24 - pub block: BlockRecordBlock<'a>, 24 + pub block: BlockBlock<'a>, 25 25 } 26 26 27 27 #[jacquard_derive::open_union] ··· 36 36 )] 37 37 #[serde(tag = "$type")] 38 38 #[serde(bound(deserialize = "'de: 'a"))] 39 - pub enum BlockRecordBlock<'a> { 39 + pub enum BlockBlock<'a> { 40 40 #[serde(rename = "pub.leaflet.blocks.iframe")] 41 41 Iframe(Box<crate::pub_leaflet::blocks::iframe::Iframe<'a>>), 42 42 #[serde(rename = "pub.leaflet.blocks.text")]
+10 -10
crates/jacquard-api/src/pub_leaflet/publication.rs
··· 75 75 pub struct Theme<'a> { 76 76 #[serde(skip_serializing_if = "std::option::Option::is_none")] 77 77 #[serde(borrow)] 78 - pub accent_background: std::option::Option<ThemeRecordAccentBackground<'a>>, 78 + pub accent_background: std::option::Option<ThemeAccentBackground<'a>>, 79 79 #[serde(skip_serializing_if = "std::option::Option::is_none")] 80 80 #[serde(borrow)] 81 - pub accent_text: std::option::Option<ThemeRecordAccentText<'a>>, 81 + pub accent_text: std::option::Option<ThemeAccentText<'a>>, 82 82 #[serde(skip_serializing_if = "std::option::Option::is_none")] 83 83 #[serde(borrow)] 84 - pub background_color: std::option::Option<ThemeRecordBackgroundColor<'a>>, 84 + pub background_color: std::option::Option<ThemeBackgroundColor<'a>>, 85 85 #[serde(skip_serializing_if = "std::option::Option::is_none")] 86 86 #[serde(borrow)] 87 87 pub background_image: std::option::Option< ··· 89 89 >, 90 90 #[serde(skip_serializing_if = "std::option::Option::is_none")] 91 91 #[serde(borrow)] 92 - pub page_background: std::option::Option<ThemeRecordPageBackground<'a>>, 92 + pub page_background: std::option::Option<ThemePageBackground<'a>>, 93 93 #[serde(skip_serializing_if = "std::option::Option::is_none")] 94 94 #[serde(borrow)] 95 - pub primary: std::option::Option<ThemeRecordPrimary<'a>>, 95 + pub primary: std::option::Option<ThemePrimary<'a>>, 96 96 #[serde(skip_serializing_if = "std::option::Option::is_none")] 97 97 pub show_page_background: std::option::Option<bool>, 98 98 } ··· 109 109 )] 110 110 #[serde(tag = "$type")] 111 111 #[serde(bound(deserialize = "'de: 'a"))] 112 - pub enum ThemeRecordAccentBackground<'a> { 112 + pub enum ThemeAccentBackground<'a> { 113 113 #[serde(rename = "pub.leaflet.theme.color#rgba")] 114 114 ColorRgba(Box<crate::pub_leaflet::theme::color::Rgba<'a>>), 115 115 #[serde(rename = "pub.leaflet.theme.color#rgb")] ··· 128 128 )] 129 129 #[serde(tag = "$type")] 130 130 #[serde(bound(deserialize = "'de: 'a"))] 131 - pub enum ThemeRecordAccentText<'a> { 131 + pub enum ThemeAccentText<'a> { 132 132 #[serde(rename = "pub.leaflet.theme.color#rgba")] 133 133 ColorRgba(Box<crate::pub_leaflet::theme::color::Rgba<'a>>), 134 134 #[serde(rename = "pub.leaflet.theme.color#rgb")] ··· 147 147 )] 148 148 #[serde(tag = "$type")] 149 149 #[serde(bound(deserialize = "'de: 'a"))] 150 - pub enum ThemeRecordBackgroundColor<'a> { 150 + pub enum ThemeBackgroundColor<'a> { 151 151 #[serde(rename = "pub.leaflet.theme.color#rgba")] 152 152 ColorRgba(Box<crate::pub_leaflet::theme::color::Rgba<'a>>), 153 153 #[serde(rename = "pub.leaflet.theme.color#rgb")] ··· 166 166 )] 167 167 #[serde(tag = "$type")] 168 168 #[serde(bound(deserialize = "'de: 'a"))] 169 - pub enum ThemeRecordPageBackground<'a> { 169 + pub enum ThemePageBackground<'a> { 170 170 #[serde(rename = "pub.leaflet.theme.color#rgba")] 171 171 ColorRgba(Box<crate::pub_leaflet::theme::color::Rgba<'a>>), 172 172 #[serde(rename = "pub.leaflet.theme.color#rgb")] ··· 185 185 )] 186 186 #[serde(tag = "$type")] 187 187 #[serde(bound(deserialize = "'de: 'a"))] 188 - pub enum ThemeRecordPrimary<'a> { 188 + pub enum ThemePrimary<'a> { 189 189 #[serde(rename = "pub.leaflet.theme.color#rgba")] 190 190 ColorRgba(Box<crate::pub_leaflet::theme::color::Rgba<'a>>), 191 191 #[serde(rename = "pub.leaflet.theme.color#rgb")]
+32 -1
crates/jacquard-api/src/pub_leaflet/richtext/facet.rs
··· 123 123 #[serde(rename_all = "camelCase")] 124 124 pub struct Facet<'a> { 125 125 #[serde(borrow)] 126 - pub features: Vec<jacquard_common::types::value::Data<'a>>, 126 + pub features: Vec<FacetFeaturesItem<'a>>, 127 127 #[serde(borrow)] 128 128 pub index: crate::pub_leaflet::richtext::facet::ByteSlice<'a>, 129 + } 130 + 131 + #[jacquard_derive::open_union] 132 + #[derive( 133 + serde::Serialize, 134 + serde::Deserialize, 135 + Debug, 136 + Clone, 137 + PartialEq, 138 + Eq, 139 + jacquard_derive::IntoStatic 140 + )] 141 + #[serde(tag = "$type")] 142 + #[serde(bound(deserialize = "'de: 'a"))] 143 + pub enum FacetFeaturesItem<'a> { 144 + #[serde(rename = "pub.leaflet.richtext.facet#link")] 145 + Link(Box<crate::pub_leaflet::richtext::facet::Link<'a>>), 146 + #[serde(rename = "pub.leaflet.richtext.facet#code")] 147 + Code(Box<crate::pub_leaflet::richtext::facet::Code<'a>>), 148 + #[serde(rename = "pub.leaflet.richtext.facet#highlight")] 149 + Highlight(Box<crate::pub_leaflet::richtext::facet::Highlight<'a>>), 150 + #[serde(rename = "pub.leaflet.richtext.facet#underline")] 151 + Underline(Box<crate::pub_leaflet::richtext::facet::Underline<'a>>), 152 + #[serde(rename = "pub.leaflet.richtext.facet#strikethrough")] 153 + Strikethrough(Box<crate::pub_leaflet::richtext::facet::Strikethrough<'a>>), 154 + #[serde(rename = "pub.leaflet.richtext.facet#id")] 155 + Id(Box<crate::pub_leaflet::richtext::facet::Id<'a>>), 156 + #[serde(rename = "pub.leaflet.richtext.facet#bold")] 157 + Bold(Box<crate::pub_leaflet::richtext::facet::Bold<'a>>), 158 + #[serde(rename = "pub.leaflet.richtext.facet#italic")] 159 + Italic(Box<crate::pub_leaflet::richtext::facet::Italic<'a>>), 129 160 } 130 161 131 162 ///Facet feature for strikethrough markup
+2 -13
crates/jacquard-api/src/sh_tangled/knot/list_keys.rs
··· 56 56 PartialEq, 57 57 Eq, 58 58 thiserror::Error, 59 - miette::Diagnostic 59 + miette::Diagnostic, 60 + jacquard_derive::IntoStatic 60 61 )] 61 62 #[serde(tag = "error", content = "message")] 62 63 #[serde(bound(deserialize = "'de: 'a"))] ··· 77 78 Ok(()) 78 79 } 79 80 Self::Unknown(err) => write!(f, "Unknown error: {:?}", err), 80 - } 81 - } 82 - } 83 - 84 - impl jacquard_common::IntoStatic for ListKeysError<'_> { 85 - type Output = ListKeysError<'static>; 86 - fn into_static(self) -> Self::Output { 87 - match self { 88 - ListKeysError::InternalServerError(v) => { 89 - ListKeysError::InternalServerError(v.into_static()) 90 - } 91 - ListKeysError::Unknown(v) => ListKeysError::Unknown(v.into_static()), 92 81 } 93 82 } 94 83 }
+2 -10
crates/jacquard-api/src/sh_tangled/knot/version.rs
··· 30 30 PartialEq, 31 31 Eq, 32 32 thiserror::Error, 33 - miette::Diagnostic 33 + miette::Diagnostic, 34 + jacquard_derive::IntoStatic 34 35 )] 35 36 #[serde(tag = "error", content = "message")] 36 37 #[serde(bound(deserialize = "'de: 'a"))] ··· 39 40 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 40 41 match self { 41 42 Self::Unknown(err) => write!(f, "Unknown error: {:?}", err), 42 - } 43 - } 44 - } 45 - 46 - impl jacquard_common::IntoStatic for VersionError<'_> { 47 - type Output = VersionError<'static>; 48 - fn into_static(self) -> Self::Output { 49 - match self { 50 - VersionError::Unknown(v) => VersionError::Unknown(v.into_static()), 51 43 } 52 44 } 53 45 }
+2 -11
crates/jacquard-api/src/sh_tangled/owner.rs
··· 30 30 PartialEq, 31 31 Eq, 32 32 thiserror::Error, 33 - miette::Diagnostic 33 + miette::Diagnostic, 34 + jacquard_derive::IntoStatic 34 35 )] 35 36 #[serde(tag = "error", content = "message")] 36 37 #[serde(bound(deserialize = "'de: 'a"))] ··· 51 52 Ok(()) 52 53 } 53 54 Self::Unknown(err) => write!(f, "Unknown error: {:?}", err), 54 - } 55 - } 56 - } 57 - 58 - impl jacquard_common::IntoStatic for OwnerError<'_> { 59 - type Output = OwnerError<'static>; 60 - fn into_static(self) -> Self::Output { 61 - match self { 62 - OwnerError::OwnerNotFound(v) => OwnerError::OwnerNotFound(v.into_static()), 63 - OwnerError::Unknown(v) => OwnerError::Unknown(v.into_static()), 64 55 } 65 56 } 66 57 }
+2 -16
crates/jacquard-api/src/sh_tangled/repo/archive.rs
··· 57 57 PartialEq, 58 58 Eq, 59 59 thiserror::Error, 60 - miette::Diagnostic 60 + miette::Diagnostic, 61 + jacquard_derive::IntoStatic 61 62 )] 62 63 #[serde(tag = "error", content = "message")] 63 64 #[serde(bound(deserialize = "'de: 'a"))] ··· 108 109 Ok(()) 109 110 } 110 111 Self::Unknown(err) => write!(f, "Unknown error: {:?}", err), 111 - } 112 - } 113 - } 114 - 115 - impl jacquard_common::IntoStatic for ArchiveError<'_> { 116 - type Output = ArchiveError<'static>; 117 - fn into_static(self) -> Self::Output { 118 - match self { 119 - ArchiveError::RepoNotFound(v) => ArchiveError::RepoNotFound(v.into_static()), 120 - ArchiveError::RefNotFound(v) => ArchiveError::RefNotFound(v.into_static()), 121 - ArchiveError::InvalidRequest(v) => { 122 - ArchiveError::InvalidRequest(v.into_static()) 123 - } 124 - ArchiveError::ArchiveError(v) => ArchiveError::ArchiveError(v.into_static()), 125 - ArchiveError::Unknown(v) => ArchiveError::Unknown(v.into_static()), 126 112 } 127 113 } 128 114 }
+2 -14
crates/jacquard-api/src/sh_tangled/repo/blob.rs
··· 110 110 PartialEq, 111 111 Eq, 112 112 thiserror::Error, 113 - miette::Diagnostic 113 + miette::Diagnostic, 114 + jacquard_derive::IntoStatic 114 115 )] 115 116 #[serde(tag = "error", content = "message")] 116 117 #[serde(bound(deserialize = "'de: 'a"))] ··· 161 162 Ok(()) 162 163 } 163 164 Self::Unknown(err) => write!(f, "Unknown error: {:?}", err), 164 - } 165 - } 166 - } 167 - 168 - impl jacquard_common::IntoStatic for BlobError<'_> { 169 - type Output = BlobError<'static>; 170 - fn into_static(self) -> Self::Output { 171 - match self { 172 - BlobError::RepoNotFound(v) => BlobError::RepoNotFound(v.into_static()), 173 - BlobError::RefNotFound(v) => BlobError::RefNotFound(v.into_static()), 174 - BlobError::FileNotFound(v) => BlobError::FileNotFound(v.into_static()), 175 - BlobError::InvalidRequest(v) => BlobError::InvalidRequest(v.into_static()), 176 - BlobError::Unknown(v) => BlobError::Unknown(v.into_static()), 177 165 } 178 166 } 179 167 }
+2 -17
crates/jacquard-api/src/sh_tangled/repo/branch.rs
··· 71 71 PartialEq, 72 72 Eq, 73 73 thiserror::Error, 74 - miette::Diagnostic 74 + miette::Diagnostic, 75 + jacquard_derive::IntoStatic 75 76 )] 76 77 #[serde(tag = "error", content = "message")] 77 78 #[serde(bound(deserialize = "'de: 'a"))] ··· 112 113 Ok(()) 113 114 } 114 115 Self::Unknown(err) => write!(f, "Unknown error: {:?}", err), 115 - } 116 - } 117 - } 118 - 119 - impl jacquard_common::IntoStatic for BranchError<'_> { 120 - type Output = BranchError<'static>; 121 - fn into_static(self) -> Self::Output { 122 - match self { 123 - BranchError::RepoNotFound(v) => BranchError::RepoNotFound(v.into_static()), 124 - BranchError::BranchNotFound(v) => { 125 - BranchError::BranchNotFound(v.into_static()) 126 - } 127 - BranchError::InvalidRequest(v) => { 128 - BranchError::InvalidRequest(v.into_static()) 129 - } 130 - BranchError::Unknown(v) => BranchError::Unknown(v.into_static()), 131 116 } 132 117 } 133 118 }
+2 -16
crates/jacquard-api/src/sh_tangled/repo/branches.rs
··· 51 51 PartialEq, 52 52 Eq, 53 53 thiserror::Error, 54 - miette::Diagnostic 54 + miette::Diagnostic, 55 + jacquard_derive::IntoStatic 55 56 )] 56 57 #[serde(tag = "error", content = "message")] 57 58 #[serde(bound(deserialize = "'de: 'a"))] ··· 82 83 Ok(()) 83 84 } 84 85 Self::Unknown(err) => write!(f, "Unknown error: {:?}", err), 85 - } 86 - } 87 - } 88 - 89 - impl jacquard_common::IntoStatic for BranchesError<'_> { 90 - type Output = BranchesError<'static>; 91 - fn into_static(self) -> Self::Output { 92 - match self { 93 - BranchesError::RepoNotFound(v) => { 94 - BranchesError::RepoNotFound(v.into_static()) 95 - } 96 - BranchesError::InvalidRequest(v) => { 97 - BranchesError::InvalidRequest(v.into_static()) 98 - } 99 - BranchesError::Unknown(v) => BranchesError::Unknown(v.into_static()), 100 86 } 101 87 } 102 88 }
+2 -18
crates/jacquard-api/src/sh_tangled/repo/compare.rs
··· 51 51 PartialEq, 52 52 Eq, 53 53 thiserror::Error, 54 - miette::Diagnostic 54 + miette::Diagnostic, 55 + jacquard_derive::IntoStatic 55 56 )] 56 57 #[serde(tag = "error", content = "message")] 57 58 #[serde(bound(deserialize = "'de: 'a"))] ··· 102 103 Ok(()) 103 104 } 104 105 Self::Unknown(err) => write!(f, "Unknown error: {:?}", err), 105 - } 106 - } 107 - } 108 - 109 - impl jacquard_common::IntoStatic for CompareError<'_> { 110 - type Output = CompareError<'static>; 111 - fn into_static(self) -> Self::Output { 112 - match self { 113 - CompareError::RepoNotFound(v) => CompareError::RepoNotFound(v.into_static()), 114 - CompareError::RevisionNotFound(v) => { 115 - CompareError::RevisionNotFound(v.into_static()) 116 - } 117 - CompareError::InvalidRequest(v) => { 118 - CompareError::InvalidRequest(v.into_static()) 119 - } 120 - CompareError::CompareError(v) => CompareError::CompareError(v.into_static()), 121 - CompareError::Unknown(v) => CompareError::Unknown(v.into_static()), 122 106 } 123 107 } 124 108 }
+2 -13
crates/jacquard-api/src/sh_tangled/repo/diff.rs
··· 47 47 PartialEq, 48 48 Eq, 49 49 thiserror::Error, 50 - miette::Diagnostic 50 + miette::Diagnostic, 51 + jacquard_derive::IntoStatic 51 52 )] 52 53 #[serde(tag = "error", content = "message")] 53 54 #[serde(bound(deserialize = "'de: 'a"))] ··· 88 89 Ok(()) 89 90 } 90 91 Self::Unknown(err) => write!(f, "Unknown error: {:?}", err), 91 - } 92 - } 93 - } 94 - 95 - impl jacquard_common::IntoStatic for DiffError<'_> { 96 - type Output = DiffError<'static>; 97 - fn into_static(self) -> Self::Output { 98 - match self { 99 - DiffError::RepoNotFound(v) => DiffError::RepoNotFound(v.into_static()), 100 - DiffError::RefNotFound(v) => DiffError::RefNotFound(v.into_static()), 101 - DiffError::InvalidRequest(v) => DiffError::InvalidRequest(v.into_static()), 102 - DiffError::Unknown(v) => DiffError::Unknown(v.into_static()), 103 92 } 104 93 } 105 94 }
+2 -18
crates/jacquard-api/src/sh_tangled/repo/get_default_branch.rs
··· 65 65 PartialEq, 66 66 Eq, 67 67 thiserror::Error, 68 - miette::Diagnostic 68 + miette::Diagnostic, 69 + jacquard_derive::IntoStatic 69 70 )] 70 71 #[serde(tag = "error", content = "message")] 71 72 #[serde(bound(deserialize = "'de: 'a"))] ··· 96 97 Ok(()) 97 98 } 98 99 Self::Unknown(err) => write!(f, "Unknown error: {:?}", err), 99 - } 100 - } 101 - } 102 - 103 - impl jacquard_common::IntoStatic for GetDefaultBranchError<'_> { 104 - type Output = GetDefaultBranchError<'static>; 105 - fn into_static(self) -> Self::Output { 106 - match self { 107 - GetDefaultBranchError::RepoNotFound(v) => { 108 - GetDefaultBranchError::RepoNotFound(v.into_static()) 109 - } 110 - GetDefaultBranchError::InvalidRequest(v) => { 111 - GetDefaultBranchError::InvalidRequest(v.into_static()) 112 - } 113 - GetDefaultBranchError::Unknown(v) => { 114 - GetDefaultBranchError::Unknown(v.into_static()) 115 - } 116 100 } 117 101 } 118 102 }
+2 -19
crates/jacquard-api/src/sh_tangled/repo/languages.rs
··· 94 94 PartialEq, 95 95 Eq, 96 96 thiserror::Error, 97 - miette::Diagnostic 97 + miette::Diagnostic, 98 + jacquard_derive::IntoStatic 98 99 )] 99 100 #[serde(tag = "error", content = "message")] 100 101 #[serde(bound(deserialize = "'de: 'a"))] ··· 135 136 Ok(()) 136 137 } 137 138 Self::Unknown(err) => write!(f, "Unknown error: {:?}", err), 138 - } 139 - } 140 - } 141 - 142 - impl jacquard_common::IntoStatic for LanguagesError<'_> { 143 - type Output = LanguagesError<'static>; 144 - fn into_static(self) -> Self::Output { 145 - match self { 146 - LanguagesError::RepoNotFound(v) => { 147 - LanguagesError::RepoNotFound(v.into_static()) 148 - } 149 - LanguagesError::RefNotFound(v) => { 150 - LanguagesError::RefNotFound(v.into_static()) 151 - } 152 - LanguagesError::InvalidRequest(v) => { 153 - LanguagesError::InvalidRequest(v.into_static()) 154 - } 155 - LanguagesError::Unknown(v) => LanguagesError::Unknown(v.into_static()), 156 139 } 157 140 } 158 141 }
+2 -14
crates/jacquard-api/src/sh_tangled/repo/log.rs
··· 59 59 PartialEq, 60 60 Eq, 61 61 thiserror::Error, 62 - miette::Diagnostic 62 + miette::Diagnostic, 63 + jacquard_derive::IntoStatic 63 64 )] 64 65 #[serde(tag = "error", content = "message")] 65 66 #[serde(bound(deserialize = "'de: 'a"))] ··· 110 111 Ok(()) 111 112 } 112 113 Self::Unknown(err) => write!(f, "Unknown error: {:?}", err), 113 - } 114 - } 115 - } 116 - 117 - impl jacquard_common::IntoStatic for LogError<'_> { 118 - type Output = LogError<'static>; 119 - fn into_static(self) -> Self::Output { 120 - match self { 121 - LogError::RepoNotFound(v) => LogError::RepoNotFound(v.into_static()), 122 - LogError::RefNotFound(v) => LogError::RefNotFound(v.into_static()), 123 - LogError::PathNotFound(v) => LogError::PathNotFound(v.into_static()), 124 - LogError::InvalidRequest(v) => LogError::InvalidRequest(v.into_static()), 125 - LogError::Unknown(v) => LogError::Unknown(v.into_static()), 126 114 } 127 115 } 128 116 }
+2 -12
crates/jacquard-api/src/sh_tangled/repo/tags.rs
··· 51 51 PartialEq, 52 52 Eq, 53 53 thiserror::Error, 54 - miette::Diagnostic 54 + miette::Diagnostic, 55 + jacquard_derive::IntoStatic 55 56 )] 56 57 #[serde(tag = "error", content = "message")] 57 58 #[serde(bound(deserialize = "'de: 'a"))] ··· 82 83 Ok(()) 83 84 } 84 85 Self::Unknown(err) => write!(f, "Unknown error: {:?}", err), 85 - } 86 - } 87 - } 88 - 89 - impl jacquard_common::IntoStatic for TagsError<'_> { 90 - type Output = TagsError<'static>; 91 - fn into_static(self) -> Self::Output { 92 - match self { 93 - TagsError::RepoNotFound(v) => TagsError::RepoNotFound(v.into_static()), 94 - TagsError::InvalidRequest(v) => TagsError::InvalidRequest(v.into_static()), 95 - TagsError::Unknown(v) => TagsError::Unknown(v.into_static()), 96 86 } 97 87 } 98 88 }
+2 -14
crates/jacquard-api/src/sh_tangled/repo/tree.rs
··· 93 93 PartialEq, 94 94 Eq, 95 95 thiserror::Error, 96 - miette::Diagnostic 96 + miette::Diagnostic, 97 + jacquard_derive::IntoStatic 97 98 )] 98 99 #[serde(tag = "error", content = "message")] 99 100 #[serde(bound(deserialize = "'de: 'a"))] ··· 144 145 Ok(()) 145 146 } 146 147 Self::Unknown(err) => write!(f, "Unknown error: {:?}", err), 147 - } 148 - } 149 - } 150 - 151 - impl jacquard_common::IntoStatic for TreeError<'_> { 152 - type Output = TreeError<'static>; 153 - fn into_static(self) -> Self::Output { 154 - match self { 155 - TreeError::RepoNotFound(v) => TreeError::RepoNotFound(v.into_static()), 156 - TreeError::RefNotFound(v) => TreeError::RefNotFound(v.into_static()), 157 - TreeError::PathNotFound(v) => TreeError::PathNotFound(v.into_static()), 158 - TreeError::InvalidRequest(v) => TreeError::InvalidRequest(v.into_static()), 159 - TreeError::Unknown(v) => TreeError::Unknown(v.into_static()), 160 148 } 161 149 } 162 150 }
+4 -4
crates/jacquard-api/src/sh_weaver/actor.rs
··· 44 44 #[serde(rename_all = "camelCase")] 45 45 pub struct ProfileDataView<'a> { 46 46 #[serde(borrow)] 47 - pub inner: ProfileDataViewRecordInner<'a>, 47 + pub inner: ProfileDataViewInner<'a>, 48 48 } 49 49 50 50 #[jacquard_derive::open_union] ··· 59 59 )] 60 60 #[serde(tag = "$type")] 61 61 #[serde(bound(deserialize = "'de: 'a"))] 62 - pub enum ProfileDataViewRecordInner<'a> { 62 + pub enum ProfileDataViewInner<'a> { 63 63 #[serde(rename = "sh.weaver.actor.defs#profileView")] 64 - DefsProfileView(Box<crate::sh_weaver::actor::ProfileView<'a>>), 64 + ProfileView(Box<crate::sh_weaver::actor::ProfileView<'a>>), 65 65 #[serde(rename = "app.bsky.actor.defs#profileViewDetailed")] 66 - DefsProfileViewDetailed(Box<crate::app_bsky::actor::ProfileViewDetailed<'a>>), 66 + ProfileViewDetailed(Box<crate::app_bsky::actor::ProfileViewDetailed<'a>>), 67 67 } 68 68 69 69 #[jacquard_derive::lexicon]
+8 -2
crates/jacquard-api/src/sh_weaver/edit.rs
··· 22 22 #[serde(rename_all = "camelCase")] 23 23 pub struct DocRef<'a> { 24 24 #[serde(borrow)] 25 - pub value: DocRefRecordValue<'a>, 25 + pub value: DocRefValue<'a>, 26 26 } 27 27 28 28 #[jacquard_derive::open_union] ··· 37 37 )] 38 38 #[serde(tag = "$type")] 39 39 #[serde(bound(deserialize = "'de: 'a"))] 40 - pub enum DocRefRecordValue<'a> {} 40 + pub enum DocRefValue<'a> { 41 + #[serde(rename = "sh.weaver.edit.defs#notebookRef")] 42 + NotebookRef(Box<crate::sh_weaver::edit::NotebookRef<'a>>), 43 + #[serde(rename = "sh.weaver.edit.defs#entryRef")] 44 + EntryRef(Box<crate::sh_weaver::edit::EntryRef<'a>>), 45 + } 46 + 41 47 #[jacquard_derive::lexicon] 42 48 #[derive( 43 49 serde::Serialize,
+8 -2
crates/jacquard-api/src/sh_weaver/edit/cursor.rs
··· 18 18 #[serde(rename_all = "camelCase")] 19 19 pub struct ContainerId<'a> { 20 20 #[serde(borrow)] 21 - pub value: ContainerIdRecordValue<'a>, 21 + pub value: ContainerIdValue<'a>, 22 22 } 23 23 24 24 #[jacquard_derive::open_union] ··· 33 33 )] 34 34 #[serde(tag = "$type")] 35 35 #[serde(bound(deserialize = "'de: 'a"))] 36 - pub enum ContainerIdRecordValue<'a> {} 36 + pub enum ContainerIdValue<'a> { 37 + #[serde(rename = "sh.weaver.edit.cursor#normalContainerId")] 38 + NormalContainerId(Box<crate::sh_weaver::edit::cursor::NormalContainerId<'a>>), 39 + #[serde(rename = "sh.weaver.edit.cursor#rootContainerId")] 40 + RootContainerId(Box<crate::sh_weaver::edit::cursor::RootContainerId<'a>>), 41 + } 42 + 37 43 #[jacquard_derive::lexicon] 38 44 #[derive( 39 45 serde::Serialize,
+5 -5
crates/jacquard-api/src/sh_weaver/embed/images.rs
··· 26 26 pub blurhash: std::option::Option<jacquard_common::CowStr<'a>>, 27 27 #[serde(skip_serializing_if = "std::option::Option::is_none")] 28 28 #[serde(borrow)] 29 - pub dimensions: std::option::Option<ImageRecordDimensions<'a>>, 29 + pub dimensions: std::option::Option<ImageDimensions<'a>>, 30 30 #[serde(borrow)] 31 31 pub image: jacquard_common::types::blob::Blob<'a>, 32 32 } ··· 43 43 )] 44 44 #[serde(tag = "$type")] 45 45 #[serde(bound(deserialize = "'de: 'a"))] 46 - pub enum ImageRecordDimensions<'a> { 46 + pub enum ImageDimensions<'a> { 47 47 #[serde(rename = "app.bsky.embed.defs#aspectRatio")] 48 - DefsAspectRatio(Box<crate::app_bsky::embed::AspectRatio<'a>>), 48 + AspectRatio(Box<crate::app_bsky::embed::AspectRatio<'a>>), 49 49 #[serde(rename = "sh.weaver.embed.defs#percentSize")] 50 - DefsPercentSize(Box<crate::sh_weaver::embed::PercentSize<'a>>), 50 + PercentSize(Box<crate::sh_weaver::embed::PercentSize<'a>>), 51 51 #[serde(rename = "sh.weaver.embed.defs#pixelSize")] 52 - DefsPixelSize(Box<crate::sh_weaver::embed::PixelSize<'a>>), 52 + PixelSize(Box<crate::sh_weaver::embed::PixelSize<'a>>), 53 53 } 54 54 55 55 #[jacquard_derive::lexicon]
+8 -8
crates/jacquard-api/src/sh_weaver/embed/record_with_media.rs
··· 18 18 #[serde(rename_all = "camelCase")] 19 19 pub struct RecordWithMedia<'a> { 20 20 #[serde(borrow)] 21 - pub media: RecordWithMediaRecordMedia<'a>, 21 + pub media: RecordWithMediaMedia<'a>, 22 22 #[serde(borrow)] 23 - pub record: RecordWithMediaRecordRecord<'a>, 23 + pub record: RecordWithMediaRecord<'a>, 24 24 } 25 25 26 26 #[jacquard_derive::open_union] ··· 35 35 )] 36 36 #[serde(tag = "$type")] 37 37 #[serde(bound(deserialize = "'de: 'a"))] 38 - pub enum RecordWithMediaRecordMedia<'a> { 38 + pub enum RecordWithMediaMedia<'a> { 39 39 #[serde(rename = "sh.weaver.embed.images")] 40 40 Images(Box<crate::sh_weaver::embed::images::Images<'a>>), 41 41 #[serde(rename = "sh.weaver.embed.external")] ··· 60 60 )] 61 61 #[serde(tag = "$type")] 62 62 #[serde(bound(deserialize = "'de: 'a"))] 63 - pub enum RecordWithMediaRecordRecord<'a> { 63 + pub enum RecordWithMediaRecord<'a> { 64 64 #[serde(rename = "app.bsky.embed.record")] 65 65 Record(Box<crate::app_bsky::embed::record::Record<'a>>), 66 66 #[serde(rename = "sh.weaver.embed.records")] ··· 80 80 #[serde(rename_all = "camelCase")] 81 81 pub struct View<'a> { 82 82 #[serde(borrow)] 83 - pub media: ViewRecordMedia<'a>, 83 + pub media: ViewMedia<'a>, 84 84 #[serde(borrow)] 85 - pub record: ViewRecordRecord<'a>, 85 + pub record: ViewRecord<'a>, 86 86 } 87 87 88 88 #[jacquard_derive::open_union] ··· 97 97 )] 98 98 #[serde(tag = "$type")] 99 99 #[serde(bound(deserialize = "'de: 'a"))] 100 - pub enum ViewRecordMedia<'a> { 100 + pub enum ViewMedia<'a> { 101 101 #[serde(rename = "sh.weaver.embed.images")] 102 102 Images(Box<crate::sh_weaver::embed::images::Images<'a>>), 103 103 #[serde(rename = "sh.weaver.embed.external#view")] ··· 122 122 )] 123 123 #[serde(tag = "$type")] 124 124 #[serde(bound(deserialize = "'de: 'a"))] 125 - pub enum ViewRecordRecord<'a> { 125 + pub enum ViewRecord<'a> { 126 126 #[serde(rename = "sh.weaver.embed.records#view")] 127 127 RecordsView(Box<crate::sh_weaver::embed::records::View<'a>>), 128 128 #[serde(rename = "app.bsky.embed.record#view")]
+29 -11
crates/jacquard-api/src/sh_weaver/embed/records.rs
··· 34 34 #[serde(rename_all = "camelCase")] 35 35 pub struct View<'a> { 36 36 #[serde(borrow)] 37 - pub record: ViewRecordRecord<'a>, 37 + pub record: ViewUnionRecord<'a>, 38 38 } 39 39 40 40 #[jacquard_derive::open_union] ··· 49 49 )] 50 50 #[serde(tag = "$type")] 51 51 #[serde(bound(deserialize = "'de: 'a"))] 52 - pub enum ViewRecordRecord<'a> { 52 + pub enum ViewUnionRecord<'a> { 53 + #[serde(rename = "sh.weaver.embed.records#viewRecord")] 54 + ViewRecord(Box<crate::sh_weaver::embed::records::ViewRecord<'a>>), 55 + #[serde(rename = "sh.weaver.embed.records#viewNotFound")] 56 + ViewNotFound(Box<crate::sh_weaver::embed::records::ViewNotFound<'a>>), 57 + #[serde(rename = "sh.weaver.embed.records#viewBlocked")] 58 + ViewBlocked(Box<crate::sh_weaver::embed::records::ViewBlocked<'a>>), 59 + #[serde(rename = "sh.weaver.embed.records#viewDetached")] 60 + ViewDetached(Box<crate::sh_weaver::embed::records::ViewDetached<'a>>), 53 61 #[serde(rename = "app.bsky.feed.defs#generatorView")] 54 - DefsGeneratorView(Box<crate::app_bsky::feed::GeneratorView<'a>>), 62 + GeneratorView(Box<crate::app_bsky::feed::GeneratorView<'a>>), 55 63 #[serde(rename = "app.bsky.graph.defs#listView")] 56 - DefsListView(Box<crate::app_bsky::graph::ListView<'a>>), 64 + ListView(Box<crate::app_bsky::graph::ListView<'a>>), 57 65 #[serde(rename = "app.bsky.labeler.defs#labelerView")] 58 - DefsLabelerView(Box<crate::app_bsky::labeler::LabelerView<'a>>), 66 + LabelerView(Box<crate::app_bsky::labeler::LabelerView<'a>>), 59 67 #[serde(rename = "app.bsky.graph.defs#starterPackViewBasic")] 60 - DefsStarterPackViewBasic(Box<crate::app_bsky::graph::StarterPackViewBasic<'a>>), 68 + StarterPackViewBasic(Box<crate::app_bsky::graph::StarterPackViewBasic<'a>>), 61 69 } 62 70 63 71 #[jacquard_derive::lexicon] ··· 126 134 #[serde(rename_all = "camelCase")] 127 135 pub struct ViewRecord<'a> { 128 136 #[serde(borrow)] 129 - pub author: ViewRecordRecordAuthor<'a>, 137 + pub author: crate::app_bsky::actor::ProfileViewBasic<'a>, 130 138 #[serde(borrow)] 131 139 pub cid: jacquard_common::types::string::Cid<'a>, 132 140 #[serde(skip_serializing_if = "std::option::Option::is_none")] 133 141 #[serde(borrow)] 134 - pub embeds: std::option::Option<Vec<jacquard_common::types::value::Data<'a>>>, 142 + pub embeds: std::option::Option<Vec<ViewRecordEmbedsItem<'a>>>, 135 143 pub indexed_at: jacquard_common::types::string::Datetime, 136 144 #[serde(skip_serializing_if = "std::option::Option::is_none")] 137 145 #[serde(borrow)] ··· 163 171 )] 164 172 #[serde(tag = "$type")] 165 173 #[serde(bound(deserialize = "'de: 'a"))] 166 - pub enum ViewRecordRecordAuthor<'a> { 167 - #[serde(rename = "app.bsky.actor.defs#profileViewBasic")] 168 - DefsProfileViewBasic(Box<crate::app_bsky::actor::ProfileViewBasic<'a>>), 174 + pub enum ViewRecordEmbedsItem<'a> { 175 + #[serde(rename = "app.bsky.embed.images#view")] 176 + ImagesView(Box<crate::app_bsky::embed::images::View<'a>>), 177 + #[serde(rename = "app.bsky.embed.video#view")] 178 + VideoView(Box<crate::app_bsky::embed::video::View<'a>>), 179 + #[serde(rename = "app.bsky.embed.external#view")] 180 + ExternalView(Box<crate::app_bsky::embed::external::View<'a>>), 181 + #[serde(rename = "app.bsky.embed.record#view")] 182 + RecordView(Box<crate::app_bsky::embed::record::View<'a>>), 183 + #[serde(rename = "app.bsky.embed.recordWithMedia#view")] 184 + RecordWithMediaView(Box<crate::app_bsky::embed::record_with_media::View<'a>>), 185 + #[serde(rename = "sh.weaver.embed.records#view")] 186 + View(Box<crate::sh_weaver::embed::records::View<'a>>), 169 187 }
+4 -4
crates/jacquard-api/src/sh_weaver/notebook/authors.rs
··· 22 22 pub index: std::option::Option<i64>, 23 23 #[serde(skip_serializing_if = "std::option::Option::is_none")] 24 24 #[serde(borrow)] 25 - pub profile: std::option::Option<AuthorListItemRecordProfile<'a>>, 25 + pub profile: std::option::Option<AuthorListItemProfile<'a>>, 26 26 } 27 27 28 28 #[jacquard_derive::open_union] ··· 37 37 )] 38 38 #[serde(tag = "$type")] 39 39 #[serde(bound(deserialize = "'de: 'a"))] 40 - pub enum AuthorListItemRecordProfile<'a> { 40 + pub enum AuthorListItemProfile<'a> { 41 41 #[serde(rename = "app.bsky.actor.defs#profileViewBasic")] 42 - DefsProfileViewBasic(Box<crate::app_bsky::actor::ProfileViewBasic<'a>>), 42 + ProfileViewBasic(Box<crate::app_bsky::actor::ProfileViewBasic<'a>>), 43 43 #[serde(rename = "sh.weaver.actor.defs#profileView")] 44 - DefsProfileView(Box<crate::sh_weaver::actor::ProfileView<'a>>), 44 + ProfileView(Box<crate::sh_weaver::actor::ProfileView<'a>>), 45 45 } 46 46 47 47 ///Authors of a Weaver notebook.
+18 -1
crates/jacquard-api/src/social_clippr/actor.rs
··· 13 13 pub mod search_profiles; 14 14 pub mod search_tags; 15 15 16 + #[jacquard_derive::open_union] 17 + #[derive( 18 + serde::Serialize, 19 + serde::Deserialize, 20 + Debug, 21 + Clone, 22 + PartialEq, 23 + Eq, 24 + jacquard_derive::IntoStatic 25 + )] 26 + #[serde(tag = "$type")] 27 + #[serde(bound(deserialize = "'de: 'a"))] 28 + pub enum PreferencesItem<'a> { 29 + #[serde(rename = "social.clippr.actor.defs#publishingScopesPref")] 30 + PublishingScopesPref(Box<crate::social_clippr::actor::PublishingScopesPref<'a>>), 31 + } 32 + 16 33 ///An array of refs to various preferences. 17 - pub type Preferences<'a> = Vec<jacquard_common::types::value::Data<'a>>; 34 + pub type Preferences<'a> = Vec<PreferencesItem<'a>>; 18 35 ///A view of an actor's profile. 19 36 #[jacquard_derive::lexicon] 20 37 #[derive(
+2 -19
crates/jacquard-api/src/social_grain/gallery.rs
··· 26 26 ///Self-label values for this post. Effectively content warnings. 27 27 #[serde(skip_serializing_if = "std::option::Option::is_none")] 28 28 #[serde(borrow)] 29 - pub labels: std::option::Option<GalleryRecordLabels<'a>>, 29 + pub labels: std::option::Option<crate::com_atproto::label::SelfLabels<'a>>, 30 30 #[serde(borrow)] 31 31 pub title: jacquard_common::CowStr<'a>, 32 - } 33 - 34 - #[jacquard_derive::open_union] 35 - #[derive( 36 - serde::Serialize, 37 - serde::Deserialize, 38 - Debug, 39 - Clone, 40 - PartialEq, 41 - Eq, 42 - jacquard_derive::IntoStatic 43 - )] 44 - #[serde(tag = "$type")] 45 - #[serde(bound(deserialize = "'de: 'a"))] 46 - pub enum GalleryRecordLabels<'a> { 47 - #[serde(rename = "com.atproto.label.defs#selfLabels")] 48 - DefsSelfLabels(Box<crate::com_atproto::label::SelfLabels<'a>>), 49 32 } 50 33 51 34 impl jacquard_common::types::collection::Collection for Gallery<'_> { ··· 71 54 pub indexed_at: jacquard_common::types::string::Datetime, 72 55 #[serde(skip_serializing_if = "std::option::Option::is_none")] 73 56 #[serde(borrow)] 74 - pub items: std::option::Option<Vec<jacquard_common::types::value::Data<'a>>>, 57 + pub items: std::option::Option<Vec<crate::social_grain::photo::PhotoView<'a>>>, 75 58 #[serde(skip_serializing_if = "std::option::Option::is_none")] 76 59 #[serde(borrow)] 77 60 pub labels: std::option::Option<Vec<crate::com_atproto::label::Label<'a>>>,
+22 -1
crates/jacquard-api/src/social_psky/richtext/facet.rs
··· 53 53 #[serde(rename_all = "camelCase")] 54 54 pub struct Facet<'a> { 55 55 #[serde(borrow)] 56 - pub features: Vec<jacquard_common::types::value::Data<'a>>, 56 + pub features: Vec<FacetFeaturesItem<'a>>, 57 57 #[serde(borrow)] 58 58 pub index: crate::social_psky::richtext::facet::ByteSlice<'a>, 59 + } 60 + 61 + #[jacquard_derive::open_union] 62 + #[derive( 63 + serde::Serialize, 64 + serde::Deserialize, 65 + Debug, 66 + Clone, 67 + PartialEq, 68 + Eq, 69 + jacquard_derive::IntoStatic 70 + )] 71 + #[serde(tag = "$type")] 72 + #[serde(bound(deserialize = "'de: 'a"))] 73 + pub enum FacetFeaturesItem<'a> { 74 + #[serde(rename = "social.psky.richtext.facet#mention")] 75 + Mention(Box<crate::social_psky::richtext::facet::Mention<'a>>), 76 + #[serde(rename = "social.psky.richtext.facet#link")] 77 + Link(Box<crate::social_psky::richtext::facet::Link<'a>>), 78 + #[serde(rename = "social.psky.richtext.facet#room")] 79 + Room(Box<crate::social_psky::richtext::facet::Room<'a>>), 59 80 } 60 81 61 82 ///Facet feature for mention of another account. The text is usually a handle, including a '@' prefix, but the facet reference is a DID.
+2 -15
crates/jacquard-api/src/tools_ozone/communication/create_template.rs
··· 73 73 PartialEq, 74 74 Eq, 75 75 thiserror::Error, 76 - miette::Diagnostic 76 + miette::Diagnostic, 77 + jacquard_derive::IntoStatic 77 78 )] 78 79 #[serde(tag = "error", content = "message")] 79 80 #[serde(bound(deserialize = "'de: 'a"))] ··· 93 94 Ok(()) 94 95 } 95 96 Self::Unknown(err) => write!(f, "Unknown error: {:?}", err), 96 - } 97 - } 98 - } 99 - 100 - impl jacquard_common::IntoStatic for CreateTemplateError<'_> { 101 - type Output = CreateTemplateError<'static>; 102 - fn into_static(self) -> Self::Output { 103 - match self { 104 - CreateTemplateError::DuplicateTemplateName(v) => { 105 - CreateTemplateError::DuplicateTemplateName(v.into_static()) 106 - } 107 - CreateTemplateError::Unknown(v) => { 108 - CreateTemplateError::Unknown(v.into_static()) 109 - } 110 97 } 111 98 } 112 99 }
+2 -15
crates/jacquard-api/src/tools_ozone/communication/update_template.rs
··· 82 82 PartialEq, 83 83 Eq, 84 84 thiserror::Error, 85 - miette::Diagnostic 85 + miette::Diagnostic, 86 + jacquard_derive::IntoStatic 86 87 )] 87 88 #[serde(tag = "error", content = "message")] 88 89 #[serde(bound(deserialize = "'de: 'a"))] ··· 102 103 Ok(()) 103 104 } 104 105 Self::Unknown(err) => write!(f, "Unknown error: {:?}", err), 105 - } 106 - } 107 - } 108 - 109 - impl jacquard_common::IntoStatic for UpdateTemplateError<'_> { 110 - type Output = UpdateTemplateError<'static>; 111 - fn into_static(self) -> Self::Output { 112 - match self { 113 - UpdateTemplateError::DuplicateTemplateName(v) => { 114 - UpdateTemplateError::DuplicateTemplateName(v.into_static()) 115 - } 116 - UpdateTemplateError::Unknown(v) => { 117 - UpdateTemplateError::Unknown(v.into_static()) 118 - } 119 106 } 120 107 } 121 108 }
+24 -2
crates/jacquard-api/src/tools_ozone/hosting/get_account_history.rs
··· 73 73 #[serde(borrow)] 74 74 pub created_by: jacquard_common::CowStr<'a>, 75 75 #[serde(borrow)] 76 - pub details: EventRecordDetails<'a>, 76 + pub details: EventDetails<'a>, 77 77 } 78 78 79 79 #[jacquard_derive::open_union] ··· 88 88 )] 89 89 #[serde(tag = "$type")] 90 90 #[serde(bound(deserialize = "'de: 'a"))] 91 - pub enum EventRecordDetails<'a> {} 91 + pub enum EventDetails<'a> { 92 + #[serde(rename = "tools.ozone.hosting.getAccountHistory#accountCreated")] 93 + AccountCreated( 94 + Box<crate::tools_ozone::hosting::get_account_history::AccountCreated<'a>>, 95 + ), 96 + #[serde(rename = "tools.ozone.hosting.getAccountHistory#emailUpdated")] 97 + EmailUpdated( 98 + Box<crate::tools_ozone::hosting::get_account_history::EmailUpdated<'a>>, 99 + ), 100 + #[serde(rename = "tools.ozone.hosting.getAccountHistory#emailConfirmed")] 101 + EmailConfirmed( 102 + Box<crate::tools_ozone::hosting::get_account_history::EmailConfirmed<'a>>, 103 + ), 104 + #[serde(rename = "tools.ozone.hosting.getAccountHistory#passwordUpdated")] 105 + PasswordUpdated( 106 + Box<crate::tools_ozone::hosting::get_account_history::PasswordUpdated<'a>>, 107 + ), 108 + #[serde(rename = "tools.ozone.hosting.getAccountHistory#handleUpdated")] 109 + HandleUpdated( 110 + Box<crate::tools_ozone::hosting::get_account_history::HandleUpdated<'a>>, 111 + ), 112 + } 113 + 92 114 #[jacquard_derive::lexicon] 93 115 #[derive( 94 116 serde::Serialize,
+173 -32
crates/jacquard-api/src/tools_ozone/moderation.rs
··· 178 178 pub created_at: jacquard_common::types::string::Datetime, 179 179 #[serde(skip_serializing_if = "std::option::Option::is_none")] 180 180 #[serde(borrow)] 181 - pub details: std::option::Option<BlobViewRecordDetails<'a>>, 181 + pub details: std::option::Option<BlobViewDetails<'a>>, 182 182 #[serde(borrow)] 183 183 pub mime_type: jacquard_common::CowStr<'a>, 184 184 #[serde(skip_serializing_if = "std::option::Option::is_none")] ··· 199 199 )] 200 200 #[serde(tag = "$type")] 201 201 #[serde(bound(deserialize = "'de: 'a"))] 202 - pub enum BlobViewRecordDetails<'a> {} 202 + pub enum BlobViewDetails<'a> { 203 + #[serde(rename = "tools.ozone.moderation.defs#imageDetails")] 204 + ImageDetails(Box<crate::tools_ozone::moderation::ImageDetails<'a>>), 205 + #[serde(rename = "tools.ozone.moderation.defs#videoDetails")] 206 + VideoDetails(Box<crate::tools_ozone::moderation::VideoDetails<'a>>), 207 + } 208 + 203 209 ///Logs cancellation of a scheduled takedown action for an account. 204 210 #[jacquard_derive::lexicon] 205 211 #[derive( ··· 619 625 #[serde(borrow)] 620 626 pub creator_handle: std::option::Option<jacquard_common::CowStr<'a>>, 621 627 #[serde(borrow)] 622 - pub event: ModEventViewRecordEvent<'a>, 628 + pub event: ModEventViewEvent<'a>, 623 629 pub id: i64, 624 630 #[serde(skip_serializing_if = "std::option::Option::is_none")] 625 631 #[serde(borrow)] 626 632 pub mod_tool: std::option::Option<crate::tools_ozone::moderation::ModTool<'a>>, 627 633 #[serde(borrow)] 628 - pub subject: ModEventViewRecordSubject<'a>, 634 + pub subject: ModEventViewSubject<'a>, 629 635 #[serde(borrow)] 630 636 pub subject_blob_cids: Vec<jacquard_common::CowStr<'a>>, 631 637 #[serde(skip_serializing_if = "std::option::Option::is_none")] ··· 645 651 )] 646 652 #[serde(tag = "$type")] 647 653 #[serde(bound(deserialize = "'de: 'a"))] 648 - pub enum ModEventViewRecordEvent<'a> {} 654 + pub enum ModEventViewEvent<'a> { 655 + #[serde(rename = "tools.ozone.moderation.defs#modEventTakedown")] 656 + ModEventTakedown(Box<crate::tools_ozone::moderation::ModEventTakedown<'a>>), 657 + #[serde(rename = "tools.ozone.moderation.defs#modEventReverseTakedown")] 658 + ModEventReverseTakedown( 659 + Box<crate::tools_ozone::moderation::ModEventReverseTakedown<'a>>, 660 + ), 661 + #[serde(rename = "tools.ozone.moderation.defs#modEventComment")] 662 + ModEventComment(Box<crate::tools_ozone::moderation::ModEventComment<'a>>), 663 + #[serde(rename = "tools.ozone.moderation.defs#modEventReport")] 664 + ModEventReport(Box<crate::tools_ozone::moderation::ModEventReport<'a>>), 665 + #[serde(rename = "tools.ozone.moderation.defs#modEventLabel")] 666 + ModEventLabel(Box<crate::tools_ozone::moderation::ModEventLabel<'a>>), 667 + #[serde(rename = "tools.ozone.moderation.defs#modEventAcknowledge")] 668 + ModEventAcknowledge(Box<crate::tools_ozone::moderation::ModEventAcknowledge<'a>>), 669 + #[serde(rename = "tools.ozone.moderation.defs#modEventEscalate")] 670 + ModEventEscalate(Box<crate::tools_ozone::moderation::ModEventEscalate<'a>>), 671 + #[serde(rename = "tools.ozone.moderation.defs#modEventMute")] 672 + ModEventMute(Box<crate::tools_ozone::moderation::ModEventMute<'a>>), 673 + #[serde(rename = "tools.ozone.moderation.defs#modEventUnmute")] 674 + ModEventUnmute(Box<crate::tools_ozone::moderation::ModEventUnmute<'a>>), 675 + #[serde(rename = "tools.ozone.moderation.defs#modEventMuteReporter")] 676 + ModEventMuteReporter(Box<crate::tools_ozone::moderation::ModEventMuteReporter<'a>>), 677 + #[serde(rename = "tools.ozone.moderation.defs#modEventUnmuteReporter")] 678 + ModEventUnmuteReporter( 679 + Box<crate::tools_ozone::moderation::ModEventUnmuteReporter<'a>>, 680 + ), 681 + #[serde(rename = "tools.ozone.moderation.defs#modEventEmail")] 682 + ModEventEmail(Box<crate::tools_ozone::moderation::ModEventEmail<'a>>), 683 + #[serde(rename = "tools.ozone.moderation.defs#modEventResolveAppeal")] 684 + ModEventResolveAppeal( 685 + Box<crate::tools_ozone::moderation::ModEventResolveAppeal<'a>>, 686 + ), 687 + #[serde(rename = "tools.ozone.moderation.defs#modEventDivert")] 688 + ModEventDivert(Box<crate::tools_ozone::moderation::ModEventDivert<'a>>), 689 + #[serde(rename = "tools.ozone.moderation.defs#modEventTag")] 690 + ModEventTag(Box<crate::tools_ozone::moderation::ModEventTag<'a>>), 691 + #[serde(rename = "tools.ozone.moderation.defs#accountEvent")] 692 + AccountEvent(Box<crate::tools_ozone::moderation::AccountEvent<'a>>), 693 + #[serde(rename = "tools.ozone.moderation.defs#identityEvent")] 694 + IdentityEvent(Box<crate::tools_ozone::moderation::IdentityEvent<'a>>), 695 + #[serde(rename = "tools.ozone.moderation.defs#recordEvent")] 696 + RecordEvent(Box<crate::tools_ozone::moderation::RecordEvent<'a>>), 697 + #[serde(rename = "tools.ozone.moderation.defs#modEventPriorityScore")] 698 + ModEventPriorityScore( 699 + Box<crate::tools_ozone::moderation::ModEventPriorityScore<'a>>, 700 + ), 701 + #[serde(rename = "tools.ozone.moderation.defs#ageAssuranceEvent")] 702 + AgeAssuranceEvent(Box<crate::tools_ozone::moderation::AgeAssuranceEvent<'a>>), 703 + #[serde(rename = "tools.ozone.moderation.defs#ageAssuranceOverrideEvent")] 704 + AgeAssuranceOverrideEvent( 705 + Box<crate::tools_ozone::moderation::AgeAssuranceOverrideEvent<'a>>, 706 + ), 707 + #[serde(rename = "tools.ozone.moderation.defs#revokeAccountCredentialsEvent")] 708 + RevokeAccountCredentialsEvent( 709 + Box<crate::tools_ozone::moderation::RevokeAccountCredentialsEvent<'a>>, 710 + ), 711 + #[serde(rename = "tools.ozone.moderation.defs#scheduleTakedownEvent")] 712 + ScheduleTakedownEvent( 713 + Box<crate::tools_ozone::moderation::ScheduleTakedownEvent<'a>>, 714 + ), 715 + #[serde(rename = "tools.ozone.moderation.defs#cancelScheduledTakedownEvent")] 716 + CancelScheduledTakedownEvent( 717 + Box<crate::tools_ozone::moderation::CancelScheduledTakedownEvent<'a>>, 718 + ), 719 + } 720 + 649 721 #[jacquard_derive::open_union] 650 722 #[derive( 651 723 serde::Serialize, ··· 658 730 )] 659 731 #[serde(tag = "$type")] 660 732 #[serde(bound(deserialize = "'de: 'a"))] 661 - pub enum ModEventViewRecordSubject<'a> { 733 + pub enum ModEventViewSubject<'a> { 662 734 #[serde(rename = "com.atproto.admin.defs#repoRef")] 663 - DefsRepoRef(Box<crate::com_atproto::admin::RepoRef<'a>>), 735 + RepoRef(Box<crate::com_atproto::admin::RepoRef<'a>>), 664 736 #[serde(rename = "com.atproto.repo.strongRef")] 665 737 StrongRef(Box<crate::com_atproto::repo::strong_ref::StrongRef<'a>>), 666 738 #[serde(rename = "chat.bsky.convo.defs#messageRef")] 667 - DefsMessageRef(Box<crate::chat_bsky::convo::MessageRef<'a>>), 739 + MessageRef(Box<crate::chat_bsky::convo::MessageRef<'a>>), 668 740 } 669 741 670 742 #[jacquard_derive::lexicon] ··· 683 755 #[serde(borrow)] 684 756 pub created_by: jacquard_common::types::string::Did<'a>, 685 757 #[serde(borrow)] 686 - pub event: ModEventViewDetailRecordEvent<'a>, 758 + pub event: ModEventViewDetailEvent<'a>, 687 759 pub id: i64, 688 760 #[serde(skip_serializing_if = "std::option::Option::is_none")] 689 761 #[serde(borrow)] 690 762 pub mod_tool: std::option::Option<crate::tools_ozone::moderation::ModTool<'a>>, 691 763 #[serde(borrow)] 692 - pub subject: ModEventViewDetailRecordSubject<'a>, 764 + pub subject: ModEventViewDetailSubject<'a>, 693 765 #[serde(borrow)] 694 766 pub subject_blobs: Vec<crate::tools_ozone::moderation::BlobView<'a>>, 695 767 } ··· 706 778 )] 707 779 #[serde(tag = "$type")] 708 780 #[serde(bound(deserialize = "'de: 'a"))] 709 - pub enum ModEventViewDetailRecordEvent<'a> {} 781 + pub enum ModEventViewDetailEvent<'a> { 782 + #[serde(rename = "tools.ozone.moderation.defs#modEventTakedown")] 783 + ModEventTakedown(Box<crate::tools_ozone::moderation::ModEventTakedown<'a>>), 784 + #[serde(rename = "tools.ozone.moderation.defs#modEventReverseTakedown")] 785 + ModEventReverseTakedown( 786 + Box<crate::tools_ozone::moderation::ModEventReverseTakedown<'a>>, 787 + ), 788 + #[serde(rename = "tools.ozone.moderation.defs#modEventComment")] 789 + ModEventComment(Box<crate::tools_ozone::moderation::ModEventComment<'a>>), 790 + #[serde(rename = "tools.ozone.moderation.defs#modEventReport")] 791 + ModEventReport(Box<crate::tools_ozone::moderation::ModEventReport<'a>>), 792 + #[serde(rename = "tools.ozone.moderation.defs#modEventLabel")] 793 + ModEventLabel(Box<crate::tools_ozone::moderation::ModEventLabel<'a>>), 794 + #[serde(rename = "tools.ozone.moderation.defs#modEventAcknowledge")] 795 + ModEventAcknowledge(Box<crate::tools_ozone::moderation::ModEventAcknowledge<'a>>), 796 + #[serde(rename = "tools.ozone.moderation.defs#modEventEscalate")] 797 + ModEventEscalate(Box<crate::tools_ozone::moderation::ModEventEscalate<'a>>), 798 + #[serde(rename = "tools.ozone.moderation.defs#modEventMute")] 799 + ModEventMute(Box<crate::tools_ozone::moderation::ModEventMute<'a>>), 800 + #[serde(rename = "tools.ozone.moderation.defs#modEventUnmute")] 801 + ModEventUnmute(Box<crate::tools_ozone::moderation::ModEventUnmute<'a>>), 802 + #[serde(rename = "tools.ozone.moderation.defs#modEventMuteReporter")] 803 + ModEventMuteReporter(Box<crate::tools_ozone::moderation::ModEventMuteReporter<'a>>), 804 + #[serde(rename = "tools.ozone.moderation.defs#modEventUnmuteReporter")] 805 + ModEventUnmuteReporter( 806 + Box<crate::tools_ozone::moderation::ModEventUnmuteReporter<'a>>, 807 + ), 808 + #[serde(rename = "tools.ozone.moderation.defs#modEventEmail")] 809 + ModEventEmail(Box<crate::tools_ozone::moderation::ModEventEmail<'a>>), 810 + #[serde(rename = "tools.ozone.moderation.defs#modEventResolveAppeal")] 811 + ModEventResolveAppeal( 812 + Box<crate::tools_ozone::moderation::ModEventResolveAppeal<'a>>, 813 + ), 814 + #[serde(rename = "tools.ozone.moderation.defs#modEventDivert")] 815 + ModEventDivert(Box<crate::tools_ozone::moderation::ModEventDivert<'a>>), 816 + #[serde(rename = "tools.ozone.moderation.defs#modEventTag")] 817 + ModEventTag(Box<crate::tools_ozone::moderation::ModEventTag<'a>>), 818 + #[serde(rename = "tools.ozone.moderation.defs#accountEvent")] 819 + AccountEvent(Box<crate::tools_ozone::moderation::AccountEvent<'a>>), 820 + #[serde(rename = "tools.ozone.moderation.defs#identityEvent")] 821 + IdentityEvent(Box<crate::tools_ozone::moderation::IdentityEvent<'a>>), 822 + #[serde(rename = "tools.ozone.moderation.defs#recordEvent")] 823 + RecordEvent(Box<crate::tools_ozone::moderation::RecordEvent<'a>>), 824 + #[serde(rename = "tools.ozone.moderation.defs#modEventPriorityScore")] 825 + ModEventPriorityScore( 826 + Box<crate::tools_ozone::moderation::ModEventPriorityScore<'a>>, 827 + ), 828 + #[serde(rename = "tools.ozone.moderation.defs#ageAssuranceEvent")] 829 + AgeAssuranceEvent(Box<crate::tools_ozone::moderation::AgeAssuranceEvent<'a>>), 830 + #[serde(rename = "tools.ozone.moderation.defs#ageAssuranceOverrideEvent")] 831 + AgeAssuranceOverrideEvent( 832 + Box<crate::tools_ozone::moderation::AgeAssuranceOverrideEvent<'a>>, 833 + ), 834 + #[serde(rename = "tools.ozone.moderation.defs#revokeAccountCredentialsEvent")] 835 + RevokeAccountCredentialsEvent( 836 + Box<crate::tools_ozone::moderation::RevokeAccountCredentialsEvent<'a>>, 837 + ), 838 + #[serde(rename = "tools.ozone.moderation.defs#scheduleTakedownEvent")] 839 + ScheduleTakedownEvent( 840 + Box<crate::tools_ozone::moderation::ScheduleTakedownEvent<'a>>, 841 + ), 842 + #[serde(rename = "tools.ozone.moderation.defs#cancelScheduledTakedownEvent")] 843 + CancelScheduledTakedownEvent( 844 + Box<crate::tools_ozone::moderation::CancelScheduledTakedownEvent<'a>>, 845 + ), 846 + } 847 + 710 848 #[jacquard_derive::open_union] 711 849 #[derive( 712 850 serde::Serialize, ··· 719 857 )] 720 858 #[serde(tag = "$type")] 721 859 #[serde(bound(deserialize = "'de: 'a"))] 722 - pub enum ModEventViewDetailRecordSubject<'a> {} 860 + pub enum ModEventViewDetailSubject<'a> { 861 + #[serde(rename = "tools.ozone.moderation.defs#repoView")] 862 + RepoView(Box<crate::tools_ozone::moderation::RepoView<'a>>), 863 + #[serde(rename = "tools.ozone.moderation.defs#repoViewNotFound")] 864 + RepoViewNotFound(Box<crate::tools_ozone::moderation::RepoViewNotFound<'a>>), 865 + #[serde(rename = "tools.ozone.moderation.defs#recordView")] 866 + RecordView(Box<crate::tools_ozone::moderation::RecordView<'a>>), 867 + #[serde(rename = "tools.ozone.moderation.defs#recordViewNotFound")] 868 + RecordViewNotFound(Box<crate::tools_ozone::moderation::RecordViewNotFound<'a>>), 869 + } 870 + 723 871 ///Moderation tool information for tracing the source of the action 724 872 #[jacquard_derive::lexicon] 725 873 #[derive( ··· 1375 1523 pub created_at: jacquard_common::types::string::Datetime, 1376 1524 #[serde(skip_serializing_if = "std::option::Option::is_none")] 1377 1525 #[serde(borrow)] 1378 - pub hosting: std::option::Option<SubjectStatusViewRecordHosting<'a>>, 1526 + pub hosting: std::option::Option<SubjectStatusViewHosting<'a>>, 1379 1527 pub id: i64, 1380 1528 ///Timestamp referencing when the author of the subject appealed a moderation action 1381 1529 #[serde(skip_serializing_if = "std::option::Option::is_none")] ··· 1405 1553 #[serde(borrow)] 1406 1554 pub review_state: crate::tools_ozone::moderation::SubjectReviewState<'a>, 1407 1555 #[serde(borrow)] 1408 - pub subject: SubjectStatusViewRecordSubject<'a>, 1556 + pub subject: SubjectStatusViewSubject<'a>, 1409 1557 #[serde(skip_serializing_if = "std::option::Option::is_none")] 1410 1558 #[serde(borrow)] 1411 1559 pub subject_blob_cids: std::option::Option< ··· 1437 1585 )] 1438 1586 #[serde(tag = "$type")] 1439 1587 #[serde(bound(deserialize = "'de: 'a"))] 1440 - pub enum SubjectStatusViewRecordHosting<'a> {} 1588 + pub enum SubjectStatusViewHosting<'a> { 1589 + #[serde(rename = "tools.ozone.moderation.defs#accountHosting")] 1590 + AccountHosting(Box<crate::tools_ozone::moderation::AccountHosting<'a>>), 1591 + #[serde(rename = "tools.ozone.moderation.defs#recordHosting")] 1592 + RecordHosting(Box<crate::tools_ozone::moderation::RecordHosting<'a>>), 1593 + } 1594 + 1441 1595 #[jacquard_derive::open_union] 1442 1596 #[derive( 1443 1597 serde::Serialize, ··· 1450 1604 )] 1451 1605 #[serde(tag = "$type")] 1452 1606 #[serde(bound(deserialize = "'de: 'a"))] 1453 - pub enum SubjectStatusViewRecordSubject<'a> { 1607 + pub enum SubjectStatusViewSubject<'a> { 1454 1608 #[serde(rename = "com.atproto.admin.defs#repoRef")] 1455 - DefsRepoRef(Box<crate::com_atproto::admin::RepoRef<'a>>), 1609 + RepoRef(Box<crate::com_atproto::admin::RepoRef<'a>>), 1456 1610 #[serde(rename = "com.atproto.repo.strongRef")] 1457 1611 StrongRef(Box<crate::com_atproto::repo::strong_ref::StrongRef<'a>>), 1458 1612 #[serde(rename = "chat.bsky.convo.defs#messageRef")] 1459 - DefsMessageRef(Box<crate::chat_bsky::convo::MessageRef<'a>>), 1613 + MessageRef(Box<crate::chat_bsky::convo::MessageRef<'a>>), 1460 1614 } 1461 1615 1462 1616 ///Detailed view of a subject. For record subjects, the author's repo and profile will be returned. ··· 1474 1628 pub struct SubjectView<'a> { 1475 1629 #[serde(skip_serializing_if = "std::option::Option::is_none")] 1476 1630 #[serde(borrow)] 1477 - pub profile: std::option::Option<SubjectViewRecordProfile<'a>>, 1631 + pub profile: std::option::Option<jacquard_common::types::value::Data<'a>>, 1478 1632 #[serde(skip_serializing_if = "std::option::Option::is_none")] 1479 1633 #[serde(borrow)] 1480 1634 pub record: std::option::Option< ··· 1494 1648 pub r#type: crate::com_atproto::moderation::SubjectType<'a>, 1495 1649 } 1496 1650 1497 - #[jacquard_derive::open_union] 1498 - #[derive( 1499 - serde::Serialize, 1500 - serde::Deserialize, 1501 - Debug, 1502 - Clone, 1503 - PartialEq, 1504 - Eq, 1505 - jacquard_derive::IntoStatic 1506 - )] 1507 - #[serde(tag = "$type")] 1508 - #[serde(bound(deserialize = "'de: 'a"))] 1509 - pub enum SubjectViewRecordProfile<'a> {} 1510 1651 ///Moderation event timeline event for a PLC create operation 1511 1652 #[derive( 1512 1653 serde::Serialize,
+31 -49
crates/jacquard-api/src/tools_ozone/moderation/emit_event.rs
··· 22 22 #[serde(borrow)] 23 23 pub created_by: jacquard_common::types::string::Did<'a>, 24 24 #[serde(borrow)] 25 - pub event: EmitEventRecordEvent<'a>, 25 + pub event: EmitEventEvent<'a>, 26 26 ///An optional external ID for the event, used to deduplicate events from external systems. Fails when an event of same type with the same external ID exists for the same subject. 27 27 #[serde(skip_serializing_if = "std::option::Option::is_none")] 28 28 #[serde(borrow)] ··· 32 32 #[serde(borrow)] 33 33 pub mod_tool: std::option::Option<crate::tools_ozone::moderation::ModTool<'a>>, 34 34 #[serde(borrow)] 35 - pub subject: EmitEventRecordSubject<'a>, 35 + pub subject: EmitEventSubject<'a>, 36 36 #[serde(skip_serializing_if = "std::option::Option::is_none")] 37 37 #[serde(borrow)] 38 38 pub subject_blob_cids: std::option::Option< ··· 59 59 )] 60 60 #[serde(tag = "$type")] 61 61 #[serde(bound(deserialize = "'de: 'a"))] 62 - pub enum EmitEventRecordEvent<'a> { 62 + pub enum EmitEventEvent<'a> { 63 63 #[serde(rename = "tools.ozone.moderation.defs#modEventTakedown")] 64 - DefsModEventTakedown(Box<crate::tools_ozone::moderation::ModEventTakedown<'a>>), 64 + ModEventTakedown(Box<crate::tools_ozone::moderation::ModEventTakedown<'a>>), 65 65 #[serde(rename = "tools.ozone.moderation.defs#modEventAcknowledge")] 66 - DefsModEventAcknowledge( 67 - Box<crate::tools_ozone::moderation::ModEventAcknowledge<'a>>, 68 - ), 66 + ModEventAcknowledge(Box<crate::tools_ozone::moderation::ModEventAcknowledge<'a>>), 69 67 #[serde(rename = "tools.ozone.moderation.defs#modEventEscalate")] 70 - DefsModEventEscalate(Box<crate::tools_ozone::moderation::ModEventEscalate<'a>>), 68 + ModEventEscalate(Box<crate::tools_ozone::moderation::ModEventEscalate<'a>>), 71 69 #[serde(rename = "tools.ozone.moderation.defs#modEventComment")] 72 - DefsModEventComment(Box<crate::tools_ozone::moderation::ModEventComment<'a>>), 70 + ModEventComment(Box<crate::tools_ozone::moderation::ModEventComment<'a>>), 73 71 #[serde(rename = "tools.ozone.moderation.defs#modEventLabel")] 74 - DefsModEventLabel(Box<crate::tools_ozone::moderation::ModEventLabel<'a>>), 72 + ModEventLabel(Box<crate::tools_ozone::moderation::ModEventLabel<'a>>), 75 73 #[serde(rename = "tools.ozone.moderation.defs#modEventReport")] 76 - DefsModEventReport(Box<crate::tools_ozone::moderation::ModEventReport<'a>>), 74 + ModEventReport(Box<crate::tools_ozone::moderation::ModEventReport<'a>>), 77 75 #[serde(rename = "tools.ozone.moderation.defs#modEventMute")] 78 - DefsModEventMute(Box<crate::tools_ozone::moderation::ModEventMute<'a>>), 76 + ModEventMute(Box<crate::tools_ozone::moderation::ModEventMute<'a>>), 79 77 #[serde(rename = "tools.ozone.moderation.defs#modEventUnmute")] 80 - DefsModEventUnmute(Box<crate::tools_ozone::moderation::ModEventUnmute<'a>>), 78 + ModEventUnmute(Box<crate::tools_ozone::moderation::ModEventUnmute<'a>>), 81 79 #[serde(rename = "tools.ozone.moderation.defs#modEventMuteReporter")] 82 - DefsModEventMuteReporter( 83 - Box<crate::tools_ozone::moderation::ModEventMuteReporter<'a>>, 84 - ), 80 + ModEventMuteReporter(Box<crate::tools_ozone::moderation::ModEventMuteReporter<'a>>), 85 81 #[serde(rename = "tools.ozone.moderation.defs#modEventUnmuteReporter")] 86 - DefsModEventUnmuteReporter( 82 + ModEventUnmuteReporter( 87 83 Box<crate::tools_ozone::moderation::ModEventUnmuteReporter<'a>>, 88 84 ), 89 85 #[serde(rename = "tools.ozone.moderation.defs#modEventReverseTakedown")] 90 - DefsModEventReverseTakedown( 86 + ModEventReverseTakedown( 91 87 Box<crate::tools_ozone::moderation::ModEventReverseTakedown<'a>>, 92 88 ), 93 89 #[serde(rename = "tools.ozone.moderation.defs#modEventResolveAppeal")] 94 - DefsModEventResolveAppeal( 90 + ModEventResolveAppeal( 95 91 Box<crate::tools_ozone::moderation::ModEventResolveAppeal<'a>>, 96 92 ), 97 93 #[serde(rename = "tools.ozone.moderation.defs#modEventEmail")] 98 - DefsModEventEmail(Box<crate::tools_ozone::moderation::ModEventEmail<'a>>), 94 + ModEventEmail(Box<crate::tools_ozone::moderation::ModEventEmail<'a>>), 99 95 #[serde(rename = "tools.ozone.moderation.defs#modEventDivert")] 100 - DefsModEventDivert(Box<crate::tools_ozone::moderation::ModEventDivert<'a>>), 96 + ModEventDivert(Box<crate::tools_ozone::moderation::ModEventDivert<'a>>), 101 97 #[serde(rename = "tools.ozone.moderation.defs#modEventTag")] 102 - DefsModEventTag(Box<crate::tools_ozone::moderation::ModEventTag<'a>>), 98 + ModEventTag(Box<crate::tools_ozone::moderation::ModEventTag<'a>>), 103 99 #[serde(rename = "tools.ozone.moderation.defs#accountEvent")] 104 - DefsAccountEvent(Box<crate::tools_ozone::moderation::AccountEvent<'a>>), 100 + AccountEvent(Box<crate::tools_ozone::moderation::AccountEvent<'a>>), 105 101 #[serde(rename = "tools.ozone.moderation.defs#identityEvent")] 106 - DefsIdentityEvent(Box<crate::tools_ozone::moderation::IdentityEvent<'a>>), 102 + IdentityEvent(Box<crate::tools_ozone::moderation::IdentityEvent<'a>>), 107 103 #[serde(rename = "tools.ozone.moderation.defs#recordEvent")] 108 - DefsRecordEvent(Box<crate::tools_ozone::moderation::RecordEvent<'a>>), 104 + RecordEvent(Box<crate::tools_ozone::moderation::RecordEvent<'a>>), 109 105 #[serde(rename = "tools.ozone.moderation.defs#modEventPriorityScore")] 110 - DefsModEventPriorityScore( 106 + ModEventPriorityScore( 111 107 Box<crate::tools_ozone::moderation::ModEventPriorityScore<'a>>, 112 108 ), 113 109 #[serde(rename = "tools.ozone.moderation.defs#ageAssuranceEvent")] 114 - DefsAgeAssuranceEvent(Box<crate::tools_ozone::moderation::AgeAssuranceEvent<'a>>), 110 + AgeAssuranceEvent(Box<crate::tools_ozone::moderation::AgeAssuranceEvent<'a>>), 115 111 #[serde(rename = "tools.ozone.moderation.defs#ageAssuranceOverrideEvent")] 116 - DefsAgeAssuranceOverrideEvent( 112 + AgeAssuranceOverrideEvent( 117 113 Box<crate::tools_ozone::moderation::AgeAssuranceOverrideEvent<'a>>, 118 114 ), 119 115 #[serde(rename = "tools.ozone.moderation.defs#revokeAccountCredentialsEvent")] 120 - DefsRevokeAccountCredentialsEvent( 116 + RevokeAccountCredentialsEvent( 121 117 Box<crate::tools_ozone::moderation::RevokeAccountCredentialsEvent<'a>>, 122 118 ), 123 119 #[serde(rename = "tools.ozone.moderation.defs#scheduleTakedownEvent")] 124 - DefsScheduleTakedownEvent( 120 + ScheduleTakedownEvent( 125 121 Box<crate::tools_ozone::moderation::ScheduleTakedownEvent<'a>>, 126 122 ), 127 123 #[serde(rename = "tools.ozone.moderation.defs#cancelScheduledTakedownEvent")] 128 - DefsCancelScheduledTakedownEvent( 124 + CancelScheduledTakedownEvent( 129 125 Box<crate::tools_ozone::moderation::CancelScheduledTakedownEvent<'a>>, 130 126 ), 131 127 } ··· 142 138 )] 143 139 #[serde(tag = "$type")] 144 140 #[serde(bound(deserialize = "'de: 'a"))] 145 - pub enum EmitEventRecordSubject<'a> { 141 + pub enum EmitEventSubject<'a> { 146 142 #[serde(rename = "com.atproto.admin.defs#repoRef")] 147 - DefsRepoRef(Box<crate::com_atproto::admin::RepoRef<'a>>), 143 + RepoRef(Box<crate::com_atproto::admin::RepoRef<'a>>), 148 144 #[serde(rename = "com.atproto.repo.strongRef")] 149 145 StrongRef(Box<crate::com_atproto::repo::strong_ref::StrongRef<'a>>), 150 146 } ··· 175 171 PartialEq, 176 172 Eq, 177 173 thiserror::Error, 178 - miette::Diagnostic 174 + miette::Diagnostic, 175 + jacquard_derive::IntoStatic 179 176 )] 180 177 #[serde(tag = "error", content = "message")] 181 178 #[serde(bound(deserialize = "'de: 'a"))] ··· 205 202 Ok(()) 206 203 } 207 204 Self::Unknown(err) => write!(f, "Unknown error: {:?}", err), 208 - } 209 - } 210 - } 211 - 212 - impl jacquard_common::IntoStatic for EmitEventError<'_> { 213 - type Output = EmitEventError<'static>; 214 - fn into_static(self) -> Self::Output { 215 - match self { 216 - EmitEventError::SubjectHasAction(v) => { 217 - EmitEventError::SubjectHasAction(v.into_static()) 218 - } 219 - EmitEventError::DuplicateExternalId(v) => { 220 - EmitEventError::DuplicateExternalId(v.into_static()) 221 - } 222 - EmitEventError::Unknown(v) => EmitEventError::Unknown(v.into_static()), 223 205 } 224 206 } 225 207 }
+2 -15
crates/jacquard-api/src/tools_ozone/moderation/get_account_timeline.rs
··· 47 47 PartialEq, 48 48 Eq, 49 49 thiserror::Error, 50 - miette::Diagnostic 50 + miette::Diagnostic, 51 + jacquard_derive::IntoStatic 51 52 )] 52 53 #[serde(tag = "error", content = "message")] 53 54 #[serde(bound(deserialize = "'de: 'a"))] ··· 67 68 Ok(()) 68 69 } 69 70 Self::Unknown(err) => write!(f, "Unknown error: {:?}", err), 70 - } 71 - } 72 - } 73 - 74 - impl jacquard_common::IntoStatic for GetAccountTimelineError<'_> { 75 - type Output = GetAccountTimelineError<'static>; 76 - fn into_static(self) -> Self::Output { 77 - match self { 78 - GetAccountTimelineError::RepoNotFound(v) => { 79 - GetAccountTimelineError::RepoNotFound(v.into_static()) 80 - } 81 - GetAccountTimelineError::Unknown(v) => { 82 - GetAccountTimelineError::Unknown(v.into_static()) 83 - } 84 71 } 85 72 } 86 73 }
+2 -13
crates/jacquard-api/src/tools_ozone/moderation/get_record.rs
··· 51 51 PartialEq, 52 52 Eq, 53 53 thiserror::Error, 54 - miette::Diagnostic 54 + miette::Diagnostic, 55 + jacquard_derive::IntoStatic 55 56 )] 56 57 #[serde(tag = "error", content = "message")] 57 58 #[serde(bound(deserialize = "'de: 'a"))] ··· 71 72 Ok(()) 72 73 } 73 74 Self::Unknown(err) => write!(f, "Unknown error: {:?}", err), 74 - } 75 - } 76 - } 77 - 78 - impl jacquard_common::IntoStatic for GetRecordError<'_> { 79 - type Output = GetRecordError<'static>; 80 - fn into_static(self) -> Self::Output { 81 - match self { 82 - GetRecordError::RecordNotFound(v) => { 83 - GetRecordError::RecordNotFound(v.into_static()) 84 - } 85 - GetRecordError::Unknown(v) => GetRecordError::Unknown(v.into_static()), 86 75 } 87 76 } 88 77 }
+20 -1
crates/jacquard-api/src/tools_ozone/moderation/get_records.rs
··· 35 35 #[serde(rename_all = "camelCase")] 36 36 pub struct GetRecordsOutput<'a> { 37 37 #[serde(borrow)] 38 - pub records: Vec<jacquard_common::types::value::Data<'a>>, 38 + pub records: Vec<GetRecordsOutputRecordsItem<'a>>, 39 + } 40 + 41 + #[jacquard_derive::open_union] 42 + #[derive( 43 + serde::Serialize, 44 + serde::Deserialize, 45 + Debug, 46 + Clone, 47 + PartialEq, 48 + Eq, 49 + jacquard_derive::IntoStatic 50 + )] 51 + #[serde(tag = "$type")] 52 + #[serde(bound(deserialize = "'de: 'a"))] 53 + pub enum GetRecordsOutputRecordsItem<'a> { 54 + #[serde(rename = "tools.ozone.moderation.defs#recordViewDetail")] 55 + RecordViewDetail(Box<crate::tools_ozone::moderation::RecordViewDetail<'a>>), 56 + #[serde(rename = "tools.ozone.moderation.defs#recordViewNotFound")] 57 + RecordViewNotFound(Box<crate::tools_ozone::moderation::RecordViewNotFound<'a>>), 39 58 } 40 59 41 60 ///Response type for
+2 -11
crates/jacquard-api/src/tools_ozone/moderation/get_repo.rs
··· 48 48 PartialEq, 49 49 Eq, 50 50 thiserror::Error, 51 - miette::Diagnostic 51 + miette::Diagnostic, 52 + jacquard_derive::IntoStatic 52 53 )] 53 54 #[serde(tag = "error", content = "message")] 54 55 #[serde(bound(deserialize = "'de: 'a"))] ··· 68 69 Ok(()) 69 70 } 70 71 Self::Unknown(err) => write!(f, "Unknown error: {:?}", err), 71 - } 72 - } 73 - } 74 - 75 - impl jacquard_common::IntoStatic for GetRepoError<'_> { 76 - type Output = GetRepoError<'static>; 77 - fn into_static(self) -> Self::Output { 78 - match self { 79 - GetRepoError::RepoNotFound(v) => GetRepoError::RepoNotFound(v.into_static()), 80 - GetRepoError::Unknown(v) => GetRepoError::Unknown(v.into_static()), 81 72 } 82 73 } 83 74 }
+20 -1
crates/jacquard-api/src/tools_ozone/moderation/get_repos.rs
··· 35 35 #[serde(rename_all = "camelCase")] 36 36 pub struct GetReposOutput<'a> { 37 37 #[serde(borrow)] 38 - pub repos: Vec<jacquard_common::types::value::Data<'a>>, 38 + pub repos: Vec<GetReposOutputReposItem<'a>>, 39 + } 40 + 41 + #[jacquard_derive::open_union] 42 + #[derive( 43 + serde::Serialize, 44 + serde::Deserialize, 45 + Debug, 46 + Clone, 47 + PartialEq, 48 + Eq, 49 + jacquard_derive::IntoStatic 50 + )] 51 + #[serde(tag = "$type")] 52 + #[serde(bound(deserialize = "'de: 'a"))] 53 + pub enum GetReposOutputReposItem<'a> { 54 + #[serde(rename = "tools.ozone.moderation.defs#repoViewDetail")] 55 + RepoViewDetail(Box<crate::tools_ozone::moderation::RepoViewDetail<'a>>), 56 + #[serde(rename = "tools.ozone.moderation.defs#repoViewNotFound")] 57 + RepoViewNotFound(Box<crate::tools_ozone::moderation::RepoViewNotFound<'a>>), 39 58 } 40 59 41 60 ///Response type for
+1 -14
crates/jacquard-api/src/tools_ozone/moderation/schedule_action.rs
··· 41 41 #[builder(start_fn = new)] 42 42 pub struct ScheduleAction<'a> { 43 43 #[serde(borrow)] 44 - pub action: ScheduleActionRecordAction<'a>, 44 + pub action: jacquard_common::types::value::Data<'a>, 45 45 #[serde(borrow)] 46 46 pub created_by: jacquard_common::types::string::Did<'a>, 47 47 ///This will be propagated to the moderation event when it is applied ··· 62 62 >, 63 63 } 64 64 65 - #[jacquard_derive::open_union] 66 - #[derive( 67 - serde::Serialize, 68 - serde::Deserialize, 69 - Debug, 70 - Clone, 71 - PartialEq, 72 - Eq, 73 - jacquard_derive::IntoStatic 74 - )] 75 - #[serde(tag = "$type")] 76 - #[serde(bound(deserialize = "'de: 'a"))] 77 - pub enum ScheduleActionRecordAction<'a> {} 78 65 #[jacquard_derive::lexicon] 79 66 #[derive( 80 67 serde::Serialize,
+2 -14
crates/jacquard-api/src/tools_ozone/safelink/add_rule.rs
··· 73 73 PartialEq, 74 74 Eq, 75 75 thiserror::Error, 76 - miette::Diagnostic 76 + miette::Diagnostic, 77 + jacquard_derive::IntoStatic 77 78 )] 78 79 #[serde(tag = "error", content = "message")] 79 80 #[serde(bound(deserialize = "'de: 'a"))] ··· 104 105 Ok(()) 105 106 } 106 107 Self::Unknown(err) => write!(f, "Unknown error: {:?}", err), 107 - } 108 - } 109 - } 110 - 111 - impl jacquard_common::IntoStatic for AddRuleError<'_> { 112 - type Output = AddRuleError<'static>; 113 - fn into_static(self) -> Self::Output { 114 - match self { 115 - AddRuleError::InvalidUrl(v) => AddRuleError::InvalidUrl(v.into_static()), 116 - AddRuleError::RuleAlreadyExists(v) => { 117 - AddRuleError::RuleAlreadyExists(v.into_static()) 118 - } 119 - AddRuleError::Unknown(v) => AddRuleError::Unknown(v.into_static()), 120 108 } 121 109 } 122 110 }
+2 -13
crates/jacquard-api/src/tools_ozone/safelink/remove_rule.rs
··· 69 69 PartialEq, 70 70 Eq, 71 71 thiserror::Error, 72 - miette::Diagnostic 72 + miette::Diagnostic, 73 + jacquard_derive::IntoStatic 73 74 )] 74 75 #[serde(tag = "error", content = "message")] 75 76 #[serde(bound(deserialize = "'de: 'a"))] ··· 90 91 Ok(()) 91 92 } 92 93 Self::Unknown(err) => write!(f, "Unknown error: {:?}", err), 93 - } 94 - } 95 - } 96 - 97 - impl jacquard_common::IntoStatic for RemoveRuleError<'_> { 98 - type Output = RemoveRuleError<'static>; 99 - fn into_static(self) -> Self::Output { 100 - match self { 101 - RemoveRuleError::RuleNotFound(v) => { 102 - RemoveRuleError::RuleNotFound(v.into_static()) 103 - } 104 - RemoveRuleError::Unknown(v) => RemoveRuleError::Unknown(v.into_static()), 105 94 } 106 95 } 107 96 }
+2 -13
crates/jacquard-api/src/tools_ozone/safelink/update_rule.rs
··· 73 73 PartialEq, 74 74 Eq, 75 75 thiserror::Error, 76 - miette::Diagnostic 76 + miette::Diagnostic, 77 + jacquard_derive::IntoStatic 77 78 )] 78 79 #[serde(tag = "error", content = "message")] 79 80 #[serde(bound(deserialize = "'de: 'a"))] ··· 94 95 Ok(()) 95 96 } 96 97 Self::Unknown(err) => write!(f, "Unknown error: {:?}", err), 97 - } 98 - } 99 - } 100 - 101 - impl jacquard_common::IntoStatic for UpdateRuleError<'_> { 102 - type Output = UpdateRuleError<'static>; 103 - fn into_static(self) -> Self::Output { 104 - match self { 105 - UpdateRuleError::RuleNotFound(v) => { 106 - UpdateRuleError::RuleNotFound(v.into_static()) 107 - } 108 - UpdateRuleError::Unknown(v) => UpdateRuleError::Unknown(v.into_static()), 109 98 } 110 99 } 111 100 }
+2 -13
crates/jacquard-api/src/tools_ozone/set/delete_set.rs
··· 53 53 PartialEq, 54 54 Eq, 55 55 thiserror::Error, 56 - miette::Diagnostic 56 + miette::Diagnostic, 57 + jacquard_derive::IntoStatic 57 58 )] 58 59 #[serde(tag = "error", content = "message")] 59 60 #[serde(bound(deserialize = "'de: 'a"))] ··· 74 75 Ok(()) 75 76 } 76 77 Self::Unknown(err) => write!(f, "Unknown error: {:?}", err), 77 - } 78 - } 79 - } 80 - 81 - impl jacquard_common::IntoStatic for DeleteSetError<'_> { 82 - type Output = DeleteSetError<'static>; 83 - fn into_static(self) -> Self::Output { 84 - match self { 85 - DeleteSetError::SetNotFound(v) => { 86 - DeleteSetError::SetNotFound(v.into_static()) 87 - } 88 - DeleteSetError::Unknown(v) => DeleteSetError::Unknown(v.into_static()), 89 78 } 90 79 } 91 80 }
+2 -13
crates/jacquard-api/src/tools_ozone/set/delete_values.rs
··· 44 44 PartialEq, 45 45 Eq, 46 46 thiserror::Error, 47 - miette::Diagnostic 47 + miette::Diagnostic, 48 + jacquard_derive::IntoStatic 48 49 )] 49 50 #[serde(tag = "error", content = "message")] 50 51 #[serde(bound(deserialize = "'de: 'a"))] ··· 65 66 Ok(()) 66 67 } 67 68 Self::Unknown(err) => write!(f, "Unknown error: {:?}", err), 68 - } 69 - } 70 - } 71 - 72 - impl jacquard_common::IntoStatic for DeleteValuesError<'_> { 73 - type Output = DeleteValuesError<'static>; 74 - fn into_static(self) -> Self::Output { 75 - match self { 76 - DeleteValuesError::SetNotFound(v) => { 77 - DeleteValuesError::SetNotFound(v.into_static()) 78 - } 79 - DeleteValuesError::Unknown(v) => DeleteValuesError::Unknown(v.into_static()), 80 69 } 81 70 } 82 71 }
+2 -13
crates/jacquard-api/src/tools_ozone/set/get_values.rs
··· 60 60 PartialEq, 61 61 Eq, 62 62 thiserror::Error, 63 - miette::Diagnostic 63 + miette::Diagnostic, 64 + jacquard_derive::IntoStatic 64 65 )] 65 66 #[serde(tag = "error", content = "message")] 66 67 #[serde(bound(deserialize = "'de: 'a"))] ··· 81 82 Ok(()) 82 83 } 83 84 Self::Unknown(err) => write!(f, "Unknown error: {:?}", err), 84 - } 85 - } 86 - } 87 - 88 - impl jacquard_common::IntoStatic for GetValuesError<'_> { 89 - type Output = GetValuesError<'static>; 90 - fn into_static(self) -> Self::Output { 91 - match self { 92 - GetValuesError::SetNotFound(v) => { 93 - GetValuesError::SetNotFound(v.into_static()) 94 - } 95 - GetValuesError::Unknown(v) => GetValuesError::Unknown(v.into_static()), 96 85 } 97 86 } 98 87 }
+2 -13
crates/jacquard-api/src/tools_ozone/team/add_member.rs
··· 59 59 PartialEq, 60 60 Eq, 61 61 thiserror::Error, 62 - miette::Diagnostic 62 + miette::Diagnostic, 63 + jacquard_derive::IntoStatic 63 64 )] 64 65 #[serde(tag = "error", content = "message")] 65 66 #[serde(bound(deserialize = "'de: 'a"))] ··· 80 81 Ok(()) 81 82 } 82 83 Self::Unknown(err) => write!(f, "Unknown error: {:?}", err), 83 - } 84 - } 85 - } 86 - 87 - impl jacquard_common::IntoStatic for AddMemberError<'_> { 88 - type Output = AddMemberError<'static>; 89 - fn into_static(self) -> Self::Output { 90 - match self { 91 - AddMemberError::MemberAlreadyExists(v) => { 92 - AddMemberError::MemberAlreadyExists(v.into_static()) 93 - } 94 - AddMemberError::Unknown(v) => AddMemberError::Unknown(v.into_static()), 95 84 } 96 85 } 97 86 }
+2 -16
crates/jacquard-api/src/tools_ozone/team/delete_member.rs
··· 39 39 PartialEq, 40 40 Eq, 41 41 thiserror::Error, 42 - miette::Diagnostic 42 + miette::Diagnostic, 43 + jacquard_derive::IntoStatic 43 44 )] 44 45 #[serde(tag = "error", content = "message")] 45 46 #[serde(bound(deserialize = "'de: 'a"))] ··· 70 71 Ok(()) 71 72 } 72 73 Self::Unknown(err) => write!(f, "Unknown error: {:?}", err), 73 - } 74 - } 75 - } 76 - 77 - impl jacquard_common::IntoStatic for DeleteMemberError<'_> { 78 - type Output = DeleteMemberError<'static>; 79 - fn into_static(self) -> Self::Output { 80 - match self { 81 - DeleteMemberError::MemberNotFound(v) => { 82 - DeleteMemberError::MemberNotFound(v.into_static()) 83 - } 84 - DeleteMemberError::CannotDeleteSelf(v) => { 85 - DeleteMemberError::CannotDeleteSelf(v.into_static()) 86 - } 87 - DeleteMemberError::Unknown(v) => DeleteMemberError::Unknown(v.into_static()), 88 74 } 89 75 } 90 76 }
+2 -13
crates/jacquard-api/src/tools_ozone/team/update_member.rs
··· 62 62 PartialEq, 63 63 Eq, 64 64 thiserror::Error, 65 - miette::Diagnostic 65 + miette::Diagnostic, 66 + jacquard_derive::IntoStatic 66 67 )] 67 68 #[serde(tag = "error", content = "message")] 68 69 #[serde(bound(deserialize = "'de: 'a"))] ··· 83 84 Ok(()) 84 85 } 85 86 Self::Unknown(err) => write!(f, "Unknown error: {:?}", err), 86 - } 87 - } 88 - } 89 - 90 - impl jacquard_common::IntoStatic for UpdateMemberError<'_> { 91 - type Output = UpdateMemberError<'static>; 92 - fn into_static(self) -> Self::Output { 93 - match self { 94 - UpdateMemberError::MemberNotFound(v) => { 95 - UpdateMemberError::MemberNotFound(v.into_static()) 96 - } 97 - UpdateMemberError::Unknown(v) => UpdateMemberError::Unknown(v.into_static()), 98 87 } 99 88 } 100 89 }
+10 -36
crates/jacquard-api/src/tools_ozone/verification.rs
··· 35 35 pub issuer: jacquard_common::types::string::Did<'a>, 36 36 #[serde(skip_serializing_if = "std::option::Option::is_none")] 37 37 #[serde(borrow)] 38 - pub issuer_profile: std::option::Option<VerificationViewRecordIssuerProfile<'a>>, 38 + pub issuer_profile: std::option::Option<jacquard_common::types::value::Data<'a>>, 39 39 #[serde(skip_serializing_if = "std::option::Option::is_none")] 40 40 #[serde(borrow)] 41 - pub issuer_repo: std::option::Option<VerificationViewRecordIssuerRepo<'a>>, 41 + pub issuer_repo: std::option::Option<VerificationViewIssuerRepo<'a>>, 42 42 ///Describes the reason for revocation, also indicating that the verification is no longer valid. 43 43 #[serde(skip_serializing_if = "std::option::Option::is_none")] 44 44 #[serde(borrow)] ··· 55 55 pub subject: jacquard_common::types::string::Did<'a>, 56 56 #[serde(skip_serializing_if = "std::option::Option::is_none")] 57 57 #[serde(borrow)] 58 - pub subject_profile: std::option::Option<VerificationViewRecordSubjectProfile<'a>>, 58 + pub subject_profile: std::option::Option<jacquard_common::types::value::Data<'a>>, 59 59 #[serde(skip_serializing_if = "std::option::Option::is_none")] 60 60 #[serde(borrow)] 61 - pub subject_repo: std::option::Option<VerificationViewRecordSubjectRepo<'a>>, 61 + pub subject_repo: std::option::Option<VerificationViewSubjectRepo<'a>>, 62 62 ///The AT-URI of the verification record. 63 63 #[serde(borrow)] 64 64 pub uri: jacquard_common::types::string::AtUri<'a>, ··· 76 76 )] 77 77 #[serde(tag = "$type")] 78 78 #[serde(bound(deserialize = "'de: 'a"))] 79 - pub enum VerificationViewRecordIssuerProfile<'a> {} 80 - #[jacquard_derive::open_union] 81 - #[derive( 82 - serde::Serialize, 83 - serde::Deserialize, 84 - Debug, 85 - Clone, 86 - PartialEq, 87 - Eq, 88 - jacquard_derive::IntoStatic 89 - )] 90 - #[serde(tag = "$type")] 91 - #[serde(bound(deserialize = "'de: 'a"))] 92 - pub enum VerificationViewRecordIssuerRepo<'a> { 79 + pub enum VerificationViewIssuerRepo<'a> { 93 80 #[serde(rename = "tools.ozone.moderation.defs#repoViewDetail")] 94 - DefsRepoViewDetail(Box<crate::tools_ozone::moderation::RepoViewDetail<'a>>), 81 + RepoViewDetail(Box<crate::tools_ozone::moderation::RepoViewDetail<'a>>), 95 82 #[serde(rename = "tools.ozone.moderation.defs#repoViewNotFound")] 96 - DefsRepoViewNotFound(Box<crate::tools_ozone::moderation::RepoViewNotFound<'a>>), 83 + RepoViewNotFound(Box<crate::tools_ozone::moderation::RepoViewNotFound<'a>>), 97 84 } 98 85 99 86 #[jacquard_derive::open_union] ··· 108 95 )] 109 96 #[serde(tag = "$type")] 110 97 #[serde(bound(deserialize = "'de: 'a"))] 111 - pub enum VerificationViewRecordSubjectProfile<'a> {} 112 - #[jacquard_derive::open_union] 113 - #[derive( 114 - serde::Serialize, 115 - serde::Deserialize, 116 - Debug, 117 - Clone, 118 - PartialEq, 119 - Eq, 120 - jacquard_derive::IntoStatic 121 - )] 122 - #[serde(tag = "$type")] 123 - #[serde(bound(deserialize = "'de: 'a"))] 124 - pub enum VerificationViewRecordSubjectRepo<'a> { 98 + pub enum VerificationViewSubjectRepo<'a> { 125 99 #[serde(rename = "tools.ozone.moderation.defs#repoViewDetail")] 126 - DefsRepoViewDetail(Box<crate::tools_ozone::moderation::RepoViewDetail<'a>>), 100 + RepoViewDetail(Box<crate::tools_ozone::moderation::RepoViewDetail<'a>>), 127 101 #[serde(rename = "tools.ozone.moderation.defs#repoViewNotFound")] 128 - DefsRepoViewNotFound(Box<crate::tools_ozone::moderation::RepoViewNotFound<'a>>), 102 + RepoViewNotFound(Box<crate::tools_ozone::moderation::RepoViewNotFound<'a>>), 129 103 }
+1 -18
crates/jacquard-api/src/uk_skyblur/preference.rs
··· 19 19 #[serde(rename_all = "camelCase")] 20 20 pub struct Preference<'a> { 21 21 #[serde(borrow)] 22 - pub my_page: PreferenceRecordMyPage<'a>, 23 - } 24 - 25 - #[jacquard_derive::open_union] 26 - #[derive( 27 - serde::Serialize, 28 - serde::Deserialize, 29 - Debug, 30 - Clone, 31 - PartialEq, 32 - Eq, 33 - jacquard_derive::IntoStatic 34 - )] 35 - #[serde(tag = "$type")] 36 - #[serde(bound(deserialize = "'de: 'a"))] 37 - pub enum PreferenceRecordMyPage<'a> { 38 - #[serde(rename = "uk.skyblur.preference#myPage")] 39 - PreferenceMyPage(Box<crate::uk_skyblur::preference::MyPage<'a>>), 22 + pub my_page: crate::uk_skyblur::preference::MyPage<'a>, 40 23 } 41 24 42 25 impl jacquard_common::types::collection::Collection for Preference<'_> {
+51 -1
crates/jacquard-api/src/win_tomo_x/pushat.rs
··· 1 1 // @generated by jacquard-lexicon. DO NOT EDIT. 2 2 // 3 + // Lexicon: win.tomo-x.pushat.defs 4 + // 3 5 // This file was automatically generated from Lexicon schemas. 4 6 // Any manual changes will be overwritten on the next regeneration. 5 7 6 - pub mod push_notify; 8 + pub mod allow; 9 + pub mod push_notify; 10 + 11 + pub type DeviceList<'a> = Vec<crate::win_tomo_x::pushat::DeviceListItem<'a>>; 12 + #[jacquard_derive::lexicon] 13 + #[derive( 14 + serde::Serialize, 15 + serde::Deserialize, 16 + Debug, 17 + Clone, 18 + PartialEq, 19 + Eq, 20 + jacquard_derive::IntoStatic 21 + )] 22 + #[serde(rename_all = "camelCase")] 23 + pub struct DeviceListItem<'a> { 24 + pub current: bool, 25 + pub id: jacquard_common::types::string::Tid, 26 + #[serde(borrow)] 27 + pub name: jacquard_common::CowStr<'a>, 28 + } 29 + 30 + #[jacquard_derive::lexicon] 31 + #[derive( 32 + serde::Serialize, 33 + serde::Deserialize, 34 + Debug, 35 + Clone, 36 + PartialEq, 37 + Eq, 38 + jacquard_derive::IntoStatic 39 + )] 40 + #[serde(rename_all = "camelCase")] 41 + pub struct NotifyBody<'a> { 42 + ///Body text of the notification. 43 + #[serde(borrow)] 44 + pub body: jacquard_common::CowStr<'a>, 45 + ///The URI of the icon displayed in the notification. 46 + #[serde(skip_serializing_if = "std::option::Option::is_none")] 47 + #[serde(borrow)] 48 + pub icon: std::option::Option<jacquard_common::types::string::Uri<'a>>, 49 + ///Experimental — do not use. The URI to open when the notification is clicked. 50 + #[serde(skip_serializing_if = "std::option::Option::is_none")] 51 + #[serde(borrow)] 52 + pub link: std::option::Option<jacquard_common::types::string::Uri<'a>>, 53 + ///Title text of the notification. 54 + #[serde(borrow)] 55 + pub title: jacquard_common::CowStr<'a>, 56 + }
+46 -2
crates/jacquard-api/src/win_tomo_x/pushat/push_notify.rs
··· 20 20 #[builder(start_fn = new)] 21 21 pub struct PushNotify<'a> { 22 22 #[serde(borrow)] 23 - pub body: jacquard_common::types::value::Data<'a>, 23 + pub body: crate::win_tomo_x::pushat::NotifyBody<'a>, 24 + ///The DID of the target user to whom the notification will be sent. 24 25 #[serde(borrow)] 25 26 pub target: jacquard_common::types::string::Did<'a>, 26 27 #[serde(flatten)] ··· 44 45 )] 45 46 #[serde(rename_all = "camelCase")] 46 47 pub struct PushNotifyOutput<'a> {} 48 + #[jacquard_derive::open_union] 49 + #[derive( 50 + serde::Serialize, 51 + serde::Deserialize, 52 + Debug, 53 + Clone, 54 + PartialEq, 55 + Eq, 56 + thiserror::Error, 57 + miette::Diagnostic, 58 + jacquard_derive::IntoStatic 59 + )] 60 + #[serde(tag = "error", content = "message")] 61 + #[serde(bound(deserialize = "'de: 'a"))] 62 + pub enum PushNotifyError<'a> { 63 + #[serde(rename = "ServiceNotAllowedError")] 64 + ServiceNotAllowedError(std::option::Option<String>), 65 + #[serde(rename = "DeviceNotFoundError")] 66 + DeviceNotFoundError(std::option::Option<String>), 67 + } 68 + 69 + impl std::fmt::Display for PushNotifyError<'_> { 70 + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 71 + match self { 72 + Self::ServiceNotAllowedError(msg) => { 73 + write!(f, "ServiceNotAllowedError")?; 74 + if let Some(msg) = msg { 75 + write!(f, ": {}", msg)?; 76 + } 77 + Ok(()) 78 + } 79 + Self::DeviceNotFoundError(msg) => { 80 + write!(f, "DeviceNotFoundError")?; 81 + if let Some(msg) = msg { 82 + write!(f, ": {}", msg)?; 83 + } 84 + Ok(()) 85 + } 86 + Self::Unknown(err) => write!(f, "Unknown error: {:?}", err), 87 + } 88 + } 89 + } 90 + 47 91 ///Response type for 48 92 ///win.tomo-x.pushat.pushNotify 49 93 pub struct PushNotifyResponse; ··· 51 95 const NSID: &'static str = "win.tomo-x.pushat.pushNotify"; 52 96 const ENCODING: &'static str = "application/json"; 53 97 type Output<'de> = PushNotifyOutput<'de>; 54 - type Err<'de> = jacquard_common::xrpc::GenericError<'de>; 98 + type Err<'de> = PushNotifyError<'de>; 55 99 } 56 100 57 101 impl<'de> jacquard_common::xrpc::XrpcRequest<'de> for PushNotify<'de> {
+3 -3
crates/jacquard-lexicon/Cargo.toml
··· 25 25 glob = "0.3" 26 26 heck.workspace = true 27 27 itertools.workspace = true 28 - jacquard-api = { version = "0.4", path = "../jacquard-api" } 29 - jacquard-common = { version = "0.4", path = "../jacquard-common" } 30 - jacquard-identity = { version = "0.4", path = "../jacquard-identity" } 28 + jacquard-api = { version = "0.4", git = "https://tangled.org/@nonbinary.computer/jacquard" } 29 + jacquard-common = { version = "0.4", git = "https://tangled.org/@nonbinary.computer/jacquard" } 30 + jacquard-identity = { version = "0.4", git = "https://tangled.org/@nonbinary.computer/jacquard" } 31 31 kdl = "6" 32 32 miette = { workspace = true, features = ["fancy"] } 33 33 prettyplease.workspace = true
+49 -2574
crates/jacquard-lexicon/src/codegen.rs
··· 1 1 use crate::corpus::LexiconCorpus; 2 2 use crate::error::{CodegenError, Result}; 3 - use crate::lexicon::{ 4 - LexArrayItem, LexInteger, LexObject, LexObjectProperty, LexRecord, LexString, LexStringFormat, 5 - LexUserType, LexXrpcBody, LexXrpcBodySchema, LexXrpcError, LexXrpcProcedure, LexXrpcQuery, 6 - LexXrpcSubscription, LexXrpcSubscriptionMessageSchema, 7 - }; 8 - use heck::{ToPascalCase, ToSnakeCase}; 3 + use crate::lexicon::{LexArrayItem, LexUserType}; 9 4 use proc_macro2::TokenStream; 10 5 use quote::quote; 11 6 12 - /// Convert a value string to a valid Rust variant name 13 - fn value_to_variant_name(value: &str) -> String { 14 - // Remove leading special chars and convert to pascal case 15 - let clean = value.trim_start_matches(|c: char| !c.is_alphanumeric()); 16 - let variant = clean.replace('-', "_").to_pascal_case(); 17 - 18 - // Prefix with underscore if starts with digit 19 - if variant.chars().next().map_or(false, |c| c.is_ascii_digit()) { 20 - format!("_{}", variant) 21 - } else if variant.is_empty() { 22 - "Unknown".to_string() 23 - } else { 24 - variant 25 - } 26 - } 27 - 28 - /// Sanitize a string to be safe for identifiers and filenames 29 - fn sanitize_name(s: &str) -> String { 30 - if s.is_empty() { 31 - return "unknown".to_string(); 32 - } 33 - 34 - // Replace invalid characters with underscores 35 - let mut sanitized: String = s 36 - .chars() 37 - .map(|c| { 38 - if c.is_alphanumeric() || c == '_' { 39 - c 40 - } else { 41 - '_' 42 - } 43 - }) 44 - .collect(); 45 - 46 - // Ensure it doesn't start with a digit 47 - if sanitized 48 - .chars() 49 - .next() 50 - .map_or(false, |c| c.is_ascii_digit()) 51 - { 52 - sanitized = format!("_{}", sanitized); 53 - } 54 - 55 - sanitized 56 - } 57 - 58 - /// Create an identifier, using raw identifier if necessary for keywords 59 - fn make_ident(s: &str) -> syn::Ident { 60 - if s.is_empty() { 61 - eprintln!("Warning: Empty identifier encountered, using 'unknown' as fallback"); 62 - return syn::Ident::new("unknown", proc_macro2::Span::call_site()); 63 - } 64 - 65 - let sanitized = sanitize_name(s); 66 - 67 - // Try to parse as ident, fall back to raw ident if needed 68 - syn::parse_str::<syn::Ident>(&sanitized).unwrap_or_else(|_| { 69 - eprintln!( 70 - "Warning: Invalid identifier '{}' sanitized to '{}'", 71 - s, sanitized 72 - ); 73 - syn::Ident::new_raw(&sanitized, proc_macro2::Span::call_site()) 74 - }) 75 - } 7 + mod utils; 8 + mod names; 9 + mod lifetime; 10 + mod types; 11 + mod structs; 12 + mod xrpc; 13 + mod output; 76 14 77 15 /// Code generator for lexicon types 78 16 pub struct CodeGenerator<'c> { ··· 93 31 } 94 32 } 95 33 34 + /// Generate doc comment from optional description (wrapper for utils function) 35 + fn generate_doc_comment(&self, desc: Option<&jacquard_common::CowStr>) -> TokenStream { 36 + utils::generate_doc_comment(desc) 37 + } 38 + 96 39 /// Generate code for a lexicon def 97 40 pub fn generate_def( 98 41 &self, ··· 147 90 // Top-level array becomes type alias to Vec<ItemType> 148 91 let type_name = self.def_to_type_name(nsid, def_name); 149 92 let ident = syn::Ident::new(&type_name, proc_macro2::Span::call_site()); 150 - let item_type = self.array_item_to_rust_type(nsid, &array.items)?; 151 93 let doc = self.generate_doc_comment(array.description.as_ref()); 152 94 let needs_lifetime = self.array_item_needs_lifetime(&array.items); 153 - if needs_lifetime { 154 - Ok(quote! { 155 - #doc 156 - pub type #ident<'a> = Vec<#item_type>; 157 - }) 95 + 96 + // Check if items are a union - if so, generate the union enum first 97 + if let LexArrayItem::Union(union) = &array.items { 98 + let union_name = format!("{}Item", type_name); 99 + let refs: Vec<_> = union.refs.iter().cloned().collect(); 100 + let union_def = self.generate_union(nsid, &union_name, &refs, None, union.closed)?; 101 + 102 + let union_ident = syn::Ident::new(&union_name, proc_macro2::Span::call_site()); 103 + if needs_lifetime { 104 + Ok(quote! { 105 + #union_def 106 + 107 + #doc 108 + pub type #ident<'a> = Vec<#union_ident<'a>>; 109 + }) 110 + } else { 111 + Ok(quote! { 112 + #union_def 113 + 114 + #doc 115 + pub type #ident = Vec<#union_ident>; 116 + }) 117 + } 158 118 } else { 159 - Ok(quote! { 160 - #doc 161 - pub type #ident = Vec<#item_type>; 162 - }) 119 + // Regular array item type 120 + let item_type = self.array_item_to_rust_type(nsid, &array.items)?; 121 + if needs_lifetime { 122 + Ok(quote! { 123 + #doc 124 + pub type #ident<'a> = Vec<#item_type>; 125 + }) 126 + } else { 127 + Ok(quote! { 128 + #doc 129 + pub type #ident = Vec<#item_type>; 130 + }) 131 + } 163 132 } 164 133 } 165 134 LexUserType::Boolean(_) ··· 200 169 LexUserType::XrpcSubscription(sub) => self.generate_subscription(nsid, def_name, sub), 201 170 } 202 171 } 203 - 204 - /// Generate a record type 205 - fn generate_record( 206 - &self, 207 - nsid: &str, 208 - def_name: &str, 209 - record: &LexRecord<'static>, 210 - ) -> Result<TokenStream> { 211 - match &record.record { 212 - crate::lexicon::LexRecordRecord::Object(obj) => { 213 - let type_name = self.def_to_type_name(nsid, def_name); 214 - let ident = syn::Ident::new(&type_name, proc_macro2::Span::call_site()); 215 - 216 - // Generate main struct fields 217 - let fields = self.generate_object_fields(nsid, &type_name, obj, false)?; 218 - let doc = self.generate_doc_comment(record.description.as_ref()); 219 - 220 - // Records always get a lifetime since they have the #[lexicon] attribute 221 - // which adds extra_data: BTreeMap<..., Data<'a>> 222 - let struct_def = quote! { 223 - #doc 224 - #[jacquard_derive::lexicon] 225 - #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq, jacquard_derive::IntoStatic)] 226 - #[serde(rename_all = "camelCase")] 227 - pub struct #ident<'a> { 228 - #fields 229 - } 230 - }; 231 - 232 - // Generate union types and nested object types for this record 233 - let mut unions = Vec::new(); 234 - for (field_name, field_type) in &obj.properties { 235 - match field_type { 236 - LexObjectProperty::Union(union) => { 237 - let union_name = 238 - format!("{}Record{}", type_name, field_name.to_pascal_case()); 239 - // Clone refs to avoid lifetime issues 240 - let refs: Vec<_> = union.refs.iter().cloned().collect(); 241 - let union_def = 242 - self.generate_union(nsid, &union_name, &refs, None, union.closed)?; 243 - unions.push(union_def); 244 - } 245 - LexObjectProperty::Object(nested_obj) => { 246 - let object_name = 247 - format!("{}Record{}", type_name, field_name.to_pascal_case()); 248 - let obj_def = self.generate_object(nsid, &object_name, nested_obj)?; 249 - unions.push(obj_def); 250 - } 251 - _ => {} 252 - } 253 - } 254 - 255 - // Generate Collection trait impl 256 - let collection_impl = quote! { 257 - impl jacquard_common::types::collection::Collection for #ident<'_> { 258 - const NSID: &'static str = #nsid; 259 - } 260 - }; 261 - 262 - // Generate IntoStatic impl 263 - // let field_names: Vec<&str> = obj.properties.keys().map(|k| k.as_str()).collect(); 264 - // let into_static_impl = 265 - // self.generate_into_static_for_struct(&type_name, &field_names, true, true); 266 - 267 - Ok(quote! { 268 - #struct_def 269 - #(#unions)* 270 - #collection_impl 271 - //#into_static_impl 272 - }) 273 - } 274 - } 275 - } 276 - 277 - /// Generate an object type 278 - fn generate_object( 279 - &self, 280 - nsid: &str, 281 - def_name: &str, 282 - obj: &LexObject<'static>, 283 - ) -> Result<TokenStream> { 284 - let type_name = self.def_to_type_name(nsid, def_name); 285 - let ident = syn::Ident::new(&type_name, proc_macro2::Span::call_site()); 286 - 287 - let fields = self.generate_object_fields(nsid, &type_name, obj, false)?; 288 - let doc = self.generate_doc_comment(obj.description.as_ref()); 289 - 290 - // Objects always get a lifetime since they have the #[lexicon] attribute 291 - // which adds extra_data: BTreeMap<..., Data<'a>> 292 - let struct_def = quote! { 293 - #doc 294 - #[jacquard_derive::lexicon] 295 - #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq, jacquard_derive::IntoStatic)] 296 - #[serde(rename_all = "camelCase")] 297 - pub struct #ident<'a> { 298 - #fields 299 - } 300 - }; 301 - 302 - // Generate union types and nested object types for this object 303 - let mut unions = Vec::new(); 304 - for (field_name, field_type) in &obj.properties { 305 - match field_type { 306 - LexObjectProperty::Union(union) => { 307 - let union_name = format!("{}Record{}", type_name, field_name.to_pascal_case()); 308 - let refs: Vec<_> = union.refs.iter().cloned().collect(); 309 - let union_def = 310 - self.generate_union(nsid, &union_name, &refs, None, union.closed)?; 311 - unions.push(union_def); 312 - } 313 - LexObjectProperty::Object(nested_obj) => { 314 - let object_name = format!("{}Record{}", type_name, field_name.to_pascal_case()); 315 - let obj_def = self.generate_object(nsid, &object_name, nested_obj)?; 316 - unions.push(obj_def); 317 - } 318 - _ => {} 319 - } 320 - } 321 - 322 - // Generate IntoStatic impl 323 - // let field_names: Vec<&str> = obj.properties.keys().map(|k| k.as_str()).collect(); 324 - // let into_static_impl = 325 - // self.generate_into_static_for_struct(&type_name, &field_names, true, true); 326 - 327 - Ok(quote! { 328 - #struct_def 329 - #(#unions)* 330 - //#into_static_impl 331 - }) 332 - } 333 - 334 - /// Generate fields for an object 335 - fn generate_object_fields( 336 - &self, 337 - nsid: &str, 338 - parent_type_name: &str, 339 - obj: &LexObject<'static>, 340 - is_builder: bool, 341 - ) -> Result<TokenStream> { 342 - let required = obj.required.as_ref().map(|r| r.as_slice()).unwrap_or(&[]); 343 - 344 - let mut fields = Vec::new(); 345 - for (field_name, field_type) in &obj.properties { 346 - let is_required = required.contains(field_name); 347 - let field_tokens = self.generate_field( 348 - nsid, 349 - parent_type_name, 350 - field_name, 351 - field_type, 352 - is_required, 353 - is_builder, 354 - )?; 355 - fields.push(field_tokens); 356 - } 357 - 358 - Ok(quote! { #(#fields)* }) 359 - } 360 - 361 - /// Generate a single field 362 - fn generate_field( 363 - &self, 364 - nsid: &str, 365 - parent_type_name: &str, 366 - field_name: &str, 367 - field_type: &LexObjectProperty<'static>, 368 - is_required: bool, 369 - is_builder: bool, 370 - ) -> Result<TokenStream> { 371 - if field_name.is_empty() { 372 - eprintln!( 373 - "Warning: Empty field name in lexicon '{}' type '{}', using 'unknown' as fallback", 374 - nsid, parent_type_name 375 - ); 376 - } 377 - let field_ident = make_ident(&field_name.to_snake_case()); 378 - 379 - let rust_type = 380 - self.property_to_rust_type(nsid, parent_type_name, field_name, field_type)?; 381 - let needs_lifetime = self.property_needs_lifetime(field_type); 382 - 383 - // Check if this is a CowStr field for builder(into) attribute 384 - let is_cowstr = matches!(field_type, LexObjectProperty::String(s) if s.format.is_none()); 385 - 386 - let rust_type = if is_required { 387 - rust_type 388 - } else { 389 - quote! { std::option::Option<#rust_type> } 390 - }; 391 - 392 - // Extract description from field type 393 - let description = match field_type { 394 - LexObjectProperty::Ref(r) => r.description.as_ref(), 395 - LexObjectProperty::Union(u) => u.description.as_ref(), 396 - LexObjectProperty::Bytes(b) => b.description.as_ref(), 397 - LexObjectProperty::CidLink(c) => c.description.as_ref(), 398 - LexObjectProperty::Array(a) => a.description.as_ref(), 399 - LexObjectProperty::Blob(b) => b.description.as_ref(), 400 - LexObjectProperty::Object(o) => o.description.as_ref(), 401 - LexObjectProperty::Boolean(b) => b.description.as_ref(), 402 - LexObjectProperty::Integer(i) => i.description.as_ref(), 403 - LexObjectProperty::String(s) => s.description.as_ref(), 404 - LexObjectProperty::Unknown(u) => u.description.as_ref(), 405 - }; 406 - let doc = self.generate_doc_comment(description); 407 - 408 - let mut attrs = Vec::new(); 409 - 410 - if !is_required { 411 - attrs.push(quote! { #[serde(skip_serializing_if = "std::option::Option::is_none")] }); 412 - } 413 - 414 - // Add serde(borrow) to all fields with lifetimes 415 - if needs_lifetime { 416 - attrs.push(quote! { #[serde(borrow)] }); 417 - } 418 - 419 - // Add builder(into) for CowStr fields to allow String, &str, etc., but only for builder structs 420 - if is_builder && is_cowstr { 421 - attrs.push(quote! { #[builder(into)] }); 422 - } 423 - 424 - Ok(quote! { 425 - #doc 426 - #(#attrs)* 427 - pub #field_ident: #rust_type, 428 - }) 429 - } 430 - 431 - /// Check if a property type needs a lifetime parameter 432 - fn property_needs_lifetime(&self, prop: &LexObjectProperty<'static>) -> bool { 433 - match prop { 434 - LexObjectProperty::Boolean(_) | LexObjectProperty::Integer(_) => false, 435 - LexObjectProperty::String(s) => self.string_needs_lifetime(s), 436 - LexObjectProperty::Bytes(_) => false, // Bytes is owned 437 - LexObjectProperty::CidLink(_) 438 - | LexObjectProperty::Blob(_) 439 - | LexObjectProperty::Unknown(_) => true, 440 - LexObjectProperty::Array(array) => self.array_item_needs_lifetime(&array.items), 441 - LexObjectProperty::Object(_) => true, // Nested objects have lifetimes 442 - LexObjectProperty::Ref(ref_type) => { 443 - // Check if the ref target actually needs a lifetime 444 - self.ref_needs_lifetime(&ref_type.r#ref) 445 - } 446 - LexObjectProperty::Union(_) => true, // Unions generally have lifetimes 447 - } 448 - } 449 - 450 - /// Check if an array item type needs a lifetime parameter 451 - fn array_item_needs_lifetime(&self, item: &LexArrayItem) -> bool { 452 - match item { 453 - LexArrayItem::Boolean(_) | LexArrayItem::Integer(_) => false, 454 - LexArrayItem::String(s) => self.string_needs_lifetime(s), 455 - LexArrayItem::Bytes(_) => false, 456 - LexArrayItem::CidLink(_) | LexArrayItem::Blob(_) | LexArrayItem::Unknown(_) => true, 457 - LexArrayItem::Object(_) => true, // Nested objects have lifetimes 458 - LexArrayItem::Ref(ref_type) => self.ref_needs_lifetime(&ref_type.r#ref), 459 - LexArrayItem::Union(_) => true, 460 - } 461 - } 462 - 463 - /// Check if a string type needs a lifetime parameter 464 - fn string_needs_lifetime(&self, s: &LexString) -> bool { 465 - match s.format { 466 - Some(LexStringFormat::Datetime) 467 - | Some(LexStringFormat::Language) 468 - | Some(LexStringFormat::Tid) => false, 469 - _ => true, // Most string types borrow 470 - } 471 - } 472 - 473 - /// Check if a ref needs a lifetime parameter 474 - fn ref_needs_lifetime(&self, ref_str: &str) -> bool { 475 - // Try to resolve the ref 476 - if let Some((_doc, def)) = self.corpus.resolve_ref(ref_str) { 477 - self.def_needs_lifetime(def) 478 - } else { 479 - // If we can't resolve it, assume it needs a lifetime (safe default) 480 - true 481 - } 482 - } 483 - 484 - /// Check if a lexicon def needs a lifetime parameter 485 - fn def_needs_lifetime(&self, def: &LexUserType<'static>) -> bool { 486 - match def { 487 - // Records and Objects always have lifetimes now since they get #[lexicon] attribute 488 - LexUserType::Record(_) => true, 489 - LexUserType::Object(_) => true, 490 - LexUserType::Token(_) => false, 491 - LexUserType::String(s) => { 492 - // Check if it's a known values enum or a regular string 493 - if s.known_values.is_some() { 494 - // Known values enums have Other(CowStr<'a>) variant 495 - true 496 - } else { 497 - self.string_needs_lifetime(s) 498 - } 499 - } 500 - LexUserType::Integer(_) => false, 501 - LexUserType::Boolean(_) => false, 502 - LexUserType::Bytes(_) => false, 503 - LexUserType::CidLink(_) | LexUserType::Blob(_) | LexUserType::Unknown(_) => true, 504 - LexUserType::Array(array) => self.array_item_needs_lifetime(&array.items), 505 - LexUserType::XrpcQuery(_) 506 - | LexUserType::XrpcProcedure(_) 507 - | LexUserType::XrpcSubscription(_) => { 508 - // XRPC types generate multiple structs, not a single type we can reference 509 - // Shouldn't be referenced directly 510 - true 511 - } 512 - } 513 - } 514 - 515 - /// Check if xrpc params need a lifetime parameter 516 - fn params_need_lifetime(&self, params: &crate::lexicon::LexXrpcParameters<'static>) -> bool { 517 - params.properties.values().any(|prop| { 518 - use crate::lexicon::LexXrpcParametersProperty; 519 - match prop { 520 - LexXrpcParametersProperty::Boolean(_) | LexXrpcParametersProperty::Integer(_) => { 521 - false 522 - } 523 - LexXrpcParametersProperty::String(s) => self.string_needs_lifetime(s), 524 - LexXrpcParametersProperty::Unknown(_) => true, 525 - LexXrpcParametersProperty::Array(arr) => { 526 - use crate::lexicon::LexPrimitiveArrayItem; 527 - match &arr.items { 528 - LexPrimitiveArrayItem::Boolean(_) | LexPrimitiveArrayItem::Integer(_) => { 529 - false 530 - } 531 - LexPrimitiveArrayItem::String(s) => self.string_needs_lifetime(s), 532 - LexPrimitiveArrayItem::Unknown(_) => true, 533 - } 534 - } 535 - } 536 - }) 537 - } 538 - 539 - /// Convert a property type to Rust type 540 - fn property_to_rust_type( 541 - &self, 542 - nsid: &str, 543 - parent_type_name: &str, 544 - field_name: &str, 545 - prop: &LexObjectProperty<'static>, 546 - ) -> Result<TokenStream> { 547 - match prop { 548 - LexObjectProperty::Boolean(_) => Ok(quote! { bool }), 549 - LexObjectProperty::Integer(_) => Ok(quote! { i64 }), 550 - LexObjectProperty::String(s) => Ok(self.string_to_rust_type(s)), 551 - LexObjectProperty::Bytes(_) => Ok(quote! { bytes::Bytes }), 552 - LexObjectProperty::CidLink(_) => { 553 - Ok(quote! { jacquard_common::types::cid::CidLink<'a> }) 554 - } 555 - LexObjectProperty::Blob(_) => Ok(quote! { jacquard_common::types::blob::Blob<'a> }), 556 - LexObjectProperty::Unknown(_) => Ok(quote! { jacquard_common::types::value::Data<'a> }), 557 - LexObjectProperty::Array(array) => { 558 - let item_type = self.array_item_to_rust_type(nsid, &array.items)?; 559 - Ok(quote! { Vec<#item_type> }) 560 - } 561 - LexObjectProperty::Object(_object) => { 562 - // Generate unique nested object type name: StatusView + metadata -> StatusViewRecordMetadata 563 - let object_name = 564 - format!("{}Record{}", parent_type_name, field_name.to_pascal_case()); 565 - let object_ident = syn::Ident::new(&object_name, proc_macro2::Span::call_site()); 566 - Ok(quote! { #object_ident<'a> }) 567 - } 568 - LexObjectProperty::Ref(ref_type) => { 569 - // Handle local refs (starting with #) by prepending the current NSID 570 - let ref_str = if ref_type.r#ref.starts_with('#') { 571 - format!("{}{}", nsid, ref_type.r#ref) 572 - } else { 573 - ref_type.r#ref.to_string() 574 - }; 575 - self.ref_to_rust_type(&ref_str) 576 - } 577 - LexObjectProperty::Union(_union) => { 578 - // Generate unique union type name: StatusView + embed -> StatusViewRecordEmbed 579 - let union_name = 580 - format!("{}Record{}", parent_type_name, field_name.to_pascal_case()); 581 - let union_ident = syn::Ident::new(&union_name, proc_macro2::Span::call_site()); 582 - Ok(quote! { #union_ident<'a> }) 583 - } 584 - } 585 - } 586 - 587 - /// Convert array item to Rust type 588 - fn array_item_to_rust_type(&self, nsid: &str, item: &LexArrayItem) -> Result<TokenStream> { 589 - match item { 590 - LexArrayItem::Boolean(_) => Ok(quote! { bool }), 591 - LexArrayItem::Integer(_) => Ok(quote! { i64 }), 592 - LexArrayItem::String(s) => Ok(self.string_to_rust_type(s)), 593 - LexArrayItem::Bytes(_) => Ok(quote! { bytes::Bytes }), 594 - LexArrayItem::CidLink(_) => Ok(quote! { jacquard_common::types::cid::CidLink<'a> }), 595 - LexArrayItem::Blob(_) => Ok(quote! { jacquard_common::types::blob::Blob<'a> }), 596 - LexArrayItem::Unknown(_) => Ok(quote! { jacquard_common::types::value::Data<'a> }), 597 - LexArrayItem::Object(_) => { 598 - // For inline objects in arrays, use Data since we can't generate a unique type name 599 - Ok(quote! { jacquard_common::types::value::Data<'a> }) 600 - } 601 - LexArrayItem::Ref(ref_type) => { 602 - // Handle local refs (starting with #) by prepending the current NSID 603 - let ref_str = if ref_type.r#ref.starts_with('#') { 604 - format!("{}{}", nsid, ref_type.r#ref) 605 - } else { 606 - ref_type.r#ref.to_string() 607 - }; 608 - self.ref_to_rust_type(&ref_str) 609 - } 610 - LexArrayItem::Union(_) => { 611 - // For now, use Data 612 - Ok(quote! { jacquard_common::types::value::Data<'a> }) 613 - } 614 - } 615 - } 616 - 617 - /// Convert string type to Rust type 618 - fn string_to_rust_type(&self, s: &LexString) -> TokenStream { 619 - match s.format { 620 - Some(LexStringFormat::Datetime) => { 621 - quote! { jacquard_common::types::string::Datetime } 622 - } 623 - Some(LexStringFormat::Did) => quote! { jacquard_common::types::string::Did<'a> }, 624 - Some(LexStringFormat::Handle) => quote! { jacquard_common::types::string::Handle<'a> }, 625 - Some(LexStringFormat::AtIdentifier) => { 626 - quote! { jacquard_common::types::ident::AtIdentifier<'a> } 627 - } 628 - Some(LexStringFormat::Nsid) => quote! { jacquard_common::types::string::Nsid<'a> }, 629 - Some(LexStringFormat::AtUri) => quote! { jacquard_common::types::string::AtUri<'a> }, 630 - Some(LexStringFormat::Uri) => quote! { jacquard_common::types::string::Uri<'a> }, 631 - Some(LexStringFormat::Cid) => quote! { jacquard_common::types::string::Cid<'a> }, 632 - Some(LexStringFormat::Language) => { 633 - quote! { jacquard_common::types::string::Language } 634 - } 635 - Some(LexStringFormat::Tid) => quote! { jacquard_common::types::string::Tid }, 636 - Some(LexStringFormat::RecordKey) => { 637 - quote! { jacquard_common::types::string::RecordKey<jacquard_common::types::string::Rkey<'a>> } 638 - } 639 - _ => quote! { jacquard_common::CowStr<'a> }, 640 - } 641 - } 642 - 643 - /// Convert ref to Rust type path 644 - fn ref_to_rust_type(&self, ref_str: &str) -> Result<TokenStream> { 645 - // Parse NSID and fragment 646 - let (ref_nsid, ref_def) = if let Some((nsid, fragment)) = ref_str.split_once('#') { 647 - (nsid, fragment) 648 - } else { 649 - (ref_str, "main") 650 - }; 651 - 652 - // Check if ref exists 653 - if !self.corpus.ref_exists(ref_str) { 654 - // Fallback to Data 655 - return Ok(quote! { jacquard_common::types::value::Data<'a> }); 656 - } 657 - 658 - // Convert NSID to module path 659 - // com.atproto.repo.strongRef -> com_atproto::repo::strong_ref::StrongRef 660 - // app.bsky.richtext.facet -> app_bsky::richtext::facet::Facet 661 - // app.bsky.actor.defs#nux -> app_bsky::actor::Nux (defs go in parent module) 662 - let parts: Vec<&str> = ref_nsid.split('.').collect(); 663 - let last_segment = parts.last().unwrap(); 664 - 665 - let type_name = self.def_to_type_name(ref_nsid, ref_def); 666 - 667 - let path_str = if *last_segment == "defs" && parts.len() >= 3 { 668 - // defs types go in parent module 669 - let first_two = format!("{}_{}", sanitize_name(parts[0]), sanitize_name(parts[1])); 670 - if parts.len() == 3 { 671 - // com.atproto.defs -> com_atproto::TypeName 672 - format!("{}::{}::{}", self.root_module, first_two, type_name) 673 - } else { 674 - // app.bsky.actor.defs -> app_bsky::actor::TypeName 675 - let middle: Vec<_> = parts[2..parts.len() - 1] 676 - .iter() 677 - .copied() 678 - .map(|s| sanitize_name(s)) 679 - .collect(); 680 - format!( 681 - "{}::{}::{}::{}", 682 - self.root_module, 683 - first_two, 684 - middle.join("::"), 685 - type_name 686 - ) 687 - } 688 - } else { 689 - // Regular types go in their own module file 690 - let (module_path, file_module) = if parts.len() >= 3 { 691 - // Join first two segments with underscore 692 - let first_two = format!("{}_{}", sanitize_name(parts[0]), sanitize_name(parts[1])); 693 - let file_name = sanitize_name(last_segment).to_snake_case(); 694 - 695 - if parts.len() > 3 { 696 - // Middle segments form the module path 697 - let middle: Vec<_> = parts[2..parts.len() - 1] 698 - .iter() 699 - .copied() 700 - .map(|s| sanitize_name(s)) 701 - .collect(); 702 - let base_path = format!("{}::{}", first_two, middle.join("::")); 703 - (base_path, file_name) 704 - } else { 705 - // Only 3 parts: com.atproto.label -> com_atproto, file: label 706 - (first_two, file_name) 707 - } 708 - } else if parts.len() == 2 { 709 - // e.g., "com.example" -> "com_example", file: example 710 - let first = sanitize_name(parts[0]); 711 - let file_name = sanitize_name(parts[1]).to_snake_case(); 712 - (first, file_name) 713 - } else { 714 - (parts[0].to_string(), "main".to_string()) 715 - }; 716 - 717 - format!( 718 - "{}::{}::{}::{}", 719 - self.root_module, module_path, file_module, type_name 720 - ) 721 - }; 722 - 723 - let path: syn::Path = syn::parse_str(&path_str).map_err(|e| CodegenError::Other { 724 - message: format!("Failed to parse path: {} {}", path_str, e), 725 - source: None, 726 - })?; 727 - 728 - // Only add lifetime if the target type needs it 729 - if self.ref_needs_lifetime(ref_str) { 730 - Ok(quote! { #path<'a> }) 731 - } else { 732 - Ok(quote! { #path }) 733 - } 734 - } 735 - 736 - /// Generate query type 737 - fn generate_query( 738 - &self, 739 - nsid: &str, 740 - def_name: &str, 741 - query: &LexXrpcQuery<'static>, 742 - ) -> Result<TokenStream> { 743 - let type_base = self.def_to_type_name(nsid, def_name); 744 - let mut output = Vec::new(); 745 - 746 - let params_has_lifetime = query 747 - .parameters 748 - .as_ref() 749 - .map(|p| match p { 750 - crate::lexicon::LexXrpcQueryParameter::Params(params) => { 751 - self.params_need_lifetime(params) 752 - } 753 - }) 754 - .unwrap_or(false); 755 - let has_params = query.parameters.is_some(); 756 - let has_output = query.output.is_some(); 757 - let has_errors = query.errors.is_some(); 758 - 759 - if let Some(params) = &query.parameters { 760 - let params_struct = self.generate_params_struct(&type_base, params)?; 761 - output.push(params_struct); 762 - } 763 - 764 - if let Some(body) = &query.output { 765 - let output_struct = self.generate_output_struct(nsid, &type_base, body)?; 766 - output.push(output_struct); 767 - } 768 - 769 - if let Some(errors) = &query.errors { 770 - let error_enum = self.generate_error_enum(&type_base, errors)?; 771 - output.push(error_enum); 772 - } 773 - 774 - // Generate XrpcRequest impl 775 - let output_encoding = query 776 - .output 777 - .as_ref() 778 - .map(|o| o.encoding.as_ref()) 779 - .unwrap_or("application/json"); 780 - let xrpc_impl = self.generate_xrpc_request_impl( 781 - nsid, 782 - &type_base, 783 - quote! { jacquard_common::xrpc::XrpcMethod::Query }, 784 - output_encoding, 785 - has_params, 786 - params_has_lifetime, 787 - has_output, 788 - has_errors, 789 - false, // queries never have binary inputs 790 - )?; 791 - output.push(xrpc_impl); 792 - 793 - Ok(quote! { 794 - #(#output)* 795 - }) 796 - } 797 - 798 - /// Generate procedure type 799 - fn generate_procedure( 800 - &self, 801 - nsid: &str, 802 - def_name: &str, 803 - proc: &LexXrpcProcedure<'static>, 804 - ) -> Result<TokenStream> { 805 - let type_base = self.def_to_type_name(nsid, def_name); 806 - let mut output = Vec::new(); 807 - 808 - // Check if input is a binary body (no schema) 809 - let is_binary_input = proc 810 - .input 811 - .as_ref() 812 - .map(|i| i.schema.is_none()) 813 - .unwrap_or(false); 814 - 815 - // Input bodies with schemas have lifetimes (they get #[lexicon] attribute) 816 - // Binary inputs don't have lifetimes 817 - let params_has_lifetime = proc.input.is_some() && !is_binary_input; 818 - let has_input = proc.input.is_some(); 819 - let has_output = proc.output.is_some(); 820 - let has_errors = proc.errors.is_some(); 821 - 822 - if let Some(params) = &proc.parameters { 823 - let params_struct = self.generate_params_struct_proc(&type_base, params)?; 824 - output.push(params_struct); 825 - } 826 - 827 - if let Some(body) = &proc.input { 828 - let input_struct = self.generate_input_struct(nsid, &type_base, body)?; 829 - output.push(input_struct); 830 - } 831 - 832 - if let Some(body) = &proc.output { 833 - let output_struct = self.generate_output_struct(nsid, &type_base, body)?; 834 - output.push(output_struct); 835 - } 836 - 837 - if let Some(errors) = &proc.errors { 838 - let error_enum = self.generate_error_enum(&type_base, errors)?; 839 - output.push(error_enum); 840 - } 841 - 842 - // Generate XrpcRequest impl 843 - let input_encoding = proc 844 - .input 845 - .as_ref() 846 - .map(|i| i.encoding.as_ref()) 847 - .unwrap_or("application/json"); 848 - let output_encoding = proc 849 - .output 850 - .as_ref() 851 - .map(|o| o.encoding.as_ref()) 852 - .unwrap_or("application/json"); 853 - let xrpc_impl = self.generate_xrpc_request_impl( 854 - nsid, 855 - &type_base, 856 - quote! { jacquard_common::xrpc::XrpcMethod::Procedure(#input_encoding) }, 857 - output_encoding, 858 - has_input, 859 - params_has_lifetime, 860 - has_output, 861 - has_errors, 862 - is_binary_input, 863 - )?; 864 - output.push(xrpc_impl); 865 - 866 - Ok(quote! { 867 - #(#output)* 868 - }) 869 - } 870 - 871 - fn generate_subscription( 872 - &self, 873 - nsid: &str, 874 - def_name: &str, 875 - sub: &LexXrpcSubscription<'static>, 876 - ) -> Result<TokenStream> { 877 - let type_base = self.def_to_type_name(nsid, def_name); 878 - let mut output = Vec::new(); 879 - 880 - if let Some(params) = &sub.parameters { 881 - // Extract LexXrpcParameters from the enum 882 - match params { 883 - crate::lexicon::LexXrpcSubscriptionParameter::Params(params_inner) => { 884 - let params_struct = 885 - self.generate_params_struct_inner(&type_base, params_inner)?; 886 - output.push(params_struct); 887 - } 888 - } 889 - } 890 - 891 - if let Some(message) = &sub.message { 892 - if let Some(schema) = &message.schema { 893 - let message_type = self.generate_subscription_message(nsid, &type_base, schema)?; 894 - output.push(message_type); 895 - } 896 - } 897 - 898 - if let Some(errors) = &sub.errors { 899 - let error_enum = self.generate_error_enum(&type_base, errors)?; 900 - output.push(error_enum); 901 - } 902 - 903 - Ok(quote! { 904 - #(#output)* 905 - }) 906 - } 907 - 908 - fn generate_subscription_message( 909 - &self, 910 - nsid: &str, 911 - type_base: &str, 912 - schema: &LexXrpcSubscriptionMessageSchema<'static>, 913 - ) -> Result<TokenStream> { 914 - use crate::lexicon::LexXrpcSubscriptionMessageSchema; 915 - 916 - match schema { 917 - LexXrpcSubscriptionMessageSchema::Union(union) => { 918 - // Generate a union enum for the message 919 - let enum_name = format!("{}Message", type_base); 920 - let enum_ident = syn::Ident::new(&enum_name, proc_macro2::Span::call_site()); 921 - 922 - let mut variants = Vec::new(); 923 - for ref_str in &union.refs { 924 - let ref_str_s = ref_str.as_ref(); 925 - // Parse ref to get NSID and def name 926 - let (ref_nsid, ref_def) = 927 - if let Some((nsid, fragment)) = ref_str.split_once('#') { 928 - (nsid, fragment) 929 - } else { 930 - (ref_str.as_ref(), "main") 931 - }; 932 - 933 - let variant_name = if ref_def == "main" { 934 - ref_nsid.split('.').last().unwrap().to_pascal_case() 935 - } else { 936 - ref_def.to_pascal_case() 937 - }; 938 - let variant_ident = 939 - syn::Ident::new(&variant_name, proc_macro2::Span::call_site()); 940 - let type_path = self.ref_to_rust_type(ref_str)?; 941 - 942 - variants.push(quote! { 943 - #[serde(rename = #ref_str_s)] 944 - #variant_ident(Box<#type_path>) 945 - }); 946 - } 947 - 948 - let doc = self.generate_doc_comment(union.description.as_ref()); 949 - 950 - // Generate IntoStatic impl for the enum 951 - // let variant_info: Vec<(String, EnumVariantKind)> = union 952 - // .refs 953 - // .iter() 954 - // .map(|ref_str| { 955 - // let ref_def = if let Some((_, fragment)) = ref_str.split_once('#') { 956 - // fragment 957 - // } else { 958 - // "main" 959 - // }; 960 - // let variant_name = if ref_def == "main" { 961 - // ref_str.split('.').last().unwrap().to_pascal_case() 962 - // } else { 963 - // ref_def.to_pascal_case() 964 - // }; 965 - // (variant_name, EnumVariantKind::Tuple) 966 - // }) 967 - // .collect(); 968 - // let into_static_impl = self.generate_into_static_for_enum( 969 - // &enum_name, 970 - // &variant_info, 971 - // true, 972 - // true, // open union 973 - // ); 974 - 975 - Ok(quote! { 976 - #doc 977 - #[jacquard_derive::open_union] 978 - #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq, jacquard_derive::IntoStatic)] 979 - #[serde(tag = "$type")] 980 - #[serde(bound(deserialize = "'de: 'a"))] 981 - pub enum #enum_ident<'a> { 982 - #(#variants,)* 983 - } 984 - 985 - //#into_static_impl 986 - }) 987 - } 988 - LexXrpcSubscriptionMessageSchema::Object(obj) => { 989 - // Generate a struct for the message 990 - let struct_name = format!("{}Message", type_base); 991 - let struct_ident = syn::Ident::new(&struct_name, proc_macro2::Span::call_site()); 992 - 993 - let fields = self.generate_object_fields("", &struct_name, obj, false)?; 994 - let doc = self.generate_doc_comment(obj.description.as_ref()); 995 - 996 - // Subscription message structs always get a lifetime since they have the #[lexicon] attribute 997 - // which adds extra_data: BTreeMap<..., Data<'a>> 998 - let struct_def = quote! { 999 - #doc 1000 - #[jacquard_derive::lexicon] 1001 - #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq, jacquard_derive::IntoStatic)] 1002 - #[serde(rename_all = "camelCase")] 1003 - pub struct #struct_ident<'a> { 1004 - #fields 1005 - } 1006 - }; 1007 - 1008 - // Generate union types for this message 1009 - let mut unions = Vec::new(); 1010 - for (field_name, field_type) in &obj.properties { 1011 - if let LexObjectProperty::Union(union) = field_type { 1012 - let union_name = 1013 - format!("{}Record{}", struct_name, field_name.to_pascal_case()); 1014 - let refs: Vec<_> = union.refs.iter().cloned().collect(); 1015 - let union_def = 1016 - self.generate_union(nsid, &union_name, &refs, None, union.closed)?; 1017 - unions.push(union_def); 1018 - } 1019 - } 1020 - 1021 - // Generate IntoStatic impl 1022 - // let field_names: Vec<&str> = obj.properties.keys().map(|k| k.as_str()).collect(); 1023 - // let into_static_impl = 1024 - // self.generate_into_static_for_struct(&struct_name, &field_names, true, true); 1025 - 1026 - Ok(quote! { 1027 - #struct_def 1028 - #(#unions)* 1029 - //#into_static_impl 1030 - }) 1031 - } 1032 - LexXrpcSubscriptionMessageSchema::Ref(ref_type) => { 1033 - // Just a type alias to the referenced type 1034 - // Refs generally have lifetimes, so always add <'a> 1035 - let type_name = format!("{}Message", type_base); 1036 - let ident = syn::Ident::new(&type_name, proc_macro2::Span::call_site()); 1037 - let rust_type = self.ref_to_rust_type(&ref_type.r#ref)?; 1038 - let doc = self.generate_doc_comment(ref_type.description.as_ref()); 1039 - 1040 - Ok(quote! { 1041 - #doc 1042 - pub type #ident<'a> = #rust_type; 1043 - }) 1044 - } 1045 - } 1046 - } 1047 - 1048 - /// Convert def name to Rust type name 1049 - fn def_to_type_name(&self, nsid: &str, def_name: &str) -> String { 1050 - if def_name == "main" { 1051 - // Use last segment of NSID 1052 - let base_name = nsid.split('.').last().unwrap().to_pascal_case(); 1053 - 1054 - // Check if any other def would collide with this name 1055 - if let Some(doc) = self.corpus.get(nsid) { 1056 - let has_collision = doc.defs.keys().any(|other_def| { 1057 - let other_def_str: &str = other_def.as_ref(); 1058 - other_def_str != "main" && other_def_str.to_pascal_case() == base_name 1059 - }); 1060 - 1061 - if has_collision { 1062 - return format!("{}Record", base_name); 1063 - } 1064 - } 1065 - 1066 - base_name 1067 - } else { 1068 - def_name.to_pascal_case() 1069 - } 1070 - } 1071 - 1072 - /// Convert NSID to file path relative to output directory 1073 - /// 1074 - /// - `app.bsky.feed.post` → `app_bsky/feed/post.rs` 1075 - /// - `com.atproto.label.defs` → `com_atproto/label.rs` (defs go in parent) 1076 - fn nsid_to_file_path(&self, nsid: &str) -> std::path::PathBuf { 1077 - let parts: Vec<&str> = nsid.split('.').collect(); 1078 - 1079 - if parts.len() < 2 { 1080 - // Shouldn't happen with valid NSIDs, but handle gracefully 1081 - return format!("{}.rs", sanitize_name(parts[0])).into(); 1082 - } 1083 - 1084 - let last = parts.last().unwrap(); 1085 - 1086 - if *last == "defs" && parts.len() >= 3 { 1087 - // defs go in parent module: com.atproto.label.defs → com_atproto/label.rs 1088 - let first_two = format!("{}_{}", sanitize_name(parts[0]), sanitize_name(parts[1])); 1089 - if parts.len() == 3 { 1090 - // com.atproto.defs → com_atproto.rs 1091 - format!("{}.rs", first_two).into() 1092 - } else { 1093 - // com.atproto.label.defs → com_atproto/label.rs 1094 - let middle: Vec<&str> = parts[2..parts.len() - 1].iter().copied().collect(); 1095 - let mut path = std::path::PathBuf::from(first_two); 1096 - for segment in &middle[..middle.len() - 1] { 1097 - path.push(sanitize_name(segment)); 1098 - } 1099 - path.push(format!("{}.rs", sanitize_name(middle.last().unwrap()))); 1100 - path 1101 - } 1102 - } else { 1103 - // Regular path: app.bsky.feed.post → app_bsky/feed/post.rs 1104 - let first_two = format!("{}_{}", sanitize_name(parts[0]), sanitize_name(parts[1])); 1105 - let mut path = std::path::PathBuf::from(first_two); 1106 - 1107 - for segment in &parts[2..parts.len() - 1] { 1108 - path.push(sanitize_name(segment)); 1109 - } 1110 - 1111 - path.push(format!("{}.rs", sanitize_name(&last.to_snake_case()))); 1112 - path 1113 - } 1114 - } 1115 - 1116 - /// Generate all code for the corpus, organized by file 1117 - /// Returns a map of file paths to (tokens, optional NSID) 1118 - pub fn generate_all( 1119 - &self, 1120 - ) -> Result<std::collections::BTreeMap<std::path::PathBuf, (TokenStream, Option<String>)>> { 1121 - use std::collections::BTreeMap; 1122 - 1123 - let mut file_contents: BTreeMap<std::path::PathBuf, Vec<TokenStream>> = BTreeMap::new(); 1124 - let mut file_nsids: BTreeMap<std::path::PathBuf, String> = BTreeMap::new(); 1125 - 1126 - // Generate code for all lexicons 1127 - for (nsid, doc) in self.corpus.iter() { 1128 - let file_path = self.nsid_to_file_path(nsid.as_ref()); 1129 - 1130 - // Track which NSID this file is for 1131 - file_nsids.insert(file_path.clone(), nsid.to_string()); 1132 - 1133 - for (def_name, def) in &doc.defs { 1134 - let tokens = self.generate_def(nsid.as_ref(), def_name.as_ref(), def)?; 1135 - file_contents 1136 - .entry(file_path.clone()) 1137 - .or_default() 1138 - .push(tokens); 1139 - } 1140 - } 1141 - 1142 - // Combine all tokens for each file 1143 - let mut result = BTreeMap::new(); 1144 - for (path, tokens_vec) in file_contents { 1145 - let nsid = file_nsids.get(&path).cloned(); 1146 - result.insert(path, (quote! { #(#tokens_vec)* }, nsid)); 1147 - } 1148 - 1149 - Ok(result) 1150 - } 1151 - 1152 - /// Generate parent module files with pub mod declarations 1153 - pub fn generate_module_tree( 1154 - &self, 1155 - file_map: &std::collections::BTreeMap<std::path::PathBuf, (TokenStream, Option<String>)>, 1156 - defs_only: &std::collections::BTreeMap<std::path::PathBuf, (TokenStream, Option<String>)>, 1157 - ) -> std::collections::BTreeMap<std::path::PathBuf, (TokenStream, Option<String>)> { 1158 - use std::collections::{BTreeMap, BTreeSet}; 1159 - 1160 - // Track what modules each directory needs to declare 1161 - // Key: directory path, Value: set of module names (file stems) 1162 - let mut dir_modules: BTreeMap<std::path::PathBuf, BTreeSet<String>> = BTreeMap::new(); 1163 - 1164 - // Collect all parent directories that have files 1165 - let mut all_dirs: BTreeSet<std::path::PathBuf> = BTreeSet::new(); 1166 - for path in file_map.keys() { 1167 - if let Some(parent_dir) = path.parent() { 1168 - all_dirs.insert(parent_dir.to_path_buf()); 1169 - } 1170 - } 1171 - 1172 - for path in file_map.keys() { 1173 - if let Some(parent_dir) = path.parent() { 1174 - if let Some(file_stem) = path.file_stem().and_then(|s| s.to_str()) { 1175 - // Skip mod.rs and lib.rs - they're module files, not modules to declare 1176 - if file_stem == "mod" || file_stem == "lib" { 1177 - continue; 1178 - } 1179 - 1180 - // Always add the module declaration to parent 1181 - dir_modules 1182 - .entry(parent_dir.to_path_buf()) 1183 - .or_default() 1184 - .insert(file_stem.to_string()); 1185 - } 1186 - } 1187 - } 1188 - 1189 - // Generate module files 1190 - let mut result = BTreeMap::new(); 1191 - 1192 - for (dir, module_names) in dir_modules { 1193 - let mod_file_path = if dir.components().count() == 0 { 1194 - // Root directory -> lib.rs for library crates 1195 - std::path::PathBuf::from("lib.rs") 1196 - } else { 1197 - // Subdirectory: app_bsky/feed -> app_bsky/feed.rs (Rust 2018 style) 1198 - let dir_name = dir.file_name().and_then(|s| s.to_str()).unwrap_or("mod"); 1199 - let sanitized_dir_name = sanitize_name(dir_name); 1200 - let mut path = dir 1201 - .parent() 1202 - .unwrap_or_else(|| std::path::Path::new("")) 1203 - .to_path_buf(); 1204 - path.push(format!("{}.rs", sanitized_dir_name)); 1205 - path 1206 - }; 1207 - 1208 - let is_root = dir.components().count() == 0; 1209 - let mods: Vec<_> = module_names 1210 - .iter() 1211 - .map(|name| { 1212 - let ident = make_ident(name); 1213 - if is_root { 1214 - // Top-level modules get feature gates 1215 - quote! { 1216 - #[cfg(feature = #name)] 1217 - pub mod #ident; 1218 - } 1219 - } else { 1220 - quote! { pub mod #ident; } 1221 - } 1222 - }) 1223 - .collect(); 1224 - 1225 - // If this file already exists in defs_only (e.g., from defs), merge the content 1226 - let module_tokens = quote! { #(#mods)* }; 1227 - if let Some((existing_tokens, nsid)) = defs_only.get(&mod_file_path) { 1228 - // Put module declarations FIRST, then existing defs content 1229 - result.insert( 1230 - mod_file_path, 1231 - (quote! { #module_tokens #existing_tokens }, nsid.clone()), 1232 - ); 1233 - } else { 1234 - result.insert(mod_file_path, (module_tokens, None)); 1235 - } 1236 - } 1237 - 1238 - result 1239 - } 1240 - 1241 - /// Write all generated code to disk 1242 - pub fn write_to_disk(&self, output_dir: &std::path::Path) -> Result<()> { 1243 - // Generate all code (defs only) 1244 - let defs_files = self.generate_all()?; 1245 - let mut all_files = defs_files.clone(); 1246 - 1247 - // Generate module tree iteratively until no new files appear 1248 - loop { 1249 - let module_map = self.generate_module_tree(&all_files, &defs_files); 1250 - let old_count = all_files.len(); 1251 - 1252 - // Merge new module files 1253 - for (path, tokens) in module_map { 1254 - all_files.insert(path, tokens); 1255 - } 1256 - 1257 - if all_files.len() == old_count { 1258 - // No new files added 1259 - break; 1260 - } 1261 - } 1262 - 1263 - // Write to disk 1264 - for (path, (tokens, nsid)) in all_files { 1265 - let full_path = output_dir.join(&path); 1266 - 1267 - // Create parent directories 1268 - if let Some(parent) = full_path.parent() { 1269 - std::fs::create_dir_all(parent).map_err(|e| CodegenError::Other { 1270 - message: format!("Failed to create directory {:?}: {}", parent, e), 1271 - source: None, 1272 - })?; 1273 - } 1274 - 1275 - // Format code 1276 - let file: syn::File = syn::parse2(tokens.clone()).map_err(|e| CodegenError::Other { 1277 - message: format!( 1278 - "Failed to parse tokens for {:?}: {}\nTokens: {}", 1279 - path, e, tokens 1280 - ), 1281 - source: None, 1282 - })?; 1283 - let mut formatted = prettyplease::unparse(&file); 1284 - 1285 - // Add blank lines between top-level items for better readability 1286 - let lines: Vec<&str> = formatted.lines().collect(); 1287 - let mut result_lines = Vec::new(); 1288 - 1289 - for (i, line) in lines.iter().enumerate() { 1290 - result_lines.push(*line); 1291 - 1292 - // Add blank line after closing braces that are at column 0 (top-level items) 1293 - if *line == "}" && i + 1 < lines.len() && !lines[i + 1].is_empty() { 1294 - result_lines.push(""); 1295 - } 1296 - 1297 - // Add blank line after last pub mod declaration before structs/enums 1298 - if line.starts_with("pub mod ") && i + 1 < lines.len() { 1299 - let next_line = lines[i + 1]; 1300 - if !next_line.starts_with("pub mod ") && !next_line.is_empty() { 1301 - result_lines.push(""); 1302 - } 1303 - } 1304 - } 1305 - 1306 - formatted = result_lines.join("\n"); 1307 - 1308 - // Add header comment 1309 - let header = if let Some(nsid) = nsid { 1310 - format!( 1311 - "// @generated by jacquard-lexicon. DO NOT EDIT.\n//\n// Lexicon: {}\n//\n// This file was automatically generated from Lexicon schemas.\n// Any manual changes will be overwritten on the next regeneration.\n\n", 1312 - nsid 1313 - ) 1314 - } else { 1315 - "// @generated by jacquard-lexicon. DO NOT EDIT.\n//\n// This file was automatically generated from Lexicon schemas.\n// Any manual changes will be overwritten on the next regeneration.\n\n".to_string() 1316 - }; 1317 - formatted = format!("{}{}", header, formatted); 1318 - 1319 - // Write file 1320 - std::fs::write(&full_path, formatted).map_err(|e| CodegenError::Other { 1321 - message: format!("Failed to write file {:?}: {}", full_path, e), 1322 - source: None, 1323 - })?; 1324 - } 1325 - 1326 - Ok(()) 1327 - } 1328 - 1329 - /// Generate doc comment from description 1330 - fn generate_doc_comment(&self, desc: Option<&jacquard_common::CowStr>) -> TokenStream { 1331 - if let Some(desc) = desc { 1332 - let doc = desc.as_ref(); 1333 - quote! { #[doc = #doc] } 1334 - } else { 1335 - quote! {} 1336 - } 1337 - } 1338 - 1339 - /// Generate params struct from XRPC query parameters 1340 - fn generate_params_struct( 1341 - &self, 1342 - type_base: &str, 1343 - params: &crate::lexicon::LexXrpcQueryParameter<'static>, 1344 - ) -> Result<TokenStream> { 1345 - use crate::lexicon::LexXrpcQueryParameter; 1346 - match params { 1347 - LexXrpcQueryParameter::Params(p) => self.generate_params_struct_inner(type_base, p), 1348 - } 1349 - } 1350 - 1351 - /// Generate params struct from XRPC procedure parameters (query string params) 1352 - fn generate_params_struct_proc( 1353 - &self, 1354 - type_base: &str, 1355 - params: &crate::lexicon::LexXrpcProcedureParameter<'static>, 1356 - ) -> Result<TokenStream> { 1357 - use crate::lexicon::LexXrpcProcedureParameter; 1358 - match params { 1359 - // For procedures, query string params still get "Params" suffix since the main struct is the input 1360 - LexXrpcProcedureParameter::Params(p) => { 1361 - let struct_name = format!("{}Params", type_base); 1362 - let ident = syn::Ident::new(&struct_name, proc_macro2::Span::call_site()); 1363 - self.generate_params_struct_inner_with_name(&ident, p) 1364 - } 1365 - } 1366 - } 1367 - 1368 - /// Generate params struct inner (shared implementation) 1369 - fn generate_params_struct_inner( 1370 - &self, 1371 - type_base: &str, 1372 - p: &crate::lexicon::LexXrpcParameters<'static>, 1373 - ) -> Result<TokenStream> { 1374 - let ident = syn::Ident::new(type_base, proc_macro2::Span::call_site()); 1375 - self.generate_params_struct_inner_with_name(&ident, p) 1376 - } 1377 - 1378 - /// Generate params struct with custom name 1379 - fn generate_params_struct_inner_with_name( 1380 - &self, 1381 - ident: &syn::Ident, 1382 - p: &crate::lexicon::LexXrpcParameters<'static>, 1383 - ) -> Result<TokenStream> { 1384 - let required = p.required.as_ref().map(|r| r.as_slice()).unwrap_or(&[]); 1385 - let mut fields = Vec::new(); 1386 - let mut default_fns = Vec::new(); 1387 - 1388 - for (field_name, field_type) in &p.properties { 1389 - let is_required = required.contains(field_name); 1390 - let (field_tokens, default_fn) = 1391 - self.generate_param_field_with_default("", field_name, field_type, is_required)?; 1392 - fields.push(field_tokens); 1393 - if let Some(fn_def) = default_fn { 1394 - default_fns.push(fn_def); 1395 - } 1396 - } 1397 - 1398 - let doc = self.generate_doc_comment(p.description.as_ref()); 1399 - let needs_lifetime = self.params_need_lifetime(p); 1400 - 1401 - let derives = quote! { 1402 - #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq, bon::Builder, jacquard_derive::IntoStatic)] 1403 - #[builder(start_fn = new)] 1404 - }; 1405 - 1406 - // Generate IntoStatic impl 1407 - // let field_names: Vec<&str> = p.properties.keys().map(|k| k.as_str()).collect(); 1408 - // let type_name = ident.to_string(); 1409 - // let into_static_impl = 1410 - // self.generate_into_static_for_struct(&type_name, &field_names, needs_lifetime, false); 1411 - 1412 - if needs_lifetime { 1413 - Ok(quote! { 1414 - #(#default_fns)* 1415 - 1416 - #doc 1417 - #derives 1418 - #[serde(rename_all = "camelCase")] 1419 - pub struct #ident<'a> { 1420 - #(#fields)* 1421 - } 1422 - 1423 - //#into_static_impl 1424 - }) 1425 - } else { 1426 - Ok(quote! { 1427 - #(#default_fns)* 1428 - 1429 - #doc 1430 - #derives 1431 - #[serde(rename_all = "camelCase")] 1432 - pub struct #ident { 1433 - #(#fields)* 1434 - } 1435 - 1436 - //#into_static_impl 1437 - }) 1438 - } 1439 - } 1440 - 1441 - /// Generate param field with serde default if present 1442 - /// Returns (field_tokens, optional_default_function) 1443 - fn generate_param_field_with_default( 1444 - &self, 1445 - nsid: &str, 1446 - field_name: &str, 1447 - field_type: &crate::lexicon::LexXrpcParametersProperty<'static>, 1448 - is_required: bool, 1449 - ) -> Result<(TokenStream, Option<TokenStream>)> { 1450 - use crate::lexicon::LexXrpcParametersProperty; 1451 - use heck::ToSnakeCase; 1452 - 1453 - // Get base field 1454 - let base_field = self.generate_param_field(nsid, field_name, field_type, is_required)?; 1455 - 1456 - // Generate default function and attribute for required fields with defaults 1457 - // For optional fields, just add doc comments 1458 - let (doc_comment, serde_attr, default_fn) = if is_required { 1459 - match field_type { 1460 - LexXrpcParametersProperty::Boolean(b) if b.default.is_some() => { 1461 - let v = b.default.unwrap(); 1462 - let fn_name = format!("_default_{}", field_name.to_snake_case()); 1463 - let fn_ident = syn::Ident::new(&fn_name, proc_macro2::Span::call_site()); 1464 - ( 1465 - Some(format!("Defaults to `{}`", v)), 1466 - Some(quote! { #[serde(default = #fn_name)] }), 1467 - Some(quote! { 1468 - fn #fn_ident() -> bool { #v } 1469 - }), 1470 - ) 1471 - } 1472 - LexXrpcParametersProperty::Integer(i) if i.default.is_some() => { 1473 - let v = i.default.unwrap(); 1474 - let fn_name = format!("_default_{}", field_name.to_snake_case()); 1475 - let fn_ident = syn::Ident::new(&fn_name, proc_macro2::Span::call_site()); 1476 - ( 1477 - Some(format!("Defaults to `{}`", v)), 1478 - Some(quote! { #[serde(default = #fn_name)] }), 1479 - Some(quote! { 1480 - fn #fn_ident() -> i64 { #v } 1481 - }), 1482 - ) 1483 - } 1484 - LexXrpcParametersProperty::String(s) if s.default.is_some() => { 1485 - let v = s.default.as_ref().unwrap().as_ref(); 1486 - let fn_name = format!("_default_{}", field_name.to_snake_case()); 1487 - let fn_ident = syn::Ident::new(&fn_name, proc_macro2::Span::call_site()); 1488 - ( 1489 - Some(format!("Defaults to `\"{}\"`", v)), 1490 - Some(quote! { #[serde(default = #fn_name)] }), 1491 - Some(quote! { 1492 - fn #fn_ident() -> jacquard_common::CowStr<'static> { 1493 - jacquard_common::CowStr::from(#v) 1494 - } 1495 - }), 1496 - ) 1497 - } 1498 - _ => (None, None, None), 1499 - } 1500 - } else { 1501 - // Optional fields - just doc comments, no serde defaults 1502 - let doc = match field_type { 1503 - LexXrpcParametersProperty::Integer(i) => { 1504 - let mut parts = Vec::new(); 1505 - if let Some(def) = i.default { 1506 - parts.push(format!("default: {}", def)); 1507 - } 1508 - if let Some(min) = i.minimum { 1509 - parts.push(format!("min: {}", min)); 1510 - } 1511 - if let Some(max) = i.maximum { 1512 - parts.push(format!("max: {}", max)); 1513 - } 1514 - if !parts.is_empty() { 1515 - Some(format!("({})", parts.join(", "))) 1516 - } else { 1517 - None 1518 - } 1519 - } 1520 - LexXrpcParametersProperty::String(s) => { 1521 - let mut parts = Vec::new(); 1522 - if let Some(def) = s.default.as_ref() { 1523 - parts.push(format!("default: \"{}\"", def.as_ref())); 1524 - } 1525 - if let Some(min) = s.min_length { 1526 - parts.push(format!("min length: {}", min)); 1527 - } 1528 - if let Some(max) = s.max_length { 1529 - parts.push(format!("max length: {}", max)); 1530 - } 1531 - if !parts.is_empty() { 1532 - Some(format!("({})", parts.join(", "))) 1533 - } else { 1534 - None 1535 - } 1536 - } 1537 - LexXrpcParametersProperty::Boolean(b) => { 1538 - b.default.map(|v| format!("(default: {})", v)) 1539 - } 1540 - _ => None, 1541 - }; 1542 - (doc, None, None) 1543 - }; 1544 - 1545 - let doc = doc_comment.as_ref().map(|d| quote! { #[doc = #d] }); 1546 - let field_with_attrs = match (doc, serde_attr) { 1547 - (Some(doc), Some(attr)) => quote! { 1548 - #doc 1549 - #attr 1550 - #base_field 1551 - }, 1552 - (Some(doc), None) => quote! { 1553 - #doc 1554 - #base_field 1555 - }, 1556 - (None, Some(attr)) => quote! { 1557 - #attr 1558 - #base_field 1559 - }, 1560 - (None, None) => base_field, 1561 - }; 1562 - 1563 - Ok((field_with_attrs, default_fn)) 1564 - } 1565 - 1566 - /// Generate input struct from XRPC body 1567 - fn generate_input_struct( 1568 - &self, 1569 - nsid: &str, 1570 - type_base: &str, 1571 - body: &LexXrpcBody<'static>, 1572 - ) -> Result<TokenStream> { 1573 - let ident = syn::Ident::new(type_base, proc_macro2::Span::call_site()); 1574 - 1575 - // Check if this is a binary body (no schema, just raw bytes) 1576 - let is_binary_body = body.schema.is_none(); 1577 - 1578 - let fields = if let Some(schema) = &body.schema { 1579 - self.generate_body_fields("", type_base, schema, true)? 1580 - } else { 1581 - // Binary body: just a bytes field 1582 - quote! { 1583 - pub body: bytes::Bytes, 1584 - } 1585 - }; 1586 - 1587 - let doc = self.generate_doc_comment(body.description.as_ref()); 1588 - 1589 - // Binary bodies don't need #[lexicon] attribute or lifetime 1590 - let struct_def = if is_binary_body { 1591 - quote! { 1592 - #doc 1593 - #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq, bon::Builder, jacquard_derive::IntoStatic)] 1594 - #[builder(start_fn = new)] 1595 - #[serde(rename_all = "camelCase")] 1596 - pub struct #ident { 1597 - #fields 1598 - } 1599 - } 1600 - } else { 1601 - // Input structs with schemas: manually add extra_data field with #[builder(default)] 1602 - // for bon compatibility. The #[lexicon] macro will see it exists and skip adding it. 1603 - quote! { 1604 - #doc 1605 - #[jacquard_derive::lexicon] 1606 - #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq, bon::Builder, jacquard_derive::IntoStatic)] 1607 - #[serde(rename_all = "camelCase")] 1608 - #[builder(start_fn = new)] 1609 - pub struct #ident<'a> { 1610 - #fields 1611 - #[serde(flatten)] 1612 - #[serde(borrow)] 1613 - #[builder(default)] 1614 - pub extra_data: ::std::collections::BTreeMap< 1615 - ::jacquard_common::smol_str::SmolStr, 1616 - ::jacquard_common::types::value::Data<'a> 1617 - >, 1618 - } 1619 - } 1620 - }; 1621 - 1622 - // Generate union types if schema is an Object 1623 - let mut unions = Vec::new(); 1624 - if let Some(crate::lexicon::LexXrpcBodySchema::Object(obj)) = &body.schema { 1625 - for (field_name, field_type) in &obj.properties { 1626 - if let LexObjectProperty::Union(union) = field_type { 1627 - let union_name = format!("{}Record{}", type_base, field_name.to_pascal_case()); 1628 - let refs: Vec<_> = union.refs.iter().cloned().collect(); 1629 - let union_def = 1630 - self.generate_union(nsid, &union_name, &refs, None, union.closed)?; 1631 - unions.push(union_def); 1632 - } 1633 - } 1634 - } 1635 - 1636 - // Generate IntoStatic impl 1637 - // let into_static_impl = if is_binary_body { 1638 - // // Binary bodies: simple clone of the Bytes field 1639 - // quote! { 1640 - // impl jacquard_common::IntoStatic for #ident { 1641 - // type Output = #ident; 1642 - // fn into_static(self) -> Self::Output { 1643 - // self 1644 - // } 1645 - // } 1646 - // } 1647 - // } else { 1648 - // let field_names: Vec<&str> = match &body.schema { 1649 - // Some(crate::lexicon::LexXrpcBodySchema::Object(obj)) => { 1650 - // obj.properties.keys().map(|k| k.as_str()).collect() 1651 - // } 1652 - // Some(_) => { 1653 - // // For Ref or Union schemas, there's just a single flattened field 1654 - // vec!["value"] 1655 - // } 1656 - // None => { 1657 - // // No schema means no fields, just extra_data 1658 - // vec![] 1659 - // } 1660 - // }; 1661 - // self.generate_into_static_for_struct(type_base, &field_names, true, true) 1662 - // }; 1663 - 1664 - Ok(quote! { 1665 - #struct_def 1666 - #(#unions)* 1667 - //#into_static_impl 1668 - }) 1669 - } 1670 - 1671 - /// Generate output struct from XRPC body 1672 - fn generate_output_struct( 1673 - &self, 1674 - nsid: &str, 1675 - type_base: &str, 1676 - body: &LexXrpcBody<'static>, 1677 - ) -> Result<TokenStream> { 1678 - let struct_name = format!("{}Output", type_base); 1679 - let ident = syn::Ident::new(&struct_name, proc_macro2::Span::call_site()); 1680 - 1681 - let fields = if let Some(schema) = &body.schema { 1682 - self.generate_body_fields("", &struct_name, schema, false)? 1683 - } else { 1684 - quote! {} 1685 - }; 1686 - 1687 - let doc = self.generate_doc_comment(body.description.as_ref()); 1688 - 1689 - // Output structs always get a lifetime since they have the #[lexicon] attribute 1690 - // which adds extra_data: BTreeMap<..., Data<'a>> 1691 - let struct_def = quote! { 1692 - #doc 1693 - #[jacquard_derive::lexicon] 1694 - #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq, jacquard_derive::IntoStatic)] 1695 - #[serde(rename_all = "camelCase")] 1696 - pub struct #ident<'a> { 1697 - #fields 1698 - } 1699 - }; 1700 - 1701 - // Generate union types if schema is an Object 1702 - let mut unions = Vec::new(); 1703 - if let Some(crate::lexicon::LexXrpcBodySchema::Object(obj)) = &body.schema { 1704 - for (field_name, field_type) in &obj.properties { 1705 - if let LexObjectProperty::Union(union) = field_type { 1706 - let union_name = 1707 - format!("{}Record{}", struct_name, field_name.to_pascal_case()); 1708 - let refs: Vec<_> = union.refs.iter().cloned().collect(); 1709 - let union_def = 1710 - self.generate_union(nsid, &union_name, &refs, None, union.closed)?; 1711 - unions.push(union_def); 1712 - } 1713 - } 1714 - } 1715 - 1716 - // Generate IntoStatic impl 1717 - // let field_names: Vec<&str> = match &body.schema { 1718 - // Some(crate::lexicon::LexXrpcBodySchema::Object(obj)) => { 1719 - // obj.properties.keys().map(|k| k.as_str()).collect() 1720 - // } 1721 - // Some(_) => { 1722 - // // For Ref or Union schemas, there's just a single flattened field 1723 - // vec!["value"] 1724 - // } 1725 - // None => { 1726 - // // No schema means no fields, just extra_data 1727 - // vec![] 1728 - // } 1729 - // }; 1730 - // let into_static_impl = 1731 - // self.generate_into_static_for_struct(&struct_name, &field_names, true, true); 1732 - 1733 - Ok(quote! { 1734 - #struct_def 1735 - #(#unions)* 1736 - //#into_static_impl 1737 - }) 1738 - } 1739 - 1740 - /// Generate fields from XRPC body schema 1741 - fn generate_body_fields( 1742 - &self, 1743 - nsid: &str, 1744 - parent_type_name: &str, 1745 - schema: &LexXrpcBodySchema<'static>, 1746 - is_builder: bool, 1747 - ) -> Result<TokenStream> { 1748 - use crate::lexicon::LexXrpcBodySchema; 1749 - 1750 - match schema { 1751 - LexXrpcBodySchema::Object(obj) => { 1752 - self.generate_object_fields(nsid, parent_type_name, obj, is_builder) 1753 - } 1754 - LexXrpcBodySchema::Ref(ref_type) => { 1755 - let rust_type = self.ref_to_rust_type(&ref_type.r#ref)?; 1756 - Ok(quote! { 1757 - #[serde(flatten)] 1758 - #[serde(borrow)] 1759 - pub value: #rust_type, 1760 - }) 1761 - } 1762 - LexXrpcBodySchema::Union(_union) => { 1763 - let rust_type = quote! { jacquard_common::types::value::Data<'a> }; 1764 - Ok(quote! { 1765 - #[serde(flatten)] 1766 - #[serde(borrow)] 1767 - pub value: #rust_type, 1768 - }) 1769 - } 1770 - } 1771 - } 1772 - 1773 - /// Generate a field for XRPC parameters 1774 - fn generate_param_field( 1775 - &self, 1776 - _nsid: &str, 1777 - field_name: &str, 1778 - field_type: &crate::lexicon::LexXrpcParametersProperty<'static>, 1779 - is_required: bool, 1780 - ) -> Result<TokenStream> { 1781 - use crate::lexicon::LexXrpcParametersProperty; 1782 - 1783 - let field_ident = make_ident(&field_name.to_snake_case()); 1784 - 1785 - let (rust_type, needs_lifetime, is_cowstr) = match field_type { 1786 - LexXrpcParametersProperty::Boolean(_) => (quote! { bool }, false, false), 1787 - LexXrpcParametersProperty::Integer(_) => (quote! { i64 }, false, false), 1788 - LexXrpcParametersProperty::String(s) => { 1789 - let is_cowstr = s.format.is_none(); // CowStr for plain strings 1790 - ( 1791 - self.string_to_rust_type(s), 1792 - self.string_needs_lifetime(s), 1793 - is_cowstr, 1794 - ) 1795 - } 1796 - LexXrpcParametersProperty::Unknown(_) => ( 1797 - quote! { jacquard_common::types::value::Data<'a> }, 1798 - true, 1799 - false, 1800 - ), 1801 - LexXrpcParametersProperty::Array(arr) => { 1802 - let needs_lifetime = match &arr.items { 1803 - crate::lexicon::LexPrimitiveArrayItem::Boolean(_) 1804 - | crate::lexicon::LexPrimitiveArrayItem::Integer(_) => false, 1805 - crate::lexicon::LexPrimitiveArrayItem::String(s) => { 1806 - self.string_needs_lifetime(s) 1807 - } 1808 - crate::lexicon::LexPrimitiveArrayItem::Unknown(_) => true, 1809 - }; 1810 - let item_type = match &arr.items { 1811 - crate::lexicon::LexPrimitiveArrayItem::Boolean(_) => quote! { bool }, 1812 - crate::lexicon::LexPrimitiveArrayItem::Integer(_) => quote! { i64 }, 1813 - crate::lexicon::LexPrimitiveArrayItem::String(s) => self.string_to_rust_type(s), 1814 - crate::lexicon::LexPrimitiveArrayItem::Unknown(_) => { 1815 - quote! { jacquard_common::types::value::Data<'a> } 1816 - } 1817 - }; 1818 - (quote! { Vec<#item_type> }, needs_lifetime, false) 1819 - } 1820 - }; 1821 - 1822 - let rust_type = if is_required { 1823 - rust_type 1824 - } else { 1825 - quote! { std::option::Option<#rust_type> } 1826 - }; 1827 - 1828 - let mut attrs = Vec::new(); 1829 - 1830 - if !is_required { 1831 - attrs.push(quote! { #[serde(skip_serializing_if = "std::option::Option::is_none")] }); 1832 - } 1833 - 1834 - // Add serde(borrow) to all fields with lifetimes 1835 - if needs_lifetime { 1836 - attrs.push(quote! { #[serde(borrow)] }); 1837 - } 1838 - 1839 - // Add builder(into) for CowStr fields to allow String, &str, etc. 1840 - if is_cowstr { 1841 - attrs.push(quote! { #[builder(into)] }); 1842 - } 1843 - 1844 - Ok(quote! { 1845 - #(#attrs)* 1846 - pub #field_ident: #rust_type, 1847 - }) 1848 - } 1849 - 1850 - /// Generate error enum from XRPC errors 1851 - fn generate_error_enum( 1852 - &self, 1853 - type_base: &str, 1854 - errors: &[LexXrpcError<'static>], 1855 - ) -> Result<TokenStream> { 1856 - let enum_name = format!("{}Error", type_base); 1857 - let ident = syn::Ident::new(&enum_name, proc_macro2::Span::call_site()); 1858 - 1859 - let mut variants = Vec::new(); 1860 - let mut display_arms = Vec::new(); 1861 - 1862 - for error in errors { 1863 - let variant_name = error.name.to_pascal_case(); 1864 - let variant_ident = syn::Ident::new(&variant_name, proc_macro2::Span::call_site()); 1865 - 1866 - let error_name = error.name.as_ref(); 1867 - let doc = self.generate_doc_comment(error.description.as_ref()); 1868 - 1869 - variants.push(quote! { 1870 - #doc 1871 - #[serde(rename = #error_name)] 1872 - #variant_ident(std::option::Option<String>) 1873 - }); 1874 - 1875 - display_arms.push(quote! { 1876 - Self::#variant_ident(msg) => { 1877 - write!(f, #error_name)?; 1878 - if let Some(msg) = msg { 1879 - write!(f, ": {}", msg)?; 1880 - } 1881 - Ok(()) 1882 - } 1883 - }); 1884 - } 1885 - 1886 - // Generate IntoStatic impl 1887 - let variant_info: Vec<(String, EnumVariantKind)> = errors 1888 - .iter() 1889 - .map(|e| (e.name.to_pascal_case(), EnumVariantKind::Tuple)) 1890 - .collect(); 1891 - let into_static_impl = 1892 - self.generate_into_static_for_enum(&enum_name, &variant_info, true, true); 1893 - 1894 - Ok(quote! { 1895 - #[jacquard_derive::open_union] 1896 - #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq, thiserror::Error, miette::Diagnostic)] 1897 - #[serde(tag = "error", content = "message")] 1898 - #[serde(bound(deserialize = "'de: 'a"))] 1899 - pub enum #ident<'a> { 1900 - #(#variants,)* 1901 - } 1902 - 1903 - impl std::fmt::Display for #ident<'_> { 1904 - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 1905 - match self { 1906 - #(#display_arms)* 1907 - Self::Unknown(err) => write!(f, "Unknown error: {:?}", err), 1908 - } 1909 - } 1910 - } 1911 - 1912 - #into_static_impl 1913 - }) 1914 - } 1915 - 1916 - /// Generate enum for string with known values 1917 - fn generate_known_values_enum( 1918 - &self, 1919 - nsid: &str, 1920 - def_name: &str, 1921 - string: &LexString<'static>, 1922 - ) -> Result<TokenStream> { 1923 - let type_name = self.def_to_type_name(nsid, def_name); 1924 - let ident = syn::Ident::new(&type_name, proc_macro2::Span::call_site()); 1925 - 1926 - let known_values = string.known_values.as_ref().unwrap(); 1927 - let mut variants = Vec::new(); 1928 - let mut from_str_arms = Vec::new(); 1929 - let mut as_str_arms = Vec::new(); 1930 - 1931 - for value in known_values { 1932 - // Convert value to valid Rust identifier 1933 - let value_str = value.as_ref(); 1934 - let variant_name = value_to_variant_name(value_str); 1935 - let variant_ident = syn::Ident::new(&variant_name, proc_macro2::Span::call_site()); 1936 - 1937 - variants.push(quote! { 1938 - #variant_ident 1939 - }); 1940 - 1941 - from_str_arms.push(quote! { 1942 - #value_str => Self::#variant_ident 1943 - }); 1944 - 1945 - as_str_arms.push(quote! { 1946 - Self::#variant_ident => #value_str 1947 - }); 1948 - } 1949 - 1950 - let doc = self.generate_doc_comment(string.description.as_ref()); 1951 - 1952 - // Generate IntoStatic impl 1953 - let variant_info: Vec<(String, EnumVariantKind)> = known_values 1954 - .iter() 1955 - .map(|value| { 1956 - let variant_name = value_to_variant_name(value.as_ref()); 1957 - (variant_name, EnumVariantKind::Unit) 1958 - }) 1959 - .chain(std::iter::once(( 1960 - "Other".to_string(), 1961 - EnumVariantKind::Tuple, 1962 - ))) 1963 - .collect(); 1964 - let into_static_impl = 1965 - self.generate_into_static_for_enum(&type_name, &variant_info, true, false); 1966 - 1967 - Ok(quote! { 1968 - #doc 1969 - #[derive(Debug, Clone, PartialEq, Eq, Hash)] 1970 - pub enum #ident<'a> { 1971 - #(#variants,)* 1972 - Other(jacquard_common::CowStr<'a>), 1973 - } 1974 - 1975 - impl<'a> #ident<'a> { 1976 - pub fn as_str(&self) -> &str { 1977 - match self { 1978 - #(#as_str_arms,)* 1979 - Self::Other(s) => s.as_ref(), 1980 - } 1981 - } 1982 - } 1983 - 1984 - impl<'a> From<&'a str> for #ident<'a> { 1985 - fn from(s: &'a str) -> Self { 1986 - match s { 1987 - #(#from_str_arms,)* 1988 - _ => Self::Other(jacquard_common::CowStr::from(s)), 1989 - } 1990 - } 1991 - } 1992 - 1993 - impl<'a> From<String> for #ident<'a> { 1994 - fn from(s: String) -> Self { 1995 - match s.as_str() { 1996 - #(#from_str_arms,)* 1997 - _ => Self::Other(jacquard_common::CowStr::from(s)), 1998 - } 1999 - } 2000 - } 2001 - 2002 - impl<'a> AsRef<str> for #ident<'a> { 2003 - fn as_ref(&self) -> &str { 2004 - self.as_str() 2005 - } 2006 - } 2007 - 2008 - impl<'a> serde::Serialize for #ident<'a> { 2009 - fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> 2010 - where 2011 - S: serde::Serializer, 2012 - { 2013 - serializer.serialize_str(self.as_str()) 2014 - } 2015 - } 2016 - 2017 - impl<'de, 'a> serde::Deserialize<'de> for #ident<'a> 2018 - where 2019 - 'de: 'a, 2020 - { 2021 - fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 2022 - where 2023 - D: serde::Deserializer<'de>, 2024 - { 2025 - let s = <&'de str>::deserialize(deserializer)?; 2026 - Ok(Self::from(s)) 2027 - } 2028 - } 2029 - 2030 - #into_static_impl 2031 - }) 2032 - } 2033 - 2034 - /// Generate enum for integer with enum values 2035 - fn generate_integer_enum( 2036 - &self, 2037 - nsid: &str, 2038 - def_name: &str, 2039 - integer: &LexInteger<'static>, 2040 - ) -> Result<TokenStream> { 2041 - let type_name = self.def_to_type_name(nsid, def_name); 2042 - let ident = syn::Ident::new(&type_name, proc_macro2::Span::call_site()); 2043 - 2044 - let enum_values = integer.r#enum.as_ref().unwrap(); 2045 - let mut variants = Vec::new(); 2046 - let mut from_i64_arms = Vec::new(); 2047 - let mut to_i64_arms = Vec::new(); 2048 - 2049 - for value in enum_values { 2050 - let variant_name = format!("Value{}", value.abs()); 2051 - let variant_ident = syn::Ident::new(&variant_name, proc_macro2::Span::call_site()); 2052 - 2053 - variants.push(quote! { 2054 - #[serde(rename = #value)] 2055 - #variant_ident 2056 - }); 2057 - 2058 - from_i64_arms.push(quote! { 2059 - #value => Self::#variant_ident 2060 - }); 2061 - 2062 - to_i64_arms.push(quote! { 2063 - Self::#variant_ident => #value 2064 - }); 2065 - } 2066 - 2067 - let doc = self.generate_doc_comment(integer.description.as_ref()); 2068 - 2069 - Ok(quote! { 2070 - #doc 2071 - #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 2072 - pub enum #ident { 2073 - #(#variants,)* 2074 - #[serde(untagged)] 2075 - Other(i64), 2076 - } 2077 - 2078 - impl #ident { 2079 - pub fn as_i64(&self) -> i64 { 2080 - match self { 2081 - #(#to_i64_arms,)* 2082 - Self::Other(n) => *n, 2083 - } 2084 - } 2085 - } 2086 - 2087 - impl From<i64> for #ident { 2088 - fn from(n: i64) -> Self { 2089 - match n { 2090 - #(#from_i64_arms,)* 2091 - _ => Self::Other(n), 2092 - } 2093 - } 2094 - } 2095 - 2096 - impl serde::Serialize for #ident { 2097 - fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> 2098 - where 2099 - S: serde::Serializer, 2100 - { 2101 - serializer.serialize_i64(self.as_i64()) 2102 - } 2103 - } 2104 - 2105 - impl<'de> serde::Deserialize<'de> for #ident { 2106 - fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 2107 - where 2108 - D: serde::Deserializer<'de>, 2109 - { 2110 - let n = i64::deserialize(deserializer)?; 2111 - Ok(Self::from(n)) 2112 - } 2113 - } 2114 - }) 2115 - } 2116 - 2117 - /// Generate XrpcRequest trait impl for a query or procedure 2118 - fn generate_xrpc_request_impl( 2119 - &self, 2120 - nsid: &str, 2121 - type_base: &str, 2122 - method: TokenStream, 2123 - output_encoding: &str, 2124 - has_params: bool, 2125 - params_has_lifetime: bool, 2126 - has_output: bool, 2127 - has_errors: bool, 2128 - is_binary_input: bool, 2129 - ) -> Result<TokenStream> { 2130 - let output_type = if has_output { 2131 - let output_ident = syn::Ident::new( 2132 - &format!("{}Output", type_base), 2133 - proc_macro2::Span::call_site(), 2134 - ); 2135 - quote! { #output_ident<'de> } 2136 - } else { 2137 - quote! { () } 2138 - }; 2139 - 2140 - let error_type = if has_errors { 2141 - let error_ident = syn::Ident::new( 2142 - &format!("{}Error", type_base), 2143 - proc_macro2::Span::call_site(), 2144 - ); 2145 - quote! { #error_ident<'de> } 2146 - } else { 2147 - quote! { jacquard_common::xrpc::GenericError<'de> } 2148 - }; 2149 - 2150 - // Generate the response type that implements XrpcResp 2151 - let response_ident = syn::Ident::new( 2152 - &format!("{}Response", type_base), 2153 - proc_macro2::Span::call_site(), 2154 - ); 2155 - 2156 - // Generate the endpoint type that implements XrpcEndpoint 2157 - let endpoint_ident = syn::Ident::new( 2158 - &format!("{}Request", type_base), 2159 - proc_macro2::Span::call_site(), 2160 - ); 2161 - 2162 - let response_type = quote! { 2163 - #[doc = "Response type for "] 2164 - #[doc = #nsid] 2165 - pub struct #response_ident; 2166 - 2167 - impl jacquard_common::xrpc::XrpcResp for #response_ident { 2168 - const NSID: &'static str = #nsid; 2169 - const ENCODING: &'static str = #output_encoding; 2170 - type Output<'de> = #output_type; 2171 - type Err<'de> = #error_type; 2172 - } 2173 - }; 2174 - 2175 - // Generate encode_body() method for binary inputs 2176 - let encode_body_method = if is_binary_input { 2177 - quote! { 2178 - fn encode_body(&self) -> Result<Vec<u8>, jacquard_common::xrpc::EncodeError> { 2179 - Ok(self.body.to_vec()) 2180 - } 2181 - } 2182 - } else { 2183 - quote! {} 2184 - }; 2185 - 2186 - // Generate decode_body() method for binary inputs 2187 - let decode_body_method = if is_binary_input { 2188 - quote! { 2189 - fn decode_body( 2190 - body: &'de [u8], 2191 - ) -> Result<Box<Self>, jacquard_common::error::DecodeError> { 2192 - Ok(Box::new(Self { 2193 - body: bytes::Bytes::copy_from_slice(body), 2194 - })) 2195 - } 2196 - } 2197 - } else { 2198 - quote! {} 2199 - }; 2200 - 2201 - let endpoint_path = format!("/xrpc/{}", nsid); 2202 - 2203 - if has_params { 2204 - // Implement on the params/input struct itself 2205 - let request_ident = syn::Ident::new(type_base, proc_macro2::Span::call_site()); 2206 - let impl_target = if params_has_lifetime { 2207 - quote! { #request_ident<'de> } 2208 - } else { 2209 - quote! { #request_ident } 2210 - }; 2211 - 2212 - Ok(quote! { 2213 - #response_type 2214 - 2215 - impl<'de> jacquard_common::xrpc::XrpcRequest<'de> for #impl_target { 2216 - const NSID: &'static str = #nsid; 2217 - const METHOD: jacquard_common::xrpc::XrpcMethod = #method; 2218 - 2219 - type Response = #response_ident; 2220 - 2221 - #encode_body_method 2222 - #decode_body_method 2223 - } 2224 - 2225 - #[doc = "Endpoint type for "] 2226 - #[doc = #nsid] 2227 - pub struct #endpoint_ident; 2228 - 2229 - impl jacquard_common::xrpc::XrpcEndpoint for #endpoint_ident { 2230 - const PATH: &'static str = #endpoint_path; 2231 - const METHOD: jacquard_common::xrpc::XrpcMethod = #method; 2232 - 2233 - type Request<'de> = #impl_target; 2234 - type Response = #response_ident; 2235 - } 2236 - }) 2237 - } else { 2238 - // No params - generate a marker struct 2239 - let request_ident = syn::Ident::new(type_base, proc_macro2::Span::call_site()); 2240 - 2241 - Ok(quote! { 2242 - /// XRPC request marker type 2243 - #[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize, jacquard_derive::IntoStatic)] 2244 - pub struct #request_ident; 2245 - 2246 - #response_type 2247 - 2248 - impl<'de> jacquard_common::xrpc::XrpcRequest<'de> for #request_ident { 2249 - const NSID: &'static str = #nsid; 2250 - const METHOD: jacquard_common::xrpc::XrpcMethod = #method; 2251 - 2252 - type Response = #response_ident; 2253 - } 2254 - 2255 - #[doc = "Endpoint type for "] 2256 - #[doc = #nsid] 2257 - pub struct #endpoint_ident; 2258 - 2259 - impl jacquard_common::xrpc::XrpcEndpoint for #endpoint_ident { 2260 - const PATH: &'static str = #endpoint_path; 2261 - const METHOD: jacquard_common::xrpc::XrpcMethod = #method; 2262 - 2263 - type Request<'de> = #request_ident; 2264 - type Response = #response_ident; 2265 - } 2266 - }) 2267 - } 2268 - } 2269 - 2270 - /// Generate a union enum 2271 - pub fn generate_union( 2272 - &self, 2273 - current_nsid: &str, 2274 - union_name: &str, 2275 - refs: &[jacquard_common::CowStr<'static>], 2276 - description: Option<&str>, 2277 - closed: Option<bool>, 2278 - ) -> Result<TokenStream> { 2279 - let enum_ident = syn::Ident::new(union_name, proc_macro2::Span::call_site()); 2280 - 2281 - // Extract namespace prefix from current NSID (first two segments: "sh.weaver" from "sh.weaver.embed.recordWithMedia") 2282 - let parts: Vec<_> = current_nsid.splitn(3, '.').collect(); 2283 - let current_namespace = if parts.len() >= 2 { 2284 - format!("{}.{}", parts[0], parts[1]) 2285 - } else { 2286 - current_nsid.to_string() 2287 - }; 2288 - 2289 - // First pass: collect all variant names and detect collisions 2290 - #[derive(Debug)] 2291 - struct VariantInfo<'a> { 2292 - ref_str: &'a str, 2293 - ref_nsid: &'a str, 2294 - simple_name: String, 2295 - is_current_namespace: bool, 2296 - } 2297 - 2298 - let mut variant_infos = Vec::new(); 2299 - for ref_str in refs { 2300 - // Parse ref to get NSID and def name 2301 - let (ref_nsid, ref_def) = if let Some((nsid, fragment)) = ref_str.split_once('#') { 2302 - (nsid, fragment) 2303 - } else { 2304 - (ref_str.as_ref(), "main") 2305 - }; 2306 - 2307 - // Skip unknown refs - they'll be handled by Unknown variant 2308 - if !self.corpus.ref_exists(ref_str.as_ref()) { 2309 - continue; 2310 - } 2311 - 2312 - // Check if ref is in current namespace 2313 - let is_current_namespace = ref_nsid.starts_with(&current_namespace); 2314 - 2315 - // Generate simple variant name (without namespace prefix) 2316 - let simple_name = if ref_def == "main" { 2317 - ref_nsid.split('.').last().unwrap().to_pascal_case() 2318 - } else { 2319 - let last_segment = ref_nsid.split('.').last().unwrap().to_pascal_case(); 2320 - format!("{}{}", last_segment, ref_def.to_pascal_case()) 2321 - }; 2322 - 2323 - variant_infos.push(VariantInfo { 2324 - ref_str: ref_str.as_ref(), 2325 - ref_nsid, 2326 - simple_name, 2327 - is_current_namespace, 2328 - }); 2329 - } 2330 - 2331 - // Second pass: detect collisions and disambiguate 2332 - use std::collections::HashMap; 2333 - let mut name_counts: HashMap<String, usize> = HashMap::new(); 2334 - for info in &variant_infos { 2335 - *name_counts.entry(info.simple_name.clone()).or_insert(0) += 1; 2336 - } 2337 - 2338 - let mut variants = Vec::new(); 2339 - for info in variant_infos { 2340 - let has_collision = name_counts.get(&info.simple_name).copied().unwrap_or(0) > 1; 2341 - 2342 - // Track namespace dependency for foreign refs 2343 - if !info.is_current_namespace { 2344 - let parts: Vec<_> = info.ref_nsid.splitn(3, '.').collect(); 2345 - let foreign_namespace = if parts.len() >= 2 { 2346 - format!("{}.{}", parts[0], parts[1]) 2347 - } else { 2348 - info.ref_nsid.to_string() 2349 - }; 2350 - self.namespace_deps 2351 - .borrow_mut() 2352 - .entry(current_namespace.clone()) 2353 - .or_default() 2354 - .insert(foreign_namespace); 2355 - } 2356 - 2357 - // Disambiguate: add second NSID segment prefix only to foreign refs when there's a collision 2358 - let variant_name = if has_collision && !info.is_current_namespace { 2359 - // Get second segment (namespace identifier: "bsky" from "app.bsky.embed.images") 2360 - let segments: Vec<&str> = info.ref_nsid.split('.').collect(); 2361 - let prefix = if segments.len() >= 2 { 2362 - segments[1].to_pascal_case() 2363 - } else { 2364 - // Fallback: use first segment if only one exists 2365 - segments[0].to_pascal_case() 2366 - }; 2367 - format!("{}{}", prefix, info.simple_name) 2368 - } else { 2369 - info.simple_name.clone() 2370 - }; 2371 - 2372 - let variant_ident = syn::Ident::new(&variant_name, proc_macro2::Span::call_site()); 2373 - 2374 - // Get the Rust type for this ref 2375 - let rust_type = self.ref_to_rust_type(info.ref_str)?; 2376 - 2377 - // Add serde rename for the full NSID 2378 - let ref_str_literal = info.ref_str; 2379 - variants.push(quote! { 2380 - #[serde(rename = #ref_str_literal)] 2381 - #variant_ident(Box<#rust_type>) 2382 - }); 2383 - } 2384 - 2385 - let doc = description 2386 - .map(|d| quote! { #[doc = #d] }) 2387 - .unwrap_or_else(|| quote! {}); 2388 - 2389 - // Only add open_union if not closed 2390 - let is_open = closed != Some(true); 2391 - 2392 - // Generate IntoStatic impl 2393 - // let variant_info: Vec<(String, EnumVariantKind)> = refs 2394 - // .iter() 2395 - // .filter_map(|ref_str| { 2396 - // // Skip unknown refs 2397 - // if !self.corpus.ref_exists(ref_str.as_ref()) { 2398 - // return None; 2399 - // } 2400 - 2401 - // let (ref_nsid, ref_def) = if let Some((nsid, fragment)) = ref_str.split_once('#') { 2402 - // (nsid, fragment) 2403 - // } else { 2404 - // (ref_str.as_ref(), "main") 2405 - // }; 2406 - 2407 - // let variant_name = if ref_def == "main" { 2408 - // ref_nsid.split('.').last().unwrap().to_pascal_case() 2409 - // } else { 2410 - // let last_segment = ref_nsid.split('.').last().unwrap().to_pascal_case(); 2411 - // format!("{}{}", last_segment, ref_def.to_pascal_case()) 2412 - // }; 2413 - // Some((variant_name, EnumVariantKind::Tuple)) 2414 - // }) 2415 - // .collect(); 2416 - // let into_static_impl = 2417 - // self.generate_into_static_for_enum(union_name, &variant_info, true, is_open); 2418 - 2419 - if is_open { 2420 - Ok(quote! { 2421 - #doc 2422 - #[jacquard_derive::open_union] 2423 - #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq, jacquard_derive::IntoStatic)] 2424 - #[serde(tag = "$type")] 2425 - #[serde(bound(deserialize = "'de: 'a"))] 2426 - pub enum #enum_ident<'a> { 2427 - #(#variants,)* 2428 - } 2429 - 2430 - //#into_static_impl 2431 - }) 2432 - } else { 2433 - Ok(quote! { 2434 - #doc 2435 - #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq, jacquard_derive::IntoStatic)] 2436 - #[serde(tag = "$type")] 2437 - #[serde(bound(deserialize = "'de: 'a"))] 2438 - pub enum #enum_ident<'a> { 2439 - #(#variants,)* 2440 - } 2441 - 2442 - //#into_static_impl 2443 - }) 2444 - } 2445 - } 2446 - 2447 - /// Generate IntoStatic impl for a struct 2448 - #[allow(dead_code)] 2449 - fn generate_into_static_for_struct( 2450 - &self, 2451 - type_name: &str, 2452 - field_names: &[&str], 2453 - has_lifetime: bool, 2454 - has_extra_data: bool, 2455 - ) -> TokenStream { 2456 - let ident = syn::Ident::new(type_name, proc_macro2::Span::call_site()); 2457 - 2458 - let field_idents: Vec<_> = field_names 2459 - .iter() 2460 - .map(|name| make_ident(&name.to_snake_case())) 2461 - .collect(); 2462 - 2463 - if has_lifetime { 2464 - let field_conversions: Vec<_> = field_idents 2465 - .iter() 2466 - .map(|field| quote! { #field: self.#field.into_static() }) 2467 - .collect(); 2468 - 2469 - let extra_data_conversion = if has_extra_data { 2470 - quote! { extra_data: self.extra_data.into_static(), } 2471 - } else { 2472 - quote! {} 2473 - }; 2474 - 2475 - quote! { 2476 - impl jacquard_common::IntoStatic for #ident<'_> { 2477 - type Output = #ident<'static>; 2478 - 2479 - fn into_static(self) -> Self::Output { 2480 - #ident { 2481 - #(#field_conversions,)* 2482 - #extra_data_conversion 2483 - } 2484 - } 2485 - } 2486 - } 2487 - } else { 2488 - quote! { 2489 - impl jacquard_common::IntoStatic for #ident { 2490 - type Output = #ident; 2491 - 2492 - fn into_static(self) -> Self::Output { 2493 - self 2494 - } 2495 - } 2496 - } 2497 - } 2498 - } 2499 - 2500 - /// Generate IntoStatic impl for an enum 2501 - fn generate_into_static_for_enum( 2502 - &self, 2503 - type_name: &str, 2504 - variant_info: &[(String, EnumVariantKind)], 2505 - has_lifetime: bool, 2506 - is_open: bool, 2507 - ) -> TokenStream { 2508 - let ident = syn::Ident::new(type_name, proc_macro2::Span::call_site()); 2509 - 2510 - if has_lifetime { 2511 - let variant_conversions: Vec<_> = variant_info 2512 - .iter() 2513 - .map(|(variant_name, kind)| { 2514 - let variant_ident = syn::Ident::new(variant_name, proc_macro2::Span::call_site()); 2515 - match kind { 2516 - EnumVariantKind::Unit => { 2517 - quote! { 2518 - #ident::#variant_ident => #ident::#variant_ident 2519 - } 2520 - } 2521 - EnumVariantKind::Tuple => { 2522 - quote! { 2523 - #ident::#variant_ident(v) => #ident::#variant_ident(v.into_static()) 2524 - } 2525 - } 2526 - EnumVariantKind::Struct(fields) => { 2527 - let field_idents: Vec<_> = fields 2528 - .iter() 2529 - .map(|f| make_ident(&f.to_snake_case())) 2530 - .collect(); 2531 - let field_conversions: Vec<_> = field_idents 2532 - .iter() 2533 - .map(|f| quote! { #f: #f.into_static() }) 2534 - .collect(); 2535 - quote! { 2536 - #ident::#variant_ident { #(#field_idents,)* } => #ident::#variant_ident { 2537 - #(#field_conversions,)* 2538 - } 2539 - } 2540 - } 2541 - } 2542 - }) 2543 - .collect(); 2544 - 2545 - let unknown_conversion = if is_open { 2546 - quote! { 2547 - #ident::Unknown(v) => #ident::Unknown(v.into_static()), 2548 - } 2549 - } else { 2550 - quote! {} 2551 - }; 2552 - 2553 - quote! { 2554 - impl jacquard_common::IntoStatic for #ident<'_> { 2555 - type Output = #ident<'static>; 2556 - 2557 - fn into_static(self) -> Self::Output { 2558 - match self { 2559 - #(#variant_conversions,)* 2560 - #unknown_conversion 2561 - } 2562 - } 2563 - } 2564 - } 2565 - } else { 2566 - quote! { 2567 - impl jacquard_common::IntoStatic for #ident { 2568 - type Output = #ident; 2569 - 2570 - fn into_static(self) -> Self::Output { 2571 - self 2572 - } 2573 - } 2574 - } 2575 - } 2576 - } 2577 - 2578 - /// Get namespace dependencies collected during code generation 2579 - pub fn get_namespace_dependencies( 2580 - &self, 2581 - ) -> std::collections::HashMap<String, std::collections::HashSet<String>> { 2582 - self.namespace_deps.borrow().clone() 2583 - } 2584 - 2585 - /// Generate Cargo.toml features section from namespace dependencies 2586 - pub fn generate_cargo_features(&self, lib_rs_path: Option<&std::path::Path>) -> String { 2587 - use std::fmt::Write; 2588 - 2589 - let deps = self.namespace_deps.borrow(); 2590 - let mut all_namespaces: std::collections::HashSet<String> = 2591 - std::collections::HashSet::new(); 2592 - 2593 - // Collect all namespaces from the corpus (first two segments of each NSID) 2594 - for (nsid, _doc) in self.corpus.iter() { 2595 - let parts: Vec<_> = nsid.as_str().splitn(3, '.').collect(); 2596 - let namespace = if parts.len() >= 2 { 2597 - format!("{}.{}", parts[0], parts[1]) 2598 - } else { 2599 - nsid.to_string() 2600 - }; 2601 - all_namespaces.insert(namespace); 2602 - } 2603 - 2604 - // Also collect existing feature names from lib.rs 2605 - let mut existing_features = std::collections::HashSet::new(); 2606 - if let Some(lib_rs) = lib_rs_path { 2607 - if let Ok(content) = std::fs::read_to_string(lib_rs) { 2608 - for line in content.lines() { 2609 - if let Some(feature) = line 2610 - .trim() 2611 - .strip_prefix("#[cfg(feature = \"") 2612 - .and_then(|s| s.strip_suffix("\")]")) 2613 - { 2614 - existing_features.insert(feature.to_string()); 2615 - } 2616 - } 2617 - } 2618 - } 2619 - 2620 - let mut output = String::new(); 2621 - writeln!(&mut output, "# Generated namespace features").unwrap(); 2622 - 2623 - // Convert namespace to feature name (matching module path sanitization) 2624 - let to_feature_name = |ns: &str| { 2625 - ns.split('.') 2626 - .map(|segment| { 2627 - // Apply same sanitization as module names 2628 - let mut result = segment.replace('-', "_"); 2629 - // Prefix with underscore if starts with digit 2630 - if result.chars().next().map_or(false, |c| c.is_ascii_digit()) { 2631 - result.insert(0, '_'); 2632 - } 2633 - result 2634 - }) 2635 - .collect::<Vec<_>>() 2636 - .join("_") 2637 - }; 2638 - 2639 - // Collect all feature names (from corpus + existing lib.rs) 2640 - let mut all_feature_names = std::collections::HashSet::new(); 2641 - for ns in &all_namespaces { 2642 - all_feature_names.insert(to_feature_name(ns)); 2643 - } 2644 - all_feature_names.extend(existing_features); 2645 - 2646 - // Sort for consistent output 2647 - let mut feature_names: Vec<_> = all_feature_names.iter().collect(); 2648 - feature_names.sort(); 2649 - 2650 - // Map namespace to feature name for dependency lookup 2651 - let mut ns_to_feature: std::collections::HashMap<&str, String> = 2652 - std::collections::HashMap::new(); 2653 - for ns in &all_namespaces { 2654 - ns_to_feature.insert(ns.as_str(), to_feature_name(ns)); 2655 - } 2656 - 2657 - for feature_name in feature_names { 2658 - // Find corresponding namespace for this feature (if any) to look up deps 2659 - let feature_deps: Vec<String> = all_namespaces 2660 - .iter() 2661 - .find(|ns| to_feature_name(ns) == *feature_name) 2662 - .and_then(|ns| deps.get(ns.as_str())) 2663 - .map(|ns_deps| { 2664 - let mut dep_features: Vec<_> = ns_deps 2665 - .iter() 2666 - .map(|d| format!("\"{}\"", to_feature_name(d))) 2667 - .collect(); 2668 - dep_features.sort(); 2669 - dep_features 2670 - }) 2671 - .unwrap_or_default(); 2672 - 2673 - if !feature_deps.is_empty() { 2674 - writeln!( 2675 - &mut output, 2676 - "{} = [{}]", 2677 - feature_name, 2678 - feature_deps.join(", ") 2679 - ) 2680 - .unwrap(); 2681 - } else { 2682 - writeln!(&mut output, "{} = []", feature_name).unwrap(); 2683 - } 2684 - } 2685 - 2686 - output 2687 - } 2688 - } 2689 - 2690 - /// Enum variant kind for IntoStatic generation 2691 - #[derive(Debug, Clone)] 2692 - #[allow(dead_code)] 2693 - enum EnumVariantKind { 2694 - Unit, 2695 - Tuple, 2696 - Struct(Vec<String>), 2697 172 } 2698 173 2699 174 #[cfg(test)]
+114
crates/jacquard-lexicon/src/codegen/lifetime.rs
··· 1 + use super::CodeGenerator; 2 + use crate::lexicon::{ 3 + LexArrayItem, LexObjectProperty, LexString, LexStringFormat, LexUserType, 4 + }; 5 + 6 + impl<'c> CodeGenerator<'c> { 7 + /// Check if a property type needs a lifetime parameter 8 + pub(super) fn property_needs_lifetime(&self, prop: &LexObjectProperty<'static>) -> bool { 9 + match prop { 10 + LexObjectProperty::Boolean(_) | LexObjectProperty::Integer(_) => false, 11 + LexObjectProperty::String(s) => self.string_needs_lifetime(s), 12 + LexObjectProperty::Bytes(_) => false, // Bytes is owned 13 + LexObjectProperty::CidLink(_) 14 + | LexObjectProperty::Blob(_) 15 + | LexObjectProperty::Unknown(_) => true, 16 + LexObjectProperty::Array(array) => self.array_item_needs_lifetime(&array.items), 17 + LexObjectProperty::Object(_) => true, // Nested objects have lifetimes 18 + LexObjectProperty::Ref(ref_type) => { 19 + // Check if the ref target actually needs a lifetime 20 + self.ref_needs_lifetime(&ref_type.r#ref) 21 + } 22 + LexObjectProperty::Union(_) => true, // Unions generally have lifetimes 23 + } 24 + } 25 + 26 + /// Check if an array item type needs a lifetime parameter 27 + pub(super) fn array_item_needs_lifetime(&self, item: &LexArrayItem) -> bool { 28 + match item { 29 + LexArrayItem::Boolean(_) | LexArrayItem::Integer(_) => false, 30 + LexArrayItem::String(s) => self.string_needs_lifetime(s), 31 + LexArrayItem::Bytes(_) => false, 32 + LexArrayItem::CidLink(_) | LexArrayItem::Blob(_) | LexArrayItem::Unknown(_) => true, 33 + LexArrayItem::Object(_) => true, // Nested objects have lifetimes 34 + LexArrayItem::Ref(ref_type) => self.ref_needs_lifetime(&ref_type.r#ref), 35 + LexArrayItem::Union(_) => true, 36 + } 37 + } 38 + 39 + /// Check if a string type needs a lifetime parameter 40 + pub(super) fn string_needs_lifetime(&self, s: &LexString) -> bool { 41 + match s.format { 42 + Some(LexStringFormat::Datetime) 43 + | Some(LexStringFormat::Language) 44 + | Some(LexStringFormat::Tid) => false, 45 + _ => true, // Most string types borrow 46 + } 47 + } 48 + 49 + /// Check if a ref needs a lifetime parameter 50 + pub(super) fn ref_needs_lifetime(&self, ref_str: &str) -> bool { 51 + // Try to resolve the ref 52 + if let Some((_doc, def)) = self.corpus.resolve_ref(ref_str) { 53 + self.def_needs_lifetime(def) 54 + } else { 55 + // If we can't resolve it, assume it needs a lifetime (safe default) 56 + true 57 + } 58 + } 59 + 60 + /// Check if a lexicon def needs a lifetime parameter 61 + pub(super) fn def_needs_lifetime(&self, def: &LexUserType<'static>) -> bool { 62 + match def { 63 + // Records and Objects always have lifetimes now since they get #[lexicon] attribute 64 + LexUserType::Record(_) => true, 65 + LexUserType::Object(_) => true, 66 + LexUserType::Token(_) => false, 67 + LexUserType::String(s) => { 68 + // Check if it's a known values enum or a regular string 69 + if s.known_values.is_some() { 70 + // Known values enums have Other(CowStr<'a>) variant 71 + true 72 + } else { 73 + self.string_needs_lifetime(s) 74 + } 75 + } 76 + LexUserType::Integer(_) => false, 77 + LexUserType::Boolean(_) => false, 78 + LexUserType::Bytes(_) => false, 79 + LexUserType::CidLink(_) | LexUserType::Blob(_) | LexUserType::Unknown(_) => true, 80 + LexUserType::Array(array) => self.array_item_needs_lifetime(&array.items), 81 + LexUserType::XrpcQuery(_) 82 + | LexUserType::XrpcProcedure(_) 83 + | LexUserType::XrpcSubscription(_) => { 84 + // XRPC types generate multiple structs, not a single type we can reference 85 + // Shouldn't be referenced directly 86 + true 87 + } 88 + } 89 + } 90 + 91 + /// Check if xrpc params need a lifetime parameter 92 + pub(super) fn params_need_lifetime(&self, params: &crate::lexicon::LexXrpcParameters<'static>) -> bool { 93 + params.properties.values().any(|prop| { 94 + use crate::lexicon::LexXrpcParametersProperty; 95 + match prop { 96 + LexXrpcParametersProperty::Boolean(_) | LexXrpcParametersProperty::Integer(_) => { 97 + false 98 + } 99 + LexXrpcParametersProperty::String(s) => self.string_needs_lifetime(s), 100 + LexXrpcParametersProperty::Unknown(_) => true, 101 + LexXrpcParametersProperty::Array(arr) => { 102 + use crate::lexicon::LexPrimitiveArrayItem; 103 + match &arr.items { 104 + LexPrimitiveArrayItem::Boolean(_) | LexPrimitiveArrayItem::Integer(_) => { 105 + false 106 + } 107 + LexPrimitiveArrayItem::String(s) => self.string_needs_lifetime(s), 108 + LexPrimitiveArrayItem::Unknown(_) => true, 109 + } 110 + } 111 + } 112 + }) 113 + } 114 + }
+132
crates/jacquard-lexicon/src/codegen/names.rs
··· 1 + use super::utils::sanitize_name; 2 + use super::CodeGenerator; 3 + use heck::{ToPascalCase, ToSnakeCase}; 4 + 5 + impl<'c> CodeGenerator<'c> { 6 + /// Check if a single-variant union is self-referential 7 + pub(super) fn is_self_referential_union( 8 + &self, 9 + nsid: &str, 10 + parent_type_name: &str, 11 + union: &crate::lexicon::LexRefUnion, 12 + ) -> bool { 13 + if union.refs.len() != 1 { 14 + return false; 15 + } 16 + 17 + let ref_str = if union.refs[0].starts_with('#') { 18 + format!("{}{}", nsid, union.refs[0]) 19 + } else { 20 + union.refs[0].to_string() 21 + }; 22 + 23 + let (ref_nsid, ref_def) = if let Some((nsid_part, fragment)) = ref_str.split_once('#') { 24 + (nsid_part, fragment) 25 + } else { 26 + (ref_str.as_str(), "main") 27 + }; 28 + 29 + let ref_type_name = self.def_to_type_name(ref_nsid, ref_def); 30 + ref_type_name == parent_type_name 31 + } 32 + 33 + /// Helper to generate field-based type name with collision detection 34 + pub(super) fn generate_field_type_name( 35 + &self, 36 + nsid: &str, 37 + parent_type_name: &str, 38 + field_name: &str, 39 + suffix: &str, // "" for union/object, "Item" for array unions 40 + ) -> String { 41 + let base_name = format!("{}{}{}", parent_type_name, field_name.to_pascal_case(), suffix); 42 + 43 + // Check for collisions with lexicon defs 44 + if let Some(doc) = self.corpus.get(nsid) { 45 + let def_names: std::collections::HashSet<String> = doc 46 + .defs 47 + .keys() 48 + .map(|name| self.def_to_type_name(nsid, name.as_ref())) 49 + .collect(); 50 + 51 + if def_names.contains(&base_name) { 52 + // Use "Union" suffix for union types, "Record" for objects 53 + let disambiguator = if suffix.is_empty() && !parent_type_name.is_empty() { 54 + "Union" 55 + } else { 56 + "Record" 57 + }; 58 + return format!("{}{}{}{}", parent_type_name, disambiguator, field_name.to_pascal_case(), suffix); 59 + } 60 + } 61 + 62 + base_name 63 + } 64 + 65 + /// Convert lexicon def name to Rust type name 66 + pub(super) fn def_to_type_name(&self, nsid: &str, def_name: &str) -> String { 67 + if def_name == "main" { 68 + // Use last segment of NSID 69 + let base_name = nsid.split('.').last().unwrap().to_pascal_case(); 70 + 71 + // Check if any other def would collide with this name 72 + if let Some(doc) = self.corpus.get(nsid) { 73 + let has_collision = doc.defs.keys().any(|other_def| { 74 + let other_def_str: &str = other_def.as_ref(); 75 + other_def_str != "main" && other_def_str.to_pascal_case() == base_name 76 + }); 77 + 78 + if has_collision { 79 + return format!("{}Record", base_name); 80 + } 81 + } 82 + 83 + base_name 84 + } else { 85 + def_name.to_pascal_case() 86 + } 87 + } 88 + 89 + /// Convert NSID to file path relative to output directory 90 + /// 91 + /// - `app.bsky.feed.post` → `app_bsky/feed/post.rs` 92 + /// - `com.atproto.label.defs` → `com_atproto/label.rs` (defs go in parent) 93 + pub(super) fn nsid_to_file_path(&self, nsid: &str) -> std::path::PathBuf { 94 + let parts: Vec<&str> = nsid.split('.').collect(); 95 + 96 + if parts.len() < 2 { 97 + // Shouldn't happen with valid NSIDs, but handle gracefully 98 + return format!("{}.rs", sanitize_name(parts[0])).into(); 99 + } 100 + 101 + let last = parts.last().unwrap(); 102 + 103 + if *last == "defs" && parts.len() >= 3 { 104 + // defs go in parent module: com.atproto.label.defs → com_atproto/label.rs 105 + let first_two = format!("{}_{}", sanitize_name(parts[0]), sanitize_name(parts[1])); 106 + if parts.len() == 3 { 107 + // com.atproto.defs → com_atproto.rs 108 + format!("{}.rs", first_two).into() 109 + } else { 110 + // com.atproto.label.defs → com_atproto/label.rs 111 + let middle: Vec<&str> = parts[2..parts.len() - 1].iter().copied().collect(); 112 + let mut path = std::path::PathBuf::from(first_two); 113 + for segment in &middle[..middle.len() - 1] { 114 + path.push(sanitize_name(segment)); 115 + } 116 + path.push(format!("{}.rs", sanitize_name(middle.last().unwrap()))); 117 + path 118 + } 119 + } else { 120 + // Regular path: app.bsky.feed.post → app_bsky/feed/post.rs 121 + let first_two = format!("{}_{}", sanitize_name(parts[0]), sanitize_name(parts[1])); 122 + let mut path = std::path::PathBuf::from(first_two); 123 + 124 + for segment in &parts[2..parts.len() - 1] { 125 + path.push(sanitize_name(segment)); 126 + } 127 + 128 + path.push(format!("{}.rs", sanitize_name(&last.to_snake_case()))); 129 + path 130 + } 131 + } 132 + }
+329
crates/jacquard-lexicon/src/codegen/output.rs
··· 1 + use crate::error::{CodegenError, Result}; 2 + use proc_macro2::TokenStream; 3 + use quote::quote; 4 + use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; 5 + 6 + use super::utils::{make_ident, sanitize_name}; 7 + use super::CodeGenerator; 8 + 9 + impl<'c> CodeGenerator<'c> { 10 + /// Generate all code for the corpus, organized by file 11 + /// Returns a map of file paths to (tokens, optional NSID) 12 + pub fn generate_all( 13 + &self, 14 + ) -> Result<BTreeMap<std::path::PathBuf, (TokenStream, Option<String>)>> { 15 + let mut file_contents: BTreeMap<std::path::PathBuf, Vec<TokenStream>> = BTreeMap::new(); 16 + let mut file_nsids: BTreeMap<std::path::PathBuf, String> = BTreeMap::new(); 17 + 18 + // Generate code for all lexicons 19 + for (nsid, doc) in self.corpus.iter() { 20 + let file_path = self.nsid_to_file_path(nsid.as_ref()); 21 + 22 + // Track which NSID this file is for 23 + file_nsids.insert(file_path.clone(), nsid.to_string()); 24 + 25 + for (def_name, def) in &doc.defs { 26 + let tokens = self.generate_def(nsid.as_ref(), def_name.as_ref(), def)?; 27 + file_contents 28 + .entry(file_path.clone()) 29 + .or_default() 30 + .push(tokens); 31 + } 32 + } 33 + 34 + // Combine all tokens for each file 35 + let mut result = BTreeMap::new(); 36 + for (path, tokens_vec) in file_contents { 37 + let nsid = file_nsids.get(&path).cloned(); 38 + result.insert(path, (quote! { #(#tokens_vec)* }, nsid)); 39 + } 40 + 41 + Ok(result) 42 + } 43 + 44 + /// Generate parent module files with pub mod declarations 45 + pub fn generate_module_tree( 46 + &self, 47 + file_map: &BTreeMap<std::path::PathBuf, (TokenStream, Option<String>)>, 48 + defs_only: &BTreeMap<std::path::PathBuf, (TokenStream, Option<String>)>, 49 + ) -> BTreeMap<std::path::PathBuf, (TokenStream, Option<String>)> { 50 + // Track what modules each directory needs to declare 51 + // Key: directory path, Value: set of module names (file stems) 52 + let mut dir_modules: BTreeMap<std::path::PathBuf, BTreeSet<String>> = BTreeMap::new(); 53 + 54 + // Collect all parent directories that have files 55 + let mut all_dirs: BTreeSet<std::path::PathBuf> = BTreeSet::new(); 56 + for path in file_map.keys() { 57 + if let Some(parent_dir) = path.parent() { 58 + all_dirs.insert(parent_dir.to_path_buf()); 59 + } 60 + } 61 + 62 + for path in file_map.keys() { 63 + if let Some(parent_dir) = path.parent() { 64 + if let Some(file_stem) = path.file_stem().and_then(|s| s.to_str()) { 65 + // Skip mod.rs and lib.rs - they're module files, not modules to declare 66 + if file_stem == "mod" || file_stem == "lib" { 67 + continue; 68 + } 69 + 70 + // Always add the module declaration to parent 71 + dir_modules 72 + .entry(parent_dir.to_path_buf()) 73 + .or_default() 74 + .insert(file_stem.to_string()); 75 + } 76 + } 77 + } 78 + 79 + // Generate module files 80 + let mut result = BTreeMap::new(); 81 + 82 + for (dir, module_names) in dir_modules { 83 + let mod_file_path = if dir.components().count() == 0 { 84 + // Root directory -> lib.rs for library crates 85 + std::path::PathBuf::from("lib.rs") 86 + } else { 87 + // Subdirectory: app_bsky/feed -> app_bsky/feed.rs (Rust 2018 style) 88 + let dir_name = dir.file_name().and_then(|s| s.to_str()).unwrap_or("mod"); 89 + let sanitized_dir_name = sanitize_name(dir_name); 90 + let mut path = dir 91 + .parent() 92 + .unwrap_or_else(|| std::path::Path::new("")) 93 + .to_path_buf(); 94 + path.push(format!("{}.rs", sanitized_dir_name)); 95 + path 96 + }; 97 + 98 + let is_root = dir.components().count() == 0; 99 + let mods: Vec<_> = module_names 100 + .iter() 101 + .map(|name| { 102 + let ident = make_ident(name); 103 + if is_root { 104 + // Top-level modules get feature gates 105 + quote! { 106 + #[cfg(feature = #name)] 107 + pub mod #ident; 108 + } 109 + } else { 110 + quote! { pub mod #ident; } 111 + } 112 + }) 113 + .collect(); 114 + 115 + // If this file already exists in defs_only (e.g., from defs), merge the content 116 + let module_tokens = quote! { #(#mods)* }; 117 + if let Some((existing_tokens, nsid)) = defs_only.get(&mod_file_path) { 118 + // Put module declarations FIRST, then existing defs content 119 + result.insert( 120 + mod_file_path, 121 + (quote! { #module_tokens #existing_tokens }, nsid.clone()), 122 + ); 123 + } else { 124 + result.insert(mod_file_path, (module_tokens, None)); 125 + } 126 + } 127 + 128 + result 129 + } 130 + 131 + /// Write all generated code to disk 132 + pub fn write_to_disk(&self, output_dir: &std::path::Path) -> Result<()> { 133 + // Generate all code (defs only) 134 + let defs_files = self.generate_all()?; 135 + let mut all_files = defs_files.clone(); 136 + 137 + // Generate module tree iteratively until no new files appear 138 + loop { 139 + let module_map = self.generate_module_tree(&all_files, &defs_files); 140 + let old_count = all_files.len(); 141 + 142 + // Merge new module files 143 + for (path, tokens) in module_map { 144 + all_files.insert(path, tokens); 145 + } 146 + 147 + if all_files.len() == old_count { 148 + // No new files added 149 + break; 150 + } 151 + } 152 + 153 + // Write to disk 154 + for (path, (tokens, nsid)) in all_files { 155 + let full_path = output_dir.join(&path); 156 + 157 + // Create parent directories 158 + if let Some(parent) = full_path.parent() { 159 + std::fs::create_dir_all(parent).map_err(|e| CodegenError::Other { 160 + message: format!("Failed to create directory {:?}: {}", parent, e), 161 + source: None, 162 + })?; 163 + } 164 + 165 + // Format code 166 + let file: syn::File = syn::parse2(tokens.clone()).map_err(|e| CodegenError::Other { 167 + message: format!( 168 + "Failed to parse tokens for {:?}: {}\nTokens: {}", 169 + path, e, tokens 170 + ), 171 + source: None, 172 + })?; 173 + let mut formatted = prettyplease::unparse(&file); 174 + 175 + // Add blank lines between top-level items for better readability 176 + let lines: Vec<&str> = formatted.lines().collect(); 177 + let mut result_lines = Vec::new(); 178 + 179 + for (i, line) in lines.iter().enumerate() { 180 + result_lines.push(*line); 181 + 182 + // Add blank line after closing braces that are at column 0 (top-level items) 183 + if *line == "}" && i + 1 < lines.len() && !lines[i + 1].is_empty() { 184 + result_lines.push(""); 185 + } 186 + 187 + // Add blank line after last pub mod declaration before structs/enums 188 + if line.starts_with("pub mod ") && i + 1 < lines.len() { 189 + let next_line = lines[i + 1]; 190 + if !next_line.starts_with("pub mod ") && !next_line.is_empty() { 191 + result_lines.push(""); 192 + } 193 + } 194 + } 195 + 196 + formatted = result_lines.join("\n"); 197 + 198 + // Add header comment 199 + let header = if let Some(nsid) = nsid { 200 + format!( 201 + "// @generated by jacquard-lexicon. DO NOT EDIT.\n//\n// Lexicon: {}\n//\n// This file was automatically generated from Lexicon schemas.\n// Any manual changes will be overwritten on the next regeneration.\n\n", 202 + nsid 203 + ) 204 + } else { 205 + "// @generated by jacquard-lexicon. DO NOT EDIT.\n//\n// This file was automatically generated from Lexicon schemas.\n// Any manual changes will be overwritten on the next regeneration.\n\n".to_string() 206 + }; 207 + formatted = format!("{}{}", header, formatted); 208 + 209 + // Write file 210 + std::fs::write(&full_path, formatted).map_err(|e| CodegenError::Other { 211 + message: format!("Failed to write file {:?}: {}", full_path, e), 212 + source: None, 213 + })?; 214 + } 215 + 216 + Ok(()) 217 + } 218 + 219 + /// Get namespace dependencies collected during code generation 220 + pub fn get_namespace_dependencies( 221 + &self, 222 + ) -> HashMap<String, HashSet<String>> { 223 + self.namespace_deps.borrow().clone() 224 + } 225 + 226 + /// Generate Cargo.toml features section from namespace dependencies 227 + pub fn generate_cargo_features(&self, lib_rs_path: Option<&std::path::Path>) -> String { 228 + use std::fmt::Write; 229 + 230 + let deps = self.namespace_deps.borrow(); 231 + let mut all_namespaces: HashSet<String> = 232 + HashSet::new(); 233 + 234 + // Collect all namespaces from the corpus (first two segments of each NSID) 235 + for (nsid, _doc) in self.corpus.iter() { 236 + let parts: Vec<_> = nsid.as_str().splitn(3, '.').collect(); 237 + let namespace = if parts.len() >= 2 { 238 + format!("{}.{}", parts[0], parts[1]) 239 + } else { 240 + nsid.to_string() 241 + }; 242 + all_namespaces.insert(namespace); 243 + } 244 + 245 + // Also collect existing feature names from lib.rs 246 + let mut existing_features = HashSet::new(); 247 + if let Some(lib_rs) = lib_rs_path { 248 + if let Ok(content) = std::fs::read_to_string(lib_rs) { 249 + for line in content.lines() { 250 + if let Some(feature) = line 251 + .trim() 252 + .strip_prefix("#[cfg(feature = \"") 253 + .and_then(|s| s.strip_suffix("\")]")) 254 + { 255 + existing_features.insert(feature.to_string()); 256 + } 257 + } 258 + } 259 + } 260 + 261 + let mut output = String::new(); 262 + writeln!(&mut output, "# Generated namespace features").unwrap(); 263 + 264 + // Convert namespace to feature name (matching module path sanitization) 265 + let to_feature_name = |ns: &str| { 266 + ns.split('.') 267 + .map(|segment| { 268 + // Apply same sanitization as module names 269 + let mut result = segment.replace('-', "_"); 270 + // Prefix with underscore if starts with digit 271 + if result.chars().next().map_or(false, |c| c.is_ascii_digit()) { 272 + result.insert(0, '_'); 273 + } 274 + result 275 + }) 276 + .collect::<Vec<_>>() 277 + .join("_") 278 + }; 279 + 280 + // Collect all feature names (from corpus + existing lib.rs) 281 + let mut all_feature_names = HashSet::new(); 282 + for ns in &all_namespaces { 283 + all_feature_names.insert(to_feature_name(ns)); 284 + } 285 + all_feature_names.extend(existing_features); 286 + 287 + // Sort for consistent output 288 + let mut feature_names: Vec<_> = all_feature_names.iter().collect(); 289 + feature_names.sort(); 290 + 291 + // Map namespace to feature name for dependency lookup 292 + let mut ns_to_feature: HashMap<&str, String> = 293 + HashMap::new(); 294 + for ns in &all_namespaces { 295 + ns_to_feature.insert(ns.as_str(), to_feature_name(ns)); 296 + } 297 + 298 + for feature_name in feature_names { 299 + // Find corresponding namespace for this feature (if any) to look up deps 300 + let feature_deps: Vec<String> = all_namespaces 301 + .iter() 302 + .find(|ns| to_feature_name(ns) == *feature_name) 303 + .and_then(|ns| deps.get(ns.as_str())) 304 + .map(|ns_deps| { 305 + let mut dep_features: Vec<_> = ns_deps 306 + .iter() 307 + .map(|d| format!("\"{}\"", to_feature_name(d))) 308 + .collect(); 309 + dep_features.sort(); 310 + dep_features 311 + }) 312 + .unwrap_or_default(); 313 + 314 + if !feature_deps.is_empty() { 315 + writeln!( 316 + &mut output, 317 + "{} = [{}]", 318 + feature_name, 319 + feature_deps.join(", ") 320 + ) 321 + .unwrap(); 322 + } else { 323 + writeln!(&mut output, "{} = []", feature_name).unwrap(); 324 + } 325 + } 326 + 327 + output 328 + } 329 + }
+759
crates/jacquard-lexicon/src/codegen/structs.rs
··· 1 + use crate::error::Result; 2 + use crate::lexicon::{ 3 + LexArrayItem, LexInteger, LexObject, LexObjectProperty, LexRecord, LexString, 4 + }; 5 + use heck::{ToPascalCase, ToSnakeCase}; 6 + use proc_macro2::TokenStream; 7 + use quote::quote; 8 + 9 + use super::utils::{make_ident, value_to_variant_name}; 10 + use super::CodeGenerator; 11 + 12 + /// Enum variant kind for IntoStatic generation 13 + #[derive(Debug, Clone)] 14 + #[allow(dead_code)] 15 + pub(super) enum EnumVariantKind { 16 + Unit, 17 + Tuple, 18 + Struct(Vec<String>), 19 + } 20 + 21 + impl<'c> CodeGenerator<'c> { 22 + pub(super) fn generate_record( 23 + &self, 24 + nsid: &str, 25 + def_name: &str, 26 + record: &LexRecord<'static>, 27 + ) -> Result<TokenStream> { 28 + match &record.record { 29 + crate::lexicon::LexRecordRecord::Object(obj) => { 30 + let type_name = self.def_to_type_name(nsid, def_name); 31 + let ident = syn::Ident::new(&type_name, proc_macro2::Span::call_site()); 32 + 33 + // Generate main struct fields 34 + let fields = self.generate_object_fields(nsid, &type_name, obj, false)?; 35 + let doc = self.generate_doc_comment(record.description.as_ref()); 36 + 37 + // Records always get a lifetime since they have the #[lexicon] attribute 38 + // which adds extra_data: BTreeMap<..., Data<'a>> 39 + let struct_def = quote! { 40 + #doc 41 + #[jacquard_derive::lexicon] 42 + #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq, jacquard_derive::IntoStatic)] 43 + #[serde(rename_all = "camelCase")] 44 + pub struct #ident<'a> { 45 + #fields 46 + } 47 + }; 48 + 49 + // Generate union types and nested object types for this record 50 + let mut unions = Vec::new(); 51 + for (field_name, field_type) in &obj.properties { 52 + match field_type { 53 + LexObjectProperty::Union(union) => { 54 + // Skip empty, single-variant unions unless they're self-referential 55 + if !union.refs.is_empty() && (union.refs.len() > 1 || self.is_self_referential_union(nsid, &type_name, union)) { 56 + let union_name = self.generate_field_type_name(nsid, &type_name, field_name, ""); 57 + let refs: Vec<_> = union.refs.iter().cloned().collect(); 58 + let union_def = 59 + self.generate_union(nsid, &union_name, &refs, None, union.closed)?; 60 + unions.push(union_def); 61 + } 62 + } 63 + LexObjectProperty::Object(nested_obj) => { 64 + let object_name = self.generate_field_type_name(nsid, &type_name, field_name, ""); 65 + let obj_def = self.generate_object(nsid, &object_name, nested_obj)?; 66 + unions.push(obj_def); 67 + } 68 + LexObjectProperty::Array(array) => { 69 + if let LexArrayItem::Union(union) = &array.items { 70 + // Skip single-variant array unions 71 + if union.refs.len() > 1 { 72 + let union_name = self.generate_field_type_name(nsid, &type_name, field_name, "Item"); 73 + let refs: Vec<_> = union.refs.iter().cloned().collect(); 74 + let union_def = self.generate_union(nsid, &union_name, &refs, None, union.closed)?; 75 + unions.push(union_def); 76 + } 77 + } 78 + } 79 + _ => {} 80 + } 81 + } 82 + 83 + // Generate Collection trait impl 84 + let collection_impl = quote! { 85 + impl jacquard_common::types::collection::Collection for #ident<'_> { 86 + const NSID: &'static str = #nsid; 87 + } 88 + }; 89 + 90 + Ok(quote! { 91 + #struct_def 92 + #(#unions)* 93 + #collection_impl 94 + }) 95 + } 96 + } 97 + } 98 + 99 + /// Generate an object type 100 + pub(super) fn generate_object( 101 + &self, 102 + nsid: &str, 103 + def_name: &str, 104 + obj: &LexObject<'static>, 105 + ) -> Result<TokenStream> { 106 + let type_name = self.def_to_type_name(nsid, def_name); 107 + let ident = syn::Ident::new(&type_name, proc_macro2::Span::call_site()); 108 + 109 + let fields = self.generate_object_fields(nsid, &type_name, obj, false)?; 110 + let doc = self.generate_doc_comment(obj.description.as_ref()); 111 + 112 + // Objects always get a lifetime since they have the #[lexicon] attribute 113 + // which adds extra_data: BTreeMap<..., Data<'a>> 114 + let struct_def = quote! { 115 + #doc 116 + #[jacquard_derive::lexicon] 117 + #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq, jacquard_derive::IntoStatic)] 118 + #[serde(rename_all = "camelCase")] 119 + pub struct #ident<'a> { 120 + #fields 121 + } 122 + }; 123 + 124 + // Generate union types and nested object types for this object 125 + let mut unions = Vec::new(); 126 + for (field_name, field_type) in &obj.properties { 127 + match field_type { 128 + LexObjectProperty::Union(union) => { 129 + // Skip empty, single-variant unions unless they're self-referential 130 + if !union.refs.is_empty() && (union.refs.len() > 1 || self.is_self_referential_union(nsid, &type_name, union)) { 131 + let union_name = self.generate_field_type_name(nsid, &type_name, field_name, ""); 132 + let refs: Vec<_> = union.refs.iter().cloned().collect(); 133 + let union_def = 134 + self.generate_union(nsid, &union_name, &refs, None, union.closed)?; 135 + unions.push(union_def); 136 + } 137 + } 138 + LexObjectProperty::Object(nested_obj) => { 139 + let object_name = self.generate_field_type_name(nsid, &type_name, field_name, ""); 140 + let obj_def = self.generate_object(nsid, &object_name, nested_obj)?; 141 + unions.push(obj_def); 142 + } 143 + LexObjectProperty::Array(array) => { 144 + if let LexArrayItem::Union(union) = &array.items { 145 + // Skip single-variant array unions 146 + if union.refs.len() > 1 { 147 + let union_name = self.generate_field_type_name(nsid, &type_name, field_name, "Item"); 148 + let refs: Vec<_> = union.refs.iter().cloned().collect(); 149 + let union_def = self.generate_union(nsid, &union_name, &refs, None, union.closed)?; 150 + unions.push(union_def); 151 + } 152 + } 153 + } 154 + _ => {} 155 + } 156 + } 157 + 158 + Ok(quote! { 159 + #struct_def 160 + #(#unions)* 161 + }) 162 + } 163 + 164 + /// Generate fields for an object 165 + pub(super) fn generate_object_fields( 166 + &self, 167 + nsid: &str, 168 + parent_type_name: &str, 169 + obj: &LexObject<'static>, 170 + is_builder: bool, 171 + ) -> Result<TokenStream> { 172 + let required = obj.required.as_ref().map(|r| r.as_slice()).unwrap_or(&[]); 173 + 174 + let mut fields = Vec::new(); 175 + for (field_name, field_type) in &obj.properties { 176 + let is_required = required.contains(field_name); 177 + let field_tokens = self.generate_field( 178 + nsid, 179 + parent_type_name, 180 + field_name, 181 + field_type, 182 + is_required, 183 + is_builder, 184 + )?; 185 + fields.push(field_tokens); 186 + } 187 + 188 + Ok(quote! { #(#fields)* }) 189 + } 190 + 191 + /// Generate a single field 192 + pub(super) fn generate_field( 193 + &self, 194 + nsid: &str, 195 + parent_type_name: &str, 196 + field_name: &str, 197 + field_type: &LexObjectProperty<'static>, 198 + is_required: bool, 199 + is_builder: bool, 200 + ) -> Result<TokenStream> { 201 + if field_name.is_empty() { 202 + eprintln!( 203 + "Warning: Empty field name in lexicon '{}' type '{}', using 'unknown' as fallback", 204 + nsid, parent_type_name 205 + ); 206 + } 207 + let field_ident = make_ident(&field_name.to_snake_case()); 208 + 209 + let rust_type = 210 + self.property_to_rust_type(nsid, parent_type_name, field_name, field_type)?; 211 + let needs_lifetime = self.property_needs_lifetime(field_type); 212 + 213 + // Check if this is a CowStr field for builder(into) attribute 214 + let is_cowstr = matches!(field_type, LexObjectProperty::String(s) if s.format.is_none()); 215 + 216 + let rust_type = if is_required { 217 + rust_type 218 + } else { 219 + quote! { std::option::Option<#rust_type> } 220 + }; 221 + 222 + // Extract description from field type 223 + let description = match field_type { 224 + LexObjectProperty::Ref(r) => r.description.as_ref(), 225 + LexObjectProperty::Union(u) => u.description.as_ref(), 226 + LexObjectProperty::Bytes(b) => b.description.as_ref(), 227 + LexObjectProperty::CidLink(c) => c.description.as_ref(), 228 + LexObjectProperty::Array(a) => a.description.as_ref(), 229 + LexObjectProperty::Blob(b) => b.description.as_ref(), 230 + LexObjectProperty::Object(o) => o.description.as_ref(), 231 + LexObjectProperty::Boolean(b) => b.description.as_ref(), 232 + LexObjectProperty::Integer(i) => i.description.as_ref(), 233 + LexObjectProperty::String(s) => s.description.as_ref(), 234 + LexObjectProperty::Unknown(u) => u.description.as_ref(), 235 + }; 236 + let doc = self.generate_doc_comment(description); 237 + 238 + let mut attrs = Vec::new(); 239 + 240 + if !is_required { 241 + attrs.push(quote! { #[serde(skip_serializing_if = "std::option::Option::is_none")] }); 242 + } 243 + 244 + // Add serde(borrow) to all fields with lifetimes 245 + if needs_lifetime { 246 + attrs.push(quote! { #[serde(borrow)] }); 247 + } 248 + 249 + // Add builder(into) for CowStr fields to allow String, &str, etc., but only for builder structs 250 + if is_builder && is_cowstr { 251 + attrs.push(quote! { #[builder(into)] }); 252 + } 253 + 254 + Ok(quote! { 255 + #doc 256 + #(#attrs)* 257 + pub #field_ident: #rust_type, 258 + }) 259 + } 260 + 261 + /// Generate a union enum for refs 262 + pub fn generate_union( 263 + &self, 264 + current_nsid: &str, 265 + union_name: &str, 266 + refs: &[jacquard_common::CowStr<'static>], 267 + description: Option<&str>, 268 + closed: Option<bool>, 269 + ) -> Result<TokenStream> { 270 + let enum_ident = syn::Ident::new(union_name, proc_macro2::Span::call_site()); 271 + 272 + // Extract namespace prefix from current NSID (first two segments: "sh.weaver" from "sh.weaver.embed.recordWithMedia") 273 + let parts: Vec<_> = current_nsid.splitn(3, '.').collect(); 274 + let current_namespace = if parts.len() >= 2 { 275 + format!("{}.{}", parts[0], parts[1]) 276 + } else { 277 + current_nsid.to_string() 278 + }; 279 + 280 + // First pass: collect all variant names and detect collisions 281 + #[derive(Debug)] 282 + struct VariantInfo { 283 + ref_str: String, 284 + ref_nsid: String, 285 + simple_name: String, 286 + is_current_namespace: bool, 287 + } 288 + 289 + let mut variant_infos = Vec::new(); 290 + for ref_str in refs { 291 + // Normalize local refs (starting with #) by prepending current NSID 292 + let normalized_ref = if ref_str.starts_with('#') { 293 + format!("{}{}", current_nsid, ref_str) 294 + } else { 295 + ref_str.to_string() 296 + }; 297 + 298 + // Parse ref to get NSID and def name 299 + let (ref_nsid_str, ref_def) = if let Some((nsid, fragment)) = normalized_ref.split_once('#') { 300 + (nsid, fragment) 301 + } else { 302 + (normalized_ref.as_str(), "main") 303 + }; 304 + 305 + // Skip unknown refs - they'll be handled by Unknown variant 306 + if !self.corpus.ref_exists(&normalized_ref) { 307 + continue; 308 + } 309 + 310 + // Check if ref is in current namespace and if it's the same module 311 + let is_current_namespace = ref_nsid_str.starts_with(&current_namespace); 312 + let is_same_module = ref_nsid_str == current_nsid; 313 + 314 + // Generate simple variant name (without namespace prefix) 315 + let last_segment = ref_nsid_str.split('.').last().unwrap(); 316 + let simple_name = if ref_def == "main" { 317 + // For main, use the last NSID segment 318 + // e.g. app.bsky.embed.images#main -> Images 319 + last_segment.to_pascal_case() 320 + } else if last_segment == "defs" { 321 + // For defs modules, just use the fragment name without "Defs" prefix 322 + // e.g. app.bsky.embed.defs#images -> Images (not DefsImages) 323 + ref_def.to_pascal_case() 324 + } else if is_same_module { 325 + // For same-module refs, just use the fragment name to avoid redundancy 326 + // e.g. sh.weaver.embed.records#viewRecord in records.rs -> ViewRecord (not RecordsViewRecord) 327 + ref_def.to_pascal_case() 328 + } else { 329 + // For other fragments, include the last NSID segment to avoid collisions 330 + // e.g. app.bsky.embed.images#view -> ImagesView 331 + // app.bsky.embed.video#view -> VideoView 332 + format!("{}{}", last_segment.to_pascal_case(), ref_def.to_pascal_case()) 333 + }; 334 + 335 + variant_infos.push(VariantInfo { 336 + ref_str: normalized_ref.clone(), 337 + ref_nsid: ref_nsid_str.to_string(), 338 + simple_name, 339 + is_current_namespace, 340 + }); 341 + } 342 + 343 + // Second pass: detect collisions and disambiguate 344 + use std::collections::HashMap; 345 + let mut name_counts: HashMap<String, usize> = HashMap::new(); 346 + for info in &variant_infos { 347 + *name_counts.entry(info.simple_name.clone()).or_insert(0) += 1; 348 + } 349 + 350 + let mut variants = Vec::new(); 351 + for info in variant_infos { 352 + let has_collision = name_counts.get(&info.simple_name).copied().unwrap_or(0) > 1; 353 + 354 + // Track namespace dependency for foreign refs 355 + if !info.is_current_namespace { 356 + let parts: Vec<_> = info.ref_nsid.splitn(3, '.').collect(); 357 + let foreign_namespace = if parts.len() >= 2 { 358 + format!("{}.{}", parts[0], parts[1]) 359 + } else { 360 + info.ref_nsid.to_string() 361 + }; 362 + self.namespace_deps 363 + .borrow_mut() 364 + .entry(current_namespace.clone()) 365 + .or_default() 366 + .insert(foreign_namespace); 367 + } 368 + 369 + // Disambiguate: add second NSID segment prefix only to foreign refs when there's a collision 370 + let variant_name = if has_collision && !info.is_current_namespace { 371 + // Get second segment (namespace identifier: "bsky" from "app.bsky.embed.images") 372 + let segments: Vec<&str> = info.ref_nsid.split('.').collect(); 373 + let prefix = if segments.len() >= 2 { 374 + segments[1].to_pascal_case() 375 + } else { 376 + // Fallback: use first segment if only one exists 377 + segments[0].to_pascal_case() 378 + }; 379 + format!("{}{}", prefix, info.simple_name) 380 + } else { 381 + info.simple_name.clone() 382 + }; 383 + 384 + let variant_ident = syn::Ident::new(&variant_name, proc_macro2::Span::call_site()); 385 + 386 + // Get the Rust type for this ref 387 + let rust_type = self.ref_to_rust_type(&info.ref_str)?; 388 + 389 + // Add serde rename for the full NSID 390 + let ref_str_literal = &info.ref_str; 391 + variants.push(quote! { 392 + #[serde(rename = #ref_str_literal)] 393 + #variant_ident(Box<#rust_type>) 394 + }); 395 + } 396 + 397 + let doc = description 398 + .map(|d| quote! { #[doc = #d] }) 399 + .unwrap_or_else(|| quote! {}); 400 + 401 + // Only add open_union if not closed 402 + let is_open = closed != Some(true); 403 + 404 + if is_open { 405 + Ok(quote! { 406 + #doc 407 + #[jacquard_derive::open_union] 408 + #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq, jacquard_derive::IntoStatic)] 409 + #[serde(tag = "$type")] 410 + #[serde(bound(deserialize = "'de: 'a"))] 411 + pub enum #enum_ident<'a> { 412 + #(#variants,)* 413 + } 414 + }) 415 + } else { 416 + Ok(quote! { 417 + #doc 418 + #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq, jacquard_derive::IntoStatic)] 419 + #[serde(tag = "$type")] 420 + #[serde(bound(deserialize = "'de: 'a"))] 421 + pub enum #enum_ident<'a> { 422 + #(#variants,)* 423 + } 424 + }) 425 + } 426 + } 427 + 428 + /// Generate enum for string with known values 429 + pub(super) fn generate_known_values_enum( 430 + &self, 431 + nsid: &str, 432 + def_name: &str, 433 + string: &LexString<'static>, 434 + ) -> Result<TokenStream> { 435 + let type_name = self.def_to_type_name(nsid, def_name); 436 + let ident = syn::Ident::new(&type_name, proc_macro2::Span::call_site()); 437 + 438 + let known_values = string.known_values.as_ref().unwrap(); 439 + let mut variants = Vec::new(); 440 + let mut from_str_arms = Vec::new(); 441 + let mut as_str_arms = Vec::new(); 442 + 443 + for value in known_values { 444 + // Convert value to valid Rust identifier 445 + let value_str = value.as_ref(); 446 + let variant_name = value_to_variant_name(value_str); 447 + let variant_ident = syn::Ident::new(&variant_name, proc_macro2::Span::call_site()); 448 + 449 + variants.push(quote! { 450 + #variant_ident 451 + }); 452 + 453 + from_str_arms.push(quote! { 454 + #value_str => Self::#variant_ident 455 + }); 456 + 457 + as_str_arms.push(quote! { 458 + Self::#variant_ident => #value_str 459 + }); 460 + } 461 + 462 + let doc = self.generate_doc_comment(string.description.as_ref()); 463 + 464 + // Generate IntoStatic impl 465 + let variant_info: Vec<(String, EnumVariantKind)> = known_values 466 + .iter() 467 + .map(|value| { 468 + let variant_name = value_to_variant_name(value.as_ref()); 469 + (variant_name, EnumVariantKind::Unit) 470 + }) 471 + .chain(std::iter::once(( 472 + "Other".to_string(), 473 + EnumVariantKind::Tuple, 474 + ))) 475 + .collect(); 476 + let into_static_impl = 477 + self.generate_into_static_for_enum(&type_name, &variant_info, true, false); 478 + 479 + Ok(quote! { 480 + #doc 481 + #[derive(Debug, Clone, PartialEq, Eq, Hash)] 482 + pub enum #ident<'a> { 483 + #(#variants,)* 484 + Other(jacquard_common::CowStr<'a>), 485 + } 486 + 487 + impl<'a> #ident<'a> { 488 + pub fn as_str(&self) -> &str { 489 + match self { 490 + #(#as_str_arms,)* 491 + Self::Other(s) => s.as_ref(), 492 + } 493 + } 494 + } 495 + 496 + impl<'a> From<&'a str> for #ident<'a> { 497 + fn from(s: &'a str) -> Self { 498 + match s { 499 + #(#from_str_arms,)* 500 + _ => Self::Other(jacquard_common::CowStr::from(s)), 501 + } 502 + } 503 + } 504 + 505 + impl<'a> From<String> for #ident<'a> { 506 + fn from(s: String) -> Self { 507 + match s.as_str() { 508 + #(#from_str_arms,)* 509 + _ => Self::Other(jacquard_common::CowStr::from(s)), 510 + } 511 + } 512 + } 513 + 514 + impl<'a> AsRef<str> for #ident<'a> { 515 + fn as_ref(&self) -> &str { 516 + self.as_str() 517 + } 518 + } 519 + 520 + impl<'a> serde::Serialize for #ident<'a> { 521 + fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> 522 + where 523 + S: serde::Serializer, 524 + { 525 + serializer.serialize_str(self.as_str()) 526 + } 527 + } 528 + 529 + impl<'de, 'a> serde::Deserialize<'de> for #ident<'a> 530 + where 531 + 'de: 'a, 532 + { 533 + fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 534 + where 535 + D: serde::Deserializer<'de>, 536 + { 537 + let s = <&'de str>::deserialize(deserializer)?; 538 + Ok(Self::from(s)) 539 + } 540 + } 541 + 542 + #into_static_impl 543 + }) 544 + } 545 + 546 + /// Generate enum for integer with enum values 547 + pub(super) fn generate_integer_enum( 548 + &self, 549 + nsid: &str, 550 + def_name: &str, 551 + integer: &LexInteger<'static>, 552 + ) -> Result<TokenStream> { 553 + let type_name = self.def_to_type_name(nsid, def_name); 554 + let ident = syn::Ident::new(&type_name, proc_macro2::Span::call_site()); 555 + 556 + let enum_values = integer.r#enum.as_ref().unwrap(); 557 + let mut variants = Vec::new(); 558 + let mut from_i64_arms = Vec::new(); 559 + let mut to_i64_arms = Vec::new(); 560 + 561 + for value in enum_values { 562 + let variant_name = format!("Value{}", value.abs()); 563 + let variant_ident = syn::Ident::new(&variant_name, proc_macro2::Span::call_site()); 564 + 565 + variants.push(quote! { 566 + #[serde(rename = #value)] 567 + #variant_ident 568 + }); 569 + 570 + from_i64_arms.push(quote! { 571 + #value => Self::#variant_ident 572 + }); 573 + 574 + to_i64_arms.push(quote! { 575 + Self::#variant_ident => #value 576 + }); 577 + } 578 + 579 + let doc = self.generate_doc_comment(integer.description.as_ref()); 580 + 581 + Ok(quote! { 582 + #doc 583 + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 584 + pub enum #ident { 585 + #(#variants,)* 586 + #[serde(untagged)] 587 + Other(i64), 588 + } 589 + 590 + impl #ident { 591 + pub fn as_i64(&self) -> i64 { 592 + match self { 593 + #(#to_i64_arms,)* 594 + Self::Other(n) => *n, 595 + } 596 + } 597 + } 598 + 599 + impl From<i64> for #ident { 600 + fn from(n: i64) -> Self { 601 + match n { 602 + #(#from_i64_arms,)* 603 + _ => Self::Other(n), 604 + } 605 + } 606 + } 607 + 608 + impl serde::Serialize for #ident { 609 + fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> 610 + where 611 + S: serde::Serializer, 612 + { 613 + serializer.serialize_i64(self.as_i64()) 614 + } 615 + } 616 + 617 + impl<'de> serde::Deserialize<'de> for #ident { 618 + fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 619 + where 620 + D: serde::Deserializer<'de>, 621 + { 622 + let n = i64::deserialize(deserializer)?; 623 + Ok(Self::from(n)) 624 + } 625 + } 626 + }) 627 + } 628 + 629 + /// Generate IntoStatic impl for a struct 630 + #[allow(dead_code)] 631 + pub(super) fn generate_into_static_for_struct( 632 + &self, 633 + type_name: &str, 634 + field_names: &[&str], 635 + has_lifetime: bool, 636 + has_extra_data: bool, 637 + ) -> TokenStream { 638 + let ident = syn::Ident::new(type_name, proc_macro2::Span::call_site()); 639 + 640 + let field_idents: Vec<_> = field_names 641 + .iter() 642 + .map(|name| make_ident(&name.to_snake_case())) 643 + .collect(); 644 + 645 + if has_lifetime { 646 + let field_conversions: Vec<_> = field_idents 647 + .iter() 648 + .map(|field| quote! { #field: self.#field.into_static() }) 649 + .collect(); 650 + 651 + let extra_data_conversion = if has_extra_data { 652 + quote! { extra_data: self.extra_data.into_static(), } 653 + } else { 654 + quote! {} 655 + }; 656 + 657 + quote! { 658 + impl jacquard_common::IntoStatic for #ident<'_> { 659 + type Output = #ident<'static>; 660 + 661 + fn into_static(self) -> Self::Output { 662 + #ident { 663 + #(#field_conversions,)* 664 + #extra_data_conversion 665 + } 666 + } 667 + } 668 + } 669 + } else { 670 + quote! { 671 + impl jacquard_common::IntoStatic for #ident { 672 + type Output = #ident; 673 + 674 + fn into_static(self) -> Self::Output { 675 + self 676 + } 677 + } 678 + } 679 + } 680 + } 681 + 682 + /// Generate IntoStatic impl for an enum 683 + pub(super) fn generate_into_static_for_enum( 684 + &self, 685 + type_name: &str, 686 + variant_info: &[(String, EnumVariantKind)], 687 + has_lifetime: bool, 688 + is_open: bool, 689 + ) -> TokenStream { 690 + let ident = syn::Ident::new(type_name, proc_macro2::Span::call_site()); 691 + 692 + if has_lifetime { 693 + let variant_conversions: Vec<_> = variant_info 694 + .iter() 695 + .map(|(variant_name, kind)| { 696 + let variant_ident = syn::Ident::new(variant_name, proc_macro2::Span::call_site()); 697 + match kind { 698 + EnumVariantKind::Unit => { 699 + quote! { 700 + #ident::#variant_ident => #ident::#variant_ident 701 + } 702 + } 703 + EnumVariantKind::Tuple => { 704 + quote! { 705 + #ident::#variant_ident(v) => #ident::#variant_ident(v.into_static()) 706 + } 707 + } 708 + EnumVariantKind::Struct(fields) => { 709 + let field_idents: Vec<_> = fields 710 + .iter() 711 + .map(|f| make_ident(&f.to_snake_case())) 712 + .collect(); 713 + let field_conversions: Vec<_> = field_idents 714 + .iter() 715 + .map(|f| quote! { #f: #f.into_static() }) 716 + .collect(); 717 + quote! { 718 + #ident::#variant_ident { #(#field_idents,)* } => #ident::#variant_ident { 719 + #(#field_conversions,)* 720 + } 721 + } 722 + } 723 + } 724 + }) 725 + .collect(); 726 + 727 + let unknown_conversion = if is_open { 728 + quote! { 729 + #ident::Unknown(v) => #ident::Unknown(v.into_static()), 730 + } 731 + } else { 732 + quote! {} 733 + }; 734 + 735 + quote! { 736 + impl jacquard_common::IntoStatic for #ident<'_> { 737 + type Output = #ident<'static>; 738 + 739 + fn into_static(self) -> Self::Output { 740 + match self { 741 + #(#variant_conversions,)* 742 + #unknown_conversion 743 + } 744 + } 745 + } 746 + } 747 + } else { 748 + quote! { 749 + impl jacquard_common::IntoStatic for #ident { 750 + type Output = #ident; 751 + 752 + fn into_static(self) -> Self::Output { 753 + self 754 + } 755 + } 756 + } 757 + } 758 + } 759 + }
+259
crates/jacquard-lexicon/src/codegen/types.rs
··· 1 + use crate::error::Result; 2 + use crate::lexicon::{LexArrayItem, LexObjectProperty, LexString, LexStringFormat}; 3 + use heck::ToSnakeCase; 4 + use proc_macro2::TokenStream; 5 + use quote::quote; 6 + 7 + use super::CodeGenerator; 8 + 9 + impl<'c> CodeGenerator<'c> { 10 + /// Convert a property type to Rust type 11 + pub(super) fn property_to_rust_type( 12 + &self, 13 + nsid: &str, 14 + parent_type_name: &str, 15 + field_name: &str, 16 + prop: &LexObjectProperty<'static>, 17 + ) -> Result<TokenStream> { 18 + match prop { 19 + LexObjectProperty::Boolean(_) => Ok(quote! { bool }), 20 + LexObjectProperty::Integer(_) => Ok(quote! { i64 }), 21 + LexObjectProperty::String(s) => Ok(self.string_to_rust_type(s)), 22 + LexObjectProperty::Bytes(_) => Ok(quote! { bytes::Bytes }), 23 + LexObjectProperty::CidLink(_) => { 24 + Ok(quote! { jacquard_common::types::cid::CidLink<'a> }) 25 + } 26 + LexObjectProperty::Blob(_) => Ok(quote! { jacquard_common::types::blob::Blob<'a> }), 27 + LexObjectProperty::Unknown(_) => Ok(quote! { jacquard_common::types::value::Data<'a> }), 28 + LexObjectProperty::Array(array) => { 29 + // For arrays with union items, check if multi-variant 30 + if let LexArrayItem::Union(union) = &array.items { 31 + if union.refs.is_empty() { 32 + // Empty union: fall back to Data 33 + Ok(quote! { Vec<jacquard_common::types::value::Data<'a>> }) 34 + } else if union.refs.len() == 1 { 35 + // Single-variant: use the ref type directly 36 + let ref_str = if union.refs[0].starts_with('#') { 37 + format!("{}{}", nsid, union.refs[0]) 38 + } else { 39 + union.refs[0].to_string() 40 + }; 41 + let ref_type = self.ref_to_rust_type(&ref_str)?; 42 + Ok(quote! { Vec<#ref_type> }) 43 + } else { 44 + // Multi-variant: use generated union type 45 + let union_name = self.generate_field_type_name(nsid, parent_type_name, field_name, "Item"); 46 + let union_ident = syn::Ident::new(&union_name, proc_macro2::Span::call_site()); 47 + Ok(quote! { Vec<#union_ident<'a>> }) 48 + } 49 + } else { 50 + let item_type = self.array_item_to_rust_type(nsid, &array.items)?; 51 + Ok(quote! { Vec<#item_type> }) 52 + } 53 + } 54 + LexObjectProperty::Object(_object) => { 55 + // Generate unique nested object type name with collision detection 56 + let object_name = self.generate_field_type_name(nsid, parent_type_name, field_name, ""); 57 + let object_ident = syn::Ident::new(&object_name, proc_macro2::Span::call_site()); 58 + Ok(quote! { #object_ident<'a> }) 59 + } 60 + LexObjectProperty::Ref(ref_type) => { 61 + // Handle local refs (starting with #) by prepending the current NSID 62 + let ref_str = if ref_type.r#ref.starts_with('#') { 63 + format!("{}{}", nsid, ref_type.r#ref) 64 + } else { 65 + ref_type.r#ref.to_string() 66 + }; 67 + self.ref_to_rust_type(&ref_str) 68 + } 69 + LexObjectProperty::Union(union) => { 70 + if union.refs.is_empty() { 71 + // Empty union: fall back to Data 72 + Ok(quote! { jacquard_common::types::value::Data<'a> }) 73 + } else if union.refs.len() == 1 { 74 + // Check if this is a self-reference 75 + let ref_str = if union.refs[0].starts_with('#') { 76 + format!("{}{}", nsid, union.refs[0]) 77 + } else { 78 + union.refs[0].to_string() 79 + }; 80 + 81 + // Parse ref to get type name 82 + let (ref_nsid, ref_def) = if let Some((nsid_part, fragment)) = ref_str.split_once('#') { 83 + (nsid_part, fragment) 84 + } else { 85 + (ref_str.as_str(), "main") 86 + }; 87 + let ref_type_name = self.def_to_type_name(ref_nsid, ref_def); 88 + 89 + // If self-referential, keep union for indirection (variants are boxed) 90 + if ref_type_name == parent_type_name { 91 + let union_name = self.generate_field_type_name(nsid, parent_type_name, field_name, ""); 92 + let union_ident = syn::Ident::new(&union_name, proc_macro2::Span::call_site()); 93 + Ok(quote! { #union_ident<'a> }) 94 + } else { 95 + // Non-self-ref single-variant: use the ref type directly 96 + self.ref_to_rust_type(&ref_str) 97 + } 98 + } else { 99 + // Multi-variant: generate union type with collision detection 100 + let union_name = self.generate_field_type_name(nsid, parent_type_name, field_name, ""); 101 + let union_ident = syn::Ident::new(&union_name, proc_macro2::Span::call_site()); 102 + Ok(quote! { #union_ident<'a> }) 103 + } 104 + } 105 + } 106 + } 107 + 108 + /// Convert array item to Rust type 109 + pub(super) fn array_item_to_rust_type(&self, nsid: &str, item: &LexArrayItem) -> Result<TokenStream> { 110 + match item { 111 + LexArrayItem::Boolean(_) => Ok(quote! { bool }), 112 + LexArrayItem::Integer(_) => Ok(quote! { i64 }), 113 + LexArrayItem::String(s) => Ok(self.string_to_rust_type(s)), 114 + LexArrayItem::Bytes(_) => Ok(quote! { bytes::Bytes }), 115 + LexArrayItem::CidLink(_) => Ok(quote! { jacquard_common::types::cid::CidLink<'a> }), 116 + LexArrayItem::Blob(_) => Ok(quote! { jacquard_common::types::blob::Blob<'a> }), 117 + LexArrayItem::Unknown(_) => Ok(quote! { jacquard_common::types::value::Data<'a> }), 118 + LexArrayItem::Object(_) => { 119 + // For inline objects in arrays, use Data since we can't generate a unique type name 120 + Ok(quote! { jacquard_common::types::value::Data<'a> }) 121 + } 122 + LexArrayItem::Ref(ref_type) => { 123 + // Handle local refs (starting with #) by prepending the current NSID 124 + let ref_str = if ref_type.r#ref.starts_with('#') { 125 + format!("{}{}", nsid, ref_type.r#ref) 126 + } else { 127 + ref_type.r#ref.to_string() 128 + }; 129 + self.ref_to_rust_type(&ref_str) 130 + } 131 + LexArrayItem::Union(_) => { 132 + // For now, use Data 133 + Ok(quote! { jacquard_common::types::value::Data<'a> }) 134 + } 135 + } 136 + } 137 + 138 + /// Convert string type to Rust type 139 + pub(super) fn string_to_rust_type(&self, s: &LexString) -> TokenStream { 140 + match s.format { 141 + Some(LexStringFormat::Datetime) => { 142 + quote! { jacquard_common::types::string::Datetime } 143 + } 144 + Some(LexStringFormat::Did) => quote! { jacquard_common::types::string::Did<'a> }, 145 + Some(LexStringFormat::Handle) => quote! { jacquard_common::types::string::Handle<'a> }, 146 + Some(LexStringFormat::AtIdentifier) => { 147 + quote! { jacquard_common::types::ident::AtIdentifier<'a> } 148 + } 149 + Some(LexStringFormat::Nsid) => quote! { jacquard_common::types::string::Nsid<'a> }, 150 + Some(LexStringFormat::AtUri) => quote! { jacquard_common::types::string::AtUri<'a> }, 151 + Some(LexStringFormat::Uri) => quote! { jacquard_common::types::string::Uri<'a> }, 152 + Some(LexStringFormat::Cid) => quote! { jacquard_common::types::string::Cid<'a> }, 153 + Some(LexStringFormat::Language) => { 154 + quote! { jacquard_common::types::string::Language } 155 + } 156 + Some(LexStringFormat::Tid) => quote! { jacquard_common::types::string::Tid }, 157 + Some(LexStringFormat::RecordKey) => { 158 + quote! { jacquard_common::types::string::RecordKey<jacquard_common::types::string::Rkey<'a>> } 159 + } 160 + _ => quote! { jacquard_common::CowStr<'a> }, 161 + } 162 + } 163 + 164 + /// Convert ref to Rust type path 165 + pub(super) fn ref_to_rust_type(&self, ref_str: &str) -> Result<TokenStream> { 166 + use crate::error::CodegenError; 167 + use super::utils::sanitize_name; 168 + 169 + // Parse NSID and fragment 170 + let (ref_nsid, ref_def) = if let Some((nsid, fragment)) = ref_str.split_once('#') { 171 + (nsid, fragment) 172 + } else { 173 + (ref_str, "main") 174 + }; 175 + 176 + // Check if ref exists 177 + if !self.corpus.ref_exists(ref_str) { 178 + // Fallback to Data 179 + return Ok(quote! { jacquard_common::types::value::Data<'a> }); 180 + } 181 + 182 + // Convert NSID to module path 183 + // com.atproto.repo.strongRef -> com_atproto::repo::strong_ref::StrongRef 184 + // app.bsky.richtext.facet -> app_bsky::richtext::facet::Facet 185 + // app.bsky.actor.defs#nux -> app_bsky::actor::Nux (defs go in parent module) 186 + let parts: Vec<&str> = ref_nsid.split('.').collect(); 187 + let last_segment = parts.last().unwrap(); 188 + 189 + let type_name = self.def_to_type_name(ref_nsid, ref_def); 190 + 191 + let path_str = if *last_segment == "defs" && parts.len() >= 3 { 192 + // defs types go in parent module 193 + let first_two = format!("{}_{}", sanitize_name(parts[0]), sanitize_name(parts[1])); 194 + if parts.len() == 3 { 195 + // com.atproto.defs -> com_atproto::TypeName 196 + format!("{}::{}::{}", self.root_module, first_two, type_name) 197 + } else { 198 + // app.bsky.actor.defs -> app_bsky::actor::TypeName 199 + let middle: Vec<_> = parts[2..parts.len() - 1] 200 + .iter() 201 + .copied() 202 + .map(|s| sanitize_name(s)) 203 + .collect(); 204 + format!( 205 + "{}::{}::{}::{}", 206 + self.root_module, 207 + first_two, 208 + middle.join("::"), 209 + type_name 210 + ) 211 + } 212 + } else { 213 + // Regular types go in their own module file 214 + let (module_path, file_module) = if parts.len() >= 3 { 215 + // Join first two segments with underscore 216 + let first_two = format!("{}_{}", sanitize_name(parts[0]), sanitize_name(parts[1])); 217 + let file_name = sanitize_name(last_segment).to_snake_case(); 218 + 219 + if parts.len() > 3 { 220 + // Middle segments form the module path 221 + let middle: Vec<_> = parts[2..parts.len() - 1] 222 + .iter() 223 + .copied() 224 + .map(|s| sanitize_name(s)) 225 + .collect(); 226 + let base_path = format!("{}::{}", first_two, middle.join("::")); 227 + (base_path, file_name) 228 + } else { 229 + // Only 3 parts: com.atproto.label -> com_atproto, file: label 230 + (first_two, file_name) 231 + } 232 + } else if parts.len() == 2 { 233 + // e.g., "com.example" -> "com_example", file: example 234 + let first = sanitize_name(parts[0]); 235 + let file_name = sanitize_name(parts[1]).to_snake_case(); 236 + (first, file_name) 237 + } else { 238 + (parts[0].to_string(), "main".to_string()) 239 + }; 240 + 241 + format!( 242 + "{}::{}::{}::{}", 243 + self.root_module, module_path, file_module, type_name 244 + ) 245 + }; 246 + 247 + let path: syn::Path = syn::parse_str(&path_str).map_err(|e| CodegenError::Other { 248 + message: format!("Failed to parse path: {} {}", path_str, e), 249 + source: None, 250 + })?; 251 + 252 + // Only add lifetime if the target type needs it 253 + if self.ref_needs_lifetime(ref_str) { 254 + Ok(quote! { #path<'a> }) 255 + } else { 256 + Ok(quote! { #path }) 257 + } 258 + } 259 + }
+81
crates/jacquard-lexicon/src/codegen/utils.rs
··· 1 + use heck::ToPascalCase; 2 + use jacquard_common::CowStr; 3 + use proc_macro2::TokenStream; 4 + use quote::quote; 5 + 6 + /// Convert a value string to a valid Rust variant name 7 + pub(super) fn value_to_variant_name(value: &str) -> String { 8 + // Remove leading special chars and convert to pascal case 9 + let clean = value.trim_start_matches(|c: char| !c.is_alphanumeric()); 10 + let variant = clean.replace('-', "_").to_pascal_case(); 11 + 12 + // Prefix with underscore if starts with digit 13 + if variant.chars().next().map_or(false, |c| c.is_ascii_digit()) { 14 + format!("_{}", variant) 15 + } else if variant.is_empty() { 16 + "Unknown".to_string() 17 + } else { 18 + variant 19 + } 20 + } 21 + 22 + /// Sanitize a string to be safe for identifiers and filenames 23 + pub(super) fn sanitize_name(s: &str) -> String { 24 + if s.is_empty() { 25 + return "unknown".to_string(); 26 + } 27 + 28 + // Replace invalid characters with underscores 29 + let mut sanitized: String = s 30 + .chars() 31 + .map(|c| { 32 + if c.is_alphanumeric() || c == '_' { 33 + c 34 + } else { 35 + '_' 36 + } 37 + }) 38 + .collect(); 39 + 40 + // Ensure it doesn't start with a digit 41 + if sanitized 42 + .chars() 43 + .next() 44 + .map_or(false, |c| c.is_ascii_digit()) 45 + { 46 + sanitized = format!("_{}", sanitized); 47 + } 48 + 49 + sanitized 50 + } 51 + 52 + /// Create an identifier, using raw identifier if necessary for keywords 53 + pub(super) fn make_ident(s: &str) -> syn::Ident { 54 + if s.is_empty() { 55 + eprintln!("Warning: Empty identifier encountered, using 'unknown' as fallback"); 56 + return syn::Ident::new("unknown", proc_macro2::Span::call_site()); 57 + } 58 + 59 + let sanitized = sanitize_name(s); 60 + 61 + // Try to parse as ident, fall back to raw ident if needed 62 + syn::parse_str::<syn::Ident>(&sanitized).unwrap_or_else(|_| { 63 + eprintln!( 64 + "Warning: Invalid identifier '{}' sanitized to '{}'", 65 + s, sanitized 66 + ); 67 + syn::Ident::new_raw(&sanitized, proc_macro2::Span::call_site()) 68 + }) 69 + } 70 + 71 + /// Generate doc comment from optional description 72 + pub(super) fn generate_doc_comment(desc: Option<&CowStr>) -> TokenStream { 73 + if let Some(description) = desc { 74 + let desc_str = description.as_ref(); 75 + quote! { 76 + #[doc = #desc_str] 77 + } 78 + } else { 79 + quote! {} 80 + } 81 + }
+1006
crates/jacquard-lexicon/src/codegen/xrpc.rs
··· 1 + use crate::error::Result; 2 + use crate::lexicon::{ 3 + LexArrayItem, LexObjectProperty, LexXrpcBody, LexXrpcBodySchema, LexXrpcError, 4 + LexXrpcProcedure, LexXrpcQuery, LexXrpcSubscription, LexXrpcSubscriptionMessageSchema, 5 + }; 6 + use heck::{ToPascalCase, ToSnakeCase}; 7 + use proc_macro2::TokenStream; 8 + use quote::quote; 9 + 10 + use super::utils::make_ident; 11 + use super::CodeGenerator; 12 + 13 + impl<'c> CodeGenerator<'c> { 14 + /// Generate query type 15 + pub(super) fn generate_query( 16 + &self, 17 + nsid: &str, 18 + def_name: &str, 19 + query: &LexXrpcQuery<'static>, 20 + ) -> Result<TokenStream> { 21 + let type_base = self.def_to_type_name(nsid, def_name); 22 + let mut output = Vec::new(); 23 + 24 + let params_has_lifetime = query 25 + .parameters 26 + .as_ref() 27 + .map(|p| match p { 28 + crate::lexicon::LexXrpcQueryParameter::Params(params) => { 29 + self.params_need_lifetime(params) 30 + } 31 + }) 32 + .unwrap_or(false); 33 + let has_params = query.parameters.is_some(); 34 + let has_output = query.output.is_some(); 35 + let has_errors = query.errors.is_some(); 36 + 37 + if let Some(params) = &query.parameters { 38 + let params_struct = self.generate_params_struct(&type_base, params)?; 39 + output.push(params_struct); 40 + } 41 + 42 + if let Some(body) = &query.output { 43 + let output_struct = self.generate_output_struct(nsid, &type_base, body)?; 44 + output.push(output_struct); 45 + } 46 + 47 + if let Some(errors) = &query.errors { 48 + let error_enum = self.generate_error_enum(&type_base, errors)?; 49 + output.push(error_enum); 50 + } 51 + 52 + // Generate XrpcRequest impl 53 + let output_encoding = query 54 + .output 55 + .as_ref() 56 + .map(|o| o.encoding.as_ref()) 57 + .unwrap_or("application/json"); 58 + let xrpc_impl = self.generate_xrpc_request_impl( 59 + nsid, 60 + &type_base, 61 + quote! { jacquard_common::xrpc::XrpcMethod::Query }, 62 + output_encoding, 63 + has_params, 64 + params_has_lifetime, 65 + has_output, 66 + has_errors, 67 + false, // queries never have binary inputs 68 + )?; 69 + output.push(xrpc_impl); 70 + 71 + Ok(quote! { 72 + #(#output)* 73 + }) 74 + } 75 + 76 + /// Generate procedure type 77 + pub(super) fn generate_procedure( 78 + &self, 79 + nsid: &str, 80 + def_name: &str, 81 + proc: &LexXrpcProcedure<'static>, 82 + ) -> Result<TokenStream> { 83 + let type_base = self.def_to_type_name(nsid, def_name); 84 + let mut output = Vec::new(); 85 + 86 + // Check if input is a binary body (no schema) 87 + let is_binary_input = proc 88 + .input 89 + .as_ref() 90 + .map(|i| i.schema.is_none()) 91 + .unwrap_or(false); 92 + 93 + // Input bodies with schemas have lifetimes (they get #[lexicon] attribute) 94 + // Binary inputs don't have lifetimes 95 + let params_has_lifetime = proc.input.is_some() && !is_binary_input; 96 + let has_input = proc.input.is_some(); 97 + let has_output = proc.output.is_some(); 98 + let has_errors = proc.errors.is_some(); 99 + 100 + if let Some(params) = &proc.parameters { 101 + let params_struct = self.generate_params_struct_proc(&type_base, params)?; 102 + output.push(params_struct); 103 + } 104 + 105 + if let Some(body) = &proc.input { 106 + let input_struct = self.generate_input_struct(nsid, &type_base, body)?; 107 + output.push(input_struct); 108 + } 109 + 110 + if let Some(body) = &proc.output { 111 + let output_struct = self.generate_output_struct(nsid, &type_base, body)?; 112 + output.push(output_struct); 113 + } 114 + 115 + if let Some(errors) = &proc.errors { 116 + let error_enum = self.generate_error_enum(&type_base, errors)?; 117 + output.push(error_enum); 118 + } 119 + 120 + // Generate XrpcRequest impl 121 + let input_encoding = proc 122 + .input 123 + .as_ref() 124 + .map(|i| i.encoding.as_ref()) 125 + .unwrap_or("application/json"); 126 + let output_encoding = proc 127 + .output 128 + .as_ref() 129 + .map(|o| o.encoding.as_ref()) 130 + .unwrap_or("application/json"); 131 + let xrpc_impl = self.generate_xrpc_request_impl( 132 + nsid, 133 + &type_base, 134 + quote! { jacquard_common::xrpc::XrpcMethod::Procedure(#input_encoding) }, 135 + output_encoding, 136 + has_input, 137 + params_has_lifetime, 138 + has_output, 139 + has_errors, 140 + is_binary_input, 141 + )?; 142 + output.push(xrpc_impl); 143 + 144 + Ok(quote! { 145 + #(#output)* 146 + }) 147 + } 148 + 149 + pub(super) fn generate_subscription( 150 + &self, 151 + nsid: &str, 152 + def_name: &str, 153 + sub: &LexXrpcSubscription<'static>, 154 + ) -> Result<TokenStream> { 155 + let type_base = self.def_to_type_name(nsid, def_name); 156 + let mut output = Vec::new(); 157 + 158 + if let Some(params) = &sub.parameters { 159 + // Extract LexXrpcParameters from the enum 160 + match params { 161 + crate::lexicon::LexXrpcSubscriptionParameter::Params(params_inner) => { 162 + let params_struct = 163 + self.generate_params_struct_inner(&type_base, params_inner)?; 164 + output.push(params_struct); 165 + } 166 + } 167 + } 168 + 169 + if let Some(message) = &sub.message { 170 + if let Some(schema) = &message.schema { 171 + let message_type = self.generate_subscription_message(nsid, &type_base, schema)?; 172 + output.push(message_type); 173 + } 174 + } 175 + 176 + if let Some(errors) = &sub.errors { 177 + let error_enum = self.generate_error_enum(&type_base, errors)?; 178 + output.push(error_enum); 179 + } 180 + 181 + Ok(quote! { 182 + #(#output)* 183 + }) 184 + } 185 + 186 + pub(super) fn generate_subscription_message( 187 + &self, 188 + nsid: &str, 189 + type_base: &str, 190 + schema: &LexXrpcSubscriptionMessageSchema<'static>, 191 + ) -> Result<TokenStream> { 192 + use crate::lexicon::LexXrpcSubscriptionMessageSchema; 193 + 194 + match schema { 195 + LexXrpcSubscriptionMessageSchema::Union(union) => { 196 + // Generate a union enum for the message 197 + let enum_name = format!("{}Message", type_base); 198 + let enum_ident = syn::Ident::new(&enum_name, proc_macro2::Span::call_site()); 199 + 200 + let mut variants = Vec::new(); 201 + for ref_str in &union.refs { 202 + let ref_str_s = ref_str.as_ref(); 203 + // Parse ref to get NSID and def name 204 + let (ref_nsid, ref_def) = 205 + if let Some((nsid, fragment)) = ref_str.split_once('#') { 206 + (nsid, fragment) 207 + } else { 208 + (ref_str.as_ref(), "main") 209 + }; 210 + 211 + let variant_name = if ref_def == "main" { 212 + ref_nsid.split('.').last().unwrap().to_pascal_case() 213 + } else { 214 + ref_def.to_pascal_case() 215 + }; 216 + let variant_ident = 217 + syn::Ident::new(&variant_name, proc_macro2::Span::call_site()); 218 + let type_path = self.ref_to_rust_type(ref_str)?; 219 + 220 + variants.push(quote! { 221 + #[serde(rename = #ref_str_s)] 222 + #variant_ident(Box<#type_path>) 223 + }); 224 + } 225 + 226 + let doc = self.generate_doc_comment(union.description.as_ref()); 227 + 228 + Ok(quote! { 229 + #doc 230 + #[jacquard_derive::open_union] 231 + #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq, jacquard_derive::IntoStatic)] 232 + #[serde(tag = "$type")] 233 + #[serde(bound(deserialize = "'de: 'a"))] 234 + pub enum #enum_ident<'a> { 235 + #(#variants,)* 236 + } 237 + }) 238 + } 239 + LexXrpcSubscriptionMessageSchema::Object(obj) => { 240 + // Generate a struct for the message 241 + let struct_name = format!("{}Message", type_base); 242 + let struct_ident = syn::Ident::new(&struct_name, proc_macro2::Span::call_site()); 243 + 244 + let fields = self.generate_object_fields("", &struct_name, obj, false)?; 245 + let doc = self.generate_doc_comment(obj.description.as_ref()); 246 + 247 + // Subscription message structs always get a lifetime since they have the #[lexicon] attribute 248 + // which adds extra_data: BTreeMap<..., Data<'a>> 249 + let struct_def = quote! { 250 + #doc 251 + #[jacquard_derive::lexicon] 252 + #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq, jacquard_derive::IntoStatic)] 253 + #[serde(rename_all = "camelCase")] 254 + pub struct #struct_ident<'a> { 255 + #fields 256 + } 257 + }; 258 + 259 + // Generate union types for this message 260 + let mut unions = Vec::new(); 261 + for (field_name, field_type) in &obj.properties { 262 + match field_type { 263 + LexObjectProperty::Union(union) => { 264 + // Skip empty, single-variant unions unless they're self-referential 265 + if !union.refs.is_empty() && (union.refs.len() > 1 || self.is_self_referential_union(nsid, &struct_name, union)) { 266 + let union_name = self.generate_field_type_name(nsid, &struct_name, field_name, ""); 267 + let refs: Vec<_> = union.refs.iter().cloned().collect(); 268 + let union_def = 269 + self.generate_union(nsid, &union_name, &refs, None, union.closed)?; 270 + unions.push(union_def); 271 + } 272 + } 273 + LexObjectProperty::Array(array) => { 274 + if let LexArrayItem::Union(union) = &array.items { 275 + // Skip single-variant array unions 276 + if union.refs.len() > 1 { 277 + let union_name = self.generate_field_type_name(nsid, &struct_name, field_name, "Item"); 278 + let refs: Vec<_> = union.refs.iter().cloned().collect(); 279 + let union_def = self.generate_union(nsid, &union_name, &refs, None, union.closed)?; 280 + unions.push(union_def); 281 + } 282 + } 283 + } 284 + _ => {} 285 + } 286 + } 287 + 288 + Ok(quote! { 289 + #struct_def 290 + #(#unions)* 291 + }) 292 + } 293 + LexXrpcSubscriptionMessageSchema::Ref(ref_type) => { 294 + // Just a type alias to the referenced type 295 + // Refs generally have lifetimes, so always add <'a> 296 + let type_name = format!("{}Message", type_base); 297 + let ident = syn::Ident::new(&type_name, proc_macro2::Span::call_site()); 298 + let rust_type = self.ref_to_rust_type(&ref_type.r#ref)?; 299 + let doc = self.generate_doc_comment(ref_type.description.as_ref()); 300 + 301 + Ok(quote! { 302 + #doc 303 + pub type #ident<'a> = #rust_type; 304 + }) 305 + } 306 + } 307 + } 308 + 309 + /// Generate params struct from XRPC query parameters 310 + pub(super) fn generate_params_struct( 311 + &self, 312 + type_base: &str, 313 + params: &crate::lexicon::LexXrpcQueryParameter<'static>, 314 + ) -> Result<TokenStream> { 315 + use crate::lexicon::LexXrpcQueryParameter; 316 + match params { 317 + LexXrpcQueryParameter::Params(p) => self.generate_params_struct_inner(type_base, p), 318 + } 319 + } 320 + 321 + /// Generate params struct from XRPC procedure parameters (query string params) 322 + pub(super) fn generate_params_struct_proc( 323 + &self, 324 + type_base: &str, 325 + params: &crate::lexicon::LexXrpcProcedureParameter<'static>, 326 + ) -> Result<TokenStream> { 327 + use crate::lexicon::LexXrpcProcedureParameter; 328 + match params { 329 + // For procedures, query string params still get "Params" suffix since the main struct is the input 330 + LexXrpcProcedureParameter::Params(p) => { 331 + let struct_name = format!("{}Params", type_base); 332 + let ident = syn::Ident::new(&struct_name, proc_macro2::Span::call_site()); 333 + self.generate_params_struct_inner_with_name(&ident, p) 334 + } 335 + } 336 + } 337 + 338 + /// Generate params struct inner (shared implementation) 339 + pub(super) fn generate_params_struct_inner( 340 + &self, 341 + type_base: &str, 342 + p: &crate::lexicon::LexXrpcParameters<'static>, 343 + ) -> Result<TokenStream> { 344 + let ident = syn::Ident::new(type_base, proc_macro2::Span::call_site()); 345 + self.generate_params_struct_inner_with_name(&ident, p) 346 + } 347 + 348 + /// Generate params struct with custom name 349 + pub(super) fn generate_params_struct_inner_with_name( 350 + &self, 351 + ident: &syn::Ident, 352 + p: &crate::lexicon::LexXrpcParameters<'static>, 353 + ) -> Result<TokenStream> { 354 + let required = p.required.as_ref().map(|r| r.as_slice()).unwrap_or(&[]); 355 + let mut fields = Vec::new(); 356 + let mut default_fns = Vec::new(); 357 + 358 + for (field_name, field_type) in &p.properties { 359 + let is_required = required.contains(field_name); 360 + let (field_tokens, default_fn) = 361 + self.generate_param_field_with_default("", field_name, field_type, is_required)?; 362 + fields.push(field_tokens); 363 + if let Some(fn_def) = default_fn { 364 + default_fns.push(fn_def); 365 + } 366 + } 367 + 368 + let doc = self.generate_doc_comment(p.description.as_ref()); 369 + let needs_lifetime = self.params_need_lifetime(p); 370 + 371 + let derives = quote! { 372 + #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq, bon::Builder, jacquard_derive::IntoStatic)] 373 + #[builder(start_fn = new)] 374 + }; 375 + 376 + if needs_lifetime { 377 + Ok(quote! { 378 + #(#default_fns)* 379 + 380 + #doc 381 + #derives 382 + #[serde(rename_all = "camelCase")] 383 + pub struct #ident<'a> { 384 + #(#fields)* 385 + } 386 + }) 387 + } else { 388 + Ok(quote! { 389 + #(#default_fns)* 390 + 391 + #doc 392 + #derives 393 + #[serde(rename_all = "camelCase")] 394 + pub struct #ident { 395 + #(#fields)* 396 + } 397 + }) 398 + } 399 + } 400 + 401 + /// Generate input struct from XRPC body 402 + pub(super) fn generate_input_struct( 403 + &self, 404 + nsid: &str, 405 + type_base: &str, 406 + body: &LexXrpcBody<'static>, 407 + ) -> Result<TokenStream> { 408 + let ident = syn::Ident::new(type_base, proc_macro2::Span::call_site()); 409 + 410 + // Check if this is a binary body (no schema, just raw bytes) 411 + let is_binary_body = body.schema.is_none(); 412 + 413 + let fields = if let Some(schema) = &body.schema { 414 + self.generate_body_fields("", type_base, schema, true)? 415 + } else { 416 + // Binary body: just a bytes field 417 + quote! { 418 + pub body: bytes::Bytes, 419 + } 420 + }; 421 + 422 + let doc = self.generate_doc_comment(body.description.as_ref()); 423 + 424 + // Binary bodies don't need #[lexicon] attribute or lifetime 425 + let struct_def = if is_binary_body { 426 + quote! { 427 + #doc 428 + #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq, bon::Builder, jacquard_derive::IntoStatic)] 429 + #[builder(start_fn = new)] 430 + #[serde(rename_all = "camelCase")] 431 + pub struct #ident { 432 + #fields 433 + } 434 + } 435 + } else { 436 + // Input structs with schemas: manually add extra_data field with #[builder(default)] 437 + // for bon compatibility. The #[lexicon] macro will see it exists and skip adding it. 438 + quote! { 439 + #doc 440 + #[jacquard_derive::lexicon] 441 + #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq, bon::Builder, jacquard_derive::IntoStatic)] 442 + #[serde(rename_all = "camelCase")] 443 + #[builder(start_fn = new)] 444 + pub struct #ident<'a> { 445 + #fields 446 + #[serde(flatten)] 447 + #[serde(borrow)] 448 + #[builder(default)] 449 + pub extra_data: ::std::collections::BTreeMap< 450 + ::jacquard_common::smol_str::SmolStr, 451 + ::jacquard_common::types::value::Data<'a> 452 + >, 453 + } 454 + } 455 + }; 456 + 457 + // Generate union types if schema is an Object 458 + let mut unions = Vec::new(); 459 + if let Some(crate::lexicon::LexXrpcBodySchema::Object(obj)) = &body.schema { 460 + for (field_name, field_type) in &obj.properties { 461 + match field_type { 462 + LexObjectProperty::Union(union) => { 463 + // Skip empty, single-variant unions unless they're self-referential 464 + if !union.refs.is_empty() && (union.refs.len() > 1 || self.is_self_referential_union(nsid, type_base, union)) { 465 + let union_name = self.generate_field_type_name(nsid, type_base, field_name, ""); 466 + let refs: Vec<_> = union.refs.iter().cloned().collect(); 467 + let union_def = 468 + self.generate_union(nsid, &union_name, &refs, None, union.closed)?; 469 + unions.push(union_def); 470 + } 471 + } 472 + LexObjectProperty::Array(array) => { 473 + if let LexArrayItem::Union(union) = &array.items { 474 + // Skip single-variant array unions 475 + if union.refs.len() > 1 { 476 + let union_name = self.generate_field_type_name(nsid, type_base, field_name, "Item"); 477 + let refs: Vec<_> = union.refs.iter().cloned().collect(); 478 + let union_def = self.generate_union(nsid, &union_name, &refs, None, union.closed)?; 479 + unions.push(union_def); 480 + } 481 + } 482 + } 483 + _ => {} 484 + } 485 + } 486 + } 487 + 488 + Ok(quote! { 489 + #struct_def 490 + #(#unions)* 491 + }) 492 + } 493 + 494 + /// Generate output struct from XRPC body 495 + pub(super) fn generate_output_struct( 496 + &self, 497 + nsid: &str, 498 + type_base: &str, 499 + body: &LexXrpcBody<'static>, 500 + ) -> Result<TokenStream> { 501 + let struct_name = format!("{}Output", type_base); 502 + let ident = syn::Ident::new(&struct_name, proc_macro2::Span::call_site()); 503 + 504 + let fields = if let Some(schema) = &body.schema { 505 + self.generate_body_fields("", &struct_name, schema, false)? 506 + } else { 507 + quote! {} 508 + }; 509 + 510 + let doc = self.generate_doc_comment(body.description.as_ref()); 511 + 512 + // Output structs always get a lifetime since they have the #[lexicon] attribute 513 + // which adds extra_data: BTreeMap<..., Data<'a>> 514 + let struct_def = quote! { 515 + #doc 516 + #[jacquard_derive::lexicon] 517 + #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq, jacquard_derive::IntoStatic)] 518 + #[serde(rename_all = "camelCase")] 519 + pub struct #ident<'a> { 520 + #fields 521 + } 522 + }; 523 + 524 + // Generate union types if schema is an Object 525 + let mut unions = Vec::new(); 526 + if let Some(crate::lexicon::LexXrpcBodySchema::Object(obj)) = &body.schema { 527 + for (field_name, field_type) in &obj.properties { 528 + match field_type { 529 + LexObjectProperty::Union(union) => { 530 + // Skip single-variant unions unless they're self-referential 531 + if union.refs.len() > 1 || self.is_self_referential_union(nsid, &struct_name, union) { 532 + let union_name = self.generate_field_type_name(nsid, &struct_name, field_name, ""); 533 + let refs: Vec<_> = union.refs.iter().cloned().collect(); 534 + let union_def = 535 + self.generate_union(nsid, &union_name, &refs, None, union.closed)?; 536 + unions.push(union_def); 537 + } 538 + } 539 + LexObjectProperty::Array(array) => { 540 + if let LexArrayItem::Union(union) = &array.items { 541 + // Skip single-variant array unions 542 + if union.refs.len() > 1 { 543 + let union_name = self.generate_field_type_name(nsid, &struct_name, field_name, "Item"); 544 + let refs: Vec<_> = union.refs.iter().cloned().collect(); 545 + let union_def = self.generate_union(nsid, &union_name, &refs, None, union.closed)?; 546 + unions.push(union_def); 547 + } 548 + } 549 + } 550 + _ => {} 551 + } 552 + } 553 + } 554 + 555 + Ok(quote! { 556 + #struct_def 557 + #(#unions)* 558 + }) 559 + } 560 + 561 + /// Generate fields from XRPC body schema 562 + pub(super) fn generate_body_fields( 563 + &self, 564 + nsid: &str, 565 + parent_type_name: &str, 566 + schema: &LexXrpcBodySchema<'static>, 567 + is_builder: bool, 568 + ) -> Result<TokenStream> { 569 + use crate::lexicon::LexXrpcBodySchema; 570 + 571 + match schema { 572 + LexXrpcBodySchema::Object(obj) => { 573 + self.generate_object_fields(nsid, parent_type_name, obj, is_builder) 574 + } 575 + LexXrpcBodySchema::Ref(ref_type) => { 576 + let rust_type = self.ref_to_rust_type(&ref_type.r#ref)?; 577 + Ok(quote! { 578 + #[serde(flatten)] 579 + #[serde(borrow)] 580 + pub value: #rust_type, 581 + }) 582 + } 583 + LexXrpcBodySchema::Union(_union) => { 584 + let rust_type = quote! { jacquard_common::types::value::Data<'a> }; 585 + Ok(quote! { 586 + #[serde(flatten)] 587 + #[serde(borrow)] 588 + pub value: #rust_type, 589 + }) 590 + } 591 + } 592 + } 593 + 594 + /// Generate a field for XRPC parameters 595 + pub(super) fn generate_param_field( 596 + &self, 597 + _nsid: &str, 598 + field_name: &str, 599 + field_type: &crate::lexicon::LexXrpcParametersProperty<'static>, 600 + is_required: bool, 601 + ) -> Result<TokenStream> { 602 + use crate::lexicon::LexXrpcParametersProperty; 603 + 604 + let field_ident = make_ident(&field_name.to_snake_case()); 605 + 606 + let (rust_type, needs_lifetime, is_cowstr) = match field_type { 607 + LexXrpcParametersProperty::Boolean(_) => (quote! { bool }, false, false), 608 + LexXrpcParametersProperty::Integer(_) => (quote! { i64 }, false, false), 609 + LexXrpcParametersProperty::String(s) => { 610 + let is_cowstr = s.format.is_none(); // CowStr for plain strings 611 + ( 612 + self.string_to_rust_type(s), 613 + self.string_needs_lifetime(s), 614 + is_cowstr, 615 + ) 616 + } 617 + LexXrpcParametersProperty::Unknown(_) => ( 618 + quote! { jacquard_common::types::value::Data<'a> }, 619 + true, 620 + false, 621 + ), 622 + LexXrpcParametersProperty::Array(arr) => { 623 + let needs_lifetime = match &arr.items { 624 + crate::lexicon::LexPrimitiveArrayItem::Boolean(_) 625 + | crate::lexicon::LexPrimitiveArrayItem::Integer(_) => false, 626 + crate::lexicon::LexPrimitiveArrayItem::String(s) => { 627 + self.string_needs_lifetime(s) 628 + } 629 + crate::lexicon::LexPrimitiveArrayItem::Unknown(_) => true, 630 + }; 631 + let item_type = match &arr.items { 632 + crate::lexicon::LexPrimitiveArrayItem::Boolean(_) => quote! { bool }, 633 + crate::lexicon::LexPrimitiveArrayItem::Integer(_) => quote! { i64 }, 634 + crate::lexicon::LexPrimitiveArrayItem::String(s) => self.string_to_rust_type(s), 635 + crate::lexicon::LexPrimitiveArrayItem::Unknown(_) => { 636 + quote! { jacquard_common::types::value::Data<'a> } 637 + } 638 + }; 639 + (quote! { Vec<#item_type> }, needs_lifetime, false) 640 + } 641 + }; 642 + 643 + let rust_type = if is_required { 644 + rust_type 645 + } else { 646 + quote! { std::option::Option<#rust_type> } 647 + }; 648 + 649 + let mut attrs = Vec::new(); 650 + 651 + if !is_required { 652 + attrs.push(quote! { #[serde(skip_serializing_if = "std::option::Option::is_none")] }); 653 + } 654 + 655 + // Add serde(borrow) to all fields with lifetimes 656 + if needs_lifetime { 657 + attrs.push(quote! { #[serde(borrow)] }); 658 + } 659 + 660 + // Add builder(into) for CowStr fields to allow String, &str, etc. 661 + if is_cowstr { 662 + attrs.push(quote! { #[builder(into)] }); 663 + } 664 + 665 + Ok(quote! { 666 + #(#attrs)* 667 + pub #field_ident: #rust_type, 668 + }) 669 + } 670 + 671 + /// Generate param field with serde default if present 672 + /// Returns (field_tokens, optional_default_function) 673 + pub(super) fn generate_param_field_with_default( 674 + &self, 675 + nsid: &str, 676 + field_name: &str, 677 + field_type: &crate::lexicon::LexXrpcParametersProperty<'static>, 678 + is_required: bool, 679 + ) -> Result<(TokenStream, Option<TokenStream>)> { 680 + use crate::lexicon::LexXrpcParametersProperty; 681 + use heck::ToSnakeCase; 682 + 683 + // Get base field 684 + let base_field = self.generate_param_field(nsid, field_name, field_type, is_required)?; 685 + 686 + // Generate default function and attribute for required fields with defaults 687 + // For optional fields, just add doc comments 688 + let (doc_comment, serde_attr, default_fn) = if is_required { 689 + match field_type { 690 + LexXrpcParametersProperty::Boolean(b) if b.default.is_some() => { 691 + let v = b.default.unwrap(); 692 + let fn_name = format!("_default_{}", field_name.to_snake_case()); 693 + let fn_ident = syn::Ident::new(&fn_name, proc_macro2::Span::call_site()); 694 + ( 695 + Some(format!("Defaults to `{}`", v)), 696 + Some(quote! { #[serde(default = #fn_name)] }), 697 + Some(quote! { 698 + fn #fn_ident() -> bool { #v } 699 + }), 700 + ) 701 + } 702 + LexXrpcParametersProperty::Integer(i) if i.default.is_some() => { 703 + let v = i.default.unwrap(); 704 + let fn_name = format!("_default_{}", field_name.to_snake_case()); 705 + let fn_ident = syn::Ident::new(&fn_name, proc_macro2::Span::call_site()); 706 + ( 707 + Some(format!("Defaults to `{}`", v)), 708 + Some(quote! { #[serde(default = #fn_name)] }), 709 + Some(quote! { 710 + fn #fn_ident() -> i64 { #v } 711 + }), 712 + ) 713 + } 714 + LexXrpcParametersProperty::String(s) if s.default.is_some() => { 715 + let v = s.default.as_ref().unwrap().as_ref(); 716 + let fn_name = format!("_default_{}", field_name.to_snake_case()); 717 + let fn_ident = syn::Ident::new(&fn_name, proc_macro2::Span::call_site()); 718 + ( 719 + Some(format!("Defaults to `\"{}\"`", v)), 720 + Some(quote! { #[serde(default = #fn_name)] }), 721 + Some(quote! { 722 + fn #fn_ident() -> jacquard_common::CowStr<'static> { 723 + jacquard_common::CowStr::from(#v) 724 + } 725 + }), 726 + ) 727 + } 728 + _ => (None, None, None), 729 + } 730 + } else { 731 + // Optional fields - just doc comments, no serde defaults 732 + let doc = match field_type { 733 + LexXrpcParametersProperty::Integer(i) => { 734 + let mut parts = Vec::new(); 735 + if let Some(def) = i.default { 736 + parts.push(format!("default: {}", def)); 737 + } 738 + if let Some(min) = i.minimum { 739 + parts.push(format!("min: {}", min)); 740 + } 741 + if let Some(max) = i.maximum { 742 + parts.push(format!("max: {}", max)); 743 + } 744 + if !parts.is_empty() { 745 + Some(format!("({})", parts.join(", "))) 746 + } else { 747 + None 748 + } 749 + } 750 + LexXrpcParametersProperty::String(s) => { 751 + let mut parts = Vec::new(); 752 + if let Some(def) = s.default.as_ref() { 753 + parts.push(format!("default: \"{}\"", def.as_ref())); 754 + } 755 + if let Some(min) = s.min_length { 756 + parts.push(format!("min length: {}", min)); 757 + } 758 + if let Some(max) = s.max_length { 759 + parts.push(format!("max length: {}", max)); 760 + } 761 + if !parts.is_empty() { 762 + Some(format!("({})", parts.join(", "))) 763 + } else { 764 + None 765 + } 766 + } 767 + LexXrpcParametersProperty::Boolean(b) => { 768 + b.default.map(|v| format!("(default: {})", v)) 769 + } 770 + _ => None, 771 + }; 772 + (doc, None, None) 773 + }; 774 + 775 + let doc = doc_comment.as_ref().map(|d| quote! { #[doc = #d] }); 776 + let field_with_attrs = match (doc, serde_attr) { 777 + (Some(doc), Some(attr)) => quote! { 778 + #doc 779 + #attr 780 + #base_field 781 + }, 782 + (Some(doc), None) => quote! { 783 + #doc 784 + #base_field 785 + }, 786 + (None, Some(attr)) => quote! { 787 + #attr 788 + #base_field 789 + }, 790 + (None, None) => base_field, 791 + }; 792 + 793 + Ok((field_with_attrs, default_fn)) 794 + } 795 + 796 + /// Generate error enum from XRPC errors 797 + pub(super) fn generate_error_enum( 798 + &self, 799 + type_base: &str, 800 + errors: &[LexXrpcError<'static>], 801 + ) -> Result<TokenStream> { 802 + let enum_name = format!("{}Error", type_base); 803 + let ident = syn::Ident::new(&enum_name, proc_macro2::Span::call_site()); 804 + 805 + let mut variants = Vec::new(); 806 + let mut display_arms = Vec::new(); 807 + 808 + for error in errors { 809 + let variant_name = error.name.to_pascal_case(); 810 + let variant_ident = syn::Ident::new(&variant_name, proc_macro2::Span::call_site()); 811 + 812 + let error_name = error.name.as_ref(); 813 + let doc = self.generate_doc_comment(error.description.as_ref()); 814 + 815 + variants.push(quote! { 816 + #doc 817 + #[serde(rename = #error_name)] 818 + #variant_ident(std::option::Option<String>) 819 + }); 820 + 821 + display_arms.push(quote! { 822 + Self::#variant_ident(msg) => { 823 + write!(f, #error_name)?; 824 + if let Some(msg) = msg { 825 + write!(f, ": {}", msg)?; 826 + } 827 + Ok(()) 828 + } 829 + }); 830 + } 831 + 832 + // IntoStatic impl is generated by the derive macro now 833 + 834 + Ok(quote! { 835 + #[jacquard_derive::open_union] 836 + #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq, thiserror::Error, miette::Diagnostic, jacquard_derive::IntoStatic)] 837 + #[serde(tag = "error", content = "message")] 838 + #[serde(bound(deserialize = "'de: 'a"))] 839 + pub enum #ident<'a> { 840 + #(#variants,)* 841 + } 842 + 843 + impl std::fmt::Display for #ident<'_> { 844 + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 845 + match self { 846 + #(#display_arms)* 847 + Self::Unknown(err) => write!(f, "Unknown error: {:?}", err), 848 + } 849 + } 850 + } 851 + }) 852 + } 853 + 854 + /// Generate XrpcRequest trait impl for a query or procedure 855 + pub(super) fn generate_xrpc_request_impl( 856 + &self, 857 + nsid: &str, 858 + type_base: &str, 859 + method: TokenStream, 860 + output_encoding: &str, 861 + has_params: bool, 862 + params_has_lifetime: bool, 863 + has_output: bool, 864 + has_errors: bool, 865 + is_binary_input: bool, 866 + ) -> Result<TokenStream> { 867 + let output_type = if has_output { 868 + let output_ident = syn::Ident::new( 869 + &format!("{}Output", type_base), 870 + proc_macro2::Span::call_site(), 871 + ); 872 + quote! { #output_ident<'de> } 873 + } else { 874 + quote! { () } 875 + }; 876 + 877 + let error_type = if has_errors { 878 + let error_ident = syn::Ident::new( 879 + &format!("{}Error", type_base), 880 + proc_macro2::Span::call_site(), 881 + ); 882 + quote! { #error_ident<'de> } 883 + } else { 884 + quote! { jacquard_common::xrpc::GenericError<'de> } 885 + }; 886 + 887 + // Generate the response type that implements XrpcResp 888 + let response_ident = syn::Ident::new( 889 + &format!("{}Response", type_base), 890 + proc_macro2::Span::call_site(), 891 + ); 892 + 893 + // Generate the endpoint type that implements XrpcEndpoint 894 + let endpoint_ident = syn::Ident::new( 895 + &format!("{}Request", type_base), 896 + proc_macro2::Span::call_site(), 897 + ); 898 + 899 + let response_type = quote! { 900 + #[doc = "Response type for "] 901 + #[doc = #nsid] 902 + pub struct #response_ident; 903 + 904 + impl jacquard_common::xrpc::XrpcResp for #response_ident { 905 + const NSID: &'static str = #nsid; 906 + const ENCODING: &'static str = #output_encoding; 907 + type Output<'de> = #output_type; 908 + type Err<'de> = #error_type; 909 + } 910 + }; 911 + 912 + // Generate encode_body() method for binary inputs 913 + let encode_body_method = if is_binary_input { 914 + quote! { 915 + fn encode_body(&self) -> Result<Vec<u8>, jacquard_common::xrpc::EncodeError> { 916 + Ok(self.body.to_vec()) 917 + } 918 + } 919 + } else { 920 + quote! {} 921 + }; 922 + 923 + // Generate decode_body() method for binary inputs 924 + let decode_body_method = if is_binary_input { 925 + quote! { 926 + fn decode_body( 927 + body: &'de [u8], 928 + ) -> Result<Box<Self>, jacquard_common::error::DecodeError> { 929 + Ok(Box::new(Self { 930 + body: bytes::Bytes::copy_from_slice(body), 931 + })) 932 + } 933 + } 934 + } else { 935 + quote! {} 936 + }; 937 + 938 + let endpoint_path = format!("/xrpc/{}", nsid); 939 + 940 + if has_params { 941 + // Implement on the params/input struct itself 942 + let request_ident = syn::Ident::new(type_base, proc_macro2::Span::call_site()); 943 + let impl_target = if params_has_lifetime { 944 + quote! { #request_ident<'de> } 945 + } else { 946 + quote! { #request_ident } 947 + }; 948 + 949 + Ok(quote! { 950 + #response_type 951 + 952 + impl<'de> jacquard_common::xrpc::XrpcRequest<'de> for #impl_target { 953 + const NSID: &'static str = #nsid; 954 + const METHOD: jacquard_common::xrpc::XrpcMethod = #method; 955 + 956 + type Response = #response_ident; 957 + 958 + #encode_body_method 959 + #decode_body_method 960 + } 961 + 962 + #[doc = "Endpoint type for "] 963 + #[doc = #nsid] 964 + pub struct #endpoint_ident; 965 + 966 + impl jacquard_common::xrpc::XrpcEndpoint for #endpoint_ident { 967 + const PATH: &'static str = #endpoint_path; 968 + const METHOD: jacquard_common::xrpc::XrpcMethod = #method; 969 + 970 + type Request<'de> = #impl_target; 971 + type Response = #response_ident; 972 + } 973 + }) 974 + } else { 975 + // No params - generate a marker struct 976 + let request_ident = syn::Ident::new(type_base, proc_macro2::Span::call_site()); 977 + 978 + Ok(quote! { 979 + /// XRPC request marker type 980 + #[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize, jacquard_derive::IntoStatic)] 981 + pub struct #request_ident; 982 + 983 + #response_type 984 + 985 + impl<'de> jacquard_common::xrpc::XrpcRequest<'de> for #request_ident { 986 + const NSID: &'static str = #nsid; 987 + const METHOD: jacquard_common::xrpc::XrpcMethod = #method; 988 + 989 + type Response = #response_ident; 990 + } 991 + 992 + #[doc = "Endpoint type for "] 993 + #[doc = #nsid] 994 + pub struct #endpoint_ident; 995 + 996 + impl jacquard_common::xrpc::XrpcEndpoint for #endpoint_ident { 997 + const PATH: &'static str = #endpoint_path; 998 + const METHOD: jacquard_common::xrpc::XrpcMethod = #method; 999 + 1000 + type Request<'de> = #request_ident; 1001 + type Response = #response_ident; 1002 + } 1003 + }) 1004 + } 1005 + } 1006 + }