Skip to content

Commit

Permalink
Implements profile loading (#1165)
Browse files Browse the repository at this point in the history
  • Loading branch information
ultimaweapon authored Dec 7, 2024
1 parent ed184e4 commit b2b4db0
Show file tree
Hide file tree
Showing 8 changed files with 144 additions and 56 deletions.
1 change: 0 additions & 1 deletion gui/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ endif()

# Setup GUI.
add_executable(obliteration WIN32 MACOSX_BUNDLE
app_data.cpp
main.cpp
main_window.cpp
path.cpp
Expand Down
16 changes: 0 additions & 16 deletions gui/app_data.cpp

This file was deleted.

5 changes: 0 additions & 5 deletions gui/app_data.hpp

This file was deleted.

17 changes: 14 additions & 3 deletions gui/src/data/mod.rs
Original file line number Diff line number Diff line change
@@ -1,36 +1,44 @@
pub use self::part::*;
pub use self::prof::*;

use std::io::ErrorKind;
use std::path::{Path, PathBuf};
use thiserror::Error;

mod part;
mod prof;

/// Manages all files and directories that stored in the data root.
pub struct DataMgr {
root: PathBuf,
part: Part,
prof: Prof,
}

impl DataMgr {
pub fn new(root: impl Into<PathBuf>) -> Result<Self, DataError> {
// Build path for top-level items.
let root: PathBuf = root.into();
let part = root.join("part");
let prof = root.join("prof");

// Create top-level directories.
Self::create_dir(&part)?;
Self::create_dir(&prof)?;

Ok(Self {
root,
part: Part::new(part),
prof: Prof::new(prof),
})
}

pub fn part(&self) -> &Part {
&self.part
}

pub fn prof(&self) -> &Prof {
&self.prof
}

fn create_dir(path: &Path) -> Result<(), DataError> {
if let Err(e) = std::fs::create_dir(path) {
if e.kind() != ErrorKind::AlreadyExists {
Expand All @@ -42,9 +50,12 @@ impl DataMgr {
}
}

/// Represents an error when [`DataMgr`] fails to construct.
/// Represents an error when operation on data root fails.
#[derive(Debug, Error)]
pub enum DataError {
#[error("couldn't create {0}")]
CreateDirectory(PathBuf, #[source] std::io::Error),

#[error("couldn't list item in {0}")]
ReadDirectory(PathBuf, #[source] std::io::Error),
}
48 changes: 48 additions & 0 deletions gui/src/data/prof.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
use super::DataError;
use std::path::{Path, PathBuf};
use uuid::Uuid;

/// Manages profile data stored on the filesystem.
pub struct Prof {
root: PathBuf,
}

impl Prof {
pub(super) fn new(root: PathBuf) -> Self {
Self { root }
}

pub fn list(&self) -> Result<impl Iterator<Item = Result<PathBuf, DataError>> + '_, DataError> {
std::fs::read_dir(&self.root)
.map_err(|e| DataError::ReadDirectory(self.root.clone(), e))
.map(|iter| List {
iter,
path: &self.root,
})
}

pub fn data(&self, id: Uuid) -> PathBuf {
let mut buf = Uuid::encode_buffer();
let id = id.as_hyphenated().encode_lower(&mut buf);

self.root.join(id)
}
}

/// Implementation of [`Iterator`] to enumerate profile directories.
struct List<'a> {
iter: std::fs::ReadDir,
path: &'a Path,
}

impl<'a> Iterator for List<'a> {
type Item = Result<PathBuf, DataError>;

fn next(&mut self) -> Option<Self::Item> {
self.iter
.next()?
.map_err(|e| DataError::ReadDirectory(self.path.into(), e))
.map(|i| i.path())
.into()
}
}
44 changes: 40 additions & 4 deletions gui/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#![windows_subsystem = "windows"]

use self::data::DataError;
use self::graphics::{Graphics, PhysicalDevice, Screen};
use self::profile::{Profile, ProfileModel};
use self::setup::{run_setup, SetupError};
Expand All @@ -14,7 +15,7 @@ use std::error::Error;
use std::io::Write;
use std::net::SocketAddrV4;
use std::panic::PanicHookInfo;
use std::path::Path;
use std::path::{Path, PathBuf};
use std::process::{Child, Command, ExitCode, Stdio};
use std::rc::Rc;
use std::sync::{Arc, Mutex, Weak};
Expand Down Expand Up @@ -135,8 +136,31 @@ fn run_vmm(args: &Args) -> Result<(), ApplicationError> {
}
};

// TODO: load profiles from filesystem
let profiles = vec![Profile::default()];
// Load profiles.
let mut profiles = Vec::new();

for l in data.prof().list().map_err(ApplicationError::ListProfile)? {
let l = l.map_err(ApplicationError::ListProfile)?;
let p = Profile::load(&l).map_err(ApplicationError::LoadProfile)?;

profiles.push(p);
}

// Create default profile if user does not have any profiles.
if profiles.is_empty() {
// Create directory.
let p = Profile::default();
let l = data.prof().data(p.id());

if let Err(e) = std::fs::create_dir(&l) {
return Err(ApplicationError::CreateDirectory(l, e));
}

// Save.
p.save(&l).map_err(ApplicationError::SaveDefaultProfile)?;

profiles.push(p);
}

// Get VMM arguments.
let args = if let Some(debug_addr) = args.debug {
Expand Down Expand Up @@ -350,7 +374,19 @@ enum ApplicationError {
#[error("couldn't run setup wizard")]
Setup(#[source] SetupError),

#[error("get current executable path")]
#[error("couldn't list available profiles")]
ListProfile(#[source] DataError),

#[error("couldn't load profile")]
LoadProfile(#[source] self::profile::LoadError),

#[error("couldn't create {0}")]
CreateDirectory(PathBuf, #[source] std::io::Error),

#[error("couldn't save default profile")]
SaveDefaultProfile(#[source] self::profile::SaveError),

#[error("couldn't get application executable path")]
GetCurrentExePath(#[source] std::io::Error),

#[error("failed to start debug server on {1}")]
Expand Down
60 changes: 34 additions & 26 deletions gui/src/profile/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use serde::{Deserialize, Serialize};
use std::fmt::{Display, Formatter};
use std::fs::File;
use std::num::NonZero;
use std::path::Path;
use std::path::{Path, PathBuf};
use std::time::SystemTime;
use thiserror::Error;
use uuid::Uuid;
Expand All @@ -24,26 +24,36 @@ pub struct Profile {
}

impl Profile {
pub fn load(path: impl AsRef<Path>) -> Result<Self, LoadError> {
let path = path.as_ref().join("profile.bin");

let file = File::open(&path).map_err(LoadError::Open)?;
pub fn load(root: impl AsRef<Path>) -> Result<Self, LoadError> {
// Open profile.
let root = root.as_ref();
let path = root.join("profile.bin");
let file = match File::open(&path) {
Ok(v) => v,
Err(e) => return Err(LoadError::OpenFile(path, e)),
};

let profile = ciborium::from_reader(file).map_err(LoadError::Load)?;
// Read profile.
let profile = match ciborium::from_reader(file) {
Ok(v) => v,
Err(e) => return Err(LoadError::ReadProfile(path, e)),
};

Ok(profile)
}

pub fn save(&self, path: impl AsRef<Path>) -> Result<(), SaveError> {
let path = path.as_ref();

std::fs::create_dir_all(path).map_err(SaveError::CreateDir)?;

let path = path.join("profile.bin");

let file = File::create(path).map_err(SaveError::CreateFile)?;
pub fn save(&self, root: impl AsRef<Path>) -> Result<(), SaveError> {
// Write profile.
let root = root.as_ref();
let path = root.join("profile.bin");
let file = match File::create(&path) {
Ok(v) => v,
Err(e) => return Err(SaveError::CreateFile(path, e)),
};

ciborium::into_writer(self, file).map_err(SaveError::WriteFile)?;
if let Err(e) = ciborium::into_writer(self, file) {
return Err(SaveError::WriteProfile(path, e));
}

Ok(())
}
Expand Down Expand Up @@ -97,21 +107,19 @@ impl Display for DisplayResolution {
/// Represents an error when [`Profile::load()`] fails.
#[derive(Debug, Error)]
pub enum LoadError {
#[error("couldn't open the profile file")]
Open(#[source] std::io::Error),
#[error("couldn't open {0}")]
OpenFile(PathBuf, #[source] std::io::Error),

#[error("couldn't load the profile file")]
Load(#[source] ciborium::de::Error<std::io::Error>),
#[error("couldn't read {0}")]
ReadProfile(PathBuf, #[source] ciborium::de::Error<std::io::Error>),
}

/// Represents an error when [`Profile::save()`] fails.
#[derive(Debug, Error)]
pub enum SaveError {
#[error("couldn't create the directory")]
CreateDir(#[source] std::io::Error),

#[error("couldn't create the profile file")]
CreateFile(#[source] std::io::Error),
#[error("couldn't create {0}")]
CreateFile(PathBuf, #[source] std::io::Error),

#[error("couldn't write the profile file")]
WriteFile(#[source] ciborium::ser::Error<std::io::Error>),
#[error("couldn't write {0}")]
WriteProfile(PathBuf, #[source] ciborium::ser::Error<std::io::Error>),
}
9 changes: 8 additions & 1 deletion gui/src/setup/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,14 @@ pub fn run_setup() -> Result<Option<DataMgr>, SetupError> {
return Ok(None);
}

todo!()
// Load data root.
let root = read_data_root().map_err(SetupError::ReadDataRoot)?.unwrap();
let mgr = match DataMgr::new(root.as_str()) {
Ok(v) => v,
Err(e) => return Err(SetupError::DataManager(root.into(), e)),
};

Ok(Some(mgr))
}

async fn browse_data_root(win: SetupWizard) {
Expand Down

0 comments on commit b2b4db0

Please sign in to comment.