use std::collections::HashSet;
use linera_base::{data_types::UserApplicationDescription, identifiers::UserApplicationId};
use linera_views::{
context::Context,
map_view::HashedMapView,
views::{ClonableView, HashableView},
};
#[cfg(with_testing)]
use {
linera_views::context::{create_test_memory_context, MemoryContext},
linera_views::views::View,
std::collections::BTreeMap,
};
use crate::SystemExecutionError;
#[cfg(test)]
#[path = "unit_tests/applications_tests.rs"]
mod applications_tests;
#[derive(Debug, ClonableView, HashableView)]
pub struct ApplicationRegistryView<C> {
pub known_applications: HashedMapView<C, UserApplicationId, UserApplicationDescription>,
}
#[cfg(with_testing)]
#[derive(Default, Eq, PartialEq, Debug, Clone)]
pub struct ApplicationRegistry {
pub known_applications: BTreeMap<UserApplicationId, UserApplicationDescription>,
}
impl<C> ApplicationRegistryView<C>
where
C: Context + Clone + Send + Sync + 'static,
{
#[cfg(with_testing)]
pub fn import(&mut self, registry: ApplicationRegistry) -> Result<(), SystemExecutionError> {
for (id, description) in registry.known_applications {
self.known_applications.insert(&id, description)?;
}
Ok(())
}
pub async fn register_application(
&mut self,
application: UserApplicationDescription,
) -> Result<UserApplicationId, SystemExecutionError> {
for required_id in &application.required_application_ids {
self.describe_application(*required_id).await?;
}
let id = UserApplicationId::from(&application);
self.known_applications.insert(&id, application)?;
Ok(id)
}
pub async fn register_new_application(
&mut self,
application_id: UserApplicationId,
parameters: Vec<u8>,
required_application_ids: Vec<UserApplicationId>,
) -> Result<(), SystemExecutionError> {
for required_id in &required_application_ids {
self.describe_application(*required_id).await?;
}
let UserApplicationId {
bytecode_id,
creation,
} = application_id;
let description = UserApplicationDescription {
bytecode_id,
parameters,
creation,
required_application_ids,
};
self.known_applications
.insert(&application_id, description)?;
Ok(())
}
pub async fn describe_application(
&self,
id: UserApplicationId,
) -> Result<UserApplicationDescription, SystemExecutionError> {
self.known_applications
.get(&id)
.await?
.ok_or_else(|| SystemExecutionError::UnknownApplicationId(Box::new(id)))
}
pub async fn find_dependencies(
&self,
mut stack: Vec<UserApplicationId>,
) -> Result<Vec<UserApplicationId>, SystemExecutionError> {
let mut result = Vec::new();
let mut sorted = HashSet::new();
let mut seen = HashSet::new();
while let Some(id) = stack.pop() {
if sorted.contains(&id) {
continue;
}
if seen.contains(&id) {
sorted.insert(id);
result.push(id);
continue;
}
seen.insert(id);
stack.push(id);
let app = self.describe_application(id).await?;
for child in app.required_application_ids.iter().rev() {
if !seen.contains(child) {
stack.push(*child);
}
}
}
Ok(result)
}
pub async fn describe_applications_with_dependencies(
&self,
ids: Vec<UserApplicationId>,
) -> Result<Vec<UserApplicationDescription>, SystemExecutionError> {
let ids_with_deps = self.find_dependencies(ids).await?;
let mut result = Vec::new();
for id in ids_with_deps {
let description = self.describe_application(id).await?;
result.push(description);
}
Ok(result)
}
}
#[cfg(with_testing)]
impl ApplicationRegistryView<MemoryContext<()>>
where
MemoryContext<()>: Context + Clone + Send + Sync + 'static,
{
pub async fn new() -> Self {
let context = create_test_memory_context();
Self::load(context)
.await
.expect("Loading from memory should work")
}
}