hyperion_framework/containerisation/traits.rs
1// -------------------------------------------------------------------------------------------------
2// Hyperion Framework
3// https://github.com/robert-hannah/hyperion-framework
4//
5// A lightweight component-based TCP framework for building service-oriented Rust applications with
6// CLI control, async messaging, and lifecycle management.
7//
8// Copyright 2025 Robert Hannah
9//
10// Licensed under the Apache License, Version 2.0 (the "License");
11// you may not use this file except in compliance with the License.
12// You may obtain a copy of the License at
13//
14// http://www.apache.org/licenses/LICENSE-2.0
15//
16// Unless required by applicable law or agreed to in writing, software
17// distributed under the License is distributed on an "AS IS" BASIS,
18// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19// See the License for the specific language governing permissions and
20// limitations under the License.
21// -------------------------------------------------------------------------------------------------
22
23// Standard
24use std::collections::HashMap;
25use std::sync::Arc as StdArc;
26use std::sync::atomic::AtomicUsize;
27
28// Package
29use async_trait::async_trait;
30use tokio::sync::Notify;
31use tokio::sync::mpsc::{Receiver, Sender};
32
33// Local
34use crate::messages::client_broker_message::ClientBrokerMessage;
35use crate::messages::container_directive::ContainerDirective;
36
37// Traits
38pub trait Initialisable {
39 type ConfigType;
40 fn initialise(
41 container_state: StdArc<AtomicUsize>,
42 container_state_notify: StdArc<Notify>,
43 config: StdArc<Self::ConfigType>,
44 ) -> Self;
45}
46// For example, Hyperion Network Containerisation - Initialise Component
47// impl Initialisable for Component {
48// type ConfigType = Config;
49// fn initialise(container_state: StdArc<AtomicUsize>, container_state_notify: StdArc<Notify>, config: StdArc<Self::ConfigType>) -> Self {
50// // Will panic if there's a problem, which we want seeing we do this on startup
51// Component::new(container_state, container_state_notify, config)
52// }
53// }
54
55#[async_trait]
56pub trait Run {
57 // Uses async_trait which isn't perfect and does introduce a small overhead, but it is a lot
58 // cleaner than the alternative. Since this is only used once to start the component, I don't
59 // see it as a big deal. Performance overhead comes from lack of return type (compiler can't optimise)
60
61 // Using self, instead of &mut self, as the clone must be consumed by Run to ensure a fresh
62 // state is kept in the container
63 type Message;
64 async fn run(
65 self,
66 comp_in_rx: Receiver<Self::Message>,
67 comp_out_tx: Sender<ClientBrokerMessage<Self::Message>>,
68 );
69}
70// For example, Hyperion Network Containerisation - Run Component
71// #[async_trait]
72// impl Run for Component {
73// type Message = ContainerMessage;
74// async fn run(mut self, mut comp_in_rx: Receiver<Self::Message>, comp_out_tx: Sender<ClientBrokerMessage<Self::Message>>) {
75// log::debug!("{} has started successfully", self.config.container_id.name);
76// loop {
77// if self.component_state == ComponentState::Dead { break; }
78// tokio::select! {
79// Some(message) = comp_in_rx.recv() => {
80// log::trace!("{} received message: {:?}", self.config.container_id.name, message);
81// if let Some(result) = self.process_incoming_message(message).await {
82// let from_location = format!("{} main loop", self.config.container_id.name);
83// let to_location = format!("{} Container", self.config.container_id.name);
84// add_to_tx_with_retry(&comp_out_tx, &result, &from_location, &to_location).await;
85// }
86// }
87// _ = self.container_state_notify.notified() => {
88// // Check if container is shutting down
89// if self.container_state.load(Ordering::SeqCst) == ContainerState::ShuttingDown as usize {
90// self.component_state = ComponentState::Dormant;
91// break;
92// }
93// }
94// }
95// }
96// log::info!("{} task has closed", self.config.container_id.name);
97// }
98// }
99
100pub trait HyperionContainerDirectiveMessage {
101 fn get_container_directive_message(&self) -> Option<&ContainerDirective>;
102}
103// For example,
104// impl HyperionContainerDirectiveMessage for ContainerMessage {
105// // Gets ContainerDirective if is instance
106// fn get_container_directive_message(&self) -> Option<&ContainerDirective> {
107// if let ContainerMessage::ContainerDirectiveMsg(directive) = self {
108// Some(directive)
109// } else {
110// None
111// }
112// }
113// }
114
115pub trait ContainerIdentidy {
116 fn container_identity(&self) -> HashMap<String, String>;
117}
118// For example,
119// impl ContainerIdentidy for Config {
120// fn container_identity(&self) -> HashMap<String, String> {
121// let mut identity = HashMap::new();
122// identity.insert("name".to_string(), self.container.name.clone());
123// identity.insert("version".to_string(), self.container.version.clone());
124// identity.insert("version_title".to_string(), self.container.version_title.clone());
125// identity.insert("software_collection".to_string(), self.container.software_collection.clone());
126// identity
127// }
128// }
129
130pub trait LogLevel {
131 fn log_level(&self) -> &str;
132}
133// For example,
134// impl LogLevel for Config {
135// fn log_level(&self) -> &str {
136// &self.logging.level
137// }
138// }