Files
kestrove-core/crates/kv-model/src/item.rs
T
2026-10-15 18:30:14 +09:00

86 lines
2.2 KiB
Rust

use serde::{Deserialize, Serialize};
use uuid::Uuid;
use zeroize::Zeroize;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ItemKind {
Login,
SecureNote,
Card,
Identity,
}
/// A stored WebAuthn credential (docs/05 "Passkeys").
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Zeroize)]
pub struct Passkey {
pub credential_id: Vec<u8>,
pub rp_id: String,
pub user_handle: Vec<u8>,
pub user_name: String,
/// PKCS#8 DER of the P-256 private key.
pub private_key: Vec<u8>,
pub sign_count: u32,
pub created_at: i64,
}
/// One item. Field-level structure is what makes three-way merges possible (docs/03).
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Item {
pub id: Uuid,
pub kind: ItemKind,
pub title: String,
#[serde(default)]
pub username: Option<String>,
#[serde(default)]
pub password: Option<String>,
#[serde(default)]
pub urls: Vec<String>,
/// `otpauth://` URI or a bare base32 secret.
#[serde(default)]
pub totp: Option<String>,
#[serde(default)]
pub notes: Option<String>,
#[serde(default)]
pub passkeys: Vec<Passkey>,
#[serde(default)]
pub tags: Vec<String>,
#[serde(default)]
pub favorite: bool,
/// Android package names this login may fill (verified via Digital Asset Links when possible).
#[serde(default)]
pub android_apps: Vec<String>,
pub created_at: i64,
pub modified_at: i64,
}
impl Item {
pub fn new_login(title: impl Into<String>, now: i64) -> Self {
Self {
id: Uuid::new_v4(),
kind: ItemKind::Login,
title: title.into(),
username: None,
password: None,
urls: vec![],
totp: None,
notes: None,
passkeys: vec![],
tags: vec![],
favorite: false,
android_apps: vec![],
created_at: now,
modified_at: now,
}
}
}
impl Drop for Item {
fn drop(&mut self) {
self.password.zeroize();
self.totp.zeroize();
self.notes.zeroize();
self.passkeys.zeroize();
}
}