diff --git a/crates/idfoto-core/src/attachment.rs b/crates/idfoto-core/src/attachment.rs new file mode 100644 index 0000000..8af857c --- /dev/null +++ b/crates/idfoto-core/src/attachment.rs @@ -0,0 +1,75 @@ +//! Attachment refs (carried on Item) and summaries (carried in Manifest). +//! +//! Encryption helpers (`encrypt_attachment`, `decrypt_attachment`) are added +//! later in Task 22 once the crypto module is settled. + +use serde::{Deserialize, Serialize}; + +use crate::ids::AttachmentId; + +/// Reference to an attachment, carried on the Item record. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct AttachmentRef { + pub id: AttachmentId, + pub filename: String, + pub mime_type: String, + /// Plaintext size in bytes. + pub size: u64, + /// Unix-seconds when this attachment was added. + pub created: i64, +} + +/// Compact summary of an attachment, carried in the Manifest so the popup +/// can show attachment indicators without decrypting the item file. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct AttachmentSummary { + pub id: AttachmentId, + pub filename: String, + pub mime_type: String, + pub size: u64, +} + +impl From<&AttachmentRef> for AttachmentSummary { + fn from(r: &AttachmentRef) -> Self { + Self { + id: r.id.clone(), + filename: r.filename.clone(), + mime_type: r.mime_type.clone(), + size: r.size, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn attachment_ref_round_trip() { + let r = AttachmentRef { + id: AttachmentId("0123456789abcdef".into()), + filename: "doc.pdf".into(), + mime_type: "application/pdf".into(), + size: 12345, + created: 1_700_000_000, + }; + let json = serde_json::to_string(&r).unwrap(); + let parsed: AttachmentRef = serde_json::from_str(&json).unwrap(); + assert_eq!(parsed.filename, "doc.pdf"); + assert_eq!(parsed.size, 12345); + } + + #[test] + fn attachment_summary_from_ref() { + let r = AttachmentRef { + id: AttachmentId("aabb".into()), + filename: "x.txt".into(), + mime_type: "text/plain".into(), + size: 5, + created: 0, + }; + let s: AttachmentSummary = (&r).into(); + assert_eq!(s.filename, "x.txt"); + assert_eq!(s.id, r.id); + } +} diff --git a/crates/idfoto-core/src/lib.rs b/crates/idfoto-core/src/lib.rs index 49f50ad..bfed8db 100644 --- a/crates/idfoto-core/src/lib.rs +++ b/crates/idfoto-core/src/lib.rs @@ -47,6 +47,9 @@ pub use item_types::{ItemCore, ItemType}; pub mod item; pub use item::{Field, FieldKind, FieldValue, Section}; +pub mod attachment; +pub use attachment::{AttachmentRef, AttachmentSummary}; + pub mod crypto; pub use crypto::{decrypt, derive_master_key, encrypt, KdfParams};