1use crate::groups::Page;
2use crate::prelude::*;
3use uuid::Uuid;
4
5pub struct CorporationProjectsGroup<'a> {
7 pub(crate) esi: &'a Esi,
8}
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub enum ProjectStateFilter {
13 All,
15 Active,
17}
18
19impl std::fmt::Display for ProjectStateFilter {
20 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
21 f.write_str(match self {
22 ProjectStateFilter::All => "All",
23 ProjectStateFilter::Active => "Active",
24 })
25 }
26}
27
28#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
30pub enum ProjectState {
31 Unspecified,
33 Active,
35 Closed,
37 Completed,
39 Expired,
41 Deleted,
43 #[serde(other)]
45 Unrecognized,
46}
47
48#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
50pub struct ProjectProgress {
51 pub current: i64,
53 pub desired: i64,
55}
56
57#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
59pub struct ProjectReward {
60 pub initial: f64,
62 pub remaining: f64,
64}
65
66#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
68pub struct ProjectSummary {
69 pub id: Uuid,
71 pub last_modified: String,
73 pub name: String,
75 pub progress: ProjectProgress,
77 pub reward: Option<ProjectReward>,
79 pub state: ProjectState,
81}
82
83#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
85pub struct ProjectLocation {
86 pub solar_system_id: Option<i64>,
88 pub constellation_id: Option<i64>,
90 pub region_id: Option<i64>,
92}
93
94#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
96pub struct ProjectIdentity {
97 pub character_id: Option<i64>,
99 pub corporation_id: Option<i64>,
101 pub alliance_id: Option<i64>,
103 pub faction_id: Option<i64>,
105}
106
107#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
109pub struct TypeOrGroup {
110 pub type_id: Option<i64>,
112 pub group_id: Option<i64>,
114}
115
116#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
118pub struct DockingLocation {
119 pub structure_id: Option<i64>,
121 pub station_id: Option<i64>,
123}
124
125#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
127pub struct ProjectArchetype {
128 pub archetype_id: Option<i64>,
130}
131
132#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
134pub struct ProjectFaction {
135 pub faction_id: Option<i64>,
137}
138
139#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
141pub struct ProjectSignature {
142 pub signature_type_id: Option<i64>,
144}
145
146#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
148pub struct ProjectCorporation {
149 pub corporation_id: Option<i64>,
151}
152
153#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
155pub struct FwComplexConfig {
156 #[serde(default)]
158 pub archetypes: Vec<ProjectArchetype>,
159 #[serde(default)]
161 pub factions: Vec<ProjectFaction>,
162 #[serde(default)]
164 pub locations: Vec<ProjectLocation>,
165}
166
167#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
169pub struct ShipActivityConfig {
170 #[serde(default)]
172 pub identities: Vec<ProjectIdentity>,
173 #[serde(default)]
175 pub locations: Vec<ProjectLocation>,
176 #[serde(default)]
178 pub ships: Vec<TypeOrGroup>,
179}
180
181#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
183pub struct LocationsConfig {
184 #[serde(default)]
186 pub locations: Vec<ProjectLocation>,
187}
188
189#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
191pub struct DeliverItemConfig {
192 #[serde(default)]
194 pub docking_locations: Vec<DockingLocation>,
195 #[serde(default)]
197 pub items: Vec<TypeOrGroup>,
198 pub office_id: Option<i64>,
200}
201
202#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
204pub struct EarnLoyaltyPointConfig {
205 #[serde(default)]
207 pub corporations: Vec<ProjectCorporation>,
208}
209
210#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
212pub enum ManufactureOwner {
213 Any,
215 Corporation,
217 Character,
219 #[serde(other)]
221 Unrecognized,
222}
223
224#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
226pub struct ManufactureItemConfig {
227 #[serde(default)]
229 pub docking_locations: Vec<DockingLocation>,
230 #[serde(default)]
232 pub items: Vec<TypeOrGroup>,
233 pub owner: ManufactureOwner,
235}
236
237#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
239pub struct MineMaterialConfig {
240 #[serde(default)]
242 pub locations: Vec<ProjectLocation>,
243 #[serde(default)]
245 pub materials: Vec<TypeOrGroup>,
246}
247
248#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
250pub struct ScanSignatureConfig {
251 #[serde(default)]
253 pub locations: Vec<ProjectLocation>,
254 #[serde(default)]
256 pub signatures: Vec<ProjectSignature>,
257}
258
259#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
261pub enum ConflictType {
262 Any,
264 Pvp,
266 Pve,
268 #[serde(other)]
270 Unrecognized,
271}
272
273#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
275pub struct ShipInsuranceConfig {
276 pub conflict_type: ConflictType,
278 #[serde(default)]
280 pub identities: Vec<ProjectIdentity>,
281 #[serde(default)]
283 pub locations: Vec<ProjectLocation>,
284 pub reimburse_implants: bool,
286 #[serde(default)]
288 pub ships: Vec<TypeOrGroup>,
289}
290
291#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
293pub struct UnknownProjectConfig {
294 pub data: Option<serde_json::Value>,
296 #[serde(rename = "type")]
298 pub config_type: String,
299}
300
301#[derive(Debug, Clone, PartialEq, Default, Deserialize, Serialize)]
304pub struct ProjectConfiguration {
305 pub capture_fw_complex: Option<FwComplexConfig>,
307 pub damage_ship: Option<ShipActivityConfig>,
309 pub defend_fw_complex: Option<FwComplexConfig>,
311 pub deliver_item: Option<DeliverItemConfig>,
313 pub destroy_npc: Option<LocationsConfig>,
315 pub destroy_ship: Option<ShipActivityConfig>,
317 pub earn_loyalty_point: Option<EarnLoyaltyPointConfig>,
319 pub lost_ship: Option<ShipActivityConfig>,
321 pub manual: Option<serde_json::Value>,
323 pub manufacture_item: Option<ManufactureItemConfig>,
325 pub mine_material: Option<MineMaterialConfig>,
327 pub remote_boost_shield: Option<ShipActivityConfig>,
329 pub remote_repair_armor: Option<ShipActivityConfig>,
331 pub salvage_wreck: Option<LocationsConfig>,
333 pub scan_signature: Option<ScanSignatureConfig>,
335 pub ship_insurance: Option<ShipInsuranceConfig>,
337 pub unknown: Option<UnknownProjectConfig>,
339}
340
341#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
343pub struct ContributionRules {
344 pub participation_limit: Option<i64>,
346 pub reward_per_contribution: Option<f64>,
348 pub submission_limit: Option<i64>,
350 pub submission_multiplier: Option<f64>,
352}
353
354#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
356pub struct ProjectCreator {
357 pub id: i64,
359 pub name: String,
361}
362
363#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
365pub enum ProjectCareer {
366 Unspecified,
368 Explorer,
370 Industrialist,
372 Enforcer,
374 #[serde(rename = "Soldier of Fortune")]
376 SoldierOfFortune,
377 #[serde(other)]
379 Unrecognized,
380}
381
382#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
384pub struct ProjectDetails {
385 pub career: ProjectCareer,
387 pub created: String,
389 pub description: String,
391 pub expires: Option<String>,
393 pub finished: Option<String>,
395}
396
397#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
399pub struct Project {
400 pub configuration: ProjectConfiguration,
402 pub contribution: Option<ContributionRules>,
404 pub creator: ProjectCreator,
406 pub details: ProjectDetails,
408 pub id: Uuid,
410 pub last_modified: String,
412 pub name: String,
414 pub progress: ProjectProgress,
416 pub reward: Option<ProjectReward>,
418 pub state: ProjectState,
420}
421
422#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
424pub struct ProjectContribution {
425 pub contributed: i64,
427 pub last_modified: Option<String>,
429}
430
431#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
433pub struct ProjectContributor {
434 pub contributed: i64,
436 pub id: i64,
438 pub name: String,
440}
441
442impl CorporationProjectsGroup<'_> {
443 api_get!(
444 list,
447 "GetCorporationsProjectsListing",
448 RequestType::Authenticated,
449 Page<ProjectSummary>,
450 (corporation_id: i64) => "{corporation_id}";
451 Optional(after: String) => "after",
452 Optional(before: String) => "before",
453 Optional(limit: i64) => "limit",
454 Optional(state: ProjectStateFilter) => "state"
455 );
456
457 api_get!(
458 get_project,
460 "GetCorporationsProjectsDetail",
461 RequestType::Authenticated,
462 Project,
463 (corporation_id: i64) => "{corporation_id}",
464 (project_id: Uuid) => "{project_id}"
465 );
466
467 api_get!(
468 get_contribution,
470 "GetCorporationsProjectsContribution",
471 RequestType::Authenticated,
472 ProjectContribution,
473 (corporation_id: i64) => "{corporation_id}",
474 (project_id: Uuid) => "{project_id}",
475 (character_id: i64) => "{character_id}"
476 );
477
478 api_get!(
479 list_contributors,
484 "GetCorporationsProjectsContributors",
485 RequestType::Authenticated,
486 Page<ProjectContributor>,
487 (corporation_id: i64) => "{corporation_id}",
488 (project_id: Uuid) => "{project_id}";
489 Optional(after: String) => "after",
490 Optional(before: String) => "before",
491 Optional(limit: i64) => "limit"
492 );
493}
494
495#[cfg(test)]
496mod tests {
497 use super::*;
498
499 #[test]
500 fn test_parse_project_listing() {
501 let json = r#"{"cursor": {"after": "abc"}, "projects": [
502 {"id": "6f1d8a5e-0b7c-4d3e-9a2f-1c5b7e9d0a11", "last_modified": "t", "name": "n",
503 "progress": {"current": 5000000000, "desired": 9000000000},
504 "reward": {"initial": 1000.5, "remaining": 10.0}, "state": "Active"},
505 {"id": "6f1d8a5e-0b7c-4d3e-9a2f-1c5b7e9d0a12", "last_modified": "t", "name": "m",
506 "progress": {"current": 0, "desired": 1}, "state": "Archived"}]}"#;
507 let page: Page<ProjectSummary> = serde_json::from_str(json).unwrap();
508 assert_eq!(page.items[0].progress.current, 5_000_000_000);
509 assert_eq!(page.items[0].state, ProjectState::Active);
510 assert_eq!(page.items[1].state, ProjectState::Unrecognized);
511 assert_eq!(page.cursor.unwrap().after.as_deref(), Some("abc"));
512 }
513
514 #[test]
515 fn test_parse_project_with_each_configuration_shape() {
516 let base = |config: &str| {
517 format!(
518 r#"{{"configuration": {config},
519 "creator": {{"id": 2124758468, "name": "c"}},
520 "details": {{"career": "Soldier of Fortune", "created": "t", "description": "d"}},
521 "id": "6f1d8a5e-0b7c-4d3e-9a2f-1c5b7e9d0a11", "last_modified": "t", "name": "n",
522 "progress": {{"current": 1, "desired": 2}}, "state": "Closed"}}"#
523 )
524 };
525 let mining: Project = serde_json::from_str(&base(
526 r#"{"mine_material": {"locations": [{"region_id": 10000002}],
527 "materials": [{"group_id": 450}]}}"#,
528 ))
529 .unwrap();
530 assert_eq!(mining.details.career, ProjectCareer::SoldierOfFortune);
531 let cfg = mining.configuration.mine_material.unwrap();
532 assert_eq!(cfg.locations[0].region_id, Some(10000002));
533 assert_eq!(cfg.materials[0].group_id, Some(450));
534
535 let insurance: Project = serde_json::from_str(&base(
536 r#"{"ship_insurance": {"conflict_type": "Pvp", "reimburse_implants": true}}"#,
537 ))
538 .unwrap();
539 assert_eq!(
540 insurance
541 .configuration
542 .ship_insurance
543 .unwrap()
544 .conflict_type,
545 ConflictType::Pvp
546 );
547
548 let manual: Project = serde_json::from_str(&base(r#"{"manual": {}}"#)).unwrap();
549 assert!(manual.configuration.manual.is_some());
550 assert!(manual.configuration.mine_material.is_none());
551
552 let future: Project = serde_json::from_str(&base(r#"{"teleport_cat": {"x": 1}}"#)).unwrap();
553 assert_eq!(future.configuration, ProjectConfiguration::default());
554 }
555
556 #[test]
557 fn test_parse_contribution_and_contributors() {
558 let c: ProjectContribution = serde_json::from_str(r#"{"contributed": 7}"#).unwrap();
559 assert_eq!(c.last_modified, None);
560 let page: Page<ProjectContributor> = serde_json::from_str(
561 r#"{"contributors": [{"contributed": 3000000000, "id": 1, "name": "x"}]}"#,
562 )
563 .unwrap();
564 assert_eq!(page.items[0].contributed, 3_000_000_000);
565 assert!(page.cursor.is_none());
566 }
567
568 #[test]
569 fn test_state_filter_display() {
570 assert_eq!(ProjectStateFilter::Active.to_string(), "Active");
571 assert_eq!(ProjectStateFilter::All.to_string(), "All");
572 }
573}