惯用地扩展 Rust Path 中的波浪号 [英] Expand tilde in Rust Path idiomatically

查看:28
本文介绍了惯用地扩展 Rust Path 中的波浪号的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

Sometimes, for instance when reading some configuration file, you read a file path entered by the user without going through the shell (for instance, you get ~/test).

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

As Option 2 below doesn’t write to test file in user home directory, I’m wondering if there is something more idiomatic than 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() 以保持示例简短.生产代码中应该有一些错误处理.

Note: unwrap() is used here to keep the example short. There should be some error handling in production code.

推荐答案

  1. 最惯用的方法是使用现有的 crate,在本例中为 shellexpand (githubcrates.io) 似乎可以满足您的需求:>

  1. The most idiomatic way would be to just use an existing crate, in this case shellexpand (github, crates.io) seems to do what you want:

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 输入类型模仿标准图书馆.这就是为什么该方法接受所有 Path-like输入,例如 &str&OsStr&Path.
    • Path::new 不分配任何东西,它指向与 &str 完全相同的字节.
    • strip_prefix("~/").unwrap() 不应该在这里失败,因为我们检查了路径是否以 ~ 开头并且不仅仅是~.唯一的办法就是路径以 ~/ 开头(因为 starts_with已定义).
    • The P: AsRef<Path> input type imitates what the standard library does. This is why the method accepts all Path-like inputs, like &str, &OsStr, and &Path.
    • Path::new doesn't allocate anything, it points to exactly the same bytes as the &str.
    • strip_prefix("~/").unwrap() should never fail here, because we checked that the path starts with ~ and is not just ~. The only way how this can be is that the path starts with ~/ (because of how starts_with is defined).

    这篇关于惯用地扩展 Rust Path 中的波浪号的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

  • 查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆