gravityfile_core/
path_serde.rs1use serde::ser::{SerializeSeq, SerializeStruct};
3use serde::{Deserialize, Deserializer, Serialize, Serializer};
4use std::path::{Path, PathBuf};
5
6pub fn serialize<S: Serializer>(path: &Path, serializer: S) -> Result<S::Ok, S::Error> {
7 if !serializer.is_human_readable() || path.to_str().is_some() {
8 return path.serialize(serializer);
9 }
10 let mut record = serializer.serialize_struct("NativePath", 2)?;
11 #[cfg(unix)]
12 {
13 use std::os::unix::ffi::OsStrExt;
14 record.serialize_field("platform", "unix")?;
15 record.serialize_field("bytes", path.as_os_str().as_bytes())?;
16 }
17 #[cfg(windows)]
18 {
19 use std::os::windows::ffi::OsStrExt;
20 record.serialize_field("platform", "windows")?;
21 record.serialize_field("wide", &path.as_os_str().encode_wide().collect::<Vec<_>>())?;
22 }
23 #[cfg(not(any(unix, windows)))]
24 return Err(serde::ser::Error::custom(
25 "native path encoding unavailable on this platform",
26 ));
27 record.end()
28}
29
30#[derive(Deserialize)]
31#[serde(tag = "platform", rename_all = "lowercase", deny_unknown_fields)]
32enum NativePath {
33 Unix { bytes: Vec<u8> },
34 Windows { wide: Vec<u16> },
35}
36
37#[derive(Deserialize)]
38#[serde(untagged)]
39enum HumanPath {
40 Text(String),
41 Native(NativePath),
42}
43
44pub fn deserialize<'de, D: Deserializer<'de>>(deserializer: D) -> Result<PathBuf, D::Error> {
45 if !deserializer.is_human_readable() {
46 return PathBuf::deserialize(deserializer);
47 }
48 match HumanPath::deserialize(deserializer)? {
49 HumanPath::Text(text) => Ok(PathBuf::from(text)),
50 HumanPath::Native(NativePath::Unix { bytes }) => {
51 #[cfg(unix)]
52 {
53 use std::os::unix::ffi::OsStringExt;
54 Ok(std::ffi::OsString::from_vec(bytes).into())
55 }
56 #[cfg(not(unix))]
57 {
58 let _ = bytes;
59 Err(serde::de::Error::custom(
60 "Unix native path cannot be decoded on this platform",
61 ))
62 }
63 }
64 HumanPath::Native(NativePath::Windows { wide }) => {
65 #[cfg(windows)]
66 {
67 use std::os::windows::ffi::OsStringExt;
68 Ok(std::ffi::OsString::from_wide(&wide).into())
69 }
70 #[cfg(not(windows))]
71 {
72 let _ = wide;
73 Err(serde::de::Error::custom(
74 "Windows native path cannot be decoded on this platform",
75 ))
76 }
77 }
78 }
79}
80
81struct PathRef<'a>(&'a Path);
82impl Serialize for PathRef<'_> {
83 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
84 serialize(self.0, serializer)
85 }
86}
87#[derive(Deserialize)]
88struct OwnedPath(#[serde(deserialize_with = "deserialize")] PathBuf);
89
90pub mod list {
92 use super::*;
93 pub fn serialize<S: Serializer>(paths: &[PathBuf], serializer: S) -> Result<S::Ok, S::Error> {
94 let mut sequence = serializer.serialize_seq(Some(paths.len()))?;
95 for path in paths {
96 sequence.serialize_element(&PathRef(path))?;
97 }
98 sequence.end()
99 }
100 pub fn deserialize<'de, D: Deserializer<'de>>(
101 deserializer: D,
102 ) -> Result<Vec<PathBuf>, D::Error> {
103 Ok(Vec::<OwnedPath>::deserialize(deserializer)?
104 .into_iter()
105 .map(|path| path.0)
106 .collect())
107 }
108}
109
110pub mod optional_pair {
112 use super::*;
113 pub fn serialize<S: Serializer, T: Serialize>(
114 pair: &Option<(PathBuf, T)>,
115 serializer: S,
116 ) -> Result<S::Ok, S::Error> {
117 match pair {
118 Some((path, value)) => serializer.serialize_some(&(PathRef(path), value)),
119 None => serializer.serialize_none(),
120 }
121 }
122 pub fn deserialize<'de, D: Deserializer<'de>, T: Deserialize<'de>>(
123 deserializer: D,
124 ) -> Result<Option<(PathBuf, T)>, D::Error> {
125 Ok(Option::<(OwnedPath, T)>::deserialize(deserializer)?
126 .map(|(path, value)| (path.0, value)))
127 }
128}
129
130pub mod triples {
132 use super::*;
133 pub fn serialize<S: Serializer, A: Serialize, B: Serialize>(
134 values: &[(PathBuf, A, B)],
135 serializer: S,
136 ) -> Result<S::Ok, S::Error> {
137 let mut sequence = serializer.serialize_seq(Some(values.len()))?;
138 for (path, a, b) in values {
139 sequence.serialize_element(&(PathRef(path), a, b))?;
140 }
141 sequence.end()
142 }
143 pub fn deserialize<'de, D: Deserializer<'de>, A: Deserialize<'de>, B: Deserialize<'de>>(
144 deserializer: D,
145 ) -> Result<Vec<(PathBuf, A, B)>, D::Error> {
146 Ok(Vec::<(OwnedPath, A, B)>::deserialize(deserializer)?
147 .into_iter()
148 .map(|(path, a, b)| (path.0, a, b))
149 .collect())
150 }
151}