Skip to main content

harn_cli/package/lockfile/
exports.rs

1//! The package-export surface a manifest declares, projected into the shape
2//! the lock file records and consumers read back.
3
4use crate::package::*;
5
6#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
7pub struct PackageLockExports {
8    #[serde(default, skip_serializing_if = "Vec::is_empty")]
9    pub modules: Vec<PackageLockExport>,
10    #[serde(default, skip_serializing_if = "Vec::is_empty")]
11    pub tools: Vec<PackageLockExport>,
12    #[serde(default, skip_serializing_if = "Vec::is_empty")]
13    pub skills: Vec<PackageLockExport>,
14    #[serde(default, skip_serializing_if = "Vec::is_empty")]
15    pub personas: Vec<String>,
16}
17
18impl PackageLockExports {
19    pub(crate) fn is_empty(&self) -> bool {
20        self.modules.is_empty()
21            && self.tools.is_empty()
22            && self.skills.is_empty()
23            && self.personas.is_empty()
24    }
25}
26
27#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
28pub struct PackageLockExport {
29    pub name: String,
30    #[serde(default, skip_serializing_if = "Option::is_none")]
31    pub path: Option<String>,
32    #[serde(default, skip_serializing_if = "Option::is_none")]
33    pub symbol: Option<String>,
34}
35
36pub(crate) fn package_lock_exports_from_manifest(manifest: &Manifest) -> PackageLockExports {
37    let mut modules: Vec<PackageLockExport> = manifest
38        .exports
39        .iter()
40        .map(|(name, path)| PackageLockExport {
41            name: name.clone(),
42            path: Some(path.clone()),
43            symbol: None,
44        })
45        .collect();
46    modules.sort_by(|left, right| left.name.cmp(&right.name));
47
48    let (mut tools, mut skills) = manifest
49        .package
50        .as_ref()
51        .map(|package| {
52            let tools = package
53                .tools
54                .iter()
55                .map(|tool| PackageLockExport {
56                    name: tool.name.clone(),
57                    path: Some(tool.module.clone()),
58                    symbol: Some(tool.symbol.clone()),
59                })
60                .collect::<Vec<_>>();
61            let skills = package
62                .skills
63                .iter()
64                .map(|skill| PackageLockExport {
65                    name: skill.name.clone(),
66                    path: Some(skill.path.clone()),
67                    symbol: None,
68                })
69                .collect::<Vec<_>>();
70            (tools, skills)
71        })
72        .unwrap_or_default();
73    tools.sort_by(|left, right| left.name.cmp(&right.name));
74    skills.sort_by(|left, right| left.name.cmp(&right.name));
75
76    let mut personas: Vec<String> = manifest
77        .personas
78        .iter()
79        .filter_map(|persona| persona.name.clone())
80        .collect();
81    personas.sort();
82    personas.dedup();
83
84    PackageLockExports {
85        modules,
86        tools,
87        skills,
88        personas,
89    }
90}
91
92pub(crate) fn normalized_requirements(values: &[String]) -> Vec<String> {
93    let mut out: Vec<String> = values
94        .iter()
95        .map(|value| value.trim())
96        .filter(|value| !value.is_empty())
97        .map(str::to_string)
98        .collect();
99    out.sort();
100    out.dedup();
101    out
102}