jsona_util/environment/
native.rs

1use async_trait::async_trait;
2use time::OffsetDateTime;
3use url::Url;
4
5use anyhow::anyhow;
6use std::path::PathBuf;
7
8use super::Environment;
9use crate::util::url::{to_file_path, to_url};
10
11#[derive(Clone)]
12pub struct NativeEnvironment {
13    handle: tokio::runtime::Handle,
14}
15
16impl NativeEnvironment {
17    #[must_use]
18    pub fn new() -> Self {
19        Self {
20            handle: tokio::runtime::Handle::current(),
21        }
22    }
23}
24
25impl Default for NativeEnvironment {
26    fn default() -> Self {
27        Self::new()
28    }
29}
30
31#[async_trait(?Send)]
32impl Environment for NativeEnvironment {
33    type Stdin = tokio::io::Stdin;
34    type Stdout = tokio::io::Stdout;
35    type Stderr = tokio::io::Stderr;
36
37    fn now(&self) -> time::OffsetDateTime {
38        OffsetDateTime::now_utc()
39    }
40
41    fn spawn<F>(&self, fut: F)
42    where
43        F: futures::Future + Send + 'static,
44        F::Output: Send,
45    {
46        self.handle.spawn(fut);
47    }
48
49    fn spawn_local<F>(&self, fut: F)
50    where
51        F: futures::Future + 'static,
52    {
53        tokio::task::spawn_local(fut);
54    }
55
56    fn env_var(&self, name: &str) -> Option<String> {
57        std::env::var(name).ok()
58    }
59
60    fn atty_stderr(&self) -> bool {
61        atty::is(atty::Stream::Stderr)
62    }
63
64    fn stdin(&self) -> Self::Stdin {
65        tokio::io::stdin()
66    }
67
68    fn stdout(&self) -> Self::Stdout {
69        tokio::io::stdout()
70    }
71
72    fn stderr(&self) -> Self::Stderr {
73        tokio::io::stderr()
74    }
75
76    async fn read_file(&self, path: &Url) -> Result<Vec<u8>, anyhow::Error> {
77        let path = to_file_path(path).ok_or_else(|| anyhow!("failed to read file at ${path}"))?;
78        Ok(tokio::fs::read(PathBuf::from(path)).await?)
79    }
80
81    async fn write_file(&self, path: &Url, bytes: &[u8]) -> Result<(), anyhow::Error> {
82        let path = to_file_path(path).ok_or_else(|| anyhow!("failed to read file at ${path}"))?;
83        Ok(tokio::fs::write(PathBuf::from(path), bytes).await?)
84    }
85
86    #[cfg(feature = "fetch")]
87    async fn fetch_file(&self, path: &Url) -> Result<Vec<u8>, anyhow::Error> {
88        let client = reqwest::Client::builder()
89            .timeout(std::time::Duration::from_secs(30))
90            .build()
91            .unwrap();
92        let data = client
93            .get(path.clone())
94            .send()
95            .await?
96            .bytes()
97            .await?
98            .to_vec();
99        Ok(data)
100    }
101
102    #[cfg(not(feature = "fetch"))]
103    async fn fetch_file(&self, url: &Url) -> Result<Vec<u8>, anyhow::Error> {
104        anyhow::bail!("failed to fetch `{url}`, fetch is not supported")
105    }
106
107    fn root_uri(&self) -> Option<Url> {
108        let cwd = std::env::current_dir().ok()?;
109        to_url(&cwd.display().to_string(), &None)
110    }
111}