holochain_types/app/app_manifest.rs
1#![warn(missing_docs)]
2
3//! The App Manifest format.
4//!
5//! A running Holochain App (hApp) consists of a collection of Cells (instances
6//! of DNA), and these Cells may be shared amongst different apps, enabling
7//! inter-app communication. Therefore, in order to install an App, there needs
8//! to be a precise specification of what kinds of Cells that App needs available
9//! in order to function properly. Such a specification must include info such as:
10//! - the acceptable DNA versions that a Cell may use (made possible via DNA
11//! Migrations, which are not yet implemented)
12//! - whether a given Cell should be created fresh, or an existing Cell be
13//! borrowed from an already-installed app
14//! - whether the app can create cloned copies of a Cell
15//!
16//! The App Manifest is such a specification. Rather than specify a fixed list
17//! of Cells (which would be impossible because each user will be using different
18//! Agents and potentially even different versions of a DNA), the manifest
19//! is mainly defined by a collection of "roles",
20//! each of which may be populated with Cells (instances of DNA) either during
21//! app installation or during runtime. Aside from the role definitions, an
22//! app also has a `name`, which is used as the `installed_app_id` and must be
23//! globally unique, as well as a `description`, which is intended for humans only.
24//!
25//! Each Role definition specifies what kind of Cell can occupy it.
26//! You can think of a Role as a declaration of some piece of functionality
27//! that an app needs in order to function, which will be provided by some Cell
28//! in a flexible manner depending on the state of the conductor at the time of
29//! installation.
30//!
31//! Each Role definition is made up of:
32//! - a RoleName, which only needs to be unique within this App
33//! - a provisioning strategy, [`CellProvisioning`], which describes if and how a Cell
34//! should be created freshly for this app, or whether an existing Cell should
35//! occupy this role
36//! - a DNA descriptor, [`AppRoleDnaManifest`], which describes where to find the DNA,
37//! the acceptable range of versions, and the cloning limitations.
38
39use holochain_zome_types::prelude::*;
40use mr_bundle::{Location, Manifest};
41use std::path::PathBuf;
42
43pub(crate) mod app_manifest_v1;
44pub mod app_manifest_validated;
45mod current;
46mod error;
47
48pub use app_manifest_v1::{AppRoleDnaManifest, CellProvisioning};
49pub use current::*;
50pub use error::*;
51
52use self::app_manifest_validated::AppManifestValidated;
53
54use super::{InstalledCell, ModifiersMap};
55
56/// Container struct which uses the `manifest_version` field to determine
57/// which manifest version to deserialize to.
58#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize, derive_more::From)]
59#[serde(tag = "manifest_version")]
60#[allow(missing_docs)]
61pub enum AppManifest {
62 #[serde(rename = "1")]
63 V1(AppManifestV1),
64}
65
66impl Manifest for AppManifest {
67 fn locations(&self) -> Vec<Location> {
68 match self {
69 AppManifest::V1(m) => m
70 .roles
71 .iter()
72 .filter_map(|role| role.dna.location.clone())
73 .collect(),
74 }
75 }
76
77 fn path() -> PathBuf {
78 "happ.yaml".into()
79 }
80
81 fn bundle_extension() -> &'static str {
82 "happ"
83 }
84}
85
86impl AppManifest {
87 /// Get the supplied name of the app
88 pub fn app_name(&self) -> &str {
89 match self {
90 Self::V1(AppManifestV1 { name, .. }) => name,
91 }
92 }
93
94 /// Convert this human-focused manifest into a validated, concise representation
95 pub fn validate(self) -> AppManifestResult<AppManifestValidated> {
96 match self {
97 Self::V1(manifest) => manifest.validate(),
98 }
99 }
100
101 /// Update the network seed for all DNAs used in Create-provisioned Cells.
102 /// Cells with other provisioning strategies are not affected.
103 pub fn set_network_seed(&mut self, network_seed: NetworkSeed) {
104 match self {
105 Self::V1(manifest) => manifest.set_network_seed(network_seed),
106 }
107 }
108
109 /// Selectively override the modifiers for the specified roles. Only modifier fields with
110 /// `Some(T)` will override existing values. If `None` is provided for a modifier field
111 /// the corresponding value in the manifest will remain untouched.
112 pub fn override_modifiers(&mut self, modifiers: ModifiersMap) -> AppManifestResult<()> {
113 match self {
114 Self::V1(manifest) => manifest.override_modifiers(modifiers),
115 }
116 }
117
118 /// Returns the list of app roles that this manifest declares
119 pub fn app_roles(&self) -> Vec<AppRoleManifest> {
120 match self {
121 Self::V1(manifest) => manifest.roles.clone(),
122 }
123 }
124
125 /// Derive a manifest from a list of InstalledCells, filling in some values
126 /// with defaults.
127 pub fn from_legacy(cells: impl Iterator<Item = InstalledCell>) -> Self {
128 let roles = cells
129 .map(|InstalledCell { role_name, .. }| {
130 let path = PathBuf::from(role_name.clone());
131 AppRoleManifest {
132 name: role_name,
133 provisioning: None,
134 dna: AppRoleDnaManifest {
135 location: Some(mr_bundle::Location::Bundled(path)),
136 modifiers: Default::default(),
137 installed_hash: None,
138 clone_limit: 256,
139 },
140 }
141 })
142 .collect();
143
144 AppManifestCurrent {
145 name: "[autogenerated manifest]".into(),
146 description: Some("Generated by `fn new_legacy`".into()),
147 roles,
148 allow_deferred_memproofs: false,
149 }
150 .into()
151 }
152}
153
154#[cfg(test)]
155mod tests {
156
157 use mr_bundle::Manifest;
158
159 use crate::app::app_manifest::{AppManifest, AppManifestV1Builder, AppRoleManifest};
160
161 #[test]
162 /// Replicate this test for any new version of the manifest that gets created
163 fn app_manifest_v1_helper_functions() {
164 let app_name = String::from("sample-app");
165
166 let role_name = String::from("sample-dna");
167 let role_manifest = AppRoleManifest::sample(role_name);
168
169 let sample_app_manifest_v1 = AppManifestV1Builder::default()
170 .name(app_name.clone())
171 .description(Some(String::from("Some description")))
172 .roles(vec![role_manifest.clone()])
173 .build()
174 .unwrap();
175 let sample_app_manifest = AppManifest::V1(sample_app_manifest_v1.clone());
176
177 assert_eq!(app_name, sample_app_manifest.app_name());
178 assert_eq!(vec![role_manifest], sample_app_manifest.app_roles());
179 assert_eq!(
180 vec![sample_app_manifest_v1
181 .roles
182 .first()
183 .unwrap()
184 .dna
185 .location
186 .clone()
187 .unwrap()],
188 sample_app_manifest.locations()
189 );
190 }
191}