1use crate::mtp::{DateTime, ObjectFormat};
4use crate::ptp::{AssociationType, ObjectFormatCode, ObjectInfo as PtpObjectInfo};
5
6#[derive(Debug, Clone)]
8pub struct NewObjectInfo {
9 pub filename: String,
11 pub size: u64,
13 pub format: Option<ObjectFormat>,
15 pub modified: Option<DateTime>,
17}
18
19impl NewObjectInfo {
20 #[must_use]
22 pub fn file(filename: impl Into<String>, size: u64) -> Self {
23 let filename = filename.into();
24 let format = detect_format_from_filename(&filename);
25 Self {
26 filename,
27 size,
28 format: Some(format),
29 modified: None,
30 }
31 }
32
33 #[must_use]
35 pub fn folder(name: impl Into<String>) -> Self {
36 Self {
37 filename: name.into(),
38 size: 0,
39 format: Some(ObjectFormat::ASSOCIATION),
40 modified: None,
41 }
42 }
43
44 #[must_use]
46 pub fn with_format(filename: impl Into<String>, size: u64, format: ObjectFormat) -> Self {
47 Self {
48 filename: filename.into(),
49 size,
50 format: Some(format),
51 modified: None,
52 }
53 }
54
55 #[must_use]
57 pub fn with_modified(mut self, modified: DateTime) -> Self {
58 self.modified = Some(modified);
59 self
60 }
61
62 pub(crate) fn to_object_info(&self) -> PtpObjectInfo {
64 let format = self.format.unwrap_or(ObjectFormat::UNDEFINED);
65 let is_folder = format.is_association();
66
67 PtpObjectInfo {
68 format: ObjectFormatCode::from(format.code()),
69 size: self.size,
70 filename: self.filename.clone(),
71 modified: self.modified.map(DateTime::to_ptp),
72 association_type: if is_folder {
73 AssociationType::GenericFolder
74 } else {
75 AssociationType::None
76 },
77 ..Default::default()
78 }
79 }
80}
81
82fn detect_format_from_filename(filename: &str) -> ObjectFormat {
84 if let Some(ext) = filename.rsplit('.').next() {
85 ObjectFormat::from(ObjectFormatCode::from_extension(ext))
86 } else {
87 ObjectFormat::UNDEFINED
88 }
89}
90
91#[cfg(test)]
92mod tests {
93 use super::*;
94
95 fn fmt(code: ObjectFormatCode) -> ObjectFormat {
96 ObjectFormat::from(code)
97 }
98
99 #[test]
100 fn test_new_object_info_file() {
101 let info = NewObjectInfo::file("test.mp3", 1000);
102 assert_eq!(info.filename, "test.mp3");
103 assert_eq!(info.size, 1000);
104 assert_eq!(info.format, Some(fmt(ObjectFormatCode::Mp3)));
105 }
106
107 #[test]
108 fn test_new_object_info_folder() {
109 let info = NewObjectInfo::folder("Music");
110 assert_eq!(info.filename, "Music");
111 assert_eq!(info.size, 0);
112 assert_eq!(info.format, Some(ObjectFormat::ASSOCIATION));
113 }
114
115 #[test]
116 fn test_format_detection() {
117 assert_eq!(
118 detect_format_from_filename("song.mp3"),
119 fmt(ObjectFormatCode::Mp3)
120 );
121 assert_eq!(
122 detect_format_from_filename("photo.jpg"),
123 fmt(ObjectFormatCode::Jpeg)
124 );
125 assert_eq!(
126 detect_format_from_filename("video.mp4"),
127 fmt(ObjectFormatCode::Mp4Container)
128 );
129 assert_eq!(
130 detect_format_from_filename("unknown.xyz"),
131 ObjectFormat::UNDEFINED
132 );
133 }
134
135 #[test]
136 fn test_with_format() {
137 let info =
138 NewObjectInfo::with_format("document.bin", 500, fmt(ObjectFormatCode::Executable));
139 assert_eq!(info.filename, "document.bin");
140 assert_eq!(info.size, 500);
141 assert_eq!(info.format, Some(fmt(ObjectFormatCode::Executable)));
142 }
143
144 #[test]
145 fn test_with_modified() {
146 let dt = DateTime {
147 year: 2024,
148 month: 6,
149 day: 15,
150 hour: 10,
151 minute: 30,
152 second: 0,
153 };
154 let info = NewObjectInfo::file("test.txt", 100).with_modified(dt);
155 assert_eq!(info.modified, Some(dt));
156 }
157
158 #[test]
159 fn test_to_object_info_file() {
160 let info = NewObjectInfo::file("test.mp3", 1000);
161 let ptp_info = info.to_object_info();
162
163 assert_eq!(ptp_info.format, ObjectFormatCode::Mp3);
164 assert_eq!(ptp_info.size, 1000);
165 assert_eq!(ptp_info.filename, "test.mp3");
166 assert_eq!(ptp_info.association_type, AssociationType::None);
167 }
168
169 #[test]
170 fn test_to_object_info_folder() {
171 let info = NewObjectInfo::folder("Music");
172 let ptp_info = info.to_object_info();
173
174 assert_eq!(ptp_info.format, ObjectFormatCode::Association);
175 assert_eq!(ptp_info.size, 0);
176 assert_eq!(ptp_info.filename, "Music");
177 assert_eq!(ptp_info.association_type, AssociationType::GenericFolder);
178 }
179
180 #[test]
181 fn test_format_detection_case_insensitive() {
182 assert_eq!(
183 detect_format_from_filename("SONG.MP3"),
184 fmt(ObjectFormatCode::Mp3)
185 );
186 assert_eq!(
187 detect_format_from_filename("Photo.JPG"),
188 fmt(ObjectFormatCode::Jpeg)
189 );
190 }
191
192 #[test]
193 fn test_format_detection_no_extension() {
194 assert_eq!(
195 detect_format_from_filename("noextension"),
196 ObjectFormat::UNDEFINED
197 );
198 }
199}