feat(core): add AttachmentRef + AttachmentSummary
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
75
crates/idfoto-core/src/attachment.rs
Normal file
75
crates/idfoto-core/src/attachment.rs
Normal file
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -47,6 +47,9 @@ pub use item_types::{ItemCore, ItemType};
|
|||||||
pub mod item;
|
pub mod item;
|
||||||
pub use item::{Field, FieldKind, FieldValue, Section};
|
pub use item::{Field, FieldKind, FieldValue, Section};
|
||||||
|
|
||||||
|
pub mod attachment;
|
||||||
|
pub use attachment::{AttachmentRef, AttachmentSummary};
|
||||||
|
|
||||||
pub mod crypto;
|
pub mod crypto;
|
||||||
pub use crypto::{decrypt, derive_master_key, encrypt, KdfParams};
|
pub use crypto::{decrypt, derive_master_key, encrypt, KdfParams};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user