mirror of
https://github.com/Blah-IM/blahrs.git
synced 2025-05-01 00:31:09 +00:00
Simplify rich text on-wire format
This commit is contained in:
parent
c492bb2537
commit
370722731b
5 changed files with 49 additions and 73 deletions
59
Cargo.lock
generated
59
Cargo.lock
generated
|
@ -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]]
|
||||
|
|
|
@ -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"] }
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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',
|
||||
|
|
54
src/types.rs
54
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<RichTextPiece>);
|
||||
|
||||
// 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::<TextAttrs>")]
|
||||
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::<TextAttrs>")]
|
||||
pub attrs: Option<TextAttrs>,
|
||||
impl Serialize for RichTextPiece {
|
||||
fn serialize<S>(&self, ser: S) -> Result<S::Ok, S::Error>
|
||||
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<T: Default + PartialEq>(v: &T) -> bool {
|
||||
|
@ -123,16 +131,19 @@ impl<'de> Deserialize<'de> for RichText {
|
|||
let pieces = <Vec<RichTextPieceRaw>>::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<RoomMember>);
|
|||
impl Serialize for RoomMemberList {
|
||||
fn serialize<S>(&self, ser: S) -> Result<S::Ok, S::Error>
|
||||
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::<RichText>(raw).unwrap();
|
||||
assert!(text.is_canonical());
|
||||
assert_eq!(
|
||||
|
|
Loading…
Add table
Reference in a new issue