sos_migrate/import/csv/
chrome.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 ChromePasswordRecord {
20 pub name: String,
22 pub url: Option<String>,
24 pub username: String,
26 pub password: String,
28 pub note: Option<String>,
30}
31
32impl From<ChromePasswordRecord> for GenericPasswordRecord {
33 fn from(value: ChromePasswordRecord) -> Self {
34 let label = if value.name.is_empty() {
35 UNTITLED.to_owned()
36 } else {
37 value.name
38 };
39
40 let url = if let Some(url) = value.url {
41 let mut websites = Vec::new();
42 for u in url.split(",") {
43 if let Ok(url) = u.trim().parse::<Url>() {
44 websites.push(url);
45 }
46 }
47 websites
48 } else {
49 vec![]
50 };
51
52 Self {
53 label,
54 url,
55 username: value.username,
56 password: value.password,
57 otp_auth: None,
58 tags: None,
59 note: value.note,
60 }
61 }
62}
63
64impl From<ChromePasswordRecord> for GenericCsvEntry {
65 fn from(value: ChromePasswordRecord) -> Self {
66 Self::Password(value.into())
67 }
68}
69
70pub async fn parse_reader<R: AsyncRead + Unpin + Send>(
72 reader: R,
73) -> Result<Vec<ChromePasswordRecord>> {
74 read_csv_records::<ChromePasswordRecord, _>(reader).await
75}
76
77pub async fn parse_path<P: AsRef<Path>>(
79 path: P,
80) -> Result<Vec<ChromePasswordRecord>> {
81 parse_reader(vfs::File::open(path).await?).await
82}
83
84pub struct ChromePasswordCsv;
86
87#[async_trait]
88impl Convert for ChromePasswordCsv {
89 type Input = PathBuf;
90
91 async fn convert(
92 &self,
93 source: Self::Input,
94 vault: Vault,
95 key: &AccessKey,
96 ) -> crate::Result<Vault> {
97 let records: Vec<GenericCsvEntry> = parse_path(source)
98 .await?
99 .into_iter()
100 .map(|r| r.into())
101 .collect();
102 GenericCsvConvert.convert(records, vault, key).await
103 }
104}