Skip to main content

esi_openapi/groups/
corporation_projects.rs

1use crate::groups::Page;
2use crate::prelude::*;
3use uuid::Uuid;
4
5/// Endpoints for Corporation Projects
6pub struct CorporationProjectsGroup<'a> {
7    pub(crate) esi: &'a Esi,
8}
9
10/// Which projects to list.
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub enum ProjectStateFilter {
13    /// All projects.
14    All,
15    /// Only active projects.
16    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/// The state of a corporation project.
29#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
30pub enum ProjectState {
31    /// Not specified.
32    Unspecified,
33    /// Open.
34    Active,
35    /// Closed.
36    Closed,
37    /// Completed.
38    Completed,
39    /// Expired.
40    Expired,
41    /// Deleted.
42    Deleted,
43    /// A value this version of the crate does not know about.
44    #[serde(other)]
45    Unrecognized,
46}
47
48/// Progress of a project.
49#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
50pub struct ProjectProgress {
51    /// Current progress.
52    pub current: i64,
53    /// Progress needed to complete the project.
54    pub desired: i64,
55}
56
57/// Reward of a project, in ISK.
58#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
59pub struct ProjectReward {
60    /// Initial reward.
61    pub initial: f64,
62    /// Reward left.
63    pub remaining: f64,
64}
65
66/// A project in the project listing.
67#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
68pub struct ProjectSummary {
69    /// ID of the project.
70    pub id: Uuid,
71    /// When the project was last modified.
72    pub last_modified: String,
73    /// Name of the project.
74    pub name: String,
75    /// Progress of the project.
76    pub progress: ProjectProgress,
77    /// Reward of the project.
78    pub reward: Option<ProjectReward>,
79    /// State of the project.
80    pub state: ProjectState,
81}
82
83/// A place a project refers to. Exactly one of the fields is set.
84#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
85pub struct ProjectLocation {
86    /// A solar system.
87    pub solar_system_id: Option<i64>,
88    /// A constellation.
89    pub constellation_id: Option<i64>,
90    /// A region.
91    pub region_id: Option<i64>,
92}
93
94/// An entity a project refers to. Exactly one of the fields is set.
95#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
96pub struct ProjectIdentity {
97    /// A character.
98    pub character_id: Option<i64>,
99    /// A corporation.
100    pub corporation_id: Option<i64>,
101    /// An alliance.
102    pub alliance_id: Option<i64>,
103    /// A faction.
104    pub faction_id: Option<i64>,
105}
106
107/// A type or a group of types. Exactly one of the fields is set.
108#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
109pub struct TypeOrGroup {
110    /// A type.
111    pub type_id: Option<i64>,
112    /// A group of types.
113    pub group_id: Option<i64>,
114}
115
116/// A place to dock. Exactly one of the fields is set.
117#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
118pub struct DockingLocation {
119    /// A structure.
120    pub structure_id: Option<i64>,
121    /// A station.
122    pub station_id: Option<i64>,
123}
124
125/// An archetype of a faction warfare complex.
126#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
127pub struct ProjectArchetype {
128    /// ID of the archetype.
129    pub archetype_id: Option<i64>,
130}
131
132/// A faction a project refers to.
133#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
134pub struct ProjectFaction {
135    /// ID of the faction.
136    pub faction_id: Option<i64>,
137}
138
139/// A signature type a project refers to.
140#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
141pub struct ProjectSignature {
142    /// ID of the signature type.
143    pub signature_type_id: Option<i64>,
144}
145
146/// A corporation a project refers to.
147#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
148pub struct ProjectCorporation {
149    /// ID of the corporation.
150    pub corporation_id: Option<i64>,
151}
152
153/// Configuration of the faction warfare complex projects.
154#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
155pub struct FwComplexConfig {
156    /// Archetypes of the complexes.
157    #[serde(default)]
158    pub archetypes: Vec<ProjectArchetype>,
159    /// Factions involved.
160    #[serde(default)]
161    pub factions: Vec<ProjectFaction>,
162    /// Where the complexes are.
163    #[serde(default)]
164    pub locations: Vec<ProjectLocation>,
165}
166
167/// Configuration of projects that involve ships and other pilots.
168#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
169pub struct ShipActivityConfig {
170    /// Entities involved.
171    #[serde(default)]
172    pub identities: Vec<ProjectIdentity>,
173    /// Where it happens.
174    #[serde(default)]
175    pub locations: Vec<ProjectLocation>,
176    /// Ships involved.
177    #[serde(default)]
178    pub ships: Vec<TypeOrGroup>,
179}
180
181/// Configuration of projects that only refer to locations.
182#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
183pub struct LocationsConfig {
184    /// Where it happens.
185    #[serde(default)]
186    pub locations: Vec<ProjectLocation>,
187}
188
189/// Configuration of an item delivery project.
190#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
191pub struct DeliverItemConfig {
192    /// Where the items can be delivered.
193    #[serde(default)]
194    pub docking_locations: Vec<DockingLocation>,
195    /// Items to deliver.
196    #[serde(default)]
197    pub items: Vec<TypeOrGroup>,
198    /// Office to deliver to.
199    pub office_id: Option<i64>,
200}
201
202/// Configuration of a loyalty point project.
203#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
204pub struct EarnLoyaltyPointConfig {
205    /// Corporations whose loyalty points count.
206    #[serde(default)]
207    pub corporations: Vec<ProjectCorporation>,
208}
209
210/// Who must own the items of a manufacturing project.
211#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
212pub enum ManufactureOwner {
213    /// Anyone.
214    Any,
215    /// A corporation.
216    Corporation,
217    /// A character.
218    Character,
219    /// A value this version of the crate does not know about.
220    #[serde(other)]
221    Unrecognized,
222}
223
224/// Configuration of a manufacturing project.
225#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
226pub struct ManufactureItemConfig {
227    /// Where the items can be delivered.
228    #[serde(default)]
229    pub docking_locations: Vec<DockingLocation>,
230    /// Items to manufacture.
231    #[serde(default)]
232    pub items: Vec<TypeOrGroup>,
233    /// Who must own the items.
234    pub owner: ManufactureOwner,
235}
236
237/// Configuration of a mining project.
238#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
239pub struct MineMaterialConfig {
240    /// Where to mine.
241    #[serde(default)]
242    pub locations: Vec<ProjectLocation>,
243    /// Materials to mine.
244    #[serde(default)]
245    pub materials: Vec<TypeOrGroup>,
246}
247
248/// Configuration of a signature scanning project.
249#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
250pub struct ScanSignatureConfig {
251    /// Where to scan.
252    #[serde(default)]
253    pub locations: Vec<ProjectLocation>,
254    /// Signature types to scan.
255    #[serde(default)]
256    pub signatures: Vec<ProjectSignature>,
257}
258
259/// The kind of conflict a ship insurance project covers.
260#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
261pub enum ConflictType {
262    /// Any conflict.
263    Any,
264    /// Player versus player.
265    Pvp,
266    /// Player versus environment.
267    Pve,
268    /// A value this version of the crate does not know about.
269    #[serde(other)]
270    Unrecognized,
271}
272
273/// Configuration of a ship insurance project.
274#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
275pub struct ShipInsuranceConfig {
276    /// Kind of conflict covered.
277    pub conflict_type: ConflictType,
278    /// Entities involved.
279    #[serde(default)]
280    pub identities: Vec<ProjectIdentity>,
281    /// Where it applies.
282    #[serde(default)]
283    pub locations: Vec<ProjectLocation>,
284    /// Whether implants are reimbursed.
285    pub reimburse_implants: bool,
286    /// Ships covered.
287    #[serde(default)]
288    pub ships: Vec<TypeOrGroup>,
289}
290
291/// A project type this version of the crate does not know about.
292#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
293pub struct UnknownProjectConfig {
294    /// Raw data of the configuration.
295    pub data: Option<serde_json::Value>,
296    /// Name of the project type.
297    #[serde(rename = "type")]
298    pub config_type: String,
299}
300
301/// The configuration of a project. Exactly one of the fields is set, depending
302/// on the project type; the fields that are not set are `None`.
303#[derive(Debug, Clone, PartialEq, Default, Deserialize, Serialize)]
304pub struct ProjectConfiguration {
305    /// Capture a faction warfare complex.
306    pub capture_fw_complex: Option<FwComplexConfig>,
307    /// Damage ships.
308    pub damage_ship: Option<ShipActivityConfig>,
309    /// Defend a faction warfare complex.
310    pub defend_fw_complex: Option<FwComplexConfig>,
311    /// Deliver items.
312    pub deliver_item: Option<DeliverItemConfig>,
313    /// Destroy NPCs.
314    pub destroy_npc: Option<LocationsConfig>,
315    /// Destroy ships.
316    pub destroy_ship: Option<ShipActivityConfig>,
317    /// Earn loyalty points.
318    pub earn_loyalty_point: Option<EarnLoyaltyPointConfig>,
319    /// Lose ships.
320    pub lost_ship: Option<ShipActivityConfig>,
321    /// Manually tracked project (no data).
322    pub manual: Option<serde_json::Value>,
323    /// Manufacture items.
324    pub manufacture_item: Option<ManufactureItemConfig>,
325    /// Mine materials.
326    pub mine_material: Option<MineMaterialConfig>,
327    /// Remote boost shields.
328    pub remote_boost_shield: Option<ShipActivityConfig>,
329    /// Remote repair armor.
330    pub remote_repair_armor: Option<ShipActivityConfig>,
331    /// Salvage wrecks.
332    pub salvage_wreck: Option<LocationsConfig>,
333    /// Scan signatures.
334    pub scan_signature: Option<ScanSignatureConfig>,
335    /// Insure ships.
336    pub ship_insurance: Option<ShipInsuranceConfig>,
337    /// A project type this version of the crate does not know about.
338    pub unknown: Option<UnknownProjectConfig>,
339}
340
341/// Limits and rewards for contributions to a project.
342#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
343pub struct ContributionRules {
344    /// Maximum number of participants.
345    pub participation_limit: Option<i64>,
346    /// ISK paid per contribution.
347    pub reward_per_contribution: Option<f64>,
348    /// Maximum number of submissions per participant.
349    pub submission_limit: Option<i64>,
350    /// Multiplier applied to submissions.
351    pub submission_multiplier: Option<f64>,
352}
353
354/// The character that created a project.
355#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
356pub struct ProjectCreator {
357    /// Character ID of the creator.
358    pub id: i64,
359    /// Name of the creator.
360    pub name: String,
361}
362
363/// The career a project belongs to.
364#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
365pub enum ProjectCareer {
366    /// Not specified.
367    Unspecified,
368    /// Explorer.
369    Explorer,
370    /// Industrialist.
371    Industrialist,
372    /// Enforcer.
373    Enforcer,
374    /// Soldier of Fortune.
375    #[serde(rename = "Soldier of Fortune")]
376    SoldierOfFortune,
377    /// A value this version of the crate does not know about.
378    #[serde(other)]
379    Unrecognized,
380}
381
382/// General details of a project.
383#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
384pub struct ProjectDetails {
385    /// Career of the project.
386    pub career: ProjectCareer,
387    /// When the project was created.
388    pub created: String,
389    /// Description of the project.
390    pub description: String,
391    /// When the project expires.
392    pub expires: Option<String>,
393    /// When the project finished.
394    pub finished: Option<String>,
395}
396
397/// A corporation project.
398#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
399pub struct Project {
400    /// Configuration of the project.
401    pub configuration: ProjectConfiguration,
402    /// Contribution rules.
403    pub contribution: Option<ContributionRules>,
404    /// Creator of the project.
405    pub creator: ProjectCreator,
406    /// General details.
407    pub details: ProjectDetails,
408    /// ID of the project.
409    pub id: Uuid,
410    /// When the project was last modified.
411    pub last_modified: String,
412    /// Name of the project.
413    pub name: String,
414    /// Progress of the project.
415    pub progress: ProjectProgress,
416    /// Reward of the project.
417    pub reward: Option<ProjectReward>,
418    /// State of the project.
419    pub state: ProjectState,
420}
421
422/// What a character contributed to a project.
423#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
424pub struct ProjectContribution {
425    /// Amount contributed.
426    pub contributed: i64,
427    /// When the contribution was last modified.
428    pub last_modified: Option<String>,
429}
430
431/// A contributor to a project.
432#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
433pub struct ProjectContributor {
434    /// Amount contributed.
435    pub contributed: i64,
436    /// Character ID of the contributor.
437    pub id: i64,
438    /// Name of the contributor.
439    pub name: String,
440}
441
442impl CorporationProjectsGroup<'_> {
443    api_get!(
444        /// List the projects of a corporation. Use the cursor of the returned
445        /// page as `after` or `before` to walk through the list.
446        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 the details of a project of a corporation.
459        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 what a character contributed to a project.
469        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 the contributors to a project. Use the cursor of the returned
480        /// page as `after` or `before` to walk through the list.
481        ///
482        /// Requires the Project Manager role.
483        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}