summaryrefslogtreecommitdiffstats
path: root/src/remake/kind.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/remake/kind.rs')
-rw-r--r--src/remake/kind.rs48
1 files changed, 48 insertions, 0 deletions
diff --git a/src/remake/kind.rs b/src/remake/kind.rs
index ae2025d..2b24422 100644
--- a/src/remake/kind.rs
+++ b/src/remake/kind.rs
@@ -17,6 +17,9 @@
//! Protocol data under `_continuum` is not a kind's business: the generic part carries
//! it forward and replays it unchanged.
+use std::path::Path;
+
+use anyhow::{Context, anyhow, bail};
use automerge::AutoCommit;
use base64::engine::{Engine, general_purpose::STANDARD as BASE64};
use serde::{Deserialize, Serialize};
@@ -24,6 +27,7 @@ use serde_json::Value as JsonValue;
use super::snapshot::{META_ROOT, canonical};
use crate::document::store::{self, StoreError};
+use crate::files::{FileKind, Files};
/// Name of the kind used when a request doesn't name one.
pub(crate) const DEFAULT_KIND: &str = "compact";
@@ -91,6 +95,50 @@ pub(crate) fn by_name(name: &str) -> Option<&'static dyn RemakeKind> {
}
}
+/// Builds a new swarm's genesis content into `target` (`--genesis --remake SPEC`), the
+/// way a remake of that kind builds a new epoch from an empty one:
+/// * `import:PATH`: an Automerge document, e.g. a backup (its content, without history
+/// and protocol data);
+/// * `replace:PATH`: a JSON document, the store root;
+/// * `compact`: nothing (an empty document compacts to itself).
+pub(crate) fn build_genesis(
+ spec: &str,
+ files: &Files,
+ target: &mut AutoCommit,
+) -> anyhow::Result<()> {
+ let (name, path) = match spec.split_once(':') {
+ Some((name, path)) => (name, Some(Path::new(path))),
+ None => (spec, None),
+ };
+ let kind = by_name(name).ok_or_else(|| anyhow!("unknown remake kind {name:?}"))?;
+ let request = match (kind.name(), path) {
+ ("compact", None) => return Ok(()),
+ ("compact", Some(_)) => bail!("compact takes no file"),
+ (_, None) => bail!("{name} needs a file: {name}:PATH"),
+ (name, Some(path)) => {
+ let bytes = files
+ .read(FileKind::Remake, path)
+ .with_context(|| format!("Reading {}", path.display()))?
+ .with_context(|| format!("{} doesn't exist", path.display()))?;
+ match name {
+ "import" => serde_json::json!({ "document": BASE64.encode(&bytes) }),
+ "replace" => {
+ let document: JsonValue = serde_json::from_slice(&bytes)
+ .with_context(|| format!("{} is not JSON", path.display()))?;
+ serde_json::json!({ "document": document })
+ }
+ other => bail!("{other} can't make a genesis document"),
+ }
+ }
+ };
+ let prepared = kind
+ .propose(&request)
+ .map_err(|reason| anyhow!("{name}: {reason}"))?;
+ kind.build(&AutoCommit::new(), prepared.content.as_deref(), target)?;
+ target.commit();
+ Ok(())
+}
+
/// Paths owned by the protocol rather than by any kind.
pub(crate) fn is_reserved(path: &[String]) -> bool {
path.first().is_some_and(|root| root == META_ROOT)