提交 cd2d0f00 authored 作者: Serhij S's avatar Serhij S

derive WorkerOpts

上级 4de725fa
[package]
name = "roboplc-derive"
version = "0.1.0"
version = "0.1.1"
edition = "2021"
authors = ["Serhij S. <div@altertech.com>"]
license = "Apache-2.0"
......
extern crate proc_macro;
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, Data, DeriveInput, Fields, Meta, NestedMeta};
use syn::{parse_macro_input, Data, DeriveInput, Fields, Lit, Meta, MetaNameValue, NestedMeta};
/// # Panics
///
......@@ -32,33 +32,20 @@ pub fn data_policy_derive(input: TokenStream) -> TokenStream {
default_policy_impl = false;
if let Meta::List(meta_list) = attr.parse_meta().unwrap() {
for nested_meta in meta_list.nested {
if let NestedMeta::Meta(meta) = nested_meta {
match meta
.path()
delivery_policy_value = match nested_meta {
NestedMeta::Meta(meta) => parse_delivery_policy(
meta.path()
.get_ident()
.map(ToString::to_string)
.as_deref()
{
Some("single") => {
delivery_policy_value =
quote! { ::roboplc::DeliveryPolicy::Single }
}
Some("single_optional") => {
delivery_policy_value =
quote! { ::roboplc::DeliveryPolicy::SingleOptional }
}
Some("optional") => {
delivery_policy_value =
quote! { ::roboplc::DeliveryPolicy::Optional }
}
Some("always") => {
delivery_policy_value =
quote! { ::roboplc::DeliveryPolicy::Always }
}
Some(v) => panic!("Unknown policy variant: {}", v),
None => panic!("Policy variant not specified"),
}
}
.map(|v| v.to_string().to_lowercase())
.as_deref(),
),
NestedMeta::Lit(lit) => match lit {
Lit::Str(lit_str) => parse_delivery_policy(Some(
&lit_str.value().to_lowercase(),
)),
_ => panic!("data_delivery value must be a string"),
},
};
}
} else {
panic!("unable to parse data_delivery attribute");
......@@ -176,3 +163,161 @@ pub fn data_policy_derive(input: TokenStream) -> TokenStream {
_ => panic!("DataPolicy can only be derived for enums"),
}
}
fn parse_delivery_policy(s: Option<&str>) -> proc_macro2::TokenStream {
match s {
Some("single") => quote! { ::roboplc::DeliveryPolicy::Single },
Some("single_optional") => quote! { ::roboplc::DeliveryPolicy::SingleOptional },
Some("optional") => quote! { ::roboplc::DeliveryPolicy::Optional },
Some("always") => quote! { ::roboplc::DeliveryPolicy::Always },
Some(v) => panic!("Unknown policy variant: {}", v),
None => panic!("Policy variant not specified"),
}
}
/// # Panics
///
/// Will panic if the worker name is not specified or is invalid
#[allow(clippy::too_many_lines)]
#[proc_macro_derive(WorkerOpts, attributes(worker_opts))]
pub fn worker_opts_derive(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let name = input.ident;
let mut worker_name = None;
let mut stack_size = None;
let mut scheduling = None;
let mut priority = None;
let mut cpus = Vec::new();
for attr in input.attrs {
if attr.path.is_ident("worker_opts") {
if let Ok(Meta::List(meta_list)) = attr.parse_meta() {
for meta in &meta_list.nested {
if let NestedMeta::Meta(Meta::NameValue(MetaNameValue { path, lit, .. })) = meta
{
if path.is_ident("name") {
if let Lit::Str(lit_str) = lit {
worker_name = Some(lit_str.value());
} else {
panic!("worker name must be a quoted string");
}
} else if path.is_ident("stack_size") {
if let Lit::Int(lit_int) = lit {
stack_size = Some(lit_int.base10_parse::<usize>().unwrap());
}
} else if path.is_ident("scheduling") {
scheduling = Some(parse_scheduling(lit));
} else if path.is_ident("priority") {
if let Lit::Int(lit_int) = lit {
priority = Some(lit_int.base10_parse::<i32>().unwrap());
}
} else if path.is_ident("cpu") {
if let Lit::Int(lit_int) = lit {
cpus.push(lit_int.base10_parse::<usize>().unwrap());
} else if let Lit::Str(lit_str) = lit {
let value = lit_str.value();
if value.contains('-') {
let bounds: Vec<&str> = value.split('-').collect();
if bounds.len() == 2 {
if let (Ok(start), Ok(end)) =
(bounds[0].parse::<usize>(), bounds[1].parse::<usize>())
{
for cpu in start..=end {
cpus.push(cpu);
}
}
}
} else if let Ok(cpu) = value.parse::<usize>() {
cpus.push(cpu);
} else {
panic!("Invalid cpu value: {}", value);
}
}
}
}
}
}
}
}
let worker_name = worker_name.expect("worker name is not specified");
assert!(
worker_name.len() <= 15,
"Worker name must be 15 characters or less"
);
let stack_size_impl = if let Some(s) = stack_size {
quote! {
fn worker_stack_size(&self) -> Option<usize> {
Some(#s)
}
}
} else {
quote! {}
};
let priority_impl = if let Some(p) = priority {
quote! {
fn worker_priority(&self) -> Option<i32> {
Some(#p)
}
}
} else {
quote! {}
};
let cpus_impl = if cpus.is_empty() {
quote! {}
} else {
quote! {
fn worker_cpu_ids(&self) -> Option<&[usize]> {
Some(&[#(#cpus),*])
}
}
};
let sched = if let Some(sched) = scheduling {
match sched.to_lowercase().as_str() {
"roundrobin" => Some(quote! { ::roboplc::thread_rt::Scheduling::RoundRobin }),
"fifo" => Some(quote! { ::roboplc::thread_rt::Scheduling::FIFO }),
"idle" => Some(quote! { ::roboplc::thread_rt::Scheduling::Idle }),
"batch" => Some(quote! { ::roboplc::thread_rt::Scheduling::Batch }),
"deadline" => Some(quote! { ::roboplc::thread_rt::Scheduling::DeadLine }),
"normal" => Some(quote! { ::roboplc::thread_rt::Scheduling::Normal }),
v => panic!("Unknown scheduling policy: {}", v),
}
} else {
None
};
let scheduling_impl = if let Some(s) = sched {
quote! {
fn worker_scheduling(&self) -> ::roboplc::thread_rt::Scheduling {
#s
}
}
} else {
quote! {}
};
let expanded = quote! {
impl WorkerOptions for #name {
fn worker_name(&self) -> &str {
#worker_name
}
#stack_size_impl
#scheduling_impl
#priority_impl
#cpus_impl
}
};
expanded.into()
}
fn parse_scheduling(lit: &Lit) -> String {
match lit {
Lit::Str(lit_str) => lit_str.value(),
Lit::Int(lit_int) => lit_int.to_string(),
_ => "normal".to_string(),
}
}
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论