Skip to main content

siglus_scene_vm/runtime/
net.rs

1use anyhow::{anyhow, Context, Result};
2use std::path::Path;
3#[cfg(any(target_os = "macos", target_os = "linux", target_os = "windows"))]
4use std::process::Command;
5
6#[derive(Debug, Default, Clone)]
7pub struct TnmNet {
8    pub last_target: Option<String>,
9    pub last_status: Option<u16>,
10    pub last_error: Option<String>,
11}
12
13impl TnmNet {
14    fn clear_status(&mut self, target: &str) {
15        self.last_target = Some(target.to_string());
16        self.last_status = None;
17        self.last_error = None;
18    }
19
20    fn set_error(&mut self, target: &str, err: &str) {
21        self.last_target = Some(target.to_string());
22        self.last_status = None;
23        self.last_error = Some(err.to_string());
24    }
25
26    pub fn open_target(&mut self, target: &str) -> Result<()> {
27        self.clear_status(target);
28
29        #[cfg(any(target_os = "macos", target_os = "linux", target_os = "windows"))]
30        {
31            #[cfg(target_os = "macos")]
32            let mut cmd = {
33                let mut c = Command::new("open");
34                c.arg(target);
35                c
36            };
37            #[cfg(target_os = "linux")]
38            let mut cmd = {
39                let mut c = Command::new("xdg-open");
40                c.arg(target);
41                c
42            };
43            #[cfg(target_os = "windows")]
44            let mut cmd = {
45                let mut c = Command::new("cmd");
46                c.args(["/C", "start", "", target]);
47                c
48            };
49
50            let status = cmd
51                .status()
52                .with_context(|| format!("open external target {target}"))?;
53            if status.success() {
54                Ok(())
55            } else {
56                let msg = format!("external opener exited with status {status}");
57                self.set_error(target, &msg);
58                Err(anyhow!(msg))
59            }
60        }
61
62        #[cfg(not(any(target_os = "macos", target_os = "linux", target_os = "windows")))]
63        {
64            let msg = "external opener is not implemented on this platform";
65            self.set_error(target, msg);
66            log::error!("{}: {}", msg, target);
67            Err(anyhow!(msg))
68        }
69    }
70
71    pub fn open_file(&mut self, path: &Path) -> Result<()> {
72        let target = path
73            .to_str()
74            .ok_or_else(|| anyhow!("non-utf8 file path"))?
75            .to_string();
76        self.open_target(&target)
77    }
78
79    pub fn open_url(&mut self, url: &str) -> Result<()> {
80        self.open_target(url)
81    }
82
83    pub fn get_bytes(&mut self, url: &str) -> Result<Vec<u8>> {
84        self.clear_status(url);
85
86        #[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
87        {
88            let msg = "network GET is disabled on wasm32-unknown-unknown";
89            self.set_error(url, msg);
90            log::error!("{}: {}", msg, url);
91            return Err(anyhow!(msg));
92        }
93
94        #[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
95        {
96            let response = ureq::get(url)
97                .call()
98                .with_context(|| format!("GET {url}"))?;
99            self.last_status = Some(response.status());
100            let mut reader = response.into_reader();
101            let mut bytes = Vec::new();
102            std::io::Read::read_to_end(&mut reader, &mut bytes)
103                .with_context(|| format!("read response body from {url}"))?;
104            Ok(bytes)
105        }
106    }
107
108    pub fn post_bytes(&mut self, url: &str, content_type: &str, body: &[u8]) -> Result<Vec<u8>> {
109        self.clear_status(url);
110
111        #[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
112        {
113            let msg = "network POST is disabled on wasm32-unknown-unknown";
114            self.set_error(url, msg);
115            log::error!("{}: {} content_type={}", msg, url, content_type);
116            let _ = body;
117            return Err(anyhow!(msg));
118        }
119
120        #[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
121        {
122            let response = ureq::post(url)
123                .set("Content-Type", content_type)
124                .send_bytes(body)
125                .with_context(|| format!("POST {url}"))?;
126            self.last_status = Some(response.status());
127            let mut reader = response.into_reader();
128            let mut bytes = Vec::new();
129            std::io::Read::read_to_end(&mut reader, &mut bytes)
130                .with_context(|| format!("read response body from {url}"))?;
131            Ok(bytes)
132        }
133    }
134}