insta/
utils.rs

1use std::{
2    borrow::Cow,
3    env,
4    io::Write,
5    path::Path,
6    process::{Command, Stdio},
7};
8
9/// Are we running in in a CI environment?
10pub fn is_ci() -> bool {
11    match env::var("CI").ok().as_deref() {
12        Some("false") | Some("0") | Some("") => false,
13        None => env::var("TF_BUILD").is_ok(),
14        Some(_) => true,
15    }
16}
17
18#[cfg(feature = "colors")]
19pub use console::style;
20
21#[cfg(not(feature = "colors"))]
22mod fake_colors {
23    pub struct FakeStyledObject<D>(D);
24
25    macro_rules! style_attr {
26        ($($name:ident)*) => {
27            $(
28                #[inline]
29                pub fn $name(self) -> FakeStyledObject<D> { self }
30            )*
31        }
32    }
33
34    impl<D> FakeStyledObject<D> {
35        style_attr!(red green yellow cyan bold dim underlined);
36    }
37
38    impl<D: std::fmt::Display> std::fmt::Display for FakeStyledObject<D> {
39        fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
40            std::fmt::Display::fmt(&self.0, f)
41        }
42    }
43
44    pub fn style<D>(val: D) -> FakeStyledObject<D> {
45        FakeStyledObject(val)
46    }
47}
48
49#[cfg(not(feature = "colors"))]
50pub use self::fake_colors::*;
51
52/// Returns the term width that insta should use.
53pub fn term_width() -> usize {
54    #[cfg(feature = "colors")]
55    {
56        console::Term::stdout().size().1 as usize
57    }
58    #[cfg(not(feature = "colors"))]
59    {
60        74
61    }
62}
63
64/// Converts a path into a string that can be persisted.
65pub fn path_to_storage(path: &Path) -> String {
66    #[cfg(windows)]
67    {
68        path.to_str().unwrap().replace('\\', "/")
69    }
70
71    #[cfg(not(windows))]
72    {
73        path.to_string_lossy().into()
74    }
75}
76
77/// Tries to format a given rust expression with rustfmt
78pub fn format_rust_expression(value: &str) -> Cow<'_, str> {
79    const PREFIX: &str = "const x:() = ";
80    const SUFFIX: &str = ";\n";
81    if let Ok(mut proc) = Command::new("rustfmt")
82        .arg("--emit=stdout")
83        .arg("--edition=2018")
84        .stdin(Stdio::piped())
85        .stdout(Stdio::piped())
86        .stderr(Stdio::null())
87        .spawn()
88    {
89        {
90            let stdin = proc.stdin.as_mut().unwrap();
91            stdin.write_all(PREFIX.as_bytes()).unwrap();
92            stdin.write_all(value.as_bytes()).unwrap();
93            stdin.write_all(SUFFIX.as_bytes()).unwrap();
94        }
95        if let Ok(output) = proc.wait_with_output() {
96            if output.status.success() {
97                // slice between after the prefix and before the suffix
98                // (currently 14 from the start and 2 before the end, respectively)
99                let start = PREFIX.len() + 1;
100                let end = output.stdout.len() - SUFFIX.len();
101                return std::str::from_utf8(&output.stdout[start..end])
102                    .unwrap()
103                    .replace("\r\n", "\n")
104                    .into();
105            }
106        }
107    }
108    Cow::Borrowed(value)
109}
110
111#[cfg(feature = "_cargo_insta_internal")]
112pub fn get_cargo() -> std::ffi::OsString {
113    let cargo = env::var_os("CARGO");
114    let cargo = cargo
115        .as_deref()
116        .unwrap_or_else(|| std::ffi::OsStr::new("cargo"));
117    cargo.to_os_string()
118}
119
120#[test]
121fn test_format_rust_expression() {
122    use crate::assert_snapshot;
123    assert_snapshot!(format_rust_expression("vec![1,2,3]"), @"vec![1, 2, 3]");
124    assert_snapshot!(format_rust_expression("vec![1,2,3].iter()"), @"vec![1, 2, 3].iter()");
125    assert_snapshot!(format_rust_expression(r#"    "aoeu""#), @r###""aoeu""###);
126    assert_snapshot!(format_rust_expression(r#"  "aoe😄""#), @r###""aoe😄""###);
127    assert_snapshot!(format_rust_expression("😄😄😄😄😄"), @"😄😄😄😄😄")
128}