feat(builtins): use proc macro for builtins
This commit is contained in:
parent
bd23c3e40d
commit
23f69a33e5
7 changed files with 198 additions and 41 deletions
102
Cargo.lock
generated
102
Cargo.lock
generated
|
@ -33,18 +33,65 @@ dependencies = [
|
|||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling"
|
||||
version = "0.14.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4529658bdda7fd6769b8614be250cdcfc3aeb0ee72fe66f9e41e5e5eb73eac02"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"darling_macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling_core"
|
||||
version = "0.14.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "649c91bc01e8b1eac09fb91e8dbc7d517684ca6be8ebc75bb9cafc894f9fdb6f"
|
||||
dependencies = [
|
||||
"fnv",
|
||||
"ident_case",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"strsim",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling_macro"
|
||||
version = "0.14.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ddfc69c5bfcbd2fc09a0f38451d2daf0e372e367986a83906d1b0dbc88134fb5"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "encode_unicode"
|
||||
version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
|
||||
|
||||
[[package]]
|
||||
name = "fnv"
|
||||
version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
||||
|
||||
[[package]]
|
||||
name = "ident_case"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "1.9.1"
|
||||
|
@ -114,6 +161,18 @@ dependencies = [
|
|||
"oyster_runtime",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "oyster_builtin_proc"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"darling",
|
||||
"oyster_runtime",
|
||||
"proc-macro-crate",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "oyster_lineedit"
|
||||
version = "0.0.0"
|
||||
|
@ -132,24 +191,36 @@ version = "0.0.0"
|
|||
dependencies = [
|
||||
"insta",
|
||||
"nix",
|
||||
"oyster_builtin_proc",
|
||||
"oyster_parser",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.40"
|
||||
name = "proc-macro-crate"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd96a1e8ed2596c337f8eae5f24924ec83f5ad5ab21ea8e455d3566c69fbcaf7"
|
||||
checksum = "eda0fc3b0fb7c975631757e14d9049da17374063edb6ebbcbc54d880d4fe94e9"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"thiserror",
|
||||
"toml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.47"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.20"
|
||||
version = "1.0.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3bcdf212e9776fbcb2d23ab029360416bb1706b1aea2d1a5ba002727cbcab804"
|
||||
checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
@ -210,10 +281,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "2e24979f63a11545f5f2c60141afe249d4f19f84581ea2138065e400941d83d3"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.98"
|
||||
name = "strsim"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd"
|
||||
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.102"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3fcd952facd492f9be3ef0d0b7032a6e442ee9b361d4acc2b1d0c4aaa5f613a1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -250,6 +327,15 @@ dependencies = [
|
|||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.5.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.1"
|
||||
|
|
18
crates/oyster_builtin_proc/Cargo.toml
Normal file
18
crates/oyster_builtin_proc/Cargo.toml
Normal file
|
@ -0,0 +1,18 @@
|
|||
[package]
|
||||
name = "oyster_builtin_proc"
|
||||
version = "0.0.0"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
doctest = false
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
darling = "~0.14.1"
|
||||
proc-macro-crate = "^1.2.1"
|
||||
proc-macro2 = { version = "1.0.47", default-features = false }
|
||||
quote = { version = "^1.0.21", default-features = false }
|
||||
syn = { version = "^1.0.102", default-features = false, features = [ "full" ] }
|
||||
|
||||
[dev-dependencies]
|
||||
oyster_runtime = { path = "../oyster_runtime" }
|
53
crates/oyster_builtin_proc/src/lib.rs
Normal file
53
crates/oyster_builtin_proc/src/lib.rs
Normal file
|
@ -0,0 +1,53 @@
|
|||
use darling::FromMeta;
|
||||
use proc_macro::TokenStream;
|
||||
use proc_macro2::Span;
|
||||
use proc_macro_crate::{crate_name, FoundCrate};
|
||||
use quote::quote;
|
||||
use syn::{parse_macro_input, AttributeArgs, Ident, ItemFn, Signature};
|
||||
|
||||
#[derive(FromMeta)]
|
||||
struct AttrArgs {
|
||||
#[darling(default)]
|
||||
description: String,
|
||||
}
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn builtin(attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
let attrs = parse_macro_input!(attr as AttributeArgs);
|
||||
let ItemFn {
|
||||
vis, sig, block, ..
|
||||
} = parse_macro_input!(item as ItemFn);
|
||||
let Signature {
|
||||
ident,
|
||||
fn_token,
|
||||
inputs,
|
||||
..
|
||||
} = sig;
|
||||
|
||||
let name = ident.to_string();
|
||||
let AttrArgs { description } = match AttrArgs::from_list(&attrs) {
|
||||
Ok(args) => args,
|
||||
Err(err) => return err.write_errors().into(),
|
||||
};
|
||||
|
||||
let builtin = match crate_name("oyster_runtime").expect("oyster_runtime missing") {
|
||||
FoundCrate::Itself => quote! { crate::builtins::Builtin },
|
||||
FoundCrate::Name(name) => {
|
||||
let ident = Ident::new(&name, Span::call_site());
|
||||
quote! { #ident::builtins::Builtin }
|
||||
}
|
||||
};
|
||||
|
||||
TokenStream::from(quote! {
|
||||
#[allow(non_upper_case_globals)]
|
||||
#vis static #ident: #builtin = {
|
||||
#fn_token #ident(#inputs) #block
|
||||
|
||||
#builtin {
|
||||
name: #name,
|
||||
description: #description,
|
||||
fun: #ident,
|
||||
}
|
||||
};
|
||||
})
|
||||
}
|
10
crates/oyster_builtin_proc/tests/it/main.rs
Normal file
10
crates/oyster_builtin_proc/tests/it/main.rs
Normal file
|
@ -0,0 +1,10 @@
|
|||
use std::borrow::Cow;
|
||||
|
||||
use oyster_builtin_proc::builtin;
|
||||
use oyster_runtime::Shell;
|
||||
|
||||
#[test]
|
||||
fn normal_usage() {
|
||||
#[builtin(description = "some text")]
|
||||
fn test(_: &mut Shell, _: &[Cow<str>]) {}
|
||||
}
|
|
@ -7,6 +7,7 @@ edition = "2021"
|
|||
doctest = false
|
||||
|
||||
[dependencies]
|
||||
oyster_builtin_proc = { path = "../oyster_builtin_proc" }
|
||||
oyster_parser = { path = "../oyster_parser" }
|
||||
thiserror = "1.0.35"
|
||||
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
use std::{borrow::Cow, collections::HashMap};
|
||||
|
||||
use oyster_builtin_proc::builtin;
|
||||
|
||||
use crate::Shell;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
|
@ -9,8 +11,8 @@ pub struct Builtin {
|
|||
pub fun: fn(shell: &mut Shell, args: &[Cow<str>]),
|
||||
}
|
||||
|
||||
pub static HELP: Builtin = {
|
||||
pub fn help(shell: &mut Shell, _args: &[Cow<str>]) {
|
||||
#[builtin(description = "prints help for different builtins")]
|
||||
fn help(shell: &mut Shell, _args: &[Cow<str>]) {
|
||||
println!(
|
||||
r"oyster help:
|
||||
|
||||
|
@ -20,14 +22,7 @@ These are the loaded builtins:"
|
|||
for builtin in shell.builtins().iter() {
|
||||
println!(" {: <8} {}", builtin.name, builtin.description);
|
||||
}
|
||||
}
|
||||
|
||||
Builtin {
|
||||
name: "help",
|
||||
description: "prints help for different builtins",
|
||||
fun: help,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Used to register and retrieve builtins.
|
||||
#[derive(Default)]
|
||||
|
@ -51,7 +46,7 @@ impl BuiltinMap {
|
|||
|
||||
/// Add default builtins.
|
||||
pub fn add_defaults(&mut self) {
|
||||
self.add(HELP);
|
||||
self.add(help);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use std::{
|
||||
borrow::Cow,
|
||||
env,
|
||||
fs::File,
|
||||
io::{BufRead, BufReader, Write},
|
||||
|
@ -13,12 +14,19 @@ use nix::{
|
|||
sys,
|
||||
unistd::{self, ForkResult},
|
||||
};
|
||||
use oyster_runtime::{builtins::Builtin, Shell};
|
||||
use oyster_builtin_proc::builtin;
|
||||
use oyster_runtime::Shell;
|
||||
|
||||
// TODO: test signal return codes
|
||||
|
||||
ioctl_write_int_bad!(tiocsctty, libc::TIOCSCTTY);
|
||||
|
||||
#[builtin(description = "test builtin")]
|
||||
fn test_builtin(_: &mut Shell, _: &[Cow<str>]) {
|
||||
// XXX: this is a workaround around libtest's use of io::set_output_capture
|
||||
let _ = write!(std::io::stdout(), "this is a test\n");
|
||||
}
|
||||
|
||||
/// Forks to redirect stdin, stderr, stdout, then run the commands.
|
||||
/// Relies on inserting a NUL byte in the end, so there shouldn't be NUL in the output.
|
||||
fn collect_output<F>(mut f: F) -> String
|
||||
|
@ -184,14 +192,7 @@ fn simple_builtin() {
|
|||
|
||||
let actual = collect_output(|| {
|
||||
let mut shell = Shell::new().unwrap();
|
||||
shell.builtins_mut().add(Builtin {
|
||||
name: "test_builtin",
|
||||
description: "test",
|
||||
fun: |_, _| {
|
||||
// XXX: this is a workaround around libtest's use of io::set_output_capture
|
||||
let _ = write!(std::io::stdout(), "this is a test\n");
|
||||
},
|
||||
});
|
||||
shell.builtins_mut().add(test_builtin);
|
||||
shell.run(&ast).unwrap();
|
||||
});
|
||||
|
||||
|
@ -220,14 +221,7 @@ fn builtin_redirection() {
|
|||
|
||||
let actual = collect_output(|| {
|
||||
let mut shell = Shell::new().unwrap();
|
||||
shell.builtins_mut().add(Builtin {
|
||||
name: "test_builtin",
|
||||
description: "test",
|
||||
fun: |_, _| {
|
||||
// XXX: this is a workaround around libtest's use of io::set_output_capture
|
||||
let _ = write!(std::io::stdout(), "this is a test\n");
|
||||
},
|
||||
});
|
||||
shell.builtins_mut().add(test_builtin);
|
||||
shell.run(&ast).unwrap();
|
||||
});
|
||||
|
||||
|
|
Loading…
Reference in a new issue