sos_migrate/import/
mod.rs1use crate::{Error, Result};
3use enum_iterator::Sequence;
4use futures::StreamExt;
5use serde::de::DeserializeOwned;
6use std::{fmt, path::PathBuf, str::FromStr};
7use tokio::io::AsyncRead;
8
9pub mod csv;
10
11#[cfg(all(target_os = "macos", feature = "keychain-access"))]
12pub mod keychain;
13
14pub(crate) async fn read_csv_records<
15 T: DeserializeOwned,
16 R: AsyncRead + Unpin + Send,
17>(
18 reader: R,
19) -> Result<Vec<T>> {
20 let mut rows = Vec::new();
21 let mut rdr = csv_async::AsyncReaderBuilder::new()
22 .flexible(true)
23 .create_deserializer(reader);
24 let mut records = rdr.deserialize::<T>();
25 while let Some(record) = records.next().await {
26 let record = record?;
27 rows.push(record);
28 }
29 Ok(rows)
30}
31
32#[derive(Debug, Clone, Sequence)]
34pub enum ImportFormat {
35 OnePasswordCsv,
37 DashlaneZip,
39 BitwardenCsv,
41 ChromeCsv,
43 FirefoxCsv,
45 MacosCsv,
47}
48
49impl fmt::Display for ImportFormat {
50 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
51 write!(
52 f,
53 "{}",
54 match self {
55 Self::OnePasswordCsv => "onepassword.csv",
56 Self::DashlaneZip => "dashlane.zip",
57 Self::BitwardenCsv => "bitwarden.csv",
58 Self::ChromeCsv => "chrome.csv",
59 Self::FirefoxCsv => "firefox.csv",
60 Self::MacosCsv => "macos.csv",
61 }
62 )
63 }
64}
65
66impl FromStr for ImportFormat {
67 type Err = Error;
68 fn from_str(s: &str) -> Result<Self> {
69 Ok(match s {
70 "onepassword.csv" => Self::OnePasswordCsv,
71 "dashlane.zip" => Self::DashlaneZip,
72 "bitwarden.csv" => Self::BitwardenCsv,
73 "chrome.csv" => Self::ChromeCsv,
74 "firefox.csv" => Self::FirefoxCsv,
75 "macos.csv" => Self::MacosCsv,
76 _ => return Err(Error::UnknownImportFormat(s.to_owned())),
77 })
78 }
79}
80
81#[derive(Debug)]
83pub struct ImportTarget {
84 pub format: ImportFormat,
86 pub path: PathBuf,
88 pub folder_name: String,
90}