diff --git a/kclvm/driver/src/kpm_metadata.rs b/kclvm/driver/src/kpm_metadata.rs index 996d404fa..ed1abdc39 100644 --- a/kclvm/driver/src/kpm_metadata.rs +++ b/kclvm/driver/src/kpm_metadata.rs @@ -1,7 +1,8 @@ +use crate::{get_path_for_executable, kcl, lookup_the_nearest_file_dir}; use anyhow::{bail, Ok, Result}; use kclvm_parser::LoadProgramOptions; use serde::{Deserialize, Serialize}; -use std::{collections::HashMap, env, iter, path::PathBuf, process::Command}; +use std::{collections::HashMap, path::PathBuf, process::Command}; const MANIFEST_FILE: &str = "kcl.mod"; @@ -115,71 +116,7 @@ pub(crate) fn fetch_mod_metadata(manifest_path: PathBuf) -> Result { } } -/// [`lookup_the_nearest_file_dir`] will start from [`from`] and search for file [`the_nearest_file`] in the parent directories. -/// If found, it will return the [`Some`] of [`the_nearest_file`] file path. If not found, it will return [`None`] -pub(crate) fn lookup_the_nearest_file_dir( - from: PathBuf, - the_nearest_file: &str, -) -> Option { - let mut current_dir = from; - - loop { - let found_path = current_dir.join(the_nearest_file); - if found_path.is_file() { - return current_dir.canonicalize().ok(); - } - - match current_dir.parent() { - Some(parent) => current_dir = parent.to_path_buf(), - None => return None, - } - } -} - -/// [`kcl`] will return the path for executable kcl binary. -pub fn kcl() -> PathBuf { - get_path_for_executable("kcl") -} - /// [`kpm`] will return the path for executable kpm binary. pub fn kpm() -> PathBuf { get_path_for_executable("kpm") } - -/// [`get_path_for_executable`] will return the path for [`executable_name`]. -pub fn get_path_for_executable(executable_name: &'static str) -> PathBuf { - // The current implementation checks $PATH for an executable to use: - // `` - // example: for , this tries just , which will succeed if is on the $PATH - - if lookup_in_path(executable_name) { - return executable_name.into(); - } - - executable_name.into() -} - -/// [`lookup_in_path`] will search for an executable file [`exec`] in the environment variable ‘PATH’. -/// If found, return true, otherwise return false. -fn lookup_in_path(exec: &str) -> bool { - let paths = env::var_os("PATH").unwrap_or_default(); - env::split_paths(&paths) - .map(|path| path.join(exec)) - .find_map(probe) - .is_some() -} - -/// [`probe`] check if the given path points to a file. -/// If it does, return [`Some`] of the path. -/// If not, check if adding the current operating system's executable file extension (if any) to the path points to a file. -/// If it does, return [`Some`] of the path with the extension added. -/// If neither, return [`None`]. -fn probe(path: PathBuf) -> Option { - let with_extension = match env::consts::EXE_EXTENSION { - "" => None, - it => Some(path.with_extension(it)), - }; - iter::once(path) - .chain(with_extension) - .find(|it| it.is_file()) -} diff --git a/kclvm/driver/src/kpm_update.rs b/kclvm/driver/src/kpm_update.rs index 7835bf018..a1db1cdbc 100644 --- a/kclvm/driver/src/kpm_update.rs +++ b/kclvm/driver/src/kpm_update.rs @@ -1,9 +1,12 @@ -use crate::kpm_metadata::get_path_for_executable; +use crate::{kcl, lookup_the_nearest_file_dir}; use anyhow::{bail, Result}; use std::{path::PathBuf, process::Command}; const MANIFEST_FILE: &str = "kcl.mod"; +/// Update the KCL module. +/// +/// This function calls `kcl mod update` to update the KCL module. pub(crate) fn update_kcl_module(manifest_path: PathBuf) -> Result<()> { match lookup_the_nearest_file_dir(manifest_path.clone(), MANIFEST_FILE) { Some(mod_dir) => { @@ -31,24 +34,3 @@ pub(crate) fn update_kcl_module(manifest_path: PathBuf) -> Result<()> { ), } } -pub fn kcl() -> PathBuf { - get_path_for_executable("kcl") -} - -pub(crate) fn lookup_the_nearest_file_dir( - from: PathBuf, - the_nearest_file: &str, -) -> Option { - let mut current_dir = from; - - loop { - let found_path = current_dir.join(the_nearest_file); - if found_path.is_file() { - return current_dir.canonicalize().ok(); - } - match current_dir.parent() { - Some(parent) => current_dir = parent.to_path_buf(), - None => return None, - } - } -} diff --git a/kclvm/driver/src/lib.rs b/kclvm/driver/src/lib.rs index 9a2f15e55..cbbe7d267 100644 --- a/kclvm/driver/src/lib.rs +++ b/kclvm/driver/src/lib.rs @@ -1,4 +1,5 @@ use anyhow::Result; +use std::{env, iter}; pub mod arguments; pub mod kpm_metadata; pub mod kpm_update; @@ -218,6 +219,70 @@ pub fn lookup_kcl_yaml(dir: &PathBuf) -> io::Result { } } +/// Get the path for the KCL executable. +fn kcl() -> PathBuf { + get_path_for_executable("kcl") +} + +/// [`get_path_for_executable`] will return the path for [`executable_name`]. +pub fn get_path_for_executable(executable_name: &'static str) -> PathBuf { + // The current implementation checks $PATH for an executable to use: + // `` + // example: for , this tries just , which will succeed if is on the $PATH + + if lookup_in_path(executable_name) { + return executable_name.into(); + } + + executable_name.into() +} + +/// [`lookup_in_path`] will search for an executable file [`exec`] in the environment variable ‘PATH’. +/// If found, return true, otherwise return false. +fn lookup_in_path(exec: &str) -> bool { + let paths = env::var_os("PATH").unwrap_or_default(); + env::split_paths(&paths) + .map(|path| path.join(exec)) + .find_map(probe) + .is_some() +} + +/// [`probe`] check if the given path points to a file. +/// If it does, return [`Some`] of the path. +/// If not, check if adding the current operating system's executable file extension (if any) to the path points to a file. +/// If it does, return [`Some`] of the path with the extension added. +/// If neither, return [`None`]. +fn probe(path: PathBuf) -> Option { + let with_extension = match env::consts::EXE_EXTENSION { + "" => None, + it => Some(path.with_extension(it)), + }; + iter::once(path) + .chain(with_extension) + .find(|it| it.is_file()) +} + +/// [`lookup_the_nearest_file_dir`] will start from [`from`] and search for file [`the_nearest_file`] in the parent directories. +/// If found, it will return the [`Some`] of [`the_nearest_file`] file path. If not found, it will return [`None`] +pub(crate) fn lookup_the_nearest_file_dir( + from: PathBuf, + the_nearest_file: &str, +) -> Option { + let mut current_dir = from; + + loop { + let found_path = current_dir.join(the_nearest_file); + if found_path.is_file() { + return current_dir.canonicalize().ok(); + } + + match current_dir.parent() { + Some(parent) => current_dir = parent.to_path_buf(), + None => return None, + } + } +} + /// For the KCL project, some definitions may be introduced through multi-file /// compilation (kcl.yaml). This function is used to start from a single file and try /// to find a `compile unit` that contains all definitions diff --git a/kclvm/driver/src/test_data/kpm_update/kcl.mod b/kclvm/driver/src/test_data/kpm_update/kcl.mod new file mode 100644 index 000000000..d3ed2fe74 --- /dev/null +++ b/kclvm/driver/src/test_data/kpm_update/kcl.mod @@ -0,0 +1,7 @@ +[package] +name = "kpm_update" +edition = "0.0.1" +version = "0.0.1" + +[dependencies] +k8s = "1.29" diff --git a/kclvm/driver/src/test_data/kpm_update/subdir/main.k b/kclvm/driver/src/test_data/kpm_update/subdir/main.k new file mode 100644 index 000000000..e69de29bb diff --git a/kclvm/driver/src/tests.rs b/kclvm/driver/src/tests.rs index 279f1cb3c..876d0e3c8 100644 --- a/kclvm/driver/src/tests.rs +++ b/kclvm/driver/src/tests.rs @@ -8,9 +8,10 @@ use kclvm_parser::LoadProgramOptions; use walkdir::WalkDir; use crate::arguments::parse_key_value_pair; -use crate::kpm_metadata::{fetch_metadata, fill_pkg_maps_for_k_file, lookup_the_nearest_file_dir}; +use crate::kpm_metadata::fetch_metadata; use crate::kpm_update::update_kcl_module; use crate::{canonicalize_input_files, expand_input_files, get_pkg_list}; +use crate::{fill_pkg_maps_for_k_file, lookup_the_nearest_file_dir}; #[test] fn test_canonicalize_input_files() { @@ -382,28 +383,42 @@ fn test_get_pkg_list() { ); } -#[cfg(test)] -// Define a mock structure to simulate the behavior of Command::output -struct MockCommand { - output: Result, +#[test] +fn test_lookup_the_nearest_file_dir_for_update() { + let path = PathBuf::from(".") + .join("src") + .join("test_data") + .join("kpm_update"); + let result = lookup_the_nearest_file_dir(path.clone(), "kcl.mod"); + assert!(result.is_some()); + assert_eq!( + result.unwrap().display().to_string(), + path.canonicalize().unwrap().display().to_string() + ); + + let main_path = path.join("subdir").join("main.k"); + let result = lookup_the_nearest_file_dir(main_path, "kcl.mod"); + assert!(result.is_some()); + assert_eq!( + result.unwrap().display().to_string(), + path.canonicalize().unwrap().display().to_string() + ); } -// Define a mock structure to represent the output of Command::output -struct MockCommandOutput { - status: std::process::ExitStatus, - stderr: Vec, + +fn test_fetch_mod_metadata() { + let path = PathBuf::from(".") + .join("src") + .join("test_data") + .join("kpm_update"); + + let update_mod = update_kcl_module(path.clone()); + // Show more information when the test fails. + println!("{:?}", update_mod); + assert!(!update_mod.is_err()); } #[test] -fn test_update_kcl_module_failure() { - let manifest_path = PathBuf::from("path/to/manifest"); - fn mock_command_new_failing(_command: &str) -> MockCommand { - MockCommand { - output: Err(std::io::Error::new( - std::io::ErrorKind::Other, - "Command failed", - )), - } - } - let result = update_kcl_module(manifest_path); - assert!(result.is_err()); +fn test_update_module() { + test_fetch_mod_metadata(); + println!("test_update_module() passed"); }