Skip to main content

dpm_interchange/mspdi/
report.rs

1//! Per-item interchange reports; they describe a candidate or export and never change state.
2
3use dpm_model::{DependencyId, DependencyKind, Key, WorkItemId, WorkKind, WorkStatus};
4use serde::{Deserialize, Serialize};
5
6/// One field-level observation about a mapped value.
7#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
8pub struct Finding {
9    /// Source or DPM field the observation concerns.
10    pub field: String,
11    /// What was approximated, rejected or omitted, and why.
12    pub detail: String,
13}
14
15impl Finding {
16    pub(crate) fn new(field: &str, detail: impl Into<String>) -> Self {
17        Self {
18            field: field.into(),
19            detail: detail.into(),
20        }
21    }
22}
23
24/// One local field that the candidate changes on existing work.
25#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
26pub struct FieldChange {
27    /// DPM field: `title`, `order`, `objective`, `kind`, `parent`, `priority`, `estimate` or
28    /// `calendar`.
29    pub field: String,
30    /// Local value before the import.
31    pub before: String,
32    /// Value in the candidate.
33    pub after: String,
34}
35
36/// One field that the candidate changes on an existing local dependency.
37#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
38pub struct DependencyChange {
39    /// Stable dependency identity.
40    pub dependency: DependencyId,
41    /// Local relation, as `KIND PREDECESSOR -> SUCCESSOR` with work keys.
42    pub relation: String,
43    /// Dependency field; an import changes only `lag` and `lag_basis`.
44    pub field: String,
45    /// Local value before the import.
46    pub before: String,
47    /// Value in the candidate.
48    pub after: String,
49}
50
51/// Identity of the source project named by the document.
52#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
53pub struct SourceSummary {
54    /// Document format; always `mspdi`.
55    pub format: String,
56    /// Source project GUID, when the document records one.
57    pub project_guid: Option<String>,
58    /// Source project `Name`, or its `Title` when the document has no name, when present.
59    pub name: Option<String>,
60    /// What an import carries: plan structure, never calendar dates.
61    pub scope: String,
62}
63
64/// Local work that a source task maps to in the candidate.
65#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
66pub struct WorkReference {
67    /// Stable work identity: the task GUID, or an identity derived from the project GUID and UID,
68    /// or from the target project, key prefix and UID; the item's `identity` finding says which.
69    pub id: WorkItemId,
70    /// Work key; existing keys are kept, new work receives `PREFIX-UID`.
71    pub key: Key,
72    /// Mapped work kind.
73    pub kind: WorkKind,
74    /// Lifecycle in the candidate; imports never advance it.
75    pub status: WorkStatus,
76}
77
78/// What the candidate does with one source task.
79#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
80pub enum ItemOutcome {
81    /// New work is proposed.
82    Created,
83    /// Existing work with the same identity changes.
84    Updated,
85    /// Existing work with the same identity already matches.
86    Unchanged,
87    /// The task is not imported; `rejected` gives the reason.
88    Skipped,
89}
90
91/// Mapping report for one source task.
92#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
93pub struct ItemReport {
94    /// Source task UID, unique within the document only.
95    pub uid: i64,
96    /// Source task GUID, when present.
97    pub guid: Option<String>,
98    /// Source task name.
99    pub name: String,
100    /// Candidate action.
101    pub outcome: ItemOutcome,
102    /// Local work in the candidate; absent when skipped.
103    pub work: Option<WorkReference>,
104    /// Source fields carried over exactly.
105    pub preserved: Vec<String>,
106    /// Source fields carried over with a documented loss of precision or meaning, and defaults
107    /// applied to new work where the source omits a field.
108    pub approximated: Vec<Finding>,
109    /// Source data present in the document that the candidate does not carry.
110    pub rejected: Vec<Finding>,
111    /// Fields the source omits or leaves empty, so existing work keeps its local value.
112    pub kept: Vec<String>,
113    /// Every local field the candidate changes on existing work; empty unless `Updated`.
114    pub changes: Vec<FieldChange>,
115}
116
117/// How one source `PredecessorLink` maps to dependencies.
118#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
119pub enum LinkOutcome {
120    /// Relation and lag carried over exactly, and every local dependency it updates already
121    /// matches.
122    Preserved,
123    /// Relation and lag carried over exactly, but a local dependency it updates changes; `changes`
124    /// gives before and after values.
125    Changed,
126    /// Carried over with an approximated lag; `changes` still lists any local dependency change.
127    Approximated,
128    /// Not carried over; `notes` gives the reason.
129    Rejected,
130}
131
132/// Mapping report for one source `PredecessorLink`.
133#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
134pub struct LinkReport {
135    /// Predecessor task UID, when the link names one.
136    pub predecessor_uid: Option<i64>,
137    /// Successor task UID: the task that holds the link.
138    pub successor_uid: i64,
139    /// Relation abbreviation, or the raw type code when unsupported.
140    pub relation: Option<String>,
141    /// Source lag in tenths of a minute.
142    pub link_lag: i64,
143    /// Source lag format code.
144    pub lag_format: Option<u32>,
145    /// Candidate action.
146    pub outcome: LinkOutcome,
147    /// Expansion, merge, approximation or rejection details.
148    pub notes: Vec<String>,
149    /// Every field the candidate changes on an existing local dependency this link maps to; empty
150    /// when each one already matches.
151    #[serde(default)]
152    pub changes: Vec<DependencyChange>,
153    /// Candidate dependencies carrying this link.
154    pub dependencies: Vec<DependencyId>,
155}
156
157/// A local dependency between imported work that the source no longer contains.
158#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
159pub struct RemovedDependency {
160    /// Stable dependency identity.
161    pub id: DependencyId,
162    /// Predecessor work key.
163    pub predecessor: Key,
164    /// Successor work key.
165    pub successor: Key,
166    /// Relation kind.
167    pub kind: DependencyKind,
168    /// Local lag in hours.
169    pub lag_hours: f64,
170}
171
172/// What the candidate does with one source calendar.
173#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
174pub enum CalendarOutcome {
175    /// A new workspace calendar definition is proposed.
176    Created,
177    /// A workspace calendar of the same name changes to the source definition.
178    Updated,
179    /// A workspace calendar of the same name already has the source definition.
180    Unchanged,
181    /// The source calendar equals a built-in calendar (`standard` or `always`), which it maps to.
182    BuiltIn,
183    /// The calendar is not imported; `rejected` gives the reason.
184    Skipped,
185}
186
187/// Mapping report for one source `<Calendar>`.
188#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
189pub struct CalendarReport {
190    /// Source calendar UID, unique within the document only.
191    pub uid: i64,
192    /// Source calendar name.
193    pub name: String,
194    /// Workspace calendar name in the candidate; absent when skipped.
195    pub calendar: Option<String>,
196    /// Candidate action.
197    pub outcome: CalendarOutcome,
198    /// Source values carried with a documented loss.
199    pub approximated: Vec<Finding>,
200    /// Source data the candidate does not carry, such as recurring exceptions.
201    pub rejected: Vec<Finding>,
202    /// Existing work that follows an `Updated` calendar, whose schedule the update changes
203    /// whether or not the import touches it.
204    #[serde(default, skip_serializing_if = "Vec::is_empty")]
205    pub affected_work: Vec<Key>,
206}
207
208/// Full import report returned with the candidate and its reviewed preview.
209#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
210pub struct ImportReport {
211    /// Source document identity.
212    pub source: SourceSummary,
213    /// Project receiving the imported work.
214    pub target_project: Key,
215    /// One entry per source task, in document order.
216    pub items: Vec<ItemReport>,
217    /// One entry per source link, in document order.
218    pub links: Vec<LinkReport>,
219    /// Local dependencies between imported work that the candidate removes because the source
220    /// omits them.
221    pub removed_dependencies: Vec<RemovedDependency>,
222    /// One entry per source calendar when calendars are imported with a time zone; empty and
223    /// omitted otherwise.
224    #[serde(default, skip_serializing_if = "Vec::is_empty")]
225    pub calendars: Vec<CalendarReport>,
226    /// Document-level data the candidate does not carry, such as resources, and calendars when no
227    /// time zone is given.
228    pub rejected: Vec<Finding>,
229    /// Work in the target project that the source does not name; the candidate keeps it unchanged.
230    pub retained: Vec<Key>,
231}
232
233/// Export report for one work item.
234#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
235pub struct ItemExportReport {
236    /// Work key.
237    pub key: Key,
238    /// Written task UID; unique within this file only.
239    pub uid: u32,
240    /// Written task GUID: the stable work identity.
241    pub guid: String,
242    /// Fields written exactly.
243    pub preserved: Vec<String>,
244    /// Fields written with a documented loss of precision.
245    pub approximated: Vec<Finding>,
246    /// DPM data with no MSPDI representation in the supported subset.
247    pub omitted: Vec<Finding>,
248}
249
250/// Export report for one dependency touching the exported project.
251#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
252pub struct DependencyExportReport {
253    /// Stable dependency identity.
254    pub id: DependencyId,
255    /// Whether the relation is written.
256    pub written: bool,
257    /// DPM attributes with no MSPDI representation, or the reason it is not written.
258    pub notes: Vec<String>,
259}
260
261/// Full export report returned with the document.
262#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
263pub struct ExportReport {
264    /// Exported project key.
265    pub project: Key,
266    /// Written project GUID: the stable project identity.
267    pub project_guid: String,
268    /// One entry per written task, in document order.
269    pub items: Vec<ItemExportReport>,
270    /// Dependencies with at least one endpoint in the project.
271    pub dependencies: Vec<DependencyExportReport>,
272    /// Workspace data outside the supported subset.
273    pub omitted: Vec<Finding>,
274}