diff --git a/.github/workflows/ci-linux.yml b/.github/workflows/ci-linux.yml index 2f877114..83b119af 100644 --- a/.github/workflows/ci-linux.yml +++ b/.github/workflows/ci-linux.yml @@ -34,7 +34,7 @@ jobs: - name: Install Flatpak runtimes run: | flatpak install --noninteractive flathub \ - org.kde.Platform//6.6 org.kde.Sdk//6.6 \ + org.kde.Platform//6.7 org.kde.Sdk//6.7 \ org.freedesktop.Sdk.Extension.rust-stable//23.08 if: ${{ steps.flatpak-runtime.outputs.cache-hit != 'true' }} - name: Restore build files diff --git a/.github/workflows/ci-mac.yml b/.github/workflows/ci-mac.yml index 979787fb..48ee5b66 100644 --- a/.github/workflows/ci-mac.yml +++ b/.github/workflows/ci-mac.yml @@ -21,9 +21,9 @@ jobs: name: ${{ inputs.name }} runs-on: ${{ inputs.macos }} env: - CMAKE_PREFIX_PATH: qt/6.6.0/macos - QT_URL_BASE: https://download.qt.io/online/qtsdkrepository/mac_x64/desktop/qt6_660/qt.qt6.660.clang_64/6.6.0-0-202310040910qtbase-MacOS-MacOS_12-Clang-MacOS-MacOS_12-X86_64-ARM64.7z - QT_URL_SVG: https://download.qt.io/online/qtsdkrepository/mac_x64/desktop/qt6_660/qt.qt6.660.clang_64/6.6.0-0-202310040910qtsvg-MacOS-MacOS_12-Clang-MacOS-MacOS_12-X86_64-ARM64.7z + CMAKE_PREFIX_PATH: qt/6.7.2/macos + QT_URL_BASE: https://download.qt.io/online/qtsdkrepository/mac_x64/desktop/qt6_672/qt.qt6.672.clang_64/6.7.2-0-202406110330qtbase-MacOS-MacOS_13-Clang-MacOS-MacOS_13-X86_64-ARM64.7z + QT_URL_SVG: https://download.qt.io/online/qtsdkrepository/mac_x64/desktop/qt6_672/qt.qt6.672.clang_64/6.7.2-0-202406110330qtsvg-MacOS-MacOS_13-Clang-MacOS-MacOS_13-X86_64-ARM64.7z steps: - name: Checkout source uses: actions/checkout@v4 diff --git a/flatpak.yml b/flatpak.yml index 9eae697c..05d30f06 100644 --- a/flatpak.yml +++ b/flatpak.yml @@ -1,7 +1,7 @@ app-id: io.github.obhq.Obliteration default-branch: stable runtime: org.kde.Platform -runtime-version: '6.6' +runtime-version: '6.7' platform-extensions: - org.freedesktop.Platform.GL.default sdk: org.kde.Sdk diff --git a/src/core.h b/src/core.h index cb047155..278580d8 100644 --- a/src/core.h +++ b/src/core.h @@ -13,6 +13,13 @@ struct Param; struct Pkg; +/** + * Log category. + */ +enum VmmLog { + VmmLog_Info, +}; + /** * Error object managed by Rust side. */ @@ -41,6 +48,26 @@ struct VmmScreen { ; }; +/** + * Contains VMM event information. + */ +enum VmmEvent_Tag { + VmmEvent_Log, +}; + +struct VmmEvent_Log_Body { + enum VmmLog ty; + const char *data; + size_t len; +}; + +struct VmmEvent { + enum VmmEvent_Tag tag; + union { + struct VmmEvent_Log_Body log; + }; +}; + #ifdef __cplusplus extern "C" { #endif // __cplusplus @@ -89,12 +116,14 @@ struct RustError *update_firmware(const char *root, void vmm_free(struct Vmm *vmm); -struct Vmm *vmm_run(const char *kernel, const struct VmmScreen *screen, struct RustError **err); +struct Vmm *vmm_run(const char *kernel, + const struct VmmScreen *screen, + bool (*event)(const struct VmmEvent*, void*), + void *cx, + struct RustError **err); struct RustError *vmm_draw(struct Vmm *vmm); -void vmm_logs(const struct Vmm *vmm, void *cx, void (*cb)(uint8_t, const char*, size_t, void*)); - #if defined(__linux__) extern int kvm_check_version(int kvm, bool *compat); #endif diff --git a/src/core/build.rs b/src/core/build.rs index 00f4098a..8b610e8f 100644 --- a/src/core/build.rs +++ b/src/core/build.rs @@ -40,6 +40,7 @@ fn main() { conf.export .rename .insert("KvmTranslation".into(), "kvm_translation".into()); + conf.enumeration.prefix_with_name = true; conf.defines .insert("target_os = linux".into(), "__linux__".into()); conf.defines diff --git a/src/core/src/vmm/hw/console/context.rs b/src/core/src/vmm/hw/console/context.rs index 2486908d..a2ccb500 100644 --- a/src/core/src/vmm/hw/console/context.rs +++ b/src/core/src/vmm/hw/console/context.rs @@ -1,7 +1,8 @@ -use super::{Console, Log}; +use super::Console; use crate::vmm::hv::{CpuIo, IoBuf}; use crate::vmm::hw::{DeviceContext, Ram}; -use obvirt::console::{Commit, Memory}; +use crate::vmm::VmmEvent; +use obvirt::console::{Memory, MsgType}; use std::error::Error; use std::mem::offset_of; use thiserror::Error; @@ -10,8 +11,6 @@ use thiserror::Error; pub struct Context<'a> { dev: &'a Console, ram: &'a Ram, - file_len: usize, - file: String, msg_len: usize, msg: String, } @@ -21,14 +20,12 @@ impl<'a> Context<'a> { Self { dev, ram, - file_len: 0, - file: String::new(), msg_len: 0, msg: String::new(), } } - fn read_u32(&self, off: usize, exit: &mut dyn CpuIo) -> Result { + fn read_u8(&self, off: usize, exit: &mut dyn CpuIo) -> Result { // Get data. let data = match exit.buffer() { IoBuf::Write(v) => v, @@ -36,9 +33,11 @@ impl<'a> Context<'a> { }; // Parse data. - data.try_into() - .map(|v| u32::from_ne_bytes(v)) - .map_err(|_| ExecError::InvalidData(off)) + if data.len() != 1 { + Err(ExecError::InvalidData(off)) + } else { + Ok(data[0]) + } } fn read_usize(&self, off: usize, exit: &mut dyn CpuIo) -> Result { @@ -84,7 +83,7 @@ impl<'a> Context<'a> { } impl<'a> DeviceContext for Context<'a> { - fn exec(&mut self, exit: &mut dyn CpuIo) -> Result<(), Box> { + fn exec(&mut self, exit: &mut dyn CpuIo) -> Result> { // Check field. let off = exit.addr() - self.dev.addr; @@ -92,36 +91,34 @@ impl<'a> DeviceContext for Context<'a> { self.msg_len = self.read_usize(off, exit)?; } else if off == offset_of!(Memory, msg_addr) { self.msg.push_str(self.read_str(off, exit, self.msg_len)?); - } else if off == offset_of!(Memory, file_len) { - self.file_len = self.read_usize(off, exit)?; - } else if off == offset_of!(Memory, file_addr) { - self.file = self.read_str(off, exit, self.file_len)?.to_owned(); } else if off == offset_of!(Memory, commit) { // Parse data. - let commit = self.read_u32(off, exit)?; - let (ty, line) = match Commit::parse(commit) { + let commit = self.read_u8(off, exit)?; + let ty = match MsgType::from_u8(commit) { Some(v) => v, None => return Err(Box::new(ExecError::InvalidCommit(commit))), }; - // Push log. - let mut logs = self.dev.logs.lock().unwrap(); + // Trigger event. + let msg = std::mem::take(&mut self.msg); + let status = match ty { + MsgType::Info => unsafe { + self.dev.event.invoke(VmmEvent::Log { + ty: ty.into(), + data: msg.as_ptr().cast(), + len: msg.len(), + }) + }, + }; - logs.push_back(Log { - ty, - file: std::mem::take(&mut self.file), - line, - msg: std::mem::take(&mut self.msg), - }); - - while logs.len() > 10000 { - logs.pop_front(); + if !status { + return Ok(false); } } else { return Err(Box::new(ExecError::UnknownField(off))); } - Ok(()) + Ok(true) } } @@ -141,5 +138,5 @@ enum ExecError { TranslateVaddrFailed(usize, #[source] Box), #[error("{0:#} is not a valid commit")] - InvalidCommit(u32), + InvalidCommit(u8), } diff --git a/src/core/src/vmm/hw/console/mod.rs b/src/core/src/vmm/hw/console/mod.rs index da0ad342..92526d71 100644 --- a/src/core/src/vmm/hw/console/mod.rs +++ b/src/core/src/vmm/hw/console/mod.rs @@ -1,9 +1,8 @@ use self::context::Context; use super::{Device, DeviceContext, Ram}; -use obvirt::console::{Memory, MsgType}; -use std::collections::VecDeque; +use crate::vmm::VmmEventHandler; +use obvirt::console::Memory; use std::num::NonZero; -use std::sync::Mutex; mod context; @@ -11,11 +10,11 @@ mod context; pub struct Console { addr: usize, len: NonZero, - logs: Mutex>, + event: VmmEventHandler, } impl Console { - pub fn new(addr: usize, vm_page_size: NonZero) -> Self { + pub fn new(addr: usize, vm_page_size: NonZero, event: VmmEventHandler) -> Self { let len = size_of::() .checked_next_multiple_of(vm_page_size.get()) .and_then(NonZero::new) @@ -23,11 +22,7 @@ impl Console { addr.checked_add(len.get()).unwrap(); - Self { - addr, - len, - logs: Mutex::default(), - } + Self { addr, len, event } } } @@ -44,11 +39,3 @@ impl Device for Console { Box::new(Context::new(self, ram)) } } - -/// Contains data for each logging entry. -struct Log { - ty: MsgType, - file: String, - line: u32, - msg: String, -} diff --git a/src/core/src/vmm/hw/mod.rs b/src/core/src/vmm/hw/mod.rs index 9dece26f..5a23f468 100644 --- a/src/core/src/vmm/hw/mod.rs +++ b/src/core/src/vmm/hw/mod.rs @@ -1,3 +1,4 @@ +use super::VmmEventHandler; use crate::vmm::hv::CpuIo; use std::collections::BTreeMap; use std::error::Error; @@ -10,12 +11,16 @@ pub use self::ram::*; mod console; mod ram; -pub fn setup_devices(start_addr: usize, vm_page_size: NonZero) -> DeviceTree { +pub fn setup_devices( + start_addr: usize, + vm_page_size: NonZero, + event: VmmEventHandler, +) -> DeviceTree { let mut map = BTreeMap::>::new(); // Console. let addr = start_addr; - let console = Arc::new(Console::new(addr, vm_page_size)); + let console = Arc::new(Console::new(addr, vm_page_size, event)); assert!(map.insert(console.addr(), console.clone()).is_none()); @@ -60,5 +65,5 @@ pub trait Device: Send + Sync { /// Context to execute memory-mapped I/O operations on a virtual device. pub trait DeviceContext { - fn exec(&mut self, exit: &mut dyn CpuIo) -> Result<(), Box>; + fn exec(&mut self, exit: &mut dyn CpuIo) -> Result>; } diff --git a/src/core/src/vmm/mod.rs b/src/core/src/vmm/mod.rs index c970a25e..1c0876fc 100644 --- a/src/core/src/vmm/mod.rs +++ b/src/core/src/vmm/mod.rs @@ -5,16 +5,15 @@ use self::screen::Screen; use crate::error::RustError; use obconf::{BootEnv, Vm}; use obvirt::console::MsgType; -use std::collections::{BTreeMap, VecDeque}; +use std::collections::BTreeMap; use std::error::Error; use std::ffi::{c_char, c_void, CStr}; use std::io::Read; use std::num::NonZero; -use std::ops::Deref; use std::ptr::null_mut; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::mpsc::Sender; -use std::sync::{Arc, Mutex}; +use std::sync::Arc; use std::thread::JoinHandle; use thiserror::Error; @@ -32,6 +31,8 @@ pub unsafe extern "C" fn vmm_free(vmm: *mut Vmm) { pub unsafe extern "C" fn vmm_run( kernel: *const c_char, screen: *const VmmScreen, + event: unsafe extern "C" fn(*const VmmEvent, *mut c_void) -> bool, + cx: *mut c_void, err: *mut *mut RustError, ) -> *mut Vmm { // Check if path UTF-8. @@ -359,7 +360,8 @@ pub unsafe extern "C" fn vmm_run( } // Allocate arguments. - let devices = Arc::new(setup_devices(Ram::SIZE, vm_page_size)); + let event = VmmEventHandler { fp: event, cx }; + let devices = Arc::new(setup_devices(Ram::SIZE, vm_page_size, event)); let env = BootEnv::Vm(Vm { console: devices.console().addr(), }); @@ -398,7 +400,6 @@ pub unsafe extern "C" fn vmm_run( }; // Setup arguments for main CPU. - let logs = Arc::new(Mutex::new(VecDeque::new())); let shutdown = Arc::new(AtomicBool::new(false)); let args = CpuArgs { hv, @@ -440,7 +441,6 @@ pub unsafe extern "C" fn vmm_run( let vmm = Vmm { cpus: vec![main], screen, - logs, shutdown, }; @@ -455,19 +455,6 @@ pub unsafe extern "C" fn vmm_draw(vmm: *mut Vmm) -> *mut RustError { } } -#[no_mangle] -pub unsafe extern "C" fn vmm_logs( - vmm: *const Vmm, - cx: *mut c_void, - cb: unsafe extern "C" fn(u8, *const c_char, usize, *mut c_void), -) { - let logs = (*vmm).logs.lock().unwrap(); - - for (ty, msg) in logs.deref() { - cb(*ty as u8, msg.as_ptr().cast(), msg.len(), cx); - } -} - fn main_cpu(args: &CpuArgs, entry: usize, map: RamMap, status: Sender>) { // Create vCPU. let mut cpu = match args.hv.create_cpu(0) { @@ -616,7 +603,13 @@ fn run_cpu(mut cpu: impl Cpu, args: &CpuArgs) { // Check if I/O. match exit.into_io() { Ok(io) => match exec_io(&mut devices, io) { - Ok(_) => continue, + Ok(status) => { + if !status { + args.shutdown.store(true, Ordering::Relaxed); + } + + continue; + } Err(_) => todo!(), }, Err(_) => todo!(), @@ -627,7 +620,7 @@ fn run_cpu(mut cpu: impl Cpu, args: &CpuArgs) { fn exec_io<'a>( devices: &mut BTreeMap, NonZero)>, mut io: impl CpuIo, -) -> Result<(), Box> { +) -> Result> { // Get target device. let addr = io.addr(); let (_, (dev, end)) = devices.range_mut(..=addr).last().unwrap(); @@ -663,7 +656,6 @@ fn get_page_size() -> Result, std::io::Error> { pub struct Vmm { cpus: Vec>, screen: self::screen::Default, - logs: Arc>>, shutdown: Arc, } @@ -689,6 +681,48 @@ pub struct VmmScreen { pub view: usize, } +/// Encapsulates a function to handle VMM events. +#[derive(Clone, Copy)] +struct VmmEventHandler { + fp: unsafe extern "C" fn(*const VmmEvent, *mut c_void) -> bool, + cx: *mut c_void, +} + +impl VmmEventHandler { + unsafe fn invoke(self, e: VmmEvent) -> bool { + (self.fp)(&e, self.cx) + } +} + +unsafe impl Send for VmmEventHandler {} +unsafe impl Sync for VmmEventHandler {} + +/// Contains VMM event information. +#[repr(C)] +#[allow(dead_code)] // TODO: Figure out why Rust think fields in each enum are not used. +pub enum VmmEvent { + Log { + ty: VmmLog, + data: *const c_char, + len: usize, + }, +} + +/// Log category. +#[repr(C)] +#[derive(Clone, Copy)] +pub enum VmmLog { + Info, +} + +impl From for VmmLog { + fn from(value: MsgType) -> Self { + match value { + MsgType::Info => Self::Info, + } + } +} + /// Encapsulates arguments for a function to run a CPU. struct CpuArgs { hv: Arc, diff --git a/src/main.cpp b/src/main.cpp index 39e2fa02..cf58e398 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,4 +1,3 @@ -#include "core.hpp" #include "initialize_wizard.hpp" #include "main_window.hpp" #include "settings.hpp" @@ -10,6 +9,7 @@ #include #include #include +#include #ifndef __APPLE__ #include #include @@ -27,14 +27,19 @@ static void panicHook( size_t mlen, void *cx) { - QMetaObject::invokeMethod(reinterpret_cast(cx), [=]() { + auto main = reinterpret_cast(cx); + auto type = QThread::currentThread() == main->thread() + ? Qt::DirectConnection + : Qt::BlockingQueuedConnection; + + QMetaObject::invokeMethod(main, [=]() { auto text = QString("An unexpected error occurred at %1:%2: %3") .arg(QString::fromUtf8(file, flen)) .arg(line) .arg(QString::fromUtf8(msg, mlen)); QMessageBox::critical(nullptr, "Fatal Error", text); - }); + }, type); } int main(int argc, char *argv[]) diff --git a/src/main_window.cpp b/src/main_window.cpp index 71f5867e..495b2673 100644 --- a/src/main_window.cpp +++ b/src/main_window.cpp @@ -30,6 +30,7 @@ #endif #include +#include #include #include @@ -234,25 +235,14 @@ void MainWindow::openSystemFolder() void MainWindow::viewLogs() { - // Check for previous window. if (m_logs) { m_logs->activateWindow(); m_logs->raise(); - return; - } else if (!m_kernel) { - QMessageBox::information(this, "Information", "The kernel is not running."); - return; + } else { + m_logs = new LogsViewer(); + m_logs->setAttribute(Qt::WA_DeleteOnClose); + m_logs->show(); } - - // Create a window. - m_logs = new LogsViewer(); - - vmm_logs(m_kernel, m_logs.get(), [](uint8_t, const char *msg, size_t len, void *cx) { - reinterpret_cast(cx)->append(QString::fromUtf8(msg, len)); - }); - - m_logs->setAttribute(Qt::WA_DeleteOnClose); - m_logs->show(); } void MainWindow::reportIssue() @@ -332,7 +322,7 @@ void MainWindow::startKernel() } #endif - vmm = vmm_run(kernel.c_str(), &screen, &error); + vmm = vmm_run(kernel.c_str(), &screen, MainWindow::vmmHandler, this, &error); if (!vmm) { m_main->setCurrentIndex(0); @@ -373,6 +363,19 @@ void MainWindow::updateScreen() m_screen->requestUpdate(); } +void MainWindow::log(VmmLog type, const QString &msg) +{ + if (m_logs) { + m_logs->append(msg); + } else { + switch (type) { + case VmmLog_Info: + std::cout << msg.toStdString(); + break; + } + } +} + bool MainWindow::loadGame(const QString &gameId) { auto gamesDirectory = readGamesDirectorySetting(); @@ -453,3 +456,21 @@ bool MainWindow::requireEmulatorStopped() return false; } + +bool MainWindow::vmmHandler(const VmmEvent *ev, void *cx) +{ + auto w = reinterpret_cast(cx); + + switch (ev->tag) { + case VmmEvent_Log: + QMetaObject::invokeMethod( + w, + &MainWindow::log, + Qt::QueuedConnection, + ev->log.ty, + QString::fromUtf8(ev->log.data, ev->log.len)); + break; + } + + return true; +} diff --git a/src/main_window.hpp b/src/main_window.hpp index 1f83ce31..6124f97d 100644 --- a/src/main_window.hpp +++ b/src/main_window.hpp @@ -37,14 +37,17 @@ private slots: void updateScreen(); private: + void log(VmmLog type, const QString &msg); bool loadGame(const QString &gameId); void restoreGeometry(); bool requireEmulatorStopped(); + static bool vmmHandler(const VmmEvent *ev, void *cx); + QStackedWidget *m_main; GameListModel *m_games; LaunchSettings *m_launch; Screen *m_screen; QPointer m_logs; - Rust m_kernel; + Rust m_kernel; // Destroy first. }; diff --git a/src/obkrnl/Cargo.toml b/src/obkrnl/Cargo.toml index 88840a78..2e45e3a8 100644 --- a/src/obkrnl/Cargo.toml +++ b/src/obkrnl/Cargo.toml @@ -4,6 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] +anstyle = { version = "1.0.8", default-features = false } macros = { path = "../macros" } obconf = { path = "../obconf" } obvirt = { path = "../obvirt" } diff --git a/src/obkrnl/src/console/mod.rs b/src/obkrnl/src/console/mod.rs index 1f9b7c7a..4e444ded 100644 --- a/src/obkrnl/src/console/mod.rs +++ b/src/obkrnl/src/console/mod.rs @@ -1,16 +1,17 @@ use crate::config::boot_env; -use core::fmt::Arguments; +use anstyle::{AnsiColor, Color, Style}; +use core::fmt::{Arguments, Display, Formatter}; use obconf::BootEnv; use obvirt::console::MsgType; mod vm; -/// Write single line of information log. +/// Write information log. /// /// When running inside a VM each call will cause a VM to exit multiple times so don't do this in a /// performance critical path. /// -/// The line should not contains LF character. +/// The LF character will be automatically appended. #[macro_export] macro_rules! info { ($($args:tt)*) => { @@ -18,9 +19,37 @@ macro_rules! info { }; } -#[doc(hidden)] pub fn info(file: &str, line: u32, msg: Arguments) { + let log = Log { + style: Style::new().fg_color(Some(Color::Ansi(AnsiColor::BrightCyan))), + cat: 'I', + file, + line, + msg, + }; + match boot_env() { - BootEnv::Vm(env) => self::vm::print(env, MsgType::Info, file, line, msg), + BootEnv::Vm(env) => self::vm::print(env, MsgType::Info, log), + } +} + +/// [`Display`] implementation to format each log. +struct Log<'a> { + style: Style, + cat: char, + file: &'a str, + line: u32, + msg: Arguments<'a>, +} + +impl<'a> Display for Log<'a> { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + writeln!( + f, + "{}++++++++++++++++++ {} {}:{}{0:#}", + self.style, self.cat, self.file, self.line + )?; + self.msg.fmt(f)?; + Ok(()) } } diff --git a/src/obkrnl/src/console/vm.rs b/src/obkrnl/src/console/vm.rs index a528098a..b17de331 100644 --- a/src/obkrnl/src/console/vm.rs +++ b/src/obkrnl/src/console/vm.rs @@ -1,17 +1,15 @@ -use core::fmt::{Arguments, Write}; +use core::fmt::{Display, Write}; use core::ptr::{addr_of_mut, write_volatile}; use obconf::Vm; -use obvirt::console::{Commit, Memory, MsgType}; +use obvirt::console::{Memory, MsgType}; -pub fn print(env: &Vm, ty: MsgType, file: &str, line: u32, msg: Arguments) { +pub fn print(env: &Vm, ty: MsgType, msg: impl Display) { let c = env.console as *mut Memory; + let mut w = Writer(c); - unsafe { write_volatile(addr_of_mut!((*c).file_len), file.len()) }; - unsafe { write_volatile(addr_of_mut!((*c).file_addr), file.as_ptr() as usize) }; + writeln!(w, "{msg}").unwrap(); - Writer(c).write_fmt(msg).unwrap(); - - unsafe { write_volatile(addr_of_mut!((*c).commit), Commit::new(ty, line)) }; + unsafe { write_volatile(addr_of_mut!((*c).commit), ty) }; } struct Writer(*mut Memory); diff --git a/src/obvirt/src/console/mod.rs b/src/obvirt/src/console/mod.rs index 2459831b..4bf417d1 100644 --- a/src/obvirt/src/console/mod.rs +++ b/src/obvirt/src/console/mod.rs @@ -2,41 +2,14 @@ /// /// The sequence of operations on a console memory is per-cpu. The kernel will start each log by: /// -/// 1. Write [`Self::file_len`] then [`Self::file_addr`]. -/// 2. Write [`Self::msg_len`] then [`Self::msg_addr`]. -/// 3. Repeat step 2 until the whole message has been written. -/// 4. Write [`Self::commit`]. +/// 1. Write [`Self::msg_len`] then [`Self::msg_addr`]. +/// 2. Repeat step 1 until the whole message has been written. +/// 3. Write [`Self::commit`]. #[repr(C)] pub struct Memory { - pub file_len: usize, - pub file_addr: usize, pub msg_len: usize, pub msg_addr: usize, - pub commit: Commit, -} - -/// Struct to commit a log. -#[repr(transparent)] -pub struct Commit(u32); - -impl Commit { - /// # Panics - /// If `line` greater than 0xffffff. - pub fn new(ty: MsgType, line: u32) -> Self { - assert!(line <= 0xffffff); - - Self((ty as u32) << 24 | line) - } - - pub fn parse(raw: u32) -> Option<(MsgType, u32)> { - let line = raw & 0xffffff; - let ty = match (raw >> 24) as u8 { - v if v == MsgType::Info as u8 => MsgType::Info, - _ => return None, - }; - - Some((ty, line)) - } + pub commit: MsgType, } /// Type of console message. @@ -45,3 +18,14 @@ impl Commit { pub enum MsgType { Info, } + +impl MsgType { + pub fn from_u8(v: u8) -> Option { + let v = match v { + v if v == MsgType::Info as u8 => MsgType::Info, + _ => return None, + }; + + Some(v) + } +}