chore: rename project quicnprotochat -> quicproquo (binaries: qpq)
Rename the entire workspace:
- Crate packages: quicnprotochat-{core,proto,server,client,gui,p2p,mobile} -> quicproquo-*
- Binary names: quicnprotochat -> qpq, quicnprotochat-server -> qpq-server,
quicnprotochat-gui -> qpq-gui
- Default files: *-state.bin -> qpq-state.bin, *-server.toml -> qpq-server.toml,
*.db -> qpq.db
- Environment variable prefix: QUICNPROTOCHAT_* -> QPQ_*
- App identifier: chat.quicnproto.gui -> chat.quicproquo.gui
- Proto package: quicnprotochat.bench -> quicproquo.bench
- All documentation, Docker, CI, and script references updated
HKDF domain-separation strings and P2P ALPN remain unchanged for
backward compatibility with existing encrypted state and wire protocol.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
78
crates/quicproquo-server/src/federation/address.rs
Normal file
78
crates/quicproquo-server/src/federation/address.rs
Normal file
@@ -0,0 +1,78 @@
|
||||
//! Parse `username@domain` federated addresses.
|
||||
//!
|
||||
//! A bare `username` (no `@`) is treated as local.
|
||||
|
||||
/// A parsed federated address.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct FederatedAddress {
|
||||
pub username: String,
|
||||
pub domain: Option<String>,
|
||||
}
|
||||
|
||||
impl FederatedAddress {
|
||||
/// Parse a `user@domain` string. Bare `user` → domain is `None`.
|
||||
pub fn parse(input: &str) -> Self {
|
||||
// Split on the *last* '@' so usernames can contain '@' in theory.
|
||||
match input.rsplit_once('@') {
|
||||
Some((user, domain)) if !domain.is_empty() && !user.is_empty() => Self {
|
||||
username: user.to_string(),
|
||||
domain: Some(domain.to_string()),
|
||||
},
|
||||
_ => Self {
|
||||
username: input.to_string(),
|
||||
domain: None,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if this address refers to a local user (no domain or domain matches local).
|
||||
pub fn is_local(&self, local_domain: &str) -> bool {
|
||||
match &self.domain {
|
||||
None => true,
|
||||
Some(d) => d == local_domain,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn bare_username() {
|
||||
let addr = FederatedAddress::parse("alice");
|
||||
assert_eq!(addr.username, "alice");
|
||||
assert_eq!(addr.domain, None);
|
||||
assert!(addr.is_local("example.com"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn user_at_domain() {
|
||||
let addr = FederatedAddress::parse("alice@remote.example.com");
|
||||
assert_eq!(addr.username, "alice");
|
||||
assert_eq!(addr.domain, Some("remote.example.com".into()));
|
||||
assert!(!addr.is_local("local.example.com"));
|
||||
assert!(addr.is_local("remote.example.com"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn trailing_at_is_bare() {
|
||||
let addr = FederatedAddress::parse("alice@");
|
||||
assert_eq!(addr.username, "alice@");
|
||||
assert_eq!(addr.domain, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn leading_at_is_bare() {
|
||||
let addr = FederatedAddress::parse("@domain.com");
|
||||
assert_eq!(addr.username, "@domain.com");
|
||||
assert_eq!(addr.domain, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multiple_at_uses_last() {
|
||||
let addr = FederatedAddress::parse("user@org@domain.com");
|
||||
assert_eq!(addr.username, "user@org");
|
||||
assert_eq!(addr.domain, Some("domain.com".into()));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user