1use crate::prelude::*;
2
3pub struct CosmeticsGroup<'a> {
5 pub(crate) esi: &'a Esi,
6}
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize)]
10#[serde(rename_all = "snake_case")]
11pub enum PatternBlendMode {
12 Normal,
14 Subtract,
16 Exclusion,
18 Nested,
20 NestedInverted,
22 #[serde(other)]
24 Unrecognized,
25}
26
27#[derive(Debug, Clone, Copy, PartialEq, Deserialize)]
29#[allow(missing_docs)]
30pub struct Vector3 {
31 pub x: f64,
32 pub y: f64,
33 pub z: f64,
34}
35
36#[derive(Debug, Clone, Copy, PartialEq, Deserialize)]
38#[allow(missing_docs)]
39pub struct Vector4 {
40 pub w: f64,
41 pub x: f64,
42 pub y: f64,
43 pub z: f64,
44}
45
46#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize)]
48#[allow(missing_docs)]
49pub struct PatternProjection {
50 pub slot1: bool,
51 pub slot2: bool,
52 pub slot3: bool,
53 pub slot4: bool,
54}
55
56#[derive(Debug, Clone, Copy, PartialEq, Deserialize)]
58#[allow(missing_docs)]
59pub struct PatternTransform {
60 pub position: Vector3,
61 pub rotation: Vector4,
62 pub scaling: Vector3,
63}
64
65#[derive(Debug, Clone, Copy, PartialEq, Deserialize)]
67#[allow(missing_docs)]
68pub struct PatternConfiguration {
69 pub mirrored: bool,
70 pub projection: PatternProjection,
71 pub transform: PatternTransform,
72}
73
74#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize)]
76#[allow(missing_docs)]
77pub struct NanocoatingComponent {
78 pub id: i64,
79}
80
81#[derive(Debug, Clone, Copy, PartialEq, Deserialize)]
83#[allow(missing_docs)]
84pub struct PatternComponent {
85 pub configuration: PatternConfiguration,
86 pub id: i64,
87}
88
89#[derive(Debug, Clone, Copy, PartialEq, Deserialize)]
91#[serde(untagged)]
92#[allow(missing_docs)]
93pub enum SlotConfiguration {
94 Nanocoating { nanocoating: NanocoatingComponent },
95 Pattern { pattern: PatternComponent },
96}
97
98#[derive(Debug, Clone, Copy, PartialEq, Deserialize)]
100#[allow(missing_docs)]
101pub struct SkinrSlot {
102 pub configuration: SlotConfiguration,
103 pub id: i64,
104}
105
106#[derive(Debug, Clone, PartialEq, Deserialize)]
108#[allow(missing_docs)]
109pub struct SkinrLayout {
110 pub pattern_blend_mode: PatternBlendMode,
111 pub slots: Vec<SkinrSlot>,
112}
113
114#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize)]
116#[allow(missing_docs)]
117pub struct SkinrTier {
118 pub level: i64,
119}
120
121#[derive(Debug, Clone, PartialEq, Deserialize)]
123#[allow(missing_docs)]
124pub struct Skinr {
125 pub creator_id: i64,
127 pub id: String,
128 pub layout: SkinrLayout,
129 pub line: Option<String>,
130 pub name: String,
131 pub ship_type_id: i64,
132 pub tier: SkinrTier,
133}
134
135impl CosmeticsGroup<'_> {
136 api_get!(
137 get_skinr,
139 "GetCosmeticsSkinr",
140 RequestType::Public,
141 Skinr,
142 (skinr_id: &str) => "{skinr_id}"
143 );
144}
145
146#[cfg(test)]
147mod tests {
148 use super::{Skinr, SlotConfiguration};
149 use serde_json::json;
150
151 #[test]
152 fn test_parse_skinr_with_both_slot_kinds() {
153 let vec3 = json!({"x": 0.0, "y": 0.0, "z": 0.0});
154 let value = json!({
155 "creator_id": 2_112_000_000_i64,
156 "id": "abc",
157 "name": "Design",
158 "ship_type_id": 587,
159 "tier": {"level": 2},
160 "layout": {
161 "pattern_blend_mode": "nested_inverted",
162 "slots": [
163 {"id": 1, "configuration": {"nanocoating": {"id": 10}}},
164 {"id": 2, "configuration": {"pattern": {
165 "id": 20,
166 "configuration": {
167 "mirrored": false,
168 "projection": {
169 "slot1": true, "slot2": false, "slot3": false, "slot4": false
170 },
171 "transform": {
172 "position": vec3,
173 "scaling": vec3,
174 "rotation": {"w": 1.0, "x": 0.0, "y": 0.0, "z": 0.0}
175 }
176 }
177 }}}
178 ]
179 }
180 });
181 let skinr: Skinr = serde_json::from_value(value).unwrap();
182 assert!(skinr.line.is_none());
183 assert!(matches!(
184 skinr.layout.slots[0].configuration,
185 SlotConfiguration::Nanocoating { .. }
186 ));
187 assert!(matches!(
188 skinr.layout.slots[1].configuration,
189 SlotConfiguration::Pattern { .. }
190 ));
191 }
192}
193
194#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
196pub struct CharacterSkinrLicense {
197 pub activated: bool,
199 pub skinr_id: String,
201 pub unactivated: i64,
203}
204
205#[derive(Debug, Deserialize)]
206struct SkinrLicenses {
207 licenses: Vec<CharacterSkinrLicense>,
208}
209
210#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
212#[serde(rename_all = "snake_case")]
213pub enum SkinrComponentType {
214 Nanocoating,
216 Pattern,
218 #[serde(other)]
220 Unrecognized,
221}
222
223#[derive(Debug, Clone, Default, PartialEq, Eq, Deserialize, Serialize)]
225pub struct ComponentRuns {
226 pub remaining: Option<i64>,
228 pub unlimited: Option<bool>,
230}
231
232#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
234#[allow(missing_docs)]
235pub struct SkinrComponentLicense {
236 pub component_id: i64,
237 pub runs: ComponentRuns,
238 #[serde(rename = "type")]
239 pub component_type: SkinrComponentType,
240}
241
242#[derive(Debug, Deserialize)]
243struct SkinrComponentLicenses {
244 licenses: Vec<SkinrComponentLicense>,
245}
246
247impl CosmeticsGroup<'_> {
248 pub async fn get_character_skinr(
250 &self,
251 character_id: i64,
252 ) -> EsiResult<Vec<CharacterSkinrLicense>> {
253 let path = self
254 .esi
255 .get_endpoint_for_op_id("GetCharactersCosmeticsSkinr")?
256 .replace("{character_id}", &character_id.to_string());
257 let wrapper: SkinrLicenses = self
258 .esi
259 .query("GET", RequestType::Authenticated, &path, None, None)
260 .await?;
261 Ok(wrapper.licenses)
262 }
263
264 pub async fn get_character_skinr_components(
266 &self,
267 character_id: i64,
268 ) -> EsiResult<Vec<SkinrComponentLicense>> {
269 let path = self
270 .esi
271 .get_endpoint_for_op_id("GetCharactersCosmeticsSkinrComponents")?
272 .replace("{character_id}", &character_id.to_string());
273 let wrapper: SkinrComponentLicenses = self
274 .esi
275 .query("GET", RequestType::Authenticated, &path, None, None)
276 .await?;
277 Ok(wrapper.licenses)
278 }
279}
280
281#[cfg(test)]
282mod license_tests {
283 use super::*;
284
285 #[test]
286 fn test_parse_component_licenses() {
287 let w: SkinrComponentLicenses = serde_json::from_str(
288 r#"{"licenses":[{"component_id":1,"type":"pattern","runs":{"unlimited":true}},
289 {"component_id":2,"type":"x","runs":{"remaining":3}}]}"#,
290 )
291 .unwrap();
292 assert_eq!(w.licenses[0].runs.unlimited, Some(true));
293 assert_eq!(w.licenses[1].runs.remaining, Some(3));
294 assert_eq!(
295 w.licenses[1].component_type,
296 SkinrComponentType::Unrecognized
297 );
298 }
299}