[workspace]
members = [
- "proxmox-tools",
+ "proxmox",
"proxmox-api",
"proxmox-api-macro",
+ "proxmox-sortable-macro",
"proxmox-sys",
- "proxmox",
-
- "sorted-data",
+ "proxmox-tools",
# 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
--- /dev/null
+[package]
+name = "proxmox-sortable-macro"
+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" ] }
--- /dev/null
+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))
+}
--- /dev/null
+use proxmox_sortable_macro::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)]);
+}
proxmox-api-macro = { path = "../proxmox-api-macro", optional = true }
proxmox-sys = { path = "../proxmox-sys" }
proxmox-tools = { path = "../proxmox-tools" }
-sorted-data = { path = "../sorted-data", optional = true }
+proxmox-sortable-macro = { path = "../proxmox-sortable-macro", optional = true }
[features]
default = []
valgrind = ["proxmox-tools/valgrind"]
api-macro = ["proxmox-api-macro"]
-sortable-macro = ["sorted-data"]
+sortable-macro = ["proxmox-sortable-macro"]
#[cfg(feature = "sortable-macro")]
pub use proxmox_tools::identity;
+
+#[cfg(feature = "sortable-macro")]
+pub use proxmox_sortable_macro as sortable_macro;
+
#[cfg(feature = "sortable-macro")]
-pub use sorted_data::{self, sortable};
+pub use proxmox_sortable_macro::sortable;
+++ /dev/null
-[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" ] }
+++ /dev/null
-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))
-}
+++ /dev/null
-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)]);
-}