sos_migrate/import/
mod.rs

1//! Import secrets from other providers and software.
2use 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/// File formats supported for import.
33#[derive(Debug, Clone, Sequence)]
34pub enum ImportFormat {
35    /// 1Password CSV file.
36    OnePasswordCsv,
37    /// Dashlane zip archive.
38    DashlaneZip,
39    /// Bitwarden CSV file.
40    BitwardenCsv,
41    /// Chrome CSV file.
42    ChromeCsv,
43    /// Firefox CSV file.
44    FirefoxCsv,
45    /// MacOS CSV file.
46    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/// Target for an import operation.
82#[derive(Debug)]
83pub struct ImportTarget {
84    /// Expected file format.
85    pub format: ImportFormat,
86    /// Path to the file.
87    pub path: PathBuf,
88    /// Name for the folder.
89    pub folder_name: String,
90}