use crate::error::Error;
use serde::{Serialize, Serializer, ser};
use std::borrow::Cow;
use std::ops::Deref;
use std::path::{Component, Path, PathBuf};
pub(crate) const TOKEN: &str = "$ser_nix::private::Path";
pub(crate) struct RawEmitter<'a> {
pub output: &'a mut String,
}
impl ser::Serializer for RawEmitter<'_> {
type Ok = ();
type Error = Error;
type SerializeSeq = ser::Impossible<(), Error>;
type SerializeTuple = ser::Impossible<(), Error>;
type SerializeTupleStruct = ser::Impossible<(), Error>;
type SerializeTupleVariant = ser::Impossible<(), Error>;
type SerializeMap = ser::Impossible<(), Error>;
type SerializeStruct = ser::Impossible<(), Error>;
type SerializeStructVariant = ser::Impossible<(), Error>;
fn serialize_str(self, v: &str) -> Result<Self::Ok, Self::Error> {
self.output.push_str(v);
Ok(())
}
fn serialize_i128(self, _v: i128) -> Result<Self::Ok, Self::Error> {
Err(ser::Error::custom("expected string"))
}
fn serialize_u128(self, _v: u128) -> Result<Self::Ok, Self::Error> {
Err(ser::Error::custom("expected string"))
}
fn serialize_bool(self, _v: bool) -> Result<Self::Ok, Self::Error> {
Err(ser::Error::custom("expected string"))
}
fn serialize_i8(self, _v: i8) -> Result<Self::Ok, Self::Error> {
Err(ser::Error::custom("expected string"))
}
fn serialize_i16(self, _v: i16) -> Result<Self::Ok, Self::Error> {
Err(ser::Error::custom("expected string"))
}
fn serialize_i32(self, _v: i32) -> Result<Self::Ok, Self::Error> {
Err(ser::Error::custom("expected string"))
}
fn serialize_i64(self, _v: i64) -> Result<Self::Ok, Self::Error> {
Err(ser::Error::custom("expected string"))
}
fn serialize_u8(self, _v: u8) -> Result<Self::Ok, Self::Error> {
Err(ser::Error::custom("expected string"))
}
fn serialize_u16(self, _v: u16) -> Result<Self::Ok, Self::Error> {
Err(ser::Error::custom("expected string"))
}
fn serialize_u32(self, _v: u32) -> Result<Self::Ok, Self::Error> {
Err(ser::Error::custom("expected string"))
}
fn serialize_u64(self, _v: u64) -> Result<Self::Ok, Self::Error> {
Err(ser::Error::custom("expected string"))
}
fn serialize_f32(self, _v: f32) -> Result<Self::Ok, Self::Error> {
Err(ser::Error::custom("expected string"))
}
fn serialize_f64(self, _v: f64) -> Result<Self::Ok, Self::Error> {
Err(ser::Error::custom("expected string"))
}
fn serialize_char(self, _v: char) -> Result<Self::Ok, Self::Error> {
Err(ser::Error::custom("expected string"))
}
fn serialize_bytes(self, _v: &[u8]) -> Result<Self::Ok, Self::Error> {
Err(ser::Error::custom("expected string"))
}
fn serialize_none(self) -> Result<Self::Ok, Self::Error> {
Err(ser::Error::custom("expected string"))
}
fn serialize_some<T: ?Sized + Serialize>(self, _value: &T) -> Result<Self::Ok, Self::Error> {
Err(ser::Error::custom("expected string"))
}
fn serialize_unit(self) -> Result<Self::Ok, Self::Error> {
Err(ser::Error::custom("expected string"))
}
fn serialize_unit_struct(self, _name: &'static str) -> Result<Self::Ok, Self::Error> {
Err(ser::Error::custom("expected string"))
}
fn serialize_unit_variant(
self,
_name: &'static str,
_variant_index: u32,
_variant: &'static str,
) -> Result<Self::Ok, Self::Error> {
Err(ser::Error::custom("expected string"))
}
fn serialize_newtype_struct<T: ?Sized + Serialize>(
self,
_name: &'static str,
_value: &T,
) -> Result<Self::Ok, Self::Error> {
Err(ser::Error::custom("expected string"))
}
fn serialize_newtype_variant<T: ?Sized + Serialize>(
self,
_name: &'static str,
_variant_index: u32,
_variant: &'static str,
_value: &T,
) -> Result<Self::Ok, Self::Error> {
Err(ser::Error::custom("expected string"))
}
fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> {
Err(ser::Error::custom("expected string"))
}
fn serialize_tuple(self, _len: usize) -> Result<Self::SerializeTuple, Self::Error> {
Err(ser::Error::custom("expected string"))
}
fn serialize_tuple_struct(
self,
_name: &'static str,
_len: usize,
) -> Result<Self::SerializeTupleStruct, Self::Error> {
Err(ser::Error::custom("expected string"))
}
fn serialize_tuple_variant(
self,
_name: &'static str,
_variant_index: u32,
_variant: &'static str,
_len: usize,
) -> Result<Self::SerializeTupleVariant, Self::Error> {
Err(ser::Error::custom("expected string"))
}
fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap, Self::Error> {
Err(ser::Error::custom("expected string"))
}
fn serialize_struct(
self,
_name: &'static str,
_len: usize,
) -> Result<Self::SerializeStruct, Self::Error> {
Err(ser::Error::custom("expected string"))
}
fn serialize_struct_variant(
self,
_name: &'static str,
_variant_index: u32,
_variant: &'static str,
_len: usize,
) -> Result<Self::SerializeStructVariant, Self::Error> {
Err(ser::Error::custom("expected string"))
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct NixPathBuf(PathBuf);
impl NixPathBuf {
pub fn new(path: impl Into<PathBuf>) -> Self {
NixPathBuf(path.into())
}
pub fn as_path(&self) -> &Path {
&self.0
}
pub fn into_path_buf(self) -> PathBuf {
self.0
}
}
impl From<PathBuf> for NixPathBuf {
fn from(path: PathBuf) -> Self {
NixPathBuf(path)
}
}
impl From<&Path> for NixPathBuf {
fn from(path: &Path) -> Self {
NixPathBuf(path.to_path_buf())
}
}
impl From<&str> for NixPathBuf {
fn from(s: &str) -> Self {
NixPathBuf(PathBuf::from(s))
}
}
impl AsRef<Path> for NixPathBuf {
fn as_ref(&self) -> &Path {
&self.0
}
}
impl Deref for NixPathBuf {
type Target = Path;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl Serialize for NixPathBuf {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serialize_path(&self.0, serializer)
}
}
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct NixPath<'a>(&'a Path);
impl<'a> NixPath<'a> {
pub fn new(path: &'a Path) -> Self {
NixPath(path)
}
pub fn as_path(&self) -> &Path {
self.0
}
}
impl<'a> From<&'a Path> for NixPath<'a> {
fn from(path: &'a Path) -> Self {
NixPath(path)
}
}
impl AsRef<Path> for NixPath<'_> {
fn as_ref(&self) -> &Path {
self.0
}
}
impl Serialize for NixPath<'_> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serialize_path(self.0, serializer)
}
}
fn needs_quoting(s: &str) -> bool {
s.bytes().any(|b| {
!matches!(b,
b'a'..=b'z' | b'A'..=b'Z' | b'0'..=b'9'
| b'.' | b'_' | b'-' | b'+' | b'/'
)
})
}
fn escape_nix_string_into(s: &str, out: &mut String) {
let mut chars = s.chars().peekable();
while let Some(c) = chars.next() {
match c {
'"' => out.push_str("\\\""),
'\\' => out.push_str("\\\\"),
'$' if chars.peek() == Some(&'{') => out.push_str("\\$"),
'\n' => out.push_str("\\n"),
'\t' => out.push_str("\\t"),
c => out.push(c),
}
}
}
fn serialize_path<S>(path: &Path, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let path_str = path
.to_str()
.ok_or_else(|| serde::ser::Error::custom("path contains invalid UTF-8 characters"))?;
let has_special_chars = needs_quoting(path_str);
let result: Cow<str> = if path.is_absolute() {
if has_special_chars {
let mut buf = String::with_capacity(6 + path_str.len());
buf.push_str("/. + \"");
escape_nix_string_into(path_str, &mut buf);
buf.push('"');
Cow::Owned(buf)
} else {
Cow::Borrowed(path_str)
}
} else {
match path.components().next() {
Some(Component::CurDir) => {
if has_special_chars {
let rest = path.strip_prefix(Component::CurDir).unwrap();
let rest_str = rest.to_str().unwrap();
let mut buf = String::with_capacity(7 + rest_str.len());
buf.push_str("./. + \"");
escape_nix_string_into(rest_str, &mut buf);
buf.push('"');
Cow::Owned(buf)
} else {
Cow::Borrowed(path_str)
}
}
Some(Component::ParentDir) => {
if has_special_chars {
let rest = path.strip_prefix(Component::ParentDir).unwrap();
let rest_str = rest.to_str().unwrap();
let mut buf = String::with_capacity(8 + rest_str.len());
buf.push_str("../. + \"");
escape_nix_string_into(rest_str, &mut buf);
buf.push('"');
Cow::Owned(buf)
} else {
Cow::Borrowed(path_str)
}
}
_ => {
if has_special_chars {
let mut buf = String::with_capacity(7 + path_str.len());
buf.push_str("./. + \"");
escape_nix_string_into(path_str, &mut buf);
buf.push('"');
Cow::Owned(buf)
} else {
let mut buf = String::with_capacity(2 + path_str.len());
buf.push_str("./");
buf.push_str(path_str);
Cow::Owned(buf)
}
}
}
};
serializer.serialize_newtype_struct(TOKEN, result.as_ref())
}
pub fn as_nix_path<S>(value: &Path, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serialize_path(value, serializer)
}
pub fn as_optional_nix_path<P, S>(value: &Option<P>, serializer: S) -> Result<S::Ok, S::Error>
where
P: AsRef<Path>,
S: Serializer,
{
match value {
Some(v) => serialize_path(v.as_ref(), serializer),
None => serializer.serialize_none(),
}
}