use crate::algorithm::Printer; use crate::iter::IterDelimited; use crate::INDENT; use std::ptr; use syn::{ AngleBracketedGenericArguments, AssocConst, AssocType, Constraint, Expr, GenericArgument, ParenthesizedGenericArguments, Path, PathArguments, PathSegment, QSelf, }; #[derive(Copy, Clone, PartialEq)] pub enum PathKind { // a::B Simple, // a::B Type, // a::B:: Expr, } impl Printer { pub fn path(&mut self, path: &Path, kind: PathKind) { assert!(!path.segments.is_empty()); for segment in path.segments.iter().delimited() { if !segment.is_first || path.leading_colon.is_some() { self.word("::"); } self.path_segment(&segment, kind); } } pub fn path_segment(&mut self, segment: &PathSegment, kind: PathKind) { self.ident(&segment.ident); self.path_arguments(&segment.arguments, kind); } fn path_arguments(&mut self, arguments: &PathArguments, kind: PathKind) { match arguments { PathArguments::None => {} PathArguments::AngleBracketed(arguments) => { self.angle_bracketed_generic_arguments(arguments, kind); } PathArguments::Parenthesized(arguments) => { self.parenthesized_generic_arguments(arguments); } } } fn generic_argument(&mut self, arg: &GenericArgument) { match arg { GenericArgument::Lifetime(lifetime) => self.lifetime(lifetime), GenericArgument::Type(ty) => self.ty(ty), GenericArgument::Const(expr) => { match expr { Expr::Lit(expr) => self.expr_lit(expr), Expr::Block(expr) => self.expr_block(expr), // ERROR CORRECTION: Add braces to make sure that the // generated code is valid. _ => { self.word("{"); self.expr(expr); self.word("}"); } } } GenericArgument::AssocType(assoc) => self.assoc_type(assoc), GenericArgument::AssocConst(assoc) => self.assoc_const(assoc), GenericArgument::Constraint(constraint) => self.constraint(constraint), #[cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))] _ => unimplemented!("unknown GenericArgument"), } } pub fn angle_bracketed_generic_arguments( &mut self, generic: &AngleBracketedGenericArguments, path_kind: PathKind, ) { if generic.args.is_empty() || path_kind == PathKind::Simple { return; } if path_kind == PathKind::Expr { self.word("::"); } self.word("<"); self.cbox(INDENT); self.zerobreak(); // Print lifetimes before types/consts/bindings, regardless of their // order in self.args. #[derive(Ord, PartialOrd, Eq, PartialEq)] enum Group { First, Second, } fn group(arg: &GenericArgument) -> Group { match arg { GenericArgument::Lifetime(_) => Group::First, GenericArgument::Type(_) | GenericArgument::Const(_) | GenericArgument::AssocType(_) | GenericArgument::AssocConst(_) | GenericArgument::Constraint(_) => Group::Second, #[cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))] _ => Group::Second, } } let last = generic.args.iter().max_by_key(|param| group(param)); for current_group in [Group::First, Group::Second] { for arg in &generic.args { if group(arg) == current_group { self.generic_argument(arg); self.trailing_comma(ptr::eq(arg, last.unwrap())); } } } self.offset(-INDENT); self.end(); self.word(">"); } fn assoc_type(&mut self, assoc: &AssocType) { self.ident(&assoc.ident); if let Some(generics) = &assoc.generics { self.angle_bracketed_generic_arguments(generics, PathKind::Type); } self.word(" = "); self.ty(&assoc.ty); } fn assoc_const(&mut self, assoc: &AssocConst) { self.ident(&assoc.ident); if let Some(generics) = &assoc.generics { self.angle_bracketed_generic_arguments(generics, PathKind::Type); } self.word(" = "); self.expr(&assoc.value); } fn constraint(&mut self, constraint: &Constraint) { self.ident(&constraint.ident); if let Some(generics) = &constraint.generics { self.angle_bracketed_generic_arguments(generics, PathKind::Type); } self.ibox(INDENT); for bound in constraint.bounds.iter().delimited() { if bound.is_first { self.word(": "); } else { self.space(); self.word("+ "); } self.type_param_bound(&bound); } self.end(); } fn parenthesized_generic_arguments(&mut self, arguments: &ParenthesizedGenericArguments) { self.cbox(INDENT); self.word("("); self.zerobreak(); for ty in arguments.inputs.iter().delimited() { self.ty(&ty); self.trailing_comma(ty.is_last); } self.offset(-INDENT); self.word(")"); self.return_type(&arguments.output); self.end(); } pub fn qpath(&mut self, qself: &Option, path: &Path, kind: PathKind) { let qself = match qself { Some(qself) => qself, None => { self.path(path, kind); return; } }; assert!(qself.position < path.segments.len()); self.word("<"); self.ty(&qself.ty); let mut segments = path.segments.iter(); if qself.position > 0 { self.word(" as "); for segment in segments.by_ref().take(qself.position).delimited() { if !segment.is_first || path.leading_colon.is_some() { self.word("::"); } self.path_segment(&segment, PathKind::Type); if segment.is_last { self.word(">"); } } } else { self.word(">"); } for segment in segments { self.word("::"); self.path_segment(segment, kind); } } }