diff --git a/Cargo.lock b/Cargo.lock index 05ab96a..0917931 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -107,7 +107,7 @@ checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn", ] [[package]] @@ -240,8 +240,7 @@ dependencies = [ "serde-aux", "serde-constant", "serde_json", - "serde_tuple", - "syn 2.0.76", + "syn", "tokio", "tokio-stream", "tower-http", @@ -352,7 +351,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.76", + "syn", ] [[package]] @@ -432,7 +431,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn", ] [[package]] @@ -585,7 +584,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn", ] [[package]] @@ -1051,7 +1050,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn", ] [[package]] @@ -1110,7 +1109,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn", ] [[package]] @@ -1432,7 +1431,7 @@ checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn", ] [[package]] @@ -1457,27 +1456,6 @@ dependencies = [ "serde", ] -[[package]] -name = "serde_tuple" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4f025b91216f15a2a32aa39669329a475733590a015835d1783549a56d09427" -dependencies = [ - "serde", - "serde_tuple_macros", -] - -[[package]] -name = "serde_tuple_macros" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4076151d1a2b688e25aaf236997933c66e18b870d0369f8b248b8ab2be630d7e" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -1579,17 +1557,6 @@ version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - [[package]] name = "syn" version = "2.0.76" @@ -1699,7 +1666,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn", ] [[package]] @@ -1812,7 +1779,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn", ] [[package]] @@ -1977,7 +1944,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.76", + "syn", "wasm-bindgen-shared", ] @@ -2011,7 +1978,7 @@ checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -2193,7 +2160,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 53b6c98..54b402c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,7 +22,6 @@ serde = { version = "1.0.209", features = ["derive"] } serde-aux = "4.5.0" serde-constant = "0.1.0" serde_json = "1.0.127" -serde_tuple = "0.5.0" tokio = { version = "1.39.3", features = ["macros", "rt-multi-thread", "sync"] } tokio-stream = { version = "0.1.15", features = ["sync"] } tower-http = { version = "0.5.2", features = ["cors", "limit"] } diff --git a/docs/webapi.yaml b/docs/webapi.yaml index 70a63b0..995147a 100644 --- a/docs/webapi.yaml +++ b/docs/webapi.yaml @@ -83,7 +83,7 @@ paths: payload: typ: chat room: 7ed9e067-ec37-4054-9fc2-b1bd890929bd - rich_text: [["before "],["bold ",{"b":true}],["italic bold ",{"b":true,"i":true}],["end"]] + rich_text: ["before ",["bold ",{"b":true}],["italic bold ",{"b":true,"i":true}],"end"] timestamp: 1724966284 user: 83ce46ced47ec0391c64846cbb6c507250ead4985b6a044d68751edc46015dd7 responses: diff --git a/pages/main.js b/pages/main.js index d0eff03..fc18c8e 100644 --- a/pages/main.js +++ b/pages/main.js @@ -122,8 +122,8 @@ async function showChatMsg(chat) { function richTextToHtml(richText) { let ret = '' - for (let [text, attrs] of richText) { - if (attrs === undefined) attrs = {}; + for (let e of richText) { + const [text, attrs] = typeof e === 'string' ? [e, {}] : e; // Incomplete cases. const tags = [ [attrs.b, 'b'], @@ -241,7 +241,7 @@ async function postChat(text) { if (text.startsWith('[')) { richText = JSON.parse(text); } else { - richText = [[text]]; + richText = [text]; } const signedPayload = await signData({ typ: 'chat', diff --git a/src/types.rs b/src/types.rs index b243752..e5def22 100644 --- a/src/types.rs +++ b/src/types.rs @@ -12,8 +12,7 @@ use ed25519_dalek::{ Signature, Signer, SigningKey, VerifyingKey, PUBLIC_KEY_LENGTH, SIGNATURE_LENGTH, }; use rand_core::RngCore; -use serde::{de, Deserialize, Deserializer, Serialize}; -use serde_tuple::{Deserialize_tuple, Serialize_tuple}; +use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; use uuid::Uuid; const TIMESTAMP_TOLERENCE: u64 = 90; @@ -93,22 +92,31 @@ pub struct ChatPayload { #[serde(transparent)] pub struct RichText(pub Vec); -// NB. This field is excluded from field order check, because it has tuple representation. -#[derive(Debug, PartialEq, Eq, Serialize_tuple)] +#[derive(Debug, PartialEq, Eq)] pub struct RichTextPiece { - pub text: String, - #[serde(skip_serializing_if = "is_default::")] pub attrs: TextAttrs, + pub text: String, } -/// The protocol representation of `RichTextPiece` which keeps nullity of `attrs` for -/// canonicalization check. -// NB. This field is excluded from field order check, because it has tuple representation. -#[derive(Debug, Deserialize_tuple)] -struct RichTextPieceRaw { - pub text: String, - #[serde(default, skip_serializing_if = "is_default::")] - pub attrs: Option, +impl Serialize for RichTextPiece { + fn serialize(&self, ser: S) -> Result + where + S: Serializer, + { + if is_default(&self.attrs) { + self.text.serialize(ser) + } else { + (&self.text, &self.attrs).serialize(ser) + } + } +} + +/// The protocol representation of `RichTextPiece`. +#[derive(Debug, Clone, Deserialize)] +#[serde(untagged)] +enum RichTextPieceRaw { + Text(String), + TextWithAttrs(String, TextAttrs), } fn is_default(v: &T) -> bool { @@ -123,16 +131,19 @@ impl<'de> Deserialize<'de> for RichText { let pieces = >::deserialize(de)?; if pieces .iter() - .any(|p| matches!(&p.attrs, Some(attrs) if is_default(attrs))) + .any(|p| matches!(&p, RichTextPieceRaw::TextWithAttrs(_, attrs) if is_default(attrs))) { return Err(de::Error::custom("not in canonical form")); } let this = Self( pieces .into_iter() - .map(|RichTextPieceRaw { text, attrs }| RichTextPiece { - text, - attrs: attrs.unwrap_or_default(), + .map(|raw| { + let (text, attrs) = match raw { + RichTextPieceRaw::Text(text) => (text, TextAttrs::default()), + RichTextPieceRaw::TextWithAttrs(text, attrs) => (text, attrs), + }; + RichTextPiece { text, attrs } }) .collect(), ); @@ -144,7 +155,7 @@ impl<'de> Deserialize<'de> for RichText { } // TODO: This protocol format is quite large. Could use bitflags for database. -#[derive(Debug, Default, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] pub struct TextAttrs { #[serde(default, rename = "b", skip_serializing_if = "is_default")] pub bold: bool, @@ -286,7 +297,7 @@ pub struct RoomMemberList(pub Vec); impl Serialize for RoomMemberList { fn serialize(&self, ser: S) -> Result where - S: serde::Serializer, + S: Serializer, { self.0.serialize(ser) } @@ -465,8 +476,7 @@ mod tests { #[test] fn rich_text_serde() { - let raw = - r#"[["before "],["bold ",{"b":true}],["italic bold ",{"b":true,"i":true}],["end"]]"#; + let raw = r#"["before ",["bold ",{"b":true}],["italic bold ",{"b":true,"i":true}],"end"]"#; let text = serde_json::from_str::(raw).unwrap(); assert!(text.is_canonical()); assert_eq!(