在開發程式的時候難免會需要在程式中引入外部的檔案,為了方便管理我們也常會將這些檔案放置在程式專案目錄下。然而在程式碼撰寫程式路徑於執行階段讀取檔案時,檔案路徑的正確性需要等到執行階段的時候才會知道,就算寫錯了而找不到這個檔案,程式專案也是能成功通過編譯,這就會使得程式在執行階段有出現問題的可能。



Rust內建的includeinclude_strinclude_bytes巨集會在編譯階段去檢查檔案是否存在,這是因為在編譯階段的時候就要把檔案引用進來。但有時候我們會希望檔案是在程式執行階段才引入,所以就要有個機制是讓Rust能夠在編譯專案的時候,去檢查檔案路徑是否存在。

Manifest Dir Macros

「Manifest Dir Macros」是筆者開發的套件,提供了一些類似函數的巨集,能夠在編譯階段檢查或是操作相對於CARGO_MANIFEST_DIR的檔案路徑。CARGO_MANIFEST_DIR就是Cargo程式專案的Cargo.toml的所在目錄,也就是程式專案的根目錄啦!

Crates.io

Cargo.toml

manifest-dir-macros = "*"

使用方法

manifest_dir_macros這個crate提供了以下幾個巨集:

  • absolute_path:只允許傳入的路徑為絕對路徑。回傳絕對路徑。
  • directory_absolute_path:只允許傳入的路徑為絕對路徑,且是個目錄(必須存在)。回傳絕對路徑。
  • directory_path:允許傳入的路徑可為絕對路徑或相對路徑。如果是相對路徑,會使其相對於CARGO_MANIFEST_DIR。且是個目錄(必須存在)。回傳絕對路徑。
  • directory_relative_path:只允許傳入的路徑為相對路徑,會使其相對於CARGO_MANIFEST_DIR,且是個目錄(必須存在)。回傳絕對路徑。
  • exist_absolute_path:只允許傳入的路徑為絕對路徑,且必須存在。回傳絕對路徑。
  • exist_path:允許傳入的路徑可為絕對路徑或相對路徑。如果是相對路徑,會使其相對於CARGO_MANIFEST_DIR。且必須存在。回傳絕對路徑。
  • exist_relative_path:只允許傳入的路徑為相對路徑,會使其相對於CARGO_MANIFEST_DIR,且必須存在。回傳絕對路徑。
  • file_absolute_path:只允許傳入的路徑為絕對路徑,且是個檔案(必須存在)。回傳絕對路徑。
  • file_path:允許傳入的路徑可為絕對路徑或相對路徑。如果是相對路徑,會使其相對於CARGO_MANIFEST_DIR。且是個檔案(必須存在)。回傳絕對路徑。
  • file_relative_path:只允許傳入的路徑為相對路徑,會使其相對於CARGO_MANIFEST_DIR,且是個檔案(必須存在)。回傳絕對路徑。
  • get_extension:回傳傳入的路徑的副檔名。如果沒有指定預設值,路徑中的副檔名不存在的話會編譯失敗。
  • get_file_name:回傳傳入的路徑的檔案名稱。如果沒有指定預設值,路徑中的檔案名稱不存在的話會編譯失敗。
  • get_file_stem:回傳傳入的路徑的不包含副檔名的檔案名稱(主檔名)。如果沒有指定預設值,路徑中的主檔名不存在的話會編譯失敗。
  • get_parent:回傳傳入的路徑的上一層檔案路徑。如果沒有指定預設值,路徑已經是最上一層的話會編譯失敗。
  • mime_guess:須啟用mime_guess特色才能使用。藉由檔案路徑(或者準確的說,路徑中的副檔名),來猜測並回傳檔案的MIME Type,如果沒有指定預設值,且無法猜到檔案的MIME Type的話會編譯失敗。
  • not_directory_absolute_path:只允許傳入的路徑為絕對路徑,且不是個目錄(必須存在)。回傳絕對路徑。
  • not_directory_path:允許傳入的路徑可為絕對路徑或相對路徑。如果是相對路徑,會使其相對於CARGO_MANIFEST_DIR。且不是個目錄(必須存在)。回傳絕對路徑。
  • not_directory_relative_path:只允許傳入的路徑為相對路徑,會使其相對於CARGO_MANIFEST_DIR,且不是個目錄(必須存在)。回傳絕對路徑。
  • path:允許傳入的路徑可為絕對路徑或相對路徑。如果是相對路徑,會使其相對於CARGO_MANIFEST_DIR。回傳絕對路徑。
  • relative_path:只允許傳入的路徑為相對路徑,會使其相對於CARGO_MANIFEST_DIR。回傳絕對路徑。

傳給巨集的檔案路徑可以是一個字串定數,也可以是多個用逗號隔開的檔名(字串定數),巨集會根據編譯環境的作業系統將這些檔名其加上適當的斜線或反斜線,串接在一起成一個路徑。

如果有啟用tuple特色,還可以用巢狀的「元組(tuple)」,來串接多個檔名(字串定數),這個用法可以與macro_rule巨集搭配使用。

範例程式碼如下:

#[macro_use] extern crate manifest_dir_macros;

println!(path!("Cargo.toml"));
println!(path!("src/lib.rs"));
println!(path!("src", "lib.rs"));
println!(path!("src", "lib.rs", "/bin"));
println!(path!("/usr"));

println!(exist_path!("Cargo.toml"));
println!(directory_path!("src"));
println!(not_directory_path!("Cargo.toml"));
println!(file_path!("Cargo.toml"));

println!(relative_path!("Cargo.toml"));
println!(directory_relative_path!("src"));
println!(not_directory_relative_path!("Cargo.toml"));
println!(file_relative_path!("Cargo.toml"));

println!(get_file_name!("src/lib.rs"));
println!(get_file_name!(default = "main.rs", "/"));
println!(get_file_stem!("src/lib.rs"));
println!(get_file_stem!(default = "lib", "/"));
println!(get_extension!("src/lib.rs"));
println!(get_extension!(default = "rs", "src/lib"));
println!(get_parent!("src/lib.rs"));
println!(get_parent!(default = "/home", "/"));

// enable the `mime_guess` feature
{
    println!(mime_guess!("src/lib.rs"));
    println!(mime_guess!(default = "application/octet-stream", "Cargo.lock"));
}

// The `tuple` feature lets these macros above support to input nested literal string tuples, which is useful when you want to use these macros inside a `macro_rule!` macro and concatenate with other literal strings.
// `$x:expr` matchers can be used in these macros thus.
{
    println!(path!(("foo",)));
    println!(path!(("foo", "bar")));
    println!(path!("a", ("foo", "bar")));
    println!(path!(("foo", "bar"), "a"));
    println!(path!(("foo", "bar"), ("a", "b")));
    println!(path!(("foo", "bar", ("a", "b")), ("c", "d")));
}