virtual_file_transfer/
virtual_file.rs1use crate::Result;
2use crate::file_source::FileSource;
3use crate::source_reader::SourceReader;
4use crate::validation::validate_file_name;
5use std::io;
6
7pub struct VirtualFile {
13 name: String,
14 size: Option<u64>,
15 source: Box<dyn FileSource>,
16}
17
18impl VirtualFile {
19 pub fn new(name: &str, source: Box<dyn FileSource>) -> Result<Self> {
30 validate_file_name(name)?;
31
32 Ok(Self {
33 name: name.to_string(),
34 size: None,
35 source,
36 })
37 }
38
39 pub fn name(&self) -> &str {
41 &self.name
42 }
43
44 pub fn size(&self) -> Option<u64> {
49 self.size
50 }
51
52 pub fn with_size(mut self, size: u64) -> Self {
57 self.size = Some(size);
58 self
59 }
60
61 pub(crate) fn open(&self) -> io::Result<SourceReader> {
62 self.source.open()
63 }
64}
65
66#[cfg(test)]
67mod tests {
68 use super::*;
69 use crate::{Error, InputError};
70 use std::io::{self, Read};
71 struct StubSource;
72
73 impl FileSource for StubSource {
74 fn open(&self) -> io::Result<SourceReader> {
75 Ok(SourceReader::seekable(std::io::Cursor::new(
76 b"hello".to_vec(),
77 )))
78 }
79 }
80
81 #[test]
82 fn creates_virtual_file() {
83 let file = VirtualFile::new("hello.txt", Box::new(StubSource)).unwrap();
84
85 assert_eq!(file.name(), "hello.txt");
86 assert_eq!(file.size(), None);
87 }
88
89 #[test]
90 fn reads_from_file_source() {
91 let file = VirtualFile::new("hello.txt", Box::new(StubSource)).unwrap();
92
93 let mut reader = file.open().unwrap();
94
95 let mut buffer = [0u8; 5];
96 let count = reader.read(&mut buffer).unwrap();
97
98 assert_eq!(count, 5);
99 assert_eq!(&buffer, b"hello");
100 }
101
102 #[test]
103 fn rejects_empty_name() {
104 let result = VirtualFile::new("", Box::new(StubSource));
105
106 assert!(matches!(
107 result,
108 Err(Error::InvalidInput(InputError::EmptyName))
109 ));
110 }
111}