【问题标题】:Expand tilde in Rust Path idiomatically习惯性地在 Rust Path 中展开波浪号
【发布时间】:2019-06-13 12:33:29
【问题描述】:

有时,例如在读取某个配置文件时,您无需通过 shell 即可读取用户输入的文件路径(例如,您会得到~/test)。

由于下面的Option 2 不会写入用户主目录中的测试文件,我想知道是否有比Option 1 更惯用的东西。

use std::env::var;
use std::fs::File;
use std::io::prelude::*;
use std::path::Path;

fn write_to(path: &Path) {
    let mut f = File::create(path).unwrap();
    f.write_all("Hi".as_bytes()).unwrap();
}

fn main() {
    // Option 1
    let from_env = format!("{}/test", var("HOME").unwrap());
    let with_var = Path::new(&from_env);
    // Create $HOME/test
    write_to(with_var);

    // Option 2
    let with_tilde = Path::new("~/test");
    // Create the test file in current directory, provided a directory ./~ exists
    write_to(with_tilde);
}

注意:此处使用unwrap() 以保持示例简短。生产代码中应该有一些错误处理。

【问题讨论】:

    标签: path rust directory home-directory


    【解决方案1】:
    1. 最惯用的方法是只使用现有的 crate,在这种情况下 shellexpand (github, crates.io) 似乎可以满足您的需求:

      extern crate shellexpand; // 1.0.0
      
      #[test]
      fn test_shellexpand() {
          let home = std::env::var("HOME").unwrap();
          assert_eq!(shellexpand::tilde("~/foo"), format!("{}/foo", home));
      }
      
    2. 或者,您可以尝试使用dirs (crates.io)。这是一个草图:

      extern crate dirs; // 1.0.4
      
      use std::path::{Path, PathBuf};
      
      fn expand_tilde<P: AsRef<Path>>(path_user_input: P) -> Option<PathBuf> {
          let p = path_user_input.as_ref();
          if !p.starts_with("~") {
              return Some(p.to_path_buf());
          }
          if p == Path::new("~") {
              return dirs::home_dir();
          }
          dirs::home_dir().map(|mut h| {
              if h == Path::new("/") {
                  // Corner case: `h` root directory;
                  // don't prepend extra `/`, just drop the tilde.
                  p.strip_prefix("~").unwrap().to_path_buf()
              } else {
                  h.push(p.strip_prefix("~/").unwrap());
                  h
              }
          })
      }
      

      用法示例:

      #[test]
      fn test_expand_tilde() {
          // Should work on your linux box during tests, would fail in stranger
          // environments!
          let home = std::env::var("HOME").unwrap();
          let projects = PathBuf::from(format!("{}/Projects", home));
          assert_eq!(expand_tilde("~/Projects"), Some(projects));
          assert_eq!(expand_tilde("/foo/bar"), Some("/foo/bar".into()));
          assert_eq!(
              expand_tilde("~alice/projects"),
              Some("~alice/projects".into())
          );
      }
      

      一些备注:

      • P: AsRef&lt;Path&gt; 输入类型模仿标准 图书馆有。这就是为什么该方法接受所有Path-like 输入,例如 &amp;str&amp;OsStr&amp;Path
      • Path::new 没有分配任何东西,它指向 与&amp;str 完全相同的字节。
      • strip_prefix("~/").unwrap() 绝对不能在这里失败, 因为我们检查了路径是否以~ 开头并且 不仅仅是~。唯一的办法就是这样 路径以~/ 开头(因为starts_with 已定义)。

    【讨论】:

      猜你喜欢
      • 2015-04-07
      • 2019-05-03
      • 2013-07-10
      • 2011-06-06
      • 2013-03-29
      • 2019-01-04
      相关资源
      最近更新 更多