refactor(cli): move parse helpers into parse.rs
This commit is contained in:
@@ -16,6 +16,7 @@ use anyhow::{bail, Context, Result};
|
|||||||
use clap::{CommandFactory, Parser, Subcommand};
|
use clap::{CommandFactory, Parser, Subcommand};
|
||||||
use clap_complete::{generate, Shell};
|
use clap_complete::{generate, Shell};
|
||||||
|
|
||||||
|
use crate::parse::{base32_decode_lenient, guess_mime, parse_month_year};
|
||||||
use crate::prompt::{prompt, prompt_keep, prompt_keep_opt, prompt_optional, prompt_secret, prompt_yesno};
|
use crate::prompt::{prompt, prompt_keep, prompt_keep_opt, prompt_optional, prompt_secret, prompt_yesno};
|
||||||
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
@@ -915,46 +916,6 @@ fn build_totp_item(
|
|||||||
Ok(item)
|
Ok(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_month_year(s: &str) -> Result<relicario_core::MonthYear> {
|
|
||||||
// Accepts MM/YYYY or MM-YYYY or MM/YY.
|
|
||||||
let (m_str, y_str) = s.split_once(['/', '-'])
|
|
||||||
.ok_or_else(|| anyhow::anyhow!("expected MM/YYYY"))?;
|
|
||||||
let month: u8 = m_str.parse().context("invalid month")?;
|
|
||||||
let year: u16 = if y_str.len() == 2 {
|
|
||||||
2000 + y_str.parse::<u16>().context("invalid 2-digit year")?
|
|
||||||
} else {
|
|
||||||
y_str.parse().context("invalid year")?
|
|
||||||
};
|
|
||||||
Ok(relicario_core::MonthYear { month, year })
|
|
||||||
}
|
|
||||||
|
|
||||||
fn guess_mime(filename: &str) -> String {
|
|
||||||
let lower = filename.to_ascii_lowercase();
|
|
||||||
match lower.rsplit_once('.').map(|(_, ext)| ext).unwrap_or("") {
|
|
||||||
"pdf" => "application/pdf",
|
|
||||||
"png" => "image/png",
|
|
||||||
"jpg" | "jpeg" => "image/jpeg",
|
|
||||||
"txt" => "text/plain",
|
|
||||||
"json" => "application/json",
|
|
||||||
_ => "application/octet-stream",
|
|
||||||
}.to_string()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn base32_decode_lenient(s: &str) -> Result<Vec<u8>> {
|
|
||||||
let cleaned: String = s.chars()
|
|
||||||
.filter(|c| !c.is_whitespace())
|
|
||||||
.collect::<String>()
|
|
||||||
.to_ascii_uppercase()
|
|
||||||
.trim_end_matches('=')
|
|
||||||
.to_string();
|
|
||||||
let padded = {
|
|
||||||
let rem = cleaned.len() % 8;
|
|
||||||
if rem == 0 { cleaned } else { format!("{}{}", cleaned, "=".repeat(8 - rem)) }
|
|
||||||
};
|
|
||||||
data_encoding::BASE32.decode(padded.as_bytes())
|
|
||||||
.map_err(|e| anyhow::anyhow!("invalid base32: {e}"))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn commit_paths(vault: &crate::session::UnlockedVault, message: &str, paths: &[&str]) -> Result<()> {
|
fn commit_paths(vault: &crate::session::UnlockedVault, message: &str, paths: &[&str]) -> Result<()> {
|
||||||
let mut args: Vec<&str> = vec!["add"];
|
let mut args: Vec<&str> = vec!["add"];
|
||||||
args.extend_from_slice(paths);
|
args.extend_from_slice(paths);
|
||||||
|
|||||||
@@ -3,3 +3,45 @@
|
|||||||
//! Phase 7 of the CLI restructure migrates these to `relicario-core` and
|
//! Phase 7 of the CLI restructure migrates these to `relicario-core` and
|
||||||
//! turns this file into a thin re-export shim. They live here for now so
|
//! turns this file into a thin re-export shim. They live here for now so
|
||||||
//! the Phase 1 relocation stays mechanical.
|
//! the Phase 1 relocation stays mechanical.
|
||||||
|
|
||||||
|
use anyhow::{Context, Result};
|
||||||
|
|
||||||
|
pub(crate) fn parse_month_year(s: &str) -> Result<relicario_core::MonthYear> {
|
||||||
|
// Accepts MM/YYYY or MM-YYYY or MM/YY.
|
||||||
|
let (m_str, y_str) = s.split_once(['/', '-'])
|
||||||
|
.ok_or_else(|| anyhow::anyhow!("expected MM/YYYY"))?;
|
||||||
|
let month: u8 = m_str.parse().context("invalid month")?;
|
||||||
|
let year: u16 = if y_str.len() == 2 {
|
||||||
|
2000 + y_str.parse::<u16>().context("invalid 2-digit year")?
|
||||||
|
} else {
|
||||||
|
y_str.parse().context("invalid year")?
|
||||||
|
};
|
||||||
|
Ok(relicario_core::MonthYear { month, year })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn guess_mime(filename: &str) -> String {
|
||||||
|
let lower = filename.to_ascii_lowercase();
|
||||||
|
match lower.rsplit_once('.').map(|(_, ext)| ext).unwrap_or("") {
|
||||||
|
"pdf" => "application/pdf",
|
||||||
|
"png" => "image/png",
|
||||||
|
"jpg" | "jpeg" => "image/jpeg",
|
||||||
|
"txt" => "text/plain",
|
||||||
|
"json" => "application/json",
|
||||||
|
_ => "application/octet-stream",
|
||||||
|
}.to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn base32_decode_lenient(s: &str) -> Result<Vec<u8>> {
|
||||||
|
let cleaned: String = s.chars()
|
||||||
|
.filter(|c| !c.is_whitespace())
|
||||||
|
.collect::<String>()
|
||||||
|
.to_ascii_uppercase()
|
||||||
|
.trim_end_matches('=')
|
||||||
|
.to_string();
|
||||||
|
let padded = {
|
||||||
|
let rem = cleaned.len() % 8;
|
||||||
|
if rem == 0 { cleaned } else { format!("{}{}", cleaned, "=".repeat(8 - rem)) }
|
||||||
|
};
|
||||||
|
data_encoding::BASE32.decode(padded.as_bytes())
|
||||||
|
.map_err(|e| anyhow::anyhow!("invalid base32: {e}"))
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user