Add rustfmt and clippy to CI and address clippy warnings.

This commit is contained in:
Jimmy Cuadra 2019-06-02 17:35:26 -07:00
parent a92e71f873
commit 6a09f1f754
7 changed files with 140 additions and 23 deletions

View File

@ -1 +0,0 @@
merge_imports = true

View File

@ -1,4 +1,12 @@
language: "rust"
before_script:
- "rustup component add rustfmt"
- "rustup component add clippy"
script:
- "cargo fmt --all -- --check"
- "cargo clippy --all-targets --all-features -- -D warnings"
- "cargo build --verbose"
- "cargo test --verbose"
notifications:
email: false
irc:

View File

@ -1,11 +1,20 @@
//! Details of the `metadata` section of the procedural macro.
use syn::{punctuated::Pair, Expr, FieldValue, Lit, Member};
/// The result of processing the `metadata` section of the macro.
pub struct Metadata {
/// The description field.
pub description: String,
/// The method field.
pub method: String,
/// The name field.
pub name: String,
/// The path field.
pub path: String,
/// The rate_limited field.
pub rate_limited: bool,
/// The description field.
pub requires_authentication: bool,
}
@ -101,7 +110,7 @@ impl From<Vec<FieldValue>> for Metadata {
}
}
Metadata {
Self {
description: description.expect("ruma_api! `metadata` is missing `description`"),
method: method.expect("ruma_api! `metadata` is missing `method`"),
name: name.expect("ruma_api! `metadata` is missing `name`"),

View File

@ -1,3 +1,5 @@
//! Details of the `ruma-api` procedural macro.
use proc_macro2::{Span, TokenStream};
use quote::{quote, ToTokens};
use syn::{
@ -12,6 +14,7 @@ mod response;
use self::{metadata::Metadata, request::Request, response::Response};
/// Removes `serde` attributes from struct fields.
pub fn strip_serde_attrs(field: &Field) -> Field {
let mut field = field.clone();
@ -39,15 +42,19 @@ pub fn strip_serde_attrs(field: &Field) -> Field {
field
}
/// The result of processing the `ruma_api` macro, ready for output back to source code.
pub struct Api {
/// The `metadata` section of the macro.
metadata: Metadata,
/// The `request` section of the macro.
request: Request,
/// The `response` section of the macro.
response: Response,
}
impl From<RawApi> for Api {
fn from(raw_api: RawApi) -> Self {
Api {
Self {
metadata: raw_api.metadata.into(),
request: raw_api.request.into(),
response: raw_api.response.into(),
@ -88,7 +95,7 @@ impl ToTokens for Api {
let request_path_init_fields = self.request.request_path_init_fields();
let path_segments = path_str[1..].split('/').into_iter();
let path_segments = path_str[1..].split('/');
let path_segment_push = path_segments.clone().map(|segment| {
let arg = if segment.starts_with(':') {
let path_var = &segment[1..];
@ -450,6 +457,7 @@ impl ToTokens for Api {
type Request = Request;
type Response = Response;
/// Metadata for this endpoint.
const METADATA: ::ruma_api::Metadata = ::ruma_api::Metadata {
description: #description,
method: ::http::Method::#method,
@ -465,6 +473,7 @@ impl ToTokens for Api {
}
}
/// Custom keyword macros for syn.
mod kw {
use syn::custom_keyword;
@ -473,9 +482,13 @@ mod kw {
custom_keyword!(response);
}
/// The entire `ruma_api!` macro structure directly as it appears in the source code..
pub struct RawApi {
/// The `metadata` section of the macro.
pub metadata: Vec<FieldValue>,
/// The `request` section of the macro.
pub request: Vec<Field>,
/// The `response` section of the macro.
pub response: Vec<Field>,
}
@ -493,7 +506,7 @@ impl Parse for RawApi {
let response;
braced!(response in input);
Ok(RawApi {
Ok(Self {
metadata: metadata
.parse_terminated::<FieldValue, Token![,]>(FieldValue::parse)?
.into_iter()

View File

@ -1,14 +1,19 @@
//! Details of the `request` section of the procedural macro.
use proc_macro2::{Span, TokenStream};
use quote::{quote, quote_spanned, ToTokens};
use syn::{spanned::Spanned, Field, Ident, Lit, Meta, NestedMeta};
use crate::api::strip_serde_attrs;
/// The result of processing the `request` section of the macro.
pub struct Request {
/// The fields of the request.
fields: Vec<RequestField>,
}
impl Request {
/// Produces code to add necessary HTTP headers to an `http::Request`.
pub fn add_headers_to_request(&self) -> TokenStream {
let append_stmts = self.header_fields().map(|request_field| {
let (field, header_name_string) = match request_field {
@ -33,6 +38,7 @@ impl Request {
}
}
/// Produces code to extract fields from the HTTP headers in an `http::Request`.
pub fn parse_headers_from_request(&self) -> TokenStream {
let fields = self.header_fields().map(|request_field| {
let (field, header_name_string) = match request_field {
@ -56,43 +62,50 @@ impl Request {
}
}
/// Whether or not this request has any data in the HTTP body.
pub fn has_body_fields(&self) -> bool {
self.fields.iter().any(|field| field.is_body())
}
/// Whether or not this request has any data in HTTP headers.
pub fn has_header_fields(&self) -> bool {
self.fields.iter().any(|field| field.is_header())
}
/// Whether or not this request has any data in the URL path.
pub fn has_path_fields(&self) -> bool {
self.fields.iter().any(|field| field.is_path())
}
/// Whether or not this request has any data in the query string.
pub fn has_query_fields(&self) -> bool {
self.fields.iter().any(|field| field.is_query())
}
/// Produces an iterator over all the header fields.
pub fn header_fields(&self) -> impl Iterator<Item = &RequestField> {
self.fields.iter().filter(|field| field.is_header())
}
/// Gets the number of path fields.
pub fn path_field_count(&self) -> usize {
self.fields.iter().filter(|field| field.is_path()).count()
}
/// Gets the path field with the given name.
pub fn path_field(&self, name: &str) -> Option<&Field> {
self.fields
.iter()
.flat_map(|f| f.field_(RequestFieldKind::Path))
.flat_map(|f| f.field_of_kind(RequestFieldKind::Path))
.find(|field| {
field
.ident
.as_ref()
.expect("expected field to have an identifier")
.to_string()
== name
})
}
/// Returns the body field.
pub fn newtype_body_field(&self) -> Option<&Field> {
for request_field in self.fields.iter() {
match *request_field {
@ -106,33 +119,41 @@ impl Request {
None
}
/// Produces code for a struct initializer for body fields on a variable named `request`.
pub fn request_body_init_fields(&self) -> TokenStream {
self.struct_init_fields(RequestFieldKind::Body, quote!(request))
}
/// Produces code for a struct initializer for path fields on a variable named `request`.
pub fn request_path_init_fields(&self) -> TokenStream {
self.struct_init_fields(RequestFieldKind::Path, quote!(request))
}
/// Produces code for a struct initializer for query string fields on a variable named `request`.
pub fn request_query_init_fields(&self) -> TokenStream {
self.struct_init_fields(RequestFieldKind::Query, quote!(request))
}
/// Produces code for a struct initializer for body fields on a variable named `request_body`.
pub fn request_init_body_fields(&self) -> TokenStream {
self.struct_init_fields(RequestFieldKind::Body, quote!(request_body))
}
/// Produces code for a struct initializer for query string fields on a variable named
/// `request_query`.
pub fn request_init_query_fields(&self) -> TokenStream {
self.struct_init_fields(RequestFieldKind::Query, quote!(request_query))
}
/// Produces code for a struct initializer for the given field kind to be accessed through the
/// given variable name.
fn struct_init_fields(
&self,
request_field_kind: RequestFieldKind,
src: TokenStream,
) -> TokenStream {
let fields = self.fields.iter().filter_map(|f| {
f.field_(request_field_kind).map(|field| {
f.field_of_kind(request_field_kind).map(|field| {
let field_name = field
.ident
.as_ref()
@ -222,7 +243,7 @@ impl From<Vec<Field>> for Request {
RequestField::new(field_kind, field, header)
}).collect();
Request { fields }
Self { fields }
}
}
@ -345,16 +366,23 @@ impl ToTokens for Request {
}
}
/// The types of fields that a request can have.
pub enum RequestField {
/// JSON data in the body of the request.
Body(Field),
/// Data in an HTTP header.
Header(Field, String),
/// A specific data type in the body of the request.
NewtypeBody(Field),
/// Data that appears in the URL path.
Path(Field),
/// Data that appears in the query string.
Query(Field),
}
impl RequestField {
fn new(kind: RequestFieldKind, field: Field, header: Option<String>) -> RequestField {
/// Creates a new `RequestField`.
fn new(kind: RequestFieldKind, field: Field, header: Option<String>) -> Self {
match kind {
RequestFieldKind::Body => RequestField::Body(field),
RequestFieldKind::Header => {
@ -366,6 +394,7 @@ impl RequestField {
}
}
/// Gets the kind of the request field.
fn kind(&self) -> RequestFieldKind {
match *self {
RequestField::Body(..) => RequestFieldKind::Body,
@ -376,33 +405,39 @@ impl RequestField {
}
}
/// Whether or not this request field is a body kind.
fn is_body(&self) -> bool {
self.kind() == RequestFieldKind::Body
}
/// Whether or not this request field is a header kind.
fn is_header(&self) -> bool {
self.kind() == RequestFieldKind::Header
}
/// Whether or not this request field is a path kind.
fn is_path(&self) -> bool {
self.kind() == RequestFieldKind::Path
}
/// Whether or not this request field is a query string kind.
fn is_query(&self) -> bool {
self.kind() == RequestFieldKind::Query
}
/// Gets the inner `Field` value.
fn field(&self) -> &Field {
match *self {
RequestField::Body(ref field) => field,
RequestField::Header(ref field, _) => field,
RequestField::NewtypeBody(ref field) => field,
RequestField::Path(ref field) => field,
RequestField::Query(ref field) => field,
RequestField::Body(ref field)
| RequestField::Header(ref field, _)
| RequestField::NewtypeBody(ref field)
| RequestField::Path(ref field)
| RequestField::Query(ref field) => field,
}
}
fn field_(&self, kind: RequestFieldKind) -> Option<&Field> {
/// Gets the inner `Field` value if it's of the provided kind.
fn field_of_kind(&self, kind: RequestFieldKind) -> Option<&Field> {
if self.kind() == kind {
Some(self.field())
} else {
@ -411,11 +446,17 @@ impl RequestField {
}
}
/// The types of fields that a request can have, without their values.
#[derive(Clone, Copy, PartialEq, Eq)]
enum RequestFieldKind {
/// See the similarly named variant of `RequestField`.
Body,
/// See the similarly named variant of `RequestField`.
Header,
/// See the similarly named variant of `RequestField`.
NewtypeBody,
/// See the similarly named variant of `RequestField`.
Path,
/// See the similarly named variant of `RequestField`.
Query,
}

View File

@ -1,30 +1,39 @@
//! Details of the `response` section of the procedural macro.
use proc_macro2::{Span, TokenStream};
use quote::{quote, quote_spanned, ToTokens};
use syn::{spanned::Spanned, Field, Ident, Lit, Meta, NestedMeta};
use crate::api::strip_serde_attrs;
/// The result of processing the `request` section of the macro.
pub struct Response {
/// The fields of the response.
fields: Vec<ResponseField>,
}
impl Response {
/// Whether or not this response has any data in the HTTP body.
pub fn has_body_fields(&self) -> bool {
self.fields.iter().any(|field| field.is_body())
}
/// Whether or not this response has any fields.
pub fn has_fields(&self) -> bool {
!self.fields.is_empty()
}
/// Whether or not this response has any data in HTTP headers.
pub fn has_header_fields(&self) -> bool {
self.fields.iter().any(|field| field.is_header())
}
/// Whether or not this response has any data in the HTTP body.
pub fn has_body(&self) -> bool {
self.fields.iter().any(|field| !field.is_header())
}
/// Produces code for a request struct initializer.
pub fn init_fields(&self) -> TokenStream {
let fields = self
.fields
@ -75,6 +84,7 @@ impl Response {
}
}
/// Produces code to add necessary HTTP headers to an `http::Response`.
pub fn apply_header_fields(&self) -> TokenStream {
let header_calls = self.fields.iter().filter_map(|response_field| {
if let ResponseField::Header(ref field, ref header) = *response_field {
@ -98,8 +108,9 @@ impl Response {
}
}
/// Produces code to initialize the struct that will be used to create the response body.
pub fn to_body(&self) -> TokenStream {
if let Some(ref field) = self.newtype_body_field() {
if let Some(field) = self.newtype_body_field() {
let field_name = field
.ident
.as_ref()
@ -131,6 +142,7 @@ impl Response {
}
}
/// Gets the newtype body field, if this request has one.
pub fn newtype_body_field(&self) -> Option<&Field> {
for response_field in self.fields.iter() {
match *response_field {
@ -217,7 +229,7 @@ impl From<Vec<Field>> for Response {
}
}).collect();
Response { fields }
Self { fields }
}
}
@ -291,28 +303,35 @@ impl ToTokens for Response {
}
}
/// The types of fields that a response can have.
pub enum ResponseField {
/// JSON data in the body of the response.
Body(Field),
/// Data in an HTTP header.
Header(Field, String),
/// A specific data type in the body of the response.
NewtypeBody(Field),
}
impl ResponseField {
/// Gets the inner `Field` value.
fn field(&self) -> &Field {
match *self {
ResponseField::Body(ref field) => field,
ResponseField::Header(ref field, _) => field,
ResponseField::NewtypeBody(ref field) => field,
ResponseField::Body(ref field)
| ResponseField::Header(ref field, _)
| ResponseField::NewtypeBody(ref field) => field,
}
}
/// Whether or not this response field is a body kind.
fn is_body(&self) -> bool {
match *self {
ResponseField::Body(..) => true,
ResponseField::Body(_) => true,
_ => false,
}
}
/// Whether or not this response field is a header kind.
fn is_header(&self) -> bool {
match *self {
ResponseField::Header(..) => true,
@ -321,8 +340,12 @@ impl ResponseField {
}
}
/// The types of fields that a response can have, without their values.
enum ResponseFieldKind {
/// See the similarly named variant of `ResponseField`.
Body,
/// See the similarly named variant of `ResponseField`.
Header,
/// See the similarly named variant of `ResponseField`.
NewtypeBody,
}

View File

@ -3,7 +3,31 @@
//!
//! See the documentation for the `ruma_api!` macro for usage details.
#![deny(missing_debug_implementations)]
#![deny(
missing_copy_implementations,
missing_debug_implementations,
// missing_docs, # Uncomment when https://github.com/rust-lang/rust/pull/60562 is released.
warnings
)]
#![warn(
clippy::empty_line_after_outer_attr,
clippy::expl_impl_clone_on_copy,
clippy::if_not_else,
clippy::items_after_statements,
clippy::match_same_arms,
clippy::mem_forget,
clippy::missing_docs_in_private_items,
clippy::multiple_inherent_impl,
clippy::mut_mut,
clippy::needless_borrow,
clippy::needless_continue,
clippy::single_match_else,
clippy::unicode_not_nfc,
clippy::use_self,
clippy::used_underscore_binding,
clippy::wrong_pub_self_convention,
clippy::wrong_self_convention
)]
#![recursion_limit = "256"]
extern crate proc_macro;