mirror of
https://git.proxmox.com/git/proxmox
synced 2025-05-21 16:24:10 +00:00
import sorted-data proc macro
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
This commit is contained in:
parent
729fed5eac
commit
ceb3ff5544
@ -6,6 +6,8 @@ members = [
|
||||
"proxmox-sys",
|
||||
"proxmox",
|
||||
|
||||
"sorted-data",
|
||||
|
||||
# This is an api server test and may be temporarily broken by changes to
|
||||
# proxmox-api or proxmox-api-macro, but should ultimately be updated to work
|
||||
# again as it's supposed to serve as an example code!
|
||||
|
15
sorted-data/Cargo.toml
Normal file
15
sorted-data/Cargo.toml
Normal file
@ -0,0 +1,15 @@
|
||||
[package]
|
||||
name = "sorted-data"
|
||||
version = "0.1.0"
|
||||
authors = ["Wolfgang Bumiller <w.bumiller@proxmox.com>"]
|
||||
edition = "2018"
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
#failure = { version = "0.1", default-features = false, features = ["std"] }
|
||||
failure = "0.1"
|
||||
proc-macro2 = "1.0"
|
||||
quote = "1.0"
|
||||
syn = { version = "1.0", features = [ "full", "visit-mut" ] }
|
116
sorted-data/src/lib.rs
Normal file
116
sorted-data/src/lib.rs
Normal file
@ -0,0 +1,116 @@
|
||||
extern crate proc_macro;
|
||||
extern crate proc_macro2;
|
||||
|
||||
use std::iter::FromIterator;
|
||||
use std::mem;
|
||||
|
||||
use failure::Error;
|
||||
|
||||
use proc_macro::TokenStream as TokenStream_1;
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::quote;
|
||||
use syn::punctuated::Punctuated;
|
||||
use syn::spanned::Spanned;
|
||||
use syn::visit_mut::VisitMut;
|
||||
use syn::Ident;
|
||||
|
||||
macro_rules! format_err {
|
||||
($span:expr => $($msg:tt)*) => { syn::Error::new_spanned($span, format!($($msg)*)) };
|
||||
($span:expr, $($msg:tt)*) => { syn::Error::new($span, format!($($msg)*)) };
|
||||
}
|
||||
|
||||
//macro_rules! bail {
|
||||
// ($span:expr => $($msg:tt)*) => { return Err(format_err!($span => $($msg)*).into()) };
|
||||
// ($span:expr, $($msg:tt)*) => { return Err(format_err!($span, $($msg)*).into()) };
|
||||
//}
|
||||
|
||||
fn handle_error(data: Result<TokenStream, Error>) -> TokenStream {
|
||||
match data {
|
||||
Ok(output) => output,
|
||||
Err(err) => match err.downcast::<syn::Error>() {
|
||||
Ok(err) => err.to_compile_error(),
|
||||
Err(err) => panic!("error in sorted_struct macro: {}", err),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Enable the `sorted!` expression-position macro in a statement.
|
||||
#[proc_macro_attribute]
|
||||
pub fn sortable(_attr: TokenStream_1, item: TokenStream_1) -> TokenStream_1 {
|
||||
handle_error(sortable_do(item.into())).into()
|
||||
}
|
||||
|
||||
struct SortedData;
|
||||
|
||||
impl VisitMut for SortedData {
|
||||
fn visit_expr_macro_mut(&mut self, i: &mut syn::ExprMacro) {
|
||||
if i.mac.path.is_ident("sorted") {
|
||||
let span = i.mac.path.span();
|
||||
i.mac.path.segments = Punctuated::new();
|
||||
i.mac.path.segments.push(syn::PathSegment {
|
||||
ident: Ident::new("identity", span),
|
||||
arguments: Default::default(),
|
||||
});
|
||||
|
||||
let tokens = mem::replace(&mut i.mac.tokens, TokenStream::new());
|
||||
i.mac.tokens = handle_error(sort_data(tokens));
|
||||
}
|
||||
// and recurse:
|
||||
self.visit_macro_mut(&mut i.mac)
|
||||
}
|
||||
}
|
||||
|
||||
fn sortable_do(item: TokenStream) -> Result<TokenStream, Error> {
|
||||
let mut item: syn::Item = syn::parse2(item)?;
|
||||
SortedData.visit_item_mut(&mut item);
|
||||
Ok(quote!(#item))
|
||||
}
|
||||
|
||||
fn sort_data(data: TokenStream) -> Result<TokenStream, Error> {
|
||||
let mut array: syn::ExprArray = syn::parse2(data)?;
|
||||
let span = array.span();
|
||||
|
||||
let mut fields: Vec<syn::Expr> = mem::replace(&mut array.elems, Punctuated::new())
|
||||
.into_iter()
|
||||
.collect();
|
||||
|
||||
let mut err = None;
|
||||
fields.sort_by(|a, b| {
|
||||
if err.is_some() {
|
||||
return std::cmp::Ordering::Equal;
|
||||
}
|
||||
|
||||
use syn::{Expr, Lit};
|
||||
match (a, b) {
|
||||
// We can sort an array of literals:
|
||||
(Expr::Lit(a), Expr::Lit(b)) => match (&a.lit, &b.lit) {
|
||||
(Lit::Str(a), Lit::Str(b)) => return a.value().cmp(&b.value()),
|
||||
_ => err = Some(format_err!(span, "can only sort by string literals!")),
|
||||
},
|
||||
|
||||
// We can sort an array of tuples where the first element is a literal:
|
||||
(Expr::Tuple(a), Expr::Tuple(b)) => match (a.elems.first(), b.elems.first()) {
|
||||
(Some(Expr::Lit(a)), Some(Expr::Lit(b))) => match (&a.lit, &b.lit) {
|
||||
(Lit::Str(a), Lit::Str(b)) => return a.value().cmp(&b.value()),
|
||||
_ => err = Some(format_err!(span, "can only sort by string literals!")),
|
||||
},
|
||||
_ => {
|
||||
err = Some(format_err!(
|
||||
span,
|
||||
"can only sort tuples starting with literals!"
|
||||
))
|
||||
}
|
||||
},
|
||||
_ => err = Some(format_err!(span, "don't know how to sort this data!")),
|
||||
}
|
||||
std::cmp::Ordering::Equal
|
||||
});
|
||||
|
||||
if let Some(err) = err {
|
||||
return Err(err.into());
|
||||
}
|
||||
|
||||
array.elems = Punctuated::from_iter(fields);
|
||||
|
||||
Ok(quote!(#array))
|
||||
}
|
28
sorted-data/tests/test.rs
Normal file
28
sorted-data/tests/test.rs
Normal file
@ -0,0 +1,28 @@
|
||||
use sorted_data::sortable;
|
||||
|
||||
// The way #[sorted] works we require an 'identity' macro due to the inability of the syntax tree
|
||||
// visitor to change the type of a syntax tree element.
|
||||
//
|
||||
// Iow.: it replaces `sorted!([3, 2, 1])` with `identity!([1, 2, 3])`.
|
||||
macro_rules! identity {
|
||||
($($x:tt)*) => { $($x)* }
|
||||
}
|
||||
|
||||
// In a normal project we would use this Cargo.toml line:
|
||||
//
|
||||
// [dependencies]
|
||||
// proxmox = { version = "0.1", features = [ "sortable-macro" ] }
|
||||
//
|
||||
// Then:
|
||||
// use proxmox::{sortable, identity};
|
||||
|
||||
#[test]
|
||||
fn test_id() {
|
||||
#[sortable]
|
||||
const FOO: [&str; 3] = sorted!(["3", "2", "1"]);
|
||||
assert_eq!(FOO, ["1", "2", "3"]);
|
||||
|
||||
#[sortable]
|
||||
const FOO2: [(&str, usize); 3] = sorted!([("3", 1), ("2", 2), ("1", 3)]);
|
||||
assert_eq!(FOO2, [("1", 3), ("2", 2), ("3", 1)]);
|
||||
}
|
Loading…
Reference in New Issue
Block a user