banished_banishedinfo/
native.rs1use std::io::Read;
5use std::path::{Path, PathBuf};
6
7use ureq::ResponseExt;
8
9use crate::{Error, Mod, Sort, Version};
10
11pub struct Client {
13 agent: ureq::Agent,
14 user_agent: String,
15}
16
17impl Client {
18 pub fn new(version: &str) -> Client {
20 Client {
21 agent: ureq::Agent::config_builder()
22 .http_status_as_error(false)
23 .timeout_global(Some(std::time::Duration::from_secs(600)))
24 .build()
25 .into(),
26 user_agent: crate::user_agent(version),
27 }
28 }
29
30 fn get(
32 &self,
33 url: &str,
34 limit: u64,
35 mut progress: impl FnMut(u64, Option<u64>),
36 ) -> Result<(String, Vec<u8>), Error> {
37 let mut response = self
38 .agent
39 .get(url)
40 .header("User-Agent", &self.user_agent)
41 .call()
42 .map_err(|e| Error::Network(e.to_string()))?;
43 let status = response.status().as_u16();
44 if !(200..300).contains(&status) {
45 return Err(Error::Status(status));
46 }
47 let final_url = response.get_uri().to_string();
48 let total = response
49 .headers()
50 .get("content-length")
51 .and_then(|v| v.to_str().ok())
52 .and_then(|v| v.parse().ok());
53 let mut reader = response.body_mut().with_config().limit(limit).reader();
54 let mut data = Vec::new();
55 let mut chunk = vec![0; 64 << 10];
56 loop {
57 let n = reader
58 .read(&mut chunk)
59 .map_err(|e| Error::Network(e.to_string()))?;
60 if n == 0 {
61 break;
62 }
63 data.extend_from_slice(&chunk[..n]);
64 progress(data.len() as u64, total);
65 }
66 Ok((final_url, data))
67 }
68
69 pub fn image(&self, url: &str) -> Result<Vec<u8>, Error> {
71 self.get(url, 4 << 20, |_, _| {}).map(|(_, b)| b)
72 }
73
74 fn page(&self, url: &str) -> Result<String, Error> {
75 let (_, body) = self.get(url, 16 << 20, |_, _| {})?;
76 String::from_utf8(body).map_err(|e| Error::Answer(e.to_string()))
77 }
78
79 pub fn list(
81 &self,
82 sort: Sort,
83 section: &str,
84 start: u32,
85 count: u32,
86 ) -> Result<Vec<Mod>, Error> {
87 let (_, body) = self.get(
88 &crate::list_url(sort, section, start, count),
89 16 << 20,
90 |_, _| {},
91 )?;
92 crate::parse_list(&body)
93 }
94
95 pub fn search(&self, text: &str) -> Result<Vec<Mod>, Error> {
97 crate::parse_search(&self.page(&crate::search_url(text))?)
98 }
99
100 pub fn versions(&self, m: &Mod) -> Result<Vec<Version>, Error> {
102 let versions = crate::parse_versions(&self.page(&m.page())?, m.id);
103 if versions.is_empty() {
104 return Err(Error::Answer(format!("no downloads on {}", m.page())));
105 }
106 Ok(versions)
107 }
108
109 pub fn download(
111 &self,
112 version: &Version,
113 progress: impl FnMut(u64, Option<u64>),
114 ) -> Result<(String, Vec<u8>), Error> {
115 let limit = banished_nexus::install::MAX_PACKAGE * 2;
116 let (final_url, data) = self.get(&version.file, limit, progress)?;
117 Ok((crate::file_name(&final_url), data))
118 }
119
120 pub fn install(
123 &self,
124 version: &Version,
125 dir: &Path,
126 progress: impl FnMut(u64, Option<u64>),
127 ) -> Result<Vec<PathBuf>, Error> {
128 let (name, data) = self.download(version, progress)?;
129 let packages = banished_nexus::install::packages(&name, &data)
130 .map_err(|e| Error::Install(e.to_string()))?;
131 banished_nexus::native::install(dir, &packages).map_err(|e| Error::Install(e.to_string()))
132 }
133}