jsona_util/environment/
native.rs1use 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}