Merge remote-tracking branch 'origin/master' into refactor-apply-newline-style
This commit is contained in:
commit
703a2eab19
10
Cargo.lock
generated
10
Cargo.lock
generated
@ -143,6 +143,15 @@ dependencies = [
|
||||
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "config_proc_macro"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 0.15.32 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "constant_time_eq"
|
||||
version = "0.1.3"
|
||||
@ -772,6 +781,7 @@ dependencies = [
|
||||
"atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"bytecount 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cargo_metadata 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"config_proc_macro 0.1.0",
|
||||
"derive-new 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"diff 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"dirs 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -60,6 +60,8 @@ dirs = "1.0.4"
|
||||
ignore = "0.4.6"
|
||||
annotate-snippets = { version = "0.5.0", features = ["ansi_term"] }
|
||||
|
||||
config_proc_macro = { path = "config_proc_macro" }
|
||||
|
||||
# A noop dependency that changes in the Rust repository, it's a bit of a hack.
|
||||
# See the `src/tools/rustc-workspace-hack/README.md` file in `rust-lang/rust`
|
||||
# for more information.
|
||||
|
1
config_proc_macro/.gitignore
vendored
Normal file
1
config_proc_macro/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
target/
|
68
config_proc_macro/Cargo.lock
generated
Normal file
68
config_proc_macro/Cargo.lock
generated
Normal file
@ -0,0 +1,68 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
[[package]]
|
||||
name = "config_proc_macro"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 0.15.29 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "0.4.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "0.6.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.89"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.89"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 0.15.29 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "0.15.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[metadata]
|
||||
"checksum proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)" = "4d317f9caece796be1980837fd5cb3dfec5613ebdb04ad0956deea83ce168915"
|
||||
"checksum quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)" = "cdd8e04bd9c52e0342b406469d494fcb033be4bdbe5c606016defbb1681411e1"
|
||||
"checksum serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)" = "92514fb95f900c9b5126e32d020f5c6d40564c27a5ea6d1d7d9f157a96623560"
|
||||
"checksum serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)" = "bb6eabf4b5914e88e24eea240bb7c9f9a2cbc1bbbe8d961d381975ec3c6b806c"
|
||||
"checksum syn 0.15.29 (registry+https://github.com/rust-lang/crates.io-index)" = "1825685f977249735d510a242a6727b46efe914bb67e38d30c071b1b72b1d5c2"
|
||||
"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
|
20
config_proc_macro/Cargo.toml
Normal file
20
config_proc_macro/Cargo.toml
Normal file
@ -0,0 +1,20 @@
|
||||
[package]
|
||||
name = "config_proc_macro"
|
||||
version = "0.1.0"
|
||||
authors = ["topecongiro <seuchida@gmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
proc-macro2 = "0.4"
|
||||
quote = "0.6"
|
||||
syn = { version = "0.15", features = ["full", "visit"] }
|
||||
|
||||
[dev-dependencies]
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
|
||||
[features]
|
||||
default = []
|
||||
debug-with-rustfmt = []
|
57
config_proc_macro/src/attrs.rs
Normal file
57
config_proc_macro/src/attrs.rs
Normal file
@ -0,0 +1,57 @@
|
||||
//! This module provides utilities for handling attributes on variants
|
||||
//! of `config_type` enum. Currently there are two types of attributes
|
||||
//! that could appear on the variants of `config_type` enum: `doc_hint`
|
||||
//! and `value`. Both comes in the form of name-value pair whose value
|
||||
//! is string literal.
|
||||
|
||||
/// Returns the value of the first `doc_hint` attribute in the given slice or
|
||||
/// `None` if `doc_hint` attribute is not available.
|
||||
pub fn find_doc_hint(attrs: &[syn::Attribute]) -> Option<String> {
|
||||
attrs.iter().filter_map(doc_hint).next()
|
||||
}
|
||||
|
||||
/// Returns `true` if the given attribute is a `doc_hint` attribute.
|
||||
pub fn is_doc_hint(attr: &syn::Attribute) -> bool {
|
||||
is_attr_name_value(attr, "doc_hint")
|
||||
}
|
||||
|
||||
/// Returns a string literal value if the given attribute is `doc_hint`
|
||||
/// attribute or `None` otherwise.
|
||||
pub fn doc_hint(attr: &syn::Attribute) -> Option<String> {
|
||||
get_name_value_str_lit(attr, "doc_hint")
|
||||
}
|
||||
|
||||
/// Returns the value of the first `value` attribute in the given slice or
|
||||
/// `None` if `value` attribute is not available.
|
||||
pub fn find_config_value(attrs: &[syn::Attribute]) -> Option<String> {
|
||||
attrs.iter().filter_map(config_value).next()
|
||||
}
|
||||
|
||||
/// Returns a string literal value if the given attribute is `value`
|
||||
/// attribute or `None` otherwise.
|
||||
pub fn config_value(attr: &syn::Attribute) -> Option<String> {
|
||||
get_name_value_str_lit(attr, "value")
|
||||
}
|
||||
|
||||
/// Returns `true` if the given attribute is a `value` attribute.
|
||||
pub fn is_config_value(attr: &syn::Attribute) -> bool {
|
||||
is_attr_name_value(attr, "value")
|
||||
}
|
||||
|
||||
fn is_attr_name_value(attr: &syn::Attribute, name: &str) -> bool {
|
||||
attr.interpret_meta().map_or(false, |meta| match meta {
|
||||
syn::Meta::NameValue(syn::MetaNameValue { ref ident, .. }) if ident == name => true,
|
||||
_ => false,
|
||||
})
|
||||
}
|
||||
|
||||
fn get_name_value_str_lit(attr: &syn::Attribute, name: &str) -> Option<String> {
|
||||
attr.interpret_meta().and_then(|meta| match meta {
|
||||
syn::Meta::NameValue(syn::MetaNameValue {
|
||||
ref ident,
|
||||
lit: syn::Lit::Str(ref lit_str),
|
||||
..
|
||||
}) if ident == name => Some(lit_str.value()),
|
||||
_ => None,
|
||||
})
|
||||
}
|
15
config_proc_macro/src/config_type.rs
Normal file
15
config_proc_macro/src/config_type.rs
Normal file
@ -0,0 +1,15 @@
|
||||
use proc_macro2::TokenStream;
|
||||
|
||||
use crate::item_enum::define_config_type_on_enum;
|
||||
use crate::item_struct::define_config_type_on_struct;
|
||||
|
||||
/// Defines `config_type` on enum or struct.
|
||||
// FIXME: Implement this on struct.
|
||||
pub fn define_config_type(input: &syn::Item) -> TokenStream {
|
||||
match input {
|
||||
syn::Item::Struct(st) => define_config_type_on_struct(st),
|
||||
syn::Item::Enum(en) => define_config_type_on_enum(en),
|
||||
_ => panic!("Expected enum or struct"),
|
||||
}
|
||||
.unwrap()
|
||||
}
|
178
config_proc_macro/src/item_enum.rs
Normal file
178
config_proc_macro/src/item_enum.rs
Normal file
@ -0,0 +1,178 @@
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::quote;
|
||||
|
||||
use crate::attrs::*;
|
||||
use crate::utils::*;
|
||||
|
||||
type Variants = syn::punctuated::Punctuated<syn::Variant, syn::Token![,]>;
|
||||
|
||||
/// Defines and implements `config_type` enum.
|
||||
pub fn define_config_type_on_enum(em: &syn::ItemEnum) -> syn::Result<TokenStream> {
|
||||
let syn::ItemEnum {
|
||||
vis,
|
||||
enum_token,
|
||||
ident,
|
||||
generics,
|
||||
variants,
|
||||
..
|
||||
} = em;
|
||||
|
||||
let mod_name_str = format!("__define_config_type_on_enum_{}", ident);
|
||||
let mod_name = syn::Ident::new(&mod_name_str, ident.span());
|
||||
let variants = fold_quote(variants.iter().map(process_variant), |meta| quote!(#meta,));
|
||||
|
||||
let impl_doc_hint = impl_doc_hint(&em.ident, &em.variants);
|
||||
let impl_from_str = impl_from_str(&em.ident, &em.variants);
|
||||
let impl_serde = impl_serde(&em.ident, &em.variants);
|
||||
let impl_deserialize = impl_deserialize(&em.ident, &em.variants);
|
||||
|
||||
Ok(quote! {
|
||||
#[allow(non_snake_case)]
|
||||
mod #mod_name {
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub #enum_token #ident #generics { #variants }
|
||||
#impl_doc_hint
|
||||
#impl_from_str
|
||||
#impl_serde
|
||||
#impl_deserialize
|
||||
}
|
||||
#vis use #mod_name::#ident;
|
||||
})
|
||||
}
|
||||
|
||||
/// Remove attributes specific to `config_proc_macro` from enum variant fields.
|
||||
fn process_variant(variant: &syn::Variant) -> TokenStream {
|
||||
let metas = variant
|
||||
.attrs
|
||||
.iter()
|
||||
.filter(|attr| !is_doc_hint(attr) && !is_config_value(attr));
|
||||
let attrs = fold_quote(metas, |meta| quote!(#meta));
|
||||
let syn::Variant { ident, fields, .. } = variant;
|
||||
quote!(#attrs #ident #fields)
|
||||
}
|
||||
|
||||
fn impl_doc_hint(ident: &syn::Ident, variants: &Variants) -> TokenStream {
|
||||
let doc_hint = variants
|
||||
.iter()
|
||||
.map(doc_hint_of_variant)
|
||||
.collect::<Vec<_>>()
|
||||
.join("|");
|
||||
let doc_hint = format!("[{}]", doc_hint);
|
||||
quote! {
|
||||
use crate::config::ConfigType;
|
||||
impl ConfigType for #ident {
|
||||
fn doc_hint() -> String {
|
||||
#doc_hint.to_owned()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn impl_from_str(ident: &syn::Ident, variants: &Variants) -> TokenStream {
|
||||
let vs = variants
|
||||
.iter()
|
||||
.filter(|v| is_unit(v))
|
||||
.map(|v| (config_value_of_variant(v), &v.ident));
|
||||
let if_patterns = fold_quote(vs, |(s, v)| {
|
||||
quote! {
|
||||
if #s.eq_ignore_ascii_case(s) {
|
||||
return Ok(#ident::#v);
|
||||
}
|
||||
}
|
||||
});
|
||||
quote! {
|
||||
impl ::std::str::FromStr for #ident {
|
||||
type Err = &'static str;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
#if_patterns
|
||||
return Err("Bad variant");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn doc_hint_of_variant(variant: &syn::Variant) -> String {
|
||||
find_doc_hint(&variant.attrs).unwrap_or(variant.ident.to_string())
|
||||
}
|
||||
|
||||
fn config_value_of_variant(variant: &syn::Variant) -> String {
|
||||
find_config_value(&variant.attrs).unwrap_or(variant.ident.to_string())
|
||||
}
|
||||
|
||||
fn impl_serde(ident: &syn::Ident, variants: &Variants) -> TokenStream {
|
||||
let arms = fold_quote(variants.iter(), |v| {
|
||||
let v_ident = &v.ident;
|
||||
let pattern = match v.fields {
|
||||
syn::Fields::Named(..) => quote!(#ident::v_ident{..}),
|
||||
syn::Fields::Unnamed(..) => quote!(#ident::#v_ident(..)),
|
||||
syn::Fields::Unit => quote!(#ident::#v_ident),
|
||||
};
|
||||
let option_value = config_value_of_variant(v);
|
||||
quote! {
|
||||
#pattern => serializer.serialize_str(&#option_value),
|
||||
}
|
||||
});
|
||||
|
||||
quote! {
|
||||
impl ::serde::ser::Serialize for #ident {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: ::serde::ser::Serializer,
|
||||
{
|
||||
use serde::ser::Error;
|
||||
match self {
|
||||
#arms
|
||||
_ => Err(S::Error::custom(format!("Cannot serialize {:?}", self))),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Currently only unit variants are supported.
|
||||
fn impl_deserialize(ident: &syn::Ident, variants: &Variants) -> TokenStream {
|
||||
let supported_vs = variants.iter().filter(|v| is_unit(v));
|
||||
let if_patterns = fold_quote(supported_vs, |v| {
|
||||
let config_value = config_value_of_variant(v);
|
||||
let variant_ident = &v.ident;
|
||||
quote! {
|
||||
if #config_value.eq_ignore_ascii_case(s) {
|
||||
return Ok(#ident::#variant_ident);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let supported_vs = variants.iter().filter(|v| is_unit(v));
|
||||
let allowed = fold_quote(supported_vs.map(config_value_of_variant), |s| quote!(#s,));
|
||||
|
||||
quote! {
|
||||
impl<'de> serde::de::Deserialize<'de> for #ident {
|
||||
fn deserialize<D>(d: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
use serde::de::{Error, Visitor};
|
||||
use std::marker::PhantomData;
|
||||
use std::fmt;
|
||||
struct StringOnly<T>(PhantomData<T>);
|
||||
impl<'de, T> Visitor<'de> for StringOnly<T>
|
||||
where T: serde::Deserializer<'de> {
|
||||
type Value = String;
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
formatter.write_str("string")
|
||||
}
|
||||
fn visit_str<E>(self, value: &str) -> Result<String, E> {
|
||||
Ok(String::from(value))
|
||||
}
|
||||
}
|
||||
let s = &d.deserialize_string(StringOnly::<D>(PhantomData))?;
|
||||
|
||||
#if_patterns
|
||||
|
||||
static ALLOWED: &'static[&str] = &[#allowed];
|
||||
Err(D::Error::unknown_variant(&s, ALLOWED))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
5
config_proc_macro/src/item_struct.rs
Normal file
5
config_proc_macro/src/item_struct.rs
Normal file
@ -0,0 +1,5 @@
|
||||
use proc_macro2::TokenStream;
|
||||
|
||||
pub fn define_config_type_on_struct(_st: &syn::ItemStruct) -> syn::Result<TokenStream> {
|
||||
unimplemented!()
|
||||
}
|
27
config_proc_macro/src/lib.rs
Normal file
27
config_proc_macro/src/lib.rs
Normal file
@ -0,0 +1,27 @@
|
||||
//! This crate provides a derive macro for `ConfigType`.
|
||||
|
||||
#![recursion_limit = "256"]
|
||||
|
||||
extern crate proc_macro;
|
||||
|
||||
mod attrs;
|
||||
mod config_type;
|
||||
mod item_enum;
|
||||
mod item_struct;
|
||||
mod utils;
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
use syn::parse_macro_input;
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn config_type(_args: TokenStream, input: TokenStream) -> TokenStream {
|
||||
let input = parse_macro_input!(input as syn::Item);
|
||||
let output = config_type::define_config_type(&input);
|
||||
|
||||
#[cfg(feature = "debug-with-rustfmt")]
|
||||
{
|
||||
utils::debug_with_rustfmt(&output);
|
||||
}
|
||||
|
||||
TokenStream::from(output)
|
||||
}
|
45
config_proc_macro/src/utils.rs
Normal file
45
config_proc_macro/src/utils.rs
Normal file
@ -0,0 +1,45 @@
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::{quote, ToTokens};
|
||||
|
||||
pub fn fold_quote<F, I, T>(input: impl Iterator<Item = I>, f: F) -> TokenStream
|
||||
where
|
||||
F: Fn(I) -> T,
|
||||
T: ToTokens,
|
||||
{
|
||||
input.fold(quote! {}, |acc, x| {
|
||||
let y = f(x);
|
||||
quote! { #acc #y }
|
||||
})
|
||||
}
|
||||
|
||||
pub fn is_unit(v: &syn::Variant) -> bool {
|
||||
match v.fields {
|
||||
syn::Fields::Unit => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "debug-with-rustfmt")]
|
||||
/// Pretty-print the output of proc macro using rustfmt.
|
||||
pub fn debug_with_rustfmt(input: &TokenStream) {
|
||||
use std::io::Write;
|
||||
use std::process::{Command, Stdio};
|
||||
|
||||
let mut child = Command::new("rustfmt")
|
||||
.stdin(Stdio::piped())
|
||||
.stdout(Stdio::piped())
|
||||
.spawn()
|
||||
.expect("Failed to spawn rustfmt in stdio mode");
|
||||
{
|
||||
let stdin = child.stdin.as_mut().expect("Failed to get stdin");
|
||||
stdin
|
||||
.write_all(format!("{}", input).as_bytes())
|
||||
.expect("Failed to write to stdin");
|
||||
}
|
||||
let rustfmt_output = child.wait_with_output().expect("rustfmt has failed");
|
||||
|
||||
eprintln!(
|
||||
"{}",
|
||||
String::from_utf8(rustfmt_output.stdout).expect("rustfmt returned non-UTF8 string")
|
||||
);
|
||||
}
|
20
config_proc_macro/tests/smoke.rs
Normal file
20
config_proc_macro/tests/smoke.rs
Normal file
@ -0,0 +1,20 @@
|
||||
pub mod config {
|
||||
pub trait ConfigType: Sized {
|
||||
fn doc_hint() -> String;
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[allow(unused_imports)]
|
||||
mod tests {
|
||||
use config_proc_macro::config_type;
|
||||
|
||||
#[config_type]
|
||||
enum Bar {
|
||||
Foo,
|
||||
Bar,
|
||||
#[doc_hint = "foo_bar"]
|
||||
FooBar,
|
||||
FooFoo(i32),
|
||||
}
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
//! Configuration options related to rewriting a list.
|
||||
|
||||
use crate::config::config_type::ConfigType;
|
||||
use config_proc_macro::config_type;
|
||||
|
||||
use crate::config::IndentStyle;
|
||||
|
||||
/// The definitive formatting tactic for lists.
|
||||
@ -25,31 +26,27 @@ impl DefinitiveListTactic {
|
||||
/// Formatting tactic for lists. This will be cast down to a
|
||||
/// `DefinitiveListTactic` depending on the number and length of the items and
|
||||
/// their comments.
|
||||
#[derive(Eq, PartialEq, Debug, Copy, Clone)]
|
||||
#[config_type]
|
||||
pub enum ListTactic {
|
||||
// One item per row.
|
||||
/// One item per row.
|
||||
Vertical,
|
||||
// All items on one row.
|
||||
/// All items on one row.
|
||||
Horizontal,
|
||||
// Try Horizontal layout, if that fails then vertical.
|
||||
/// Try Horizontal layout, if that fails then vertical.
|
||||
HorizontalVertical,
|
||||
// HorizontalVertical with a soft limit of n characters.
|
||||
/// HorizontalVertical with a soft limit of n characters.
|
||||
LimitedHorizontalVertical(usize),
|
||||
// Pack as many items as possible per row over (possibly) many rows.
|
||||
/// Pack as many items as possible per row over (possibly) many rows.
|
||||
Mixed,
|
||||
}
|
||||
|
||||
impl_enum_serialize_and_deserialize!(ListTactic, Vertical, Horizontal, HorizontalVertical, Mixed);
|
||||
|
||||
#[derive(Eq, PartialEq, Debug, Copy, Clone)]
|
||||
#[config_type]
|
||||
pub enum SeparatorTactic {
|
||||
Always,
|
||||
Never,
|
||||
Vertical,
|
||||
}
|
||||
|
||||
impl_enum_serialize_and_deserialize!(SeparatorTactic, Always, Never, Vertical);
|
||||
|
||||
impl SeparatorTactic {
|
||||
pub fn from_bool(b: bool) -> SeparatorTactic {
|
||||
if b {
|
||||
@ -61,14 +58,12 @@ impl SeparatorTactic {
|
||||
}
|
||||
|
||||
/// Where to put separator.
|
||||
#[derive(Eq, PartialEq, Debug, Copy, Clone)]
|
||||
#[config_type]
|
||||
pub enum SeparatorPlace {
|
||||
Front,
|
||||
Back,
|
||||
}
|
||||
|
||||
impl_enum_serialize_and_deserialize!(SeparatorPlace, Front, Back);
|
||||
|
||||
impl SeparatorPlace {
|
||||
pub fn is_front(self) -> bool {
|
||||
self == SeparatorPlace::Front
|
||||
|
@ -3,182 +3,85 @@ use std::fmt;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use atty;
|
||||
use config_proc_macro::config_type;
|
||||
use serde::de::{Deserialize, Deserializer, SeqAccess, Visitor};
|
||||
|
||||
use crate::config::config_type::ConfigType;
|
||||
use crate::config::lists::*;
|
||||
use crate::config::Config;
|
||||
|
||||
/// Macro that will stringify the enum variants or a provided textual repr
|
||||
#[macro_export]
|
||||
macro_rules! configuration_option_enum_stringify {
|
||||
($variant:ident) => {
|
||||
stringify!($variant)
|
||||
};
|
||||
|
||||
($_variant:ident: $value:expr) => {
|
||||
stringify!($value)
|
||||
};
|
||||
#[config_type]
|
||||
pub enum NewlineStyle {
|
||||
/// Auto-detect based on the raw source input.
|
||||
Auto,
|
||||
/// Force CRLF (`\r\n`).
|
||||
Windows,
|
||||
/// Force CR (`\n).
|
||||
Unix,
|
||||
/// `\r\n` in Windows, `\n`` on other platforms.
|
||||
Native,
|
||||
}
|
||||
|
||||
/// Macro for deriving implementations of Serialize/Deserialize for enums
|
||||
#[macro_export]
|
||||
macro_rules! impl_enum_serialize_and_deserialize {
|
||||
( $e:ident, $( $variant:ident $(: $value:expr)* ),* ) => {
|
||||
impl ::serde::ser::Serialize for $e {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where S: ::serde::ser::Serializer
|
||||
{
|
||||
use serde::ser::Error;
|
||||
|
||||
// We don't know whether the user of the macro has given us all options.
|
||||
#[allow(unreachable_patterns)]
|
||||
match *self {
|
||||
$(
|
||||
$e::$variant => serializer.serialize_str(
|
||||
configuration_option_enum_stringify!($variant $(: $value)*)
|
||||
),
|
||||
)*
|
||||
_ => {
|
||||
Err(S::Error::custom(format!("Cannot serialize {:?}", self)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> ::serde::de::Deserialize<'de> for $e {
|
||||
fn deserialize<D>(d: D) -> Result<Self, D::Error>
|
||||
where D: ::serde::Deserializer<'de> {
|
||||
use serde::de::{Error, Visitor};
|
||||
use std::marker::PhantomData;
|
||||
use std::fmt;
|
||||
struct StringOnly<T>(PhantomData<T>);
|
||||
impl<'de, T> Visitor<'de> for StringOnly<T>
|
||||
where T: ::serde::Deserializer<'de> {
|
||||
type Value = String;
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
formatter.write_str("string")
|
||||
}
|
||||
fn visit_str<E>(self, value: &str) -> Result<String, E> {
|
||||
Ok(String::from(value))
|
||||
}
|
||||
}
|
||||
let s = d.deserialize_string(StringOnly::<D>(PhantomData))?;
|
||||
$(
|
||||
if configuration_option_enum_stringify!($variant $(: $value)*)
|
||||
.eq_ignore_ascii_case(&s) {
|
||||
return Ok($e::$variant);
|
||||
}
|
||||
)*
|
||||
static ALLOWED: &'static[&str] = &[
|
||||
$(configuration_option_enum_stringify!($variant $(: $value)*),)*];
|
||||
Err(D::Error::unknown_variant(&s, ALLOWED))
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::str::FromStr for $e {
|
||||
type Err = &'static str;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
$(
|
||||
if configuration_option_enum_stringify!($variant $(: $value)*)
|
||||
.eq_ignore_ascii_case(s) {
|
||||
return Ok($e::$variant);
|
||||
}
|
||||
)*
|
||||
Err("Bad variant")
|
||||
}
|
||||
}
|
||||
|
||||
impl ConfigType for $e {
|
||||
fn doc_hint() -> String {
|
||||
let mut variants = Vec::new();
|
||||
$(
|
||||
variants.push(
|
||||
configuration_option_enum_stringify!($variant $(: $value)*)
|
||||
);
|
||||
)*
|
||||
format!("[{}]", variants.join("|"))
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! configuration_option_enum {
|
||||
($e:ident: $( $name:ident $(: $value:expr)* ),+ $(,)*) => (
|
||||
#[derive(Copy, Clone, Eq, PartialEq)]
|
||||
pub enum $e {
|
||||
$( $name ),+
|
||||
}
|
||||
|
||||
impl ::std::fmt::Debug for $e {
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
|
||||
f.write_str(match self {
|
||||
$(
|
||||
$e::$name => configuration_option_enum_stringify!($name $(: $value)*),
|
||||
)+
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl_enum_serialize_and_deserialize!($e, $( $name $(: $value)* ),+);
|
||||
);
|
||||
}
|
||||
|
||||
configuration_option_enum! { NewlineStyle:
|
||||
Auto, // Auto-detect based on the raw source input
|
||||
Windows, // \r\n
|
||||
Unix, // \n
|
||||
Native, // \r\n in Windows, \n on other platforms
|
||||
}
|
||||
|
||||
configuration_option_enum! { BraceStyle:
|
||||
#[config_type]
|
||||
/// Where to put the opening brace of items (`fn`, `impl`, etc.).
|
||||
pub enum BraceStyle {
|
||||
/// Put the opening brace on the next line.
|
||||
AlwaysNextLine,
|
||||
/// Put the opening brace on the same line, if possible.
|
||||
PreferSameLine,
|
||||
// Prefer same line except where there is a where-clause, in which case force
|
||||
// the brace to the next line.
|
||||
/// Prefer the same line except where there is a where-clause, in which
|
||||
/// case force the brace to be put on the next line.
|
||||
SameLineWhere,
|
||||
}
|
||||
|
||||
configuration_option_enum! { ControlBraceStyle:
|
||||
// K&R style, Rust community default
|
||||
#[config_type]
|
||||
/// Where to put the opening brace of conditional expressions (`if`, `match`, etc.).
|
||||
pub enum ControlBraceStyle {
|
||||
/// K&R style, Rust community default
|
||||
AlwaysSameLine,
|
||||
// Stroustrup style
|
||||
/// Stroustrup style
|
||||
ClosingNextLine,
|
||||
// Allman style
|
||||
/// Allman style
|
||||
AlwaysNextLine,
|
||||
}
|
||||
|
||||
configuration_option_enum! { IndentStyle:
|
||||
// First line on the same line as the opening brace, all lines aligned with
|
||||
// the first line.
|
||||
#[config_type]
|
||||
/// How to indent.
|
||||
pub enum IndentStyle {
|
||||
/// First line on the same line as the opening brace, all lines aligned with
|
||||
/// the first line.
|
||||
Visual,
|
||||
// First line is on a new line and all lines align with block indent.
|
||||
/// First line is on a new line and all lines align with **block** indent.
|
||||
Block,
|
||||
}
|
||||
|
||||
configuration_option_enum! { Density:
|
||||
// Fit as much on one line as possible.
|
||||
#[config_type]
|
||||
/// How to place a list-like items.
|
||||
pub enum Density {
|
||||
/// Fit as much on one line as possible.
|
||||
Compressed,
|
||||
// Use more lines.
|
||||
/// Use more lines.
|
||||
Tall,
|
||||
// Place every item on a separate line.
|
||||
/// Place every item on a separate line.
|
||||
Vertical,
|
||||
}
|
||||
|
||||
configuration_option_enum! { TypeDensity:
|
||||
// No spaces around "=" and "+"
|
||||
#[config_type]
|
||||
/// Spacing around type combinators.
|
||||
pub enum TypeDensity {
|
||||
/// No spaces around "=" and "+"
|
||||
Compressed,
|
||||
// Spaces around " = " and " + "
|
||||
/// Spaces around " = " and " + "
|
||||
Wide,
|
||||
}
|
||||
|
||||
configuration_option_enum! { Heuristics:
|
||||
// Turn off any heuristics
|
||||
#[config_type]
|
||||
/// To what extent does rustfmt pursue its heuristics?
|
||||
pub enum Heuristics {
|
||||
/// Turn off any heuristics
|
||||
Off,
|
||||
// Turn on max heuristics
|
||||
/// Turn on max heuristics
|
||||
Max,
|
||||
// Use Rustfmt's defaults
|
||||
/// Use Rustfmt's defaults
|
||||
Default,
|
||||
}
|
||||
|
||||
@ -193,45 +96,51 @@ impl Density {
|
||||
}
|
||||
}
|
||||
|
||||
configuration_option_enum! { ReportTactic:
|
||||
#[config_type]
|
||||
pub enum ReportTactic {
|
||||
Always,
|
||||
Unnumbered,
|
||||
Never,
|
||||
}
|
||||
|
||||
// What Rustfmt should emit. Mostly corresponds to the `--emit` command line
|
||||
// option.
|
||||
configuration_option_enum! { EmitMode:
|
||||
// Emits to files.
|
||||
/// What Rustfmt should emit. Mostly corresponds to the `--emit` command line
|
||||
/// option.
|
||||
#[config_type]
|
||||
pub enum EmitMode {
|
||||
/// Emits to files.
|
||||
Files,
|
||||
// Writes the output to stdout.
|
||||
/// Writes the output to stdout.
|
||||
Stdout,
|
||||
// Displays how much of the input file was processed
|
||||
/// Displays how much of the input file was processed
|
||||
Coverage,
|
||||
// Unfancy stdout
|
||||
/// Unfancy stdout
|
||||
Checkstyle,
|
||||
// Output the changed lines (for internal value only)
|
||||
/// Output the changed lines (for internal value only)
|
||||
ModifiedLines,
|
||||
// Checks if a diff can be generated. If so, rustfmt outputs a diff and quits with exit code 1.
|
||||
// This option is designed to be run in CI where a non-zero exit signifies non-standard code
|
||||
// formatting. Used for `--check`.
|
||||
/// Checks if a diff can be generated. If so, rustfmt outputs a diff and
|
||||
/// quits with exit code 1.
|
||||
/// This option is designed to be run in CI where a non-zero exit signifies
|
||||
/// non-standard code formatting. Used for `--check`.
|
||||
Diff,
|
||||
}
|
||||
|
||||
// Client-preference for coloured output.
|
||||
configuration_option_enum! { Color:
|
||||
// Always use color, whether it is a piped or terminal output
|
||||
/// Client-preference for coloured output.
|
||||
#[config_type]
|
||||
pub enum Color {
|
||||
/// Always use color, whether it is a piped or terminal output
|
||||
Always,
|
||||
// Never use color
|
||||
/// Never use color
|
||||
Never,
|
||||
// Automatically use color, if supported by terminal
|
||||
/// Automatically use color, if supported by terminal
|
||||
Auto,
|
||||
}
|
||||
|
||||
configuration_option_enum! { Version:
|
||||
// 1.x.y
|
||||
#[config_type]
|
||||
/// rustfmt format style version.
|
||||
pub enum Version {
|
||||
/// 1.x.y. When specified, rustfmt will format in the same style as 1.0.0.
|
||||
One,
|
||||
// 2.x.y
|
||||
/// 2.x.y. When specified, rustfmt will formatin the the latest style.
|
||||
Two,
|
||||
}
|
||||
|
||||
@ -246,12 +155,14 @@ impl Color {
|
||||
}
|
||||
}
|
||||
|
||||
// How chatty should Rustfmt be?
|
||||
configuration_option_enum! { Verbosity:
|
||||
// Emit more.
|
||||
/// How chatty should Rustfmt be?
|
||||
#[config_type]
|
||||
pub enum Verbosity {
|
||||
/// Emit more.
|
||||
Verbose,
|
||||
/// Default.
|
||||
Normal,
|
||||
// Emit as little as possible.
|
||||
/// Emit as little as possible.
|
||||
Quiet,
|
||||
}
|
||||
|
||||
@ -417,10 +328,17 @@ pub trait CliOptions {
|
||||
fn config_path(&self) -> Option<&Path>;
|
||||
}
|
||||
|
||||
// The edition of the compiler (RFC 2052)
|
||||
configuration_option_enum! { Edition:
|
||||
Edition2015: 2015,
|
||||
Edition2018: 2018,
|
||||
/// The edition of the syntax and semntics of code (RFC 2052).
|
||||
#[config_type]
|
||||
pub enum Edition {
|
||||
#[value = "2015"]
|
||||
#[doc_hint = "2015"]
|
||||
/// Edition 2015.
|
||||
Edition2015,
|
||||
#[value = "2018"]
|
||||
#[doc_hint = "2018"]
|
||||
/// Edition 2018.
|
||||
Edition2018,
|
||||
}
|
||||
|
||||
impl Default for Edition {
|
||||
|
@ -612,7 +612,7 @@ pub fn extract_post_comment(
|
||||
post_snippet[1..].trim_matches(white_space)
|
||||
} else if post_snippet.starts_with(separator) {
|
||||
post_snippet[separator.len()..].trim_matches(white_space)
|
||||
} else if post_snippet.ends_with(',') {
|
||||
} else if post_snippet.ends_with(',') && !post_snippet.trim().starts_with("//") {
|
||||
post_snippet[..(post_snippet.len() - 1)].trim_matches(white_space)
|
||||
} else {
|
||||
post_snippet
|
||||
|
7
tests/source/issue-3532.rs
Normal file
7
tests/source/issue-3532.rs
Normal file
@ -0,0 +1,7 @@
|
||||
fn foo(a: T) {
|
||||
match a {
|
||||
1 => {}
|
||||
0 => {}
|
||||
// _ => panic!("doesn't format!"),
|
||||
}
|
||||
}
|
5
tests/target/issue-2941.rs
Normal file
5
tests/target/issue-2941.rs
Normal file
@ -0,0 +1,5 @@
|
||||
// rustfmt-wrap_comments: true
|
||||
|
||||
//! ```
|
||||
//! \
|
||||
//! ```
|
6
tests/target/issue-3532.rs
Normal file
6
tests/target/issue-3532.rs
Normal file
@ -0,0 +1,6 @@
|
||||
fn foo(a: T) {
|
||||
match a {
|
||||
1 => {}
|
||||
0 => {} // _ => panic!("doesn't format!"),
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user