use exonum::{
blockchain::config::{GenesisConfigBuilder, InstanceInitParams},
merkledb::BinaryValue,
runtime::{migrations::MigrateData, ArtifactId, InstanceId, RuntimeIdentifier},
};
use std::{marker::PhantomData, mem};
use self::sealed::Sealed;
use crate::{DefaultInstance, RustRuntimeBuilder, ServiceFactory};
mod sealed {
pub trait Sealed {}
}
pub trait Deploy: Sealed {
fn deploy(self, genesis: &mut GenesisConfigBuilder, rt: &mut RustRuntimeBuilder);
}
#[derive(Debug)]
pub struct Simple(());
#[derive(Debug)]
pub struct Migrating(());
#[derive(Debug)]
pub struct Spec<T, Kind> {
service: T,
instances: Vec<InstanceInitParams>,
default_instance: Option<InstanceInitParams>,
_kind: PhantomData<Kind>,
}
impl<T: ServiceFactory, Kind> Spec<T, Kind> {
pub fn with_instance(
mut self,
id: InstanceId,
name: impl Into<String>,
constructor: impl BinaryValue,
) -> Self {
self.instances.push(InstanceInitParams::new(
id,
name,
self.service.artifact_id(),
constructor,
));
self
}
}
impl<T: DefaultInstance, Kind> Spec<T, Kind> {
pub fn with_default_instance(mut self) -> Self {
self.default_instance = Some(self.service.default_instance());
self
}
}
impl<T: ServiceFactory> Spec<T, Simple> {
pub fn new(service: T) -> Self {
Self {
service,
instances: vec![],
default_instance: None,
_kind: PhantomData,
}
}
}
impl<T: ServiceFactory> Spec<T, Migrating> {
pub fn migrating(service: T) -> Self {
Self {
service,
instances: vec![],
default_instance: None,
_kind: PhantomData,
}
}
}
impl<T: Clone, Kind> Clone for Spec<T, Kind> {
fn clone(&self) -> Self {
Self {
service: self.service.clone(),
instances: self.instances.clone(),
default_instance: self.default_instance.clone(),
_kind: PhantomData,
}
}
}
impl<T: ServiceFactory> Sealed for Spec<T, Simple> {}
impl<T: ServiceFactory> Deploy for Spec<T, Simple> {
fn deploy(self, genesis: &mut GenesisConfigBuilder, rt: &mut RustRuntimeBuilder) {
let mut new_config = mem::take(genesis).with_artifact(self.service.artifact_id());
let instances = self.default_instance.into_iter().chain(self.instances);
for instance in instances {
new_config = new_config.with_instance(instance);
}
*genesis = new_config;
*rt = mem::take(rt).with_factory(self.service);
}
}
impl<T: ServiceFactory> Sealed for Spec<T, Migrating> {}
impl<T: ServiceFactory + MigrateData> Deploy for Spec<T, Migrating> {
fn deploy(self, genesis: &mut GenesisConfigBuilder, rt: &mut RustRuntimeBuilder) {
let mut new_config = mem::take(genesis).with_artifact(self.service.artifact_id());
let instances = self.default_instance.into_iter().chain(self.instances);
for instance in instances {
new_config = new_config.with_instance(instance);
}
*genesis = new_config;
*rt = mem::take(rt).with_migrating_factory(self.service);
}
}
#[derive(Debug)]
pub struct JustFactory<T, Kind> {
service: T,
_kind: PhantomData<Kind>,
}
impl<T: ServiceFactory> JustFactory<T, Simple> {
pub fn new(service: T) -> Self {
Self {
service,
_kind: PhantomData,
}
}
}
impl<T: ServiceFactory> Sealed for JustFactory<T, Simple> {}
impl<T: ServiceFactory> Deploy for JustFactory<T, Simple> {
fn deploy(self, _: &mut GenesisConfigBuilder, rt: &mut RustRuntimeBuilder) {
*rt = mem::take(rt).with_factory(self.service);
}
}
impl<T: ServiceFactory + MigrateData> JustFactory<T, Migrating> {
pub fn migrating(service: T) -> Self {
Self {
service,
_kind: PhantomData,
}
}
}
impl<T: ServiceFactory + MigrateData> Sealed for JustFactory<T, Migrating> {}
impl<T: ServiceFactory + MigrateData> Deploy for JustFactory<T, Migrating> {
fn deploy(self, _: &mut GenesisConfigBuilder, rt: &mut RustRuntimeBuilder) {
*rt = mem::take(rt).with_migrating_factory(self.service);
}
}
#[derive(Debug, Clone)]
pub struct ForeignSpec {
artifact: ArtifactId,
deploy_spec: Option<Vec<u8>>,
instances: Vec<InstanceInitParams>,
}
impl ForeignSpec {
pub fn new(artifact: ArtifactId) -> Self {
assert_ne!(
artifact.runtime_id,
RuntimeIdentifier::Rust as u32,
"Deploying Rust artifacts with `ForeignSpec` does not make sense; the Rust runtime \
will not hold the service factory necessary to instantiate corresponding services. \
Use `Spec` instead"
);
Self {
artifact,
deploy_spec: None,
instances: vec![],
}
}
pub fn with_deploy_spec(mut self, spec: impl BinaryValue) -> Self {
self.deploy_spec = Some(spec.into_bytes());
self
}
pub fn with_instance(
mut self,
id: InstanceId,
name: impl Into<String>,
constructor: impl BinaryValue,
) -> Self {
self.instances.push(InstanceInitParams::new(
id,
name,
self.artifact.clone(),
constructor,
));
self
}
}
impl Sealed for ForeignSpec {}
impl Deploy for ForeignSpec {
fn deploy(self, genesis: &mut GenesisConfigBuilder, _: &mut RustRuntimeBuilder) {
let mut new_config = if let Some(deploy_spec) = self.deploy_spec {
mem::take(genesis).with_parametric_artifact(self.artifact, deploy_spec)
} else {
mem::take(genesis).with_artifact(self.artifact)
};
for instance in self.instances {
new_config = new_config.with_instance(instance);
}
*genesis = new_config;
}
}