| // Copyright 2020 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| use { |
| crate::model::{ |
| actions::{Action, ActionKey, ActionSet, DiscoverAction}, |
| component::{ |
| Component, ComponentInstance, InstanceState, ResolvedInstanceState, |
| WeakComponentInstance, |
| }, |
| error::ModelError, |
| hooks::{Event, EventError, EventErrorPayload, EventPayload}, |
| resolver::Resolver, |
| }, |
| async_trait::async_trait, |
| std::convert::TryFrom, |
| std::sync::Arc, |
| }; |
| |
| /// Resolves a component instance's declaration and initializes its state. |
| pub struct ResolveAction {} |
| |
| impl ResolveAction { |
| pub fn new() -> Self { |
| Self {} |
| } |
| } |
| |
| #[async_trait] |
| impl Action for ResolveAction { |
| type Output = Result<Component, ModelError>; |
| async fn handle(&self, component: &Arc<ComponentInstance>) -> Self::Output { |
| do_resolve(component).await |
| } |
| fn key(&self) -> ActionKey { |
| ActionKey::Resolve |
| } |
| } |
| |
| async fn do_resolve(component: &Arc<ComponentInstance>) -> Result<Component, ModelError> { |
| // Ensure `Resolved` is dispatched after `Discovered`. |
| ActionSet::register(component.clone(), DiscoverAction::new()).await?; |
| let result = async move { |
| let first_resolve = { |
| let state = component.lock_state().await; |
| match *state { |
| InstanceState::New => { |
| panic!("Component should be at least discovered") |
| } |
| InstanceState::Discovered => true, |
| InstanceState::Resolved(_) => false, |
| InstanceState::Destroyed => { |
| return Err(ModelError::instance_not_found(component.abs_moniker.clone())); |
| } |
| } |
| }; |
| let component_info = |
| component.environment.resolve(&component.component_url).await.map_err(|err| { |
| ModelError::ResolverError { url: component.component_url.clone(), err } |
| })?; |
| let component_info = Component::try_from(component_info)?; |
| if first_resolve { |
| { |
| let mut state = component.lock_state().await; |
| match *state { |
| InstanceState::Resolved(_) => { |
| panic!("Component was marked Resolved during Resolve action?"); |
| } |
| InstanceState::Destroyed => { |
| return Err(ModelError::instance_not_found(component.abs_moniker.clone())); |
| } |
| InstanceState::New | InstanceState::Discovered => {} |
| } |
| state.set(InstanceState::Resolved( |
| ResolvedInstanceState::new(component, component_info.decl.clone()).await, |
| )); |
| } |
| } |
| Ok((component_info, first_resolve)) |
| } |
| .await; |
| |
| match result { |
| Ok((component_info, false)) => Ok(component_info), |
| Ok((component_info, true)) => { |
| let event = Event::new( |
| component, |
| Ok(EventPayload::Resolved { |
| component: WeakComponentInstance::from(component), |
| resolved_url: component_info.resolved_url.clone(), |
| decl: component_info.decl.clone(), |
| }), |
| ); |
| component.hooks.dispatch(&event).await?; |
| Ok(component_info) |
| } |
| Err(e) => { |
| let event = |
| Event::new(component, Err(EventError::new(&e, EventErrorPayload::Resolved))); |
| component.hooks.dispatch(&event).await?; |
| Err(e) |
| } |
| } |
| } |