samod_core/loader.rs
1use std::sync::{Arc, Mutex};
2
3use crate::{
4 PeerId, UnixTimestamp,
5 actors::{
6 driver::{Driver, StepResult},
7 hub::Hub,
8 loading::{self, Loading},
9 },
10 io::{IoResult, IoTask, StorageResult, StorageTask},
11};
12
13/// A state machine for loading a samod repository.
14///
15/// `SamodLoader` handles the initialization phase of a samod repository,
16/// coordinating between the user and the driver to load or generate the storage ID
17/// and perform any other setup operations required before the repository can be used.
18///
19/// ## Usage
20///
21/// ```rust,no_run
22/// use samod_core::{PeerId, SamodLoader, LoaderState, UnixTimestamp, io::{StorageResult, IoResult}};
23/// use rand::SeedableRng;
24///
25/// let rng = rand::rngs::StdRng::from_rng(&mut rand::rng());
26/// let mut loader = SamodLoader::new(rng, PeerId::from("test"), UnixTimestamp::now());
27///
28/// loop {
29/// match loader.step(UnixTimestamp::now()) {
30/// LoaderState::NeedIo(tasks) => {
31/// // Execute IO tasks and provide results
32/// for task in tasks {
33/// // ... execute task ...
34/// # let result: IoResult<StorageResult> = todo!();
35/// loader.provide_io_result(UnixTimestamp::now(), result);
36/// }
37/// }
38/// LoaderState::Loaded(samod) => {
39/// // Repository is loaded and ready to use
40/// break;
41/// }
42/// }
43/// }
44/// ```
45pub struct SamodLoader<R> {
46 driver: Driver<Loading<R>>,
47}
48
49/// The current state of the loader.
50pub enum LoaderState {
51 /// The loader needs IO operations to be performed.
52 ///
53 /// The caller should execute all provided IO tasks and call
54 /// `provide_io_result` for each completed task, then call `step` again.
55 NeedIo(Vec<IoTask<StorageTask>>),
56
57 /// Loading is complete and the samod repository is ready to use.
58 Loaded(Hub),
59}
60
61impl<R: rand::Rng + Clone + Send + Sync + 'static> SamodLoader<R> {
62 /// Creates a new samod loader.
63 ///
64 /// # Arguments
65 ///
66 /// * `now` - The current timestamp for initialization
67 ///
68 /// # Returns
69 ///
70 /// A new `SamodLoader` ready to begin the loading process.
71 pub fn new(rng: R, local_peer_id: PeerId, now: UnixTimestamp) -> Self {
72 let driver = Driver::spawn(now, |args| loading::load(rng, local_peer_id, args));
73
74 Self { driver }
75 }
76
77 /// Advances the loader state machine.
78 ///
79 /// This method should be called repeatedly until `LoaderState::Loaded` is returned.
80 /// When `LoaderState::NeedIo` is returned, the caller must execute the provided
81 /// IO tasks and call `provide_io_result` for each one before calling `step` again.
82 ///
83 /// # Arguments
84 ///
85 /// * `now` - The current timestamp
86 ///
87 /// # Returns
88 ///
89 /// The current state of the loader.
90 pub fn step(&mut self, now: UnixTimestamp) -> LoaderState {
91 let new_tasks = match self.driver.step(now) {
92 StepResult::Suspend(tasks) => tasks,
93 StepResult::Complete {
94 results,
95 complete: (hub_state, rng),
96 } => {
97 assert!(results.is_empty());
98 let state = Arc::new(Mutex::new(hub_state));
99 let hub = Hub::new(rng, now, state);
100 return LoaderState::Loaded(hub);
101 }
102 };
103
104 LoaderState::NeedIo(new_tasks)
105 }
106
107 /// Provides the result of an IO operation.
108 ///
109 /// This method should be called for each IO task that was returned by `step`.
110 /// The loader passes the result directly to the driver for processing.
111 ///
112 /// # Arguments
113 ///
114 /// * `result` - The result of executing an IO task
115 pub fn provide_io_result(&mut self, now: UnixTimestamp, result: IoResult<StorageResult>) {
116 self.driver.handle_io_complete(now, result);
117 }
118}