use crate::{Error, Result};
use enum_iterator::Sequence;
use futures::StreamExt;
use serde::de::DeserializeOwned;
use std::{fmt, path::PathBuf, str::FromStr};
use tokio::io::AsyncRead;
pub mod csv;
#[cfg(all(target_os = "macos", feature = "keychain-access"))]
pub mod keychain;
pub(crate) async fn read_csv_records<
T: DeserializeOwned,
R: AsyncRead + Unpin + Send,
>(
reader: R,
) -> Result<Vec<T>> {
let mut rows = Vec::new();
let mut rdr = csv_async::AsyncReaderBuilder::new()
.flexible(true)
.create_deserializer(reader);
let mut records = rdr.deserialize::<T>();
while let Some(record) = records.next().await {
let record = record?;
rows.push(record);
}
Ok(rows)
}
#[derive(Debug, Clone, Sequence)]
pub enum ImportFormat {
OnePasswordCsv,
DashlaneZip,
BitwardenCsv,
ChromeCsv,
FirefoxCsv,
MacosCsv,
}
impl fmt::Display for ImportFormat {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{}",
match self {
Self::OnePasswordCsv => "onepassword.csv",
Self::DashlaneZip => "dashlane.zip",
Self::BitwardenCsv => "bitwarden.csv",
Self::ChromeCsv => "chrome.csv",
Self::FirefoxCsv => "firefox.csv",
Self::MacosCsv => "macos.csv",
}
)
}
}
impl FromStr for ImportFormat {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
Ok(match s {
"onepassword.csv" => Self::OnePasswordCsv,
"dashlane.zip" => Self::DashlaneZip,
"bitwarden.csv" => Self::BitwardenCsv,
"chrome.csv" => Self::ChromeCsv,
"firefox.csv" => Self::FirefoxCsv,
"macos.csv" => Self::MacosCsv,
_ => return Err(Error::UnknownImportFormat(s.to_owned())),
})
}
}
#[derive(Debug)]
pub struct ImportTarget {
pub format: ImportFormat,
pub path: PathBuf,
pub folder_name: String,
}