use crate::fs;
use crate::fs::write_options::WriteOptions;
use crate::PackageJson;
use anyhow::{format_err, Result};
use std::env;
use std::path::{Path, PathBuf};
pub const PACKAGE_JSON_FILENAME: &str = "package.json";
#[derive(Debug)]
pub struct PackageJsonManager {
file_path: Option<PathBuf>,
json: PackageJson,
write_options: Option<WriteOptions>,
}
impl Default for PackageJsonManager {
fn default() -> Self {
Self {
file_path: None,
json: Default::default(),
write_options: Some(WriteOptions::default()),
}
}
}
impl PackageJsonManager {
pub fn new() -> Self {
Default::default()
}
pub fn with_file_path<FilePath>(path: FilePath) -> Self
where
FilePath: AsRef<Path>,
{
Self {
file_path: Some(path.as_ref().to_path_buf()),
..Default::default()
}
}
pub fn with_write_options(options: WriteOptions) -> Self {
Self {
write_options: Some(options),
..Default::default()
}
}
pub fn locate_closest(&mut self) -> Result<PathBuf> {
env::current_dir().map(|cwd| self.locate_closest_from(cwd))?
}
pub fn locate_closest_from<P: AsRef<Path>>(&mut self, from: P) -> Result<PathBuf> {
fs::find_closest_file(PACKAGE_JSON_FILENAME, from).map(|file_path| {
self.file_path = Some(file_path);
self.file_path.as_ref().unwrap().to_owned()
})
}
pub fn set_file_path<FilePath: AsRef<Path>>(&mut self, file_path: FilePath) {
self.file_path = Some(file_path.as_ref().to_path_buf());
}
pub fn get_file_path(&mut self) -> Option<&Path> {
self.file_path.as_deref()
}
pub fn take_file_path(&mut self) -> Option<PathBuf> {
self.file_path.take()
}
fn read(&mut self) -> Result<()> {
self
.file_path
.as_ref()
.map(|file_path| {
fs::read_json(file_path).map(|json| {
self.json = json;
})
})
.unwrap_or_else(|| {
Err(format_err!(
"Couldn't find an available {} file.",
PACKAGE_JSON_FILENAME
))
})
}
pub fn read_ref(&mut self) -> Result<&PackageJson> {
self.read().map(|_| &self.json)
}
pub fn read_mut(&mut self) -> Result<&mut PackageJson> {
self.read().map(|_| &mut self.json)
}
pub fn write(&mut self) -> Result<()> {
self
.file_path
.as_ref()
.map(|file_path| {
fs::write_json(
file_path,
&self.json,
self
.write_options
.as_ref()
.expect("self.write_options should not be None"),
)
})
.unwrap_or_else(|| {
Err(format_err!(
"Couldn't find an available {} file.",
PACKAGE_JSON_FILENAME
))
})
}
pub fn write_to(&mut self, file_path: &Path) -> Result<()> {
fs::write_json(
file_path,
&self.json,
self
.write_options
.as_ref()
.expect("self.write_options should not be None"),
)
}
}
impl AsRef<PackageJson> for PackageJsonManager {
fn as_ref(&self) -> &PackageJson {
&self.json
}
}
impl AsMut<PackageJson> for PackageJsonManager {
fn as_mut(&mut self) -> &mut PackageJson {
&mut self.json
}
}
#[test]
fn test_new() {
use std::path::PathBuf;
let mut manager = PackageJsonManager::new();
assert_eq!(manager.file_path, None);
let file_path = PathBuf::from("/path/to/package.json");
manager.set_file_path(&file_path);
assert_eq!(manager.file_path, Some(file_path.to_owned()));
assert_eq!(manager.get_file_path(), Some(file_path.as_ref()));
assert_eq!(manager.take_file_path(), Some(file_path.to_owned()));
assert_eq!(manager.file_path, None);
}
#[test]
fn test_readable() {
use crate::PACKAGE_JSON_FILENAME;
use std::env::current_dir;
use std::fs::{create_dir_all, File};
use std::io::Write;
use tempfile::tempdir_in;
let mut manager = PackageJsonManager::new();
assert!(manager.read_ref().is_err(), "found an available file.");
assert!(
manager.get_file_path().is_none(),
"found an available file."
);
assert!(
manager.locate_closest().is_err(),
"found an available file."
);
let dir = tempdir_in(current_dir().unwrap()).expect("create temp_dir failed!");
let file_path = dir.path().join(format!("a/b/c/{}", PACKAGE_JSON_FILENAME));
let file_dir = file_path.parent().unwrap();
let deeper_file_dir = file_dir.join("d/e/f");
create_dir_all(file_dir).expect("create a/b/c dir failed!");
create_dir_all(deeper_file_dir.as_path()).expect("create d/e/f dir field!");
let mut valid_file = File::create(&file_path).expect("create file failed!");
let valid_json = r#"{
"name": "test",
"version": "0.0.1"
}"#;
valid_file
.write_all(valid_json.as_bytes())
.expect("write json failed");
for (dir, expect) in [
(dir.path(), None),
(file_dir, Some(file_path.to_owned())),
(deeper_file_dir.as_path(), Some(file_path.to_owned())),
] {
assert_eq!(manager.locate_closest_from(dir).ok(), expect);
if expect.is_some() {
assert!(manager.read_ref().is_ok(), "read file failed.");
assert_eq!(manager.get_file_path().map(|p| p.to_path_buf()), expect);
if let Ok(json) = manager.read_ref() {
assert_eq!(json.name, "test");
assert_eq!(json.version, "0.0.1");
}
let handler = manager.as_ref();
assert_eq!(handler.name, "test");
assert_eq!(handler.version, "0.0.1");
assert!(!handler.private);
} else {
assert!(manager.read_ref().is_err(), "read field successful.")
}
}
}
#[test]
fn test_writable() {
use crate::PACKAGE_JSON_FILENAME;
use std::env::current_dir;
use std::fs::{create_dir_all, File};
use std::io::Write;
use tempfile::tempdir_in;
let mut manager = PackageJsonManager::new();
assert!(manager.write().is_err(), "found an available file.");
let dir = tempdir_in(current_dir().unwrap()).expect("create temp_dir failed!");
let file_path = dir.path().join(format!("a/b/c/{}", PACKAGE_JSON_FILENAME));
let file_dir = file_path.parent().unwrap();
create_dir_all(file_dir).expect("create a/b/c dir failed!");
let mut valid_file = File::create(&file_path).expect("create file failed!");
let valid_json = r#"{
"name": "test",
"version": "0.0.1"
}"#;
valid_file
.write_all(valid_json.as_bytes())
.expect("write json failed");
manager.set_file_path(&file_path);
{
let file_writer = manager.read_mut();
assert!(
file_writer.is_ok(),
"{}",
format!("create file writer failed: {:?}", file_writer)
);
if let Ok(mut json) = file_writer {
json.name = "test2".to_string();
json.version = "0.0.2".to_string();
assert!(manager.write().is_ok());
}
let file_reader = manager.as_ref();
assert_eq!(file_reader.name, "test2");
assert_eq!(file_reader.version, "0.0.2");
}
{
let mut mutable_handler = manager.as_mut();
mutable_handler.name = "test3".to_string();
mutable_handler.version = "0.0.3".to_string();
let file_reader = manager.as_ref();
assert_eq!(file_reader.name, "test3");
assert_eq!(file_reader.version, "0.0.3");
}
}