Skip to main content

gitee_cli_rs/api/
gists.rs

1use super::client::Client;
2use crate::error::Result;
3use crate::models::Gist;
4
5pub struct Gists<'a> {
6    client: &'a Client,
7}
8
9pub struct CreateGist<'a> {
10    pub description: &'a str,
11    pub public: bool,
12    pub files: &'a [(String, String)],
13}
14
15pub struct UpdateGist<'a> {
16    pub files: &'a [(String, String)],
17    pub description: Option<&'a str>,
18}
19
20impl Gists<'_> {
21    pub(crate) fn new(client: &Client) -> Gists<'_> {
22        Gists { client }
23    }
24
25    pub fn list(&self, limit: usize) -> Result<Vec<Gist>> {
26        self.client.get_paged("/gists", &[], limit)
27    }
28
29    pub fn get(&self, id: &str) -> Result<Gist> {
30        self.client.get(&format!("/gists/{id}"), &[])
31    }
32
33    /// Gitee gist create uses Rails-style urlencoded nested fields:
34    /// `files[<name>][content]=<text>`, plus required `description` (1–30 chars)
35    /// and `public` sent as the string `"true"` or `"false"`.
36    pub fn create(&self, req: &CreateGist<'_>) -> Result<Gist> {
37        let mut pairs = vec![
38            ("description".to_string(), req.description.to_string()),
39            (
40                "public".to_string(),
41                Client::bool_str(req.public).to_string(),
42            ),
43        ];
44        push_file_fields(&mut pairs, req.files);
45        let form = Client::str_refs(&pairs);
46        self.client.post("/gists", &form)
47    }
48
49    pub fn update(&self, id: &str, req: &UpdateGist<'_>) -> Result<Gist> {
50        let mut pairs: Vec<(String, String)> = Vec::new();
51        if let Some(d) = req.description {
52            pairs.push(("description".to_string(), d.to_string()));
53        }
54        push_file_fields(&mut pairs, req.files);
55        let form = Client::str_refs(&pairs);
56        self.client.patch(&format!("/gists/{id}"), &form)
57    }
58
59    pub fn delete(&self, id: &str) -> Result<()> {
60        self.client.delete_ok(&format!("/gists/{id}"))
61    }
62}
63
64fn push_file_fields(pairs: &mut Vec<(String, String)>, files: &[(String, String)]) {
65    for (name, content) in files {
66        pairs.push((format!("files[{name}][content]"), content.clone()));
67    }
68}
69
70/// Gitee requires a non-empty `description` (1–30 chars). When `--desc` is
71/// omitted the CLI defaults to the first file name, truncated to fit the limit.
72pub fn truncate_description(desc: &str) -> String {
73    let trimmed = desc.trim();
74    if trimmed.is_empty() {
75        return "gist".to_string();
76    }
77    if trimmed.chars().count() <= 30 {
78        trimmed.to_string()
79    } else {
80        trimmed.chars().take(30).collect()
81    }
82}