From d810117fa64630429741849e6b51c79b7434f8ac Mon Sep 17 00:00:00 2001 From: Manuel Cillero Date: Sat, 16 Aug 2025 11:54:48 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=A7=91=E2=80=8D=F0=9F=92=BB=20Redefine=20?= =?UTF-8?q?funci=C3=B3n=20para=20directorios=20absolutos?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/config.rs | 27 +++++++---------------- src/util.rs | 53 ++++++++++++++++++++++++++++++--------------- tests/util.rs | 60 +++++++++++++++++++++++++++++++++++++++++++-------- 3 files changed, 95 insertions(+), 45 deletions(-) diff --git a/src/config.rs b/src/config.rs index 08067fe..27cf630 100644 --- a/src/config.rs +++ b/src/config.rs @@ -110,11 +110,13 @@ //! } //! ``` +use crate::util; + use config::builder::DefaultState; use config::{Config, ConfigBuilder, File}; use std::env; -use std::path::{Path, PathBuf}; +use std::path::PathBuf; use std::sync::LazyLock; // Nombre del directorio de configuración por defecto. @@ -125,25 +127,12 @@ const DEFAULT_RUN_MODE: &str = "default"; /// Valores originales cargados desde los archivos de configuración como pares `clave = valor`. pub static CONFIG_VALUES: LazyLock> = LazyLock::new(|| { - // Determina el directorio de configuración: - // - Usa CONFIG_DIR si está definido en el entorno (p.ej.: CONFIG_DIR=/etc/myapp ./myapp). - // - Si no, intenta DEFAULT_CONFIG_DIR dentro del proyecto (en CARGO_MANIFEST_DIR). - // - Si nada de esto aplica, entonces usa DEFAULT_CONFIG_DIR relativo al ejecutable. - let config_dir: PathBuf = if let Ok(env_dir) = env::var("CONFIG_DIR") { - env_dir.into() - } else if let Ok(manifest_dir) = env::var("CARGO_MANIFEST_DIR") { - let manifest_config = Path::new(&manifest_dir).join(DEFAULT_CONFIG_DIR); - if manifest_config.exists() { - manifest_config - } else { - DEFAULT_CONFIG_DIR.into() - } - } else { - DEFAULT_CONFIG_DIR.into() - }; + // CONFIG_DIR (si existe) o DEFAULT_CONFIG_DIR. Si no se puede resolver, se usa tal cual. + let dir = env::var_os("CONFIG_DIR").unwrap_or_else(|| DEFAULT_CONFIG_DIR.into()); + let config_dir = util::resolve_absolute_dir(&dir).unwrap_or_else(|_| PathBuf::from(&dir)); - // Determina el modo de ejecución según la variable de entorno PAGETOP_RUN_MODE. Por defecto usa - // DEFAULT_RUN_MODE si no está definida (p.ej.: PAGETOP_RUN_MODE=production ./myapp). + // Modo de ejecución según la variable de entorno PAGETOP_RUN_MODE. Si no está definida, se usa + // por defecto, DEFAULT_RUN_MODE (p.ej.: PAGETOP_RUN_MODE=production). let rm = env::var("PAGETOP_RUN_MODE").unwrap_or_else(|_| DEFAULT_RUN_MODE.into()); Config::builder() diff --git a/src/util.rs b/src/util.rs index b605125..21537c5 100644 --- a/src/util.rs +++ b/src/util.rs @@ -2,35 +2,44 @@ use crate::trace; +use std::env; use std::io; use std::path::{Path, PathBuf}; // FUNCIONES ÚTILES ******************************************************************************** -/// Devuelve la ruta absoluta a un directorio existente. +/// Resuelve y valida la ruta de un directorio existente, devolviendo una ruta absoluta. /// -/// * Si `relative_path` es una ruta absoluta, entonces se ignora `root_path`. -/// * Si la ruta final es relativa, se convierte en absoluta respecto al directorio actual. -/// * Devuelve error si la ruta no existe o no es un directorio. +/// - Si la ruta es relativa, se resuelve respecto al directorio del proyecto según la variable de +/// entorno `CARGO_MANIFEST_DIR` (si existe) o, en su defecto, respecto al directorio actual de +/// trabajo. +/// - Normaliza y valida la ruta final (resuelve `.`/`..` y enlaces simbólicos). +/// - Devuelve error si la ruta no existe o no es un directorio. /// -/// # Ejemplo +/// # Ejemplos /// /// ```rust,no_run /// use pagetop::prelude::*; /// -/// let root = "/home/user"; -/// let rel = "documents"; -/// println!("{:#?}", util::absolute_dir(root, rel)); +/// // Ruta relativa, se resuelve respecto a CARGO_MANIFEST_DIR o al directorio actual (`cwd`). +/// println!("{:#?}", util::resolve_absolute_dir("documents")); +/// +/// // Ruta absoluta, se normaliza y valida tal cual. +/// println!("{:#?}", util::resolve_absolute_dir("/var/www")); /// ``` -pub fn absolute_dir(root_path: P, relative_path: Q) -> io::Result -where - P: AsRef, - Q: AsRef, -{ - // Une ambas rutas: - // - Si `relative_path` es absoluta, el `join` la devuelve tal cual, descartando `root_path`. - // - Si el resultado es aún relativo, lo será respecto al directorio actual. - let candidate = root_path.as_ref().join(relative_path.as_ref()); +pub fn resolve_absolute_dir>(path: P) -> io::Result { + let path = path.as_ref(); + + let candidate = if path.is_absolute() { + path.to_path_buf() + } else { + // Directorio base CARGO_MANIFEST_DIR si está disponible; o current_dir() en su defecto. + env::var_os("CARGO_MANIFEST_DIR") + .map(PathBuf::from) + .or_else(|| env::current_dir().ok()) + .unwrap_or_else(|| PathBuf::from(".")) + .join(path) + }; // Resuelve `.`/`..`, enlaces simbólicos y obtiene la ruta absoluta en un único paso. let absolute_dir = candidate.canonicalize()?; @@ -47,6 +56,16 @@ where } } +/// Devuelve la ruta absoluta a un directorio existente. +#[deprecated(since = "0.3.0", note = "Use [`resolve_absolute_dir`] instead")] +pub fn absolute_dir(root_path: P, relative_path: Q) -> io::Result +where + P: AsRef, + Q: AsRef, +{ + resolve_absolute_dir(root_path.as_ref().join(relative_path.as_ref())) +} + // MACROS ÚTILES *********************************************************************************** #[doc(hidden)] diff --git a/tests/util.rs b/tests/util.rs index 6e0896f..70699a7 100644 --- a/tests/util.rs +++ b/tests/util.rs @@ -1,6 +1,6 @@ use pagetop::prelude::*; -use std::{fs, io}; +use std::{env, fs, io}; use tempfile::TempDir; #[cfg(unix)] @@ -13,15 +13,36 @@ mod unix { // /tmp//sub let td = TempDir::new()?; - let root = td.path(); - let sub = root.join("sub"); + let sub = td.path().join("sub"); fs::create_dir(&sub)?; - let abs = util::absolute_dir(root, "sub")?; + let abs = util::resolve_absolute_dir(&sub)?; assert_eq!(abs, std::fs::canonicalize(&sub)?); Ok(()) } + #[pagetop::test] + async fn ok_relative_dir_with_manifest() -> io::Result<()> { + let _app = service::test::init_service(Application::new().test()).await; + + let td = TempDir::new()?; + let sub = td.path().join("sub"); + fs::create_dir(&sub)?; + + // Fija CARGO_MANIFEST_DIR para que "sub" se resuelva contra td.path() + let prev_manifest_dir = env::var_os("CARGO_MANIFEST_DIR"); + env::set_var("CARGO_MANIFEST_DIR", td.path()); + let res = util::resolve_absolute_dir("sub"); + // Restaura entorno. + match prev_manifest_dir { + Some(v) => env::set_var("CARGO_MANIFEST_DIR", v), + None => env::remove_var("CARGO_MANIFEST_DIR"), + } + + assert_eq!(res?, std::fs::canonicalize(&sub)?); + Ok(()) + } + #[pagetop::test] async fn error_not_a_directory() -> io::Result<()> { let _app = service::test::init_service(Application::new().test()).await; @@ -30,7 +51,7 @@ mod unix { let file = td.path().join("foo.txt"); fs::write(&file, b"data")?; - let err = util::absolute_dir(td.path(), "foo.txt").unwrap_err(); + let err = util::resolve_absolute_dir(&file).unwrap_err(); assert_eq!(err.kind(), io::ErrorKind::InvalidInput); Ok(()) } @@ -46,15 +67,36 @@ mod windows { // C:\Users\...\Temp\... let td = TempDir::new()?; - let root = td.path(); - let sub = root.join("sub"); + let sub = td.path().join("sub"); fs::create_dir(&sub)?; - let abs = util::absolute_dir(root, sub.as_path())?; + let abs = util::resolve_absolute_dir(&sub)?; assert_eq!(abs, std::fs::canonicalize(&sub)?); Ok(()) } + #[pagetop::test] + async fn ok_relative_dir_with_manifest() -> io::Result<()> { + let _app = service::test::init_service(Application::new().test()).await; + + let td = TempDir::new()?; + let sub = td.path().join("sub"); + fs::create_dir(&sub)?; + + // Fija CARGO_MANIFEST_DIR para que "sub" se resuelva contra td.path() + let prev_manifest_dir = env::var_os("CARGO_MANIFEST_DIR"); + env::set_var("CARGO_MANIFEST_DIR", td.path()); + let res = util::resolve_absolute_dir("sub"); + // Restaura entorno. + match prev_manifest_dir { + Some(v) => env::set_var("CARGO_MANIFEST_DIR", v), + None => env::remove_var("CARGO_MANIFEST_DIR"), + } + + assert_eq!(res?, std::fs::canonicalize(&sub)?); + Ok(()) + } + #[pagetop::test] async fn error_not_a_directory() -> io::Result<()> { let _app = service::test::init_service(Application::new().test()).await; @@ -63,7 +105,7 @@ mod windows { let file = td.path().join("foo.txt"); fs::write(&file, b"data")?; - let err = util::absolute_dir(td.path(), "foo.txt").unwrap_err(); + let err = util::resolve_absolute_dir(&file).unwrap_err(); assert_eq!(err.kind(), io::ErrorKind::InvalidInput); Ok(()) }