- èæ¯
- CidLink
- serde_json, serde_ipld_dagcbor
- åé¡ç¹: ãã¼ã¿ãã©ã¼ãããã«ãã£ã¦å¯¾è±¡ã®åãç°ãªã
- æåã®è§£æ±ºç: is_human_readable() ã«ããåå²
- 解決ç(ï¼): Ipld ãçµç±ããã¼ã¿ã®æ§é ã«ãã£ã¦åå²ãã
- æ±ç¨çï¼ ãªè§£æ±ºç: Untagged
- ãã³ããã¼ã¯
- å®è£ çµæ
èæ¯
Blueskyã®AT Protocolã®Rustçã©ã¤ãã©ãªãä½ã£ã¦ããã
ãã®ä¸ã§æè¿å®è£ ããæ©è½ã®è©±ã
CidLink
AT Protocolã§æ±ããã¼ã¿ã¢ãã«ã®Specã¯ã以ä¸ã®ãã¼ã¸ã§æ¸ããã¦ããã
ãã®ä¸ã«ãLexiconã§cid-link
ã¨ããååã§æ±ãããåãããã
https://atproto.com/specs/lexicon#cid-link
ã¤ã¾ãIPLDã®Link
ãCID
ã§è¡¨ç¾ããåãã¨ãããã¨ã®ããã ã
ã§ããããã®ãã¼ã¿ãæ±ãããã ãããã®ãã¼ã¿ãã©ã¼ãããã2種é¡ããã
IPLDã§ã¯ãã¼ã¿éä¿¡ã®ããã®Codecã¨ãã¦ãbinary formatã®DAG-CBOR
㨠human-readable formatã®DAG-JSON
ãå®ãã¦ããã
AT Protocolã§ã¯ãå¹ççã«ãã¼ã¿ãæ±ãããå ´åã«ã¯DAG-CBOR
ãç¨ããXRPCã®HTTP APIãªã©ã§ã¯DAG-JSON
ã¨ã¯ç°ãªãè¦ç´ã®JSONãã©ã¼ããããæ±ãããããã
ã§ãcid-link
ã«ã¤ãã¦ã¯ä»¥ä¸ã®ããã«æ¸ããã¦ããã
link
field type (Lexicon typecid-link
). In DAG-CBOR encodes as a binary CID (multibase type 0x00) in a bytestring with CBOR tag 42. In JSON, encodes as$link
object
DAG-CBORã§ã¯ã以ä¸ã®ãããªãã¤ããªè¡¨ç¾ã®CIDãå«ããã¤ãåã
0xd8, 0x2a, 0x58, 0x25, 0x00, 0x01, 0x71, 0x12, 0x20, 0x65, 0x06, 0x2a, 0x5a, 0x5a, 0x00, 0xfc, 0x16, 0xd7, 0x3c, 0x69, 0x44, 0x23, 0x7c, 0xcb, 0xc1, 0x5b, 0x1c, 0x4a, 0x72, 0x34, 0x48, 0x93, 0x36, 0x89, 0x1d, 0x09, 0x17, 0x41, 0xa2, 0x39, 0xd0,
JSONã§ã¯ã以ä¸ã®ãã㪠$link
ã¨ããåä¸ã®ãã¼ãå«ããªã¸ã§ã¯ãã
{ "$link": "bafyreidfayvfuwqa7qlnopdjiqrxzs6blmoeu4rujcjtnci5beludirz2a" }
ã¨ãã¦è¡¨ç¾ããããããã
serde_json, serde_ipld_dagcbor
Rustã§JSONãªã©ã®ãã¼ã¿ãã©ã¼ããã㧠Serialize/Deserialize ãããã¨ãªãã¨ã¾ãééããªã serde ã使ããã¨ã«ãªãã ããã
serde
èªä½ã¯ Serialize/Deserialize ãè¡ãããã§ã¯ãªããããã¾ã§Rustã®ãã¼ã¿æ§é ã«å¯¾ãã¦æ±ç¨çã«ããããå¯è½ã«ããããã®ãã¬ã¼ã ã¯ã¼ã¯ãã¨ããæãã
ATriumã§ã¯LexiconããçæãããåXRPCã«é¢é£ãããã¼ã¿ã®åãã©ã¤ãã©ãªã¨ãã¦æä¾ããã®ã§ããããã®åã«å¯¾ãã¦åºæ¬çã«ã¯serde
ã®attributesãä»ä¸ããã ãã§ãå®éã«ä½ããã®ãã¼ã¿ãã©ã¼ãããã«å¯¾å¿ãã Seriazlier/Deserializer ã使ã£ã¦å¤ææä½ãããã®ã¯ã©ã¤ãã©ãªã®ã¦ã¼ã¶ãã¨ãããã¨ã«ãªãã
å®éã®ã¨ãããJSONãã¼ã¿ãæ±ããªã serde_json
ä¸æã ããã
DAG-CBORã«ã¤ãã¦ã¯ãCBORãã¼ã¿ãæ±ããã¨ãã§ããã©ã¤ãã©ãªãå¹¾ã¤ãåå¨ãã¦ãããã2024-03æç¹ã§IPLDã®Link
ãæ£ããæ±ãããã®ã¨ãã¦ã¯ serde_ipld_dagcbor
ãç¾å®çãªé¸æè¢ã«ãªãããã ã£ãã
ã®ã§ããã®2ã¤ã使ã£ã¦å®éã«ä½¿ããããã¼ã¿ã«å¯¾ãã¦æ£ãã Serialize/Deserialize ã§ããããã«ãããã¨ãããã¨ãèããã
åé¡ç¹: ãã¼ã¿ãã©ã¼ãããã«ãã£ã¦å¯¾è±¡ã®åãç°ãªã
JSONã®å ´å/DAG-CBORã®å ´åãããããç¬ç«ãã¦èããã°ãæ§é ã«åããã¦åãå®ç¾©ããã ããªã®ã§ç°¡åã ã
#[derive(Serialize, Deserialize, Debug)] struct CidLink { #[serde(rename = "$link")] link: String, } fn main() { let cid_link_from_json = serde_json::from_str::<CidLink>(...); println!("{cid_link_from_json:?}"); // => Ok(CidLink { link: "bafyreidfayvfuwqa7qlnopdjiqrxzs6blmoeu4rujcjtnci5beludirz2a" }) }
#[derive(Serialize, Deserialize, Debug)] struct CidLink(cid::Cid); fn main() { let cid_link_from_dagcbor = serde_ipld_dagcbor::from_slice::<CidLink>(...); println!("{cid_link_from_dagcbor:?}"); // => Ok(CidLink(Cid(bafyreidfayvfuwqa7qlnopdjiqrxzs6blmoeu4rujcjtnci5beludirz2a))) }
ã§ãåé¡ã¯ä¸¡æ¹ã«å¯¾å¿ãããã¨ããå ´åãã©ã¤ãã©ãªã®ã¦ã¼ã¶ãJSONã使ãã DAG-CBORã使ããã©ã¡ããã ãã§ããã°ã¾ã feature flags ã§åãæ¿ãããªã©ã®å¯¾å¿ãå¯è½ã ãããã©ã¡ãã使ããã¨ããã¦ã¼ã¹ã±ã¼ã¹ãèããããã®ã§ã
serde_json
ã使ã£ã¦ããå ´åã¯$link
ãã¼ãå«ããªãã¸ã§ã¯ãserde_ipld_dagcbor
ã使ã£ã¦ããå ´åã¯cid::Cid
ã¨ãã¦åã CidLink
ã¨ããååã®åã«æ
å ±ãæ ¼ç´ã§ããããã«ãããã
æåã®è§£æ±ºç: is_human_readable()
ã«ããåå²
åºæ¬çã«ã¯ serde
èªä½ã¯ãå¼ã°ãã Serializer/Deserializer ã«ã¤ãã¦ã®æ
å ±ãç¥ããã¨ãã§ããªãã
ãã Serialize
ã Deserialize
ãèªåã§å®è£
ããã¨ããã®ã¨ãã«å¼æ°ã«å«ã¾ãã serializer
ã deserializer
ã«å¯¾ã㦠.is_human_readable()
ã¨ããã¡ã½ãããå¼ã¶ãã¨ã§ä¸ã¤æ
å ±ãå¾ãããã
ãã㯠serde_json
ã使ã£ã¦ãã㨠true
ã«ãªãã serde_ipld_dagcbor
ã使ã£ã¦ããã¨åºæ¬çã«ã¯ false
ã«ãªãã®ã§ã以ä¸ã®ããã«åå²ããããã¨ã§çµ±ä¸ãã CidLink
ã§ä¸¡æ¹ã®ãã¼ã¿ãã©ã¼ããããæ±ããã¨ãã§ããã
#[derive(Debug)] struct CidLink(Cid); #[derive(Serialize, Deserialize)] struct LinkObject { #[serde(rename = "$link")] link: String, } impl Serialize for CidLink { fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> { if serializer.is_human_readable() { LinkObject { link: self.0.to_string(), } .serialize(serializer) } else { self.0.serialize(serializer) } } } impl<'de> Deserialize<'de> for CidLink { fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> { if deserializer.is_human_readable() { let obj = LinkObject::deserialize(deserializer)?; Ok(Self( Cid::try_from(obj.link.as_str()).map_err(serde::de::Error::custom)?, )) } else { Ok(Self(Cid::deserialize(deserializer)?)) } } }
ããã§è§£æ±ºããã§ãããã§ãã⦠ã¨ããããã¨ããã ããããããããªãã
ãã¾ããããªãã±ã¼ã¹
CidLink
åä½ãä¸æãå¦çããã¦ãã¦ãããããåã«ã㤠enum
ã "Internally tagged" ã "Untagged" ã§åºå¥ãããã¨ããã¨ãåé¡ãçããããã ã
ä¾ãã°ã以ä¸ã®ãããªãã®ã
#[derive(Serialize, Deserialize, Debug)] #[serde(tag = "tag", rename_all = "lowercase")] enum Parent { Foo(Child), Bar(Child), } #[derive(Serialize, Deserialize, Debug)] struct Child { cid: CidLink, }
ããã¯ã "tag"
ãã¼ã§æå®ãããvariantã¨ãã¦deserializeã試ã¿ããJSONã§ããã¨
[ { "tag": "foo", "cid": { "$link": "bafyreidfayvfuwqa7qlnopdjiqrxzs6blmoeu4rujcjtnci5beludirz2a" } }, { "tag": "bar", "cid": { "$link": "bafyreidfayvfuwqa7qlnopdjiqrxzs6blmoeu4rujcjtnci5beludirz2a" } } ]
ã¨ãã£ãé
åã§æ¸¡ãããã¨ãã1ã¤ãã®è¦ç´ 㯠Parent::Foo(Child)
ã2ã¤ç®ã®è¦ç´ 㯠Parent::Bar(Child)
ã¨ãã¦deserializeãããã¨ã«ãªãã
ããã¨åæ§ã®æ§é ãæã¤DAG-CBORã®ãã¼ã¿ã serde_ipld_dagcbor
ã§deserializeããã¨
(ããããããããã±ã¼ã¹ã§Cidãå«ããã®ãdeserializeã§ããªãã¨ããåé¡ ããã£ãã)
Error: Msg("invalid type: newtype struct, expected struct LinkObject")
ã¨ãã£ãã¨ã©ã¼ã«ãªã£ã¦ãã¾ãã
deserializer.is_human_readable()
ã§åå²ãã¦ããã¨ããã§debug printãã¦ã¿ãã¨åãããããã®ãããªæ§é ã®ãã¼ã¿ãdeserializeããã¨ãã¯ã serde_ipld_dagcbor
ã使ã£ã¦ãã¦ã .is_human_readable()
㯠true
ã«ãªã£ã¦ãã¾ããããã
serde
ã®ç´°ããæåãç¥ããªããã©ã Internally tagged ã Untagged ã®å ´åã¯ä¸åº¦mapãã¼ã¿ã¨ãã¦ä¿æãã¦ããtagãå
容ãè¦ã¦åã決å®ããå¿
è¦ãããããï¼ããããç®çã®åã«ãããã³ã°ããéã«ä½¿ãããdeserializerã¯ã¾ãå¥ç©ã«ãªããããã .is_human_readable()
ã¯æå³ãããã®ã«ã¯ãªããªãããã ã
ãããããã®ãããã
ãªã®ã§ãä¸è¿°ã®ããã« enum
ã使ã£ã¦ããç®æã®ä¸ã§ã¯ .is_human_readable()
ã«ããåå²ã¯æ©è½ããªãã
解決ç(ï¼): Ipld
ãçµç±ããã¼ã¿ã®æ§é ã«ãã£ã¦åå²ãã
serde_ipld_dagcbor
ã¨ããååã®éãããã㯠Ipld
ã¨ãããã¼ã¿ã¢ãã«ãå©ç¨ãããã¨ãæ³å®ããã¦ããããã®ãã¼ã¿ã¢ãã«ã¯(äºææ§ã©ãããããææ¡ã§ãã¦ããªãããã©) serde_json::Value
ã¨åãããã«æ§é åããããã¼ã¿ãä¿æã§ãããJSONã«ã¯ç¡ã Link
ã¨ãããã®ãããç¹ã§JSONã®ä¸ä½äºæã¨èãã¦ãè¯ããããããªãã
ã¨ãããã¨ã§ãdeserializeããããã¼ã¿ãä¸åº¦ Ipld
ã«å¤æãã¦ãã¾ãããã®æ§é ãè¦ã¦ãã¼ã¿ãã©ã¼ããããæ¨å®ãã¦åå²ãããã¨ããæ段ãã¨ã£ãã
use libipld_core::ipld::Ipld; impl<'de> Deserialize<'de> for CidLink { fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> { let ipld = Ipld::deserialize(deserializer)?; match &ipld { Ipld::Link(cid) => { return Ok(Self(*cid)); } Ipld::Map(map) => { if map.len() == 1 { if let Some(Ipld::String(link)) = map.get("$link") { return Ok(Self( Cid::try_from(link.as_str()).map_err(serde::de::Error::custom)?, )); } } } _ => {} } Err(serde::de::Error::custom("Invalid cid-link")) } }
å°ãªãã¨ã CidLink
ã¨ãã¦ã®ãã¼ã¿ã§ããã°ã Ipld::deserialize(deserializer)
ã¯åé¡ãªãæåããããã®çµæ㯠Ipld::Link
ãã "$link"
ãã¼ãå«ã Ipld::Map
ãã©ã¡ãããã«ãªãã¯ãã§ãåè
ãªããã®ã¾ã¾å¾ãããCid
ãå©ç¨ããå¾è
ã§ããã°ãã® "$link"
ã®å¤ããCid
ã復å
ããã
ãã®ææ³ã§ããã°ã .is_human_readable()
ã«ä¾åããã«æ£ããå¤å¥ã§ããã©ã¡ãã®ãã¼ã¿ãåæ§ã«deserializeã§ããã
fn main() { let parents_json = serde_json::from_str::<Vec<Parent>>(...)?; println!("{parents_json:?}"); // => Ok([Foo(Child { cid: CidLink(Cid(bafyreidfayvfuwqa7qlnopdjiqrxzs6blmoeu4rujcjtnci5beludirz2a)) }), Bar(Child { cid: CidLink(Cid(bafyreidfayvfuwqa7qlnopdjiqrxzs6blmoeu4rujcjtnci5beludirz2a)) })]) let parents_dagcbor = serde_ipld_dagcbor::from_slice::<Vec<Parent>>(...)?; println!("{parents_dagcbor:?}"); // => Ok([Foo(Child { cid: CidLink(Cid(bafyreidfayvfuwqa7qlnopdjiqrxzs6blmoeu4rujcjtnci5beludirz2a)) }), Bar(Child { cid: CidLink(Cid(bafyreidfayvfuwqa7qlnopdjiqrxzs6blmoeu4rujcjtnci5beludirz2a)) })]) }
ä»åã®ãããªã±ã¼ã¹ã§ããæ©è½ããªããããããªãããä¸å¿ããã§åé¡ã¯è§£æ±ºããã
(ä»ã«ãã£ã¨è¯ãæ¹æ³ããåç¥ã®æ¹ãããã°æãã¦ãã ããâ¦)
æ±ç¨çï¼ ãªè§£æ±ºç: Untagged
ä¸è¬çã«ã¯ããã®ããã«å ´åã«ãã£ã¦ç°ãªãåã«deserializeãããå ´å㯠Untagged ãªenumã使ãã®ãè¯ãã®ãããããªãã
#[derive(Serialize, Deserialize)] #[serde(untagged)] pub enum CidLink { Raw(Cid), LinkObject { #[serde(rename = "$link")] link: String, }, }
serde(untagged)
ã®å ´åã¯variantãé çªã«è©¦ããæåã«deserializeã«æåãããã®ãçµæã¨ãã¦è¿ãã
ã®ã§ãä¸ã®ä¾ã®å ´åã¯ã¾ã Cid
åä½ã¨ãã¦deserializeãã¦ã¿ã¦ã失æããã "$link"
ãã¼ãæã¤ãªãã¸ã§ã¯ãã¨ãã¦deserializeãã¦ã¿ããã¨ããåä½ã«ãªããè¨è¿°ã®é åºãå¤ããã°è©¦è¡ã®é åºãå¤åããã
ãã³ããã¼ã¯
ä¸è¿°ãã"Untagged"ã®å ´åã¯ãè¨è¿°ã®é åºã大äºã«ãªããä¸ã®ä¾ã®éãã ã¨JSONã®ãã¼ã¿ã¯æ¯åæåã«Cid
ã¨ãã¦deserialize試è¡ãã¦å¤±æãã¦ãããããªãã¸ã§ã¯ãã¨ãã¦è©¦è¡ãã¨ãªãå¹çãæªãããããé åºãéã«ããã¨ä»åº¦ã¯DAG-CBORã®ãã¼ã¿ãå¦çããéã«æ¯åãªãã¸ã§ã¯ãã¨ãã¦è©¦è¡ãã¦å¤±æãã¦â¦ã¨ãªãã
ä»åã¯å¯¾è±¡ã2種é¡ã ããªã®ã§å·®ã¯å°ãããããããªããããããä½ç¨®é¡ãããã¨â¦ã
ãã®ç¹ã§ã¯Ipld
ãçµç±ããææ³ã®æ¹ããä¸éã®å¤æå¦çã¯å
¥ããå®å®ããå¹çã¯æå¾
ã§ããã
å½ç¶ãªãããJSONãªãæåãããªãã¸ã§ã¯ãã¨ã㦠DAG-CBORãªãæåããCid
ã¨ãã¦deserializeããã®ãæãå¹ççã§éãã
ãããããåºæºã¨ãã¦ããRawâLinkObjectã®untagged (untagged_1
)ããLinkObjectâRawã®untagged (untagged_2
)ããIpldçµç± (via_ipld
)ãã®ããããã®deserializeããã³ããã¼ã¯ã¨ã£ã¦ã¿ãã
running 8 tests test bench_cbor_only ... bench: 59 ns/iter (+/- 1) test bench_cbor_untagged_1 ... bench: 83 ns/iter (+/- 2) test bench_cbor_untagged_2 ... bench: 172 ns/iter (+/- 8) test bench_cbor_via_ipld ... bench: 63 ns/iter (+/- 1) test bench_json_only ... bench: 77 ns/iter (+/- 2) test bench_json_untagged_1 ... bench: 276 ns/iter (+/- 4) test bench_json_untagged_2 ... bench: 134 ns/iter (+/- 9) test bench_json_via_ipld ... bench: 325 ns/iter (+/- 6)
DAG-CBORã«é¢ãã¦ã¯ã untagged_2
ããã¯ãæ¯åLinkObjectã®è©¦è¡ã®å¾ã«ãªãã®ã§3åã»ã©é
ããªã£ã¦ãã¾ããä¸æ¹ã§ via_ipld
ã¯ã»ã¼åçã®é度ã§å¦çã§ãã¦ããããã ã
JSONã«é¢ãã¦ã¯ãã©ãã大ããé
ããªãããã ãæå¤ã«ã untagged_2
ã§ã2åãããé
ããªãâ¦ãvia_ipld
ã¯cidã®parseå¦çãå
¥ãã®ã§å½ç¶ãªããæãé
ããªã£ã¦ãã¾ããã¨ããçµæã ã£ãã
å®è£ çµæ
ã¨ãããã¨ã§ã
- ã©ããã¦ãJSONã ããæ±ãã¨ãã¨æ¯è¼ããã¨é ããªã
- ããããXRPC Requstã«ã¯JSONãã使ããªã
- DAG-CBORãå¿ è¦ã«ãªãã®ã¯Subscriptionãªã©repoãã¼ã¿ãèªãã¨ãã®ã¿
ã¨ãããã¨ããã£ã¦ãdag-cbor
featureãæå¹ã«ããã¨ãã®ã¿ãIpld
ãçµç±ããæ¹å¼ã§ä¸¡æ¹ã®ãã¼ã¿ãã©ã¼ãããã«å¯¾å¿ããããã«ããã
ãã®å¾
ãã®å®è£
ãããå¾ã types::string::Cid
ã¨ããåãå°å
¥ããã¦ãCid
ã®æåå表ç¾ã§ãããã®ã¯ãã®åã§validationããããã«ãªã£ããLinkObjectã®ãã®ãå¤ã¯ String
ã§ã¯ãªããã® types::string::Cid
ã使ãã¹ãã§ããããªãã¨ãã¯ãJSONã®é度差ããããªã«æ°ã«ãã¦ãä»æ¹ãªãæãã«ãªã£ã¦ããã
running 8 tests test bench_cbor_only ... bench: 59 ns/iter (+/- 0) test bench_cbor_untagged_1 ... bench: 78 ns/iter (+/- 3) test bench_cbor_untagged_2 ... bench: 169 ns/iter (+/- 3) test bench_cbor_via_ipld ... bench: 63 ns/iter (+/- 3) test bench_json_only ... bench: 227 ns/iter (+/- 4) test bench_json_untagged_1 ... bench: 426 ns/iter (+/- 6) test bench_json_untagged_2 ... bench: 288 ns/iter (+/- 5) test bench_json_via_ipld ... bench: 324 ns/iter (+/- 6)
ãã¯ã feature flags ã§ã®åãæ¿ãã¯å»æ¢ãã¦ãå¿ ãIpldãçµç±ããæ¹å¼ã«ãã¦ãã¾ã£ã¦è¯ããããããªãã