惯用地在Rust路径中扩展波浪号

时间:2019-01-19 13:30:01

标签: path rust directory home-directory

有时候,例如,当读取某些配置文件时,您将读取用户输入的文件路径而无需通过外壳程序(例如,您得到body: Column( children: [ Text('foo'), Expanded( child: Container( child: ListView.builder( shrinkWrap: true, itemBuilder: ......., itemCount: ....., ), ), ), Text('bar') ] ) ,)。

由于下面的~/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); } 来简化示例。生产代码中应该有一些错误处理。

1 个答案:

答案 0 :(得分:4)

  1. 最惯用的方法是仅使用现有的板条箱,在这种情况下,shellexpandgithubcrates.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. 或者,您可以尝试使用dirscrates.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("~") {
            if p == Path::new("~") {
                dirs::home_dir()
            } else {
                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
                    }
                })
            }
        } else {
            Some(p.to_path_buf())
        }
    }
    

    用法示例:

    #[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<Path>输入类型模仿了标准 图书馆呢。这就是为什么该方法接受所有类似Path的原因 输入,例如&str&OsStr&Path
    • Path::new不分配任何内容,它指向 与&str完全相同的字节。
    • strip_prefix("~/").unwrap()应该永远不会失败, 因为我们检查了路径以~开头, 不只是~。唯一的方法是 路径以~/开头(由于starts_with 已定义)。