un-workspace

This commit is contained in:
Aiden McClelland
2024-05-03 13:15:49 -06:00
parent 320d832359
commit 5374aef88d
28 changed files with 43 additions and 2601 deletions

1
.gitignore vendored
View File

@@ -1 +1,2 @@
target/ target/
rpc.sock

View File

@@ -1,2 +1,42 @@
[workspace] [package]
members = ["rpc-toolkit", "rpc-toolkit-macro", "rpc-toolkit-macro-internals"] authors = ["Aiden McClelland <me@drbonez.dev>"]
edition = "2018"
name = "rpc-toolkit"
version = "0.2.3"
description = "A toolkit for creating JSON-RPC 2.0 servers with automatic cli bindings"
license = "MIT"
documentation = "https://docs.rs/rpc-toolkit"
keywords = ["json", "rpc", "cli"]
repository = "https://github.com/Start9Labs/rpc-toolkit"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[features]
cbor = ["serde_cbor"]
default = []
[dependencies]
axum = "0.7.3"
async-stream = "0.3"
async-trait = "0.1"
clap = { version = "4", features = ["derive"] }
futures = "0.3"
http = "1"
http-body-util = "0.1"
# hyper = { version = "1", features = ["server", "http1", "http2", "client"] }
itertools = "0.12"
imbl-value = { git = "https://github.com/Start9Labs/imbl-value.git" }
lazy_format = "2"
lazy_static = "1.4"
openssl = { version = "0.10", features = ["vendored"] }
pin-project = "1"
reqwest = { version = "0.11" }
rpc-toolkit-macro = { version = "0.2.2", path = "../rpc-toolkit-macro" }
serde = { version = "1.0", features = ["derive"] }
serde_cbor = { version = "0.11", optional = true }
serde_json = "1.0"
thiserror = "1.0"
tokio = { version = "1", features = ["full"] }
tokio-stream = { version = "0.1", features = ["io-util", "net"] }
url = "2"
yajrc = "0.1"

View File

@@ -1,2 +0,0 @@
/target
Cargo.lock

View File

@@ -1,14 +0,0 @@
[package]
authors = ["Aiden McClelland <me@drbonez.dev>"]
edition = "2018"
name = "rpc-toolkit-macro-internals"
version = "0.2.2"
description = "internals for macros for rpc-toolkit"
license = "MIT"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
proc-macro2 = "1.0"
quote = "1.0"
syn = { version = "1.0", features = ["full", "fold"] }
itertools = "0.12"

File diff suppressed because it is too large Load Diff

View File

@@ -1,102 +0,0 @@
use std::collections::HashMap;
use syn::*;
pub mod build;
mod parse;
pub enum ExecutionContext {
Standard,
CliOnly(Path),
RpcOnly(Path),
Local(Path),
CustomCli {
custom: Path,
cli: Path,
context: Type,
is_async: bool,
},
}
impl Default for ExecutionContext {
fn default() -> Self {
ExecutionContext::Standard
}
}
#[derive(Default)]
pub struct LeafOptions {
macro_debug: Option<Path>,
blocking: Option<Path>,
is_async: bool,
aliases: Vec<LitStr>,
about: Option<LitStr>,
rename: Option<LitStr>,
exec_ctx: ExecutionContext,
display: Option<Path>,
metadata: HashMap<Ident, Lit>,
clap_attr: Vec<NestedMeta>,
}
pub struct SelfImplInfo {
path: Path,
context: Type,
is_async: bool,
blocking: bool,
}
pub struct ParentOptions {
common: LeafOptions,
subcommands: Vec<Path>,
self_impl: Option<SelfImplInfo>,
}
impl From<LeafOptions> for ParentOptions {
fn from(opt: LeafOptions) -> Self {
ParentOptions {
common: opt,
subcommands: Default::default(),
self_impl: Default::default(),
}
}
}
pub enum Options {
Leaf(LeafOptions),
Parent(ParentOptions),
}
impl Options {
fn to_parent(&mut self) -> Result<&mut ParentOptions> {
if let Options::Leaf(opt) = self {
*self = Options::Parent(std::mem::replace(opt, Default::default()).into());
}
Ok(match self {
Options::Parent(a) => a,
_ => unreachable!(),
})
}
fn common(&mut self) -> &mut LeafOptions {
match self {
Options::Leaf(ref mut opt) => opt,
Options::Parent(opt) => &mut opt.common,
}
}
}
#[derive(Clone)]
pub struct ArgOptions {
ty: Type,
name: Option<Ident>,
rename: Option<LitStr>,
parse: Option<Path>,
stdin: Option<Path>,
default: Option<Path>,
clap_attr: Vec<NestedMeta>,
}
#[derive(Clone)]
pub enum ParamType {
None,
Arg(ArgOptions),
Context(Type),
ParentData(Type),
Request,
Response,
}

View File

@@ -1,851 +0,0 @@
use syn::spanned::Spanned;
use super::*;
pub fn parse_command_attr(args: AttributeArgs) -> Result<Options> {
let mut opt = Options::Leaf(Default::default());
for arg in args {
match arg {
NestedMeta::Meta(Meta::Path(p)) if p.is_ident("macro_debug") => {
opt.common().macro_debug = Some(p);
}
NestedMeta::Meta(Meta::List(list)) if list.path.is_ident("subcommands") => {
let inner = opt.to_parent()?;
if !inner.subcommands.is_empty() {
return Err(Error::new(list.span(), "duplicate argument `subcommands`"));
}
for subcmd in list.nested {
match subcmd {
NestedMeta::Meta(Meta::Path(subcmd)) => inner.subcommands.push(subcmd),
NestedMeta::Lit(Lit::Str(s)) => {
inner.subcommands.push(syn::parse_str(&s.value())?)
}
NestedMeta::Meta(Meta::List(mut self_impl))
if self_impl.path.is_ident("self") =>
{
if self_impl.nested.len() == 1 {
match self_impl.nested.pop().unwrap().into_value() {
NestedMeta::Meta(Meta::Path(self_impl)) => {
if inner.self_impl.is_some() {
return Err(Error::new(
self_impl.span(),
"duplicate argument `self`",
));
}
inner.self_impl = Some(SelfImplInfo {
path: self_impl,
context: parse2(quote::quote! { () }).unwrap(),
is_async: false,
blocking: false,
})
}
NestedMeta::Meta(Meta::List(l)) => {
if inner.self_impl.is_some() {
return Err(Error::new(
self_impl.span(),
"duplicate argument `self`",
));
}
let mut is_async = false;
let mut blocking = false;
let mut context: Type =
parse2(quote::quote! { () }).unwrap();
for meta in &l.nested {
match meta {
NestedMeta::Meta(Meta::Path(p))
if p.is_ident("async") =>
{
is_async = true;
if blocking {
return Err(Error::new(
p.span(),
"`async` and `blocking` are mutually exclusive",
));
}
}
NestedMeta::Meta(Meta::Path(p))
if p.is_ident("blocking") =>
{
blocking = true;
if is_async {
return Err(Error::new(
p.span(),
"`async` and `blocking` are mutually exclusive",
));
}
}
NestedMeta::Meta(Meta::List(p))
if p.path.is_ident("context") =>
{
context = if let Some(NestedMeta::Meta(
Meta::Path(context),
)) = p.nested.first()
{
Type::Path(TypePath {
path: context.clone(),
qself: None,
})
} else {
return Err(Error::new(
p.span(),
"`context` requires a path argument",
));
}
}
arg => {
return Err(Error::new(
arg.span(),
"unknown argument",
))
}
}
}
inner.self_impl = Some(SelfImplInfo {
path: l.path,
context,
is_async,
blocking,
})
}
a => {
return Err(Error::new(
a.span(),
"`self` implementation must be a path, or a list",
))
}
}
} else {
return Err(Error::new(
self_impl.nested.span(),
"`self` can only have one implementation",
));
}
}
arg => {
return Err(Error::new(arg.span(), "unknown argument to `subcommands`"))
}
}
}
if inner.subcommands.is_empty() {
return Err(Error::new(
list.path.span(),
"`subcommands` requires at least 1 argument",
));
}
}
NestedMeta::Meta(Meta::Path(p)) if p.is_ident("subcommands") => {
return Err(Error::new(
p.span(),
"`subcommands` requires at least 1 argument",
));
}
NestedMeta::Meta(Meta::NameValue(nv)) if nv.path.is_ident("subcommands") => {
return Err(Error::new(
nv.path.span(),
"`subcommands` cannot be assigned to",
));
}
NestedMeta::Meta(Meta::List(list)) if list.path.is_ident("display") => {
if list.nested.len() == 1 {
match &list.nested[0] {
NestedMeta::Meta(Meta::Path(display_impl)) => {
if opt.common().display.is_some() {
return Err(Error::new(
display_impl.span(),
"duplicate argument `display`",
));
}
opt.common().display = Some(display_impl.clone())
}
a => {
return Err(Error::new(
a.span(),
"`display` implementation must be a path",
))
}
}
} else {
return Err(Error::new(
list.nested.span(),
"`display` can only have one implementation",
));
}
}
NestedMeta::Meta(Meta::Path(p)) if p.is_ident("display") => {
return Err(Error::new(p.span(), "`display` requires an argument"));
}
NestedMeta::Meta(Meta::NameValue(nv)) if nv.path.is_ident("display") => {
return Err(Error::new(
nv.path.span(),
"`display` cannot be assigned to",
));
}
NestedMeta::Meta(Meta::List(list)) if list.path.is_ident("custom_cli") => {
if list.nested.len() == 1 {
match &opt.common().exec_ctx {
ExecutionContext::Standard => match &list.nested[0] {
NestedMeta::Meta(Meta::Path(custom_cli_impl)) => {
opt.common().exec_ctx = ExecutionContext::CustomCli {
custom: list.path,
context: parse2(quote::quote!{ () }).unwrap(),
cli: custom_cli_impl.clone(),
is_async: false,
}
}
NestedMeta::Meta(Meta::List(custom_cli_impl)) => {
let mut is_async = false;
let mut context: Type = parse2(quote::quote! { () }).unwrap();
for meta in &custom_cli_impl.nested {
match meta {
NestedMeta::Meta(Meta::Path(p)) if p.is_ident("async") => is_async = true,
NestedMeta::Meta(Meta::List(p)) if p.path.is_ident("context") => {
context = if let Some(NestedMeta::Meta(Meta::Path(context))) = p.nested.first() {
Type::Path(TypePath {
path: context.clone(),
qself: None,
})
} else {
return Err(Error::new(p.span(), "`context` requires a path argument"));
}
}
arg => return Err(Error::new(arg.span(), "unknown argument")),
}
}
opt.common().exec_ctx = ExecutionContext::CustomCli {
custom: list.path,
context,
cli: custom_cli_impl.path.clone(),
is_async,
};
}
a => {
return Err(Error::new(
a.span(),
"`custom_cli` implementation must be a path, or a list with 1 argument",
))
}
},
ExecutionContext::CliOnly(_) => {
return Err(Error::new(list.span(), "duplicate argument: `cli_only`"))
}
ExecutionContext::RpcOnly(_) => {
return Err(Error::new(
list.span(),
"`cli_only` and `rpc_only` are mutually exclusive",
))
}
ExecutionContext::Local(_) => {
return Err(Error::new(
list.span(),
"`cli_only` and `local` are mutually exclusive",
))
}
ExecutionContext::CustomCli { .. } => {
return Err(Error::new(
list.span(),
"`cli_only` and `custom_cli` are mutually exclusive",
))
}
}
} else {
return Err(Error::new(
list.nested.span(),
"`custom_cli` can only have one implementation",
));
}
}
NestedMeta::Meta(Meta::Path(p)) if p.is_ident("custom_cli") => {
return Err(Error::new(p.span(), "`custom_cli` requires an argument"));
}
NestedMeta::Meta(Meta::NameValue(nv)) if nv.path.is_ident("custom_cli") => {
return Err(Error::new(
nv.path.span(),
"`custom_cli` cannot be assigned to",
));
}
NestedMeta::Meta(Meta::List(list)) if list.path.is_ident("aliases") => {
if !opt.common().aliases.is_empty() {
return Err(Error::new(list.span(), "duplicate argument `alias`"));
}
for nested in list.nested {
match nested {
NestedMeta::Lit(Lit::Str(alias)) => opt.common().aliases.push(alias),
a => return Err(Error::new(a.span(), "`alias` must be a string")),
}
}
}
NestedMeta::Meta(Meta::Path(p)) if p.is_ident("alias") => {
return Err(Error::new(p.span(), "`alias` requires an argument"));
}
NestedMeta::Meta(Meta::NameValue(nv)) if nv.path.is_ident("alias") => {
if !opt.common().aliases.is_empty() {
return Err(Error::new(nv.path.span(), "duplicate argument `alias`"));
}
if let Lit::Str(alias) = nv.lit {
opt.common().aliases.push(alias);
} else {
return Err(Error::new(nv.lit.span(), "`alias` must be a string"));
}
}
NestedMeta::Meta(Meta::Path(p)) if p.is_ident("cli_only") => {
match &opt.common().exec_ctx {
ExecutionContext::Standard => {
opt.common().exec_ctx = ExecutionContext::CliOnly(p)
}
ExecutionContext::CliOnly(_) => {
return Err(Error::new(p.span(), "duplicate argument: `cli_only`"))
}
ExecutionContext::RpcOnly(_) => {
return Err(Error::new(
p.span(),
"`cli_only` and `rpc_only` are mutually exclusive",
))
}
ExecutionContext::Local(_) => {
return Err(Error::new(
p.span(),
"`cli_only` and `local` are mutually exclusive",
))
}
ExecutionContext::CustomCli { .. } => {
return Err(Error::new(
p.span(),
"`cli_only` and `custom_cli` are mutually exclusive",
))
}
}
}
NestedMeta::Meta(Meta::List(list)) if list.path.is_ident("cli_only") => {
return Err(Error::new(
list.path.span(),
"`cli_only` does not take any arguments",
));
}
NestedMeta::Meta(Meta::NameValue(nv)) if nv.path.is_ident("cli_only") => {
return Err(Error::new(
nv.path.span(),
"`cli_only` cannot be assigned to",
));
}
NestedMeta::Meta(Meta::Path(p)) if p.is_ident("rpc_only") => {
match &opt.common().exec_ctx {
ExecutionContext::Standard => {
opt.common().exec_ctx = ExecutionContext::RpcOnly(p)
}
ExecutionContext::RpcOnly(_) => {
return Err(Error::new(p.span(), "duplicate argument: `rpc_only`"))
}
ExecutionContext::CliOnly(_) => {
return Err(Error::new(
p.span(),
"`rpc_only` and `cli_only` are mutually exclusive",
))
}
ExecutionContext::Local(_) => {
return Err(Error::new(
p.span(),
"`rpc_only` and `local` are mutually exclusive",
))
}
ExecutionContext::CustomCli { .. } => {
return Err(Error::new(
p.span(),
"`rpc_only` and `custom_cli` are mutually exclusive",
))
}
}
}
NestedMeta::Meta(Meta::List(list)) if list.path.is_ident("rpc_only") => {
return Err(Error::new(
list.path.span(),
"`rpc_only` does not take any arguments",
));
}
NestedMeta::Meta(Meta::NameValue(nv)) if nv.path.is_ident("rpc_only") => {
return Err(Error::new(
nv.path.span(),
"`rpc_only` cannot be assigned to",
));
}
NestedMeta::Meta(Meta::Path(p)) if p.is_ident("local") => {
match &opt.common().exec_ctx {
ExecutionContext::Standard => {
opt.common().exec_ctx = ExecutionContext::Local(p)
}
ExecutionContext::Local(_) => {
return Err(Error::new(p.span(), "duplicate argument: `local`"))
}
ExecutionContext::RpcOnly(_) => {
return Err(Error::new(
p.span(),
"`local` and `rpc_only` are mutually exclusive",
))
}
ExecutionContext::CliOnly(_) => {
return Err(Error::new(
p.span(),
"`local` and `cli_only` are mutually exclusive",
))
}
ExecutionContext::CustomCli { .. } => {
return Err(Error::new(
p.span(),
"`local` and `custom_cli` are mutually exclusive",
))
}
}
}
NestedMeta::Meta(Meta::List(list)) if list.path.is_ident("local") => {
return Err(Error::new(
list.path.span(),
"`local` does not take any arguments",
));
}
NestedMeta::Meta(Meta::NameValue(nv)) if nv.path.is_ident("local") => {
return Err(Error::new(nv.path.span(), "`local` cannot be assigned to"));
}
NestedMeta::Meta(Meta::Path(p)) if p.is_ident("blocking") => {
if opt.common().blocking.is_some() {
return Err(Error::new(p.span(), "duplicate argument `blocking`"));
}
opt.common().blocking = Some(p);
}
NestedMeta::Meta(Meta::List(list)) if list.path.is_ident("blocking") => {
return Err(Error::new(
list.path.span(),
"`blocking` does not take any arguments",
));
}
NestedMeta::Meta(Meta::NameValue(nv)) if nv.path.is_ident("blocking") => {
return Err(Error::new(
nv.path.span(),
"`blocking` cannot be assigned to",
));
}
NestedMeta::Meta(Meta::NameValue(nv)) if nv.path.is_ident("about") => {
if let Lit::Str(about) = nv.lit {
if opt.common().about.is_some() {
return Err(Error::new(about.span(), "duplicate argument `about`"));
}
opt.common().about = Some(about);
} else {
return Err(Error::new(nv.lit.span(), "about message must be a string"));
}
}
NestedMeta::Meta(Meta::List(list)) if list.path.is_ident("about") => {
return Err(Error::new(
list.path.span(),
"`about` does not take any arguments",
));
}
NestedMeta::Meta(Meta::Path(p)) if p.is_ident("about") => {
return Err(Error::new(p.span(), "`about` must be assigned to"));
}
NestedMeta::Meta(Meta::NameValue(nv)) if nv.path.is_ident("rename") => {
if let Lit::Str(rename) = nv.lit {
if opt.common().rename.is_some() {
return Err(Error::new(rename.span(), "duplicate argument `rename`"));
}
opt.common().rename = Some(rename);
} else {
return Err(Error::new(nv.lit.span(), "`rename` must be a string"));
}
}
NestedMeta::Meta(Meta::List(list)) if list.path.is_ident("rename") => {
return Err(Error::new(
list.path.span(),
"`rename` does not take any arguments",
));
}
NestedMeta::Meta(Meta::Path(p)) if p.is_ident("rename") => {
return Err(Error::new(p.span(), "`rename` must be assigned to"));
}
NestedMeta::Meta(Meta::List(list)) if list.path.is_ident("metadata") => {
for meta in list.nested {
match meta {
NestedMeta::Meta(Meta::NameValue(metadata_pair)) => {
let ident = metadata_pair.path.get_ident().ok_or(Error::new(
metadata_pair.path.span(),
"must be an identifier",
))?;
if opt
.common()
.metadata
.insert(ident.clone(), metadata_pair.lit)
.is_some()
{
return Err(Error::new(
ident.span(),
format!("duplicate metadata `{}`", ident),
));
}
}
a => {
return Err(Error::new(
a.span(),
"`metadata` takes a list of identifiers to be assigned to",
))
}
}
}
}
NestedMeta::Meta(Meta::Path(p)) if p.is_ident("metadata") => {
return Err(Error::new(
p.span(),
"`metadata` takes a list of identifiers to be assigned to",
));
}
NestedMeta::Meta(Meta::NameValue(nv)) if nv.path.is_ident("metadata") => {
return Err(Error::new(
nv.path.span(),
"`metadata` cannot be assigned to",
));
}
_ => {
return Err(Error::new(arg.span(), "unknown argument"));
}
}
}
if let Options::Parent(opt) = &opt {
if opt.self_impl.is_none() {
if let Some(display) = &opt.common.display {
return Err(Error::new(
display.span(),
"cannot define `display` for a command without an implementation",
));
}
match &opt.common.exec_ctx {
ExecutionContext::CliOnly(cli_only) => {
return Err(Error::new(
cli_only.span(),
"cannot define `cli_only` for a command without an implementation",
))
}
ExecutionContext::RpcOnly(rpc_only) => {
return Err(Error::new(
rpc_only.span(),
"cannot define `rpc_only` for a command without an implementation",
))
}
_ => (),
}
}
}
Ok(opt)
}
pub fn parse_arg_attr(attr: Attribute, arg: PatType) -> Result<ArgOptions> {
let arg_span = arg.span();
let meta = attr.parse_meta()?;
let mut opt = ArgOptions {
ty: *arg.ty,
name: match *arg.pat {
Pat::Ident(i) => Some(i.ident),
_ => None,
},
rename: None,
parse: None,
stdin: None,
default: None,
clap_attr: Vec::new(),
};
match meta {
Meta::List(list) => {
for arg in list.nested {
match arg {
NestedMeta::Meta(Meta::List(mut list)) if list.path.is_ident("parse") => {
if list.nested.len() == 1 {
match list.nested.pop().unwrap().into_value() {
NestedMeta::Meta(Meta::Path(parse_impl)) => {
if opt.parse.is_some() {
return Err(Error::new(
list.span(),
"duplicate argument `parse`",
));
}
opt.parse = Some(parse_impl)
}
a => {
return Err(Error::new(
a.span(),
"`parse` implementation must be a path",
))
}
}
} else {
return Err(Error::new(
list.nested.span(),
"`parse` can only have one implementation",
));
}
}
NestedMeta::Meta(Meta::Path(p)) if p.is_ident("parse") => {
return Err(Error::new(p.span(), "`parse` requires an argument"));
}
NestedMeta::Meta(Meta::NameValue(nv)) if nv.path.is_ident("parse") => {
return Err(Error::new(nv.path.span(), "`parse` cannot be assigned to"));
}
NestedMeta::Meta(Meta::Path(p)) if p.is_ident("stdin") => {
if !opt.clap_attr.is_empty() {
return Err(Error::new(
p.span(),
"`stdin` and clap parser attributes are mutually exclusive",
));
}
opt.stdin = Some(p);
}
NestedMeta::Meta(Meta::List(list)) if list.path.is_ident("stdin") => {
return Err(Error::new(
list.path.span(),
"`stdin` does not take any arguments",
));
}
NestedMeta::Meta(Meta::NameValue(nv)) if nv.path.is_ident("stdin") => {
return Err(Error::new(nv.path.span(), "`stdin` cannot be assigned to"));
}
NestedMeta::Meta(Meta::NameValue(nv)) if nv.path.is_ident("rename") => {
if let Lit::Str(rename) = nv.lit {
if opt.rename.is_some() {
return Err(Error::new(
rename.span(),
"duplicate argument `rename`",
));
}
opt.clap_attr
.push(NestedMeta::Meta(Meta::NameValue(MetaNameValue {
path: Path::from(Ident::new("name", nv.path.span())),
eq_token: nv.eq_token,
lit: Lit::Str(rename.clone()),
})));
opt.rename = Some(rename);
} else {
return Err(Error::new(nv.lit.span(), "`rename` must be a string"));
}
}
NestedMeta::Meta(Meta::List(list)) if list.path.is_ident("rename") => {
return Err(Error::new(
list.path.span(),
"`rename` does not take any arguments",
));
}
NestedMeta::Meta(Meta::Path(p)) if p.is_ident("rename") => {
return Err(Error::new(p.span(), "`rename` must be assigned to"));
}
NestedMeta::Meta(Meta::NameValue(nv)) if nv.path.is_ident("default") => {
return Err(Error::new(nv.lit.span(), "`default` cannot be assigned to"));
}
NestedMeta::Meta(Meta::List(list)) if list.path.is_ident("default") => {
return Err(Error::new(
list.path.span(),
"`default` does not take any arguments",
));
}
NestedMeta::Meta(Meta::Path(p)) if p.is_ident("default") => {
opt.clap_attr
.push(NestedMeta::Meta(Meta::Path(Path::from(Ident::new(
"default_value_t",
p.span(),
)))));
opt.default = Some(p);
}
unknown => {
if opt.stdin.is_some() {
return Err(Error::new(
unknown.span(),
"`stdin` and clap parser attributes are mutually exclusive",
));
}
opt.clap_attr.push(unknown);
}
}
}
}
Meta::Path(_) => (),
Meta::NameValue(nv) => return Err(Error::new(nv.span(), "`arg` cannot be assigned to")),
}
if opt.name.is_none() {
return Err(Error::new(
arg_span,
"cannot infer name for pattern argument",
));
}
Ok(opt)
}
pub fn parse_param_attrs(item: &mut ItemFn) -> Result<Vec<ParamType>> {
let mut params = Vec::new();
for param in item.sig.inputs.iter_mut() {
if let FnArg::Typed(param) = param {
let mut ty = ParamType::None;
let mut i = 0;
while i != param.attrs.len() {
if param.attrs[i].path.is_ident("arg") {
let attr = param.attrs.remove(i);
if matches!(ty, ParamType::None) {
ty = ParamType::Arg(parse_arg_attr(attr, param.clone())?);
} else if matches!(ty, ParamType::Arg(_)) {
return Err(Error::new(
attr.span(),
"`arg` attribute may only be specified once",
));
} else if matches!(ty, ParamType::Context(_)) {
return Err(Error::new(
attr.span(),
"`arg` and `context` are mutually exclusive",
));
} else if matches!(ty, ParamType::ParentData(_)) {
return Err(Error::new(
attr.span(),
"`arg` and `parent_data` are mutually exclusive",
));
} else if matches!(ty, ParamType::Request) {
return Err(Error::new(
attr.span(),
"`arg` and `request` are mutually exclusive",
));
} else if matches!(ty, ParamType::Response) {
return Err(Error::new(
attr.span(),
"`arg` and `response` are mutually exclusive",
));
}
} else if param.attrs[i].path.is_ident("context") {
let attr = param.attrs.remove(i);
if matches!(ty, ParamType::None) {
ty = ParamType::Context(*param.ty.clone());
} else if matches!(ty, ParamType::Context(_)) {
return Err(Error::new(
attr.span(),
"`context` attribute may only be specified once",
));
} else if matches!(ty, ParamType::Arg(_)) {
return Err(Error::new(
attr.span(),
"`arg` and `context` are mutually exclusive",
));
} else if matches!(ty, ParamType::ParentData(_)) {
return Err(Error::new(
attr.span(),
"`context` and `parent_data` are mutually exclusive",
));
} else if matches!(ty, ParamType::Request) {
return Err(Error::new(
attr.span(),
"`context` and `request` are mutually exclusive",
));
} else if matches!(ty, ParamType::Response) {
return Err(Error::new(
attr.span(),
"`context` and `response` are mutually exclusive",
));
}
} else if param.attrs[i].path.is_ident("parent_data") {
let attr = param.attrs.remove(i);
if matches!(ty, ParamType::None) {
ty = ParamType::ParentData(*param.ty.clone());
} else if matches!(ty, ParamType::ParentData(_)) {
return Err(Error::new(
attr.span(),
"`parent_data` attribute may only be specified once",
));
} else if matches!(ty, ParamType::Arg(_)) {
return Err(Error::new(
attr.span(),
"`arg` and `parent_data` are mutually exclusive",
));
} else if matches!(ty, ParamType::Context(_)) {
return Err(Error::new(
attr.span(),
"`context` and `parent_data` are mutually exclusive",
));
} else if matches!(ty, ParamType::Request) {
return Err(Error::new(
attr.span(),
"`parent_data` and `request` are mutually exclusive",
));
} else if matches!(ty, ParamType::Response) {
return Err(Error::new(
attr.span(),
"`parent_data` and `response` are mutually exclusive",
));
}
} else if param.attrs[i].path.is_ident("request") {
let attr = param.attrs.remove(i);
if matches!(ty, ParamType::None) {
ty = ParamType::Request;
} else if matches!(ty, ParamType::Request) {
return Err(Error::new(
attr.span(),
"`request` attribute may only be specified once",
));
} else if matches!(ty, ParamType::Arg(_)) {
return Err(Error::new(
attr.span(),
"`arg` and `request` are mutually exclusive",
));
} else if matches!(ty, ParamType::Context(_)) {
return Err(Error::new(
attr.span(),
"`context` and `request` are mutually exclusive",
));
} else if matches!(ty, ParamType::ParentData(_)) {
return Err(Error::new(
attr.span(),
"`parent_data` and `request` are mutually exclusive",
));
} else if matches!(ty, ParamType::Response) {
return Err(Error::new(
attr.span(),
"`request` and `response` are mutually exclusive",
));
}
} else if param.attrs[i].path.is_ident("response") {
let attr = param.attrs.remove(i);
if matches!(ty, ParamType::None) {
ty = ParamType::Response;
} else if matches!(ty, ParamType::Response) {
return Err(Error::new(
attr.span(),
"`response` attribute may only be specified once",
));
} else if matches!(ty, ParamType::Arg(_)) {
return Err(Error::new(
attr.span(),
"`arg` and `response` are mutually exclusive",
));
} else if matches!(ty, ParamType::Context(_)) {
return Err(Error::new(
attr.span(),
"`context` and `response` are mutually exclusive",
));
} else if matches!(ty, ParamType::ParentData(_)) {
return Err(Error::new(
attr.span(),
"`parent_data` and `response` are mutually exclusive",
));
} else if matches!(ty, ParamType::Request) {
return Err(Error::new(
attr.span(),
"`request` and `response` are mutually exclusive",
));
}
} else {
i += 1;
}
}
if matches!(ty, ParamType::None) {
return Err(Error::new(
param.span(),
"must specify either `arg`, `context`, `parent_data`, `request`, or `response` attributes",
));
}
params.push(ty)
} else {
return Err(Error::new(
param.span(),
"commands may not take `self` as an argument",
));
}
}
Ok(params)
}

View File

@@ -1,12 +0,0 @@
macro_rules! macro_try {
($x:expr) => {
match $x {
Ok(a) => a,
Err(e) => return e.to_compile_error(),
}
};
}
mod command;
pub use command::build::build as build_command;

View File

@@ -1,2 +0,0 @@
/target
Cargo.lock

View File

@@ -1,17 +0,0 @@
[package]
authors = ["Aiden McClelland <me@drbonez.dev>"]
edition = "2018"
name = "rpc-toolkit-macro"
version = "0.2.2"
description = "macros for rpc-toolkit"
license = "MIT"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[lib]
proc-macro = true
[dependencies]
proc-macro2 = "1.0"
rpc-toolkit-macro-internals = { version = "=0.2.2", path = "../rpc-toolkit-macro-internals" }
syn = "1.0"

View File

@@ -1,44 +0,0 @@
use proc_macro::{Span, TokenStream};
use rpc_toolkit_macro_internals::*;
#[proc_macro_attribute]
pub fn command(args: TokenStream, item: TokenStream) -> TokenStream {
let args = syn::parse_macro_input!(args as syn::AttributeArgs);
let item = syn::parse_macro_input!(item as syn::ItemFn);
build_command(args, item).into()
}
/// `#[arg(...)]` -> Take this argument as a parameter
/// - `#[arg(help = "Help text")]` -> Set help text for the arg
/// - `#[arg(alias = "ls")]` -> Set the alias `ls` in the CLI
/// - `#[arg(aliases("show", "ls"))]` -> Set the aliases `ls` and `show` in the CLI
/// - `#[arg(rename = "new_name")]` -> Set the name of the arg to `new_name` in the RPC and CLI
/// - `#[arg(short = "a")]` -> Set the "short" representation of the arg to `-a` on the CLI
/// - `#[arg(long = "arg")]` -> Set the "long" representation of the arg to `--arg` on the CLI
/// - `#[arg(parse(custom_parse_fn))]` -> Use the function `custom_parse_fn` to parse the arg from the CLI
/// - `custom_parse_fn :: Into<RpcError> err => (&str, &ArgMatches) -> Result<arg, err>`
/// - note: `arg` is the type of the argument
/// - `#[arg(stdin)]` -> Parse the argument from stdin when using the CLI
/// - `custom_parse_fn :: Into<RpcError> err => (&[u8], &ArgMatches) -> Result<arg, err>`
/// - `#[arg(count)]` -> Treat argument as flag, count occurrences
/// - `#[arg(multiple)]` -> Allow the arg to be specified multiple times. Collect the args after parsing.
#[proc_macro_attribute]
pub fn arg(_: TokenStream, _: TokenStream) -> TokenStream {
syn::Error::new(
Span::call_site().into(),
"`arg` is only allowed on arguments of a function with the `command` attribute",
)
.to_compile_error()
.into()
}
/// - `#[context]` -> Passes the application context into this parameter
#[proc_macro_attribute]
pub fn context(_: TokenStream, _: TokenStream) -> TokenStream {
syn::Error::new(
Span::call_site().into(),
"`context` is only allowed on arguments of a function with the `command` attribute",
)
.to_compile_error()
.into()
}

View File

@@ -1,2 +0,0 @@
/target
Cargo.lock

View File

@@ -1,42 +0,0 @@
[package]
authors = ["Aiden McClelland <me@drbonez.dev>"]
edition = "2018"
name = "rpc-toolkit"
version = "0.2.3"
description = "A toolkit for creating JSON-RPC 2.0 servers with automatic cli bindings"
license = "MIT"
documentation = "https://docs.rs/rpc-toolkit"
keywords = ["json", "rpc", "cli"]
repository = "https://github.com/Start9Labs/rpc-toolkit"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[features]
cbor = ["serde_cbor"]
default = []
[dependencies]
axum = "0.7.3"
async-stream = "0.3"
async-trait = "0.1"
clap = { version = "4", features = ["derive"] }
futures = "0.3"
http = "1"
http-body-util = "0.1"
# hyper = { version = "1", features = ["server", "http1", "http2", "client"] }
itertools = "0.12"
imbl-value = { git = "https://github.com/Start9Labs/imbl-value.git" }
lazy_format = "2"
lazy_static = "1.4"
openssl = { version = "0.10", features = ["vendored"] }
pin-project = "1"
reqwest = { version = "0.11" }
rpc-toolkit-macro = { version = "0.2.2", path = "../rpc-toolkit-macro" }
serde = { version = "1.0", features = ["derive"] }
serde_cbor = { version = "0.11", optional = true }
serde_json = "1.0"
thiserror = "1.0"
tokio = { version = "1", features = ["full"] }
tokio-stream = { version = "0.1", features = ["io-util", "net"] }
url = "2"
yajrc = "0.1"