Created
September 23, 2024 03:59
-
-
Save mooreniemi/5779706e4d6d787a1ca63b36fa7fb005 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use std::time::Instant; | |
// Define a trait with a simple operation | |
trait Operation { | |
fn execute(&self, input: i32) -> i32; | |
} | |
// Implement the trait for a specific type | |
struct AddOperation; | |
impl Operation for AddOperation { | |
fn execute(&self, input: i32) -> i32 { | |
input + 1 | |
} | |
} | |
// Benchmark using dynamic dispatch (dyn) | |
fn benchmark_dynamic_dispatch(op: &dyn Operation, iterations: i32) -> i32 { | |
let mut result = 0; | |
for _ in 0..iterations { | |
result = op.execute(result); | |
} | |
result | |
} | |
// Benchmark using static dispatch (generics) | |
fn benchmark_static_dispatch<T: Operation>(op: &T, iterations: i32) -> i32 { | |
let mut result = 0; | |
for _ in 0..iterations { | |
result = op.execute(result); | |
} | |
result | |
} | |
// Helper function to run trials | |
fn run_benchmark(iterations: i32) { | |
let op = AddOperation; | |
println!("\nRunning benchmark with {} iterations:", iterations); | |
// Benchmark dynamic dispatch | |
let start = Instant::now(); | |
let result_dynamic = benchmark_dynamic_dispatch(&op, iterations); | |
let duration_dynamic = start.elapsed(); | |
println!( | |
"Dynamic Dispatch Result: {}, Time: {:?}", | |
result_dynamic, duration_dynamic | |
); | |
// Benchmark static dispatch | |
let start = Instant::now(); | |
let result_static = benchmark_static_dispatch(&op, iterations); | |
let duration_static = start.elapsed(); | |
println!( | |
"Static Dispatch Result: {}, Time: {:?}", | |
result_static, duration_static | |
); | |
println!( | |
"Delta (Dynamic - Static): {:?}", | |
duration_dynamic.checked_sub(duration_static) | |
); | |
} | |
/* | |
An example run: | |
Running benchmark with 100000 iterations: | |
Dynamic Dispatch Result: 100000, Time: 1.71875ms | |
Static Dispatch Result: 100000, Time: 1.716292ms | |
Delta (Dynamic - Static): 2.458µs | |
Running benchmark with 1000000 iterations: | |
Dynamic Dispatch Result: 1000000, Time: 13.648916ms | |
Static Dispatch Result: 1000000, Time: 10.246375ms | |
Delta (Dynamic - Static): 3.402541ms | |
Running benchmark with 10000000 iterations: | |
Dynamic Dispatch Result: 10000000, Time: 57.021833ms | |
Static Dispatch Result: 10000000, Time: 37.303916ms | |
Delta (Dynamic - Static): 19.717917ms | |
Running benchmark with 100000000 iterations: | |
Dynamic Dispatch Result: 100000000, Time: 393.276125ms | |
Static Dispatch Result: 100000000, Time: 382.62025ms | |
Delta (Dynamic - Static): 10.655875ms | |
*/ | |
fn main() { | |
// Run the benchmarks with different iteration counts | |
let trials = vec![100_000, 1_000_000, 10_000_000, 100_000_000]; | |
for &iterations in &trials { | |
run_benchmark(iterations); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use rand::Rng; | |
use std::fs::File; | |
use std::io::{self, Read}; | |
use std::sync::{Arc, Mutex}; | |
use std::thread; | |
use std::time::Duration; | |
// Define traits | |
trait Readable: Send + Sync { | |
fn read(&self); | |
} | |
trait Addable: Send + Sync { | |
fn add(&self, new_content: &str); | |
} | |
// Trait for content generation | |
trait ContentGenerator: Send { | |
fn get_new_content(&self) -> String; | |
} | |
// Random content generator | |
struct RandomContentGenerator; | |
impl ContentGenerator for RandomContentGenerator { | |
fn get_new_content(&self) -> String { | |
let random_number: u32 = rand::thread_rng().gen_range(1000..9999); | |
format!("Generated random content: {}\n", random_number) | |
} | |
} | |
// File content generator | |
struct FileContentGenerator { | |
file_path: String, | |
} | |
impl FileContentGenerator { | |
fn new(file_path: String) -> Self { | |
Self { file_path } | |
} | |
} | |
impl ContentGenerator for FileContentGenerator { | |
fn get_new_content(&self) -> String { | |
let mut file = File::open(&self.file_path).expect("Failed to open file"); | |
let mut content = String::new(); | |
file.read_to_string(&mut content) | |
.expect("Failed to read file"); | |
format!("Content from file: {}\n", content) | |
} | |
} | |
// ImmutableFile only supports reading | |
struct ImmutableFile { | |
content: String, | |
} | |
impl ImmutableFile { | |
fn new(file_path: &str) -> io::Result<Self> { | |
let mut file = File::open(file_path)?; | |
let mut content = String::new(); | |
file.read_to_string(&mut content)?; | |
Ok(Self { content }) | |
} | |
} | |
impl Readable for ImmutableFile { | |
fn read(&self) { | |
println!("Immutable Read: {}", self.content); | |
} | |
} | |
// MutableFile supports reading and adding content using Mutex for locking | |
struct MutableFile { | |
content: Arc<Mutex<String>>, // Mutex for thread-safe access | |
} | |
impl MutableFile { | |
fn new(file_path: &str) -> io::Result<Self> { | |
let mut file = File::open(file_path)?; | |
let mut content = String::new(); | |
file.read_to_string(&mut content)?; | |
Ok(Self { | |
content: Arc::new(Mutex::new(content)), | |
}) | |
} | |
} | |
impl Readable for MutableFile { | |
fn read(&self) { | |
let content = self.content.lock().unwrap(); | |
println!("Mutable Read: {}", content); | |
} | |
} | |
impl Addable for MutableFile { | |
fn add(&self, new_content: &str) { | |
let mut content = self.content.lock().unwrap(); | |
content.push_str(new_content); | |
println!("Added content: {}", new_content); | |
} | |
} | |
// Enum to represent either an ImmutableFile or a MutableFile | |
#[derive(Clone)] | |
enum FileType { | |
Immutable(Arc<ImmutableFile>), | |
Mutable(Arc<MutableFile>), | |
} | |
// ReadEngine handles multiple files: one required and two optional | |
struct ReadEngine { | |
one: FileType, | |
two: Option<FileType>, | |
three: Option<FileType>, | |
} | |
impl ReadEngine { | |
fn new(one: FileType, two: Option<FileType>, three: Option<FileType>) -> Self { | |
Self { one, two, three } | |
} | |
fn read(&self) { | |
println!("Reading from file one:"); | |
match &self.one { | |
FileType::Immutable(file) => file.read(), | |
FileType::Mutable(file) => file.read(), | |
} | |
if let Some(ref file) = self.two { | |
println!("Reading from file two:"); | |
match file { | |
FileType::Immutable(file) => file.read(), | |
FileType::Mutable(file) => file.read(), | |
} | |
} | |
if let Some(ref file) = self.three { | |
println!("Reading from file three:"); | |
match file { | |
FileType::Immutable(file) => file.read(), | |
FileType::Mutable(file) => file.read(), | |
} | |
} | |
} | |
} | |
// AddEngine using dynamic dispatch for Addable | |
struct AddEngine { | |
one: Arc<dyn Addable>, | |
two: Option<Arc<dyn Addable>>, | |
three: Option<Arc<dyn Addable>>, | |
content_generator: Box<dyn ContentGenerator>, | |
} | |
impl AddEngine { | |
fn new( | |
one: Arc<dyn Addable>, | |
two: Option<Arc<dyn Addable>>, | |
three: Option<Arc<dyn Addable>>, | |
content_generator: Box<dyn ContentGenerator>, | |
) -> Self { | |
Self { | |
one, | |
two, | |
three, | |
content_generator, | |
} | |
} | |
fn add(&self, new_content: &str) { | |
println!("Adding to file one:"); | |
self.one.add(new_content); | |
if let Some(ref file) = self.two { | |
println!("Adding to file two:"); | |
file.add(new_content); | |
} | |
if let Some(ref file) = self.three { | |
println!("Adding to file three:"); | |
file.add(new_content); | |
} | |
} | |
fn start_polling(self) { | |
let one = Arc::clone(&self.one); | |
let two = self.two.clone(); | |
let three = self.three.clone(); | |
let content_generator = self.content_generator; | |
thread::spawn(move || loop { | |
let new_content = content_generator.get_new_content(); | |
println!("Polling: Adding to file one:"); | |
one.add(&new_content); | |
if let Some(ref file) = two { | |
println!("Polling: Adding to file two:"); | |
file.add(&new_content); | |
} | |
if let Some(ref file) = three { | |
println!("Polling: Adding to file three:"); | |
file.add(&new_content); | |
} | |
thread::sleep(Duration::from_secs(2)); // Polling interval | |
}); | |
} | |
} | |
// Now use `FileType` for ReadEngine and AddEngine creation | |
fn create_engines( | |
mode: &str, | |
file_paths: &[&str], | |
generator: Box<dyn ContentGenerator>, | |
) -> (ReadEngine, Option<AddEngine>) { | |
let read_one: FileType = match mode { | |
"immutable" => FileType::Immutable(Arc::new( | |
ImmutableFile::new(file_paths[0]).expect("Failed to load file"), | |
)), | |
"mutable" => FileType::Mutable(Arc::new( | |
MutableFile::new(file_paths[0]).expect("Failed to load file"), | |
)), | |
_ => panic!("Invalid mode! Use 'immutable' or 'mutable'."), | |
}; | |
let read_two = if file_paths.len() > 1 { | |
Some(match mode { | |
"immutable" => FileType::Immutable(Arc::new( | |
ImmutableFile::new(file_paths[1]).expect("Failed to load file"), | |
)), | |
"mutable" => FileType::Mutable(Arc::new( | |
MutableFile::new(file_paths[1]).expect("Failed to load file"), | |
)), | |
_ => panic!("Invalid mode! Use 'immutable' or 'mutable'."), | |
}) | |
} else { | |
None | |
}; | |
let read_three = if file_paths.len() > 2 { | |
Some(match mode { | |
"immutable" => FileType::Immutable(Arc::new( | |
ImmutableFile::new(file_paths[2]).expect("Failed to load file"), | |
)), | |
"mutable" => FileType::Mutable(Arc::new( | |
MutableFile::new(file_paths[2]).expect("Failed to load file"), | |
)), | |
_ => panic!("Invalid mode! Use 'immutable' or 'mutable'."), | |
}) | |
} else { | |
None | |
}; | |
let read_engine = ReadEngine::new(read_one.clone(), read_two.clone(), read_three.clone()); | |
let add_engine = if mode == "mutable" { | |
Some(AddEngine::new( | |
match read_one { | |
FileType::Mutable(file) => file as Arc<dyn Addable>, | |
_ => panic!("Invalid state"), | |
}, | |
read_two.and_then(|f| match f { | |
FileType::Mutable(file) => Some(file as Arc<dyn Addable>), | |
_ => None, | |
}), | |
read_three.and_then(|f| match f { | |
FileType::Mutable(file) => Some(file as Arc<dyn Addable>), | |
_ => None, | |
}), | |
generator, | |
)) | |
} else { | |
None | |
}; | |
(read_engine, add_engine) | |
} | |
fn main() { | |
let mode = std::env::args() | |
.nth(1) | |
.expect("No mode provided! Use 'immutable' or 'mutable'"); | |
let generator_type = std::env::args() | |
.nth(2) | |
.expect("No content generator type provided! Use 'random' or 'file'"); | |
// File paths | |
let file_path_one = "/tmp/example.txt"; | |
let file_path_two = "/tmp/example.txt"; | |
let file_path_three = "/tmp/example.txt"; | |
// Include optional files based on feature flags | |
let include_file_two = std::env::args().nth(3).as_deref() == Some("file2"); | |
let include_file_three = std::env::args().nth(4).as_deref() == Some("file3"); | |
// Add file paths to the vector based on whether optional files are included | |
let mut file_paths = vec![file_path_one]; // File one is always required | |
if include_file_two { | |
file_paths.push(file_path_two); | |
} | |
if include_file_three { | |
file_paths.push(file_path_three); | |
} | |
// Choose the content generator based on user input | |
let generator: Box<dyn ContentGenerator> = match generator_type.as_str() { | |
"file" => Box::new(FileContentGenerator::new(file_path_one.to_string())), | |
"random" => Box::new(RandomContentGenerator), | |
_ => panic!("Invalid content generator type! Use 'random' or 'file'."), | |
}; | |
// Create engines based on the mode (immutable or mutable) and file paths | |
let (read_engine, add_engine) = create_engines(&mode, &file_paths, generator); | |
// Start reading from files in a separate thread | |
let read_thread = thread::spawn(move || { | |
for _ in 0..5 { | |
read_engine.read(); | |
thread::sleep(Duration::from_secs(1)); | |
} | |
}); | |
// If we're in mutable mode, start polling for new content and adding it to the files | |
if let Some(add_engine) = add_engine { | |
add_engine.start_polling(); // Start polling for new content | |
} | |
// Wait for the reading thread to finish | |
read_thread.join().unwrap(); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use rand::Rng; | |
use std::fs::File; | |
use std::io::{self, Read}; | |
use std::sync::{Arc, Mutex}; | |
use std::thread; | |
use std::time::Duration; | |
// Define traits | |
trait Readable: Send + Sync { | |
fn read(&self); | |
} | |
trait Addable: Send + Sync { | |
fn add(&self, new_content: &str); | |
} | |
// Trait for content generation | |
trait ContentGenerator: Send { | |
fn get_new_content(&self) -> String; | |
} | |
// Random content generator | |
struct RandomContentGenerator; | |
impl ContentGenerator for RandomContentGenerator { | |
fn get_new_content(&self) -> String { | |
let random_number: u32 = rand::thread_rng().gen_range(1000..9999); | |
format!("Generated random content: {}\n", random_number) | |
} | |
} | |
// File content generator | |
struct FileContentGenerator { | |
file_path: String, | |
} | |
impl FileContentGenerator { | |
fn new(file_path: String) -> Self { | |
Self { file_path } | |
} | |
} | |
impl ContentGenerator for FileContentGenerator { | |
fn get_new_content(&self) -> String { | |
let mut file = File::open(&self.file_path).expect("Failed to open file"); | |
let mut content = String::new(); | |
file.read_to_string(&mut content) | |
.expect("Failed to read file"); | |
format!("Content from file: {}\n", content) | |
} | |
} | |
// ------------------- FILE IMPLEMENTATION ------------------- | |
// ImmutableFile for immutable mode | |
#[cfg(feature = "immutable")] | |
pub struct ImmutableFile { | |
content: String, | |
} | |
#[cfg(feature = "immutable")] | |
impl ImmutableFile { | |
pub fn new(file_path: &str) -> io::Result<Self> { | |
let mut file = File::open(file_path)?; | |
let mut content = String::new(); | |
file.read_to_string(&mut content)?; | |
Ok(Self { content }) | |
} | |
} | |
#[cfg(feature = "immutable")] | |
impl Readable for ImmutableFile { | |
fn read(&self) { | |
println!("Immutable Read: {}", self.content); | |
} | |
} | |
// MutableFile for mutable mode | |
#[cfg(feature = "mutable")] | |
pub struct MutableFile { | |
content: Arc<Mutex<String>>, | |
} | |
#[cfg(feature = "mutable")] | |
impl MutableFile { | |
pub fn new(file_path: &str) -> io::Result<Self> { | |
let mut file = File::open(file_path)?; | |
let mut content = String::new(); | |
file.read_to_string(&mut content)?; | |
Ok(Self { | |
content: Arc::new(Mutex::new(content)), | |
}) | |
} | |
} | |
#[cfg(feature = "mutable")] | |
impl Readable for MutableFile { | |
fn read(&self) { | |
let content = self.content.lock().unwrap(); | |
println!("Mutable Read: {}", content); | |
} | |
} | |
#[cfg(feature = "mutable")] | |
impl Addable for MutableFile { | |
fn add(&self, new_content: &str) { | |
let mut content = self.content.lock().unwrap(); | |
content.push_str(new_content); | |
println!("Added content: {}", new_content); | |
} | |
} | |
// ------------------- ENGINE IMPLEMENTATION ------------------- | |
// ReadEngine handles multiple files: one required and two optional | |
struct ReadEngine { | |
one: Arc<dyn Readable>, | |
two: Option<Arc<dyn Readable>>, | |
three: Option<Arc<dyn Readable>>, | |
} | |
impl ReadEngine { | |
fn new( | |
one: Arc<dyn Readable>, | |
two: Option<Arc<dyn Readable>>, | |
three: Option<Arc<dyn Readable>>, | |
) -> Self { | |
Self { one, two, three } | |
} | |
fn read(&self) { | |
println!("Reading from file one:"); | |
self.one.read(); | |
if let Some(ref file) = self.two { | |
println!("Reading from file two:"); | |
file.read(); | |
} | |
if let Some(ref file) = self.three { | |
println!("Reading from file three:"); | |
file.read(); | |
} | |
} | |
} | |
// AddEngine handles multiple files: one required and two optional | |
struct AddEngine { | |
one: Arc<dyn Addable>, | |
two: Option<Arc<dyn Addable>>, | |
three: Option<Arc<dyn Addable>>, | |
content_generator: Box<dyn ContentGenerator>, | |
} | |
impl AddEngine { | |
fn new( | |
one: Arc<dyn Addable>, | |
two: Option<Arc<dyn Addable>>, | |
three: Option<Arc<dyn Addable>>, | |
content_generator: Box<dyn ContentGenerator>, | |
) -> Self { | |
Self { | |
one, | |
two, | |
three, | |
content_generator, | |
} | |
} | |
fn add(&self, new_content: &str) { | |
println!("Adding to file one:"); | |
self.one.add(new_content); | |
if let Some(ref file) = self.two { | |
println!("Adding to file two:"); | |
file.add(new_content); | |
} | |
if let Some(ref file) = self.three { | |
println!("Adding to file three:"); | |
file.add(new_content); | |
} | |
} | |
fn start_polling(self) { | |
let one = Arc::clone(&self.one); | |
let two = self.two.clone(); | |
let three = self.three.clone(); | |
let content_generator = self.content_generator; | |
thread::spawn(move || loop { | |
let new_content = content_generator.get_new_content(); | |
println!("Polling: Adding to file one:"); | |
one.add(&new_content); | |
if let Some(ref file) = two { | |
println!("Polling: Adding to file two:"); | |
file.add(&new_content); | |
} | |
if let Some(ref file) = three { | |
println!("Polling: Adding to file three:"); | |
file.add(&new_content); | |
} | |
thread::sleep(Duration::from_secs(2)); // Polling interval | |
}); | |
} | |
} | |
// Create engines | |
fn create_engines( | |
file_paths: &[&str], | |
generator: Box<dyn ContentGenerator>, | |
) -> (ReadEngine, Option<AddEngine>) { | |
#[cfg(feature = "immutable")] | |
{ | |
let one = Arc::new(ImmutableFile::new(file_paths[0]).expect("Failed to load file")) | |
as Arc<dyn Readable>; | |
let two = if file_paths.len() > 1 { | |
Some( | |
Arc::new(ImmutableFile::new(file_paths[1]).expect("Failed to load file")) | |
as Arc<dyn Readable>, | |
) | |
} else { | |
None | |
}; | |
let three = if file_paths.len() > 2 { | |
Some( | |
Arc::new(ImmutableFile::new(file_paths[2]).expect("Failed to load file")) | |
as Arc<dyn Readable>, | |
) | |
} else { | |
None | |
}; | |
(ReadEngine::new(one, two, three), None) | |
} | |
#[cfg(feature = "mutable")] | |
{ | |
let one = Arc::new(MutableFile::new(file_paths[0]).expect("Failed to load file")); | |
let one_add = Arc::clone(&one) as Arc<dyn Addable>; | |
let two = if file_paths.len() > 1 { | |
let file = Arc::new(MutableFile::new(file_paths[1]).expect("Failed to load file")); | |
( | |
Some(Arc::clone(&file) as Arc<dyn Readable>), | |
Some(Arc::clone(&file) as Arc<dyn Addable>), | |
) | |
} else { | |
(None, None) | |
}; | |
let three = if file_paths.len() > 2 { | |
let file = Arc::new(MutableFile::new(file_paths[2]).expect("Failed to load file")); | |
( | |
Some(Arc::clone(&file) as Arc<dyn Readable>), | |
Some(Arc::clone(&file) as Arc<dyn Addable>), | |
) | |
} else { | |
(None, None) | |
}; | |
let read_engine = ReadEngine::new(one.clone() as Arc<dyn Readable>, two.0, three.0); | |
let add_engine = AddEngine::new(one_add, two.1, three.1, generator); | |
(read_engine, Some(add_engine)) | |
} | |
} | |
/* | |
Run with the feature gating: | |
```text | |
cargo run --example switching_seven --features mutable -- random | |
cargo run --example switching_seven --features immutable --no-default-features -- random | |
``` | |
Expects `Cargo.toml`: | |
```text | |
[features] | |
default = ["mutable"] | |
mutable = [] | |
immutable = [] | |
``` | |
*/ | |
fn main() { | |
let generator_type = std::env::args() | |
.nth(1) | |
.expect("No content generator type provided! Use 'random' or 'file'"); | |
// File paths | |
let file_path_one = "/tmp/example_one.txt"; | |
let file_path_two = "/tmp/example_two.txt"; | |
let file_path_three = "/tmp/example_three.txt"; | |
// Include optional files based on feature flags | |
let include_file_two = std::env::args().nth(2).as_deref() == Some("file2"); | |
let include_file_three = std::env::args().nth(3).as_deref() == Some("file3"); | |
// Add file paths to the vector based on whether optional files are included | |
let mut file_paths = vec![file_path_one]; // File one is always required | |
if include_file_two { | |
file_paths.push(file_path_two); | |
} | |
if include_file_three { | |
file_paths.push(file_path_three); | |
} | |
// Choose the content generator based on user input | |
// Choose the content generator based on user input | |
let generator: Box<dyn ContentGenerator> = match generator_type.as_str() { | |
"file" => Box::new(FileContentGenerator::new(file_path_one.to_string())), | |
"random" => Box::new(RandomContentGenerator), | |
_ => panic!("Invalid content generator type! Use 'random' or 'file'."), | |
}; | |
// Create engines based on the feature enabled (either mutable or immutable) | |
let (read_engine, add_engine) = create_engines(&file_paths, generator); | |
// Start reading from files in a separate thread | |
let read_thread = thread::spawn(move || { | |
for _ in 0..5 { | |
read_engine.read(); | |
thread::sleep(Duration::from_secs(1)); | |
} | |
}); | |
// If we're in mutable mode, start polling for new content and adding it to the files | |
#[cfg(feature = "mutable")] | |
if let Some(add_engine) = add_engine { | |
add_engine.start_polling(); // Start polling for new content | |
} | |
// Wait for the reading thread to finish | |
read_thread.join().unwrap(); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use rand::Rng; | |
use std::fs::File; | |
use std::io::{self, Read}; | |
use std::sync::{Arc, Mutex}; | |
use std::thread; | |
use std::time::Duration; | |
// Define traits | |
trait Readable: Send + Sync { | |
fn read(&self); | |
} | |
trait Addable: Send + Sync { | |
fn add(&self, new_content: &str); | |
} | |
// Trait for content generation | |
trait ContentGenerator: Send { | |
fn get_new_content(&self) -> String; | |
} | |
// Random content generator | |
struct RandomContentGenerator; | |
impl ContentGenerator for RandomContentGenerator { | |
fn get_new_content(&self) -> String { | |
let random_number: u32 = rand::thread_rng().gen_range(1000..9999); | |
format!("Generated random content: {}\n", random_number) | |
} | |
} | |
// File content generator | |
struct FileContentGenerator { | |
file_path: String, | |
} | |
impl FileContentGenerator { | |
fn new(file_path: String) -> Self { | |
Self { file_path } | |
} | |
} | |
impl ContentGenerator for FileContentGenerator { | |
fn get_new_content(&self) -> String { | |
let mut file = File::open(&self.file_path).expect("Failed to open file"); | |
let mut content = String::new(); | |
file.read_to_string(&mut content) | |
.expect("Failed to read file"); | |
format!("Content from file: {}\n", content) | |
} | |
} | |
// ImmutableFile only supports reading | |
struct ImmutableFile { | |
content: String, | |
} | |
impl ImmutableFile { | |
fn new(file_path: &str) -> io::Result<Self> { | |
let mut file = File::open(file_path)?; | |
let mut content = String::new(); | |
file.read_to_string(&mut content)?; | |
Ok(Self { content }) | |
} | |
} | |
impl Readable for ImmutableFile { | |
fn read(&self) { | |
println!("Immutable Read: {}", self.content); | |
} | |
} | |
// MutableFile supports reading and adding content using Mutex for locking | |
struct MutableFile { | |
content: Arc<Mutex<String>>, // Mutex for thread-safe access | |
} | |
impl MutableFile { | |
fn new(file_path: &str) -> io::Result<Self> { | |
let mut file = File::open(file_path)?; | |
let mut content = String::new(); | |
file.read_to_string(&mut content)?; | |
Ok(Self { | |
content: Arc::new(Mutex::new(content)), | |
}) | |
} | |
} | |
impl Readable for MutableFile { | |
fn read(&self) { | |
let content = self.content.lock().unwrap(); | |
println!("Mutable Read: {}", content); | |
} | |
} | |
impl Addable for MutableFile { | |
fn add(&self, new_content: &str) { | |
let mut content = self.content.lock().unwrap(); | |
content.push_str(new_content); | |
println!("Added content: {}", new_content); | |
} | |
} | |
// ReadEngine handles multiple files: one required and two optional | |
struct ReadEngine { | |
one: Arc<dyn Readable>, | |
two: Option<Arc<dyn Readable>>, | |
three: Option<Arc<dyn Readable>>, | |
} | |
impl ReadEngine { | |
fn new( | |
one: Arc<dyn Readable>, | |
two: Option<Arc<dyn Readable>>, | |
three: Option<Arc<dyn Readable>>, | |
) -> Self { | |
Self { one, two, three } | |
} | |
fn read(&self) { | |
println!("Reading from file one:"); | |
self.one.read(); | |
if let Some(ref file) = self.two { | |
println!("Reading from file two:"); | |
file.read(); | |
} | |
if let Some(ref file) = self.three { | |
println!("Reading from file three:"); | |
file.read(); | |
} | |
} | |
} | |
// AddEngine handles multiple files: one required and two optional | |
struct AddEngine { | |
one: Arc<dyn Addable>, | |
two: Option<Arc<dyn Addable>>, | |
three: Option<Arc<dyn Addable>>, | |
content_generator: Box<dyn ContentGenerator>, | |
} | |
impl AddEngine { | |
fn new( | |
one: Arc<dyn Addable>, | |
two: Option<Arc<dyn Addable>>, | |
three: Option<Arc<dyn Addable>>, | |
content_generator: Box<dyn ContentGenerator>, | |
) -> Self { | |
Self { | |
one, | |
two, | |
three, | |
content_generator, | |
} | |
} | |
fn add(&self, new_content: &str) { | |
println!("Adding to file one:"); | |
self.one.add(new_content); | |
if let Some(ref file) = self.two { | |
println!("Adding to file two:"); | |
file.add(new_content); | |
} | |
if let Some(ref file) = self.three { | |
println!("Adding to file three:"); | |
file.add(new_content); | |
} | |
} | |
fn start_polling(self) { | |
let one = Arc::clone(&self.one); | |
let two = self.two.clone(); | |
let three = self.three.clone(); | |
let content_generator = self.content_generator; | |
thread::spawn(move || loop { | |
let new_content = content_generator.get_new_content(); | |
println!("Polling: Adding to file one:"); | |
one.add(&new_content); | |
if let Some(ref file) = two { | |
println!("Polling: Adding to file two:"); | |
file.add(&new_content); | |
} | |
if let Some(ref file) = three { | |
println!("Polling: Adding to file three:"); | |
file.add(&new_content); | |
} | |
thread::sleep(Duration::from_secs(2)); // Polling interval | |
}); | |
} | |
} | |
fn create_engines( | |
mode: &str, | |
file_paths: &[&str], | |
generator: Box<dyn ContentGenerator>, | |
) -> (ReadEngine, Option<AddEngine>) { | |
// Handle 'one' separately for immutable and mutable cases | |
let (read_one, add_one) = match mode { | |
"immutable" => { | |
let file = Arc::new(ImmutableFile::new(file_paths[0]).expect("Failed to load file")); | |
(file as Arc<dyn Readable>, None) | |
} | |
"mutable" => { | |
let file = Arc::new(MutableFile::new(file_paths[0]).expect("Failed to load file")); | |
( | |
Arc::clone(&file) as Arc<dyn Readable>, | |
Some(file as Arc<dyn Addable>), | |
) | |
} | |
_ => panic!("Invalid mode! Use 'immutable' or 'mutable'."), | |
}; | |
// Handle 'two' if present | |
let (read_two, add_two) = if file_paths.len() > 1 { | |
match mode { | |
"immutable" => { | |
let file = | |
Arc::new(ImmutableFile::new(file_paths[1]).expect("Failed to load file")); | |
(Some(file as Arc<dyn Readable>), None) | |
} | |
"mutable" => { | |
let file = Arc::new(MutableFile::new(file_paths[1]).expect("Failed to load file")); | |
( | |
Some(Arc::clone(&file) as Arc<dyn Readable>), | |
Some(file as Arc<dyn Addable>), | |
) | |
} | |
_ => panic!("Invalid mode! Use 'immutable' or 'mutable'."), | |
} | |
} else { | |
(None, None) | |
}; | |
// Handle 'three' if present | |
let (read_three, add_three) = if file_paths.len() > 2 { | |
match mode { | |
"immutable" => { | |
let file = | |
Arc::new(ImmutableFile::new(file_paths[2]).expect("Failed to load file")); | |
(Some(file as Arc<dyn Readable>), None) | |
} | |
"mutable" => { | |
let file = Arc::new(MutableFile::new(file_paths[2]).expect("Failed to load file")); | |
( | |
Some(Arc::clone(&file) as Arc<dyn Readable>), | |
Some(file as Arc<dyn Addable>), | |
) | |
} | |
_ => panic!("Invalid mode! Use 'immutable' or 'mutable'."), | |
} | |
} else { | |
(None, None) | |
}; | |
// Create ReadEngine using the read files | |
let read_engine = ReadEngine::new(read_one, read_two, read_three); | |
// Create AddEngine only for mutable files | |
let add_engine = if mode == "mutable" { | |
Some(AddEngine::new( | |
add_one.expect("AddEngine requires at least one file"), | |
add_two, | |
add_three, | |
generator, | |
)) | |
} else { | |
None | |
}; | |
(read_engine, add_engine) | |
} | |
fn main() { | |
let mode = std::env::args() | |
.nth(1) | |
.expect("No mode provided! Use 'immutable' or 'mutable'"); | |
let generator_type = std::env::args() | |
.nth(2) | |
.expect("No content generator type provided! Use 'random' or 'file'"); | |
// File paths | |
let file_path_one = "/tmp/example.txt"; | |
let file_path_two = "/tmp/example.txt"; | |
let file_path_three = "/tmp/example.txt"; | |
// Include optional files based on feature flags | |
let include_file_two = std::env::args().nth(3).as_deref() == Some("file2"); | |
let include_file_three = std::env::args().nth(4).as_deref() == Some("file3"); | |
// Add file paths to the vector based on whether optional files are included | |
let mut file_paths = vec![file_path_one]; // File one is always required | |
if include_file_two { | |
file_paths.push(file_path_two); | |
} | |
if include_file_three { | |
file_paths.push(file_path_three); | |
} | |
// Choose the content generator based on user input | |
let generator: Box<dyn ContentGenerator> = match generator_type.as_str() { | |
"file" => Box::new(FileContentGenerator::new(file_path_one.to_string())), | |
"random" => Box::new(RandomContentGenerator), | |
_ => panic!("Invalid content generator type! Use 'random' or 'file'."), | |
}; | |
// Create engines based on the mode (immutable or mutable) and file paths | |
let (read_engine, add_engine) = create_engines(&mode, &file_paths, generator); | |
// Start reading from files in a separate thread | |
let read_thread = thread::spawn(move || { | |
for _ in 0..5 { | |
read_engine.read(); | |
thread::sleep(Duration::from_secs(1)); | |
} | |
}); | |
// If we're in mutable mode, start polling for new content and adding it to the files | |
if let Some(add_engine) = add_engine { | |
add_engine.start_polling(); // Start polling for new content | |
} | |
// Wait for the reading thread to finish | |
read_thread.join().unwrap(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment