1use serde_json::json;
2
3use crate::error::HtbError;
4use crate::models::sherlock::{Sherlock, SherlockCategoriesResponse, SherlockCategory};
5use crate::models::{ActionResponse, Paginated};
6
7use super::HtbClient;
8
9pub struct SherlockApi<'a>(pub(crate) &'a HtbClient);
10
11impl SherlockApi<'_> {
12 pub async fn list(&self, page: u32, per_page: u32) -> Result<Paginated<Sherlock>, HtbError> {
13 self.0
14 .get(&format!(
15 "/api/v4/sherlocks?per_page={per_page}&page={page}"
16 ))
17 .await
18 }
19
20 pub async fn categories(&self) -> Result<Vec<SherlockCategory>, HtbError> {
21 let resp: SherlockCategoriesResponse =
22 self.0.get("/api/v4/sherlocks/categories/list").await?;
23 Ok(resp.info)
24 }
25
26 pub async fn info(&self, slug: &str) -> Result<Sherlock, HtbError> {
27 let encoded = super::encode_path(slug);
28 self.0.get(&format!("/api/v4/sherlocks/{encoded}")).await
29 }
30
31 pub async fn download_link(&self, sherlock_id: u64) -> Result<String, HtbError> {
32 let resp: crate::models::challenge::ChallengeDownloadResponse = self
33 .0
34 .get(&format!("/api/v4/sherlocks/{sherlock_id}/download_link"))
35 .await?;
36 Ok(resp.url)
37 }
38
39 pub async fn submit_flag(
40 &self,
41 sherlock_id: u64,
42 task_id: u64,
43 flag: &str,
44 ) -> Result<ActionResponse, HtbError> {
45 self.0
46 .post(
47 &format!("/api/v4/sherlocks/{sherlock_id}/tasks/{task_id}/flag"),
48 &json!({"flag": flag}),
49 )
50 .await
51 }
52}