use crate::ptp::{AssociationType, DateTime, ObjectFormatCode, ObjectInfo as PtpObjectInfo};
#[derive(Debug, Clone)]
pub struct NewObjectInfo {
pub filename: String,
pub size: u64,
pub format: Option<ObjectFormatCode>,
pub modified: Option<DateTime>,
}
impl NewObjectInfo {
#[must_use]
pub fn file(filename: impl Into<String>, size: u64) -> Self {
let filename = filename.into();
let format = detect_format_from_filename(&filename);
Self {
filename,
size,
format: Some(format),
modified: None,
}
}
#[must_use]
pub fn folder(name: impl Into<String>) -> Self {
Self {
filename: name.into(),
size: 0,
format: Some(ObjectFormatCode::Association),
modified: None,
}
}
#[must_use]
pub fn with_format(filename: impl Into<String>, size: u64, format: ObjectFormatCode) -> Self {
Self {
filename: filename.into(),
size,
format: Some(format),
modified: None,
}
}
#[must_use]
pub fn with_modified(mut self, modified: DateTime) -> Self {
self.modified = Some(modified);
self
}
pub(crate) fn to_object_info(&self) -> PtpObjectInfo {
let format = self.format.unwrap_or(ObjectFormatCode::Undefined);
let is_folder = format == ObjectFormatCode::Association;
PtpObjectInfo {
format,
size: self.size,
filename: self.filename.clone(),
modified: self.modified,
association_type: if is_folder {
AssociationType::GenericFolder
} else {
AssociationType::None
},
..Default::default()
}
}
}
fn detect_format_from_filename(filename: &str) -> ObjectFormatCode {
if let Some(ext) = filename.rsplit('.').next() {
ObjectFormatCode::from_extension(ext)
} else {
ObjectFormatCode::Undefined
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_new_object_info_file() {
let info = NewObjectInfo::file("test.mp3", 1000);
assert_eq!(info.filename, "test.mp3");
assert_eq!(info.size, 1000);
assert_eq!(info.format, Some(ObjectFormatCode::Mp3));
}
#[test]
fn test_new_object_info_folder() {
let info = NewObjectInfo::folder("Music");
assert_eq!(info.filename, "Music");
assert_eq!(info.size, 0);
assert_eq!(info.format, Some(ObjectFormatCode::Association));
}
#[test]
fn test_format_detection() {
assert_eq!(
detect_format_from_filename("song.mp3"),
ObjectFormatCode::Mp3
);
assert_eq!(
detect_format_from_filename("photo.jpg"),
ObjectFormatCode::Jpeg
);
assert_eq!(
detect_format_from_filename("video.mp4"),
ObjectFormatCode::Mp4Container
);
assert_eq!(
detect_format_from_filename("unknown.xyz"),
ObjectFormatCode::Undefined
);
}
#[test]
fn test_with_format() {
let info = NewObjectInfo::with_format("document.bin", 500, ObjectFormatCode::Executable);
assert_eq!(info.filename, "document.bin");
assert_eq!(info.size, 500);
assert_eq!(info.format, Some(ObjectFormatCode::Executable));
}
#[test]
fn test_with_modified() {
let dt = DateTime {
year: 2024,
month: 6,
day: 15,
hour: 10,
minute: 30,
second: 0,
};
let info = NewObjectInfo::file("test.txt", 100).with_modified(dt);
assert_eq!(info.modified, Some(dt));
}
#[test]
fn test_to_object_info_file() {
let info = NewObjectInfo::file("test.mp3", 1000);
let ptp_info = info.to_object_info();
assert_eq!(ptp_info.format, ObjectFormatCode::Mp3);
assert_eq!(ptp_info.size, 1000);
assert_eq!(ptp_info.filename, "test.mp3");
assert_eq!(ptp_info.association_type, AssociationType::None);
}
#[test]
fn test_to_object_info_folder() {
let info = NewObjectInfo::folder("Music");
let ptp_info = info.to_object_info();
assert_eq!(ptp_info.format, ObjectFormatCode::Association);
assert_eq!(ptp_info.size, 0);
assert_eq!(ptp_info.filename, "Music");
assert_eq!(ptp_info.association_type, AssociationType::GenericFolder);
}
#[test]
fn test_format_detection_case_insensitive() {
assert_eq!(
detect_format_from_filename("SONG.MP3"),
ObjectFormatCode::Mp3
);
assert_eq!(
detect_format_from_filename("Photo.JPG"),
ObjectFormatCode::Jpeg
);
}
#[test]
fn test_format_detection_no_extension() {
assert_eq!(
detect_format_from_filename("noextension"),
ObjectFormatCode::Undefined
);
}
}