Skip to main content

pinto/service/
export.rs

1//! Read-only complete-board snapshots for machine-readable export.
2
3use super::{apply_effective_points, hierarchical, lock_board, open_board};
4use crate::backlog::{BacklogItem, Status};
5use crate::error::{Error, Result};
6use crate::service::dod::read_common_dod;
7use crate::sprint::Sprint;
8use crate::storage::{BacklogItemRepository, SprintRepository};
9use std::path::Path;
10
11/// A complete read-only snapshot of all board data exposed by `export --json`.
12///
13/// The configuration is represented as JSON because the typed configuration module is an
14/// implementation detail of the library. It is the effective, validated configuration loaded by
15/// pinto, including defaults for omitted settings.
16#[derive(Debug, Clone, PartialEq)]
17pub struct BoardSnapshot {
18    /// Active PBIs in the same hierarchical priority order as `list --json`.
19    pub items: Vec<BacklogItem>,
20    /// Sprints in the same creation order as `sprint list --json`.
21    pub sprints: Vec<Sprint>,
22    /// Effective validated board configuration.
23    pub config: serde_json::Value,
24    /// Common Definition of Done, or `None` when it is unset or empty.
25    pub dod: Option<String>,
26}
27
28/// Load one read-only snapshot containing the board PBIs, Sprints, configuration, and common DoD.
29///
30/// Configuration and all repositories are opened once, so the export uses one validated backend
31/// selection. The board write lock is acquired before configuration and storage are opened and is
32/// held until the complete snapshot has been assembled. This gives automation one consistent board
33/// view while ordinary read commands remain non-blocking.
34pub async fn export_snapshot(project_dir: &Path) -> Result<BoardSnapshot> {
35    let _lock = lock_board(project_dir).await?;
36    let (board_dir, repo, config) = open_board(project_dir).await?;
37    let (mut items, sprints, dod) = tokio::try_join!(
38        BacklogItemRepository::list(&repo),
39        SprintRepository::list(&repo),
40        read_common_dod(&board_dir),
41    )?;
42
43    apply_effective_points(
44        &mut items,
45        config.points.aggregate_children,
46        &Status::new(&config.done_column),
47    );
48    let items = hierarchical(items);
49    let config = serde_json::to_value(&config).map_err(|error| Error::Parse {
50        path: board_dir.join("config.toml"),
51        message: error.to_string(),
52    })?;
53
54    Ok(BoardSnapshot {
55        items,
56        sprints,
57        config,
58        dod,
59    })
60}