cargo_rr/
lib.rs

1use camino::{Utf8Path, Utf8PathBuf};
2use std::{
3    fs::{self, File},
4    io::{self, Read},
5};
6
7use anyhow::anyhow;
8
9mod list;
10mod record;
11mod replay;
12
13pub use list::list;
14pub use record::record;
15pub use replay::replay;
16
17pub struct Trace(Utf8PathBuf);
18
19impl Trace {
20    pub fn new(name: &str) -> anyhow::Result<Self> {
21        let root = traces_dir()?;
22        let dir = root.join(name);
23        if !dir.exists() {
24            return Err(anyhow!("Trace `{}` does not exist in `{}`", name, root));
25        }
26        Ok(Self(dir))
27    }
28
29    fn name_for_bin(bin: &Utf8Path) -> anyhow::Result<Self> {
30        let root = traces_dir()?;
31        let bin_name = bin
32            .file_name()
33            .ok_or_else(|| anyhow!("Can't get file name of bin"))?;
34        let mut dir = root.join(bin_name);
35
36        let mut suffix = 0;
37        loop {
38            if !dir.exists() {
39                break Ok(Self(dir));
40            }
41            suffix += 1;
42            dir.set_file_name(format!("{}-{}", bin_name, suffix));
43        }
44    }
45
46    pub fn name(&self) -> &str {
47        &self.0.file_name().expect("Trace dir shouldn't end in ..")
48    }
49
50    pub fn dir(&self) -> &Utf8Path {
51        self.0.as_path()
52    }
53
54    pub fn set_latest(&self) -> anyhow::Result<()> {
55        let root = self.0.parent().expect("Trace has parent");
56        fs::write(root.join("latest"), self.name())?;
57        Ok(())
58    }
59
60    pub fn latest() -> anyhow::Result<Self> {
61        let root = traces_dir()?;
62        let mut file = match File::open(root.join("latest")) {
63            Ok(f) => f,
64            Err(err) if err.kind() == io::ErrorKind::NotFound => {
65                return Err(anyhow!("No trace in `{}`", root));
66            }
67            Err(err) => return Err(err.into()),
68        };
69        let mut latest = String::new();
70        file.read_to_string(&mut latest)?;
71        let latest = latest.trim();
72        let latest = fs::canonicalize(root.join(latest))?;
73        let latest = Utf8PathBuf::from_path_buf(latest)
74            .map_err(|_| anyhow!("Path to target dir must be utf-8"))?;
75        Ok(Self(latest))
76    }
77}
78
79pub fn traces_dir() -> anyhow::Result<Utf8PathBuf> {
80    let meta = cargo_metadata::MetadataCommand::new().no_deps().exec()?;
81    let dir = meta.target_directory.join("rr");
82    fs::create_dir_all(&dir)?;
83    Ok(dir)
84}
85
86pub fn split_opts(opts: Option<&str>) -> Vec<&str> {
87    opts.map_or_else(Vec::new, |s| s.split(' ').collect())
88}