use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct Item {
pub id: String,
pub name: String,
#[serde(rename = "type")]
pub item_type: ItemType,
#[serde(skip_serializing_if = "Option::is_none")]
pub notes: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub fields: Option<HashMap<String, String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub location: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub created: Option<DateTime<Utc>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub modified: Option<DateTime<Utc>>,
}
impl Item {
pub fn new_secure_note(name: impl Into<String>, notes: impl Into<String>) -> Self {
Self {
id: uuid::Uuid::new_v4().to_string(),
name: name.into(),
item_type: ItemType::SecureNote,
notes: Some(notes.into()),
fields: None,
location: None,
created: Some(Utc::now()),
modified: Some(Utc::now()),
}
}
pub fn new_login(name: impl Into<String>, username: String, password: String) -> Self {
let mut fields = HashMap::new();
fields.insert("username".to_string(), username);
fields.insert("password".to_string(), password);
Self {
id: uuid::Uuid::new_v4().to_string(),
name: name.into(),
item_type: ItemType::Login,
notes: None,
fields: Some(fields),
location: None,
created: Some(Utc::now()),
modified: Some(Utc::now()),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub enum ItemType {
SecureNote,
Login,
SSHKey,
Identity,
Card,
}
impl std::fmt::Display for ItemType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::SecureNote => write!(f, "SecureNote"),
Self::Login => write!(f, "Login"),
Self::SSHKey => write!(f, "SSHKey"),
Self::Identity => write!(f, "Identity"),
Self::Card => write!(f, "Card"),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_new_secure_note() {
let item = Item::new_secure_note("test-key", "test-value");
assert_eq!(item.name, "test-key");
assert_eq!(item.notes.as_deref(), Some("test-value"));
assert_eq!(item.item_type, ItemType::SecureNote);
assert!(item.created.is_some());
}
#[test]
fn test_new_login() {
let item = Item::new_login(
"github",
"user@example.com".to_string(),
"password123".to_string(),
);
assert_eq!(item.name, "github");
assert_eq!(item.item_type, ItemType::Login);
let fields = item.fields.unwrap();
assert_eq!(
fields.get("username"),
Some(&"user@example.com".to_string())
);
assert_eq!(fields.get("password"), Some(&"password123".to_string()));
}
#[test]
fn test_item_type_display() {
assert_eq!(ItemType::SecureNote.to_string(), "SecureNote");
assert_eq!(ItemType::Login.to_string(), "Login");
}
#[test]
fn test_item_serialization() {
let item = Item::new_secure_note("test", "value");
let json = serde_json::to_string(&item).unwrap();
let deserialized: Item = serde_json::from_str(&json).unwrap();
assert_eq!(item, deserialized);
}
}