Define PreferredUsername struct
This commit is contained in:
parent
fc848a8d53
commit
71824aa524
@ -1,3 +1,4 @@
|
||||
use ::anyhow::{self, anyhow};
|
||||
use activitystreams::{
|
||||
actor::{ApActor, Group, Person},
|
||||
base::{AnyBase, Base, Extends},
|
||||
@ -18,6 +19,10 @@ use rocket::{
|
||||
response::{Responder, Response},
|
||||
Outcome,
|
||||
};
|
||||
use std::{
|
||||
convert::{TryFrom, TryInto},
|
||||
str::FromStr, fmt,
|
||||
};
|
||||
use tokio::{
|
||||
runtime,
|
||||
time::{sleep, Duration},
|
||||
@ -241,6 +246,86 @@ pub trait IntoId {
|
||||
fn into_id(self) -> Id;
|
||||
}
|
||||
|
||||
#[repr(transparent)]
|
||||
#[derive(Shrinkwrap, PartialEq, Eq, Clone, Serialize, Deserialize, Debug)]
|
||||
pub struct PreferredUsername(String);
|
||||
|
||||
// Mastodon allows only /[a-z0-9_]+([a-z0-9_\.-]+[a-z0-9_]+)?/i for `preferredUsername`
|
||||
impl PreferredUsername {
|
||||
fn validate(name: &str) -> anyhow::Result<()> {
|
||||
let len = name.len();
|
||||
if len < 3 {
|
||||
return Err(anyhow!("FQN must be longer than 2 characters"));
|
||||
}
|
||||
match name.chars().enumerate().find(|(pos, c)| {
|
||||
if pos == &0 || pos == &(len - 1) {
|
||||
c != &'_' && !c.is_ascii_alphanumeric()
|
||||
} else {
|
||||
match c {
|
||||
'_' | '\\' | '.' | '-' => false,
|
||||
_ => !c.is_ascii_alphanumeric(),
|
||||
}
|
||||
}
|
||||
}) {
|
||||
Some((pos, c)) => Err(anyhow!("Invaliad character at {}: {}", pos, c)),
|
||||
None => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// The given string must be match against /\A[a-z0-9_]+([a-z0-9_\.-]+[a-z0-9_]+)?\z/i in Ruby's RegExp which is required by Mastodon.
|
||||
pub unsafe fn new_unchecked(name: String) -> Self {
|
||||
Self(name)
|
||||
}
|
||||
|
||||
pub fn new(name: String) -> anyhow::Result<Self> {
|
||||
Self::validate(&name).map(|_| unsafe { Self::new_unchecked(name) })
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for PreferredUsername {
|
||||
fn fmt(&self, f:&mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
self.0.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<String> for PreferredUsername {
|
||||
type Error = anyhow::Error;
|
||||
|
||||
fn try_from(name: String) -> std::result::Result<Self, Self::Error> {
|
||||
Self::new(name)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&str> for PreferredUsername {
|
||||
type Error = anyhow::Error;
|
||||
|
||||
fn try_from(name: &str) -> std::result::Result<Self, Self::Error> {
|
||||
Self::new(name.to_owned())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PreferredUsername> for String {
|
||||
fn from(preferred_username: PreferredUsername) -> Self {
|
||||
preferred_username.0
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<str> for PreferredUsername {
|
||||
fn as_ref(&self) -> &str {
|
||||
self.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for PreferredUsername {
|
||||
type Err = anyhow::Error;
|
||||
|
||||
fn from_str(name: &str) -> std::result::Result<Self, Self::Err> {
|
||||
name.try_into()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ApSignature {
|
||||
@ -524,6 +609,35 @@ mod tests {
|
||||
use assert_json_diff::assert_json_eq;
|
||||
use serde_json::{from_str, json, to_value};
|
||||
|
||||
#[test]
|
||||
fn preferred_username() {
|
||||
assert!(PreferredUsername::new("".into()).is_err());
|
||||
assert!(PreferredUsername::new("a".into()).is_err());
|
||||
assert!(PreferredUsername::new("ab".into()).is_err());
|
||||
assert_eq!(
|
||||
"abc",
|
||||
PreferredUsername::new("abc".into()).unwrap().as_str()
|
||||
);
|
||||
assert_eq!(
|
||||
"abcd",
|
||||
PreferredUsername::new("abcd".into()).unwrap().as_str()
|
||||
);
|
||||
assert!(PreferredUsername::new("abc-".into()).is_err());
|
||||
assert!(PreferredUsername::new("日本語".into()).is_err());
|
||||
assert_eq!("abc", "abc".parse::<PreferredUsername>().unwrap().as_str());
|
||||
assert!("abc-".parse::<PreferredUsername>().is_err());
|
||||
assert_eq!(
|
||||
PreferredUsername::new("admin".into()).unwrap(),
|
||||
PreferredUsername("admin".into())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn prefferred_username_to_string() {
|
||||
let pu = PreferredUsername::new("admin".into()).unwrap();
|
||||
assert_eq!("admin".to_string(), pu.to_string());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn se_ap_signature() {
|
||||
let ap_signature = ApSignature {
|
||||
|
Loading…
Reference in New Issue
Block a user