feat(path): implement path resolution logic
This commit is contained in:
		
							parent
							
								
									65fd76c5b9
								
							
						
					
					
						commit
						279752ba59
					
				
							
								
								
									
										11
									
								
								espanso-path/Cargo.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								espanso-path/Cargo.toml
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,11 @@
 | 
			
		|||
[package]
 | 
			
		||||
name = "espanso-path"
 | 
			
		||||
version = "0.1.0"
 | 
			
		||||
authors = ["Federico Terzi <federico-terzi@users.noreply.github.com>"]
 | 
			
		||||
edition = "2018"
 | 
			
		||||
 | 
			
		||||
[dependencies]
 | 
			
		||||
log = "0.4.14"
 | 
			
		||||
anyhow = "1.0.38"
 | 
			
		||||
thiserror = "1.0.23"
 | 
			
		||||
dirs = "3.0.1"
 | 
			
		||||
							
								
								
									
										282
									
								
								espanso-path/src/lib.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										282
									
								
								espanso-path/src/lib.rs
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,282 @@
 | 
			
		|||
/*
 | 
			
		||||
 * This file is part of espanso.
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2019-2021 Federico Terzi
 | 
			
		||||
 *
 | 
			
		||||
 * espanso is free software: you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU General Public License as published by
 | 
			
		||||
 * the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
 * (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * espanso is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with espanso.  If not, see <https://www.gnu.org/licenses/>.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
use log::{debug, info};
 | 
			
		||||
use std::{
 | 
			
		||||
  fs::create_dir_all,
 | 
			
		||||
  path::{Path, PathBuf},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone)]
 | 
			
		||||
pub struct Paths {
 | 
			
		||||
  pub config: PathBuf,
 | 
			
		||||
  pub runtime: PathBuf,
 | 
			
		||||
  pub packages: PathBuf,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn resolve_paths() -> Paths {
 | 
			
		||||
  let config_dir = if let Some(config_dir) = get_config_dir() {
 | 
			
		||||
    config_dir
 | 
			
		||||
  } else {
 | 
			
		||||
    // Create the config directory if not already present
 | 
			
		||||
    let config_dir = get_default_config_path();
 | 
			
		||||
    info!("creating config directory in {:?}", config_dir);
 | 
			
		||||
    create_dir_all(&config_dir).expect("unable to create config directory");
 | 
			
		||||
    config_dir
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  let runtime_dir = if let Some(runtime_dir) = get_runtime_dir() {
 | 
			
		||||
    runtime_dir
 | 
			
		||||
  } else {
 | 
			
		||||
    // Create the runtime directory if not already present
 | 
			
		||||
    let runtime_dir = if !is_portable_mode() {
 | 
			
		||||
      get_default_runtime_path()
 | 
			
		||||
    } else {
 | 
			
		||||
      get_portable_runtime_path().expect("unable to obtain runtime directory path")
 | 
			
		||||
    };
 | 
			
		||||
    info!("creating runtime directory in {:?}", runtime_dir);
 | 
			
		||||
    create_dir_all(&runtime_dir).expect("unable to create runtime directory");
 | 
			
		||||
    runtime_dir
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  let packages_dir = if let Some(package_dir) = get_packages_dir(&config_dir, &runtime_dir) {
 | 
			
		||||
    package_dir
 | 
			
		||||
  } else {
 | 
			
		||||
    // Create the packages directory if not already present
 | 
			
		||||
    let packages_dir = get_default_packages_path(&config_dir);
 | 
			
		||||
    info!("creating packages directory in {:?}", packages_dir);
 | 
			
		||||
    create_dir_all(&packages_dir).expect("unable to create packages directory");
 | 
			
		||||
    packages_dir
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  Paths {
 | 
			
		||||
    config: config_dir,
 | 
			
		||||
    runtime: runtime_dir, 
 | 
			
		||||
    packages: packages_dir, 
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn get_config_dir() -> Option<PathBuf> {
 | 
			
		||||
  if let Some(portable_dir) = get_portable_config_dir() {
 | 
			
		||||
    // Portable mode
 | 
			
		||||
    debug!("detected portable config directory in {:?}", portable_dir);
 | 
			
		||||
    Some(portable_dir)
 | 
			
		||||
  } else if let Some(config_dir) = get_home_espanso_dir() {
 | 
			
		||||
    // $HOME/.espanso
 | 
			
		||||
    debug!("detected config directory in $HOME/.espanso");
 | 
			
		||||
    Some(config_dir)
 | 
			
		||||
  } else if let Some(config_dir) = get_home_config_espanso_dir() {
 | 
			
		||||
    // $HOME/.config/espanso
 | 
			
		||||
    debug!("detected config directory in $HOME/.config/espanso");
 | 
			
		||||
    Some(config_dir)
 | 
			
		||||
  } else if let Some(legacy_mac_dir) = get_legacy_mac_dir() {
 | 
			
		||||
    // Legacy macOS location in ~/Library/Preferences/espanso
 | 
			
		||||
    debug!(
 | 
			
		||||
      "detected legacy config directory location at {:?}",
 | 
			
		||||
      legacy_mac_dir
 | 
			
		||||
    );
 | 
			
		||||
    Some(legacy_mac_dir)
 | 
			
		||||
  } else if let Some(config_dir) = get_default_config_dir() {
 | 
			
		||||
    debug!("detected default config directory at {:?}", config_dir);
 | 
			
		||||
    Some(config_dir)
 | 
			
		||||
  } else {
 | 
			
		||||
    None
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn get_portable_config_dir() -> Option<PathBuf> {
 | 
			
		||||
  let espanso_exe_path = std::env::current_exe().expect("unable to obtain executable path");
 | 
			
		||||
  let exe_dir = espanso_exe_path.parent();
 | 
			
		||||
  if let Some(parent) = exe_dir {
 | 
			
		||||
    let config_dir = parent.join(".espanso");
 | 
			
		||||
    if config_dir.is_dir() {
 | 
			
		||||
      return Some(config_dir);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  None
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn get_home_espanso_dir() -> Option<PathBuf> {
 | 
			
		||||
  if let Some(home_dir) = dirs::home_dir() {
 | 
			
		||||
    let config_espanso_dir = home_dir.join(".espanso");
 | 
			
		||||
    if config_espanso_dir.is_dir() {
 | 
			
		||||
      return Some(config_espanso_dir);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  None
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn get_home_config_espanso_dir() -> Option<PathBuf> {
 | 
			
		||||
  if let Some(home_dir) = dirs::home_dir() {
 | 
			
		||||
    let home_espanso_dir = home_dir.join(".config").join("espanso");
 | 
			
		||||
    if home_espanso_dir.is_dir() {
 | 
			
		||||
      return Some(home_espanso_dir);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  None
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn get_default_config_dir() -> Option<PathBuf> {
 | 
			
		||||
  let config_path = get_default_config_path();
 | 
			
		||||
  if config_path.is_dir() {
 | 
			
		||||
    return Some(config_path);
 | 
			
		||||
  }
 | 
			
		||||
  None
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn get_default_config_path() -> PathBuf {
 | 
			
		||||
  let config_dir = dirs::config_dir().expect("unable to obtain dirs::config_dir()");
 | 
			
		||||
  config_dir.join("espanso")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Due to the original behavior of the dirs crate, espanso placed the config
 | 
			
		||||
// directory in the Preferences folder on macOS, but this is not an optimal
 | 
			
		||||
// approach.
 | 
			
		||||
// For more context, see: https://github.com/federico-terzi/espanso/issues/611
 | 
			
		||||
fn get_legacy_mac_dir() -> Option<PathBuf> {
 | 
			
		||||
  if cfg!(target_os = "macos") {
 | 
			
		||||
    if let Some(preferences_dir) = dirs::preference_dir() {
 | 
			
		||||
      let espanso_dir = preferences_dir.join("espanso");
 | 
			
		||||
      if espanso_dir.is_dir() {
 | 
			
		||||
        return Some(espanso_dir);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  None
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn get_runtime_dir() -> Option<PathBuf> {
 | 
			
		||||
  if let Some(runtime_dir) = get_portable_runtime_dir() {
 | 
			
		||||
    debug!("detected portable runtime dir: {:?}", runtime_dir);
 | 
			
		||||
    Some(runtime_dir)
 | 
			
		||||
  } else if let Some(legacy_dir) = get_legacy_runtime_dir() {
 | 
			
		||||
    debug!("detected legacy runtime dir: {:?}", legacy_dir);
 | 
			
		||||
    Some(legacy_dir)
 | 
			
		||||
  } else if let Some(default_dir) = get_default_runtime_dir() {
 | 
			
		||||
    debug!("detected default runtime dir: {:?}", default_dir);
 | 
			
		||||
    Some(default_dir)
 | 
			
		||||
  } else {
 | 
			
		||||
    None
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn get_portable_runtime_dir() -> Option<PathBuf> {
 | 
			
		||||
  if let Some(runtime_dir) = get_portable_runtime_path() {
 | 
			
		||||
    if runtime_dir.is_dir() {
 | 
			
		||||
      return Some(runtime_dir);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  None
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn get_portable_runtime_path() -> Option<PathBuf> {
 | 
			
		||||
  let espanso_exe_path = std::env::current_exe().expect("unable to obtain executable path");
 | 
			
		||||
  let exe_dir = espanso_exe_path.parent();
 | 
			
		||||
  if let Some(parent) = exe_dir {
 | 
			
		||||
    let config_dir = parent.join(".espanso-runtime");
 | 
			
		||||
    return Some(config_dir);
 | 
			
		||||
  }
 | 
			
		||||
  None
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn get_legacy_runtime_dir() -> Option<PathBuf> {
 | 
			
		||||
  let data_dir = dirs::data_local_dir().expect("unable to obtain dirs::data_local_dir()");
 | 
			
		||||
  let espanso_dir = data_dir.join("espanso");
 | 
			
		||||
  if espanso_dir.is_dir() {
 | 
			
		||||
    Some(espanso_dir)
 | 
			
		||||
  } else {
 | 
			
		||||
    None
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn get_default_runtime_dir() -> Option<PathBuf> {
 | 
			
		||||
  let default_dir = get_default_runtime_path();
 | 
			
		||||
  if default_dir.is_dir() {
 | 
			
		||||
    Some(default_dir)
 | 
			
		||||
  } else {
 | 
			
		||||
    None
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn get_default_runtime_path() -> PathBuf {
 | 
			
		||||
  let runtime_dir = if cfg!(target_os = "linux") {
 | 
			
		||||
    dirs::runtime_dir().expect("unable to obtain dirs::runtime_dir()")
 | 
			
		||||
  } else {
 | 
			
		||||
    dirs::cache_dir().expect("unable to obtain dirs::cache_dir()")
 | 
			
		||||
  };
 | 
			
		||||
  runtime_dir.join("espanso")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn get_packages_dir(config_dir: &Path, legacy_data_dir: &Path) -> Option<PathBuf> {
 | 
			
		||||
  if let Some(packages_dir) = get_legacy_embedded_packages_dir(config_dir) {
 | 
			
		||||
    debug!("detected legacy packages dir: {:?}", packages_dir);
 | 
			
		||||
    Some(packages_dir)
 | 
			
		||||
  } else if let Some(packages_dir) = get_legacy_packages_dir(legacy_data_dir) {
 | 
			
		||||
    debug!("detected legacy packages dir: {:?}", packages_dir);
 | 
			
		||||
    Some(packages_dir)
 | 
			
		||||
  } else if let Some(packages_dir) = get_default_packages_dir(config_dir) {
 | 
			
		||||
    debug!("detected default packages dir: {:?}", packages_dir);
 | 
			
		||||
    Some(packages_dir)
 | 
			
		||||
  } else {
 | 
			
		||||
    None
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn get_legacy_packages_dir(legacy_data_dir: &Path) -> Option<PathBuf> {
 | 
			
		||||
  let legacy_dir = legacy_data_dir.join("packages");
 | 
			
		||||
  if legacy_dir.is_dir() {
 | 
			
		||||
    Some(legacy_dir)
 | 
			
		||||
  } else {
 | 
			
		||||
    None
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn get_legacy_embedded_packages_dir(config_dir: &Path) -> Option<PathBuf> {
 | 
			
		||||
  let legacy_dir = config_dir.join("packages");
 | 
			
		||||
  if legacy_dir.is_dir() {
 | 
			
		||||
    Some(legacy_dir)
 | 
			
		||||
  } else {
 | 
			
		||||
    None
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn get_default_packages_dir(config_dir: &Path) -> Option<PathBuf> {
 | 
			
		||||
  let packages_dir = get_default_packages_path(config_dir);
 | 
			
		||||
  if packages_dir.is_dir() {
 | 
			
		||||
    Some(packages_dir)
 | 
			
		||||
  } else {
 | 
			
		||||
    None
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn get_default_packages_path(config_dir: &Path) -> PathBuf {
 | 
			
		||||
  config_dir.join("match").join("packages")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn is_portable_mode() -> bool {
 | 
			
		||||
  let espanso_exe_path = std::env::current_exe().expect("unable to obtain executable path");
 | 
			
		||||
  let exe_dir = espanso_exe_path.parent();
 | 
			
		||||
  if let Some(parent) = exe_dir {
 | 
			
		||||
    let config_dir = parent.join(".espanso");
 | 
			
		||||
    if config_dir.is_dir() {
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  false
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user