clashlib/clash/
public_handle.rs

1use std::str::FromStr;
2
3use anyhow::anyhow;
4use serde::{Deserialize, Deserializer, Serialize};
5
6/// `PublicHandle` is a hexadecimal string that uniquely identifies a clash
7/// or a puzzle. It is the last part of the URL when viewing a clash or a puzzle
8/// on the CodinGame contribution page.
9///
10/// # Examples
11///
12/// ```
13/// use clashlib::clash::PublicHandle;
14/// use std::str::FromStr;
15///
16/// let handle = PublicHandle::from_str("682102420fbce0fce95e0ee56095ea2b9924");
17/// assert!(handle.is_ok());
18/// let invalid_handle = PublicHandle::from_str("xyz");
19/// assert!(invalid_handle.is_err());
20/// ```
21#[derive(Debug, Clone, Serialize)]
22pub struct PublicHandle(String);
23
24impl FromStr for PublicHandle {
25    type Err = anyhow::Error;
26    fn from_str(s: &str) -> Result<Self, Self::Err> {
27        if s.chars().all(|ch| ch.is_ascii_hexdigit()) {
28            Ok(PublicHandle(String::from(s)))
29        } else {
30            Err(anyhow!("valid handles only contain characters 0-9 and a-f"))
31        }
32    }
33}
34
35impl std::fmt::Display for PublicHandle {
36    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
37        write!(f, "{}", self.0)
38    }
39}
40
41impl<'de> Deserialize<'de> for PublicHandle {
42    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
43    where
44        D: Deserializer<'de>,
45    {
46        let s = String::deserialize(deserializer)?;
47        FromStr::from_str(&s).map_err(serde::de::Error::custom)
48    }
49}