Skip to main content

stable_osstring_encoding/
lib.rs

1#![warn(missing_docs, clippy::all)]
2#![doc = include_str!("../readme.md")]
3
4
5
6#[cfg(not(any(unix, windows)))]
7compile_error!(
8	"This crate currently only supports Windows and Unix (Linux and Macos). Adding support for your platform is likely very easy, please consider opening an issue for it in \"stable-osstring-encoding\"'s issue tracker."
9);
10
11
12
13use std::{
14	borrow::Cow,
15	ffi::OsString,
16	path::{Path, PathBuf},
17};
18
19
20
21/// Contains the implementation for unix builds, including Linux and Macos
22#[cfg(unix)]
23pub mod impl_unix;
24/// Contains the implementation for windows builds
25#[cfg(windows)]
26pub mod impl_windows;
27
28
29
30/// Defines the encoding width
31#[cfg(unix)]
32pub type EncodingWidth = u8;
33/// Defines the encoding width
34#[cfg(windows)]
35pub type EncodingWidth = u16;
36
37/// A simple alias for `Vec<EncodingWidth>`
38pub type StableOsString = Vec<EncodingWidth>;
39
40
41
42/// Converts an `OsString` or `OsStr` to an encoding that is stable across rust compiler versions
43pub trait ToStableEncoding {
44	/// Converts an `OsString` or `OsStr` to an encoding that is stable across rust compiler versions
45	fn to_stable_encoding(&self) -> StableOsString;
46}
47
48/// Converts an `OsString` from an encoding that is stable across rust compiler versions, bypassing data copies if possible
49pub trait IntoStableEncoding {
50	/// Converts an `OsString` from an encoding that is stable across rust compiler versions, bypassing data copies if possible
51	fn into_stable_encoding(self) -> StableOsString;
52}
53
54/// Crates an `OsString` from an encoding that is stable across rust compiler versions, bypassing data copies if possible
55pub trait FromStableEncoding {
56	/// Converts an `OsString` from an encoding that is stable across rust compiler versions, bypassing data copies if possible
57	///
58	/// This takes `Into<Cow<[EncodingWidth]>>` so that either a slice can be passed (which always allocates and copies data) or a vec can be passed (which might be able to skip allocating and copying data)
59	///
60	/// # Safety
61	///
62	/// The given bytes must be compatible with the underlying of the platform's `OsStr` encoding (reminder: this crate only make it safe to pass data between different rust versions)
63	unsafe fn from_stable_encoding<'a>(encoded: impl Into<Cow<'a, [EncodingWidth]>>) -> Self;
64}
65
66
67
68impl ToStableEncoding for Path {
69	fn to_stable_encoding(&self) -> StableOsString {
70		self.as_os_str().to_stable_encoding()
71	}
72}
73
74impl ToStableEncoding for PathBuf {
75	fn to_stable_encoding(&self) -> StableOsString {
76		self.as_os_str().to_stable_encoding()
77	}
78}
79
80impl IntoStableEncoding for PathBuf {
81	fn into_stable_encoding(self) -> StableOsString {
82		self.into_os_string().to_stable_encoding()
83	}
84}
85
86impl FromStableEncoding for PathBuf {
87	unsafe fn from_stable_encoding<'a>(encoded: impl Into<Cow<'a, [EncodingWidth]>>) -> Self {
88		unsafe { PathBuf::from(OsString::from_stable_encoding(encoded)) }
89	}
90}
91
92impl<'a, T> IntoStableEncoding for Cow<'a, T>
93where
94	T: ToStableEncoding + ToOwned,
95	<T as ToOwned>::Owned: IntoStableEncoding,
96{
97	fn into_stable_encoding(self) -> StableOsString {
98		match self {
99			Cow::Borrowed(v) => v.to_stable_encoding(),
100			Cow::Owned(v) => v.into_stable_encoding(),
101		}
102	}
103}
104
105
106
107#[cfg(test)]
108mod test {
109	use crate::{FromStableEncoding, IntoStableEncoding, ToStableEncoding};
110	use std::{ffi::OsString, path::PathBuf};
111
112	#[test]
113	fn basics() {
114		let start = OsString::from("test");
115		let as_stable_1 = start.to_stable_encoding();
116		let as_stable_2 = start.into_stable_encoding();
117		assert_eq!(as_stable_1, as_stable_2);
118
119		let as_stable_1 = &*as_stable_1; // make sure &[EncodingWidth] can be given to from_stable_encoding()
120
121		let as_os_string_1 = unsafe { OsString::from_stable_encoding(as_stable_1) };
122		let as_os_string_2 = unsafe { OsString::from_stable_encoding(as_stable_2) };
123		assert_eq!(as_os_string_1, as_os_string_2);
124
125		let as_str = as_os_string_1.to_str();
126		assert_eq!(as_str, Some("test"));
127	}
128
129	#[test]
130	fn path_buf() {
131		let start = PathBuf::from(OsString::from("test"));
132		let as_stable_1 = start.to_stable_encoding();
133		let as_stable_2 = start.into_stable_encoding();
134		assert_eq!(as_stable_1, as_stable_2);
135
136		let as_stable_1 = &*as_stable_1; // make sure &[EncodingWidth] can be given to from_stable_encoding()
137
138		let as_os_string_1 = unsafe { PathBuf::from_stable_encoding(as_stable_1) };
139		let as_os_string_2 = unsafe { PathBuf::from_stable_encoding(as_stable_2) };
140		assert_eq!(as_os_string_1, as_os_string_2);
141
142		let as_str = as_os_string_1.to_str();
143		assert_eq!(as_str, Some("test"));
144	}
145}