use anyhow::{format_err, Result};
use std::fs::File;
use std::io::{Read, Write};
use std::path::{Path, PathBuf};
use self::write_options::WriteOptions;
pub(crate) mod write_options;
pub fn find_closest_file<P: AsRef<Path>>(filename: &str, current_dir: P) -> Result<PathBuf> {
let mut current_dir = PathBuf::from(current_dir.as_ref());
loop {
let file_path = current_dir.join(filename);
if file_path.exists() {
return Ok(file_path);
}
if !current_dir.pop() {
return Err(format_err!(
"Couldn't find an available \"{}\" from {}.",
filename,
current_dir.display()
));
}
}
}
pub fn read_json<Json, FilePath>(file_path: FilePath) -> Result<Json>
where
Json: serde::de::DeserializeOwned,
FilePath: AsRef<Path>,
{
let mut file = File::open(file_path)?;
let mut contents = String::new();
file.read_to_string(&mut contents)?;
let serialized_json = serde_json::from_str(&contents);
match serialized_json {
Ok(json) => Ok(json),
Err(error) => Err(format_err!(error)),
}
}
pub fn write_json<Json, FilePath>(
file_path: FilePath,
json: Json,
write_options: &WriteOptions,
) -> Result<()>
where
Json: serde::Serialize,
FilePath: AsRef<Path>,
{
let package_json = if write_options.pretty {
serde_json::to_string_pretty(&json)
} else {
serde_json::to_string(&json)
};
match package_json {
Ok(json_content) => {
File::create(file_path)?.write_all(json_content.as_bytes())?;
Ok(())
}
Err(error) => Err(format_err!(error)),
}
}
#[test]
fn test_find_closest_file() {
use crate::PACKAGE_JSON_FILENAME;
use std::env::current_dir;
use std::fs::create_dir_all;
use std::io::Write;
use tempfile::tempdir_in;
let dir = tempdir_in(current_dir().unwrap()).expect("create temp_dir failed!");
let file_path = dir
.path()
.join(format!("nest/a/b/c/{}", PACKAGE_JSON_FILENAME));
let deeper_dir = file_path.parent().unwrap().join("d/e/f");
create_dir_all(file_path.parent().unwrap())
.unwrap_or_else(|_| panic!("create {} dir failed!", PACKAGE_JSON_FILENAME));
create_dir_all(&deeper_dir).expect("create nest/a/b/c/d/e/f dir failed!");
let mut valid_file = File::create(&file_path).expect("create file failed!");
let valid_json = r#"{
"name": "test"
}
"#;
valid_file
.write_all(valid_json.as_bytes())
.expect("write json failed");
for (dir, expect) in [
(dir.path().to_path_buf(), None),
(
file_path.parent().unwrap().parent().unwrap().join("ff"),
None,
),
(
file_path.parent().unwrap().to_path_buf(),
Some(file_path.to_owned()),
),
(file_path.to_path_buf(), Some(file_path.to_owned())),
(deeper_dir, Some(file_path.to_owned())),
] {
assert_eq!(
find_closest_file(PACKAGE_JSON_FILENAME, dir).ok(),
expect,
"find_closest_file failed!"
);
}
}
#[test]
fn test_read_json() {
use std::env::current_dir;
use tempfile::tempdir_in;
let dir = tempdir_in(current_dir().unwrap()).expect("create temp_dir failed!");
let file_path = dir.path().join("read.json");
let mut file = File::create(&file_path).expect("create file failed!");
let json = r#"{
"name": "test"
}"#;
file.write_all(json.as_bytes()).expect("write json failed");
let read_json = read_json::<serde_json::Value, &Path>(&file_path).unwrap();
assert_eq!(
read_json,
serde_json::json!({"name": "test"}),
"read_json failed!"
);
}
#[test]
fn test_write_json() {
use self::write_options::WriteOptionsBuilder;
use serde::Serialize;
use std::env::current_dir;
use tempfile::tempdir_in;
let dir = tempdir_in(current_dir().unwrap()).expect("create temp_dir failed!");
let file_path = dir.path().join("test.json");
#[derive(Serialize, Debug, Clone)]
struct TestJson {
name: String,
}
let test_json = TestJson {
name: "test".to_string(),
};
[
(
WriteOptionsBuilder::default()
.pretty(false)
.build()
.expect("build WriteOptions failed!"),
r#"{"name":"test"}"#,
),
(
WriteOptionsBuilder::default()
.build()
.expect("build WriteOptions failed!"),
r#"{
"name": "test"
}"#,
),
]
.iter()
.for_each(|(options, expect)| {
write_json(&file_path, test_json.clone(), options).expect("write json failed");
assert!(file_path.exists());
let mut file = File::open(&file_path).expect("open file failed!");
let mut content = String::new();
file
.read_to_string(&mut content)
.expect("read file failed!");
assert_eq!(content, expect.to_string(), "write_json failed!");
})
}