harmony/examples/topology/src/main.rs

233 lines
6.9 KiB
Rust

// Basic traits from your example
trait Topology {}
trait Score: Clone + std::fmt::Debug {
fn get_interpret<T: Topology>(&self) -> Box<dyn Interpret<T>>;
fn name(&self) -> String;
}
trait Interpret<T: Topology> {
fn execute(&self);
}
struct Maestro<T: Topology> {
topology: T
}
impl<T: Topology> Maestro<T> {
pub fn new(topology: T) -> Self {
Maestro { topology }
}
pub fn register_score<S: Score + 'static>(&self, score: S) {
println!("Registering score: {}", score.name());
}
pub fn execute_score<S: Score + 'static>(&self, score: S) {
println!("Executing score: {}", score.name());
score.get_interpret::<T>().execute();
}
}
// Capability traits - these are used to enforce requirements
trait CommandExecution {
fn execute_command(&self, command: &[String]) -> Result<String, String>;
}
trait FileSystem {
fn read_file(&self, path: &str) -> Result<String, String>;
fn write_file(&self, path: &str, content: &str) -> Result<(), String>;
}
// A concrete topology implementation
#[derive(Clone, Debug)]
struct LinuxHostTopology {
hostname: String,
}
impl Topology for LinuxHostTopology {}
// Implement the capabilities for LinuxHostTopology
impl CommandExecution for LinuxHostTopology {
fn execute_command(&self, command: &[String]) -> Result<String, String> {
println!("Executing command on {}: {:?}", self.hostname, command);
// In a real implementation, this would use std::process::Command
Ok(format!("Command executed successfully on {}", self.hostname))
}
}
impl FileSystem for LinuxHostTopology {
fn read_file(&self, path: &str) -> Result<String, String> {
println!("Reading file {} on {}", path, self.hostname);
Ok(format!("Content of {} on {}", path, self.hostname))
}
fn write_file(&self, path: &str, content: &str) -> Result<(), String> {
println!("Writing to file {} on {}: {}", path, self.hostname, content);
Ok(())
}
}
// Another topology that doesn't support command execution
#[derive(Clone, Debug)]
struct BareMetalTopology {
device_id: String,
}
impl Topology for BareMetalTopology {}
impl FileSystem for BareMetalTopology {
fn read_file(&self, path: &str) -> Result<String, String> {
println!("Reading file {} on device {}", path, self.device_id);
Ok(format!("Content of {} on device {}", path, self.device_id))
}
fn write_file(&self, path: &str, content: &str) -> Result<(), String> {
println!("Writing to file {} on device {}: {}", path, self.device_id, content);
Ok(())
}
}
// CommandScore implementation
#[derive(Clone, Debug)]
struct CommandScore {
name: String,
args: Vec<String>,
}
impl CommandScore {
pub fn new(name: String, args: Vec<String>) -> Self {
CommandScore { name, args }
}
}
impl Score for CommandScore {
fn get_interpret<T: Topology + CommandExecution + 'static>(&self) -> Box<dyn Interpret<T>> {
// This is the key part: we constrain T to implement CommandExecution
// If T doesn't implement CommandExecution, this will fail to compile
Box::new(CommandInterpret::<T>::new(self.clone()))
}
fn name(&self) -> String {
self.name.clone()
}
}
// CommandInterpret implementation
struct CommandInterpret<T: Topology + CommandExecution> {
score: CommandScore,
_marker: std::marker::PhantomData<T>,
}
impl<T: Topology + CommandExecution> CommandInterpret<T> {
pub fn new(score: CommandScore) -> Self {
CommandInterpret {
score,
_marker: std::marker::PhantomData,
}
}
}
impl<T: Topology + CommandExecution> Interpret<T> for CommandInterpret<T> {
fn execute(&self) {
println!("Command interpret is executing: {:?}", self.score.args);
// In a real implementation, you would call the topology's execute_command method
// topology.execute_command(&self.score.args);
}
}
// FileScore implementation - a different type of score that requires FileSystem capability
#[derive(Clone, Debug)]
struct FileScore {
name: String,
path: String,
content: Option<String>,
}
impl FileScore {
pub fn new_read(name: String, path: String) -> Self {
FileScore { name, path, content: None }
}
pub fn new_write(name: String, path: String, content: String) -> Self {
FileScore { name, path, content: Some(content) }
}
}
impl Score for FileScore {
fn get_interpret<T: Topology>(&self) -> Box<dyn Interpret<T>> {
// This constrains T to implement FileSystem
Box::new(FileInterpret::<T>::new(self.clone()))
}
fn name(&self) -> String {
self.name.clone()
}
}
// FileInterpret implementation
struct FileInterpret<T: Topology + FileSystem> {
score: FileScore,
_marker: std::marker::PhantomData<T>,
}
impl<T: Topology + FileSystem> FileInterpret<T> {
pub fn new(score: FileScore) -> Self {
FileInterpret {
score,
_marker: std::marker::PhantomData,
}
}
}
impl<T: Topology + FileSystem> Interpret<T> for FileInterpret<T> {
fn execute(&self) {
match &self.score.content {
Some(content) => {
println!("File interpret is writing to {}: {}", self.score.path, content);
// In a real implementation: topology.write_file(&self.score.path, content);
},
None => {
println!("File interpret is reading from {}", self.score.path);
// In a real implementation: let content = topology.read_file(&self.score.path);
}
}
}
}
fn main() {
// Create our topologies
let linux = LinuxHostTopology { hostname: "server1.example.com".to_string() };
let bare_metal = BareMetalTopology { device_id: "device001".to_string() };
// Create our maestros
let linux_maestro = Maestro::new(linux);
let bare_metal_maestro = Maestro::new(bare_metal);
// Create scores
let command_score = CommandScore::new(
"List Files".to_string(),
vec!["ls".to_string(), "-la".to_string()]
);
let file_read_score = FileScore::new_read(
"Read Config".to_string(),
"/etc/config.json".to_string()
);
// This will work because LinuxHostTopology implements CommandExecution
linux_maestro.execute_score(command_score.clone());
// This will work because LinuxHostTopology implements FileSystem
linux_maestro.execute_score(file_read_score.clone());
// This will work because BareMetalTopology implements FileSystem
bare_metal_maestro.execute_score(file_read_score);
// This would NOT compile because BareMetalTopology doesn't implement CommandExecution:
// bare_metal_maestro.execute_score(command_score);
// The error would occur at compile time, ensuring type safety
println!("All scores executed successfully!");
}