use std::fmt;
use std::num::ParseIntError;
use std::str::FromStr;
use serde::{Deserialize, Serialize};
#[derive(
Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Serialize, Deserialize,
)]
#[serde(transparent)]
pub struct Kind(u16);
impl Kind {
pub const METADATA: Self = Self(0);
pub const TEXT_NOTE: Self = Self(1);
pub const RECOMMEND_RELAY: Self = Self(2);
pub const CONTACTS: Self = Self(3);
pub const ENCRYPTED_DIRECT_MESSAGE: Self = Self(4);
pub const EVENT_DELETION: Self = Self(5);
pub const REPOST: Self = Self(6);
pub const REACTION: Self = Self(7);
pub const WALLET_CONNECT_INFO: Self = Self(13_194);
pub const WALLET_CONNECT_REQUEST: Self = Self(23_194);
pub const WALLET_CONNECT_RESPONSE: Self = Self(23_195);
pub const WALLET_CONNECT_NOTIFICATION_LEGACY: Self = Self(23_196);
pub const WALLET_CONNECT_NOTIFICATION: Self = Self(23_197);
pub const CASHU_QUOTE: Self = Self(7_374);
pub const CASHU_TOKEN: Self = Self(7_375);
pub const CASHU_HISTORY: Self = Self(7_376);
pub const CASHU_WALLET: Self = Self(17_375);
pub const NUTZAP: Self = Self(9_321);
pub const NUTZAP_INFO: Self = Self(10_019);
pub const FILE_SERVER_LIST: Self = Self(10_096);
pub const BADGE_AWARD: Self = Self(8);
pub const ZAP_REQUEST: Self = Self(9_734);
pub const ZAP_RECEIPT: Self = Self(9_735);
pub const BADGE_DEFINITION: Self = Self(30_009);
pub const CHANNEL_CREATION: Self = Self(40);
pub const CHANNEL_METADATA: Self = Self(41);
pub const CHANNEL_MESSAGE: Self = Self(42);
pub const CHANNEL_HIDE_MESSAGE: Self = Self(43);
pub const CHANNEL_MUTE_USER: Self = Self(44);
pub const FILE_METADATA: Self = Self(1063);
pub const GENERIC_REPOST: Self = Self(16);
pub const REPORTING: Self = Self(1984);
pub const LABEL: Self = Self(1985);
pub const HIGHLIGHT: Self = Self(9_802);
pub const ZAP_GOAL: Self = Self(9_041);
pub const APP_RECOMMENDATION: Self = Self(31_989);
pub const APP_HANDLER: Self = Self(31_990);
pub const VIDEO_SHORT: Self = Self(22);
pub const VIDEO_NORMAL: Self = Self(21);
pub const VIDEO_NORMAL_ADDRESSABLE: Self = Self(34_235);
pub const VIDEO_SHORT_ADDRESSABLE: Self = Self(34_236);
pub const LIVE_STREAM: Self = Self(30_311);
pub const MEETING_SPACE: Self = Self(30_312);
pub const MEETING_ROOM: Self = Self(30_313);
pub const LIVE_CHAT_MESSAGE: Self = Self(1_311);
pub const ROOM_PRESENCE: Self = Self(10_312);
pub const CALENDAR_DATE_EVENT: Self = Self(31_922);
pub const CALENDAR_TIME_EVENT: Self = Self(31_923);
pub const CALENDAR: Self = Self(31_924);
pub const CALENDAR_RSVP: Self = Self(31_925);
pub const CLASSIFIED_LISTING: Self = Self(30_402);
pub const CLASSIFIED_LISTING_DRAFT: Self = Self(30_403);
pub const RELAY_DISCOVERY: Self = Self(30_166);
pub const RELAY_MONITOR: Self = Self(10_166);
pub const WIKI_ARTICLE: Self = Self(30_818);
pub const WIKI_REDIRECT: Self = Self(30_819);
pub const WIKI_MERGE_REQUEST: Self = Self(818);
pub const AUTHENTICATION: Self = Self(22242);
pub const SEAL: Self = Self(13);
pub const PRIVATE_DIRECT_MESSAGE: Self = Self(14);
pub const FILE_MESSAGE: Self = Self(15);
pub const GIFT_WRAP: Self = Self(1059);
pub const DM_RELAYS: Self = Self(10_050);
pub const LONG_FORM_TEXT_NOTE: Self = Self(30023);
pub const RELAY_LIST: Self = Self(10002);
pub const MUTE_LIST: Self = Self(10_000);
pub const PINNED_NOTES: Self = Self(10_001);
pub const BOOKMARKS: Self = Self(10_003);
pub const COMMUNITIES_LIST: Self = Self(10_004);
pub const PUBLIC_CHATS_LIST: Self = Self(10_005);
pub const BLOCKED_RELAYS: Self = Self(10_006);
pub const SEARCH_RELAYS: Self = Self(10_007);
pub const PROFILE_BADGES: Self = Self(10_008);
pub const SIMPLE_GROUPS_LIST: Self = Self(10_009);
pub const RELAY_FEEDS: Self = Self(10_012);
pub const INTERESTS_LIST: Self = Self(10_015);
pub const MEDIA_FOLLOWS: Self = Self(10_020);
pub const EMOJIS_LIST: Self = Self(10_030);
pub const BLOSSOM_SERVERS: Self = Self(10_063);
pub const FOLLOW_SET: Self = Self(30_000);
pub const RELAY_SET: Self = Self(30_002);
pub const BOOKMARK_SET: Self = Self(30_003);
pub const ARTICLES_CURATION_SET: Self = Self(30_004);
pub const VIDEOS_CURATION_SET: Self = Self(30_005);
pub const PICTURES_CURATION_SET: Self = Self(30_006);
pub const KIND_MUTE_SET: Self = Self(30_007);
pub const BADGE_SET: Self = Self(30_008);
pub const INTEREST_SET: Self = Self(30_015);
pub const EMOJI_SET: Self = Self(30_030);
pub const RELEASE_ARTIFACT_SET: Self = Self(30_063);
pub const APP_CURATION_SET: Self = Self(30_267);
pub const CALENDAR_SET: Self = Self(31_924);
pub const STARTER_PACK: Self = Self(39_089);
pub const MEDIA_STARTER_PACK: Self = Self(39_092);
pub const CHAT_MESSAGE: Self = Self(9);
pub const THREAD: Self = Self(11);
pub const PICTURE: Self = Self(20);
pub const PUBLIC_MESSAGE: Self = Self(24);
pub const REQUEST_TO_VANISH: Self = Self(62);
pub const CHESS: Self = Self(64);
pub const CODE_SNIPPET: Self = Self(1_337);
pub const VOICE_MESSAGE: Self = Self(1_222);
pub const VOICE_MESSAGE_REPLY: Self = Self(1_244);
pub const AUCTION_BID: Self = Self(1_021);
pub const AUCTION_BID_CONFIRMATION: Self = Self(1_022);
pub const POLL: Self = Self(1_068);
pub const POLL_RESPONSE: Self = Self(1_018);
pub const GIT_PATCH: Self = Self(1_617);
pub const GIT_PULL_REQUEST: Self = Self(1_618);
pub const GIT_PULL_REQUEST_UPDATE: Self = Self(1_619);
pub const GIT_ISSUE: Self = Self(1_621);
pub const GIT_STATUS_OPEN: Self = Self(1_630);
pub const GIT_STATUS_APPLIED: Self = Self(1_631);
pub const GIT_STATUS_CLOSED: Self = Self(1_632);
pub const GIT_STATUS_DRAFT: Self = Self(1_633);
pub const TORRENT: Self = Self(2_003);
pub const TORRENT_COMMENT: Self = Self(2_004);
pub const GROUP_JOIN_REQUEST: Self = Self(9_021);
pub const GROUP_LEAVE_REQUEST: Self = Self(9_022);
pub const GROUP_PUT_USER: Self = Self(9_000);
pub const GROUP_REMOVE_USER: Self = Self(9_001);
pub const GROUP_EDIT_METADATA: Self = Self(9_002);
pub const GROUP_DELETE_EVENT: Self = Self(9_005);
pub const GROUP_CREATE: Self = Self(9_007);
pub const GROUP_DELETE: Self = Self(9_008);
pub const GROUP_CREATE_INVITE: Self = Self(9_009);
pub const GROUP_METADATA: Self = Self(39_000);
pub const GROUP_ADMINS: Self = Self(39_001);
pub const GROUP_MEMBERS: Self = Self(39_002);
pub const GROUP_ROLES: Self = Self(39_003);
pub const DRAFT_WRAP: Self = Self(31_234);
pub const PRIVATE_STORAGE_RELAYS: Self = Self(10_013);
pub const MARKETPLACE_STALL: Self = Self(30_017);
pub const MARKETPLACE_PRODUCT: Self = Self(30_018);
pub const MARKETPLACE_UI: Self = Self(30_019);
pub const MARKETPLACE_AUCTION: Self = Self(30_020);
pub const GIT_REPOSITORY: Self = Self(30_617);
pub const GIT_REPOSITORY_STATE: Self = Self(30_618);
pub const GIT_GRASP_LIST: Self = Self(10_317);
pub const WEB_BOOKMARK: Self = Self(39_701);
#[must_use]
pub const fn new(kind: u16) -> Self {
Self(kind)
}
#[must_use]
pub const fn as_u16(self) -> u16 {
self.0
}
#[must_use]
pub const fn is_replaceable(self) -> bool {
matches!(self.0, 0 | 3 | 10_000..=19_999)
}
#[must_use]
pub const fn is_addressable(self) -> bool {
matches!(self.0, 30_000..=39_999)
}
#[must_use]
pub const fn is_ephemeral(self) -> bool {
matches!(self.0, 20_000..=29_999)
}
#[must_use]
pub const fn is_regular(self) -> bool {
matches!(self.0, 1 | 2 | 4..45 | 1000..10_000)
}
#[must_use]
pub const fn is_reserved(self) -> bool {
matches!(self.0, 40_000..=65_535)
}
#[must_use]
pub const fn is_unclassified(self) -> bool {
!(self.is_regular()
|| self.is_replaceable()
|| self.is_addressable()
|| self.is_ephemeral())
}
}
impl From<u16> for Kind {
fn from(value: u16) -> Self {
Self(value)
}
}
impl From<Kind> for u16 {
fn from(value: Kind) -> Self {
value.0
}
}
impl fmt::Display for Kind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.0, f)
}
}
impl FromStr for Kind {
type Err = ParseIntError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
s.parse::<u16>().map(Self)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn raw_round_trip() {
let kind = Kind::new(1234);
assert_eq!(kind.as_u16(), 1234);
}
#[test]
fn replaceable_classification() {
assert!(Kind::METADATA.is_replaceable());
assert!(Kind::CONTACTS.is_replaceable());
assert!(Kind::RELAY_LIST.is_replaceable());
assert!(!Kind::TEXT_NOTE.is_replaceable());
}
#[test]
fn addressable_classification() {
assert!(Kind::LONG_FORM_TEXT_NOTE.is_addressable());
assert!(!Kind::TEXT_NOTE.is_addressable());
}
#[test]
fn ephemeral_range() {
assert!(Kind::AUTHENTICATION.is_ephemeral());
assert!(!Kind::TEXT_NOTE.is_ephemeral());
}
#[test]
fn regular_range() {
assert!(Kind::TEXT_NOTE.is_regular());
assert!(Kind::REACTION.is_regular());
assert!(Kind::GIFT_WRAP.is_regular());
assert!(Kind::new(44).is_regular());
assert!(Kind::new(1_000).is_regular());
assert!(Kind::new(9_999).is_regular());
assert!(!Kind::METADATA.is_regular());
assert!(!Kind::AUTHENTICATION.is_regular());
assert!(!Kind::LONG_FORM_TEXT_NOTE.is_regular());
assert!(!Kind::new(45).is_regular());
assert!(!Kind::new(999).is_regular());
}
#[test]
fn reserved_range() {
assert!(Kind::new(40_000).is_reserved());
assert!(Kind::new(65_535).is_reserved());
assert!(!Kind::new(39_999).is_reserved());
}
#[test]
fn unclassified_range_is_distinct_from_every_category() {
assert!(Kind::new(45).is_unclassified());
assert!(Kind::new(500).is_unclassified());
assert!(Kind::new(999).is_unclassified());
assert!(Kind::new(40_000).is_unclassified());
assert!(Kind::new(65_535).is_unclassified());
assert!(!Kind::TEXT_NOTE.is_unclassified());
assert!(!Kind::METADATA.is_unclassified());
assert!(!Kind::AUTHENTICATION.is_unclassified());
assert!(!Kind::LONG_FORM_TEXT_NOTE.is_unclassified());
}
#[test]
fn classification_is_disjoint_and_complete() {
for raw in 0_u16..=u16::MAX {
let kind = Kind::new(raw);
let count = u32::from(kind.is_regular())
+ u32::from(kind.is_replaceable())
+ u32::from(kind.is_addressable())
+ u32::from(kind.is_ephemeral())
+ u32::from(kind.is_unclassified());
assert_eq!(
count, 1,
"kind {raw} matched {count} categories (must be exactly 1)"
);
}
}
#[test]
fn display_matches_integer() {
assert_eq!(Kind::TEXT_NOTE.to_string(), "1");
}
#[test]
fn parse_from_str() {
let kind: Kind = "30023".parse().unwrap();
assert_eq!(kind, Kind::LONG_FORM_TEXT_NOTE);
assert!("abc".parse::<Kind>().is_err());
}
#[test]
fn serde_is_integer() {
let kind = Kind::TEXT_NOTE;
let json = serde_json::to_string(&kind).unwrap();
assert_eq!(json, "1");
let parsed: Kind = serde_json::from_str(&json).unwrap();
assert_eq!(parsed, kind);
}
}