mod decode;
use crate::*;
use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
#[derive(Debug, Clone, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct Plan {
#[serde(deserialize_with = "read_format_version")]
pub format_version: u32,
pub assets: BTreeMap<AssetId, WorkspaceAsset>,
pub workspace: Workspace,
pub revision: u64,
pub projects: BTreeMap<ProjectId, Project>,
pub work_items: BTreeMap<WorkItemId, WorkItem>,
pub requirements: BTreeMap<RequirementId, Requirement>,
pub artifacts: BTreeMap<ArtifactId, Artifact>,
pub decisions: BTreeMap<DecisionId, Decision>,
pub risks: BTreeMap<RiskId, Risk>,
pub dependencies: Vec<Dependency>,
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub external_references: BTreeMap<ExternalReferenceId, ExternalReference>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub links: Vec<WorkLink>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub calendars: Option<Calendars>,
}
impl Plan {
pub fn empty(name: impl Into<String>) -> Self {
Self {
format_version: 3,
assets: BTreeMap::new(),
workspace: Workspace {
id: WorkspaceId::new(),
name: name.into(),
},
revision: 0,
projects: BTreeMap::new(),
work_items: BTreeMap::new(),
requirements: BTreeMap::new(),
artifacts: BTreeMap::new(),
decisions: BTreeMap::new(),
risks: BTreeMap::new(),
dependencies: Vec::new(),
external_references: BTreeMap::new(),
links: Vec::new(),
calendars: None,
}
}
pub fn find_work_by_key(&self, key: &str) -> Option<&WorkItem> {
self.work_items.values().find(|work| work.key.0 == key)
}
pub fn find_work_by_key_mut(&mut self, key: &str) -> Option<&mut WorkItem> {
self.work_items.values_mut().find(|work| work.key.0 == key)
}
pub fn find_dependency(&self, id: DependencyId) -> Option<&Dependency> {
self.dependencies.iter().find(|edge| edge.id == id)
}
pub fn enforced_dependencies(&self) -> impl Iterator<Item = &Dependency> {
self.dependencies
.iter()
.filter(|edge| edge.waiver.is_none())
}
pub fn find_decision_by_key(&self, key: &str) -> Option<&Decision> {
self.decisions
.values()
.find(|decision| decision.key.0 == key)
}
}
fn read_format_version<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Result<u32, D::Error> {
let version = u32::deserialize(deserializer)?;
if version != 3 {
return Err(serde::de::Error::custom(format!(
"unsupported plan format {version}; expected 3. Preserve the original database and export with its original DPM release; convert that export before initializing a new store"
)));
}
Ok(version)
}
#[cfg(test)]
mod tests;