use std::fs::{self, File};
use std::io::{Read, Write};
use std::path::PathBuf;
use crate::error::{Result, SpectreError};
use crate::models::*;
use crate::algorithm::{spectre_user_key, spectre_identicon};
pub fn spectre_marshal_read(file_path: &PathBuf) -> Result<(SpectreMarshalledFile, Option<SpectreMarshalledUser>)> {
if !file_path.exists() {
return Ok((
SpectreMarshalledFile::new(SPECTRE_FORMAT_DEFAULT, true),
None,
));
}
let mut file = File::open(file_path)?;
let mut contents = String::new();
file.read_to_string(&mut contents)?;
match serde_json::from_str::<SpectreMarshalledUser>(&contents) {
Ok(user) => {
let file_meta = SpectreMarshalledFile::new(SpectreFormat::JSON, user.redacted);
Ok((file_meta, Some(user)))
}
Err(_) => {
parse_flat_format(&contents)
}
}
}
pub fn spectre_marshal_write(
file_path: &PathBuf,
format: SpectreFormat,
user: &SpectreMarshalledUser,
) -> Result<()> {
if let Some(parent) = file_path.parent() {
fs::create_dir_all(parent)?;
}
let contents = match format {
SpectreFormat::JSON => {
serde_json::to_string_pretty(user)?
}
SpectreFormat::Flat => {
write_flat_format(user)?
}
SpectreFormat::None => {
return Ok(());
}
};
let mut file = File::create(file_path)?;
file.write_all(contents.as_bytes())?;
Ok(())
}
fn parse_flat_format(_contents: &str) -> Result<(SpectreMarshalledFile, Option<SpectreMarshalledUser>)> {
Err(SpectreError::InvalidFileFormat("Flat format parsing not fully implemented. Please use JSON format.".to_string()))
}
fn write_flat_format(_user: &SpectreMarshalledUser) -> Result<String> {
Err(SpectreError::InvalidFileFormat("Flat format writing not fully implemented. Please use JSON format.".to_string()))
}
pub fn spectre_marshal_auth(
user: &mut SpectreMarshalledUser,
user_secret: &str,
) -> Result<()> {
let user_key = spectre_user_key(&user.user_name, user_secret, user.algorithm)?;
if user.key_id != user_key.key_id {
return Err(SpectreError::UserSecretMismatch);
}
let identicon = spectre_identicon(&user.user_name, user_secret)?;
user.identicon = identicon;
Ok(())
}
pub fn spectre_user_path(user_name: &str, format: SpectreFormat) -> Option<PathBuf> {
let home_dir = dirs::home_dir()?;
let spectre_dir = home_dir.join(".spectre.d");
let extension = format.extension();
if extension.is_empty() {
return None;
}
let filename = format!("{}.{}", user_name, extension);
Some(spectre_dir.join(filename))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::types::*;
#[test]
fn test_user_path() {
let path = spectre_user_path("testuser", SpectreFormat::JSON);
assert!(path.is_some());
assert!(path.unwrap().to_string_lossy().contains(".spectre.d"));
}
#[test]
fn test_json_serialization() {
let user = SpectreMarshalledUser::new(
"testuser".to_string(),
[0, 1, 2, 3],
[0u8; 32],
SPECTRE_ALGORITHM_CURRENT,
);
let json = serde_json::to_string(&user);
assert!(json.is_ok());
}
}