tanoshi_lib/models/
version.rs1use std::{fmt, marker::PhantomData, str::FromStr};
2
3use serde::{
4 de::{self, MapAccess, Visitor},
5 Deserialize, Deserializer, Serialize,
6};
7
8use crate::error::Error;
9
10#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
11pub struct Version {
12 pub major: usize,
13 pub minor: usize,
14 pub patch: usize,
15}
16
17impl FromStr for Version {
18 type Err = Error;
19
20 fn from_str(s: &str) -> Result<Self, Self::Err> {
21 let split = s.split('.').collect::<Vec<&str>>();
22 match split.as_slice() {
23 [major, minor, patch] => Ok(Version {
24 major: major.parse().map_err(|_| Error::InvalidVersion)?,
25 minor: minor.parse().map_err(|_| Error::InvalidVersion)?,
26 patch: patch.parse().map_err(|_| Error::InvalidVersion)?,
27 }),
28 _ => Err(Error::InvalidVersion),
29 }
30 }
31}
32
33impl std::fmt::Display for Version {
34 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
35 f.write_fmt(format_args!("{}.{}.{}", self.major, self.minor, self.patch))
36 }
37}
38
39#[allow(dead_code)]
41fn string_or_struct<'de, T, D>(deserializer: D) -> Result<T, D::Error>
42where
43 T: Deserialize<'de> + FromStr<Err = Error>,
44 D: Deserializer<'de>,
45{
46 struct StringOrStruct<T>(PhantomData<fn() -> T>);
52
53 impl<'de, T> Visitor<'de> for StringOrStruct<T>
54 where
55 T: Deserialize<'de> + FromStr<Err = Error>,
56 {
57 type Value = T;
58
59 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
60 formatter.write_str("string or map")
61 }
62
63 fn visit_str<E>(self, value: &str) -> Result<T, E>
64 where
65 E: de::Error,
66 {
67 Ok(FromStr::from_str(value).unwrap())
68 }
69
70 fn visit_map<M>(self, map: M) -> Result<T, M::Error>
71 where
72 M: MapAccess<'de>,
73 {
74 Deserialize::deserialize(de::value::MapAccessDeserializer::new(map))
79 }
80 }
81
82 deserializer.deserialize_any(StringOrStruct(PhantomData))
83}