2021-09-23 21:12:57 +02:00

85 lines
2.1 KiB
Rust

use std::fmt;
use percent_encoding::{percent_encode, AsciiSet, CONTROLS};
use crate::{EventId, ServerName};
const BASE_URL: &str = "https://matrix.to/#/";
// Controls + Space + reserved characters from RFC 3986. In practice only the
// reserved characters will be encountered most likely, but better be safe.
// https://datatracker.ietf.org/doc/html/rfc3986/#page-13
const TO_ENCODE: &AsciiSet = &CONTROLS
.add(b':')
.add(b'/')
.add(b'?')
.add(b'#')
.add(b'[')
.add(b']')
.add(b'@')
.add(b'!')
.add(b'$')
.add(b'&')
.add(b'\'')
.add(b'(')
.add(b')')
.add(b'*')
.add(b'+')
.add(b',')
.add(b';')
.add(b'=');
/// A reference to a user, room or event.
///
/// Turn it into a `matrix.to` URL through its `Display` implementation (i.e. by
/// interpolating it in a formatting macro or via `.to_string()`).
#[derive(Debug, PartialEq, Eq)]
pub struct MatrixToRef<'a> {
id: &'a str,
event_id: Option<&'a EventId>,
via: Vec<&'a ServerName>,
}
impl<'a> MatrixToRef<'a> {
pub(crate) fn new(id: &'a str, via: Vec<&'a ServerName>) -> Self {
Self { id, event_id: None, via }
}
pub(crate) fn event(id: &'a str, event_id: &'a EventId, via: Vec<&'a ServerName>) -> Self {
Self { id, event_id: Some(event_id), via }
}
}
impl<'a> fmt::Display for MatrixToRef<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(BASE_URL)?;
write!(f, "{}", percent_encode(self.id.as_bytes(), TO_ENCODE))?;
if let Some(ev_id) = self.event_id {
write!(f, "/{}", percent_encode(ev_id.as_bytes(), TO_ENCODE))?;
}
let mut first = true;
for server_name in &self.via {
f.write_str(if first { "?via=" } else { "&via=" })?;
f.write_str(server_name.as_str())?;
first = false;
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use crate::user_id;
#[test]
fn matrix_to_ref() {
assert_eq!(
user_id!("@jplatte:notareal.hs").matrix_to_url().to_string(),
"https://matrix.to/#/%40jplatte%3Anotareal.hs"
);
}
}