evobench_tools/serde_types/
proper_dirname.rs

1/// Like `ProperFilename` but also doesn't allow a suffix
2// Here subtyping would really help. How to achievethat otherwise?
3// Deref doesn't do it, actually. XX Could use newtype utility crate
4// though?
5use 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";
44// XX Windows will be different than "bytes" and 255.
45
46impl 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}