sos_migrate/import/csv/
macos.rs1use async_trait::async_trait;
4use serde::Deserialize;
5use sos_core::crypto::AccessKey;
6use sos_vault::Vault;
7use sos_vfs as vfs;
8use std::path::{Path, PathBuf};
9use tokio::io::AsyncRead;
10use url::Url;
11
12use super::{
13 GenericCsvConvert, GenericCsvEntry, GenericPasswordRecord, UNTITLED,
14};
15use crate::{import::read_csv_records, Convert, Result};
16
17#[derive(Deserialize)]
19pub struct MacPasswordRecord {
20 #[serde(rename = "Title")]
22 pub title: String,
23 #[serde(rename = "Url")]
25 pub url: Option<Url>,
26 #[serde(rename = "Username")]
28 pub username: String,
29 #[serde(rename = "Password")]
31 pub password: String,
32 #[serde(rename = "Notes")]
34 pub notes: Option<String>,
35 #[serde(rename = "OTPAuth")]
37 pub otp_auth: Option<String>,
38}
39
40impl From<MacPasswordRecord> for GenericPasswordRecord {
41 fn from(value: MacPasswordRecord) -> Self {
42 let label = if value.title.is_empty() {
43 UNTITLED.to_owned()
44 } else {
45 value.title
46 };
47
48 let url = if let Some(url) = value.url {
49 vec![url]
50 } else {
51 vec![]
52 };
53
54 Self {
55 label,
56 url,
57 username: value.username,
58 password: value.password,
59 otp_auth: value.otp_auth,
60 tags: None,
61 note: value.notes,
62 }
63 }
64}
65
66impl From<MacPasswordRecord> for GenericCsvEntry {
67 fn from(value: MacPasswordRecord) -> Self {
68 Self::Password(value.into())
69 }
70}
71
72pub async fn parse_reader<R: AsyncRead + Unpin + Send>(
74 reader: R,
75) -> Result<Vec<MacPasswordRecord>> {
76 read_csv_records::<MacPasswordRecord, _>(reader).await
77}
78
79pub async fn parse_path<P: AsRef<Path>>(
81 path: P,
82) -> Result<Vec<MacPasswordRecord>> {
83 parse_reader(vfs::File::open(path).await?).await
84}
85
86pub struct MacPasswordCsv;
88
89#[async_trait]
90impl Convert for MacPasswordCsv {
91 type Input = PathBuf;
92
93 async fn convert(
94 &self,
95 source: Self::Input,
96 vault: Vault,
97 key: &AccessKey,
98 ) -> crate::Result<Vault> {
99 let records: Vec<GenericCsvEntry> = parse_path(source)
100 .await?
101 .into_iter()
102 .map(|r| r.into())
103 .collect();
104 GenericCsvConvert.convert(records, vault, key).await
105 }
106}