Consolidate config functions and keep `Config` values loose.
parent
223dfaf8a0
commit
d4183f2b52
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
remote: https://github.com/jrpotter/home-config.git
|
local: ""
|
||||||
|
remote: "https://github.com/jrpotter/home-config.git"
|
||||||
packages:
|
packages:
|
||||||
homesync:
|
homesync:
|
||||||
configs:
|
configs:
|
||||||
|
|
142
src/cli.rs
142
src/cli.rs
|
@ -1,142 +0,0 @@
|
||||||
use super::config::PathConfig;
|
|
||||||
use super::{config, git, path};
|
|
||||||
use ansi_term::Colour::{Green, Yellow};
|
|
||||||
use std::env::VarError;
|
|
||||||
use std::io::Write;
|
|
||||||
use std::path::PathBuf;
|
|
||||||
use std::{error, fmt, io};
|
|
||||||
use url::{ParseError, Url};
|
|
||||||
|
|
||||||
// TODO(jrpotter): Use curses to make this module behave nicer.
|
|
||||||
|
|
||||||
// ========================================
|
|
||||||
// Error
|
|
||||||
// ========================================
|
|
||||||
|
|
||||||
pub type Result<T> = std::result::Result<T, Error>;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum Error {
|
|
||||||
ConfigError(config::Error),
|
|
||||||
IOError(io::Error),
|
|
||||||
ParseError(ParseError),
|
|
||||||
VarError(VarError),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<config::Error> for Error {
|
|
||||||
fn from(err: config::Error) -> Error {
|
|
||||||
Error::ConfigError(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<git::Error> for Error {
|
|
||||||
fn from(err: git::Error) -> Error {
|
|
||||||
match err {
|
|
||||||
git::Error::IOError(e) => Error::IOError(e),
|
|
||||||
git::Error::VarError(e) => Error::VarError(e),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<io::Error> for Error {
|
|
||||||
fn from(err: io::Error) -> Error {
|
|
||||||
Error::IOError(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<path::Error> for Error {
|
|
||||||
fn from(err: path::Error) -> Error {
|
|
||||||
match err {
|
|
||||||
path::Error::IOError(e) => Error::IOError(e),
|
|
||||||
path::Error::VarError(e) => Error::VarError(e),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<ParseError> for Error {
|
|
||||||
fn from(err: ParseError) -> Error {
|
|
||||||
Error::ParseError(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<VarError> for Error {
|
|
||||||
fn from(err: VarError) -> Error {
|
|
||||||
Error::VarError(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for Error {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
Error::ConfigError(e) => write!(f, "{}", e),
|
|
||||||
Error::IOError(e) => write!(f, "{}", e),
|
|
||||||
Error::ParseError(e) => write!(f, "{}", e),
|
|
||||||
Error::VarError(e) => write!(f, "{}", e),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl error::Error for Error {}
|
|
||||||
|
|
||||||
// ========================================
|
|
||||||
// Prompts
|
|
||||||
// ========================================
|
|
||||||
|
|
||||||
fn prompt_local(config: &PathConfig) -> Result<PathBuf> {
|
|
||||||
print!(
|
|
||||||
"Local git repository <{}> (enter to continue): ",
|
|
||||||
Yellow.paint(
|
|
||||||
config
|
|
||||||
.1
|
|
||||||
.local
|
|
||||||
.as_ref()
|
|
||||||
.map_or("".to_owned(), |v| v.display().to_string())
|
|
||||||
)
|
|
||||||
);
|
|
||||||
io::stdout().flush()?;
|
|
||||||
let mut local = String::new();
|
|
||||||
io::stdin().read_line(&mut local)?;
|
|
||||||
Ok(PathBuf::from(path::expand_env(&local.trim())?))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn prompt_remote(config: &PathConfig) -> Result<Url> {
|
|
||||||
print!(
|
|
||||||
"Remote git repository <{}> (enter to continue): ",
|
|
||||||
Yellow.paint(config.1.remote.to_string())
|
|
||||||
);
|
|
||||||
io::stdout().flush()?;
|
|
||||||
let mut remote = String::new();
|
|
||||||
io::stdin().read_line(&mut remote)?;
|
|
||||||
Ok(Url::parse(&remote)?)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ========================================
|
|
||||||
// CLI
|
|
||||||
// ========================================
|
|
||||||
|
|
||||||
pub fn write_config(mut pending: PathConfig) -> Result<()> {
|
|
||||||
println!(
|
|
||||||
"Generating config at {}...\n",
|
|
||||||
Green.paint(pending.0.unresolved().display().to_string())
|
|
||||||
);
|
|
||||||
let local = prompt_local(&pending)?;
|
|
||||||
let remote = prompt_remote(&pending)?;
|
|
||||||
// Try to initialize the local respository if we can.
|
|
||||||
let resolved = git::init(&local, &pending)?;
|
|
||||||
pending.1.local = Some(resolved);
|
|
||||||
pending.1.remote = remote;
|
|
||||||
pending.write()?;
|
|
||||||
println!("\nFinished writing configuration file.");
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn list_packages(config: PathConfig) {
|
|
||||||
println!(
|
|
||||||
"Listing packages in {}...\n",
|
|
||||||
Green.paint(config.0.unresolved().display().to_string())
|
|
||||||
);
|
|
||||||
// Alphabetical ordered ensured by B-tree implementation.
|
|
||||||
for (k, _) in config.1.packages {
|
|
||||||
println!("• {}", k);
|
|
||||||
}
|
|
||||||
}
|
|
137
src/config.rs
137
src/config.rs
|
@ -1,10 +1,13 @@
|
||||||
|
use super::path;
|
||||||
use super::path::ResPathBuf;
|
use super::path::ResPathBuf;
|
||||||
|
use ansi_term::Colour::{Green, Yellow};
|
||||||
use serde_derive::{Deserialize, Serialize};
|
use serde_derive::{Deserialize, Serialize};
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
use std::env::VarError;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::path::PathBuf;
|
use std::path::{Path, PathBuf};
|
||||||
use std::{error, fmt, fs, io};
|
use std::{error, fmt, fs, io};
|
||||||
use url::Url;
|
use url::{ParseError, Url};
|
||||||
|
|
||||||
// ========================================
|
// ========================================
|
||||||
// Error
|
// Error
|
||||||
|
@ -14,14 +17,16 @@ pub type Result<T> = std::result::Result<T, Error>;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
FileError(io::Error),
|
IOError(io::Error),
|
||||||
MissingConfig,
|
MissingConfig,
|
||||||
|
ParseError(ParseError),
|
||||||
SerdeError(serde_yaml::Error),
|
SerdeError(serde_yaml::Error),
|
||||||
|
VarError(VarError),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<io::Error> for Error {
|
impl From<io::Error> for Error {
|
||||||
fn from(err: io::Error) -> Error {
|
fn from(err: io::Error) -> Error {
|
||||||
Error::FileError(err)
|
Error::IOError(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,12 +36,35 @@ impl From<serde_yaml::Error> for Error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<path::Error> for Error {
|
||||||
|
fn from(err: path::Error) -> Error {
|
||||||
|
match err {
|
||||||
|
path::Error::IOError(e) => Error::IOError(e),
|
||||||
|
path::Error::VarError(e) => Error::VarError(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ParseError> for Error {
|
||||||
|
fn from(err: ParseError) -> Error {
|
||||||
|
Error::ParseError(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<VarError> for Error {
|
||||||
|
fn from(err: VarError) -> Error {
|
||||||
|
Error::VarError(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl fmt::Display for Error {
|
impl fmt::Display for Error {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Error::FileError(e) => write!(f, "{}", e),
|
Error::IOError(e) => write!(f, "{}", e),
|
||||||
Error::MissingConfig => write!(f, "Could not find configuration file"),
|
Error::MissingConfig => write!(f, "Could not find configuration file"),
|
||||||
|
Error::ParseError(e) => write!(f, "{}", e),
|
||||||
Error::SerdeError(e) => write!(f, "{}", e),
|
Error::SerdeError(e) => write!(f, "{}", e),
|
||||||
|
Error::VarError(e) => write!(f, "{}", e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -54,7 +82,7 @@ pub struct Package {
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub local: Option<ResPathBuf>,
|
pub local: PathBuf,
|
||||||
pub remote: Url,
|
pub remote: Url,
|
||||||
pub packages: BTreeMap<String, Package>,
|
pub packages: BTreeMap<String, Package>,
|
||||||
}
|
}
|
||||||
|
@ -69,15 +97,8 @@ impl Config {
|
||||||
pub struct PathConfig(pub ResPathBuf, pub Config);
|
pub struct PathConfig(pub ResPathBuf, pub Config);
|
||||||
|
|
||||||
impl PathConfig {
|
impl PathConfig {
|
||||||
pub fn new(path: &ResPathBuf, config: Option<Config>) -> Self {
|
pub fn new(path: &ResPathBuf, config: Config) -> Self {
|
||||||
PathConfig(
|
PathConfig(path.clone(), config)
|
||||||
path.clone(),
|
|
||||||
config.unwrap_or(Config {
|
|
||||||
local: None,
|
|
||||||
remote: Url::parse("http://github.com/user/repo.git").unwrap(),
|
|
||||||
packages: BTreeMap::new(),
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(jrpotter): Create backup file before overwriting.
|
// TODO(jrpotter): Create backup file before overwriting.
|
||||||
|
@ -110,10 +131,10 @@ pub fn load(candidates: &Vec<ResPathBuf>) -> Result<PathConfig> {
|
||||||
for candidate in candidates {
|
for candidate in candidates {
|
||||||
match fs::read_to_string(candidate) {
|
match fs::read_to_string(candidate) {
|
||||||
Err(err) if err.kind() == io::ErrorKind::NotFound => continue,
|
Err(err) if err.kind() == io::ErrorKind::NotFound => continue,
|
||||||
Err(err) => return Err(Error::FileError(err)),
|
Err(err) => Err(Error::IOError(err))?,
|
||||||
Ok(contents) => {
|
Ok(contents) => {
|
||||||
let config = Config::new(&contents)?;
|
let config = Config::new(&contents)?;
|
||||||
return Ok(PathConfig::new(candidate, Some(config)));
|
return Ok(PathConfig::new(candidate, config));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -125,3 +146,85 @@ pub fn reload(config: &PathConfig) -> Result<PathConfig> {
|
||||||
println!("Configuration reloaded.");
|
println!("Configuration reloaded.");
|
||||||
load(&vec![config.0.clone()])
|
load(&vec![config.0.clone()])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ========================================
|
||||||
|
// Creation
|
||||||
|
// ========================================
|
||||||
|
|
||||||
|
fn prompt_local(path: Option<&Path>) -> Result<PathBuf> {
|
||||||
|
let default = path.map_or("$HOME/.homesync".to_owned(), |p| p.display().to_string());
|
||||||
|
print!(
|
||||||
|
"Local git repository <{}> (enter to continue): ",
|
||||||
|
Yellow.paint(&default)
|
||||||
|
);
|
||||||
|
io::stdout().flush()?;
|
||||||
|
let mut local = String::new();
|
||||||
|
io::stdin().read_line(&mut local)?;
|
||||||
|
// Defer validation this path until initialization of the repository.
|
||||||
|
let local = local.trim();
|
||||||
|
if local.is_empty() {
|
||||||
|
Ok(PathBuf::from(default))
|
||||||
|
} else {
|
||||||
|
Ok(PathBuf::from(local))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn prompt_remote(url: Option<&Url>) -> Result<Url> {
|
||||||
|
let default = url.map_or("https://github.com/owner/repo.git".to_owned(), |u| {
|
||||||
|
u.to_string()
|
||||||
|
});
|
||||||
|
print!(
|
||||||
|
"Remote git repository <{}> (enter to continue): ",
|
||||||
|
Yellow.paint(&default)
|
||||||
|
);
|
||||||
|
io::stdout().flush()?;
|
||||||
|
let mut remote = String::new();
|
||||||
|
io::stdin().read_line(&mut remote)?;
|
||||||
|
let remote = remote.trim();
|
||||||
|
if remote.is_empty() {
|
||||||
|
Ok(Url::parse(&default)?)
|
||||||
|
} else {
|
||||||
|
Ok(Url::parse(&remote)?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write(path: &ResPathBuf, loaded: Option<Config>) -> Result<PathConfig> {
|
||||||
|
println!(
|
||||||
|
"Generating config at {}...\n",
|
||||||
|
Green.paint(path.unresolved().display().to_string())
|
||||||
|
);
|
||||||
|
let local = prompt_local(match &loaded {
|
||||||
|
Some(c) => Some(c.local.as_ref()),
|
||||||
|
None => None,
|
||||||
|
})?;
|
||||||
|
let remote = prompt_remote(match &loaded {
|
||||||
|
Some(c) => Some(&c.remote),
|
||||||
|
None => None,
|
||||||
|
})?;
|
||||||
|
let generated = PathConfig(
|
||||||
|
path.clone(),
|
||||||
|
Config {
|
||||||
|
local,
|
||||||
|
remote,
|
||||||
|
packages: loaded.map_or(BTreeMap::new(), |c| c.packages),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
generated.write()?;
|
||||||
|
println!("\nFinished writing configuration file.");
|
||||||
|
Ok(generated)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========================================
|
||||||
|
// Listing
|
||||||
|
// ========================================
|
||||||
|
|
||||||
|
pub fn list_packages(config: PathConfig) {
|
||||||
|
println!(
|
||||||
|
"Listing packages in {}...\n",
|
||||||
|
Green.paint(config.0.unresolved().display().to_string())
|
||||||
|
);
|
||||||
|
// Alphabetical ordered ensured by B-tree implementation.
|
||||||
|
for (k, _) in config.1.packages {
|
||||||
|
println!("• {}", k);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
30
src/git.rs
30
src/git.rs
|
@ -53,33 +53,9 @@ impl error::Error for Error {}
|
||||||
// Validation
|
// Validation
|
||||||
// ========================================
|
// ========================================
|
||||||
|
|
||||||
fn validate_is_file(path: &Path) -> Result<()> {
|
|
||||||
let metadata = fs::metadata(path)?;
|
|
||||||
if !metadata.is_file() {
|
|
||||||
// TODO(jrpotter): Use `IsADirectory` when stable.
|
|
||||||
Err(io::Error::new(
|
|
||||||
io::ErrorKind::Other,
|
|
||||||
format!("'{}' is not a file.", path.display()),
|
|
||||||
))?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn validate_is_dir(path: &Path) -> Result<()> {
|
|
||||||
let metadata = fs::metadata(path)?;
|
|
||||||
if !metadata.is_dir() {
|
|
||||||
// TODO(jrpotter): Use `NotADirectory` when stable.
|
|
||||||
Err(io::Error::new(
|
|
||||||
io::ErrorKind::Other,
|
|
||||||
format!("'{}' is not a directory.", path.display()),
|
|
||||||
))?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn validate_local(path: &Path) -> Result<()> {
|
pub fn validate_local(path: &Path) -> Result<()> {
|
||||||
let resolved = path::resolve(path)?;
|
let resolved = path::resolve(path)?;
|
||||||
validate_is_dir(resolved.as_ref())?;
|
path::validate_is_dir(resolved.as_ref())?;
|
||||||
|
|
||||||
let mut local: PathBuf = resolved.into();
|
let mut local: PathBuf = resolved.into();
|
||||||
local.push(".git");
|
local.push(".git");
|
||||||
|
@ -92,7 +68,7 @@ pub fn validate_local(path: &Path) -> Result<()> {
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
validate_is_dir(local.as_ref())?;
|
path::validate_is_dir(local.as_ref())?;
|
||||||
|
|
||||||
local.pop();
|
local.pop();
|
||||||
local.push(".homesync");
|
local.push(".homesync");
|
||||||
|
@ -105,7 +81,7 @@ pub fn validate_local(path: &Path) -> Result<()> {
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
validate_is_file(local.as_ref())?;
|
path::validate_is_file(local.as_ref())?;
|
||||||
|
|
||||||
// TODO(jrpotter): Verify git repository is pointing to remote.
|
// TODO(jrpotter): Verify git repository is pointing to remote.
|
||||||
|
|
||||||
|
|
12
src/lib.rs
12
src/lib.rs
|
@ -1,4 +1,3 @@
|
||||||
pub mod cli;
|
|
||||||
pub mod config;
|
pub mod config;
|
||||||
pub mod daemon;
|
pub mod daemon;
|
||||||
pub mod git;
|
pub mod git;
|
||||||
|
@ -22,7 +21,7 @@ pub fn run_daemon(config: PathConfig, freq_secs: u64) -> Result<(), Box<dyn Erro
|
||||||
pub fn run_init(candidates: Vec<ResPathBuf>) -> Result<(), Box<dyn Error>> {
|
pub fn run_init(candidates: Vec<ResPathBuf>) -> Result<(), Box<dyn Error>> {
|
||||||
debug_assert!(!candidates.is_empty(), "Empty candidates found in `init`.");
|
debug_assert!(!candidates.is_empty(), "Empty candidates found in `init`.");
|
||||||
if candidates.is_empty() {
|
if candidates.is_empty() {
|
||||||
Err(config::Error::FileError(io::Error::new(
|
Err(config::Error::IOError(io::Error::new(
|
||||||
io::ErrorKind::NotFound,
|
io::ErrorKind::NotFound,
|
||||||
"No suitable config file found.",
|
"No suitable config file found.",
|
||||||
)))?;
|
)))?;
|
||||||
|
@ -31,8 +30,8 @@ pub fn run_init(candidates: Vec<ResPathBuf>) -> Result<(), Box<dyn Error>> {
|
||||||
// Check if we already have a local config somewhere. If so, reprompt
|
// Check if we already have a local config somewhere. If so, reprompt
|
||||||
// the same configuration options and override the values present in the
|
// the same configuration options and override the values present in the
|
||||||
// current YAML file.
|
// current YAML file.
|
||||||
Ok(pending) => {
|
Ok(loaded) => {
|
||||||
cli::write_config(pending)?;
|
config::write(&loaded.0, Some(loaded.1))?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
// Otherwise create a new config file at the given location. We always
|
// Otherwise create a new config file at the given location. We always
|
||||||
|
@ -42,8 +41,7 @@ pub fn run_init(candidates: Vec<ResPathBuf>) -> Result<(), Box<dyn Error>> {
|
||||||
// TODO(jrpotter): Verify I have permission to write at specified path.
|
// TODO(jrpotter): Verify I have permission to write at specified path.
|
||||||
// Make directories if necessary.
|
// Make directories if necessary.
|
||||||
Err(config::Error::MissingConfig) if !candidates.is_empty() => {
|
Err(config::Error::MissingConfig) if !candidates.is_empty() => {
|
||||||
let pending = PathConfig::new(&candidates[0], None);
|
config::write(&candidates[0], None)?;
|
||||||
cli::write_config(pending)?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Err(e) => Err(e)?,
|
Err(e) => Err(e)?,
|
||||||
|
@ -51,7 +49,7 @@ pub fn run_init(candidates: Vec<ResPathBuf>) -> Result<(), Box<dyn Error>> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run_list(config: PathConfig) -> Result<(), config::Error> {
|
pub fn run_list(config: PathConfig) -> Result<(), config::Error> {
|
||||||
cli::list_packages(config);
|
config::list_packages(config);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -60,9 +60,6 @@ fn dispatch(matches: clap::ArgMatches) -> Result<(), Box<dyn Error>> {
|
||||||
// used, even if one of higher priority is eventually defined.
|
// used, even if one of higher priority is eventually defined.
|
||||||
subcommand => {
|
subcommand => {
|
||||||
let config = homesync::config::load(&candidates)?;
|
let config = homesync::config::load(&candidates)?;
|
||||||
if let Some(local) = &config.1.local {
|
|
||||||
homesync::git::validate_local(local.as_ref())?;
|
|
||||||
}
|
|
||||||
match subcommand {
|
match subcommand {
|
||||||
Some(("add", _)) => Ok(homesync::run_add(config)?),
|
Some(("add", _)) => Ok(homesync::run_add(config)?),
|
||||||
Some(("daemon", matches)) => {
|
Some(("daemon", matches)) => {
|
||||||
|
|
30
src/path.rs
30
src/path.rs
|
@ -6,7 +6,7 @@ use std::env::VarError;
|
||||||
use std::ffi::OsString;
|
use std::ffi::OsString;
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
use std::path::{Component, Path, PathBuf};
|
use std::path::{Component, Path, PathBuf};
|
||||||
use std::{env, error, fmt, io, result, str};
|
use std::{env, error, fmt, fs, io, result, str};
|
||||||
|
|
||||||
// ========================================
|
// ========================================
|
||||||
// Error
|
// Error
|
||||||
|
@ -171,6 +171,34 @@ impl<'de> Deserialize<'de> for ResPathBuf {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ========================================
|
||||||
|
// Validation
|
||||||
|
// ========================================
|
||||||
|
|
||||||
|
pub fn validate_is_file(path: &Path) -> Result<()> {
|
||||||
|
let metadata = fs::metadata(path)?;
|
||||||
|
if !metadata.is_file() {
|
||||||
|
// TODO(jrpotter): Use `IsADirectory` when stable.
|
||||||
|
Err(io::Error::new(
|
||||||
|
io::ErrorKind::Other,
|
||||||
|
format!("'{}' is not a file.", path.display()),
|
||||||
|
))?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn validate_is_dir(path: &Path) -> Result<()> {
|
||||||
|
let metadata = fs::metadata(path)?;
|
||||||
|
if !metadata.is_dir() {
|
||||||
|
// TODO(jrpotter): Use `NotADirectory` when stable.
|
||||||
|
Err(io::Error::new(
|
||||||
|
io::ErrorKind::Other,
|
||||||
|
format!("'{}' is not a directory.", path.display()),
|
||||||
|
))?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
// ========================================
|
// ========================================
|
||||||
// Resolution
|
// Resolution
|
||||||
// ========================================
|
// ========================================
|
||||||
|
|
Loading…
Reference in New Issue