Skip to main content

veryl_metadata/
metadata_output.rs

1use crate::lockfile::{Lock, LockSource};
2use crate::metadata::Metadata;
3use crate::metadata_error::MetadataError;
4use semver::Version;
5use serde::Serialize;
6use std::collections::{BTreeMap, HashMap};
7use std::path::PathBuf;
8
9#[derive(Clone, Debug, Serialize, PartialEq)]
10pub struct MetadataOutputV2 {
11    pub format_version: usize,
12    pub root: MetadataProjectV2,
13    pub dependencies: Vec<MetadataDependencyV2>,
14}
15
16#[derive(Clone, Debug, Serialize, PartialEq)]
17pub struct MetadataProjectV2 {
18    pub name: String,
19    pub version: Option<Version>,
20    pub local_path: PathBuf,
21    pub metadata: BTreeMap<String, toml::Value>,
22}
23
24#[derive(Clone, Debug, Serialize, PartialEq)]
25pub struct MetadataDependencyV2 {
26    pub id: String,
27    pub name: String,
28    pub project: String,
29    pub source: MetadataSourceV2,
30    pub local_path: PathBuf,
31    pub metadata: BTreeMap<String, toml::Value>,
32    pub dependencies: Vec<String>,
33}
34
35#[derive(Clone, Debug, Serialize, PartialEq)]
36#[serde(tag = "kind", rename_all = "snake_case")]
37pub enum MetadataSourceV2 {
38    Path {
39        path: PathBuf,
40    },
41    Repository {
42        url: String,
43        project: String,
44        version: Version,
45        revision: String,
46        path: PathBuf,
47    },
48}
49
50impl MetadataOutputV2 {
51    pub fn from_metadata(metadata: &Metadata) -> Result<Self, MetadataError> {
52        let project_path = metadata.project_path();
53        let locks = metadata.lockfile.projects();
54        let dependency_ids = locks
55            .iter()
56            .map(|lock| (lock.source.clone(), format!("dep:{}", lock.name)))
57            .collect::<HashMap<_, _>>();
58        let mut dependencies = locks
59            .iter()
60            .map(|lock| MetadataDependencyV2::from_lock(lock, metadata, &dependency_ids))
61            .collect::<Result<Vec<_>, _>>()?;
62        dependencies.sort_by(|x, y| x.id.cmp(&y.id));
63
64        Ok(Self {
65            format_version: 2,
66            root: MetadataProjectV2 {
67                name: metadata.project.name.clone(),
68                version: metadata.project.version.clone(),
69                local_path: project_path,
70                metadata: metadata.metadata.clone().into_iter().collect(),
71            },
72            dependencies,
73        })
74    }
75}
76
77impl MetadataDependencyV2 {
78    fn from_lock(
79        lock: &Lock,
80        root_metadata: &Metadata,
81        dependency_ids: &HashMap<LockSource, String>,
82    ) -> Result<Self, MetadataError> {
83        let source = MetadataSourceV2::from_lock_source(&lock.source);
84        let metadata = root_metadata.lockfile.get_metadata(&lock.source)?;
85        let mut dependencies = lock
86            .dependencies
87            .iter()
88            .map(|dependency| {
89                dependency_ids
90                    .get(&dependency.source)
91                    .cloned()
92                    .unwrap_or_else(|| format!("dep:{}", dependency.name))
93            })
94            .collect::<Vec<_>>();
95        dependencies.sort();
96
97        Ok(Self {
98            // Lock names are conflict-disambiguated during lock generation, so they are stable ids.
99            id: format!("dep:{}", lock.name),
100            name: lock.name.clone(),
101            project: metadata.project.name.clone(),
102            local_path: metadata.project_path(),
103            metadata: metadata.metadata.into_iter().collect(),
104            source,
105            dependencies,
106        })
107    }
108}
109
110impl MetadataSourceV2 {
111    fn from_lock_source(source: &LockSource) -> Self {
112        match source {
113            LockSource::Path(path) => Self::Path { path: path.clone() },
114            LockSource::Repository(repository) => Self::Repository {
115                url: repository.url.to_string(),
116                project: repository.project.clone(),
117                version: repository.version.clone(),
118                revision: repository.revision.clone(),
119                path: repository.path.clone(),
120            },
121        }
122    }
123}