feat: add community ui (#52)

Reviewed-on: reya/coop#52
This commit is contained in:
2026-09-23 01:12:51 +00:00
parent 6ff1eeddbb
commit 1032ac3751
80 changed files with 12888 additions and 3143 deletions
+34
View File
@@ -0,0 +1,34 @@
use anyhow::{Result, anyhow};
use base64::engine::{DecodePaddingMode, GeneralPurpose, GeneralPurposeConfig};
use base64::{Engine as _, alphabet};
/// Unpadded base64url (RFC 4648 §5), 43 characters for 32 bytes: §8's value
/// encoding at any depth.
///
/// The reader tolerates non-zero trailing bits; the writer never emits them.
/// The spec's own worked example (`examples.md` §6.2) contains five such
/// values, and a reader cannot tell a mis-encoded named field from a correctly
/// encoded one, so the boundary is the writer's alone.
const BASE64URL: GeneralPurpose = GeneralPurpose::new(
&alphabet::URL_SAFE,
GeneralPurposeConfig::new()
.with_encode_padding(false)
.with_decode_padding_mode(DecodePaddingMode::RequireNone)
.with_decode_allow_trailing_bits(true),
);
pub(crate) fn encode(bytes: &[u8]) -> String {
BASE64URL.encode(bytes)
}
/// Decodes one 32-byte value, the width every §8 field has.
pub(crate) fn decode_32(value: &str) -> Result<[u8; 32]> {
let bytes = BASE64URL
.decode(value.trim())
.map_err(|error| anyhow!("invalid base64url: {error}"))?;
bytes
.as_slice()
.try_into()
.map_err(|_| anyhow!("expected 32 bytes, got {}", bytes.len()))
}
+38 -1
View File
@@ -1,12 +1,18 @@
pub mod base64url;
pub mod derive;
use anyhow::{Result, anyhow, bail};
use data_encoding::HEXLOWER;
use rand::TryRng as _;
use rand::rngs::SysRng;
use serde::Serialize;
use crate::Extra;
/// Decode a 64-character lowercase-hex string into 32 bytes.
///
/// Uppercase and other non-canonical spellings are rejected.
pub(crate) fn decode_hex_32(value: &str) -> Result<[u8; 32]> {
pub fn decode_hex_32(value: &str) -> Result<[u8; 32]> {
decode_hex_lower::<32>(value)
}
@@ -38,3 +44,34 @@ pub(crate) fn random_32() -> Result<[u8; 32]> {
fill_random(&mut bytes)?;
Ok(bytes)
}
/// Hex to unpadded base64url for one 32-byte §8 value.
pub(crate) fn hex32_to_base64(value: &str) -> Result<String> {
Ok(base64url::encode(&decode_hex_32(value)?))
}
/// Unpadded base64url to lowercase hex for one 32-byte §8 value.
pub(crate) fn base64_to_hex32(value: &str) -> Result<String> {
Ok(HEXLOWER.encode(&base64url::decode_32(value)?))
}
/// Canonical JSON bytes: the total-order tie-break every content merge uses.
pub(crate) fn canonical<T: Serialize>(value: &T) -> String {
serde_json::to_string(value).unwrap_or_default()
}
/// Unions an unknown-field map. Where both sides carry a key, the
/// lexicographically lowest canonical bytes win, so two devices converge
/// instead of flapping.
pub(crate) fn union(into: &mut Extra, other: Extra) {
for (key, value) in other {
let replace = match into.get(&key) {
Some(existing) => canonical(&value) < canonical(existing),
None => true,
};
if replace {
into.insert(key, value);
}
}
}