use crate::{
PeerId, StorageId, StorageKey, UnixTimestamp,
actors::hub::{Hub, State as HubState},
ephemera::EphemeralSession,
io::{IoResult, IoTask, IoTaskId, StorageResult, StorageTask},
};
pub struct SamodLoader {
local_peer_id: PeerId,
state: State,
}
pub enum LoaderState {
NeedIo(Vec<IoTask<StorageTask>>),
Loaded(Box<Hub>),
}
enum State {
Starting,
LoadingStorageId(IoTaskId),
StorageIdLoaded(Option<Vec<u8>>),
PuttingStorageId(IoTaskId, StorageId),
Done(StorageId),
}
impl SamodLoader {
pub fn new(local_peer_id: PeerId) -> Self {
Self {
local_peer_id,
state: State::Starting,
}
}
pub fn step<R: rand::Rng>(&mut self, rng: &mut R, _now: UnixTimestamp) -> LoaderState {
match &self.state {
State::Starting => {
let task = IoTask::new(StorageTask::Load {
key: StorageKey::storage_id_path(),
});
self.state = State::LoadingStorageId(task.task_id);
LoaderState::NeedIo(vec![task])
}
State::LoadingStorageId(_task_id) => LoaderState::NeedIo(Vec::new()),
State::StorageIdLoaded(result) => {
if let Some(result) = result {
match String::from_utf8(result.to_vec()) {
Ok(s) => {
let storage_id = StorageId::from(s);
self.state = State::Done(storage_id.clone());
let state = HubState::new(
storage_id,
self.local_peer_id.clone(),
EphemeralSession::new(rng),
);
return LoaderState::Loaded(Box::new(Hub::new(state)));
}
Err(_e) => {
tracing::warn!("storage ID was not a valid string, creating a new one");
}
}
} else {
tracing::info!("no storage ID found, generating a new one");
}
let storage_id = StorageId::new(rng);
let task = IoTask::new(StorageTask::Put {
key: StorageKey::storage_id_path(),
value: storage_id.as_str().as_bytes().to_vec(),
});
self.state = State::PuttingStorageId(task.task_id, storage_id);
LoaderState::NeedIo(vec![task])
}
State::PuttingStorageId(_task_id, _storage_id) => LoaderState::NeedIo(Vec::new()),
State::Done(storage_id) => {
let state = HubState::new(
storage_id.clone(),
self.local_peer_id.clone(),
EphemeralSession::new(rng),
);
LoaderState::Loaded(Box::new(Hub::new(state)))
}
}
}
pub fn provide_io_result(&mut self, result: IoResult<StorageResult>) {
match self.state {
State::Starting | State::Done(_) | State::StorageIdLoaded(_) => {
panic!("unexpected IO completion");
}
State::LoadingStorageId(io_task_id) => {
if io_task_id != result.task_id {
panic!(
"unexpected task ID: expected {:?}, got {:?}",
io_task_id, result.task_id
);
}
match result.payload {
StorageResult::Load { value } => {
self.state = State::StorageIdLoaded(value);
}
_ => panic!("unexpected storage result when loading storage ID"),
}
}
State::PuttingStorageId(io_task_id, ref storage_id) => {
if io_task_id != result.task_id {
panic!(
"unexpected task ID: expected {:?}, got {:?}",
io_task_id, result.task_id
);
}
match result.payload {
StorageResult::Put => self.state = State::Done(storage_id.clone()),
_ => panic!("unexpected storage result when putting storage ID"),
}
}
}
}
}