modio/request/mods/
add_mod.rs

1use std::future::IntoFuture;
2use std::path::Path;
3
4use serde_derive::Serialize;
5
6use crate::client::Client;
7use crate::error;
8use crate::request::multipart::{Form, Part};
9use crate::request::{Output, RequestBuilder, Route};
10use crate::response::ResponseFuture;
11use crate::types::id::GameId;
12use crate::types::mods::{CommunityOptions, CreditOptions, MaturityOption, Mod};
13
14/// Add a mod.
15pub struct AddMod<'a> {
16    http: &'a Client,
17    game_id: GameId,
18    logo: Part,
19    fields: AddModFields<'a>,
20}
21
22#[derive(Serialize)]
23struct AddModFields<'a> {
24    name: &'a str,
25    summary: &'a str,
26    #[serde(skip_serializing_if = "Option::is_none")]
27    visibility: Option<u8>,
28    #[serde(skip_serializing_if = "Option::is_none")]
29    name_id: Option<&'a str>,
30    #[serde(skip_serializing_if = "Option::is_none")]
31    description: Option<&'a str>,
32    #[serde(skip_serializing_if = "Option::is_none")]
33    homepage_url: Option<&'a str>,
34    #[serde(skip_serializing_if = "Option::is_none")]
35    stock: Option<u32>,
36    #[serde(skip_serializing_if = "Option::is_none")]
37    maturity_option: Option<MaturityOption>,
38    #[serde(skip_serializing_if = "Option::is_none")]
39    community_options: Option<CommunityOptions>,
40    #[serde(skip_serializing_if = "Option::is_none")]
41    credit_options: Option<CreditOptions>,
42    #[serde(skip_serializing_if = "Option::is_none")]
43    metadata_blob: Option<&'a str>,
44    #[serde(skip_serializing_if = "Option::is_none")]
45    tags: Option<&'a [&'a str]>,
46}
47
48impl<'a> AddMod<'a> {
49    pub(crate) fn new(
50        http: &'a Client,
51        game_id: GameId,
52        name: &'a str,
53        summary: &'a str,
54        logo: impl AsRef<Path>,
55    ) -> Self {
56        let logo = Part::file(logo, "logo.png").mime(mime::IMAGE_STAR);
57
58        Self {
59            http,
60            game_id,
61            logo,
62            fields: AddModFields {
63                name,
64                summary,
65                visibility: None,
66                name_id: None,
67                description: None,
68                homepage_url: None,
69                stock: None,
70                maturity_option: None,
71                community_options: None,
72                credit_options: None,
73                metadata_blob: None,
74                tags: None,
75            },
76        }
77    }
78
79    pub const fn name_id(mut self, name_id: &'a str) -> Self {
80        self.fields.name_id = Some(name_id);
81        self
82    }
83
84    pub const fn description(mut self, description: &'a str) -> Self {
85        self.fields.description = Some(description);
86        self
87    }
88
89    pub const fn homepage_url(mut self, homepage_url: &'a str) -> Self {
90        self.fields.homepage_url = Some(homepage_url);
91        self
92    }
93
94    pub const fn stock(mut self, stock: u32) -> Self {
95        self.fields.stock = Some(stock);
96        self
97    }
98
99    pub const fn maturity_option(mut self, option: MaturityOption) -> Self {
100        self.fields.maturity_option = Some(option);
101        self
102    }
103
104    pub const fn community_options(mut self, options: CommunityOptions) -> Self {
105        self.fields.community_options = Some(options);
106        self
107    }
108
109    pub const fn credit_options(mut self, options: CreditOptions) -> Self {
110        self.fields.credit_options = Some(options);
111        self
112    }
113
114    pub const fn metadata_blob(mut self, metadata: &'a str) -> Self {
115        self.fields.metadata_blob = Some(metadata);
116        self
117    }
118
119    pub const fn tags(mut self, tags: &'a [&'a str]) -> Self {
120        self.fields.tags = Some(tags);
121        self
122    }
123
124    pub const fn visible(mut self, visible: bool) -> Self {
125        self.fields.visibility = Some(if visible { 1 } else { 0 });
126        self
127    }
128}
129
130impl IntoFuture for AddMod<'_> {
131    type Output = Output<Mod>;
132    type IntoFuture = ResponseFuture<Mod>;
133
134    fn into_future(self) -> Self::IntoFuture {
135        let route = Route::AddMod {
136            game_id: self.game_id,
137        };
138
139        let mut form = Form::new();
140
141        form = form.text("name", self.fields.name.to_owned());
142        form = form.text("summary", self.fields.summary.to_owned());
143        form = form.part("logo", self.logo);
144
145        if let Some(value) = self.fields.name_id {
146            form = form.text("name_id", value.to_owned());
147        }
148        if let Some(value) = self.fields.description {
149            form = form.text("description", value.to_owned());
150        }
151        if let Some(value) = self.fields.homepage_url {
152            form = form.text("homepage_url", value.to_owned());
153        }
154        if let Some(value) = self.fields.visibility {
155            form = form.text("visible", value.to_string());
156        }
157        if let Some(value) = self.fields.stock {
158            form = form.text("stock", value.to_string());
159        }
160        if let Some(value) = self.fields.maturity_option {
161            form = form.text("maturity_option", value.to_string());
162        }
163        if let Some(value) = self.fields.community_options {
164            form = form.text("community_options", value.to_string());
165        }
166        if let Some(value) = self.fields.credit_options {
167            form = form.text("credit_options", value.to_string());
168        }
169        if let Some(value) = self.fields.metadata_blob {
170            form = form.text("metadata_blob", value.to_owned());
171        }
172        if let Some(tags) = self.fields.tags {
173            for tag in tags {
174                form = form.text("tags[]", (*tag).to_owned());
175            }
176        }
177
178        form = match serde_json::to_vec(&self.fields) {
179            Ok(json) => form.part("input_json", Part::bytes(json.into())),
180            Err(err) => return ResponseFuture::failed(error::builder(err)),
181        };
182
183        match RequestBuilder::from_route(&route).multipart(form) {
184            Ok(req) => self.http.request(req),
185            Err(err) => ResponseFuture::failed(err),
186        }
187    }
188}