type AS = AccountScope;
type ME = MgmtError;
+type IE = InternalError;
#[derive(Error,Debug,Clone,Copy,Serialize,Deserialize)]
#[derive(Hash,Ord,Eq,PartialOrd,PartialEq)]
let ok = self.save_accounts_now();
match ok {
Ok(()) => Ok(output),
- Err(e) => Err((e, output))
+ Err(e) => Err((e.into(), output))
}
}
self.save_accounts_now()?;
}
- pub fn save_accounts_now(&self) -> Result<(), InternalError> {
- panic!("xxx")
+ #[throws(AccountsSaveError)]
+ pub fn save_accounts_now(&self) {
+ let accounts = self.0.as_ref().expect("loaded");
+ let main = save_path();
+ let tmp = format!("{}.tmp", &main);
+ let f = fs::File::create(&tmp)?;
+ let f = BufWriter::new(f);
+ rmp_serde::encode::write_named(&mut f, &accounts)?;
+ f.flush()?;
+ let f = f.into_inner().map_err(|e| {
+ io::Error::new(e.error().kind(), e)
+ })?;
+ f.sync_data()?;
+ f.close()?;
+ fs::rename(&tmp, &main)?;
}
}
+#[derive(Error,Debug)]
+pub enum AccountsSaveError {
+ #[error("Error writing/installing file: {0}")]
+ IO(#[from] io::Error),
+ #[error("Error encoding msgpack: {0}")]
+ Encode(#[from] rmp_serde::encode::Error)
+}
+
+const ACCOUNTS_FILE : &str = "accounts";
+
+fn save_path() -> String {
+ format!("{}/{}", config().save_directory, &ACCOUNTS_FILE)
+}
+
+#[throws(StartupError)]
+pub fn load_accounts() {
+ let ag = AccountsGuard::lock();
+ assert!(ag.0.is_none());
+ let path = save_path();
+ let f = fs::File::open(&path);
+ let f = match f {
+ Ok(f) => f,
+ Err(e) if e.kind() == io::ErrorKind::NotFound => return,
+ e@ Err(_) => e.with_context(|| path.clone())?,
+ };
+ let f = BufReader::new(f);
+ let accounts : Accounts = rmp_serde::decode::from_read(&mut f)?;
+ let chk = |acctid: AccountId, account: &AccountName| if_chain! {
+ if accounts.names.get(account) == Some(&acctid);
+ if let Some(got_record) = accounts.records.get(acctid);
+ if got_record.account = account;
+ then { Ok(()) }
+ else { Err(IE::AccountsCorrupted(acctid, account.clone())) }
+ };
+ for (account, acctid) in &accounts.names { chk(*acctid, account) }
+ for (acctid, account) in &accounts.records { chk(*acctid, account) }
+ *ag = accounts;
+}
+
impl AccountRecord {
pub fn expire_tokens_revealed(&mut self) {
panic!("xxx")
use crate::imports::*;
+type IE = InternalError;
+
#[derive(Error,Debug)]
pub enum OnlineError {
#[error("Game in process of being destroyed")]
pub enum InternalError {
#[error("Game corrupted by previous crash")]
GameCorrupted,
+ #[error("Accounts file corrupted for acctid={0:?} account={1:?}")]
+ AccountsCorrupted(AccountId, Arc<AccountName>),
+ #[error("Error saving accounts file: {0}")]
+ AccountsSaveError(#[from] AccountsSaveError),
#[error("Server MessagePack encoding error {0}")]
MessagePackEncodeFail(#[from] rmp_serde::encode::Error),
#[error("Server MessagePack decoding error (game load failed) {0}")]
SpecError::InternalError(format!("{:?}",ie))
}
}
-
#[derive(Error,Debug,Serialize,Clone)]
pub enum ErrorSignaledViaUpdate {
InternalError,
InternalError::SVGProcessingFailed(se).into()
}
}
+
+impl From<AccountsSaveError> for MgmtError {
+ fn from(ase: AccountsSaveError) -> MgmtError { IE::from(ase).into() }
+}
// SPDX-License-Identifier: AGPL-3.0-or-later
// There is NO WARRANTY.
+use std::fs;
+use std::io;
+use std::os::unix::io::IntoRawFd;
+
+use fehler::{throw, throws};
+use libc;
+
pub trait OrdExt : Ord + Sized + Clone {
fn update_max(&mut self, new: &Self) {
if *new > *self { *self = new.clone() }
}
}
}
+
+// https://github.com/rust-lang/rust/issues/32255 :-(
+
+pub trait LocalFileExt {
+ fn close(self) -> Result<(), io::Error>;
+}
+
+impl LocalFileExt for fs::File {
+ #[throws(io::Error)]
+ fn close(self) {
+ let r = unsafe {
+ let fd = self.into_raw_fd();
+ libc::close(fd)
+ };
+ if r == 0 {
+ ()
+ } else if r == -1 {
+ throw!(io::Error::last_os_error())
+ } else {
+ panic!("close(2) returned {}", r)
+ }
+ }
+}