diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 00000000..f720b046 --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,17 @@ +environment: + matrix: + - APPVEYOR_RUST_CHANNEL: stable + - APPVEYOR_RUST_CHANNEL: nightly + +install: + # Install rust, x86_64-pc-windows-msvc host + - appveyor DownloadFile https://win.rustup.rs/ -FileName rustup-init.exe + - rustup-init.exe -y --default-host x86_64-pc-windows-msvc --default-toolchain %APPVEYOR_RUST_CHANNEL% + - set PATH=C:\msys64\usr\bin;%PATH%;C:\Users\appveyor\.cargo\bin + - rustc -vV + - cargo -vV + +build: false + +test_script: + - sh -c 'PATH=`rustc --print sysroot`/bin:$PATH ./travis.sh' diff --git a/serde/src/de/impls.rs b/serde/src/de/impls.rs index 35f3cdf2..7bffab32 100644 --- a/serde/src/de/impls.rs +++ b/serde/src/de/impls.rs @@ -26,7 +26,7 @@ use std::net; use std::path; use core::str; #[cfg(feature = "std")] -use std::ffi::CString; +use std::ffi::{CString, OsString}; #[cfg(all(feature = "std", feature="unstable"))] use std::ffi::CStr; @@ -910,6 +910,7 @@ impl Visitor for PathBufVisitor { } } + #[cfg(feature = "std")] impl Deserialize for path::PathBuf { fn deserialize(deserializer: D) -> Result @@ -921,6 +922,110 @@ impl Deserialize for path::PathBuf { /////////////////////////////////////////////////////////////////////////////// +#[cfg(all(feature = "std", any(unix, windows)))] +enum OsStringKind { + Unix, + Windows, +} + +#[cfg(all(feature = "std", any(unix, windows)))] +static OSSTR_VARIANTS: &'static [&'static str] = &["Unix", "Windows"]; + +#[cfg(all(feature = "std", any(unix, windows)))] +impl Deserialize for OsStringKind { + fn deserialize(deserializer: D) -> Result + where D: Deserializer + { + struct KindVisitor; + + impl Visitor for KindVisitor { + type Value = OsStringKind; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("`Unix` or `Windows`") + } + + fn visit_u32(self, value: u32) -> Result + where E: Error, + { + match value { + 0 => Ok(OsStringKind::Unix), + 1 => Ok(OsStringKind::Windows), + _ => Err(Error::invalid_value(Unexpected::Unsigned(value as u64), &self)) + } + } + + fn visit_str(self, value: &str) -> Result + where E: Error, + { + match value { + "Unix" => Ok(OsStringKind::Unix), + "Windows" => Ok(OsStringKind::Windows), + _ => Err(Error::unknown_variant(value, OSSTR_VARIANTS)), + } + } + + fn visit_bytes(self, value: &[u8]) -> Result + where E: Error, + { + match value { + b"Unix" => Ok(OsStringKind::Unix), + b"Windows" => Ok(OsStringKind::Windows), + _ => { + match str::from_utf8(value) { + Ok(value) => Err(Error::unknown_variant(value, OSSTR_VARIANTS)), + Err(_) => { + Err(Error::invalid_value(Unexpected::Bytes(value), &self)) + } + } + } + } + } + } + + deserializer.deserialize(KindVisitor) + } +} + +#[cfg(all(feature = "std", any(unix, windows)))] +struct OsStringVisitor; + +#[cfg(all(feature = "std", any(unix, windows)))] +impl Visitor for OsStringVisitor { + type Value = OsString; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("os string") + } + + #[cfg(unix)] + fn visit_enum(self, visitor: V) -> Result + where V: EnumVisitor, + { + use std::os::unix::ffi::OsStringExt; + + match try!(visitor.visit_variant()) { + (OsStringKind::Unix, variant) => { + variant.visit_newtype().map(OsString::from_vec) + } + (OsStringKind::Windows, _) => { + Err(Error::custom("cannot deserialize windows os string on unix")) + } + } + } +} + +#[cfg(all(feature = "std", any(unix, windows)))] +impl Deserialize for OsString { + fn deserialize(deserializer: D) -> Result + where D: Deserializer + { + deserializer.deserialize_enum("OsString", OSSTR_VARIANTS, OsStringVisitor) + } +} + +/////////////////////////////////////////////////////////////////////////////// + #[cfg(any(feature = "std", feature = "alloc"))] impl Deserialize for Box { fn deserialize(deserializer: D) -> Result, D::Error> diff --git a/serde/src/ser/impls.rs b/serde/src/ser/impls.rs index 53ac7bf0..4f817760 100644 --- a/serde/src/ser/impls.rs +++ b/serde/src/ser/impls.rs @@ -20,7 +20,7 @@ use core::ops; #[cfg(feature = "std")] use std::path; #[cfg(feature = "std")] -use std::ffi::{CString, CStr}; +use std::ffi::{CString, CStr, OsString, OsStr}; #[cfg(feature = "std")] use std::rc::Rc; #[cfg(all(feature = "alloc", not(feature = "std")))] @@ -714,6 +714,41 @@ impl Serialize for path::PathBuf { } } +#[cfg(all(feature = "std", any(unix, windows)))] +impl Serialize for OsStr { + #[cfg(unix)] + fn serialize(&self, serializer: S) -> Result + where S: Serializer + { + use std::os::unix::ffi::OsStrExt; + serializer.serialize_newtype_variant("OsString", + 0, + "Unix", + self.as_bytes()) + } + #[cfg(windows)] + fn serialize(&self, serializer: S) -> Result + where S: Serializer + { + use std::os::windows::ffi::OsStrExt; + let val = self.encode_wide().collect::>(); + serializer.serialize_newtype_variant("OsString", + 1, + "Windows", + &val) + } +} + +#[cfg(all(feature = "std", any(unix, windows)))] +#[cfg(feature = "std")] +impl Serialize for OsString { + fn serialize(&self, serializer: S) -> Result + where S: Serializer + { + self.as_os_str().serialize(serializer) + } +} + #[cfg(feature = "unstable")] impl Serialize for NonZero where T: Serialize + Zeroable diff --git a/test_suite/no_std/src/main.rs b/test_suite/no_std/src/main.rs index 6639b5cc..a4469819 100644 --- a/test_suite/no_std/src/main.rs +++ b/test_suite/no_std/src/main.rs @@ -1,7 +1,8 @@ -#![feature(lang_items, start, libc)] +#![feature(lang_items, start, libc, compiler_builtins_lib)] #![no_std] extern crate libc; +extern crate compiler_builtins; #[start] fn start(_argc: isize, _argv: *const *const u8) -> isize { diff --git a/test_suite/tests/test_de.rs b/test_suite/tests/test_de.rs index 066eb2a3..8d21844c 100644 --- a/test_suite/tests/test_de.rs +++ b/test_suite/tests/test_de.rs @@ -8,7 +8,7 @@ use std::net; use std::path::PathBuf; use std::time::Duration; use std::default::Default; -use std::ffi::CString; +use std::ffi::{CString, OsString}; #[cfg(feature = "unstable")] use std::ffi::CStr; @@ -913,6 +913,56 @@ declare_tests! { } } +#[cfg(unix)] +#[test] +fn test_osstring() { + use std::os::unix::ffi::OsStringExt; + + let value = OsString::from_vec(vec![1, 2, 3]); + let tokens = [ + Token::EnumStart("OsString"), + Token::Str("Unix"), + Token::SeqStart(Some(2)), + Token::SeqSep, + Token::U8(1), + + Token::SeqSep, + Token::U8(2), + + Token::SeqSep, + Token::U8(3), + Token::SeqEnd, + ]; + + assert_de_tokens(&value, &tokens); + assert_de_tokens_ignore(&tokens); +} + +#[cfg(windows)] +#[test] +fn test_osstring() { + use std::os::windows::ffi::OsStringExt; + + let value = OsString::from_wide(&[1, 2, 3]); + let tokens = [ + Token::EnumStart("OsString"), + Token::Str("Windows"), + Token::SeqStart(Some(2)), + Token::SeqSep, + Token::U16(1), + + Token::SeqSep, + Token::U16(2), + + Token::SeqSep, + Token::U16(3), + Token::SeqEnd, + ]; + + assert_de_tokens(&value, &tokens); + assert_de_tokens_ignore(&tokens); +} + #[cfg(feature = "unstable")] #[test] fn test_cstr() { diff --git a/test_suite/tests/test_ser.rs b/test_suite/tests/test_ser.rs index d58fdfe7..7613cb8a 100644 --- a/test_suite/tests/test_ser.rs +++ b/test_suite/tests/test_ser.rs @@ -422,6 +422,7 @@ fn test_net_ipaddr() { } #[test] +#[cfg(unix)] fn test_cannot_serialize_paths() { let path = unsafe { str::from_utf8_unchecked(b"Hello \xF0\x90\x80World") diff --git a/travis.sh b/travis.sh index d2e1a998..d5f62d28 100755 --- a/travis.sh +++ b/travis.sh @@ -10,6 +10,11 @@ channel() { pwd (set -x; cargo "$@") fi + elif [ -n "${APPVEYOR}" ]; then + if [ "${APPVEYOR_RUST_CHANNEL}" = "${CHANNEL}" ]; then + pwd + (set -x; cargo "$@") + fi else pwd (set -x; cargo "+${CHANNEL}" "$@")