1use super::client::DiscourseClient;
2use super::error::http_error;
3use super::models::{CreatePostResponse, TopicResponse};
4use anyhow::{Context, Result, anyhow};
5use serde::{Deserialize, Serialize};
6use serde_json::Value;
7
8#[derive(Debug, Deserialize, Serialize, Clone)]
9pub struct PostInfo {
10 pub id: u64,
11 pub topic_id: u64,
12 #[serde(default)]
13 pub post_number: Option<u64>,
14 #[serde(default)]
15 pub raw: Option<String>,
16}
17
18impl DiscourseClient {
19 pub fn fetch_topic(&self, topic_id: u64, include_raw: bool) -> Result<TopicResponse> {
21 let path = if include_raw {
22 format!("/t/{}.json?include_raw=1", topic_id)
23 } else {
24 format!("/t/{}.json", topic_id)
25 };
26 let response = self.get(&path)?;
27 let status = response.status();
28 let text = response.text().context("reading topic response body")?;
29 if !status.is_success() {
30 return Err(http_error("topic request", status, &text));
31 }
32 let body: TopicResponse = serde_json::from_str(&text).context("parsing topic json")?;
33 Ok(body)
34 }
35
36 pub fn fetch_post_raw(&self, post_id: u64) -> Result<Option<String>> {
38 Ok(self.fetch_post(post_id)?.raw)
39 }
40
41 pub fn fetch_post(&self, post_id: u64) -> Result<PostInfo> {
43 let path = format!("/posts/{}.json?include_raw=1", post_id);
44 let response = self.get(&path)?;
45 let status = response.status();
46 let text = response.text().context("reading post response body")?;
47 if !status.is_success() {
48 return Err(http_error("post request", status, &text));
49 }
50 let info: PostInfo = serde_json::from_str(&text).context("parsing post response")?;
51 Ok(info)
52 }
53
54 pub fn delete_post(&self, post_id: u64) -> Result<()> {
56 let path = format!("/posts/{}.json", post_id);
57 let response = self.send_retrying(|| Ok(self.delete_builder(&path)?))?;
58 let status = response.status();
59 if !status.is_success() {
60 let text = response
61 .text()
62 .unwrap_or_else(|_| "<failed to read response body>".to_string());
63 return Err(http_error("delete post request", status, &text));
64 }
65 Ok(())
66 }
67
68 pub fn move_posts(
74 &self,
75 source_topic_id: u64,
76 post_ids: &[u64],
77 dest_topic_id: u64,
78 ) -> Result<String> {
79 if post_ids.is_empty() {
80 return Err(anyhow!("no post IDs supplied to move"));
81 }
82 let dest = dest_topic_id.to_string();
83 let path = format!("/t/{}/move-posts.json", source_topic_id);
84 let mut payload: Vec<(String, String)> = Vec::new();
85 payload.push(("destination_topic_id".to_string(), dest.clone()));
86 for id in post_ids {
87 payload.push(("post_ids[]".to_string(), id.to_string()));
88 }
89 let response = self.send_retrying(|| Ok(self.post(&path)?.form(&payload)))?;
90 let status = response.status();
91 let text = response.text().context("reading move-posts response")?;
92 if !status.is_success() {
93 return Err(http_error("move posts request", status, &text));
94 }
95 let value: Value =
96 serde_json::from_str(&text).context("parsing move-posts response")?;
97 let url = value
98 .get("url")
99 .and_then(|v| v.as_str())
100 .map(|s| s.to_string())
101 .unwrap_or_else(|| format!("/t/{}", dest));
102 Ok(url)
103 }
104
105 pub fn update_post(&self, post_id: u64, raw: &str) -> Result<()> {
107 let path = format!("/posts/{}.json", post_id);
108 let payload = [("post[raw]", raw)];
109 let response = self.send_retrying(|| Ok(self.put(&path)?.form(&payload)))?;
110 let status = response.status();
111 if !status.is_success() {
112 let text = response
113 .text()
114 .unwrap_or_else(|_| "<failed to read response body>".to_string());
115 return Err(http_error("update post request", status, &text));
116 }
117 Ok(())
118 }
119
120 pub fn create_topic(&self, category_id: u64, title: &str, raw: &str) -> Result<u64> {
122 let category = category_id.to_string();
123 let payload = [("title", title), ("raw", raw), ("category", &category)];
124 let response = self.send_retrying(|| Ok(self.post("/posts.json")?.form(&payload)))?;
125 let status = response.status();
126 let text = response.text().context("reading create response body")?;
127 if !status.is_success() {
128 return Err(http_error("create topic request", status, &text));
129 }
130 let body: CreatePostResponse =
131 serde_json::from_str(&text).context("parsing create topic response")?;
132 Ok(body.topic_id)
133 }
134
135 pub fn create_post(&self, topic_id: u64, raw: &str) -> Result<u64> {
137 let topic = topic_id.to_string();
138 let payload = [("topic_id", topic.as_str()), ("raw", raw)];
139 let response = self.send_retrying(|| Ok(self.post("/posts.json")?.form(&payload)))?;
140 let status = response.status();
141 let text = response.text().context("reading create response body")?;
142 if !status.is_success() {
143 return Err(http_error("create post request", status, &text));
144 }
145 let body: CreatePostResponse =
146 serde_json::from_str(&text).context("parsing create post response")?;
147 Ok(body.id)
148 }
149}