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