evobench_tools/serde_types/
proper_dirname.rs1use std::{path::Path, str::FromStr};
6
7use serde::de::Visitor;
8
9use super::proper_filename::is_proper_filename;
10
11pub fn has_extension(v: &str) -> bool {
12 let p: &Path = v.as_ref();
13 p.extension().is_some()
14}
15
16pub fn is_proper_dirname(v: &str) -> bool {
17 is_proper_filename(v) && !has_extension(v)
18}
19
20#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Hash)]
21pub struct ProperDirname(String);
22
23impl ProperDirname {
24 pub fn as_str(&self) -> &str {
25 &self.0
26 }
27}
28
29impl AsRef<str> for ProperDirname {
30 fn as_ref(&self) -> &str {
31 self.as_str()
32 }
33}
34
35impl<'t> From<&'t ProperDirname> for &'t str {
36 fn from(value: &'t ProperDirname) -> Self {
37 value.as_str()
38 }
39}
40
41const ERR_MSG: &str = "a file name (not path), must not contain '/', '\\n', '\\0', \
42 and must not be \".\", \"..\", the empty string, or longer than 255 bytes, \
43 and not have a file extension";
44impl FromStr for ProperDirname {
47 type Err = &'static str;
48
49 fn from_str(v: &str) -> Result<Self, Self::Err> {
50 if !is_proper_dirname(v) {
51 return Err(ERR_MSG);
52 }
53 Ok(ProperDirname(v.to_owned()))
54 }
55}
56
57struct FilenameVisitor;
58impl<'de> Visitor<'de> for FilenameVisitor {
59 type Value = ProperDirname;
60
61 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
62 formatter.write_str(ERR_MSG)
63 }
64
65 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
66 where
67 E: serde::de::Error,
68 {
69 v.parse().map_err(E::custom)
70 }
71}
72
73impl<'de> serde::Deserialize<'de> for ProperDirname {
74 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
75 deserializer.deserialize_str(FilenameVisitor)
76 }
77}
78
79impl serde::Serialize for ProperDirname {
80 fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
81 where
82 S: serde::Serializer,
83 {
84 serializer.serialize_str(&self.0)
85 }
86}