diff --git a/lib/vfs/Cargo.toml b/lib/vfs/Cargo.toml index 42c6b09a..733a15cb 100644 --- a/lib/vfs/Cargo.toml +++ b/lib/vfs/Cargo.toml @@ -7,4 +7,5 @@ edition = "2021" [dependencies] yggdrasil-abi = { git = "https://git.alnyan.me/yggdrasil/yggdrasil-abi.git" } +macros = { path = "macros" } bitflags = "2.3.3" diff --git a/lib/vfs/macros/Cargo.toml b/lib/vfs/macros/Cargo.toml new file mode 100644 index 00000000..721b63a9 --- /dev/null +++ b/lib/vfs/macros/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "macros" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[lib] +proc-macro = true + +[dependencies] +lazy_static = "1.4.0" +proc-macro-crate = "1.3.1" +proc-macro2 = "1.0.66" +quote = "1.0.31" +syn = { version = "2.0.26", features = ["full"] } diff --git a/lib/vfs/macros/src/lib.rs b/lib/vfs/macros/src/lib.rs new file mode 100644 index 00000000..b011e3d2 --- /dev/null +++ b/lib/vfs/macros/src/lib.rs @@ -0,0 +1,53 @@ +use std::collections::HashSet; + +use proc_macro::TokenStream; +use proc_macro2::Ident; +use proc_macro_crate::{crate_name, FoundCrate}; +use quote::{quote, ToTokens}; +use syn::{parse_macro_input, ImplItem, ItemImpl}; + +mod vnode_impl; +use vnode_impl::IMPLS; + +#[proc_macro_attribute] +pub fn auto_vnode_impl(attr: TokenStream, input: TokenStream) -> TokenStream { + let current_crate = crate_name("vfs").unwrap(); + + let vfs_crate = match current_crate { + FoundCrate::Itself => quote!(crate), + FoundCrate::Name(name) => quote!( #name ), + }; + let mut impl_item = parse_macro_input!(input as ItemImpl); + let behavior = if attr.is_empty() { + "unimplemented".to_string() + } else { + parse_macro_input!(attr as Ident).to_string() + }; + let behavior = match behavior.as_str() { + "unimplemented" => quote! { unimplemented!() }, + "panic" => quote! { panic!() }, + "error" => quote! { Err(yggdrasil_abi::error::Error::NotImplemented) }, + _ => panic!("Unknown #[auto_vnode_impl] behavior: {:?}", behavior), + }; + + let mut missing_impls: HashSet<&str> = HashSet::from_iter(IMPLS.keys().copied()); + + for item in &impl_item.items { + match item { + ImplItem::Fn(f) => { + let name = &f.sig.ident.to_string(); + missing_impls.remove(name.as_str()); + } + _ => panic!("Unexpected item in Vnode impl"), + } + } + + for &item in &missing_impls { + let f = IMPLS.get(item).unwrap(); + let fn_impl = f(&vfs_crate, &behavior); + + impl_item.items.push(ImplItem::Verbatim(fn_impl)); + } + + impl_item.to_token_stream().into() +} diff --git a/lib/vfs/macros/src/vnode_impl.rs b/lib/vfs/macros/src/vnode_impl.rs new file mode 100644 index 00000000..939d902a --- /dev/null +++ b/lib/vfs/macros/src/vnode_impl.rs @@ -0,0 +1,60 @@ +use std::collections::HashMap; + +use proc_macro2::TokenStream as TS2; +use quote::quote; + +fn impl_open(vfs: &TS2, behavior: &TS2) -> TS2 { + quote! { + fn open(&mut self, _node: &#vfs::VnodeRef, _opts: yggdrasil_abi::io::OpenFlags) + -> Result { + #behavior + } + } +} + +fn impl_close(vfs: &TS2, behavior: &TS2) -> TS2 { + quote! { + fn close(&mut self, _node: &#vfs::VnodeRef) -> Result<(), Error> { + #behavior + } + } +} + +fn impl_create(vfs: &TS2, behavior: &TS2) -> TS2 { + quote! { + fn create(&mut self, _at: &#vfs::VnodeRef, _name: &str, _kind: #vfs::VnodeKind) + -> Result<#vfs::VnodeRef, yggdrasil_abi::error::Error> { + #behavior + } + } +} + +fn impl_write(vfs: &TS2, behavior: &TS2) -> TS2 { + quote! { + fn write(&mut self, _node: &#vfs::VnodeRef, _pos: usize, _data: &[u8]) + -> Result { + #behavior + } + } +} + +fn impl_read(vfs: &TS2, behavior: &TS2) -> TS2 { + quote! { + fn read(&mut self, _node: &#vfs::VnodeRef, _pos: usize, _data: &mut [u8]) + -> Result { + #behavior + } + } +} + +lazy_static::lazy_static! { + pub static ref IMPLS: HashMap<&'static str, fn(&TS2, &TS2) -> TS2> = { + let mut m = HashMap::new(); + m.insert("open".into(), impl_open as _); + m.insert("close".into(), impl_close as _); + m.insert("create".into(), impl_create as _); + m.insert("write".into(), impl_write as _); + m.insert("read".into(), impl_read as _); + m + }; +} diff --git a/lib/vfs/src/char.rs b/lib/vfs/src/char.rs index 848869f0..91351f7e 100644 --- a/lib/vfs/src/char.rs +++ b/lib/vfs/src/char.rs @@ -1,6 +1,7 @@ -use yggdrasil_abi::error::Error; +use macros::auto_vnode_impl; +use yggdrasil_abi::{error::Error, io::OpenFlags}; -use crate::node::{VnodeImpl, VnodeRef}; +use crate::{node::VnodeImpl, VnodeRef}; pub trait CharDevice { fn read(&'static self, blocking: bool, data: &mut [u8]) -> Result; @@ -17,12 +18,9 @@ impl CharDeviceWrapper { } } +#[auto_vnode_impl(error)] impl VnodeImpl for CharDeviceWrapper { - fn open( - &mut self, - _node: &VnodeRef, - _opts: yggdrasil_abi::io::OpenFlags, - ) -> Result { + fn open(&mut self, _node: &VnodeRef, _opts: OpenFlags) -> Result { Ok(0) } @@ -37,13 +35,4 @@ impl VnodeImpl for CharDeviceWrapper { fn write(&mut self, _node: &VnodeRef, _pos: usize, data: &[u8]) -> Result { self.device.write(true, data) } - - fn create( - &mut self, - _at: &VnodeRef, - _name: &str, - _kind: crate::node::VnodeKind, - ) -> Result { - todo!() - } } diff --git a/src/syscall.rs b/src/syscall.rs index 2359a2e5..1596b592 100644 --- a/src/syscall.rs +++ b/src/syscall.rs @@ -4,7 +4,7 @@ use core::time::Duration; use abi::{ error::{Error, IntoSyscallResult}, io::{OpenFlags, RawFd}, - SyscallFunction, + syscall::SyscallFunction, }; use vfs::{Read, Write};