use std::fs;
use std::path::PathBuf;
use crate::types::ValidLicenseInfo;
fn get_license_directory() -> Result<PathBuf, std::io::Error> {
let home_dir = std::env::var("HOME")
.or_else(|_| std::env::var("USERPROFILE"))
.map_err(|_| std::io::Error::new(std::io::ErrorKind::NotFound, "Home directory not found"))?;
let license_dir = PathBuf::from(home_dir).join(".offline-license");
if !license_dir.exists() {
fs::create_dir_all(&license_dir)?;
}
Ok(license_dir)
}
pub fn save_license(license_info: &ValidLicenseInfo, license_string: &str) -> Result<(), std::io::Error> {
let license_dir = get_license_directory()?;
let license_path = license_dir.join("license.json");
let data = serde_json::json!({
"license_string": license_string,
"license_type": license_info.license_type,
"issued_at": license_info.issued_at,
"expires_at": license_info.expires_at,
"hwid": license_info.hwid,
"metadata": license_info.metadata,
"validated_at": chrono::Utc::now(),
});
let json = serde_json::to_string_pretty(&data)?;
fs::write(license_path, json)?;
Ok(())
}
pub fn load_saved_license() -> Result<Option<(String, ValidLicenseInfo)>, std::io::Error> {
let license_dir = get_license_directory()?;
let license_path = license_dir.join("license.json");
if !license_path.exists() {
return Ok(None);
}
let content = fs::read_to_string(license_path)?;
let data: serde_json::Value = serde_json::from_str(&content)?;
let license_string = data["license_string"]
.as_str()
.ok_or_else(|| std::io::Error::new(std::io::ErrorKind::InvalidData, "Missing license_string"))?
.to_string();
let license_info = ValidLicenseInfo {
license_type: data["license_type"]
.as_str()
.ok_or_else(|| std::io::Error::new(std::io::ErrorKind::InvalidData, "Missing license_type"))?
.to_string(),
issued_at: serde_json::from_value(data["issued_at"].clone())
.map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))?,
expires_at: serde_json::from_value(data["expires_at"].clone())
.map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))?,
hwid: data["hwid"]
.as_str()
.ok_or_else(|| std::io::Error::new(std::io::ErrorKind::InvalidData, "Missing hwid"))?
.to_string(),
metadata: data["metadata"].clone().into(),
};
Ok(Some((license_string, license_info)))
}
pub fn delete_saved_license() -> Result<(), std::io::Error> {
let license_dir = get_license_directory()?;
let license_path = license_dir.join("license.json");
if license_path.exists() {
fs::remove_file(license_path)?;
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use chrono::Utc;
#[test]
fn test_license_directory() {
let dir = get_license_directory().unwrap();
assert!(dir.to_string_lossy().contains(".offline-license"));
}
#[test]
fn test_save_and_load_license() {
let license_info = ValidLicenseInfo {
license_type: "PRO".to_string(),
issued_at: Utc::now(),
expires_at: Utc::now() + chrono::Duration::days(365),
hwid: "test-hwid".to_string(),
metadata: None,
};
let license_string = "test_license_string";
save_license(&license_info, license_string).unwrap();
let loaded = load_saved_license().unwrap();
assert!(loaded.is_some());
let (loaded_string, loaded_info) = loaded.unwrap();
assert_eq!(loaded_string, license_string);
assert_eq!(loaded_info.license_type, "PRO");
delete_saved_license().unwrap();
}
}