use regex::Regex; use rusqlite::Row; use serde::{Deserialize, Serialize}; use std::collections::HashMap; use validator::{Validate, ValidationError}; lazy_static! { static ref VALID_CHARACTERS: Regex = Regex::new(r"^[a-zA-Z0-9\_]+$").unwrap(); } pub enum Nip05Table { Table, Pubkey, Username, Relays, JoinedAt, } impl sea_query::Iden for Nip05Table { fn unquoted(&self, s: &mut dyn std::fmt::Write) { write!( s, "{}", match self { Self::Table => "nip05", Self::Pubkey => "pubkey", Self::Username => "username", Self::Relays => "relays", Self::JoinedAt => "joined_at", } ) .unwrap() } } #[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] pub struct UserRow { pub pubkey: String, pub username: String, relays: Vec, joined_at: i64, } impl UserRow { pub fn new(pubkey: String, username: String, relays: Vec) -> Self { Self { pubkey, username, relays, joined_at: nostr::Timestamp::now().as_i64(), } } } impl From<&Row<'_>> for UserRow { fn from(row: &Row) -> Self { let pubkey: String = row.get("pubkey").unwrap(); let username: String = row.get("username").unwrap(); let relays_raw: String = row.get("relays").unwrap_or_default(); let joined_at: i64 = row.get("joined_at").unwrap(); let relays: Vec = match serde_json::from_str(&relays_raw) { Ok(val) => val, Err(_) => vec![], }; Self { pubkey, username, relays, joined_at, } } } #[derive(Debug, Serialize, Deserialize, PartialEq, Clone)] pub struct Nip05Profile { names: HashMap, #[serde(skip_serializing_if = "Option::is_none")] relays: Option>>, } impl From for Nip05Profile { fn from(value: UserRow) -> Self { let mut name: HashMap = HashMap::new(); name.insert(value.username, value.pubkey.clone()); let relay = match value.relays.is_empty() { true => None, false => { let mut relay: HashMap> = HashMap::new(); relay.insert(value.pubkey.clone(), value.relays); Some(relay) } }; Self { names: name, relays: relay, } } } #[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Validate)] pub struct User { #[validate(custom = "validate_pubkey")] pub pubkey: Option, #[validate(length(min = 1), regex = "VALID_CHARACTERS")] pub name: Option, } pub fn validate_pubkey(value: &str) -> Result<(), ValidationError> { use nostr::prelude::FromPkStr; match nostr::Keys::from_pk_str(value) { Ok(_) => Ok(()), Err(_) => Err(ValidationError::new("Unable to parse pubkey")), } } #[cfg(test)] mod tests { use super::{Nip05Profile, UserRow}; #[test] fn make_nip05() { let user_row = UserRow::new( "npub1m2w0ckmkgj4wtvl8muwjynh56j3qd4nddca4exdg4mdrkepvfnhsmusy54".to_string(), "test_user".to_string(), vec![], ); let nip05 = Nip05Profile::from(user_row); dbg!(&nip05); dbg!(serde_json::to_string(&nip05).unwrap()); } #[test] fn parse_relay_vec() { let relays_raw = ""; let relays: Vec = match serde_json::from_str(relays_raw) { Ok(val) => val, Err(_) => vec![], }; dbg!(relays); } }