Skip to main content

xidl_parser/versioning/map/
mod.rs

1//! Language-neutral facts describing adjacent API-version changes.
2
3mod collect;
4mod compare;
5mod declarations;
6mod helpers;
7mod model;
8
9use super::{VersionId, VersioningRecord};
10use crate::error::ParserResult;
11use crate::hir::Specification;
12use serde_json;
13use std::collections::BTreeMap;
14
15pub use model::{Change, ChangeKind, MapStep, MapVersion, TypeChange, VersionMap};
16
17/// Computes the complete, unselected version map for a validated contract.
18pub fn from_spec(
19    spec: &Specification,
20    record: &VersioningRecord,
21    contract: impl Into<String>,
22) -> ParserResult<VersionMap> {
23    from_spec_with_http(spec, record, contract, true)
24}
25
26/// Computes a version map while optionally collecting REST operation facts.
27///
28/// JSON-RPC generators pass `false`: a REST projection would otherwise invent
29/// HTTP routes for ordinary RPC interfaces. Type and declaration facts remain
30/// protocol-independent in both modes.
31pub fn from_spec_with_http(
32    spec: &Specification,
33    record: &VersioningRecord,
34    contract: impl Into<String>,
35    include_http_operations: bool,
36) -> ParserResult<VersionMap> {
37    let versions = record
38        .timeline()
39        .iter()
40        .map(|(_, version)| MapVersion {
41            id: version.ident.clone(),
42            value: version.value.clone(),
43        })
44        .collect::<Vec<_>>();
45    let mut steps = Vec::new();
46    let declarations = declarations::SourceDeclarations::collect(spec, record.timeline())?;
47    for index in 0..versions.len().saturating_sub(1) {
48        let from_id = VersionId::from_index(index);
49        let to_id = VersionId::from_index(index + 1);
50        let from = record.project(spec, from_id)?;
51        let to = record.project(spec, to_id)?;
52        let mut from_types = collect::collect_types(&from);
53        let mut to_types = collect::collect_types(&to);
54        declarations.stamp_types(&mut from_types, from_id)?;
55        declarations.stamp_types(&mut to_types, to_id)?;
56        let operations = if include_http_operations {
57            let mut from_operations = collect::collect_operations(&from)?;
58            let mut to_operations = collect::collect_operations(&to)?;
59            declarations.stamp_operations(&mut from_operations, from_id)?;
60            declarations.stamp_operations(&mut to_operations, to_id)?;
61            compare::compare_operations(&from_operations, &to_operations)
62        } else {
63            BTreeMap::new()
64        };
65        steps.push(MapStep {
66            from: versions[index].id.clone(),
67            to: versions[index + 1].id.clone(),
68            types: compare::compare_types(&from_types, &to_types),
69            operations,
70        });
71    }
72    Ok(VersionMap {
73        format: 2,
74        contract: contract.into(),
75        versions,
76        steps,
77    })
78}
79
80/// Serializes a version map with the repository's canonical JSON policy.
81pub fn to_json(map: &VersionMap) -> Result<String, serde_json::Error> {
82    let mut content = serde_json::to_string_pretty(map)?;
83    content.push('\n');
84    Ok(content)
85}