pull/189/head
Chip Senkbeil 1 year ago
parent 793844ccbe
commit 62608e0718
No known key found for this signature in database
GPG Key ID: 35EF1F8EC72A4131

@ -189,7 +189,6 @@ mod tests {
use super::*;
mod capabilities {
use super::*;
#[test]
@ -247,7 +246,7 @@ mod tests {
// and could change as we change how serialization is done. This is merely to verify
// that we can serialize since there are times when serde fails to serialize at
// runtime.
let _ = rmp_serde::to_vec(&capabilities).unwrap();
let _ = rmp_serde::encode::to_vec_named(&capabilities).unwrap();
}
#[test]
@ -256,7 +255,7 @@ mod tests {
// verify that we are not corrupting or preventing issues when serializing on a
// client/server and then trying to deserialize on the other side. This has happened
// enough times with minor changes that we need tests to verify.
let buf = rmp_serde::to_vec(
let buf = rmp_serde::encode::to_vec_named(
&[Capability {
kind: "some kind".to_string(),
description: "some description".to_string(),
@ -266,7 +265,7 @@ mod tests {
)
.unwrap();
let capabilities: Capabilities = rmp_serde::from_slice(&buf).unwrap();
let capabilities: Capabilities = rmp_serde::decode::from_slice(&buf).unwrap();
assert_eq!(
capabilities,
[Capability {
@ -327,22 +326,22 @@ mod tests {
// and could change as we change how serialization is done. This is merely to verify
// that we can serialize since there are times when serde fails to serialize at
// runtime.
let _ = rmp_serde::to_vec(&capability).unwrap();
let _ = rmp_serde::encode::to_vec_named(&capability).unwrap();
}
#[test]
fn should_be_able_to_deserialize_from_msgpack() {
// NOTE: It may seem odd that we are serializing just to deserialize, but this is to
// verify that we are not corrupting or preventing issues when serializing on a
// verify that we are not corrupting or causing issues when serializing on a
// client/server and then trying to deserialize on the other side. This has happened
// enough times with minor changes that we need tests to verify.
let buf = rmp_serde::to_vec(&Capability {
let buf = rmp_serde::encode::to_vec_named(&Capability {
kind: "some kind".to_string(),
description: "some description".to_string(),
})
.unwrap();
let capability: Capability = rmp_serde::from_slice(&buf).unwrap();
let capability: Capability = rmp_serde::decode::from_slice(&buf).unwrap();
assert_eq!(
capability,
Capability {

@ -128,6 +128,10 @@ impl BitOr for ChangeKind {
pub struct ChangeKindSet(HashSet<ChangeKind>);
impl ChangeKindSet {
pub fn new(set: impl IntoIterator<Item = ChangeKind>) -> Self {
set.into_iter().collect()
}
/// Produces an empty set of [`ChangeKind`]
pub fn empty() -> Self {
Self(HashSet::new())
@ -278,3 +282,95 @@ impl Default for ChangeKindSet {
Self::empty()
}
}
#[cfg(test)]
mod tests {
use super::*;
mod change_kind_set {
use super::*;
#[test]
fn should_be_able_to_serialize_to_json() {
let set = ChangeKindSet::new([ChangeKind::MoveTo]);
let value = serde_json::to_value(set).unwrap();
assert_eq!(value, serde_json::json!(["move_to"]));
}
#[test]
fn should_be_able_to_deserialize_from_json() {
let value = serde_json::json!(["move_to"]);
let set: ChangeKindSet = serde_json::from_value(value).unwrap();
assert_eq!(set, ChangeKindSet::new([ChangeKind::MoveTo]));
}
#[test]
fn should_be_able_to_serialize_to_msgpack() {
let set = ChangeKindSet::new([ChangeKind::MoveTo]);
// NOTE: We don't actually check the output here because it's an implementation detail
// and could change as we change how serialization is done. This is merely to verify
// that we can serialize since there are times when serde fails to serialize at
// runtime.
let _ = rmp_serde::encode::to_vec_named(&set).unwrap();
}
#[test]
fn should_be_able_to_deserialize_from_msgpack() {
// NOTE: It may seem odd that we are serializing just to deserialize, but this is to
// verify that we are not corrupting or causing issues when serializing on a
// client/server and then trying to deserialize on the other side. This has happened
// enough times with minor changes that we need tests to verify.
let buf =
rmp_serde::encode::to_vec_named(&ChangeKindSet::new([ChangeKind::MoveTo])).unwrap();
let set: ChangeKindSet = rmp_serde::decode::from_slice(&buf).unwrap();
assert_eq!(set, ChangeKindSet::new([ChangeKind::MoveTo]));
}
}
mod change_kind {
use super::*;
#[test]
fn should_be_able_to_serialize_to_json() {
let kind = ChangeKind::MoveTo;
let value = serde_json::to_value(kind).unwrap();
assert_eq!(value, serde_json::json!("move_to"));
}
#[test]
fn should_be_able_to_deserialize_from_json() {
let value = serde_json::json!("move_to");
let kind: ChangeKind = serde_json::from_value(value).unwrap();
assert_eq!(kind, ChangeKind::MoveTo);
}
#[test]
fn should_be_able_to_serialize_to_msgpack() {
let kind = ChangeKind::MoveTo;
// NOTE: We don't actually check the output here because it's an implementation detail
// and could change as we change how serialization is done. This is merely to verify
// that we can serialize since there are times when serde fails to serialize at
// runtime.
let _ = rmp_serde::encode::to_vec_named(&kind).unwrap();
}
#[test]
fn should_be_able_to_deserialize_from_msgpack() {
// NOTE: It may seem odd that we are serializing just to deserialize, but this is to
// verify that we are not corrupting or causing issues when serializing on a
// client/server and then trying to deserialize on the other side. This has happened
// enough times with minor changes that we need tests to verify.
let buf = rmp_serde::encode::to_vec_named(&ChangeKind::MoveTo).unwrap();
let kind: ChangeKind = rmp_serde::decode::from_slice(&buf).unwrap();
assert_eq!(kind, ChangeKind::MoveTo);
}
}
}

@ -43,3 +43,47 @@ impl DerefMut for Cmd {
&mut self.0
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn should_be_able_to_serialize_to_json() {
let cmd = Cmd::new("echo some text");
let value = serde_json::to_value(cmd).unwrap();
assert_eq!(value, serde_json::json!("echo some text"));
}
#[test]
fn should_be_able_to_deserialize_from_json() {
let value = serde_json::json!("echo some text");
let cmd: Cmd = serde_json::from_value(value).unwrap();
assert_eq!(cmd, Cmd::new("echo some text"));
}
#[test]
fn should_be_able_to_serialize_to_msgpack() {
let cmd = Cmd::new("echo some text");
// NOTE: We don't actually check the output here because it's an implementation detail
// and could change as we change how serialization is done. This is merely to verify
// that we can serialize since there are times when serde fails to serialize at
// runtime.
let _ = rmp_serde::encode::to_vec_named(&cmd).unwrap();
}
#[test]
fn should_be_able_to_deserialize_from_msgpack() {
// NOTE: It may seem odd that we are serializing just to deserialize, but this is to
// verify that we are not corrupting or causing issues when serializing on a
// client/server and then trying to deserialize on the other side. This has happened
// enough times with minor changes that we need tests to verify.
let buf = rmp_serde::encode::to_vec_named(&Cmd::new("echo some text")).unwrap();
let cmd: Cmd = rmp_serde::decode::from_slice(&buf).unwrap();
assert_eq!(cmd, Cmd::new("echo some text"));
}
}

@ -191,3 +191,125 @@ impl From<ErrorKind> for io::ErrorKind {
}
}
}
#[cfg(test)]
mod tests {
use super::*;
mod error {
use super::*;
#[test]
fn should_be_able_to_serialize_to_json() {
let error = Error {
kind: ErrorKind::AddrInUse,
description: "some description".to_string(),
};
let value = serde_json::to_value(error).unwrap();
assert_eq!(
value,
serde_json::json!({
"kind": "addr_in_use",
"description": "some description",
})
);
}
#[test]
fn should_be_able_to_deserialize_from_json() {
let value = serde_json::json!({
"kind": "addr_in_use",
"description": "some description",
});
let error: Error = serde_json::from_value(value).unwrap();
assert_eq!(
error,
Error {
kind: ErrorKind::AddrInUse,
description: "some description".to_string(),
}
);
}
#[test]
fn should_be_able_to_serialize_to_msgpack() {
let error = Error {
kind: ErrorKind::AddrInUse,
description: "some description".to_string(),
};
// NOTE: We don't actually check the output here because it's an implementation detail
// and could change as we change how serialization is done. This is merely to verify
// that we can serialize since there are times when serde fails to serialize at
// runtime.
let _ = rmp_serde::encode::to_vec_named(&error).unwrap();
}
#[test]
fn should_be_able_to_deserialize_from_msgpack() {
// NOTE: It may seem odd that we are serializing just to deserialize, but this is to
// verify that we are not corrupting or preventing issues when serializing on a
// client/server and then trying to deserialize on the other side. This has happened
// enough times with minor changes that we need tests to verify.
let buf = rmp_serde::encode::to_vec_named(&Error {
kind: ErrorKind::AddrInUse,
description: "some description".to_string(),
})
.unwrap();
let error: Error = rmp_serde::decode::from_slice(&buf).unwrap();
assert_eq!(
error,
Error {
kind: ErrorKind::AddrInUse,
description: "some description".to_string(),
}
);
}
}
mod error_kind {
use super::*;
#[test]
fn should_be_able_to_serialize_to_json() {
let kind = ErrorKind::AddrInUse;
let value = serde_json::to_value(kind).unwrap();
assert_eq!(value, serde_json::json!("addr_in_use"));
}
#[test]
fn should_be_able_to_deserialize_from_json() {
let value = serde_json::json!("addr_in_use");
let kind: ErrorKind = serde_json::from_value(value).unwrap();
assert_eq!(kind, ErrorKind::AddrInUse);
}
#[test]
fn should_be_able_to_serialize_to_msgpack() {
let kind = ErrorKind::AddrInUse;
// NOTE: We don't actually check the output here because it's an implementation detail
// and could change as we change how serialization is done. This is merely to verify
// that we can serialize since there are times when serde fails to serialize at
// runtime.
let _ = rmp_serde::encode::to_vec_named(&kind).unwrap();
}
#[test]
fn should_be_able_to_deserialize_from_msgpack() {
// NOTE: It may seem odd that we are serializing just to deserialize, but this is to
// verify that we are not corrupting or causing issues when serializing on a
// client/server and then trying to deserialize on the other side. This has happened
// enough times with minor changes that we need tests to verify.
let buf = rmp_serde::encode::to_vec_named(&ErrorKind::AddrInUse).unwrap();
let kind: ErrorKind = rmp_serde::decode::from_slice(&buf).unwrap();
assert_eq!(kind, ErrorKind::AddrInUse);
}
}
}

@ -41,3 +41,133 @@ impl From<StdFileType> for FileType {
}
}
}
#[cfg(test)]
mod tests {
use super::*;
mod dir_entry {
use super::*;
#[test]
fn should_be_able_to_serialize_to_json() {
let entry = DirEntry {
path: PathBuf::from("dir").join("file"),
file_type: FileType::File,
depth: 1,
};
let path = entry.path.to_str().unwrap().to_string();
let value = serde_json::to_value(entry).unwrap();
assert_eq!(
value,
serde_json::json!({
"path": path,
"file_type": "file",
"depth": 1,
})
);
}
#[test]
fn should_be_able_to_deserialize_from_json() {
let value = serde_json::json!({
"path": "test-file",
"file_type": "file",
"depth": 0,
});
let entry: DirEntry = serde_json::from_value(value).unwrap();
assert_eq!(
entry,
DirEntry {
path: PathBuf::from("test-file"),
file_type: FileType::File,
depth: 0,
}
);
}
#[test]
fn should_be_able_to_serialize_to_msgpack() {
let entry = DirEntry {
path: PathBuf::from("dir").join("file"),
file_type: FileType::File,
depth: 1,
};
// NOTE: We don't actually check the output here because it's an implementation detail
// and could change as we change how serialization is done. This is merely to verify
// that we can serialize since there are times when serde fails to serialize at
// runtime.
let _ = rmp_serde::encode::to_vec_named(&entry).unwrap();
}
#[test]
fn should_be_able_to_deserialize_from_msgpack() {
// NOTE: It may seem odd that we are serializing just to deserialize, but this is to
// verify that we are not corrupting or causing issues when serializing on a
// client/server and then trying to deserialize on the other side. This has happened
// enough times with minor changes that we need tests to verify.
let buf = rmp_serde::encode::to_vec_named(&DirEntry {
path: PathBuf::from("test-file"),
file_type: FileType::File,
depth: 0,
})
.unwrap();
let entry: DirEntry = rmp_serde::decode::from_slice(&buf).unwrap();
assert_eq!(
entry,
DirEntry {
path: PathBuf::from("test-file"),
file_type: FileType::File,
depth: 0,
}
);
}
}
mod file_type {
use super::*;
#[test]
fn should_be_able_to_serialize_to_json() {
let ty = FileType::File;
let value = serde_json::to_value(ty).unwrap();
assert_eq!(value, serde_json::json!("file"));
}
#[test]
fn should_be_able_to_deserialize_from_json() {
let value = serde_json::json!("file");
let ty: FileType = serde_json::from_value(value).unwrap();
assert_eq!(ty, FileType::File);
}
#[test]
fn should_be_able_to_serialize_to_msgpack() {
let ty = FileType::File;
// NOTE: We don't actually check the output here because it's an implementation detail
// and could change as we change how serialization is done. This is merely to verify
// that we can serialize since there are times when serde fails to serialize at
// runtime.
let _ = rmp_serde::encode::to_vec_named(&ty).unwrap();
}
#[test]
fn should_be_able_to_deserialize_from_msgpack() {
// NOTE: It may seem odd that we are serializing just to deserialize, but this is to
// verify that we are not corrupting or causing issues when serializing on a
// client/server and then trying to deserialize on the other side. This has happened
// enough times with minor changes that we need tests to verify.
let buf = rmp_serde::encode::to_vec_named(&FileType::File).unwrap();
let ty: FileType = rmp_serde::decode::from_slice(&buf).unwrap();
assert_eq!(ty, FileType::File);
}
}
}

@ -6,75 +6,93 @@ use serde::{Deserialize, Serialize};
use crate::common::FileType;
use crate::utils::{deserialize_u128_option, serialize_u128_option};
/// Represents metadata about some path on a remote machine
/// Represents metadata about some path on a remote machine.
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct Metadata {
/// Canonicalized path to the file or directory, resolving symlinks, only included
/// if flagged during the request
/// Canonicalized path to the file or directory, resolving symlinks, only included if flagged
/// during the request.
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(default)]
pub canonicalized_path: Option<PathBuf>,
/// Represents the type of the entry as a file/dir/symlink
/// Represents the type of the entry as a file/dir/symlink.
pub file_type: FileType,
/// Size of the file/directory/symlink in bytes
/// Size of the file/directory/symlink in bytes.
pub len: u64,
/// Whether or not the file/directory/symlink is marked as unwriteable
/// Whether or not the file/directory/symlink is marked as unwriteable.
pub readonly: bool,
/// Represents the last time (in milliseconds) when the file/directory/symlink was accessed;
/// can be optional as certain systems don't support this
/// can be optional as certain systems don't support this.
///
/// Note that this is represented as a string and not a number when serialized!
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(serialize_with = "serialize_u128_option")]
#[serde(deserialize_with = "deserialize_u128_option")]
#[serde(default)]
pub accessed: Option<u128>,
/// Represents when (in milliseconds) the file/directory/symlink was created;
/// can be optional as certain systems don't support this
/// can be optional as certain systems don't support this.
///
/// Note that this is represented as a string and not a number when serialized!
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(serialize_with = "serialize_u128_option")]
#[serde(deserialize_with = "deserialize_u128_option")]
#[serde(default)]
pub created: Option<u128>,
/// Represents the last time (in milliseconds) when the file/directory/symlink was modified;
/// can be optional as certain systems don't support this
/// can be optional as certain systems don't support this.
///
/// Note that this is represented as a string and not a number when serialized!
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(serialize_with = "serialize_u128_option")]
#[serde(deserialize_with = "deserialize_u128_option")]
#[serde(default)]
pub modified: Option<u128>,
/// Represents metadata that is specific to a unix remote machine
/// Represents metadata that is specific to a unix remote machine.
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(default)]
pub unix: Option<UnixMetadata>,
/// Represents metadata that is specific to a windows remote machine
/// Represents metadata that is specific to a windows remote machine.
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(default)]
pub windows: Option<WindowsMetadata>,
}
/// Represents unix-specific metadata about some path on a remote machine
/// Represents unix-specific metadata about some path on a remote machine.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct UnixMetadata {
/// Represents whether or not owner can read from the file
/// Represents whether or not owner can read from the file.
pub owner_read: bool,
/// Represents whether or not owner can write to the file
/// Represents whether or not owner can write to the file.
pub owner_write: bool,
/// Represents whether or not owner can execute the file
/// Represents whether or not owner can execute the file.
pub owner_exec: bool,
/// Represents whether or not associated group can read from the file
/// Represents whether or not associated group can read from the file.
pub group_read: bool,
/// Represents whether or not associated group can write to the file
/// Represents whether or not associated group can write to the file.
pub group_write: bool,
/// Represents whether or not associated group can execute the file
/// Represents whether or not associated group can execute the file.
pub group_exec: bool,
/// Represents whether or not other can read from the file
/// Represents whether or not other can read from the file.
pub other_read: bool,
/// Represents whether or not other can write to the file
/// Represents whether or not other can write to the file.
pub other_write: bool,
/// Represents whether or not other can execute the file
/// Represents whether or not other can execute the file.
pub other_exec: bool,
}
@ -97,7 +115,7 @@ impl From<u32> for UnixMetadata {
}
impl From<UnixMetadata> for u32 {
/// Convert to a unix mode bitset
/// Convert to a unix mode bitset.
fn from(metadata: UnixMetadata) -> Self {
let mut flags = UnixFilePermissionFlags::empty();
@ -137,7 +155,7 @@ impl From<UnixMetadata> for u32 {
impl UnixMetadata {
pub fn is_readonly(self) -> bool {
!(self.owner_read || self.group_read || self.other_read)
!(self.owner_write || self.group_write || self.other_write)
}
}
@ -308,3 +326,716 @@ bitflags! {
const VIRTUAL = 0x10000;
}
}
#[cfg(test)]
mod tests {
use super::*;
mod metadata {
use super::*;
#[test]
fn should_be_able_to_serialize_minimal_metadata_to_json() {
let metadata = Metadata {
canonicalized_path: None,
file_type: FileType::Dir,
len: 999,
readonly: true,
accessed: None,
created: None,
modified: None,
unix: None,
windows: None,
};
let value = serde_json::to_value(metadata).unwrap();
assert_eq!(
value,
serde_json::json!({
"file_type": "dir",
"len": 999,
"readonly": true,
})
);
}
#[test]
fn should_be_able_to_serialize_full_metadata_to_json() {
let metadata = Metadata {
canonicalized_path: Some(PathBuf::from("test-dir")),
file_type: FileType::Dir,
len: 999,
readonly: true,
accessed: Some(u128::MAX),
created: Some(u128::MAX),
modified: Some(u128::MAX),
unix: Some(UnixMetadata {
owner_read: true,
owner_write: false,
owner_exec: false,
group_read: true,
group_write: false,
group_exec: false,
other_read: true,
other_write: false,
other_exec: false,
}),
windows: Some(WindowsMetadata {
archive: true,
compressed: false,
encrypted: true,
hidden: false,
integrity_stream: true,
normal: false,
not_content_indexed: true,
no_scrub_data: false,
offline: true,
recall_on_data_access: false,
recall_on_open: true,
reparse_point: false,
sparse_file: true,
system: false,
temporary: true,
}),
};
// NOTE: These values are too big to normally serialize, so we have to convert them to
// a string type, which is why the value here also needs to be a string.
let max_u128_str = u128::MAX.to_string();
let value = serde_json::to_value(metadata).unwrap();
assert_eq!(
value,
serde_json::json!({
"canonicalized_path": "test-dir",
"file_type": "dir",
"len": 999,
"readonly": true,
"accessed": max_u128_str,
"created": max_u128_str,
"modified": max_u128_str,
"unix": {
"owner_read": true,
"owner_write": false,
"owner_exec": false,
"group_read": true,
"group_write": false,
"group_exec": false,
"other_read": true,
"other_write": false,
"other_exec": false,
},
"windows": {
"archive": true,
"compressed": false,
"encrypted": true,
"hidden": false,
"integrity_stream": true,
"normal": false,
"not_content_indexed": true,
"no_scrub_data": false,
"offline": true,
"recall_on_data_access": false,
"recall_on_open": true,
"reparse_point": false,
"sparse_file": true,
"system": false,
"temporary": true,
}
})
);
}
#[test]
fn should_be_able_to_deserialize_minimal_metadata_from_json() {
let value = serde_json::json!({
"file_type": "dir",
"len": 999,
"readonly": true,
});
let metadata: Metadata = serde_json::from_value(value).unwrap();
assert_eq!(
metadata,
Metadata {
canonicalized_path: None,
file_type: FileType::Dir,
len: 999,
readonly: true,
accessed: None,
created: None,
modified: None,
unix: None,
windows: None,
}
);
}
#[test]
fn should_be_able_to_deserialize_full_metadata_from_json() {
// NOTE: These values are too big to normally serialize, so we have to convert them to
// a string type, which is why the value here also needs to be a string.
let max_u128_str = u128::MAX.to_string();
let value = serde_json::json!({
"canonicalized_path": "test-dir",
"file_type": "dir",
"len": 999,
"readonly": true,
"accessed": max_u128_str,
"created": max_u128_str,
"modified": max_u128_str,
"unix": {
"owner_read": true,
"owner_write": false,
"owner_exec": false,
"group_read": true,
"group_write": false,
"group_exec": false,
"other_read": true,
"other_write": false,
"other_exec": false,
},
"windows": {
"archive": true,
"compressed": false,
"encrypted": true,
"hidden": false,
"integrity_stream": true,
"normal": false,
"not_content_indexed": true,
"no_scrub_data": false,
"offline": true,
"recall_on_data_access": false,
"recall_on_open": true,
"reparse_point": false,
"sparse_file": true,
"system": false,
"temporary": true,
}
});
let metadata: Metadata = serde_json::from_value(value).unwrap();
assert_eq!(
metadata,
Metadata {
canonicalized_path: Some(PathBuf::from("test-dir")),
file_type: FileType::Dir,
len: 999,
readonly: true,
accessed: Some(u128::MAX),
created: Some(u128::MAX),
modified: Some(u128::MAX),
unix: Some(UnixMetadata {
owner_read: true,
owner_write: false,
owner_exec: false,
group_read: true,
group_write: false,
group_exec: false,
other_read: true,
other_write: false,
other_exec: false,
}),
windows: Some(WindowsMetadata {
archive: true,
compressed: false,
encrypted: true,
hidden: false,
integrity_stream: true,
normal: false,
not_content_indexed: true,
no_scrub_data: false,
offline: true,
recall_on_data_access: false,
recall_on_open: true,
reparse_point: false,
sparse_file: true,
system: false,
temporary: true,
}),
}
);
}
#[test]
fn should_be_able_to_serialize_minimal_metadata_to_msgpack() {
let metadata = Metadata {
canonicalized_path: None,
file_type: FileType::Dir,
len: 999,
readonly: true,
accessed: None,
created: None,
modified: None,
unix: None,
windows: None,
};
// NOTE: We don't actually check the output here because it's an implementation detail
// and could change as we change how serialization is done. This is merely to verify
// that we can serialize since there are times when serde fails to serialize at
// runtime.
let _ = rmp_serde::encode::to_vec_named(&metadata).unwrap();
}
#[test]
fn should_be_able_to_serialize_full_metadata_to_msgpack() {
let metadata = Metadata {
canonicalized_path: Some(PathBuf::from("test-dir")),
file_type: FileType::Dir,
len: 999,
readonly: true,
accessed: Some(u128::MAX),
created: Some(u128::MAX),
modified: Some(u128::MAX),
unix: Some(UnixMetadata {
owner_read: true,
owner_write: false,
owner_exec: false,
group_read: true,
group_write: false,
group_exec: false,
other_read: true,
other_write: false,
other_exec: false,
}),
windows: Some(WindowsMetadata {
archive: true,
compressed: false,
encrypted: true,
hidden: false,
integrity_stream: true,
normal: false,
not_content_indexed: true,
no_scrub_data: false,
offline: true,
recall_on_data_access: false,
recall_on_open: true,
reparse_point: false,
sparse_file: true,
system: false,
temporary: true,
}),
};
// NOTE: We don't actually check the output here because it's an implementation detail
// and could change as we change how serialization is done. This is merely to verify
// that we can serialize since there are times when serde fails to serialize at
// runtime.
let _ = rmp_serde::encode::to_vec_named(&metadata).unwrap();
}
#[test]
fn should_be_able_to_deserialize_minimal_metadata_from_msgpack() {
// NOTE: It may seem odd that we are serializing just to deserialize, but this is to
// verify that we are not corrupting or preventing issues when serializing on a
// client/server and then trying to deserialize on the other side. This has happened
// enough times with minor changes that we need tests to verify.
let buf = rmp_serde::encode::to_vec_named(&Metadata {
canonicalized_path: None,
file_type: FileType::Dir,
len: 999,
readonly: true,
accessed: None,
created: None,
modified: None,
unix: None,
windows: None,
})
.unwrap();
let metadata: Metadata = rmp_serde::decode::from_slice(&buf).unwrap();
assert_eq!(
metadata,
Metadata {
canonicalized_path: None,
file_type: FileType::Dir,
len: 999,
readonly: true,
accessed: None,
created: None,
modified: None,
unix: None,
windows: None,
}
);
}
#[test]
fn should_be_able_to_deserialize_full_metadata_from_msgpack() {
// NOTE: It may seem odd that we are serializing just to deserialize, but this is to
// verify that we are not corrupting or preventing issues when serializing on a
// client/server and then trying to deserialize on the other side. This has happened
// enough times with minor changes that we need tests to verify.
let buf = rmp_serde::encode::to_vec_named(&Metadata {
canonicalized_path: Some(PathBuf::from("test-dir")),
file_type: FileType::Dir,
len: 999,
readonly: true,
accessed: Some(u128::MAX),
created: Some(u128::MAX),
modified: Some(u128::MAX),
unix: Some(UnixMetadata {
owner_read: true,
owner_write: false,
owner_exec: false,
group_read: true,
group_write: false,
group_exec: false,
other_read: true,
other_write: false,
other_exec: false,
}),
windows: Some(WindowsMetadata {
archive: true,
compressed: false,
encrypted: true,
hidden: false,
integrity_stream: true,
normal: false,
not_content_indexed: true,
no_scrub_data: false,
offline: true,
recall_on_data_access: false,
recall_on_open: true,
reparse_point: false,
sparse_file: true,
system: false,
temporary: true,
}),
})
.unwrap();
let metadata: Metadata = rmp_serde::decode::from_slice(&buf).unwrap();
assert_eq!(
metadata,
Metadata {
canonicalized_path: Some(PathBuf::from("test-dir")),
file_type: FileType::Dir,
len: 999,
readonly: true,
accessed: Some(u128::MAX),
created: Some(u128::MAX),
modified: Some(u128::MAX),
unix: Some(UnixMetadata {
owner_read: true,
owner_write: false,
owner_exec: false,
group_read: true,
group_write: false,
group_exec: false,
other_read: true,
other_write: false,
other_exec: false,
}),
windows: Some(WindowsMetadata {
archive: true,
compressed: false,
encrypted: true,
hidden: false,
integrity_stream: true,
normal: false,
not_content_indexed: true,
no_scrub_data: false,
offline: true,
recall_on_data_access: false,
recall_on_open: true,
reparse_point: false,
sparse_file: true,
system: false,
temporary: true,
}),
}
);
}
}
mod unix_metadata {
use super::*;
#[test]
fn should_be_able_to_serialize_to_json() {
let metadata = UnixMetadata {
owner_read: true,
owner_write: false,
owner_exec: false,
group_read: true,
group_write: false,
group_exec: false,
other_read: true,
other_write: false,
other_exec: false,
};
let value = serde_json::to_value(metadata).unwrap();
assert_eq!(
value,
serde_json::json!({
"owner_read": true,
"owner_write": false,
"owner_exec": false,
"group_read": true,
"group_write": false,
"group_exec": false,
"other_read": true,
"other_write": false,
"other_exec": false,
})
);
}
#[test]
fn should_be_able_to_deserialize_from_json() {
let value = serde_json::json!({
"owner_read": true,
"owner_write": false,
"owner_exec": false,
"group_read": true,
"group_write": false,
"group_exec": false,
"other_read": true,
"other_write": false,
"other_exec": false,
});
let metadata: UnixMetadata = serde_json::from_value(value).unwrap();
assert_eq!(
metadata,
UnixMetadata {
owner_read: true,
owner_write: false,
owner_exec: false,
group_read: true,
group_write: false,
group_exec: false,
other_read: true,
other_write: false,
other_exec: false,
}
);
}
#[test]
fn should_be_able_to_serialize_to_msgpack() {
let metadata = UnixMetadata {
owner_read: true,
owner_write: false,
owner_exec: false,
group_read: true,
group_write: false,
group_exec: false,
other_read: true,
other_write: false,
other_exec: false,
};
// NOTE: We don't actually check the output here because it's an implementation detail
// and could change as we change how serialization is done. This is merely to verify
// that we can serialize since there are times when serde fails to serialize at
// runtime.
let _ = rmp_serde::encode::to_vec_named(&metadata).unwrap();
}
#[test]
fn should_be_able_to_deserialize_from_msgpack() {
// NOTE: It may seem odd that we are serializing just to deserialize, but this is to
// verify that we are not corrupting or preventing issues when serializing on a
// client/server and then trying to deserialize on the other side. This has happened
// enough times with minor changes that we need tests to verify.
let buf = rmp_serde::encode::to_vec_named(&UnixMetadata {
owner_read: true,
owner_write: false,
owner_exec: false,
group_read: true,
group_write: false,
group_exec: false,
other_read: true,
other_write: false,
other_exec: false,
})
.unwrap();
let metadata: UnixMetadata = rmp_serde::decode::from_slice(&buf).unwrap();
assert_eq!(
metadata,
UnixMetadata {
owner_read: true,
owner_write: false,
owner_exec: false,
group_read: true,
group_write: false,
group_exec: false,
other_read: true,
other_write: false,
other_exec: false,
}
);
}
}
mod windows_metadata {
use super::*;
#[test]
fn should_be_able_to_serialize_to_json() {
let metadata = WindowsMetadata {
archive: true,
compressed: false,
encrypted: true,
hidden: false,
integrity_stream: true,
normal: false,
not_content_indexed: true,
no_scrub_data: false,
offline: true,
recall_on_data_access: false,
recall_on_open: true,
reparse_point: false,
sparse_file: true,
system: false,
temporary: true,
};
let value = serde_json::to_value(metadata).unwrap();
assert_eq!(
value,
serde_json::json!({
"archive": true,
"compressed": false,
"encrypted": true,
"hidden": false,
"integrity_stream": true,
"normal": false,
"not_content_indexed": true,
"no_scrub_data": false,
"offline": true,
"recall_on_data_access": false,
"recall_on_open": true,
"reparse_point": false,
"sparse_file": true,
"system": false,
"temporary": true,
})
);
}
#[test]
fn should_be_able_to_deserialize_from_json() {
let value = serde_json::json!({
"archive": true,
"compressed": false,
"encrypted": true,
"hidden": false,
"integrity_stream": true,
"normal": false,
"not_content_indexed": true,
"no_scrub_data": false,
"offline": true,
"recall_on_data_access": false,
"recall_on_open": true,
"reparse_point": false,
"sparse_file": true,
"system": false,
"temporary": true,
});
let metadata: WindowsMetadata = serde_json::from_value(value).unwrap();
assert_eq!(
metadata,
WindowsMetadata {
archive: true,
compressed: false,
encrypted: true,
hidden: false,
integrity_stream: true,
normal: false,
not_content_indexed: true,
no_scrub_data: false,
offline: true,
recall_on_data_access: false,
recall_on_open: true,
reparse_point: false,
sparse_file: true,
system: false,
temporary: true,
}
);
}
#[test]
fn should_be_able_to_serialize_to_msgpack() {
let metadata = WindowsMetadata {
archive: true,
compressed: false,
encrypted: true,
hidden: false,
integrity_stream: true,
normal: false,
not_content_indexed: true,
no_scrub_data: false,
offline: true,
recall_on_data_access: false,
recall_on_open: true,
reparse_point: false,
sparse_file: true,
system: false,
temporary: true,
};
// NOTE: We don't actually check the output here because it's an implementation detail
// and could change as we change how serialization is done. This is merely to verify
// that we can serialize since there are times when serde fails to serialize at
// runtime.
let _ = rmp_serde::encode::to_vec_named(&metadata).unwrap();
}
#[test]
fn should_be_able_to_deserialize_from_msgpack() {
// NOTE: It may seem odd that we are serializing just to deserialize, but this is to
// verify that we are not corrupting or preventing issues when serializing on a
// client/server and then trying to deserialize on the other side. This has happened
// enough times with minor changes that we need tests to verify.
let buf = rmp_serde::encode::to_vec_named(&WindowsMetadata {
archive: true,
compressed: false,
encrypted: true,
hidden: false,
integrity_stream: true,
normal: false,
not_content_indexed: true,
no_scrub_data: false,
offline: true,
recall_on_data_access: false,
recall_on_open: true,
reparse_point: false,
sparse_file: true,
system: false,
temporary: true,
})
.unwrap();
let metadata: WindowsMetadata = rmp_serde::decode::from_slice(&buf).unwrap();
assert_eq!(
metadata,
WindowsMetadata {
archive: true,
compressed: false,
encrypted: true,
hidden: false,
integrity_stream: true,
normal: false,
not_content_indexed: true,
no_scrub_data: false,
offline: true,
recall_on_data_access: false,
recall_on_open: true,
reparse_point: false,
sparse_file: true,
system: false,
temporary: true,
}
);
}
}
}

@ -27,33 +27,51 @@ pub struct SetPermissionsOptions {
/// On `Unix` platforms, this translates directly into the mode that
/// you would find with `chmod`. On all other platforms, this uses the
/// write flags to determine whether or not to set the readonly status.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct Permissions {
/// Represents whether or not owner can read from the file
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(default)]
pub owner_read: Option<bool>,
/// Represents whether or not owner can write to the file
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(default)]
pub owner_write: Option<bool>,
/// Represents whether or not owner can execute the file
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(default)]
pub owner_exec: Option<bool>,
/// Represents whether or not associated group can read from the file
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(default)]
pub group_read: Option<bool>,
/// Represents whether or not associated group can write to the file
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(default)]
pub group_write: Option<bool>,
/// Represents whether or not associated group can execute the file
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(default)]
pub group_exec: Option<bool>,
/// Represents whether or not other can read from the file
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(default)]
pub other_read: Option<bool>,
/// Represents whether or not other can write to the file
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(default)]
pub other_write: Option<bool>,
/// Represents whether or not other can execute the file
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(default)]
pub other_exec: Option<bool>,
}
@ -109,6 +127,23 @@ impl Permissions {
/// Returns true if the permission set has a value specified for each permission (no `None`
/// settings).
///
/// ```
/// use distant_protocol::Permissions;
///
/// let permissions = Permissions {
/// owner_write: Some(true),
/// group_write: Some(false),
/// other_write: Some(true),
/// owner_read: Some(false),
/// group_read: Some(true),
/// other_read: Some(false),
/// owner_exec: Some(true),
/// group_exec: Some(false),
/// other_exec: Some(true),
/// };
/// assert!(permissions.is_complete());
/// ```
pub fn is_complete(&self) -> bool {
self.owner_read.is_some()
&& self.owner_write.is_some()
@ -123,6 +158,25 @@ impl Permissions {
/// Returns `true` if permissions represent readonly, `false` if permissions represent
/// writable, and `None` if no permissions have been set to indicate either status.
///
/// ```
/// use distant_protocol::Permissions;
///
/// assert_eq!(
/// Permissions { owner_write: Some(true), ..Default::default() }.is_readonly(),
/// Some(false)
/// );
///
/// assert_eq!(
/// Permissions { owner_write: Some(false), ..Default::default() }.is_readonly(),
/// Some(true)
/// );
///
/// assert_eq!(
/// Permissions { ..Default::default() }.is_writable(),
/// None
/// );
/// ```
#[inline]
pub fn is_readonly(&self) -> Option<bool> {
// Negate the writable status to indicate whether or not readonly
@ -131,15 +185,63 @@ impl Permissions {
/// Returns `true` if permissions represent ability to write, `false` if permissions represent
/// inability to write, and `None` if no permissions have been set to indicate either status.
///
/// ```
/// use distant_protocol::Permissions;
///
/// assert_eq!(
/// Permissions { owner_write: Some(true), ..Default::default() }.is_writable(),
/// Some(true)
/// );
///
/// assert_eq!(
/// Permissions { owner_write: Some(false), ..Default::default() }.is_writable(),
/// Some(false)
/// );
///
/// assert_eq!(
/// Permissions { ..Default::default() }.is_writable(),
/// None
/// );
/// ```
#[inline]
pub fn is_writable(&self) -> Option<bool> {
self.owner_write
.zip(self.group_write)
.zip(self.other_write)
.map(|((owner, group), other)| owner || group || other)
match (self.owner_write, self.group_write, self.other_write) {
(None, None, None) => None,
(owner, group, other) => {
Some(owner.unwrap_or(false) || group.unwrap_or(false) || other.unwrap_or(false))
}
}
}
/// Applies `other` settings to `self`, overwriting any of the permissions in `self` with `other`.
///
/// ```
/// use distant_protocol::Permissions;
///
/// let mut a = Permissions {
/// owner_read: Some(true),
/// owner_write: Some(false),
/// owner_exec: None,
/// ..Default::default()
/// };
///
/// let b = Permissions {
/// owner_read: Some(false),
/// owner_write: None,
/// owner_exec: Some(true),
/// ..Default::default()
/// };
///
/// a.apply_from(&b);
///
/// assert_eq!(a, Permissions {
/// owner_read: Some(false),
/// owner_write: Some(false),
/// owner_exec: Some(true),
/// ..Default::default()
/// });
/// ```
#[inline]
pub fn apply_from(&mut self, other: &Self) {
macro_rules! apply {
@ -163,6 +265,33 @@ impl Permissions {
/// Applies `self` settings to `other`, overwriting any of the permissions in `other` with
/// `self`.
///
/// ```
/// use distant_protocol::Permissions;
///
/// let a = Permissions {
/// owner_read: Some(true),
/// owner_write: Some(false),
/// owner_exec: None,
/// ..Default::default()
/// };
///
/// let mut b = Permissions {
/// owner_read: Some(false),
/// owner_write: None,
/// owner_exec: Some(true),
/// ..Default::default()
/// };
///
/// a.apply_to(&mut b);
///
/// assert_eq!(b, Permissions {
/// owner_read: Some(true),
/// owner_write: Some(false),
/// owner_exec: Some(true),
/// ..Default::default()
/// });
/// ```
#[inline]
pub fn apply_to(&self, other: &mut Self) {
Self::apply_from(other, self)
@ -185,6 +314,41 @@ impl Permissions {
}
/// Converts to a Unix `mode` from a permission set. For any missing setting, a 0 bit is used.
///
/// ```
/// use distant_protocol::Permissions;
///
/// assert_eq!(Permissions {
/// owner_read: Some(true),
/// owner_write: Some(true),
/// owner_exec: Some(true),
/// group_read: Some(true),
/// group_write: Some(true),
/// group_exec: Some(true),
/// other_read: Some(true),
/// other_write: Some(true),
/// other_exec: Some(true),
/// }.to_unix_mode(), 0o777);
///
/// assert_eq!(Permissions {
/// owner_read: Some(true),
/// owner_write: Some(false),
/// owner_exec: Some(false),
/// group_read: Some(true),
/// group_write: Some(false),
/// group_exec: Some(false),
/// other_read: Some(true),
/// other_write: Some(false),
/// other_exec: Some(false),
/// }.to_unix_mode(), 0o444);
///
/// assert_eq!(Permissions {
/// owner_exec: Some(true),
/// group_exec: Some(true),
/// other_exec: Some(true),
/// ..Default::default()
/// }.to_unix_mode(), 0o111);
/// ```
pub fn to_unix_mode(&self) -> u32 {
let mut flags = UnixFilePermissionFlags::empty();
@ -276,3 +440,223 @@ bitflags! {
const OTHER_EXEC = 0o1;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn should_be_able_to_serialize_minimal_permissions_to_json() {
let permissions = Permissions {
owner_read: None,
owner_write: None,
owner_exec: None,
group_read: None,
group_write: None,
group_exec: None,
other_read: None,
other_write: None,
other_exec: None,
};
let value = serde_json::to_value(permissions).unwrap();
assert_eq!(value, serde_json::json!({}));
}
#[test]
fn should_be_able_to_serialize_full_permissions_to_json() {
let permissions = Permissions {
owner_read: Some(true),
owner_write: Some(false),
owner_exec: Some(true),
group_read: Some(false),
group_write: Some(true),
group_exec: Some(false),
other_read: Some(true),
other_write: Some(false),
other_exec: Some(true),
};
let value = serde_json::to_value(permissions).unwrap();
assert_eq!(
value,
serde_json::json!({
"owner_read": true,
"owner_write": false,
"owner_exec": true,
"group_read": false,
"group_write": true,
"group_exec": false,
"other_read": true,
"other_write": false,
"other_exec": true,
})
);
}
#[test]
fn should_be_able_to_deserialize_minimal_permissions_from_json() {
let value = serde_json::json!({});
let permissions: Permissions = serde_json::from_value(value).unwrap();
assert_eq!(
permissions,
Permissions {
owner_read: None,
owner_write: None,
owner_exec: None,
group_read: None,
group_write: None,
group_exec: None,
other_read: None,
other_write: None,
other_exec: None,
}
);
}
#[test]
fn should_be_able_to_deserialize_full_permissions_from_json() {
let value = serde_json::json!({
"owner_read": true,
"owner_write": false,
"owner_exec": true,
"group_read": false,
"group_write": true,
"group_exec": false,
"other_read": true,
"other_write": false,
"other_exec": true,
});
let permissions: Permissions = serde_json::from_value(value).unwrap();
assert_eq!(
permissions,
Permissions {
owner_read: Some(true),
owner_write: Some(false),
owner_exec: Some(true),
group_read: Some(false),
group_write: Some(true),
group_exec: Some(false),
other_read: Some(true),
other_write: Some(false),
other_exec: Some(true),
}
);
}
#[test]
fn should_be_able_to_serialize_minimal_permissions_to_msgpack() {
let permissions = Permissions {
owner_read: None,
owner_write: None,
owner_exec: None,
group_read: None,
group_write: None,
group_exec: None,
other_read: None,
other_write: None,
other_exec: None,
};
// NOTE: We don't actually check the output here because it's an implementation detail
// and could change as we change how serialization is done. This is merely to verify
// that we can serialize since there are times when serde fails to serialize at
// runtime.
let _ = rmp_serde::encode::to_vec_named(&permissions).unwrap();
}
#[test]
fn should_be_able_to_serialize_full_permissions_to_msgpack() {
let permissions = Permissions {
owner_read: Some(true),
owner_write: Some(false),
owner_exec: Some(true),
group_read: Some(true),
group_write: Some(false),
group_exec: Some(true),
other_read: Some(true),
other_write: Some(false),
other_exec: Some(true),
};
// NOTE: We don't actually check the output here because it's an implementation detail
// and could change as we change how serialization is done. This is merely to verify
// that we can serialize since there are times when serde fails to serialize at
// runtime.
let _ = rmp_serde::encode::to_vec_named(&permissions).unwrap();
}
#[test]
fn should_be_able_to_deserialize_minimal_permissions_from_msgpack() {
// NOTE: It may seem odd that we are serializing just to deserialize, but this is to
// verify that we are not corrupting or preventing issues when serializing on a
// client/server and then trying to deserialize on the other side. This has happened
// enough times with minor changes that we need tests to verify.
let buf = rmp_serde::encode::to_vec_named(&Permissions {
owner_read: None,
owner_write: None,
owner_exec: None,
group_read: None,
group_write: None,
group_exec: None,
other_read: None,
other_write: None,
other_exec: None,
})
.unwrap();
let permissions: Permissions = rmp_serde::decode::from_slice(&buf).unwrap();
assert_eq!(
permissions,
Permissions {
owner_read: None,
owner_write: None,
owner_exec: None,
group_read: None,
group_write: None,
group_exec: None,
other_read: None,
other_write: None,
other_exec: None,
}
);
}
#[test]
fn should_be_able_to_deserialize_full_permissions_from_msgpack() {
// NOTE: It may seem odd that we are serializing just to deserialize, but this is to
// verify that we are not corrupting or preventing issues when serializing on a
// client/server and then trying to deserialize on the other side. This has happened
// enough times with minor changes that we need tests to verify.
let buf = rmp_serde::encode::to_vec_named(&Permissions {
owner_read: Some(true),
owner_write: Some(false),
owner_exec: Some(true),
group_read: Some(true),
group_write: Some(false),
group_exec: Some(true),
other_read: Some(true),
other_write: Some(false),
other_exec: Some(true),
})
.unwrap();
let permissions: Permissions = rmp_serde::decode::from_slice(&buf).unwrap();
assert_eq!(
permissions,
Permissions {
owner_read: Some(true),
owner_write: Some(false),
owner_exec: Some(true),
group_read: Some(true),
group_write: Some(false),
group_exec: Some(true),
other_read: Some(true),
other_write: Some(false),
other_exec: Some(true),
}
);
}
}

Loading…
Cancel
Save