[yabridgectl] Allow setting up VST3 plugins

This is still missing checks for removing leftover files, symlinks for
resources and presets, and a way to differentiate between plugins with
the same name from different prefixes.
This commit is contained in:
Robbert van der Helm
2020-12-24 00:04:05 +01:00
parent bc9801c932
commit 55957ca798
4 changed files with 242 additions and 103 deletions
+140 -54
View File
@@ -23,7 +23,7 @@ use std::path::{Path, PathBuf};
use crate::config::{Config, InstallationMethod, YabridgeFiles}; use crate::config::{Config, InstallationMethod, YabridgeFiles};
use crate::files; use crate::files;
use crate::files::NativeSoFile; use crate::files::NativeFile;
use crate::utils; use crate::utils;
use crate::utils::{verify_path_setup, verify_wine_setup}; use crate::utils::{verify_path_setup, verify_wine_setup};
@@ -34,10 +34,9 @@ pub fn add_directory(config: &mut Config, path: PathBuf) -> Result<()> {
} }
/// Remove a direcotry to the plugin locations. The path is assumed to be part of /// Remove a direcotry to the plugin locations. The path is assumed to be part of
/// `config.plugin_dirs`, otherwise this si silently ignored. /// `config.plugin_dirs`, otherwise this is silently ignored.
pub fn remove_directory(config: &mut Config, path: &Path) -> Result<()> { pub fn remove_directory(config: &mut Config, path: &Path) -> Result<()> {
// We've already verified that this path is in `config.plugin_dirs` // We've already verified that this path is in `config.plugin_dirs`
// XXS: Would it be a good idea to warn about leftover .so files?
config.plugin_dirs.remove(path); config.plugin_dirs.remove(path);
config.write()?; config.write()?;
@@ -121,8 +120,9 @@ pub fn show_status(config: &Config) -> Result<()> {
for (plugin, status) in search_results.installation_status() { for (plugin, status) in search_results.installation_status() {
let status_str = match status { let status_str = match status {
Some(NativeSoFile::Regular(_)) => "copy".green(), Some(NativeFile::Regular(_)) => "copy".green(),
Some(NativeSoFile::Symlink(_)) => "symlink".green(), Some(NativeFile::Symlink(_)) => "symlink".green(),
Some(NativeFile::Directory(_)) => "invalid".red(),
None => "not installed".red(), None => "not installed".red(),
}; };
@@ -168,20 +168,47 @@ pub struct SyncOptions {
pub fn do_sync(config: &mut Config, options: &SyncOptions) -> Result<()> { pub fn do_sync(config: &mut Config, options: &SyncOptions) -> Result<()> {
let files: YabridgeFiles = config.files()?; let files: YabridgeFiles = config.files()?;
let libyabridge_vst2_hash = utils::hash_file(&files.libyabridge_vst2)?; let libyabridge_vst2_hash = utils::hash_file(&files.libyabridge_vst2)?;
println!("Using '{}'\n", files.libyabridge_vst2.display()); let libyabridge_vst3_hash = match &files.libyabridge_vst3 {
Some(path) => Some(utils::hash_file(path)?),
None => None,
};
if let Some(libyabridge_vst3_path) = &files.libyabridge_vst3 {
println!("Setting up VST2 and VST3 plugins using:");
println!("- {}", files.libyabridge_vst2.display());
println!("- {}\n", libyabridge_vst3_path.display());
} else {
println!("Setting up VST2 plugins using:");
println!("- {}\n", files.libyabridge_vst2.display());
}
let results = config let results = config
.search_directories() .search_directories()
.context("Failure while searching for plugins")?; .context("Failure while searching for plugins")?;
// Keep track of some global statistics // Keep track of some global statistics
// The number of plugins we set up yabridge for
let mut num_installed = 0; let mut num_installed = 0;
// The number of plugins we create a (new) copy of `libyabridge-{vst2,vst3}.so` for
let mut num_new = 0; let mut num_new = 0;
// The files we skipped during the scan because they turned out to not be plugins
let mut skipped_dll_files: Vec<PathBuf> = Vec::new(); let mut skipped_dll_files: Vec<PathBuf> = Vec::new();
let mut orphan_so_files: Vec<NativeSoFile> = Vec::new(); // `.so` files we found during scanning that didn't have a corresponding copy or symlink of
// `libyabridge-vst2.so`
let mut orphan_vst2_so_files: Vec<NativeFile> = Vec::new();
// All the VST3 modules we have set up yabridge for. We need this to detect leftover VST3
// modules in `~/.vst3/yabridge`.
let mut yabridge_vst3_bundles: Vec<PathBuf> = Vec::new();
for (path, search_results) in results { for (path, search_results) in results {
num_installed += search_results.vst2_files.len(); num_installed += search_results.vst2_files.len();
orphan_so_files.extend(search_results.orphans().into_iter().cloned()); num_installed += search_results.vst3_modules.len();
orphan_vst2_so_files.extend(search_results.vst2_orphans().into_iter().cloned());
yabridge_vst3_bundles.extend(
search_results
.vst3_modules
.iter()
.map(|module| module.yabridge_bundle_home()),
);
skipped_dll_files.extend(search_results.skipped_files); skipped_dll_files.extend(search_results.skipped_files);
if options.verbose { if options.verbose {
@@ -190,56 +217,58 @@ pub fn do_sync(config: &mut Config, options: &SyncOptions) -> Result<()> {
for plugin in search_results.vst2_files { for plugin in search_results.vst2_files {
let target_path = plugin.with_extension("so"); let target_path = plugin.with_extension("so");
// We'll only recreate existing files when updating yabridge, when switching between the
// symlink and copy installation methods, or when the `force` option is set. If the
// target file already exists and does not require updating, we'll just skip the file
// since some DAWs will otherwise unnecessarily reindex the file.
// We check `std::fs::symlink_metadata` instead of `Path::exists()` because the latter
// reports false for broken symlinks.
if let Ok(metadata) = fs::symlink_metadata(&target_path) {
match (options.force, &config.method) {
(false, InstallationMethod::Copy) => {
// If the target file is already a real file (not a symlink) and its hash is
// the same as the `libyabridge-vst2.so` file we're trying to copy there,
// then we don't have to do anything
if metadata.file_type().is_file()
&& utils::hash_file(&target_path)? == libyabridge_vst2_hash
{
continue;
}
}
(false, InstallationMethod::Symlink) => {
// If the target file is already a symlink to `libyabridge-vst2.so`, then we
// can skip this file
if metadata.file_type().is_symlink()
&& target_path.read_link()? == files.libyabridge_vst2
{
continue;
}
}
// With the force option we always want to recreate existing .so files
(true, _) => (),
}
utils::remove_file(&target_path)?;
};
// Since we skip some files, we'll also keep track of how many new file we've actually // Since we skip some files, we'll also keep track of how many new file we've actually
// set up // set up
if install_file(
options.force,
config.method,
&files.libyabridge_vst2,
libyabridge_vst2_hash,
&target_path,
)? {
num_new += 1; num_new += 1;
match config.method {
InstallationMethod::Copy => {
utils::copy(&files.libyabridge_vst2, &target_path)?;
}
InstallationMethod::Symlink => {
utils::symlink(&files.libyabridge_vst2, &target_path)?;
}
} }
if options.verbose { if options.verbose {
println!(" {}", plugin.display()); println!(" {}", plugin.display());
} }
} }
if let Some(libyabridge_vst3_hash) = libyabridge_vst3_hash {
for module in search_results.vst3_modules {
let native_module_path = module.yabridge_native_module_path();
// For VST3 plugins we'll first have to create the bundle structure
utils::create_dir_all(native_module_path.parent().unwrap())?;
// We'll then symlink the Windows VST3 module to that bundle to create a merged
// bundle: https://steinbergmedia.github.io/vst3_doc/vstinterfaces/vst3loc.html#mergedbundles
let windows_module_path = module.yabridge_windows_module_path();
utils::create_dir_all(windows_module_path.parent().unwrap())?;
install_file(
true,
InstallationMethod::Symlink,
&module.original_module_path(),
0,
&windows_module_path,
)?;
// TODO: Symlink resources and presets
if install_file(
options.force,
config.method,
files.libyabridge_vst3.as_ref().unwrap(),
libyabridge_vst3_hash,
&native_module_path,
)? {
num_new += 1;
}
if options.verbose {
println!(" {}", module.original_path().display());
}
}
}
if options.verbose { if options.verbose {
println!(); println!();
} }
@@ -255,19 +284,24 @@ pub fn do_sync(config: &mut Config, options: &SyncOptions) -> Result<()> {
println!(); println!();
} }
// Always warn about leftover files sicne those might cause warnings or errors when a VST host // TODO: Remove leftover files for VST3 plugins
// Always warn about leftover files since those might cause warnings or errors when a VST host
// tries to load them // tries to load them
if !orphan_so_files.is_empty() { if !orphan_vst2_so_files.is_empty() {
if options.prune { if options.prune {
println!("Removing {} leftover .so files:", orphan_so_files.len()); println!(
"Removing {} leftover .so files:",
orphan_vst2_so_files.len()
);
} else { } else {
println!( println!(
"Found {} leftover .so files, rerun with the '--prune' option to remove them:", "Found {} leftover .so files, rerun with the '--prune' option to remove them:",
orphan_so_files.len() orphan_vst2_so_files.len()
); );
} }
for file in orphan_so_files { for file in orphan_vst2_so_files {
let path = file.path(); let path = file.path();
println!("- {}", path.display()); println!("- {}", path.display());
@@ -291,6 +325,8 @@ pub fn do_sync(config: &mut Config, options: &SyncOptions) -> Result<()> {
return Ok(()); return Ok(());
} }
// The path setup is to make sure that the `libyabridge-{vst2,vst3}.so` copies can find
// `yabridge-host.exe`
if config.method == InstallationMethod::Copy { if config.method == InstallationMethod::Copy {
verify_path_setup(config)?; verify_path_setup(config)?;
} }
@@ -300,3 +336,53 @@ pub fn do_sync(config: &mut Config, options: &SyncOptions) -> Result<()> {
Ok(()) Ok(())
} }
/// Create a copy or symlink of `from` to `to`. Depending on `force`, we might not actually create a
/// new copy or symlink if `to` matches `from_hash`.
fn install_file(
force: bool,
method: InstallationMethod,
from: &Path,
from_hash: i64,
to: &Path,
) -> Result<bool> {
// We'll only recreate existing files when updating yabridge, when switching between the symlink
// and copy installation methods, or when the `force` option is set. If the target file already
// exists and does not require updating, we'll just skip the file since some DAWs will otherwise
// unnecessarily reindex the file. We check `std::fs::symlink_metadata` instead of
// `Path::exists()` because the latter reports false for broken symlinks.
if let Ok(metadata) = fs::symlink_metadata(&to) {
match (force, &method) {
(false, InstallationMethod::Copy) => {
// If the target file is already a real file (not a symlink) and its hash is
// the same as the `libyabridge-vst2.so` file we're trying to copy there,
// then we don't have to do anything
if metadata.file_type().is_file() && utils::hash_file(to)? == from_hash {
return Ok(false);
}
}
(false, InstallationMethod::Symlink) => {
// If the target file is already a symlink to `libyabridge-vst2.so`, then we
// can skip this file
if metadata.file_type().is_symlink() && to.read_link()? == from {
return Ok(false);
}
}
// With the force option we always want to recreate existing .so files
(true, _) => (),
}
utils::remove_file(&to)?;
};
match method {
InstallationMethod::Copy => {
utils::copy(from, to)?;
}
InstallationMethod::Symlink => {
utils::symlink(from, to)?;
}
}
Ok(true)
}
+1 -1
View File
@@ -73,7 +73,7 @@ pub struct Config {
} }
/// Specifies how yabridge will be set up for the found plugins. /// Specifies how yabridge will be set up for the found plugins.
#[derive(Deserialize, Serialize, Debug, PartialEq, Eq)] #[derive(Deserialize, Serialize, Debug, PartialEq, Eq, Clone, Copy)]
#[serde(rename_all = "snake_case")] #[serde(rename_all = "snake_case")]
pub enum InstallationMethod { pub enum InstallationMethod {
/// Create a copy of `libyabridge-{vst2,vst3}.so` for every Windows VST2 plugin `.dll` file or /// Create a copy of `libyabridge-{vst2,vst3}.so` for every Windows VST2 plugin `.dll` file or
+74 -47
View File
@@ -26,6 +26,7 @@ use std::process::Command;
use walkdir::WalkDir; use walkdir::WalkDir;
use crate::config::yabridge_vst3_home; use crate::config::yabridge_vst3_home;
use crate::utils::get_file_type;
/// Stores the results from searching through a directory. We'll search for Windows VST2 plugin /// Stores the results from searching through a directory. We'll search for Windows VST2 plugin
/// `.dll` files, Windows VST3 plugin modules, and native Linux `.so` files inside of a directory. /// `.dll` files, Windows VST3 plugin modules, and native Linux `.so` files inside of a directory.
@@ -44,7 +45,7 @@ pub struct SearchResults {
/// Absolute paths to any `.so` files inside of the directory, and whether they're a symlink or /// Absolute paths to any `.so` files inside of the directory, and whether they're a symlink or
/// a regular file. /// a regular file.
pub so_files: Vec<NativeSoFile>, pub so_files: Vec<NativeFile>,
} }
/// The results of the first step of the search process. We'll first index all possibly relevant /// The results of the first step of the search process. We'll first index all possibly relevant
@@ -58,22 +59,24 @@ pub struct SearchIndex {
pub vst3_files: Vec<PathBuf>, pub vst3_files: Vec<PathBuf>,
/// Absolute paths to any `.so` files inside of the directory, and whether they're a symlink or /// Absolute paths to any `.so` files inside of the directory, and whether they're a symlink or
/// a regular file. /// a regular file.
pub so_files: Vec<NativeSoFile>, pub so_files: Vec<NativeFile>,
} }
/// Native `.so` files we found during a search. /// Native `.so` files and VST3 bundle directories we found during a search.
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub enum NativeSoFile { pub enum NativeFile {
Symlink(PathBuf), Symlink(PathBuf),
Regular(PathBuf), Regular(PathBuf),
Directory(PathBuf),
} }
impl NativeSoFile { impl NativeFile {
/// Return the path of a found `.so` file. /// Return the path of a found `.so` file.
pub fn path(&self) -> &Path { pub fn path(&self) -> &Path {
match &self { match &self {
NativeSoFile::Symlink(path) => path, NativeFile::Symlink(path) | NativeFile::Regular(path) | NativeFile::Directory(path) => {
NativeSoFile::Regular(path) => path, path
}
} }
} }
} }
@@ -101,19 +104,16 @@ impl Vst3Module {
} }
} }
/// Get the path to the `libyabridge.so` file in `~/.vst3` corresponding to the bridged version /// Get the path to the Windows VST3 plugin. This can be either a file or a directory depending
/// of this module. /// on the type of moudle.
pub fn libyabridge_location(&self) -> PathBuf { pub fn original_path(&self) -> &Path {
let mut path = yabridge_vst3_home(); match &self {
path.push(self.module_name()); Vst3Module::Legacy(path, _) | Vst3Module::Bundle(path, _) => path,
path.push("Contents"); }
path.push("x86_64-linux");
path.push(self.native_module_name());
path
} }
/// Get the name of the module as a string. Should be in the format `Plugin Name.vst3`. /// Get the name of the module as a string. Should be in the format `Plugin Name.vst3`.
pub fn module_name(&self) -> &str { pub fn original_module_name(&self) -> &str {
match &self { match &self {
Vst3Module::Legacy(path, _) | Vst3Module::Bundle(path, _) => path Vst3Module::Legacy(path, _) | Vst3Module::Bundle(path, _) => path
.file_name() .file_name()
@@ -123,9 +123,32 @@ impl Vst3Module {
} }
} }
/// `module_name()` but with a `.so` file extension isntead of `.vst3`. /// Get the path to the actual `.vst3` module file.
pub fn native_module_name(&self) -> String { pub fn original_module_path(&self) -> PathBuf {
match &self { match &self {
Vst3Module::Legacy(path, _) => path.to_owned(),
Vst3Module::Bundle(bundle_home, architecture) => {
let mut path = bundle_home.join("Contents");
path.push(architecture.vst_arch());
path.push(self.original_module_name());
path
}
}
}
/// Get the path to the bundle in `~/.vst3` corresponding to the bridged version of this module.
///
/// FIXME: How do we solve naming clashes from the same VST3 plugin being installed to multiple
/// Wine prefixes?
pub fn yabridge_bundle_home(&self) -> PathBuf {
yabridge_vst3_home().join(self.original_module_name())
}
/// Get the path to the `libyabridge.so` file in `~/.vst3` corresponding to the bridged version
/// of this module.
pub fn yabridge_native_module_path(&self) -> PathBuf {
let native_module_name = match &self {
Vst3Module::Legacy(path, _) | Vst3Module::Bundle(path, _) => path Vst3Module::Legacy(path, _) | Vst3Module::Bundle(path, _) => path
.with_extension("so") .with_extension("so")
.file_name() .file_name()
@@ -133,15 +156,23 @@ impl Vst3Module {
.to_str() .to_str()
.expect("VST3 module name contains invalid UTF-8") .expect("VST3 module name contains invalid UTF-8")
.to_owned(), .to_owned(),
} };
let mut path = self.yabridge_bundle_home();
path.push("Contents");
path.push("x86_64-linux");
path.push(native_module_name);
path
} }
/// Get the path to the module. This can be either a file or a directory depending on the type /// Get the path to where we'll symlink `original_module_path`. This is part of the merged VST3
/// of moudle. /// bundle in `~/.vst3/yabridge`.
pub fn path(&self) -> &Path { pub fn yabridge_windows_module_path(&self) -> PathBuf {
match &self { let mut path = self.yabridge_bundle_home();
Vst3Module::Legacy(path, _) | Vst3Module::Bundle(path, _) => path, path.push("Contents");
} path.push(self.architecture().vst_arch());
path.push(self.original_module_name());
path
} }
} }
@@ -167,21 +198,21 @@ impl SearchResults {
/// For every found VST2 plugin and VST3 module, find the associated copy or symlink of /// For every found VST2 plugin and VST3 module, find the associated copy or symlink of
/// `libyabridge-{vst2,vst3}.so`. The returned hashmap will contain a `None` value for plugins /// `libyabridge-{vst2,vst3}.so`. The returned hashmap will contain a `None` value for plugins
/// that have not yet been set up. /// that have not yet been set up.
pub fn installation_status(&self) -> BTreeMap<&Path, Option<NativeSoFile>> { pub fn installation_status(&self) -> BTreeMap<PathBuf, Option<NativeFile>> {
let so_files: HashMap<&Path, &NativeSoFile> = self let so_files: HashMap<&Path, &NativeFile> = self
.so_files .so_files
.iter() .iter()
.map(|file| (file.path(), file)) .map(|file| (file.path(), file))
.collect(); .collect();
// Do this for the VST2 plugins // Do this for the VST2 plugins
let mut installation_status: BTreeMap<&Path, Option<NativeSoFile>> = self let mut installation_status: BTreeMap<PathBuf, Option<NativeFile>> = self
.vst2_files .vst2_files
.iter() .iter()
.map( .map(
|path| match so_files.get(path.with_extension("so").as_path()) { |path| match so_files.get(path.with_extension("so").as_path()) {
Some(&file_type) => (path.as_path(), Some(file_type.clone())), Some(&file_type) => (path.clone(), Some(file_type.clone())),
None => (path.as_path(), None), None => (path.clone(), None),
}, },
) )
.collect(); .collect();
@@ -189,29 +220,25 @@ impl SearchResults {
// And for VST3 modules. We have not stored the paths to the corresponding `.so` files yet // And for VST3 modules. We have not stored the paths to the corresponding `.so` files yet
// because they are not in any of the directories we're indexing. // because they are not in any of the directories we're indexing.
installation_status.extend(self.vst3_modules.iter().map(|module| { installation_status.extend(self.vst3_modules.iter().map(|module| {
let install_path = module.libyabridge_location(); let module_path = module.yabridge_native_module_path();
match install_path.metadata() { let install_type = get_file_type(&module_path);
Ok(metadata) if metadata.file_type().is_symlink() => { (module_path, install_type)
(module.path(), Some(NativeSoFile::Symlink(install_path)))
}
Ok(_) => (module.path(), Some(NativeSoFile::Regular(install_path))),
Err(_) => (module.path(), None),
}
})); }));
installation_status installation_status
} }
/// Find all `.so` files in the search results that do not belong to a VST2 plugin `.dll` file. /// Find all `.so` files in the search results that do not belong to a VST2 plugin `.dll` file.
/// /// We cannot yet do the same thing for VST3 plguins because they will all be installed in
/// TODO: Also do something similar for VST3 plugins /// `~/.vst3`.
pub fn orphans(&self) -> Vec<&NativeSoFile> { pub fn vst2_orphans(&self) -> Vec<&NativeFile> {
// We need to store these in a map so we can easily entries with corresponding `.dll` files // We need to store these in a map so we can easily entries with corresponding `.dll` files
let mut orphans: HashMap<&Path, &NativeSoFile> = self let mut orphans: HashMap<&Path, &NativeFile> = self
.so_files .so_files
.iter() .iter()
.map(|file| (file.path(), file)) .map(|file_type| (file_type.path(), file_type))
.collect(); .collect();
for vst2_path in &self.vst2_files { for vst2_path in &self.vst2_files {
orphans.remove(vst2_path.with_extension("so").as_path()); orphans.remove(vst2_path.with_extension("so").as_path());
} }
@@ -225,7 +252,7 @@ impl SearchResults {
pub fn index(directory: &Path) -> SearchIndex { pub fn index(directory: &Path) -> SearchIndex {
let mut dll_files: Vec<PathBuf> = Vec::new(); let mut dll_files: Vec<PathBuf> = Vec::new();
let mut vst3_files: Vec<PathBuf> = Vec::new(); let mut vst3_files: Vec<PathBuf> = Vec::new();
let mut so_files: Vec<NativeSoFile> = Vec::new(); let mut so_files: Vec<NativeFile> = Vec::new();
// XXX: We're silently skipping directories and files we don't have permission to read. This // XXX: We're silently skipping directories and files we don't have permission to read. This
// sounds like the expected behavior, but I"m not entirely sure. // sounds like the expected behavior, but I"m not entirely sure.
for (file_idx, entry) in WalkDir::new(directory) for (file_idx, entry) in WalkDir::new(directory)
@@ -251,9 +278,9 @@ pub fn index(directory: &Path) -> SearchIndex {
Some("vst3") => vst3_files.push(entry.into_path()), Some("vst3") => vst3_files.push(entry.into_path()),
Some("so") => { Some("so") => {
if entry.path_is_symlink() { if entry.path_is_symlink() {
so_files.push(NativeSoFile::Symlink(entry.into_path())); so_files.push(NativeFile::Symlink(entry.into_path()));
} else { } else {
so_files.push(NativeSoFile::Regular(entry.into_path())); so_files.push(NativeFile::Regular(entry.into_path()));
} }
} }
_ => (), _ => (),
+26
View File
@@ -30,6 +30,7 @@ use std::process::{Command, Stdio};
use textwrap::Wrapper; use textwrap::Wrapper;
use crate::config::{self, Config, KnownConfig, YABRIDGE_HOST_EXE_NAME}; use crate::config::{self, Config, KnownConfig, YABRIDGE_HOST_EXE_NAME};
use crate::files::NativeFile;
/// (Part of) the expected output when running `yabridge-host.exe`. Used to verify that everything's /// (Part of) the expected output when running `yabridge-host.exe`. Used to verify that everything's
/// working correctly. We'll only match this prefix so we can modify the exact output at a later /// working correctly. We'll only match this prefix so we can modify the exact output at a later
@@ -47,6 +48,17 @@ pub fn copy<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> Result<u64> {
}) })
} }
/// Wrapper around [`std::fs::create_dir_all()`](std::fs::create_dir_all) with a human readable
/// error message.
pub fn create_dir_all<P: AsRef<Path>>(path: P) -> Result<()> {
fs::create_dir_all(&path).with_context(|| {
format!(
"Error creating directories for '{}'",
path.as_ref().display(),
)
})
}
/// Wrapper around [`std::fs::remove_file()`](std::fs::remove_file) with a human readable error /// Wrapper around [`std::fs::remove_file()`](std::fs::remove_file) with a human readable error
/// message. /// message.
pub fn remove_file<P: AsRef<Path>>(path: P) -> Result<()> { pub fn remove_file<P: AsRef<Path>>(path: P) -> Result<()> {
@@ -66,6 +78,20 @@ pub fn symlink<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> Result<()> {
}) })
} }
/// Get the type of a file, if it exists.
pub fn get_file_type(path: &Path) -> Option<NativeFile> {
match path.metadata() {
Ok(metadata) if metadata.file_type().is_symlink() => {
Some(NativeFile::Symlink(path.to_owned()))
}
Ok(metadata) if metadata.file_type().is_dir() => {
Some(NativeFile::Directory(path.to_owned()))
}
Ok(_) => Some(NativeFile::Regular(path.to_owned())),
Err(_) => None,
}
}
/// Hash the conetnts of a file as an `i64` using Rust's built in hasher. Collisions are not a big /// Hash the conetnts of a file as an `i64` using Rust's built in hasher. Collisions are not a big
/// issue in our situation so we can get away with this. /// issue in our situation so we can get away with this.
/// ///