sftp-protocol 0.1.0

A pure Rust implementation of the SFTP protocol
Documentation
use camino::Utf8PathBuf;
use nom::IResult;
use nom::number::streaming::be_u32;
use nom::number::streaming::le_u32;

use crate::common::FileAttributes;

use super::kind::PacketType;
use super::PayloadTrait;

#[derive(Debug, Eq, PartialEq, Nom, Serialize)]
#[nom(BigEndian)]
#[cfg_attr(test, derive(test_strategy::Arbitrary))]
pub struct Open {
	pub id: u32,
	#[nom(Parse(crate::util::parse_path))]
	#[serde(serialize_with = "crate::util::path_with_u32_length")]
	pub path: Utf8PathBuf,
	pub pflags: OpenFlags,
	pub attrs: FileAttributes
}

impl PayloadTrait for Open {
	const Type: PacketType = PacketType::Open;
	fn binsize(&self) -> u32 {
		4 + (4 + self.path.as_str().len() as u32) + 4 + self.attrs.binsize()
	}
}

impl From<Open> for super::Payload {
	fn from(p: Open) -> Self {
		Self::Open(p)
	}
}

bitflags::bitflags! {
	#[derive(Default, Serialize)]
	pub struct OpenFlags: u32 {
		const Read = 0x01;
		const Write = 0x02;
		const Append = 0x04;
		const Create = 0x08;
		const Truncate = 0x10;
		const Exclude = 0x20;
	}
}

#[cfg(test)]
impl proptest::bits::BitSetLike for OpenFlags {
	fn new_bitset(_max: usize) -> Self {
		Self::empty()
	}

	fn len(&self) -> usize {
		7
	}

	fn test(&self, i: usize) -> bool {
		(self.bits() & 2 << i) != 0
	}

	fn set(&mut self, i: usize) {
		self.insert(Self::from_bits_truncate(2 << i))
	}

	fn clear(&mut self, i: usize) {
		self.remove(Self::from_bits_truncate(2 << i))
	}
}

#[cfg(test)]
impl proptest::prelude::Arbitrary for OpenFlags {
	type Parameters = ();
	type Strategy = proptest::bits::BitSetStrategy<Self>;
	fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
		proptest::bits::BitSetStrategy::masked(Self::from_bits_truncate(0x3f))
	}
}

impl<I: nom_derive::InputSlice> nom_derive::Parse<I> for OpenFlags {
	fn parse_be(i: I) -> IResult<I, Self> {
		let (i, flags) = be_u32(i)?;
		let this = Self::from_bits_truncate(flags);
		Ok((i, this))
	}

	fn parse_le(i: I) -> IResult<I, Self> {
		let (i, flags) = le_u32(i)?;
		let this = Self::from_bits_truncate(flags);
		Ok((i, this))
	}

	fn parse(i: I) -> IResult<I, Self> {
		Self::parse_be(i)
	}
}

#[cfg(test)]
mod tests {
	use test_strategy::proptest;
	use crate::parser::encode;
	use crate::parser::Parser;
	use super::*;

	#[proptest]
	fn roundtrip_whole(input: Open) {
		let mut stream = Parser::default();
		let packet = input.into_packet();
		stream.write(&encode(&packet)).unwrap();
		assert_eq!(stream.get_packet(), Ok(Some(packet)));
		assert_eq!(stream.get_packet(), Ok(None));
	}
}