习惯性地在 Rust Path 中展开波浪号

Posted

技术标签:

【中文标题】习惯性地在 Rust Path 中展开波浪号【英文标题】:Expand tilde in Rust Path idiomatically 【发布时间】: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() 以保持示例简短。生产代码中应该有一些错误处理。

【问题讨论】:

【参考方案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));
    
    

    或者,您可以尝试使用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;PathPath::new 没有分配任何东西,它指向 与&amp;str 完全相同的字节。 strip_prefix("~/").unwrap() 绝对不能在这里失败, 因为我们检查了路径是否以~ 开头并且 不仅仅是~。唯一的办法就是这样 路径以~/ 开头(因为starts_with 已定义)。

【讨论】:

以上是关于习惯性地在 Rust Path 中展开波浪号的主要内容,如果未能解决你的问题,请参考以下文章

如何在路径的上下文中使用“~”(波浪号)?

《Rust 编码规范》

在linux中home目录的作用是是啥?

shell编程养成好习惯

习惯的力量之四理直气壮的借口?

习惯的力量之四理直气壮的借口?